Skip to content

Commit 59c548c

Browse files
committed
[update] build_with_output_callback to create and play the output stream
1 parent 92ac531 commit 59c548c

1 file changed

Lines changed: 244 additions & 2 deletions

File tree

crates/lambda-rs-platform/src/cpal/device.rs

Lines changed: 244 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,147 @@ impl AudioDeviceBuilder {
530530
Callback:
531531
'static + Send + FnMut(&mut dyn AudioOutputWriter, AudioCallbackInfo),
532532
{
533-
let _ = callback;
534-
return self.build();
533+
if let Some(sample_rate) = self.sample_rate {
534+
if sample_rate == 0 {
535+
return Err(AudioError::InvalidSampleRate {
536+
requested: sample_rate,
537+
});
538+
}
539+
}
540+
541+
if let Some(channels) = self.channels {
542+
if channels == 0 {
543+
return Err(AudioError::InvalidChannels {
544+
requested: channels,
545+
});
546+
}
547+
}
548+
549+
let host = cpal_backend::default_host();
550+
551+
let device = host
552+
.default_output_device()
553+
.ok_or(AudioError::NoDefaultDevice)?;
554+
555+
let supported_configs =
556+
device.supported_output_configs().map_err(|error| {
557+
AudioError::SupportedConfigsUnavailable {
558+
details: error.to_string(),
559+
}
560+
})?;
561+
562+
let supported_configs: Vec<cpal_backend::SupportedStreamConfigRange> =
563+
supported_configs.collect();
564+
565+
let selected_config = select_output_stream_config(
566+
&supported_configs,
567+
self.sample_rate,
568+
self.channels,
569+
)?;
570+
571+
let stream_config = selected_config.config();
572+
let sample_rate = stream_config.sample_rate;
573+
let channels = stream_config.channels;
574+
575+
let sample_format = match selected_config.sample_format() {
576+
cpal_backend::SampleFormat::F32 => AudioSampleFormat::F32,
577+
cpal_backend::SampleFormat::I16 => AudioSampleFormat::I16,
578+
cpal_backend::SampleFormat::U16 => AudioSampleFormat::U16,
579+
other => {
580+
return Err(AudioError::UnsupportedSampleFormat {
581+
details: format!("{other:?}"),
582+
});
583+
}
584+
};
585+
586+
let callback_info = AudioCallbackInfo {
587+
sample_rate,
588+
channels,
589+
sample_format,
590+
};
591+
592+
let mut callback = callback;
593+
594+
let stream = match selected_config.sample_format() {
595+
cpal_backend::SampleFormat::F32 => device
596+
.build_output_stream(
597+
&stream_config,
598+
move |data: &mut [f32], _info| {
599+
invoke_output_callback_on_buffer(
600+
channels,
601+
AudioOutputBuffer::F32(data),
602+
callback_info,
603+
&mut callback,
604+
);
605+
return;
606+
},
607+
|_error| {
608+
return;
609+
},
610+
None,
611+
)
612+
.map_err(|error| AudioError::StreamBuildFailed {
613+
details: error.to_string(),
614+
})?,
615+
cpal_backend::SampleFormat::I16 => device
616+
.build_output_stream(
617+
&stream_config,
618+
move |data: &mut [i16], _info| {
619+
invoke_output_callback_on_buffer(
620+
channels,
621+
AudioOutputBuffer::I16(data),
622+
callback_info,
623+
&mut callback,
624+
);
625+
return;
626+
},
627+
|_error| {
628+
return;
629+
},
630+
None,
631+
)
632+
.map_err(|error| AudioError::StreamBuildFailed {
633+
details: error.to_string(),
634+
})?,
635+
cpal_backend::SampleFormat::U16 => device
636+
.build_output_stream(
637+
&stream_config,
638+
move |data: &mut [u16], _info| {
639+
invoke_output_callback_on_buffer(
640+
channels,
641+
AudioOutputBuffer::U16(data),
642+
callback_info,
643+
&mut callback,
644+
);
645+
return;
646+
},
647+
|_error| {
648+
return;
649+
},
650+
None,
651+
)
652+
.map_err(|error| AudioError::StreamBuildFailed {
653+
details: error.to_string(),
654+
})?,
655+
other => {
656+
return Err(AudioError::UnsupportedSampleFormat {
657+
details: format!("{other:?}"),
658+
});
659+
}
660+
};
661+
662+
stream
663+
.play()
664+
.map_err(|error| AudioError::StreamPlayFailed {
665+
details: error.to_string(),
666+
})?;
667+
668+
return Ok(AudioDevice {
669+
_stream: stream,
670+
sample_rate,
671+
channels,
672+
sample_format,
673+
});
535674
}
536675
}
537676

@@ -541,6 +680,20 @@ impl Default for AudioDeviceBuilder {
541680
}
542681
}
543682

683+
fn invoke_output_callback_on_buffer<Callback>(
684+
channels: u16,
685+
buffer: AudioOutputBuffer<'_>,
686+
callback_info: AudioCallbackInfo,
687+
callback: &mut Callback,
688+
) where
689+
Callback: FnMut(&mut dyn AudioOutputWriter, AudioCallbackInfo),
690+
{
691+
let mut writer = InterleavedAudioOutputWriter::new(channels, buffer);
692+
writer.clear();
693+
callback(&mut writer, callback_info);
694+
return;
695+
}
696+
544697
fn sample_format_priority(sample_format: cpal_backend::SampleFormat) -> u8 {
545698
match sample_format {
546699
cpal_backend::SampleFormat::F32 => {
@@ -733,6 +886,95 @@ mod tests {
733886
return;
734887
}
735888

889+
#[test]
890+
fn invoke_output_callback_on_buffer_clears_and_invokes_callback_f32() {
891+
let mut buffer_f32 = [1.0, -1.0, 0.5, -0.5];
892+
let callback_info = AudioCallbackInfo {
893+
sample_rate: 48_000,
894+
channels: 2,
895+
sample_format: AudioSampleFormat::F32,
896+
};
897+
898+
let mut callback_called = false;
899+
let mut callback = |writer: &mut dyn AudioOutputWriter,
900+
info: AudioCallbackInfo| {
901+
callback_called = true;
902+
assert_eq!(info, callback_info);
903+
assert_eq!(writer.channels(), 2);
904+
assert_eq!(writer.frames(), 2);
905+
writer.set_sample(0, 0, 0.5);
906+
return;
907+
};
908+
909+
invoke_output_callback_on_buffer(
910+
2,
911+
AudioOutputBuffer::F32(&mut buffer_f32),
912+
callback_info,
913+
&mut callback,
914+
);
915+
916+
assert!(callback_called);
917+
assert_eq!(buffer_f32, [0.5, 0.0, 0.0, 0.0]);
918+
}
919+
920+
#[test]
921+
fn invoke_output_callback_on_buffer_clears_and_invokes_callback_i16() {
922+
let mut buffer_i16 = [1, -1, 200, -200];
923+
let callback_info = AudioCallbackInfo {
924+
sample_rate: 48_000,
925+
channels: 2,
926+
sample_format: AudioSampleFormat::I16,
927+
};
928+
929+
let mut callback_called = false;
930+
let mut callback = |writer: &mut dyn AudioOutputWriter,
931+
info: AudioCallbackInfo| {
932+
callback_called = true;
933+
assert_eq!(info, callback_info);
934+
writer.set_sample(0, 0, 1.0);
935+
return;
936+
};
937+
938+
invoke_output_callback_on_buffer(
939+
2,
940+
AudioOutputBuffer::I16(&mut buffer_i16),
941+
callback_info,
942+
&mut callback,
943+
);
944+
945+
assert!(callback_called);
946+
assert_eq!(buffer_i16, [32767, 0, 0, 0]);
947+
}
948+
949+
#[test]
950+
fn invoke_output_callback_on_buffer_clears_and_invokes_callback_u16() {
951+
let mut buffer_u16 = [0, 1, 65535, 12345];
952+
let callback_info = AudioCallbackInfo {
953+
sample_rate: 48_000,
954+
channels: 2,
955+
sample_format: AudioSampleFormat::U16,
956+
};
957+
958+
let mut callback_called = false;
959+
let mut callback = |writer: &mut dyn AudioOutputWriter,
960+
info: AudioCallbackInfo| {
961+
callback_called = true;
962+
assert_eq!(info, callback_info);
963+
writer.set_sample(0, 0, -1.0);
964+
return;
965+
};
966+
967+
invoke_output_callback_on_buffer(
968+
2,
969+
AudioOutputBuffer::U16(&mut buffer_u16),
970+
callback_info,
971+
&mut callback,
972+
);
973+
974+
assert!(callback_called);
975+
assert_eq!(buffer_u16, [0, 32768, 32768, 32768]);
976+
}
977+
736978
#[test]
737979
fn writer_clear_sets_silence_for_all_formats() {
738980
let mut buffer_f32 = [1.0, -1.0, 0.5, -0.5];

0 commit comments

Comments
 (0)