1
- use super :: { Open , Sink } ;
1
+ use super :: { Open , Sink , SinkAsBytes } ;
2
2
use crate :: audio:: AudioPacket ;
3
+ use crate :: config:: AudioFormat ;
4
+ use crate :: player:: { NUM_CHANNELS , SAMPLES_PER_SECOND , SAMPLE_RATE } ;
3
5
use alsa:: device_name:: HintIter ;
4
6
use alsa:: pcm:: { Access , Format , Frames , HwParams , PCM } ;
5
7
use alsa:: { Direction , Error , ValueOr } ;
@@ -8,13 +10,14 @@ use std::ffi::CString;
8
10
use std:: io;
9
11
use std:: process:: exit;
10
12
11
- const PREFERED_PERIOD_SIZE : Frames = 5512 ; // Period of roughly 125ms
13
+ const BUFFERED_LATENCY : f32 = 0.125 ; // seconds
12
14
const BUFFERED_PERIODS : Frames = 4 ;
13
15
14
16
pub struct AlsaSink {
15
17
pcm : Option < PCM > ,
18
+ format : AudioFormat ,
16
19
device : String ,
17
- buffer : Vec < i16 > ,
20
+ buffer : Vec < u8 > ,
18
21
}
19
22
20
23
fn list_outputs ( ) {
@@ -34,23 +37,27 @@ fn list_outputs() {
34
37
}
35
38
}
36
39
37
- fn open_device ( dev_name : & str ) -> Result < ( PCM , Frames ) , Box < Error > > {
40
+ fn open_device ( dev_name : & str , format : AudioFormat ) -> Result < ( PCM , Frames ) , Box < Error > > {
38
41
let pcm = PCM :: new ( dev_name, Direction :: Playback , false ) ?;
39
- let mut period_size = PREFERED_PERIOD_SIZE ;
42
+ let alsa_format = match format {
43
+ AudioFormat :: F32 => Format :: float ( ) ,
44
+ AudioFormat :: S32 => Format :: s32 ( ) ,
45
+ AudioFormat :: S24 => Format :: s24 ( ) ,
46
+ AudioFormat :: S24_3 => Format :: S243LE ,
47
+ AudioFormat :: S16 => Format :: s16 ( ) ,
48
+ } ;
49
+
40
50
// http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
41
51
// latency = period_size * periods / (rate * bytes_per_frame)
42
- // For 16 Bit stereo data, one frame has a length of four bytes.
43
- // 500ms = buffer_size / (44100 * 4)
44
- // buffer_size_bytes = 0.5 * 44100 / 4
45
- // buffer_size_frames = 0.5 * 44100 = 22050
52
+ // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes.
53
+ let mut period_size = ( ( SAMPLES_PER_SECOND * format. size ( ) as u32 ) as f32
54
+ * ( BUFFERED_LATENCY / BUFFERED_PERIODS as f32 ) ) as Frames ;
46
55
{
47
- // Set hardware parameters: 44100 Hz / Stereo / 16 bit
48
56
let hwp = HwParams :: any ( & pcm) ?;
49
-
50
57
hwp. set_access ( Access :: RWInterleaved ) ?;
51
- hwp. set_format ( Format :: s16 ( ) ) ?;
52
- hwp. set_rate ( 44100 , ValueOr :: Nearest ) ?;
53
- hwp. set_channels ( 2 ) ?;
58
+ hwp. set_format ( alsa_format ) ?;
59
+ hwp. set_rate ( SAMPLE_RATE , ValueOr :: Nearest ) ?;
60
+ hwp. set_channels ( NUM_CHANNELS as u32 ) ?;
54
61
period_size = hwp. set_period_size_near ( period_size, ValueOr :: Greater ) ?;
55
62
hwp. set_buffer_size_near ( period_size * BUFFERED_PERIODS ) ?;
56
63
pcm. hw_params ( & hwp) ?;
@@ -64,12 +71,12 @@ fn open_device(dev_name: &str) -> Result<(PCM, Frames), Box<Error>> {
64
71
}
65
72
66
73
impl Open for AlsaSink {
67
- fn open ( device : Option < String > ) -> AlsaSink {
68
- info ! ( "Using alsa sink" ) ;
74
+ fn open ( device : Option < String > , format : AudioFormat ) -> Self {
75
+ info ! ( "Using Alsa sink with format: {:?}" , format ) ;
69
76
70
77
let name = match device. as_ref ( ) . map ( AsRef :: as_ref) {
71
78
Some ( "?" ) => {
72
- println ! ( "Listing available alsa outputs" ) ;
79
+ println ! ( "Listing available Alsa outputs: " ) ;
73
80
list_outputs ( ) ;
74
81
exit ( 0 )
75
82
}
@@ -78,8 +85,9 @@ impl Open for AlsaSink {
78
85
}
79
86
. to_string ( ) ;
80
87
81
- AlsaSink {
88
+ Self {
82
89
pcm : None ,
90
+ format : format,
83
91
device : name,
84
92
buffer : vec ! [ ] ,
85
93
}
@@ -89,12 +97,14 @@ impl Open for AlsaSink {
89
97
impl Sink for AlsaSink {
90
98
fn start ( & mut self ) -> io:: Result < ( ) > {
91
99
if self . pcm . is_none ( ) {
92
- let pcm = open_device ( & self . device ) ;
100
+ let pcm = open_device ( & self . device , self . format ) ;
93
101
match pcm {
94
102
Ok ( ( p, period_size) ) => {
95
103
self . pcm = Some ( p) ;
96
104
// Create a buffer for all samples for a full period
97
- self . buffer = Vec :: with_capacity ( ( period_size * 2 ) as usize ) ;
105
+ self . buffer = Vec :: with_capacity (
106
+ period_size as usize * BUFFERED_PERIODS as usize * self . format . size ( ) ,
107
+ ) ;
98
108
}
99
109
Err ( e) => {
100
110
error ! ( "Alsa error PCM open {}" , e) ;
@@ -111,23 +121,22 @@ impl Sink for AlsaSink {
111
121
112
122
fn stop ( & mut self ) -> io:: Result < ( ) > {
113
123
{
114
- let pcm = self . pcm . as_mut ( ) . unwrap ( ) ;
115
124
// Write any leftover data in the period buffer
116
125
// before draining the actual buffer
117
- let io = pcm. io_i16 ( ) . unwrap ( ) ;
118
- match io. writei ( & self . buffer [ ..] ) {
119
- Ok ( _) => ( ) ,
120
- Err ( err) => pcm. try_recover ( err, false ) . unwrap ( ) ,
121
- }
126
+ self . write_bytes ( & [ ] ) . expect ( "could not flush buffer" ) ;
127
+ let pcm = self . pcm . as_mut ( ) . unwrap ( ) ;
122
128
pcm. drain ( ) . unwrap ( ) ;
123
129
}
124
130
self . pcm = None ;
125
131
Ok ( ( ) )
126
132
}
127
133
128
- fn write ( & mut self , packet : & AudioPacket ) -> io:: Result < ( ) > {
134
+ sink_as_bytes ! ( ) ;
135
+ }
136
+
137
+ impl SinkAsBytes for AlsaSink {
138
+ fn write_bytes ( & mut self , data : & [ u8 ] ) -> io:: Result < ( ) > {
129
139
let mut processed_data = 0 ;
130
- let data = packet. samples ( ) ;
131
140
while processed_data < data. len ( ) {
132
141
let data_to_buffer = min (
133
142
self . buffer . capacity ( ) - self . buffer . len ( ) ,
@@ -137,16 +146,24 @@ impl Sink for AlsaSink {
137
146
. extend_from_slice ( & data[ processed_data..processed_data + data_to_buffer] ) ;
138
147
processed_data += data_to_buffer;
139
148
if self . buffer . len ( ) == self . buffer . capacity ( ) {
140
- let pcm = self . pcm . as_mut ( ) . unwrap ( ) ;
141
- let io = pcm. io_i16 ( ) . unwrap ( ) ;
142
- match io. writei ( & self . buffer ) {
143
- Ok ( _) => ( ) ,
144
- Err ( err) => pcm. try_recover ( err, false ) . unwrap ( ) ,
145
- }
149
+ self . write_buf ( ) . expect ( "could not append to buffer" ) ;
146
150
self . buffer . clear ( ) ;
147
151
}
148
152
}
149
153
150
154
Ok ( ( ) )
151
155
}
152
156
}
157
+
158
+ impl AlsaSink {
159
+ fn write_buf ( & mut self ) -> io:: Result < ( ) > {
160
+ let pcm = self . pcm . as_mut ( ) . unwrap ( ) ;
161
+ let io = pcm. io_bytes ( ) ;
162
+ match io. writei ( & self . buffer ) {
163
+ Ok ( _) => ( ) ,
164
+ Err ( err) => pcm. try_recover ( err, false ) . unwrap ( ) ,
165
+ } ;
166
+
167
+ Ok ( ( ) )
168
+ }
169
+ }
0 commit comments