split remaining tasks into own files. add triple buffer
This commit is contained in:
parent
05893b1f0e
commit
c93cdd7e1e
7 changed files with 278 additions and 120 deletions
73
src/audio_input.rs
Normal file
73
src/audio_input.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use bytemuck::bytes_of_mut;
|
||||||
|
use embassy_time::Duration;
|
||||||
|
use esp_idf_svc::hal::{
|
||||||
|
gpio::{AnyIOPin, Gpio4, Gpio5, Gpio6},
|
||||||
|
i2s::{self, I2sRx, I2S0},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{audio_process::AudioBuffer, config::AUDIO_SAMPLES_PER_BUF};
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn mic_input_task(i2s_per: I2S0, sd: Gpio5, sclk: Gpio4, ws: Gpio6) {
|
||||||
|
let std_cfg = i2s::config::StdConfig::new(
|
||||||
|
i2s::config::Config::new().role(i2s::config::Role::Controller),
|
||||||
|
i2s::config::StdClkConfig::new(
|
||||||
|
24000,
|
||||||
|
i2s::config::ClockSource::default(),
|
||||||
|
i2s::config::MclkMultiple::M256,
|
||||||
|
),
|
||||||
|
i2s::config::StdSlotConfig::msb_slot_default(
|
||||||
|
i2s::config::DataBitWidth::Bits32,
|
||||||
|
i2s::config::SlotMode::Stereo,
|
||||||
|
)
|
||||||
|
.data_bit_width(i2s::config::DataBitWidth::Bits32)
|
||||||
|
.slot_bit_width(i2s::config::SlotBitWidth::Bits32)
|
||||||
|
.slot_mode_mask(
|
||||||
|
i2s::config::SlotMode::Stereo,
|
||||||
|
i2s::config::StdSlotMask::Both,
|
||||||
|
)
|
||||||
|
.ws_width(32)
|
||||||
|
.ws_polarity(false)
|
||||||
|
.bit_shift(true)
|
||||||
|
.left_align(true)
|
||||||
|
.big_endian(false)
|
||||||
|
.bit_order_lsb(false),
|
||||||
|
i2s::config::StdGpioConfig::new(false, false, false),
|
||||||
|
);
|
||||||
|
let mut mic_drv =
|
||||||
|
i2s::I2sDriver::new_std_rx(i2s_per, &std_cfg, sclk, sd, AnyIOPin::none(), ws).unwrap();
|
||||||
|
|
||||||
|
mic_drv.rx_enable().expect("rx not enabled");
|
||||||
|
|
||||||
|
async fn ignore_mic_startup(mic: &mut i2s::I2sDriver<'_, I2sRx>) {
|
||||||
|
let mut tmp_buf: [u8; 128] = [0; 128];
|
||||||
|
loop {
|
||||||
|
mic.read_async(tmp_buf.as_mut_slice()).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
embassy_time::with_timeout(Duration::from_millis(100), ignore_mic_startup(&mut mic_drv))
|
||||||
|
.await
|
||||||
|
.expect_err("ignore died early");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let audio_in_buf: &mut AudioBuffer = audio_sender.send().await;
|
||||||
|
|
||||||
|
let buffer = bytes_of_mut(audio_in_buf);
|
||||||
|
let mut total_bytes_read: usize = 0;
|
||||||
|
let mut remaining_bytes = AUDIO_SAMPLES_PER_BUF * 4;
|
||||||
|
|
||||||
|
while total_bytes_read < AUDIO_SAMPLES_PER_BUF * 4 {
|
||||||
|
let chunk = &mut buffer[total_bytes_read..total_bytes_read + remaining_bytes];
|
||||||
|
let num_bytes_read = mic_drv.read_async(chunk).await.unwrap();
|
||||||
|
total_bytes_read += num_bytes_read;
|
||||||
|
remaining_bytes -= num_bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
if total_bytes_read != AUDIO_SAMPLES_PER_BUF * 4 {
|
||||||
|
log::warn!("buffer underflow: {}", total_bytes_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_sender.send_done();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ use embassy_sync::zerocopy_channel::Sender;
|
||||||
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, zerocopy_channel::Receiver};
|
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, zerocopy_channel::Receiver};
|
||||||
use embassy_time::{Duration, Ticker};
|
use embassy_time::{Duration, Ticker};
|
||||||
|
|
||||||
use crate::audio::{AudioStats, DspBuffer};
|
use crate::audio_process::{AudioStats, DspBuffer};
|
||||||
use crate::effects::led_effect::{LedColors, LedData, LedEffect, Rgbv};
|
use crate::effects::led_effect::{LedColors, LedData, LedEffect, Rgbv};
|
||||||
use crate::helpers::random_at_most;
|
use crate::helpers::random_at_most;
|
||||||
use crate::LED_COUNT;
|
use crate::LED_COUNT;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use embassy_sync::{
|
||||||
};
|
};
|
||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
|
|
||||||
use crate::{audio::DspBuffer, config::LED_COUNT, AudioStats};
|
use crate::{audio_process::DspBuffer, config::LED_COUNT, AudioStats};
|
||||||
|
|
||||||
pub trait LedEffect {
|
pub trait LedEffect {
|
||||||
fn render(&mut self, fft: &DspBuffer, stats: &AudioStats, leds: &mut LedColors) -> Duration;
|
fn render(&mut self, fft: &DspBuffer, stats: &AudioStats, leds: &mut LedColors) -> Duration;
|
||||||
|
|
37
src/led_output.rs
Normal file
37
src/led_output.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, zerocopy_channel::Receiver};
|
||||||
|
use esp_idf_svc::hal::{gpio::{Gpio11, Gpio12}, spi::SPI2};
|
||||||
|
|
||||||
|
use crate::effects::led_effect::LedData;
|
||||||
|
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn output_leds(
|
||||||
|
spi_per: SPI2,
|
||||||
|
sdo: Gpio11,
|
||||||
|
sck: Gpio12,
|
||||||
|
mut receiver: Receiver<'static, NoopRawMutex, LedData>,
|
||||||
|
) {
|
||||||
|
let mut led_drv = spi::SpiDeviceDriver::new_single(
|
||||||
|
spi_per,
|
||||||
|
sck,
|
||||||
|
sdo,
|
||||||
|
AnyIOPin::none(),
|
||||||
|
AnyIOPin::none(),
|
||||||
|
&spi::config::DriverConfig::new(),
|
||||||
|
&spi::config::Config::new()
|
||||||
|
.baudrate(1.MHz().into())
|
||||||
|
.data_mode(spi::config::MODE_3),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let led_data = receiver.receive().await;
|
||||||
|
|
||||||
|
let output_buffer = bytes_of(led_data);
|
||||||
|
led_drv
|
||||||
|
.write_async(output_buffer)
|
||||||
|
.await
|
||||||
|
.expect("spi write failed");
|
||||||
|
receiver.receive_done();
|
||||||
|
}
|
||||||
|
}
|
135
src/main.rs
135
src/main.rs
|
@ -1,21 +1,16 @@
|
||||||
pub mod audio;
|
pub mod audio_input;
|
||||||
|
pub mod audio_process;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod effects;
|
pub mod effects;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
|
pub mod led_output;
|
||||||
|
pub mod triple_buffer;
|
||||||
|
|
||||||
use embassy_time::Duration;
|
|
||||||
use esp_idf_svc::{
|
use esp_idf_svc::{
|
||||||
hal::{
|
hal::peripherals::Peripherals,
|
||||||
gpio::{AnyIOPin, Gpio11, Gpio12},
|
|
||||||
i2s::{self, I2sRx},
|
|
||||||
peripherals::Peripherals,
|
|
||||||
spi::{self, SPI2},
|
|
||||||
units::FromValueType,
|
|
||||||
},
|
|
||||||
sys::{esp_dsp, esp_nofail},
|
sys::{esp_dsp, esp_nofail},
|
||||||
};
|
};
|
||||||
|
|
||||||
use bytemuck::{bytes_of, bytes_of_mut};
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_sync::{
|
use embassy_sync::{
|
||||||
blocking_mutex::raw::NoopRawMutex,
|
blocking_mutex::raw::NoopRawMutex,
|
||||||
|
@ -23,12 +18,14 @@ use embassy_sync::{
|
||||||
};
|
};
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
use audio::{process_audio, AudioBuffer, AudioStats, DspBuffer};
|
use audio_input::mic_input_task;
|
||||||
|
use audio_process::{process_audio, AudioBuffer, AudioStats, DspBuffer};
|
||||||
use config::{AUDIO_BUFFERS, AUDIO_SAMPLES_PER_BUF, LED_COUNT};
|
use config::{AUDIO_BUFFERS, AUDIO_SAMPLES_PER_BUF, LED_COUNT};
|
||||||
use effects::{
|
use effects::{
|
||||||
bass_sparks::LedEffectBassSparks,
|
bass_sparks::LedEffectBassSparks,
|
||||||
led_effect::{LedData, LedEffect},
|
led_effect::{LedData, LedEffect},
|
||||||
};
|
};
|
||||||
|
use led_output::output_leds;
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn effect_task(
|
async fn effect_task(
|
||||||
|
@ -39,38 +36,6 @@ async fn effect_task(
|
||||||
led_effect.process_led_effect(input, output).await;
|
led_effect.process_led_effect(input, output).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn output_leds(
|
|
||||||
spi_per: SPI2,
|
|
||||||
sdo: Gpio11,
|
|
||||||
sck: Gpio12,
|
|
||||||
mut receiver: Receiver<'static, NoopRawMutex, LedData>,
|
|
||||||
) {
|
|
||||||
let mut led_drv = spi::SpiDeviceDriver::new_single(
|
|
||||||
spi_per,
|
|
||||||
sck,
|
|
||||||
sdo,
|
|
||||||
AnyIOPin::none(),
|
|
||||||
AnyIOPin::none(),
|
|
||||||
&spi::config::DriverConfig::new(),
|
|
||||||
&spi::config::Config::new()
|
|
||||||
.baudrate(1.MHz().into())
|
|
||||||
.data_mode(spi::config::MODE_3),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let led_data = receiver.receive().await;
|
|
||||||
|
|
||||||
let output_buffer = bytes_of(led_data);
|
|
||||||
led_drv
|
|
||||||
.write_async(output_buffer)
|
|
||||||
.await
|
|
||||||
.expect("spi write failed");
|
|
||||||
receiver.receive_done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
// It is necessary to call this function once. Otherwise some patches to the runtime
|
// It is necessary to call this function once. Otherwise some patches to the runtime
|
||||||
|
@ -111,47 +76,6 @@ async fn main(spawner: Spawner) {
|
||||||
let led_channel = LED_CHANNEL.init(Channel::new(leds));
|
let led_channel = LED_CHANNEL.init(Channel::new(leds));
|
||||||
let (led_sender, led_receiver) = led_channel.split();
|
let (led_sender, led_receiver) = led_channel.split();
|
||||||
|
|
||||||
// i2s config
|
|
||||||
let mic_i2s_per = peripherals.i2s0;
|
|
||||||
let mic_i2s_sd = peripherals.pins.gpio5;
|
|
||||||
let mic_i2s_sclk = peripherals.pins.gpio4;
|
|
||||||
let mic_i2s_ws = peripherals.pins.gpio6;
|
|
||||||
|
|
||||||
let mic_i2s_std_cfg = i2s::config::StdConfig::new(
|
|
||||||
i2s::config::Config::new().role(i2s::config::Role::Controller),
|
|
||||||
i2s::config::StdClkConfig::new(
|
|
||||||
24000,
|
|
||||||
i2s::config::ClockSource::default(),
|
|
||||||
i2s::config::MclkMultiple::M256,
|
|
||||||
),
|
|
||||||
i2s::config::StdSlotConfig::msb_slot_default(
|
|
||||||
i2s::config::DataBitWidth::Bits32,
|
|
||||||
i2s::config::SlotMode::Stereo,
|
|
||||||
)
|
|
||||||
.data_bit_width(i2s::config::DataBitWidth::Bits32)
|
|
||||||
.slot_bit_width(i2s::config::SlotBitWidth::Bits32)
|
|
||||||
.slot_mode_mask(
|
|
||||||
i2s::config::SlotMode::Stereo,
|
|
||||||
i2s::config::StdSlotMask::Both,
|
|
||||||
)
|
|
||||||
.ws_width(32)
|
|
||||||
.ws_polarity(false)
|
|
||||||
.bit_shift(true)
|
|
||||||
.left_align(true)
|
|
||||||
.big_endian(false)
|
|
||||||
.bit_order_lsb(false),
|
|
||||||
i2s::config::StdGpioConfig::new(false, false, false),
|
|
||||||
);
|
|
||||||
let mut mic_drv = i2s::I2sDriver::new_std_rx(
|
|
||||||
mic_i2s_per,
|
|
||||||
&mic_i2s_std_cfg,
|
|
||||||
mic_i2s_sclk,
|
|
||||||
mic_i2s_sd,
|
|
||||||
AnyIOPin::none(),
|
|
||||||
mic_i2s_ws,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
esp_nofail!(esp_dsp::dsps_fft2r_init_fc32(
|
esp_nofail!(esp_dsp::dsps_fft2r_init_fc32(
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
|
@ -163,6 +87,15 @@ async fn main(spawner: Spawner) {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spawner
|
||||||
|
.spawn(mic_input_task(
|
||||||
|
peripherals.i2s0,
|
||||||
|
peripherals.pins.gpio5,
|
||||||
|
peripherals.pins.gpio4,
|
||||||
|
peripherals.pins.gpio6,
|
||||||
|
))
|
||||||
|
.expect("spawn failed");
|
||||||
|
|
||||||
spawner
|
spawner
|
||||||
.spawn(process_audio(audio_receiver, fft_sender))
|
.spawn(process_audio(audio_receiver, fft_sender))
|
||||||
.expect("spawn failed");
|
.expect("spawn failed");
|
||||||
|
@ -183,38 +116,4 @@ async fn main(spawner: Spawner) {
|
||||||
led_receiver,
|
led_receiver,
|
||||||
))
|
))
|
||||||
.expect("spawn failed");
|
.expect("spawn failed");
|
||||||
|
|
||||||
mic_drv.rx_enable().expect("rx not enabled");
|
|
||||||
|
|
||||||
async fn ignore_mic_startup(mic: &mut i2s::I2sDriver<'_, I2sRx>) {
|
|
||||||
let mut tmp_buf: [u8; 128] = [0; 128];
|
|
||||||
loop {
|
|
||||||
mic.read_async(tmp_buf.as_mut_slice()).await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
embassy_time::with_timeout(Duration::from_millis(100), ignore_mic_startup(&mut mic_drv))
|
|
||||||
.await
|
|
||||||
.expect_err("ignore died early");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let audio_in_buf: &mut AudioBuffer = audio_sender.send().await;
|
|
||||||
|
|
||||||
let buffer = bytes_of_mut(audio_in_buf);
|
|
||||||
let mut total_bytes_read: usize = 0;
|
|
||||||
let mut remaining_bytes = AUDIO_SAMPLES_PER_BUF * 4;
|
|
||||||
|
|
||||||
while total_bytes_read < AUDIO_SAMPLES_PER_BUF * 4 {
|
|
||||||
let chunk = &mut buffer[total_bytes_read..total_bytes_read + remaining_bytes];
|
|
||||||
let num_bytes_read = mic_drv.read_async(chunk).await.unwrap();
|
|
||||||
total_bytes_read += num_bytes_read;
|
|
||||||
remaining_bytes -= num_bytes_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
if total_bytes_read != AUDIO_SAMPLES_PER_BUF * 4 {
|
|
||||||
log::warn!("buffer underflow: {}", total_bytes_read);
|
|
||||||
}
|
|
||||||
|
|
||||||
audio_sender.send_done();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
149
src/triple_buffer.rs
Normal file
149
src/triple_buffer.rs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use core::future::{poll_fn, Future};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_sync::blocking_mutex::{raw::RawMutex, Mutex};
|
||||||
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
|
|
||||||
|
/// Triple-buffered zero-copy Channel.
|
||||||
|
///
|
||||||
|
/// Sender will flip the input buffer when done, which will in turn
|
||||||
|
/// flip the output buffer at the next Receiver call.
|
||||||
|
pub struct TripleBuffer<'a, M: RawMutex, T> {
|
||||||
|
buf: *mut T,
|
||||||
|
phantom: PhantomData<&'a mut T>,
|
||||||
|
state: Mutex<M, RefCell<State>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, M: RawMutex, T> TripleBuffer<'a, M, T> {
|
||||||
|
pub fn new(buffers: &'a mut [T; 3]) -> Self {
|
||||||
|
Self {
|
||||||
|
buf: buffers.as_mut_ptr(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
state: Mutex::new(RefCell::new(State {
|
||||||
|
input: 0,
|
||||||
|
internal: 1,
|
||||||
|
output: None,
|
||||||
|
new_data: false,
|
||||||
|
send_waker: WakerRegistration::new(),
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Sender`] and [`Receiver`] from an existing buffer.
|
||||||
|
///
|
||||||
|
// / Further Senders and Receivers can be created through [`Sender::borrow`] and
|
||||||
|
// / [`Receiver::borrow`] respectively.
|
||||||
|
pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {
|
||||||
|
(Sender { buffer: self }, Receiver { buffer: self })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send-only access to a [`TripleBuffer`].
|
||||||
|
pub struct Sender<'a, M: RawMutex, T> {
|
||||||
|
buffer: &'a TripleBuffer<'a, M, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, M: RawMutex, T> Sender<'a, M, T> {
|
||||||
|
/// Asynchronously send a value into the buffer.
|
||||||
|
pub fn send(&mut self) -> impl Future<Output = &mut T> {
|
||||||
|
poll_fn(|_| {
|
||||||
|
self.buffer.state.lock(|s| {
|
||||||
|
let s = &mut *s.borrow_mut();
|
||||||
|
let r = unsafe { &mut *self.buffer.buf.add(s.input) };
|
||||||
|
Poll::Ready(r)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify the buffer that the sending of the value has been finalized.
|
||||||
|
pub fn send_done(&mut self) {
|
||||||
|
self.buffer.state.lock(|s| s.borrow_mut().flip_input());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive-only access to a [`Channel`].
|
||||||
|
pub struct Receiver<'a, M: RawMutex, T> {
|
||||||
|
buffer: &'a TripleBuffer<'a, M, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, M: RawMutex, T> Receiver<'a, M, T> {
|
||||||
|
/// Checks if the buffer has data available
|
||||||
|
pub fn is_ready(&self) -> bool {
|
||||||
|
self.buffer.state.lock(|s| s.borrow_mut().output.is_some())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously receive, wait for a new value.
|
||||||
|
pub fn receive_new(&mut self) -> impl Future<Output = &mut T> {
|
||||||
|
poll_fn(|cx| {
|
||||||
|
self.buffer.state.lock(|s| {
|
||||||
|
let s = &mut *s.borrow_mut();
|
||||||
|
|
||||||
|
if s.output.is_some() && s.new_data {
|
||||||
|
s.flip_output();
|
||||||
|
let r = unsafe { &mut *self.buffer.buf.add(s.output.unwrap()) };
|
||||||
|
Poll::Ready(r)
|
||||||
|
} else {
|
||||||
|
s.send_waker.register(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously receive, return with old data if possible.
|
||||||
|
pub fn receive_cached(&mut self) -> impl Future<Output = (&mut T, bool)> {
|
||||||
|
poll_fn(|cx| {
|
||||||
|
self.buffer.state.lock(|s| {
|
||||||
|
let s = &mut *s.borrow_mut();
|
||||||
|
|
||||||
|
if s.output.is_some() {
|
||||||
|
let had_new_data = s.new_data;
|
||||||
|
if s.new_data {
|
||||||
|
s.flip_output();
|
||||||
|
}
|
||||||
|
let r = unsafe { &mut *self.buffer.buf.add(s.output.unwrap()) };
|
||||||
|
Poll::Ready((r, had_new_data))
|
||||||
|
} else {
|
||||||
|
s.send_waker.register(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
input: usize,
|
||||||
|
internal: usize,
|
||||||
|
output: Option<usize>,
|
||||||
|
new_data: bool,
|
||||||
|
send_waker: WakerRegistration, // notifies when data was flipped from input to internal
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn flip_input(&mut self) {
|
||||||
|
(self.input, self.internal) = (self.internal, self.input);
|
||||||
|
self.new_data = true;
|
||||||
|
if self.output.is_none() {
|
||||||
|
self.output = match (self.input, self.internal) {
|
||||||
|
(0, 1) => Some(2),
|
||||||
|
(1, 1) => Some(2),
|
||||||
|
(0, 2) => Some(1),
|
||||||
|
(2, 0) => Some(1),
|
||||||
|
(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.send_waker.wake();
|
||||||
|
}
|
||||||
|
fn flip_output(&mut self) {
|
||||||
|
match self.output {
|
||||||
|
Some(o) => {
|
||||||
|
(self.output, self.internal) = (Some(self.internal), o);
|
||||||
|
self.new_data = false;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue