3 mpsc
::{sync_channel
, Receiver
, SyncSender
},
6 time
::{Duration
, Instant
},
10 traits
::{DeviceTrait
, HostTrait
, StreamTrait
},
11 BuildStreamError
, DevicesError
, PlayStreamError
, SampleFormat
, SampleRate
, Stream
, StreamError
,
12 SupportedStreamConfigsError
,
16 use crate::soundmodem
::{
17 InputSource
, OutputBuffer
, OutputSink
, SoundmodemErrorSender
, SoundmodemEvent
,
20 pub struct Soundcard
{
21 event_tx
: SyncSender
<SoundcardEvent
>,
25 pub fn new
<S
: Into
<String
>>(card_name
: S
) -> Result
<Self, SoundcardError
> {
26 let (card_tx
, card_rx
) = sync_channel(128);
27 let (setup_tx
, setup_rx
) = sync_channel(1);
28 spawn_soundcard_worker(card_rx
, setup_tx
, card_name
.into
());
29 match setup_rx
.recv() {
30 Ok(Ok(())) => Ok(Self { event_tx
: card_tx
}),
32 Err(_
) => Err(SoundcardError
::SoundcardInit
),
36 pub fn input(&self) -> SoundcardInputSource
{
37 SoundcardInputSource
{
38 event_tx
: self.event_tx
.clone(),
42 pub fn output(&self) -> SoundcardOutputSink
{
44 event_tx
: self.event_tx
.clone(),
48 pub fn set_rx_inverted(&self, inverted
: bool
) {
49 let _
= self.event_tx
.send(SoundcardEvent
::SetRxInverted(inverted
));
52 pub fn set_tx_inverted(&self, inverted
: bool
) {
53 let _
= self.event_tx
.send(SoundcardEvent
::SetTxInverted(inverted
));
56 pub fn supported_output_cards() -> Vec
<String
> {
58 let host
= cpal
::default_host();
59 let Ok(output_devices
) = host
.output_devices() else {
62 for d
in output_devices
{
63 let Ok(mut configs
) = d
.supported_output_configs() else {
67 .any(|config
| config
.channels() == 1 && config
.sample_format() == SampleFormat
::I16
)
69 let Ok(name
) = d
.name() else {
79 pub fn supported_input_cards() -> Vec
<String
> {
81 let host
= cpal
::default_host();
82 let Ok(input_devices
) = host
.inp
ut
_dev
ices
() else {
85 for d
in input_devices
{
86 let Ok(mut configs
) = d
.supported_input_configs() else {
90 .any(|config
| config
.channels() == 1 && config
.sample_format() == SampleFormat
::I16
)
92 let Ok(name
) = d
.name() else {
103 enum SoundcardEvent
{
107 samples
: SyncSender
<SoundmodemEvent
>,
108 errors
: SoundmodemErrorSender
,
112 event_tx
: SyncSender
<SoundmodemEvent
>,
113 buffer
: Arc
<RwLock
<OutputBuffer
>>,
114 errors
: SoundmodemErrorSender
,
119 pub struct SoundcardInputSource
{
120 event_tx
: SyncSender
<SoundcardEvent
>,
123 impl InputSource
for SoundcardInputSource
{
124 fn start(&self, samples
: SyncSender
<SoundmodemEvent
>, errors
: SoundmodemErrorSender
) {
127 .send(SoundcardEvent
::StartInput
{ samples
, errors
});
131 let _
= self.event_tx
.send(SoundcardEvent
::CloseInput
);
135 pub struct SoundcardOutputSink
{
136 event_tx
: SyncSender
<SoundcardEvent
>,
139 impl OutputSink
for SoundcardOutputSink
{
142 event_tx
: SyncSender
<SoundmodemEvent
>,
143 buffer
: Arc
<RwLock
<OutputBuffer
>>,
144 errors
: SoundmodemErrorSender
,
146 let _
= self.event_tx
.send(SoundcardEvent
::StartOutput
{
154 let _
= self.event_tx
.send(SoundcardEvent
::CloseOutput
);
158 fn spawn_soundcard_worker(
159 event_rx
: Receiver
<SoundcardEvent
>,
160 setup_tx
: SyncSender
<Result
<(), SoundcardError
>>,
163 std
::thread
::spawn(move || {
164 let host
= cpal
::default_host();
165 let Some(device
) = host
168 .find
(|d
| d
.name().unwrap
() == card_name
)
170 let _
= setup_tx
.send(Err(SoundcardError
::CardNotFound(card_name
)));
174 let _
= setup_tx
.send(Ok(()));
175 let mut rx_inverted
= false;
176 let mut tx_inverted
= false;
177 let mut input_stream
: Option
<Stream
> = None
;
178 let mut output_stream
: Option
<Stream
> = None
;
180 while let Ok(ev
) = event_rx
.recv() {
182 SoundcardEvent
::SetRxInverted(inv
) => rx_inverted
= inv
,
183 SoundcardEvent
::SetTxInverted(inv
) => tx_inverted
= inv
,
184 SoundcardEvent
::StartInput
{ samples
, errors
} => {
185 let mut input_configs
= match device
.supported_input_configs() {
188 errors
.send_error(SoundcardError
::SupportedConfigs(e
));
192 let input_config
= match input_configs
193 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
197 errors
.send_error(SoundcardError
::NoValidConfigAvailable
);
201 let input_config
= input_config
.with_sample_rate(SampleRate(48000));
202 let errors_1
= errors
.clone();
203 let stream
= match device
.build_input_stream(
204 &input_config
.into
(),
205 move |data
: &[i16], _info
: &cpal
::InputCallbackInfo
| {
206 let out
: Vec
<i16> = data
208 .map(|s
| if rx_inverted
{ s
.saturating_neg() } else { *s
})
210 let _
= samples
.try_send(SoundmodemEvent
::BasebandInput(out
.into
()));
213 errors_1
.send_error(SoundcardError
::Stream(e
));
219 errors
.send_error(SoundcardError
::StreamBuild(e
));
223 if let Err(e
) = stream
.play() {
224 errors
.send_error(SoundcardError
::StreamPlay(e
));
227 input_stream
= Some(stream
);
229 SoundcardEvent
::CloseInput
=> {
230 let _
= input_stream
.take();
232 SoundcardEvent
::StartOutput
{
237 let mut output_configs
= match device
.supported_output_configs() {
240 errors
.send_error(SoundcardError
::SupportedConfigs(e
));
244 let output_config
= match output_configs
245 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
249 errors
.send_error(SoundcardError
::NoValidConfigAvailable
);
253 let output_config
= output_config
.with_sample_rate(SampleRate(48000));
254 let errors_1
= errors
.clone();
255 let stream
= match device
.build_output_stream(
256 &output_config
.into
(),
257 move |data
: &mut [i16], info
: &cpal
::OutputCallbackInfo
| {
259 let ts
= info
.timestamp();
262 .duration_since(&ts
.callback
)
263 .unwrap
_or
(Duration
::ZERO
);
264 let mut buffer
= buffer
.write().unwrap
();
265 buffer
.latency
= latency
;
266 for out
in data
.iter
_m
ut
() {
267 if let Some(s
) = buffer
.samples
.pop_front() {
268 *out
= if tx_inverted
{ s
.saturating_neg() } else { s
};
270 } else if buffer
.idl
ing
{
273 let _
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
);
277 let _
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer
{
279 timestamp
: Instant
::now(),
283 errors_1
.send_error(SoundcardError
::Stream(e
));
289 errors
.send_error(SoundcardError
::StreamBuild(e
));
293 if let Err(e
) = stream
.play() {
294 errors
.send_error(SoundcardError
::StreamPlay(e
));
297 output_stream
= Some(stream
);
299 SoundcardEvent
::CloseOutput
=> {
300 let _
= output_stream
.take();
307 #[derive(Debug, Error)]
308 pub enum SoundcardError
{
309 #[error("sound card init aborted unexpectedly")]
312 #[error("unable to enumerate devices: {0}")]
315 #[error("unable to locate sound card '{0}' - is it in use?")]
316 CardNotFound(String
),
318 #[error("error occurred in soundcard i/o: {0}")]
319 Stream(#[source] StreamError),
321 #[error("unable to retrieve supported configs for soundcard: {0}")]
322 SupportedConfigs(#[source] SupportedStreamConfigsError),
324 #[error("could not find a suitable soundcard config")]
325 NoValidConfigAvailable
,
327 #[error("unable to build soundcard stream: {0}")]
328 StreamBuild(#[source] BuildStreamError),
330 #[error("unable to play stream")]
331 StreamPlay(#[source] PlayStreamError),