]>
code.octet-stream.net Git - m17rt/blob - m17app/src/soundcard.rs
3 mpsc
::{sync_channel
, Receiver
, SyncSender
},
6 time
::{Duration
, Instant
},
10 traits
::{DeviceTrait
, HostTrait
, StreamTrait
},
11 SampleFormat
, SampleRate
, Stream
,
16 soundmodem
::{InputSource
, OutputBuffer
, OutputSink
, SoundmodemEvent
},
19 pub struct Soundcard
{
20 event_tx
: SyncSender
<SoundcardEvent
>,
24 pub fn new
<S
: Into
<String
>>(card_name
: S
) -> Result
<Self, M17Error
> {
25 let (card_tx
, card_rx
) = sync_channel(128);
26 let (setup_tx
, setup_rx
) = sync_channel(1);
27 spawn_soundcard_worker(card_rx
, setup_tx
, card_name
.into
());
28 match setup_rx
.recv() {
29 Ok(Ok(())) => Ok(Self { event_tx
: card_tx
}),
31 Err(_
) => Err(M17Error
::SoundcardInit
),
35 pub fn input(&self) -> SoundcardInputSource
{
36 SoundcardInputSource
{
37 event_tx
: self.event_tx
.clone(),
41 pub fn output(&self) -> SoundcardOutputSink
{
43 event_tx
: self.event_tx
.clone(),
47 pub fn set_rx_inverted(&self, inverted
: bool
) {
48 let _
= self.event_tx
.send(SoundcardEvent
::SetRxInverted(inverted
));
51 pub fn set_tx_inverted(&self, inverted
: bool
) {
52 let _
= self.event_tx
.send(SoundcardEvent
::SetTxInverted(inverted
));
55 pub fn supported_output_cards() -> Vec
<String
> {
57 let host
= cpal
::default_host();
58 let Ok(output_devices
) = host
.output_devices() else {
61 for d
in output_devices
{
62 let Ok(mut configs
) = d
.supported_output_configs() else {
66 .any(|config
| config
.channels() == 1 && config
.sample_format() == SampleFormat
::I16
)
68 let Ok(name
) = d
.name() else {
78 pub fn supported_input_cards() -> Vec
<String
> {
80 let host
= cpal
::default_host();
81 let Ok(input_devices
) = host
.inp
ut
_dev
ices
() else {
84 for d
in input_devices
{
85 let Ok(mut configs
) = d
.supported_input_configs() else {
89 .any(|config
| config
.channels() == 1 && config
.sample_format() == SampleFormat
::I16
)
91 let Ok(name
) = d
.name() else {
102 enum SoundcardEvent
{
106 samples
: SyncSender
<SoundmodemEvent
>,
110 event_tx
: SyncSender
<SoundmodemEvent
>,
111 buffer
: Arc
<RwLock
<OutputBuffer
>>,
116 pub struct SoundcardInputSource
{
117 event_tx
: SyncSender
<SoundcardEvent
>,
120 impl InputSource
for SoundcardInputSource
{
121 fn start(&self, samples
: SyncSender
<SoundmodemEvent
>) {
122 let _
= self.event_tx
.send(SoundcardEvent
::StartInput
{ samples
});
126 let _
= self.event_tx
.send(SoundcardEvent
::CloseInput
);
130 pub struct SoundcardOutputSink
{
131 event_tx
: SyncSender
<SoundcardEvent
>,
134 impl OutputSink
for SoundcardOutputSink
{
135 fn start(&self, event_tx
: SyncSender
<SoundmodemEvent
>, buffer
: Arc
<RwLock
<OutputBuffer
>>) {
138 .send(SoundcardEvent
::StartOutput
{ event_tx
, buffer
});
142 let _
= self.event_tx
.send(SoundcardEvent
::CloseOutput
);
146 fn spawn_soundcard_worker(
147 event_rx
: Receiver
<SoundcardEvent
>,
148 setup_tx
: SyncSender
<Result
<(), M17Error
>>,
151 std
::thread
::spawn(move || {
152 let host
= cpal
::default_host();
153 let Some(device
) = host
156 .find
(|d
| d
.name().unwrap
() == card_name
)
158 let _
= setup_tx
.send(Err(M17Error
::SoundcardNotFound(card_name
)));
162 let _
= setup_tx
.send(Ok(()));
163 let mut rx_inverted
= false;
164 let mut tx_inverted
= false;
165 let mut input_stream
: Option
<Stream
> = None
;
166 let mut output_stream
: Option
<Stream
> = None
;
168 while let Ok(ev
) = event_rx
.recv() {
170 SoundcardEvent
::SetRxInverted(inv
) => rx_inverted
= inv
,
171 SoundcardEvent
::SetTxInverted(inv
) => tx_inverted
= inv
,
172 SoundcardEvent
::StartInput
{ samples
} => {
173 let mut input_configs
= device
.supported_input_configs().unwrap
();
174 let input_config
= input_configs
175 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
177 .with_sample_rate(SampleRate(48000));
180 &input_config
.into
(),
181 move |data
: &[i16], _info
: &cpal
::InputCallbackInfo
| {
182 let out
: Vec
<i16> = data
184 .map(|s
| if rx_inverted
{ s
.saturating_neg() } else { *s
})
187 samples
.try_send(SoundmodemEvent
::BasebandInput(out
.into
()));
191 log
::debug
!("error occurred in soundcard input: {e:?}");
196 stream
.play().unwrap
();
197 input_stream
= Some(stream
);
199 SoundcardEvent
::CloseInput
=> {
200 let _
= input_stream
.take();
202 SoundcardEvent
::StartOutput
{ event_tx
, buffer
} => {
203 let mut output_configs
= device
.supported_output_configs().unwrap
();
204 // TODO: more error handling
205 let output_config
= output_configs
206 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
208 .with_sample_rate(SampleRate(48000));
210 .build_output_stream(
211 &output_config
.into
(),
212 move |data
: &mut [i16], info
: &cpal
::OutputCallbackInfo
| {
214 let ts
= info
.timestamp();
217 .duration_since(&ts
.callback
)
218 .unwrap
_or
(Duration
::ZERO
);
219 let mut buffer
= buffer
.write().unwrap
();
220 buffer
.latency
= latency
;
221 for out
in data
.iter
_m
ut
() {
222 if let Some(s
) = buffer
.samples
.pop_front() {
223 *out
= if tx_inverted
{ s
.saturating_neg() } else { s
};
225 } else if buffer
.idl
ing
{
228 log
::debug
!("output soundcard had underrun");
229 let _
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
);
233 //debug!("latency is {} ms, taken {taken}", latency.as_millis());
234 let _
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer
{
236 timestamp
: Instant
::now(),
241 log
::debug
!("error occurred in soundcard output: {e:?}");
246 stream
.play().unwrap
();
247 output_stream
= Some(stream
);
249 SoundcardEvent
::CloseOutput
=> {
250 let _
= output_stream
.take();