@@ -2,24 +2,25 @@ use super::{Open, Sink, SinkAsBytes};
22use crate :: config:: AudioFormat ;
33use crate :: convert:: Converter ;
44use crate :: decoder:: AudioPacket ;
5- use crate :: { NUM_CHANNELS , SAMPLES_PER_SECOND , SAMPLE_RATE } ;
5+ use crate :: { NUM_CHANNELS , SAMPLE_RATE } ;
66use alsa:: device_name:: HintIter ;
7- use alsa:: pcm:: { Access , Format , Frames , HwParams , PCM } ;
7+ use alsa:: pcm:: { Access , Format , HwParams , PCM } ;
88use alsa:: { Direction , Error , ValueOr } ;
99use std:: cmp:: min;
1010use std:: ffi:: CString ;
1111use std:: io;
1212use std:: process:: exit;
1313use std:: time:: Duration ;
1414
15- const BUFFERED_LATENCY : Duration = Duration :: from_millis ( 125 ) ;
16- const BUFFERED_PERIODS : Frames = 4 ;
15+ // 125 ms Period time * 4 periods = 0.5 sec buffer.
16+ const PERIOD_TIME : Duration = Duration :: from_millis ( 125 ) ;
17+ const NUM_PERIODS : u32 = 4 ;
1718
1819pub struct AlsaSink {
1920 pcm : Option < PCM > ,
2021 format : AudioFormat ,
2122 device : String ,
22- buffer : Vec < u8 > ,
23+ period_buffer : Vec < u8 > ,
2324}
2425
2526fn list_outputs ( ) {
@@ -39,7 +40,7 @@ fn list_outputs() {
3940 }
4041}
4142
42- fn open_device ( dev_name : & str , format : AudioFormat ) -> Result < ( PCM , Frames ) , Box < Error > > {
43+ fn open_device ( dev_name : & str , format : AudioFormat ) -> Result < ( PCM , usize ) , Box < Error > > {
4344 let pcm = PCM :: new ( dev_name, Direction :: Playback , false ) ?;
4445 let alsa_format = match format {
4546 AudioFormat :: F64 => Format :: float64 ( ) ,
@@ -54,28 +55,30 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, Frames), Box
5455 AudioFormat :: S24_3 => Format :: S243BE ,
5556 } ;
5657
57- // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
58- // latency = period_size * periods / (rate * bytes_per_frame)
59- // For stereo samples encoded as 32-bit float, one frame has a length of eight bytes.
60- let mut period_size = ( ( SAMPLES_PER_SECOND * format. size ( ) as u32 ) as f32
61- * ( BUFFERED_LATENCY . as_secs_f32 ( ) / BUFFERED_PERIODS as f32 ) )
62- as Frames ;
63- {
58+ let bytes_per_period = {
6459 let hwp = HwParams :: any ( & pcm) ?;
6560 hwp. set_access ( Access :: RWInterleaved ) ?;
6661 hwp. set_format ( alsa_format) ?;
6762 hwp. set_rate ( SAMPLE_RATE , ValueOr :: Nearest ) ?;
6863 hwp. set_channels ( NUM_CHANNELS as u32 ) ?;
69- period_size = hwp. set_period_size_near ( period_size, ValueOr :: Greater ) ?;
70- hwp. set_buffer_size_near ( period_size * BUFFERED_PERIODS ) ?;
64+ // Deal strictly in time and periods.
65+ hwp. set_periods ( NUM_PERIODS , ValueOr :: Nearest ) ?;
66+ hwp. set_period_time_near ( PERIOD_TIME . as_micros ( ) as u32 , ValueOr :: Nearest ) ?;
7167 pcm. hw_params ( & hwp) ?;
7268
7369 let swp = pcm. sw_params_current ( ) ?;
74- swp. set_start_threshold ( hwp. get_buffer_size ( ) ? - hwp. get_period_size ( ) ?) ?;
70+ // Don't assume we got what we wanted.
71+ // Ask to make sure.
72+ let frames_per_period = hwp. get_period_size ( ) ?;
73+
74+ swp. set_start_threshold ( hwp. get_buffer_size ( ) ? - frames_per_period) ?;
7575 pcm. sw_params ( & swp) ?;
76- }
7776
78- Ok ( ( pcm, period_size) )
77+ // Let ALSA do the math for us.
78+ pcm. frames_to_bytes ( frames_per_period) as usize
79+ } ;
80+
81+ Ok ( ( pcm, bytes_per_period) )
7982}
8083
8184impl Open for AlsaSink {
@@ -97,7 +100,7 @@ impl Open for AlsaSink {
97100 pcm : None ,
98101 format,
99102 device : name,
100- buffer : vec ! [ ] ,
103+ period_buffer : vec ! [ ] ,
101104 }
102105 }
103106}
@@ -107,12 +110,9 @@ impl Sink for AlsaSink {
107110 if self . pcm . is_none ( ) {
108111 let pcm = open_device ( & self . device , self . format ) ;
109112 match pcm {
110- Ok ( ( p, period_size ) ) => {
113+ Ok ( ( p, bytes_per_period ) ) => {
111114 self . pcm = Some ( p) ;
112- // Create a buffer for all samples for a full period
113- self . buffer = Vec :: with_capacity (
114- period_size as usize * BUFFERED_PERIODS as usize * self . format . size ( ) ,
115- ) ;
115+ self . period_buffer = Vec :: with_capacity ( bytes_per_period) ;
116116 }
117117 Err ( e) => {
118118 error ! ( "Alsa error PCM open {}" , e) ;
@@ -147,15 +147,15 @@ impl SinkAsBytes for AlsaSink {
147147 let mut processed_data = 0 ;
148148 while processed_data < data. len ( ) {
149149 let data_to_buffer = min (
150- self . buffer . capacity ( ) - self . buffer . len ( ) ,
150+ self . period_buffer . capacity ( ) - self . period_buffer . len ( ) ,
151151 data. len ( ) - processed_data,
152152 ) ;
153- self . buffer
153+ self . period_buffer
154154 . extend_from_slice ( & data[ processed_data..processed_data + data_to_buffer] ) ;
155155 processed_data += data_to_buffer;
156- if self . buffer . len ( ) == self . buffer . capacity ( ) {
156+ if self . period_buffer . len ( ) == self . period_buffer . capacity ( ) {
157157 self . write_buf ( ) ;
158- self . buffer . clear ( ) ;
158+ self . period_buffer . clear ( ) ;
159159 }
160160 }
161161
@@ -169,7 +169,7 @@ impl AlsaSink {
169169 fn write_buf ( & mut self ) {
170170 let pcm = self . pcm . as_mut ( ) . unwrap ( ) ;
171171 let io = pcm. io_bytes ( ) ;
172- match io. writei ( & self . buffer ) {
172+ match io. writei ( & self . period_buffer ) {
173173 Ok ( _) => ( ) ,
174174 Err ( err) => pcm. try_recover ( err, false ) . unwrap ( ) ,
175175 } ;
0 commit comments