]>
code.octet-stream.net Git - m17rt/blob - m17app/src/soundcard.rs
b12ef8839a3d64f9be476e91ebae4ca63a941d39
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 {
67 config
.channels() == 1 && config
.sample_format() == SampleFormat
::I16
71 let Ok(name
) = d
.name() else {
81 pub fn supported_input_cards() -> Vec
<String
> {
83 let host
= cpal
::default_host();
84 let Ok(input_devices
) = host
.inp
ut
_dev
ices
() else {
87 for d
in input_devices
{
88 let Ok(mut configs
) = d
.supported_input_configs() else {
93 config
.channels() == 1 && config
.sample_format() == SampleFormat
::I16
97 let Ok(name
) = d
.name() else {
108 enum SoundcardEvent
{
112 samples
: SyncSender
<SoundmodemEvent
>,
116 event_tx
: SyncSender
<SoundmodemEvent
>,
117 buffer
: Arc
<RwLock
<OutputBuffer
>>,
122 pub struct SoundcardInputSource
{
123 event_tx
: SyncSender
<SoundcardEvent
>,
126 impl InputSource
for SoundcardInputSource
{
127 fn start(&self, samples
: SyncSender
<SoundmodemEvent
>) {
128 let _
= self.event_tx
.send(SoundcardEvent
::StartInput
{ samples
});
132 let _
= self.event_tx
.send(SoundcardEvent
::CloseInput
);
136 pub struct SoundcardOutputSink
{
137 event_tx
: SyncSender
<SoundcardEvent
>,
140 impl OutputSink
for SoundcardOutputSink
{
141 fn start(&self, event_tx
: SyncSender
<SoundmodemEvent
>, buffer
: Arc
<RwLock
<OutputBuffer
>>) {
144 .send(SoundcardEvent
::StartOutput
{ event_tx
, buffer
});
148 let _
= self.event_tx
.send(SoundcardEvent
::CloseOutput
);
152 fn spawn_soundcard_worker(
153 event_rx
: Receiver
<SoundcardEvent
>,
154 setup_tx
: SyncSender
<Result
<(), M17Error
>>,
157 std
::thread
::spawn(move || {
158 let host
= cpal
::default_host();
159 let Some(device
) = host
162 .find
(|d
| d
.name().unwrap
() == card_name
)
164 let _
= setup_tx
.send(Err(M17Error
::SoundcardNotFound(card_name
)));
168 let _
= setup_tx
.send(Ok(()));
169 let mut rx_inverted
= false;
170 let mut tx_inverted
= false;
171 let mut input_stream
: Option
<Stream
> = None
;
172 let mut output_stream
: Option
<Stream
> = None
;
174 while let Ok(ev
) = event_rx
.recv() {
176 SoundcardEvent
::SetRxInverted(inv
) => rx_inverted
= inv
,
177 SoundcardEvent
::SetTxInverted(inv
) => tx_inverted
= inv
,
178 SoundcardEvent
::StartInput
{ samples
} => {
179 let mut input_configs
= device
.supported_input_configs().unwrap
();
180 let input_config
= input_configs
181 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
183 .with_sample_rate(SampleRate(48000));
186 &input_config
.into
(),
187 move |data
: &[i16], _info
: &cpal
::InputCallbackInfo
| {
188 let out
: Vec
<i16> = data
190 .map(|s
| if rx_inverted
{ s
.saturating_neg() } else { *s
})
193 samples
.try_send(SoundmodemEvent
::BasebandInput(out
.into
()));
197 log
::debug
!("error occurred in soundcard input: {e:?}");
202 stream
.play().unwrap
();
203 input_stream
= Some(stream
);
205 SoundcardEvent
::CloseInput
=> {
206 let _
= input_stream
.take();
208 SoundcardEvent
::StartOutput
{ event_tx
, buffer
} => {
209 let mut output_configs
= device
.supported_output_configs().unwrap
();
210 // TODO: more error handling
211 let output_config
= output_configs
212 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
214 .with_sample_rate(SampleRate(48000));
216 .build_output_stream(
217 &output_config
.into
(),
218 move |data
: &mut [i16], info
: &cpal
::OutputCallbackInfo
| {
220 let ts
= info
.timestamp();
223 .duration_since(&ts
.callback
)
224 .unwrap
_or
(Duration
::ZERO
);
225 let mut buffer
= buffer
.write().unwrap
();
226 buffer
.latency
= latency
;
227 for out
in data
.iter
_m
ut
() {
228 if let Some(s
) = buffer
.samples
.pop_front() {
229 *out
= if tx_inverted
{ s
.saturating_neg() } else { s
};
231 } else if buffer
.idl
ing
{
234 log
::debug
!("output soundcard had underrun");
235 let _
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
);
239 //debug!("latency is {} ms, taken {taken}", latency.as_millis());
240 let _
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer
{
242 timestamp
: Instant
::now(),
247 log
::debug
!("error occurred in soundcard output: {e:?}");
252 stream
.play().unwrap
();
253 output_stream
= Some(stream
);
255 SoundcardEvent
::CloseOutput
=> {
256 let _
= output_stream
.take();