1use crate::gamepad::GamepadManager;
2use crate::*;
3use alloc::boxed::Box;
4use core::cell::Cell;
5use core::fmt::Display;
6use core::marker::PhantomData;
7use core::net::{IpAddr, Ipv4Addr, SocketAddr};
8use std::io::{Read, Write};
9use std::net::{SocketAddrV4, TcpListener, TcpStream, UdpSocket};
10use std::path::PathBuf;
11use std::sync::mpsc;
12
13const UDP_PORT_MIN: u16 = 3110;
14const UDP_PORT_MAX: u16 = 3117;
15const TCP_PORT_MIN: u16 = 3210;
16const TCP_PORT_MAX: u16 = 3217;
17const AUDIO_BUF_SIZE: usize = SAMPLE_RATE as usize / 12;
18
19static NAMES: &[&str] = &[
20 "j0vial-dharm4",
21 "angry-6r4d13y",
22 "nic3-5ugar",
23 "quirky-5co06ie",
24 "r31ax3d-s0phie",
25 "sh4rp-7oni",
26 "boring-5ky",
27 "m4gica1-5i3nn4",
28 "b0ring-che57er",
29 "shy-a5tr0",
30];
31
32#[derive(Clone)]
33pub struct DeviceConfig {
34 pub root: PathBuf,
36
37 pub tcp_ip: IpAddr,
39
40 pub udp_ip: IpAddr,
42
43 pub peers: Vec<IpAddr>,
45
46 pub wav: Option<PathBuf>,
48}
49
50impl Default for DeviceConfig {
51 fn default() -> Self {
52 let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST);
53 Self {
54 root: PathBuf::new(),
55 tcp_ip: localhost,
56 udp_ip: localhost,
57 peers: vec![localhost],
58 wav: None,
59 }
60 }
61}
62
63pub struct DeviceImpl<'a> {
64 config: DeviceConfig,
65 start: std::time::Instant,
67 gamepad: GamepadManager,
69 audio: Option<AudioWriter>,
71 wifi_status: u8,
72 network: NetworkImpl<'a>,
73 serial: SerialImpl,
74 tcp_conn: Option<TcpStream>,
75}
76
77impl<'a> DeviceImpl<'a> {
78 pub fn new(config: DeviceConfig) -> Self {
79 #[cfg(not(target_os = "android"))]
80 let audio = start_audio(&config);
81 #[cfg(target_os = "android")]
82 let audio = None;
83 Self {
84 start: std::time::Instant::now(),
85 gamepad: GamepadManager::new(),
86 audio,
87 config,
88 wifi_status: 2,
89 network: NetworkImpl::new(),
90 serial: SerialImpl::new(),
91 tcp_conn: None,
92 }
93 }
94
95 pub fn update_input(&mut self, input: InputState) {
97 self.gamepad.update_input(input)
98 }
99
100 pub fn alloc_psram(&self, size: usize) -> Vec<u8> {
101 Vec::with_capacity(size)
102 }
103}
104
105impl<'a> Device for DeviceImpl<'a> {
106 type Dir = DirImpl;
107
108 fn now(&self) -> Instant {
109 let now = std::time::Instant::now();
110 let dur = now.duration_since(self.start);
111 Instant {
112 us: dur.as_micros() as u32,
113 }
114 }
115
116 fn delay(&self, d: Duration) {
117 let dur = core::time::Duration::from_micros(d.us as u64);
118 std::thread::sleep(dur);
119 }
120
121 fn read_input(&mut self) -> Option<InputState> {
122 self.gamepad.read_input()
123 }
124
125 fn get_name(&mut self) -> Option<&'static str> {
126 let addr = self.network.local_addr?;
127 let IpAddr::V4(ip) = addr.ip() else {
128 return None;
129 };
130 let port = addr.port();
131 if port == UDP_PORT_MIN {
132 return None;
133 }
134 let hash = (ip.to_bits() + u32::from(port)) % 10;
135 let name = NAMES[hash as usize];
136 Some(name)
137 }
138
139 fn log_debug<D: Display>(&mut self, src: &str, msg: D) {
140 println!("DEBUG({src}): {msg}");
141 }
142
143 fn log_error<D: Display>(&mut self, src: &str, msg: D) {
144 eprintln!("ERROR({src}): {msg}");
145 }
146
147 fn random(&mut self) -> u32 {
148 rand::random()
149 }
150
151 fn open_dir(&mut self, path: &[&str]) -> Result<Self::Dir, FSError> {
152 let path: PathBuf = path.iter().collect();
153 let path = self.config.root.join(path);
154 if !path.exists() {
155 return Err(FSError::NotFound);
156 }
157 if !path.is_dir() {
158 return Err(FSError::OpenedFileAsDir);
159 }
160 Ok(DirImpl { path })
161 }
162
163 fn has_headphones(&mut self) -> bool {
164 false
165 }
166
167 fn get_audio_buffer(&mut self) -> &mut [i16] {
168 match &mut self.audio {
169 Some(audio) => audio.get_write_buf(),
170 None => &mut [][..],
171 }
172 }
173
174 #[cfg(target_os = "android")]
175 fn get_battery_status(&mut self) -> Option<BatteryStatus> {
176 None
177 }
178
179 #[cfg(not(target_os = "android"))]
180 fn get_battery_status(&mut self) -> Option<BatteryStatus> {
181 Some(BatteryStatus {
182 voltage: 40_000,
183 connected: false,
184 full: false,
185 })
186 }
187}
188
189pub struct DirImpl {
190 path: PathBuf,
191}
192
193impl Dir for DirImpl {
194 type Read = File;
195 type Write = File;
196
197 fn open_file(&mut self, name: &str) -> Result<Self::Read, FSError> {
198 let path = self.path.join(name);
199 let file = std::fs::File::open(path)?;
200 Ok(File { file })
201 }
202
203 fn create_file(&mut self, name: &str) -> Result<Self::Write, FSError> {
204 let path = self.path.join(name);
205 if let Some(parent) = path.parent() {
206 _ = std::fs::create_dir_all(parent);
207 }
208 let file = std::fs::File::create(path)?;
209 Ok(File { file })
210 }
211
212 fn append_file(&mut self, name: &str) -> Result<Self::Write, FSError> {
213 let path = self.path.join(name);
214 let mut opts = std::fs::OpenOptions::new();
215 let file = opts.append(true).open(path)?;
216 Ok(File { file })
217 }
218
219 fn get_file_size(&mut self, name: &str) -> Result<u32, FSError> {
220 let path = self.path.join(name);
221 let meta = std::fs::metadata(path)?;
222 Ok(meta.len() as u32)
223 }
224
225 fn remove_file(&mut self, name: &str) -> Result<(), FSError> {
226 let path = self.path.join(name);
227 let res = std::fs::remove_file(path);
228 match res {
229 Ok(_) => Ok(()),
230 Err(err) => match err.kind() {
231 std::io::ErrorKind::NotFound => Ok(()),
232 _ => Err(err.into()),
233 },
234 }
235 }
236
237 fn create_dir(&mut self, name: &str) -> Result<(), FSError> {
238 let path = self.path.join(name);
239 std::fs::create_dir(path)?;
240 Ok(())
241 }
242
243 fn remove_dir(self) -> Result<(), FSError> {
244 let res = std::fs::remove_dir_all(&self.path);
245 match res {
246 Ok(_) => Ok(()),
247 Err(err) => match err.kind() {
248 std::io::ErrorKind::NotFound => Ok(()),
249 _ => Err(err.into()),
250 },
251 }
252 }
253
254 fn iter_dir<F>(&mut self, mut f: F) -> Result<(), FSError>
255 where
256 F: FnMut(EntryKind, &[u8]),
257 {
258 let entries = std::fs::read_dir(&self.path)?;
259 for entry in entries {
260 let entry = entry?;
261 let path = entry.path();
262 let kind = if path.is_dir() {
263 EntryKind::Dir
264 } else if path.is_file() {
265 EntryKind::File
266 } else {
267 continue;
268 };
269 let fname = entry.file_name();
270 let fname = fname.as_encoded_bytes();
271 f(kind, fname);
272 }
273 Ok(())
274 }
275}
276
277pub struct File {
278 file: std::fs::File,
279}
280
281impl embedded_io::ErrorType for File {
282 type Error = std::io::Error;
283}
284
285impl embedded_io::Read for File {
286 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
287 std::io::Read::read(&mut self.file, buf)
288 }
289}
290
291impl embedded_io::Seek for File {
292 fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result<u64, Self::Error> {
293 let pos = match pos {
294 embedded_io::SeekFrom::Start(n) => std::io::SeekFrom::Start(n),
295 embedded_io::SeekFrom::End(n) => std::io::SeekFrom::End(n),
296 embedded_io::SeekFrom::Current(n) => std::io::SeekFrom::Current(n),
297 };
298 std::io::Seek::seek(&mut self.file, pos)
299 }
300}
301
302impl embedded_io::Write for File {
303 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
304 std::io::Write::write(&mut self.file, buf)
305 }
306
307 fn flush(&mut self) -> Result<(), Self::Error> {
308 std::io::Write::flush(&mut self.file)
309 }
310}
311
312pub struct NetworkImpl<'a> {
313 worker: Cell<Option<UdpWorker>>,
314 r_in: mpsc::Receiver<NetMessage>,
315 s_out: mpsc::Sender<NetMessage>,
316 s_stop: mpsc::Sender<()>,
317 local_addr: Option<SocketAddr>,
318 _life: &'a PhantomData<()>,
319}
320
321impl<'a> NetworkImpl<'a> {
322 fn new() -> Self {
323 let (s_in, r_in) = mpsc::channel();
324 let (s_out, r_out) = mpsc::channel();
325 let (s_stop, r_stop) = mpsc::channel();
326 let worker = Cell::new(Some(UdpWorker {
327 s_in,
328 r_out,
329 r_stop,
330 }));
331 Self {
332 worker,
333 r_in,
334 s_out,
335 s_stop,
336 local_addr: None,
337 _life: &PhantomData,
338 }
339 }
340}
341
342pub type Addr = SocketAddr;
343
344impl<'a> Network for DeviceImpl<'a> {
345 type Addr = SocketAddr;
346
347 fn net_local_addr(&self) -> SocketAddr {
348 self.network.local_addr.unwrap()
349 }
350
351 fn net_start(&mut self) -> NetworkResult<()> {
352 let worker = self.network.worker.replace(None);
353 let Some(worker) = worker else {
354 return Ok(());
355 };
356 let local_addr = worker.start(self.config.udp_ip)?;
357 self.network.local_addr = Some(local_addr);
358 Ok(())
359 }
360
361 fn net_stop(&mut self) -> NetworkResult<()> {
362 _ = self.network.s_stop.send(());
363 self.network = NetworkImpl::new();
364 Ok(())
365 }
366
367 fn net_advertise(&mut self) -> NetworkResult<()> {
368 let hello = b"HELLO".to_owned();
369 let hello: Box<[u8]> = Box::new(hello);
370 for ip in &self.config.peers {
371 for port in UDP_PORT_MIN..=UDP_PORT_MAX {
372 let addr = SocketAddr::new(*ip, port);
373 let res = self.network.s_out.send((addr, hello.clone()));
374 if res.is_err() {
375 return Err(NetworkError::NetThreadDeallocated);
376 }
377 }
378 }
379 Ok(())
380 }
381
382 fn net_recv(&mut self) -> NetworkResult<Option<(Self::Addr, Box<[u8]>)>> {
383 Ok(self.network.r_in.try_recv().ok())
384 }
385
386 fn net_send(&mut self, addr: Self::Addr, data: &[u8]) -> NetworkResult<()> {
387 if data.len() >= 200 {
388 return Err(NetworkError::OutMessageTooBig);
389 };
390 let msg = data.to_vec().into_boxed_slice();
391 let res = self.network.s_out.send((addr, msg));
392 if res.is_err() {
393 return Err(NetworkError::NetThreadDeallocated);
394 }
395 Ok(())
396 }
397
398 fn net_send_status(&mut self, _: Self::Addr) -> NetworkResult<firefly_types::spi::SendStatus> {
399 Ok(firefly_types::spi::SendStatus::Empty)
400 }
401}
402
403pub struct SerialImpl {
404 worker: Cell<Option<TcpWorker>>,
405 r_in: mpsc::Receiver<SerialMessage>,
406 s_out: mpsc::Sender<SerialMessage>,
407 s_stop: mpsc::Sender<()>,
408}
409
410impl SerialImpl {
411 fn new() -> Self {
412 let (s_in, r_in) = mpsc::channel();
413 let (s_out, r_out) = mpsc::channel();
414 let (s_stop, r_stop) = mpsc::channel();
415 let worker = TcpWorker {
416 s_in,
417 r_out,
418 r_stop,
419 };
420 let worker = Cell::new(Some(worker));
421 Self {
422 worker,
423 r_in,
424 s_out,
425 s_stop,
426 }
427 }
428}
429
430impl Serial for DeviceImpl<'_> {
431 fn serial_start(&mut self) -> NetworkResult<()> {
432 let worker = self.serial.worker.replace(None);
433 let Some(worker) = worker else {
434 return Ok(());
435 };
436 worker.start(self.config.tcp_ip)?;
437 Ok(())
438 }
439
440 fn serial_stop(&mut self) -> NetworkResult<()> {
441 _ = self.serial.s_stop.send(());
442 Ok(())
443 }
444
445 fn serial_recv(&mut self) -> NetworkResult<Option<Box<[u8]>>> {
446 Ok(self.serial.r_in.try_recv().ok())
447 }
448
449 fn serial_send(&mut self, data: &[u8]) -> NetworkResult<()> {
450 if data.len() >= 200 {
451 return Err(NetworkError::OutMessageTooBig);
452 };
453 let msg = data.to_vec().into_boxed_slice();
454 let res = self.serial.s_out.send(msg);
455 if res.is_err() {
456 return Err(NetworkError::NetThreadDeallocated);
457 }
458 Ok(())
459 }
460}
461
462impl Wifi for DeviceImpl<'_> {
463 fn wifi_scan(&mut self) -> NetworkResult<[String; 6]> {
464 let points = ["Default Network", "", "", "", "", ""];
465 let points = points.map(|s| s.to_string());
466 Ok(points)
467 }
468
469 fn wifi_connect(&mut self, ssid: &str, pass: &str) -> NetworkResult<()> {
470 if ssid != "Default Network" {
471 self.wifi_status = 2; } else if pass == "invalid" {
473 self.wifi_status = 1; } else {
475 self.wifi_status = 3; }
477 Ok(())
478 }
479
480 fn wifi_status(&mut self) -> NetworkResult<u8> {
481 if self.wifi_status == 3 {
482 self.wifi_status = 4; Ok(3)
484 } else {
485 Ok(self.wifi_status)
486 }
487 }
488
489 fn wifi_disconnect(&mut self) -> NetworkResult<()> {
490 self.wifi_status = 2;
491 Ok(())
492 }
493
494 fn tcp_connect(&mut self, ip: u32, port: u16) -> NetworkResult<()> {
495 if self.wifi_status != 4 {
496 return Err(NetworkError::Error("not connected to wifi"));
497 }
498 let ip = Ipv4Addr::new(
499 (ip >> 24) as u8,
500 (ip >> 16) as u8,
501 (ip >> 8) as u8,
502 ip as u8,
503 );
504 let addr = SocketAddrV4::new(ip, port);
505 let Ok(stream) = TcpStream::connect(addr) else {
506 return Err(NetworkError::CannotBind);
507 };
508 _ = stream.set_nonblocking(true);
509 self.tcp_conn = Some(stream);
510 Ok(())
511 }
512
513 fn tcp_status(&mut self) -> NetworkResult<u8> {
514 let Some(stream) = &mut self.tcp_conn else {
515 return Ok(1);
516 };
517 if stream.peer_addr().is_err() {
518 return Ok(1);
519 }
520 Ok(5)
521 }
522
523 fn tcp_send(&mut self, data: &[u8]) -> NetworkResult<()> {
524 let Some(stream) = &mut self.tcp_conn else {
525 return Err(NetworkError::NotInitialized);
526 };
527 let res = stream.write_all(data);
528 if let Err(err) = res {
529 let err = alloc::format!("{err}");
530 return Err(NetworkError::OwnedError(err));
531 }
532 Ok(())
533 }
534
535 fn tcp_recv(&mut self) -> NetworkResult<Box<[u8]>> {
536 let Some(stream) = &mut self.tcp_conn else {
537 return Err(NetworkError::NotInitialized);
538 };
539 let mut buf = vec![0; 80];
540 let n = match stream.read(&mut buf) {
541 Ok(n) => n,
542 Err(err) => {
543 if err.kind() == std::io::ErrorKind::WouldBlock {
544 0
545 } else {
546 return Err(NetworkError::Error("failed to read incoming TCP data"));
547 }
548 }
549 };
550 buf.truncate(n);
551 Ok(buf.into_boxed_slice())
552 }
553
554 fn tcp_close(&mut self) -> NetworkResult<()> {
555 let Some(stream) = self.tcp_conn.take() else {
556 return Ok(());
557 };
558 _ = stream.shutdown(std::net::Shutdown::Both);
559 Ok(())
560 }
561}
562
563type NetMessage = (SocketAddr, Box<[u8]>);
564type SerialMessage = Box<[u8]>;
565
566struct UdpWorker {
567 s_in: mpsc::Sender<NetMessage>,
568 r_out: mpsc::Receiver<NetMessage>,
569 r_stop: mpsc::Receiver<()>,
570}
571
572impl UdpWorker {
573 fn start(self, ip: IpAddr) -> Result<SocketAddr, NetworkError> {
574 let addrs: Vec<_> = (UDP_PORT_MIN..=UDP_PORT_MAX)
575 .map(|port| SocketAddr::new(ip, port))
576 .collect();
577 let socket = match UdpSocket::bind(&addrs[..]) {
578 Ok(socket) => socket,
579 Err(_) => return Err(NetworkError::CannotBind),
580 };
581 let timeout = std::time::Duration::from_millis(10);
582 socket.set_read_timeout(Some(timeout)).unwrap();
583 if let Ok(addr) = socket.local_addr() {
584 println!("listening on {addr}/udp");
585 } else {
586 println!("listening a UDP port");
587 }
588 let local_addr = socket.local_addr().unwrap();
589 std::thread::spawn(move || {
590 loop {
591 match self.r_stop.try_recv() {
592 Ok(_) | Err(mpsc::TryRecvError::Disconnected) => {
593 break;
594 }
595 Err(mpsc::TryRecvError::Empty) => {}
596 }
597 let mut buf = vec![0; 64];
598 if let Ok((size, addr)) = socket.recv_from(&mut buf) {
599 if size == 0 {
600 continue;
601 }
602 buf.truncate(size);
603 let buf = buf.into_boxed_slice();
604 _ = self.s_in.send((addr, buf));
605 }
606 if let Ok((addr, buf)) = self.r_out.try_recv() {
607 if addr == local_addr {
608 continue;
609 }
610 _ = socket.send_to(&buf, addr);
611 }
612 }
613 });
614 Ok(local_addr)
615 }
616}
617
618struct TcpWorker {
619 s_in: mpsc::Sender<SerialMessage>,
620 r_out: mpsc::Receiver<SerialMessage>,
621 r_stop: mpsc::Receiver<()>,
622}
623
624impl TcpWorker {
625 fn start(self, ip: IpAddr) -> Result<(), NetworkError> {
626 let addrs: Vec<_> = (TCP_PORT_MIN..=TCP_PORT_MAX)
627 .map(|port| SocketAddr::new(ip, port))
628 .collect();
629 let Ok(socket) = TcpListener::bind(&addrs[..]) else {
630 return Err(NetworkError::CannotBind);
631 };
632 socket.set_nonblocking(true).unwrap();
633 std::thread::spawn(move || {
634 let mut streams = RingBuf::new();
635 loop {
636 match self.r_stop.try_recv() {
637 Ok(_) | Err(mpsc::TryRecvError::Disconnected) => {
638 break;
639 }
640 Err(mpsc::TryRecvError::Empty) => {}
641 }
642
643 if let Ok((stream, _addr)) = socket.accept() {
644 stream.set_nonblocking(true).unwrap();
645 streams.push(stream);
646 };
647
648 for stream in streams.iter_mut() {
649 let mut buf = vec![0; 200];
650 let Ok(size) = stream.read(&mut buf) else {
651 continue;
652 };
653 if size == 0 {
654 continue;
655 }
656 buf.truncate(size);
657 let buf = buf.into_boxed_slice();
658 _ = self.s_in.send(buf);
659 }
660 if let Ok(buf) = self.r_out.try_recv() {
661 for stream in streams.iter_mut() {
662 _ = stream.write_all(&buf)
663 }
664 }
665 }
666 });
667 Ok(())
668 }
669}
670
671struct RingBuf {
676 data: [Option<TcpStream>; 4],
677 next: usize,
678}
679
680impl RingBuf {
681 fn new() -> Self {
682 Self {
683 data: [None, None, None, None],
684 next: 0,
685 }
686 }
687
688 fn push(&mut self, val: TcpStream) {
689 self.data[self.next] = Some(val);
690 self.next = (self.next + 1) % 4
691 }
692
693 fn iter_mut(&mut self) -> impl Iterator<Item = &mut TcpStream> {
694 self.data.iter_mut().filter_map(Option::as_mut)
695 }
696}
697
698#[cfg(not(target_os = "android"))]
699fn start_audio(config: &DeviceConfig) -> Option<AudioWriter> {
700 let wav = if let Some(filename) = &config.wav {
701 let spec = hound::WavSpec {
702 channels: 2,
703 sample_rate: SAMPLE_RATE,
704 bits_per_sample: 16,
705 sample_format: hound::SampleFormat::Int,
706 };
707 let writer = hound::WavWriter::create(filename, spec).unwrap();
708 Some(writer)
709 } else {
710 None
711 };
712
713 let (send, recv) = mpsc::sync_channel(AUDIO_BUF_SIZE);
714 let Ok(mut stream) = rodio::OutputStreamBuilder::open_default_stream() else {
715 eprintln!("WARNING: audio device is not available, sound will be disabled");
716 return None;
717 };
718 stream.log_on_drop(false);
719 let mixer = stream.mixer();
720 let source = AudioReader { wav, recv };
721 mixer.add(source);
722 let audio = AudioWriter {
723 buf: [0; AUDIO_BUF_SIZE],
724 pending_from: 0,
725 pending_to: 0,
726 send,
727 _stream: stream,
728 };
729 Some(audio)
730}
731
732struct AudioWriter {
733 buf: [i16; AUDIO_BUF_SIZE],
734 send: mpsc::SyncSender<i16>,
735 pending_from: usize,
736 pending_to: usize,
737 #[cfg(not(target_os = "android"))]
738 _stream: rodio::OutputStream,
739}
740
741impl AudioWriter {
742 fn consume(&mut self) {
743 while self.pending_from > self.pending_to {
744 let byte = self.buf[self.pending_from];
745 let res = self.send.try_send(byte);
746 if res.is_err() {
747 return;
748 }
749 if self.pending_from < self.buf.len() {
750 self.pending_from += 1;
751 } else {
752 self.pending_from = 0;
753 }
754 }
755 while self.pending_from < self.pending_to {
756 let byte = self.buf[self.pending_from];
757 let res = self.send.try_send(byte);
758 if res.is_err() {
759 return;
760 }
761 self.pending_from += 1;
762 }
763 }
764
765 fn get_write_buf(&mut self) -> &mut [i16] {
766 self.consume();
767 if self.pending_from == self.pending_to {
768 self.pending_from = 0;
769 self.pending_to = self.buf.len();
770 return &mut self.buf;
771 }
772 if self.pending_from < self.pending_to {
773 let start = self.pending_to;
774 self.pending_to = self.buf.len();
775 return &mut self.buf[start..self.pending_to];
776 }
777 return &mut [];
778 }
779}
780
781#[cfg(not(target_os = "android"))]
782struct AudioReader {
783 wav: Option<hound::WavWriter<std::io::BufWriter<std::fs::File>>>,
784 recv: mpsc::Receiver<i16>,
785}
786
787#[cfg(not(target_os = "android"))]
788impl rodio::Source for AudioReader {
789 fn current_span_len(&self) -> Option<usize> {
790 None
791 }
792
793 fn channels(&self) -> u16 {
794 2
795 }
796
797 fn sample_rate(&self) -> u32 {
798 SAMPLE_RATE
799 }
800
801 fn total_duration(&self) -> Option<core::time::Duration> {
802 None
803 }
804}
805
806#[cfg(not(target_os = "android"))]
807impl Iterator for AudioReader {
808 type Item = f32;
809
810 fn next(&mut self) -> Option<Self::Item> {
811 let s = self.recv.recv().unwrap_or_default();
812 if let Some(wav) = self.wav.as_mut() {
813 wav.write_sample(s).unwrap()
814 }
815 let s = f32::from(s) / f32::from(i16::MAX);
816 Some(s)
817 }
818}