diff --git a/Cargo.toml b/Cargo.toml index 65c33a5..4287da8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasapi" -version = "0.23.0" +version = "0.24.0" edition = "2021" rust-version = "1.76" authors = ["HEnquist "] diff --git a/examples/devices.rs b/examples/devices.rs index 029fba1..251f372 100644 --- a/examples/devices.rs +++ b/examples/devices.rs @@ -14,7 +14,7 @@ fn main() { let state = &dev.get_state().unwrap(); println!( "Device: {:?}. State: {:?}", - &dev.get_friendlyname().unwrap(), + dev.get_friendlyname().unwrap(), state ); } diff --git a/examples/playnoise_exclusive.rs b/examples/playnoise_exclusive.rs index ede461b..839eba7 100644 --- a/examples/playnoise_exclusive.rs +++ b/examples/playnoise_exclusive.rs @@ -130,11 +130,8 @@ fn main() { let render_client = audio_client.get_audiorenderclient().unwrap(); - audio_client.start_stream().unwrap(); - loop { - let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); - - let mut data = vec![0u8; buffer_frame_count as usize * blockalign as usize]; + let mut write_frames = |nbr_frames: usize| { + let mut data = vec![0u8; nbr_frames * blockalign as usize]; for frame in data.chunks_exact_mut(blockalign as usize) { let sample: u32 = rng.random(); let sample_bytes = sample.to_le_bytes(); @@ -144,16 +141,27 @@ fn main() { } } } - - trace!("write"); + trace!("write {} frames", nbr_frames); render_client - .write_to_device(buffer_frame_count as usize, &data, None) + .write_to_device(nbr_frames, &data, None) .unwrap(); trace!("write ok"); + }; + + // Fill the buffer before starting the stream, so that playback starts with + // real audio instead of an empty buffer. + // https://learn.microsoft.com/en-us/windows/win32/coreaudio/rendering-a-stream + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); + + audio_client.start_stream().unwrap(); + loop { if h_event.wait_for_event(1000).is_err() { error!("error, stopping playback"); audio_client.stop_stream().unwrap(); break; } + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); } } diff --git a/examples/playnoise_exclusive_poll.rs b/examples/playnoise_exclusive_poll.rs index 2e62ad7..9ad656c 100644 --- a/examples/playnoise_exclusive_poll.rs +++ b/examples/playnoise_exclusive_poll.rs @@ -149,12 +149,8 @@ fn main() { sleep_period.as_millis() ); - audio_client.start_stream().unwrap(); - - loop { - let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); - - let mut data = vec![0u8; buffer_frame_count as usize * blockalign as usize]; + let mut write_frames = |nbr_frames: usize| { + let mut data = vec![0u8; nbr_frames * blockalign as usize]; for frame in data.chunks_exact_mut(blockalign as usize) { let sample: u32 = rng.random(); let sample_bytes = sample.to_le_bytes(); @@ -164,12 +160,24 @@ fn main() { } } } - - debug!("write {} frames", buffer_frame_count); + debug!("write {} frames", nbr_frames); render_client - .write_to_device(buffer_frame_count as usize, &data, None) + .write_to_device(nbr_frames, &data, None) .unwrap(); trace!("write ok"); + }; + + // Fill the buffer before starting the stream, so that playback starts with + // real audio instead of an empty buffer. + // https://learn.microsoft.com/en-us/windows/win32/coreaudio/rendering-a-stream + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); + + audio_client.start_stream().unwrap(); + + loop { thread::sleep(sleep_period); + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); } } diff --git a/examples/playsine.rs b/examples/playsine.rs index 2e426d9..12b600f 100644 --- a/examples/playsine.rs +++ b/examples/playsine.rs @@ -118,11 +118,8 @@ fn main() { let render_client = audio_client.get_audiorenderclient().unwrap(); - audio_client.start_stream().unwrap(); - loop { - let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); - - let mut data = vec![0u8; buffer_frame_count as usize * blockalign as usize]; + let mut write_frames = |nbr_frames: usize| { + let mut data = vec![0u8; nbr_frames * blockalign as usize]; for frame in data.chunks_exact_mut(blockalign as usize) { let sample = gen.next().unwrap(); let sample_bytes = sample.to_le_bytes(); @@ -132,16 +129,27 @@ fn main() { } } } - - trace!("write"); + trace!("write {} frames", nbr_frames); render_client - .write_to_device(buffer_frame_count as usize, &data, None) + .write_to_device(nbr_frames, &data, None) .unwrap(); trace!("write ok"); + }; + + // Fill the buffer before starting the stream, so that playback starts with + // real audio instead of an empty buffer. + // https://learn.microsoft.com/en-us/windows/win32/coreaudio/rendering-a-stream + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); + + audio_client.start_stream().unwrap(); + loop { if h_event.wait_for_event(1000).is_err() { error!("error, stopping playback"); audio_client.stop_stream().unwrap(); break; } + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); } } diff --git a/examples/playsine_events.rs b/examples/playsine_events.rs index ed52dd9..742ed68 100644 --- a/examples/playsine_events.rs +++ b/examples/playsine_events.rs @@ -72,6 +72,24 @@ fn main() { let render_client = audio_client.get_audiorenderclient().unwrap(); + let mut write_frames = |nbr_frames: usize| { + let mut data = vec![0u8; nbr_frames * blockalign as usize]; + for frame in data.chunks_exact_mut(blockalign as usize) { + let sample = gen.next().unwrap(); + let sample_bytes = sample.to_le_bytes(); + for value in frame.chunks_exact_mut(blockalign as usize / channels) { + for (bufbyte, sinebyte) in value.iter_mut().zip(sample_bytes.iter()) { + *bufbyte = *sinebyte; + } + } + } + trace!("write {} frames", nbr_frames); + render_client + .write_to_device(nbr_frames, &data, None) + .unwrap(); + trace!("write ok"); + }; + let mut callbacks = EventCallbacks::new(); callbacks.set_simple_volume_callback(move |vol, mute, _guid| { @@ -87,30 +105,20 @@ fn main() { let _registered_events = sessioncontrol .register_session_notification(callbacks) .unwrap(); + // Fill the buffer before starting the stream, so that playback starts with + // real audio instead of an empty buffer. + // https://learn.microsoft.com/en-us/windows/win32/coreaudio/rendering-a-stream + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); + audio_client.start_stream().unwrap(); loop { - let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); - - let mut data = vec![0u8; buffer_frame_count as usize * blockalign as usize]; - for frame in data.chunks_exact_mut(blockalign as usize) { - let sample = gen.next().unwrap(); - let sample_bytes = sample.to_le_bytes(); - for value in frame.chunks_exact_mut(blockalign as usize / channels) { - for (bufbyte, sinebyte) in value.iter_mut().zip(sample_bytes.iter()) { - *bufbyte = *sinebyte; - } - } - } - - trace!("write"); - render_client - .write_to_device(buffer_frame_count as usize, &data, None) - .unwrap(); - trace!("write ok"); if h_event.wait_for_event(1000).is_err() { error!("error, stopping playback"); audio_client.stop_stream().unwrap(); break; } + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); } } diff --git a/examples/playsine_poll.rs b/examples/playsine_poll.rs index 3e9fbcd..51c81d7 100644 --- a/examples/playsine_poll.rs +++ b/examples/playsine_poll.rs @@ -123,11 +123,8 @@ fn main() { 500 * buffer_frames as u64 / desired_format.get_samplespersec() as u64, ); - audio_client.start_stream().unwrap(); - loop { - let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); - - let mut data = vec![0u8; buffer_frame_count as usize * blockalign as usize]; + let mut write_frames = |nbr_frames: usize| { + let mut data = vec![0u8; nbr_frames * blockalign as usize]; for frame in data.chunks_exact_mut(blockalign as usize) { let sample = gen.next().unwrap(); let sample_bytes = sample.to_le_bytes(); @@ -137,12 +134,23 @@ fn main() { } } } - - trace!("write"); + trace!("write {} frames", nbr_frames); render_client - .write_to_device(buffer_frame_count as usize, &data, None) + .write_to_device(nbr_frames, &data, None) .unwrap(); trace!("write ok"); + }; + + // Fill the buffer before starting the stream, so that playback starts with + // real audio instead of an empty buffer. + // https://learn.microsoft.com/en-us/windows/win32/coreaudio/rendering-a-stream + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); + + audio_client.start_stream().unwrap(); + loop { thread::sleep(sleep_period); + let buffer_frame_count = audio_client.get_available_space_in_frames().unwrap(); + write_frames(buffer_frame_count as usize); } } diff --git a/examples/processes.rs b/examples/processes.rs index 6381136..8a727dd 100644 --- a/examples/processes.rs +++ b/examples/processes.rs @@ -14,7 +14,7 @@ fn main() { let manager = dev.get_iaudiosessionmanager().unwrap(); let enumerator = manager.get_audiosessionenumerator().unwrap(); - println!("Device: {:?}", &dev.get_friendlyname().unwrap()); + println!("Device: {:?}", dev.get_friendlyname().unwrap()); for i in 0..enumerator.get_count().unwrap() { let control = enumerator.get_session(i).unwrap(); diff --git a/src/api.rs b/src/api.rs index 53f08fc..b0141cc 100644 --- a/src/api.rs +++ b/src/api.rs @@ -367,8 +367,9 @@ impl DeviceEnumerator { /// Get the device of a given Id. The Id can be obtained by calling [Device::get_id()] pub fn get_device(&self, device_id: &str) -> WasapiRes { - let w_id = PCWSTR::from_raw(HSTRING::from(device_id).as_ptr()); - let immdevice = unsafe { self.enumerator.GetDevice(w_id)? }; + // Keep the HSTRING in a variable, to make sure it outlives the call to GetDevice. + let w_id = HSTRING::from(device_id); + let immdevice = unsafe { self.enumerator.GetDevice(&w_id)? }; let device = Device::from_immdevice(immdevice)?; Ok(device) } @@ -1040,15 +1041,18 @@ impl AudioClient { } _ => 0, }; - match stream_mode { - StreamMode::PollingShared { autoconvert, .. } - | StreamMode::EventsShared { autoconvert, .. } => { - if *autoconvert { - streamflags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM - | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; - } + if matches!( + stream_mode, + StreamMode::PollingShared { + autoconvert: true, + .. + } | StreamMode::EventsShared { + autoconvert: true, + .. } - _ => {} + ) { + streamflags |= + AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; } if timing == TimingMode::Events { streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; @@ -1105,14 +1109,6 @@ impl AudioClient { Ok(buffer_frame_count) } - #[deprecated( - since = "0.17.0", - note = "please use the new function name `get_buffer_size` instead" - )] - pub fn get_bufferframecount(&self) -> WasapiRes { - self.get_buffer_size() - } - /// Get current padding in frames. /// This represents the number of frames currently in the buffer, for both capture and render devices. /// The exact meaning depends on how the AudioClient was initialized, see @@ -1144,6 +1140,12 @@ impl AudioClient { } /// Start the stream on an [IAudioClient] + /// + /// For playback, fill the buffer with data before starting the stream, + /// see [Rendering a Stream](https://learn.microsoft.com/en-us/windows/win32/coreaudio/rendering-a-stream). + /// Use [AudioClient::get_available_space_in_frames()] to get the number of frames to write. + /// When using [TimingMode::Events], the event should then be waited for + /// at the start of the playback loop, before writing more data. pub fn start_stream(&self) -> WasapiRes<()> { unsafe { self.client.Start()? }; Ok(()) @@ -1687,6 +1689,7 @@ impl BufferFlags { } } + /// Create a new [BufferFlags] struct with all flags set to false. pub fn none() -> Self { BufferFlags { data_discontinuity: false, @@ -1899,10 +1902,12 @@ impl AcousticEchoCancellationControl { &self, endpoint_id: Option, ) -> WasapiRes<()> { - let endpoint_id = if let Some(endpoint_id) = endpoint_id { - PCWSTR::from_raw(HSTRING::from(endpoint_id).as_ptr()) - } else { - PCWSTR::null() + // Keep the HSTRING in a variable, to make sure it outlives the call to + // SetEchoCancellationRenderEndpoint. + let w_id = endpoint_id.map(HSTRING::from); + let endpoint_id = match &w_id { + Some(id) => PCWSTR::from_raw(id.as_ptr()), + None => PCWSTR::null(), }; unsafe { self.control