]>
code.octet-stream.net Git - m17rt/blob - m17app/src/soundcard.rs
f89d9f4e43a9ee6a1d34ee667a90b98a7c440b8e
3 mpsc
::{sync_channel
, Receiver
, SyncSender
},
6 time
::{Duration
, Instant
},
10 traits
::{DeviceTrait
, HostTrait
, StreamTrait
},
11 SampleFormat
, SampleRate
, Stream
,
15 error
::{M17Error
, SoundmodemError
},
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
>) -> Result
<(), SoundmodemError
> {
122 Ok(self.event_tx
.send(SoundcardEvent
::StartInput
{ samples
})?
)
125 fn close(&self) -> Result
<(), SoundmodemError
> {
126 Ok(self.event_tx
.send(SoundcardEvent
::CloseInput
)?
)
130 pub struct SoundcardOutputSink
{
131 event_tx
: SyncSender
<SoundcardEvent
>,
134 impl OutputSink
for SoundcardOutputSink
{
137 event_tx
: SyncSender
<SoundmodemEvent
>,
138 buffer
: Arc
<RwLock
<OutputBuffer
>>,
139 ) -> Result
<(), SoundmodemError
> {
142 .send(SoundcardEvent
::StartOutput
{ event_tx
, buffer
})?
)
145 fn close(&self) -> Result
<(), SoundmodemError
> {
146 Ok(self.event_tx
.send(SoundcardEvent
::CloseOutput
)?
)
150 fn spawn_soundcard_worker(
151 event_rx
: Receiver
<SoundcardEvent
>,
152 setup_tx
: SyncSender
<Result
<(), M17Error
>>,
155 std
::thread
::spawn(move || {
156 let host
= cpal
::default_host();
157 let Some(device
) = host
160 .find
(|d
| d
.name().unwrap
() == card_name
)
162 let _
= setup_tx
.send(Err(M17Error
::SoundcardNotFound(card_name
)));
166 let _
= setup_tx
.send(Ok(()));
167 let mut rx_inverted
= false;
168 let mut tx_inverted
= false;
169 let mut input_stream
: Option
<Stream
> = None
;
170 let mut output_stream
: Option
<Stream
> = None
;
172 while let Ok(ev
) = event_rx
.recv() {
174 SoundcardEvent
::SetRxInverted(inv
) => rx_inverted
= inv
,
175 SoundcardEvent
::SetTxInverted(inv
) => tx_inverted
= inv
,
176 SoundcardEvent
::StartInput
{ samples
} => {
177 let mut input_configs
= device
.supported_input_configs().unwrap
();
178 let input_config
= input_configs
179 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
181 .with_sample_rate(SampleRate(48000));
184 &input_config
.into
(),
185 move |data
: &[i16], _info
: &cpal
::InputCallbackInfo
| {
186 let out
: Vec
<i16> = data
188 .map(|s
| if rx_inverted
{ s
.saturating_neg() } else { *s
})
191 samples
.try_send(SoundmodemEvent
::BasebandInput(out
.into
()));
195 log
::debug
!("error occurred in soundcard input: {e:?}");
200 stream
.play().unwrap
();
201 input_stream
= Some(stream
);
203 SoundcardEvent
::CloseInput
=> {
204 let _
= input_stream
.take();
206 SoundcardEvent
::StartOutput
{ event_tx
, buffer
} => {
207 let mut output_configs
= device
.supported_output_configs().unwrap
();
208 // TODO: more error handling
209 let output_config
= output_configs
210 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
212 .with_sample_rate(SampleRate(48000));
214 .build_output_stream(
215 &output_config
.into
(),
216 move |data
: &mut [i16], info
: &cpal
::OutputCallbackInfo
| {
218 let ts
= info
.timestamp();
221 .duration_since(&ts
.callback
)
222 .unwrap
_or
(Duration
::ZERO
);
223 let mut buffer
= buffer
.write().unwrap
();
224 buffer
.latency
= latency
;
225 for out
in data
.iter
_m
ut
() {
226 if let Some(s
) = buffer
.samples
.pop_front() {
227 *out
= if tx_inverted
{ s
.saturating_neg() } else { s
};
229 } else if buffer
.idl
ing
{
232 log
::debug
!("output soundcard had underrun");
233 let _
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
);
237 //debug!("latency is {} ms, taken {taken}", latency.as_millis());
238 let _
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer
{
240 timestamp
: Instant
::now(),
245 log
::debug
!("error occurred in soundcard output: {e:?}");
250 stream
.play().unwrap
();
251 output_stream
= Some(stream
);
253 SoundcardEvent
::CloseOutput
=> {
254 let _
= output_stream
.take();