11use std:: {
22 mem,
33 ops:: ControlFlow ,
4- ptr,
4+ ptr, slice ,
55 sync:: {
66 Arc ,
77 atomic:: { AtomicBool , AtomicU64 , Ordering } ,
@@ -665,6 +665,15 @@ fn run_input(
665665 emit_error ( error_callback, err) ;
666666 }
667667
668+ let stream = & run_ctxt. stream ;
669+ let scratch_len = if stream. sample_format == SampleFormat :: I24 {
670+ stream. max_frames_in_buffer as usize * stream. bytes_per_frame as usize / size_of :: < i32 > ( )
671+ } else {
672+ // The scratch buffer won't be used in this case.
673+ 0 // Vec::with_capacity(0) does not allocate.
674+ } ;
675+ let mut scratch_buffer = vec ! [ 0 ; scratch_len] . into_boxed_slice ( ) ;
676+
668677 loop {
669678 match process_commands_and_await_signal ( & mut run_ctxt, error_callback) {
670679 ControlFlow :: Break ( ( ) ) => break ,
@@ -675,7 +684,12 @@ fn run_input(
675684 AudioClientFlow :: Capture { ref capture_client } => capture_client. clone ( ) ,
676685 _ => unreachable ! ( ) ,
677686 } ;
678- if let Err ( err) = process_input ( & run_ctxt. stream , capture_client, data_callback) {
687+ if let Err ( err) = process_input (
688+ & run_ctxt. stream ,
689+ capture_client,
690+ data_callback,
691+ & mut scratch_buffer,
692+ ) {
679693 emit_error ( error_callback, err) ;
680694 break ;
681695 }
@@ -815,6 +829,7 @@ fn process_input(
815829 stream : & StreamInner ,
816830 capture_client : Audio :: IAudioCaptureClient ,
817831 data_callback : & mut dyn FnMut ( & Data , & CallbackInfo ) ,
832+ scratch_buffer : & mut [ i32 ] ,
818833) -> Result < ( ) , Error > {
819834 unsafe {
820835 // Get the available data in the shared buffer.
@@ -850,10 +865,26 @@ fn process_input(
850865 && flags & Audio :: AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY . 0 as u32 != 0 ;
851866
852867 debug_assert ! ( !buffer. is_null( ) ) ;
868+ let byte_count = frames_available as usize * stream. bytes_per_frame as usize ;
869+ let data = if stream. sample_format == SampleFormat :: I24 {
870+ // WASAPI stores i24 in the upper bits
871+ let source_data =
872+ slice:: from_raw_parts ( buffer. cast ( ) , byte_count / size_of :: < i32 > ( ) ) ;
873+ // use a scratch buffer since the capture buffer isn't meant to be written
874+ let dst = & mut scratch_buffer[ ..source_data. len ( ) ] ;
875+ dst. copy_from_slice ( source_data) ;
876+ for sample in dst. iter_mut ( ) {
877+ // On signed integers, >> is an arithmetic shift,
878+ // which ensures the correct upper bits get shifted in
879+ * sample >>= 8 ;
880+ }
853881
854- let data = buffer as * mut ( ) ;
855- let len = frames_available as usize * stream. bytes_per_frame as usize
856- / stream. sample_format . sample_size ( ) ;
882+ dst. as_mut_ptr ( ) . cast ( )
883+ } else {
884+ buffer. cast ( )
885+ } ;
886+
887+ let len = byte_count / stream. sample_format . sample_size ( ) ;
857888 let data = Data :: from_parts ( data, len, stream. sample_format ) ;
858889
859890 if !stream. draining . load ( Ordering :: Relaxed ) {
@@ -921,6 +952,18 @@ fn process_output(
921952 xrun : false ,
922953 } ,
923954 ) ;
955+
956+ if stream. sample_format == SampleFormat :: I24 {
957+ // WASAPI stores i24 in the upper bits
958+ #[ expect(
959+ clippy:: cast_ptr_alignment,
960+ reason = "WASAPI guarantees the buffer to be aligned to a frame boundary"
961+ ) ]
962+ let buffer_slice_i32 = slice:: from_raw_parts_mut ( buffer. cast :: < i32 > ( ) , len) ;
963+ for sample in buffer_slice_i32 {
964+ * sample <<= 8 ;
965+ }
966+ }
924967 }
925968
926969 render_client. ReleaseBuffer ( frames_available, 0 ) ?;
0 commit comments