Fix buffer overruns

This commit is contained in:
Christian Perez Llamas 2022-11-17 00:19:22 +01:00
parent 4fe834db2f
commit 1ed260b105
2 changed files with 294 additions and 221 deletions

View File

@ -3,6 +3,7 @@
//! I2S //! I2S
use core::future::poll_fn; use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll; use core::task::Poll;
@ -10,7 +11,6 @@ use embassy_cortex_m::interrupt::{InterruptExt, Priority};
use embassy_hal_common::drop::OnDrop; use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
//use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::Interrupt; use crate::interrupt::Interrupt;
use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL}; use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL};
@ -31,9 +31,9 @@ pub const SRAM_UPPER: usize = 0x3000_0000;
pub enum Error { pub enum Error {
BufferTooLong, BufferTooLong,
BufferZeroLength, BufferZeroLength,
DMABufferNotInDataMemory, BufferNotInDataMemory,
BufferMisaligned, BufferMisaligned,
// TODO: add other error variants. BufferLengthMisaligned,
} }
pub const MODE_MASTER_8000: Mode = Mode::Master { pub const MODE_MASTER_8000: Mode = Mode::Master {
@ -122,12 +122,12 @@ pub enum MckFreq {
} }
impl MckFreq { impl MckFreq {
const REGISTER_VALUES: &[u32] = &[ const REGISTER_VALUES: &'static [u32] = &[
0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000,
0x08000000, 0x06000000, 0x04100000, 0x020C0000, 0x08000000, 0x06000000, 0x04100000, 0x020C0000,
]; ];
const FREQUENCIES: &[u32] = &[ const FREQUENCIES: &'static [u32] = &[
4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936, 4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936,
256000, 256000,
]; ];
@ -162,7 +162,7 @@ pub enum Ratio {
} }
impl Ratio { impl Ratio {
const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512];
pub fn to_divisor(&self) -> u32 { pub fn to_divisor(&self) -> u32 {
Self::RATIOS[u8::from(*self) as usize] Self::RATIOS[u8::from(*self) as usize]
@ -234,23 +234,10 @@ impl From<Channels> for u8 {
} }
} }
/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. /// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload.
/// ///
/// For more details about EasyDMA, consult the module documentation. /// For more details about EasyDMA, consult the module documentation.
pub struct I2S<'d, T: Instance> { pub struct I2S<'d, T: Instance> {
output: I2sOutput<'d, T>,
input: I2sInput<'d, T>,
}
/// Transmitter interface to the UARTE peripheral obtained
/// via [Uarte]::split.
pub struct I2sOutput<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
/// Receiver interface to the UARTE peripheral obtained
/// via [Uarte]::split.
pub struct I2sInput<'d, T: Instance> {
_p: PeripheralRef<'d, T>, _p: PeripheralRef<'d, T>,
} }
@ -298,78 +285,19 @@ impl<'d, T: Instance> I2S<'d, T> {
r.enable.write(|w| w.enable().enabled()); r.enable.write(|w| w.enable().enabled());
Self { Self { _p: i2s }
output: I2sOutput {
_p: unsafe { i2s.clone_unchecked() },
},
input: I2sInput { _p: i2s },
}
} }
/// Enables the I2S module. pub fn output(self) -> Output<'d, T> {
#[inline(always)] Output { _p: self._p }
pub fn enable(&self) -> &Self {
let r = T::regs();
r.enable.write(|w| w.enable().enabled());
self
} }
/// Disables the I2S module. pub fn input(self) -> Input<'d, T> {
#[inline(always)] Input { _p: self._p }
pub fn disable(&self) -> &Self {
let r = T::regs();
r.enable.write(|w| w.enable().disabled());
self
} }
/// Starts I2S transfer. pub fn full_duplex(self) -> FullDuplex<'d, T> {
#[inline(always)] FullDuplex { _p: self._p }
pub fn start(&self) -> &Self {
let r = T::regs();
self.enable();
trace!("START");
r.tasks_start.write(|w| unsafe { w.bits(1) });
self
}
/// Stops the I2S transfer and waits until it has stopped.
#[inline(always)]
pub async fn stop(&self) {
todo!()
}
/// Enables/disables I2S transmission (TX).
#[inline(always)]
pub fn set_tx_enabled(&self, enabled: bool) -> &Self {
let r = T::regs();
r.config.txen.write(|w| w.txen().bit(enabled));
self
}
/// Enables/disables I2S reception (RX).
#[inline(always)]
pub fn set_rx_enabled(&self, enabled: bool) -> &Self {
let r = T::regs();
r.config.rxen.write(|w| w.rxen().bit(enabled));
self
}
/// Transmits the given `buffer`.
/// Buffer address must be 4 byte aligned and located in RAM.
pub async fn tx<B>(&mut self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
self.output.tx(buffer).await
}
/// Receives data into the given `buffer` until it's filled.
/// Buffer address must be 4 byte aligned and located in RAM.
pub async fn rx<B>(&mut self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
self.input.rx(buffer).await
} }
fn apply_config(c: &CONFIG, config: &Config) { fn apply_config(c: &CONFIG, config: &Config) {
@ -433,103 +361,94 @@ impl<'d, T: Instance> I2S<'d, T> {
irq.unpend(); irq.unpend();
irq.enable(); irq.enable();
r.intenclr.write(|w| w.rxptrupd().clear()); let device = Device::<T>::new();
r.intenclr.write(|w| w.txptrupd().clear()); device.disable_tx_ptr_interrupt();
device.disable_rx_ptr_interrupt();
r.events_rxptrupd.reset(); device.reset_tx_ptr_event();
r.events_txptrupd.reset(); device.reset_rx_ptr_event();
r.intenset.write(|w| w.rxptrupd().set()); device.enable_tx_ptr_interrupt();
r.intenset.write(|w| w.txptrupd().set()); device.enable_rx_ptr_interrupt();
} }
fn on_interrupt(_: *mut ()) { fn on_interrupt(_: *mut ()) {
let r = T::regs(); let device = Device::<T>::new();
let s = T::state(); let s = T::state();
if r.events_txptrupd.read().bits() != 0 { if device.is_tx_ptr_updated() {
trace!("[{}] INT", s.seq.load(Ordering::Relaxed)); trace!("TX INT");
s.tx_waker.wake(); s.tx_waker.wake();
r.intenclr.write(|w| w.txptrupd().clear()); device.disable_tx_ptr_interrupt();
} }
if r.events_rxptrupd.read().bits() != 0 { if device.is_rx_ptr_updated() {
trace!("RX INT");
s.rx_waker.wake(); s.rx_waker.wake();
r.intenclr.write(|w| w.rxptrupd().clear()); device.disable_rx_ptr_interrupt();
} }
s.overruns.fetch_add(1, Ordering::Relaxed);
} }
} }
impl<'d, T: Instance> I2sOutput<'d, T> { pub struct Output<'d, T: Instance> {
/// Transmits the given `buffer`. _p: PeripheralRef<'d, T>,
/// Buffer address must be 4 byte aligned and located in RAM. }
#[allow(unused_mut)]
pub async fn tx<B>(&mut self, buffer: B) -> Result<(), Error> impl<'d, T: Instance> Output<'d, T> {
/// Starts I2S transfer.
#[inline(always)]
pub fn start<B>(&self, buffer: B) -> Result<(), Error>
where where
B: Buffer, B: Buffer,
{ {
let ptr = buffer.bytes_ptr(); // TODO what to do if it is started already?
let len = buffer.bytes_len();
if ptr as u32 % 4 != 0 { let device = Device::<T>::new();
return Err(Error::BufferMisaligned); device.enable();
} device.set_tx_buffer(buffer)?;
if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { device.enable_tx();
return Err(Error::DMABufferNotInDataMemory); device.start();
}
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
}
let r = T::regs(); Ok(())
let s = T::state(); }
let seq = s.seq.fetch_add(1, Ordering::Relaxed); /// Stops the I2S transfer and waits until it has stopped.
if r.events_txptrupd.read().bits() != 0 && seq > 1 { #[inline(always)]
warn!("XRUN!"); pub async fn stop(&self) {
loop {} todo!()
} }
let drop = OnDrop::new(move || { /// Transmits the given `buffer`.
trace!("write drop: stopping"); /// Buffer address must be 4 byte aligned and located in RAM.
#[allow(unused_mut)]
pub async fn send<B>(&mut self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
trace!("SEND: {}", buffer.bytes_ptr() as u32);
r.intenclr.write(|w| w.txptrupd().clear()); let device = Device::<T>::new();
r.events_txptrupd.reset(); let drop = device.on_tx_drop();
r.config.txen.write(|w| w.txen().disabled());
// TX is stopped almost instantly, spinning is fine.
while r.events_txptrupd.read().bits() == 0 {}
trace!("write drop: stopped");
});
trace!("[{}] PTR", s.seq.load(Ordering::Relaxed));
r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
poll_fn(|cx| { poll_fn(|cx| {
s.tx_waker.register(cx.waker()); T::state().tx_waker.register(cx.waker());
if r.events_txptrupd.read().bits() != 0 || seq == 0 {
trace!("[{}] POLL Ready", s.seq.load(Ordering::Relaxed)); if device.is_tx_ptr_updated() {
r.events_txptrupd.reset(); trace!("TX POLL: Ready");
r.intenset.write(|w| w.txptrupd().set()); device.reset_tx_ptr_event();
let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed); device.enable_tx_ptr_interrupt();
if overruns != 0 {
warn!("XRUN: {}", overruns);
s.overruns.store(0, Ordering::Relaxed)
}
Poll::Ready(()) Poll::Ready(())
} else { } else {
trace!("[{}] POLL Pending", s.seq.load(Ordering::Relaxed)); trace!("TX POLL: Pending");
Poll::Pending Poll::Pending
} }
}) })
.await; .await;
device.set_tx_buffer(buffer)?;
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
drop.defuse(); drop.defuse();
@ -537,40 +456,180 @@ impl<'d, T: Instance> I2sOutput<'d, T> {
} }
} }
impl<'d, T: Instance> I2sInput<'d, T> { pub struct Input<'d, T: Instance> {
/// Receives into the given `buffer`. _p: PeripheralRef<'d, T>,
/// Buffer address must be 4 byte aligned and located in RAM. }
#[allow(unused_mut)]
pub async fn rx<B>(&mut self, buffer: B) -> Result<(), Error> impl<'d, T: Instance> Input<'d, T> {
// TODO
}
pub struct FullDuplex<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> FullDuplex<'d, T> {
// TODO
}
struct Device<T>(&'static RegisterBlock, PhantomData<T>);
impl<T: Instance> Device<T> {
fn new() -> Self {
Self(T::regs(), PhantomData)
}
#[inline(always)]
pub fn enable(&self) {
trace!("ENABLED");
self.0.enable.write(|w| w.enable().enabled());
}
#[inline(always)]
pub fn disable(&self) {
trace!("DISABLED");
self.0.enable.write(|w| w.enable().disabled());
}
#[inline(always)]
fn enable_tx(&self) {
trace!("TX ENABLED");
self.0.config.txen.write(|w| w.txen().enabled());
}
#[inline(always)]
fn disable_tx(&self) {
trace!("TX DISABLED");
self.0.config.txen.write(|w| w.txen().disabled());
}
#[inline(always)]
fn enable_rx(&self) {
trace!("RX ENABLED");
self.0.config.rxen.write(|w| w.rxen().enabled());
}
#[inline(always)]
fn disable_rx(&self) {
trace!("RX DISABLED");
self.0.config.rxen.write(|w| w.rxen().disabled());
}
#[inline(always)]
fn start(&self) {
trace!("START");
self.0.tasks_start.write(|w| unsafe { w.bits(1) });
}
#[inline]
fn set_tx_buffer<B>(&self, buffer: B) -> Result<(), Error>
where where
B: Buffer, B: Buffer,
{ {
let ptr = buffer.bytes_ptr(); let (ptr, maxcnt) = Self::validate_buffer(buffer)?;
let len = buffer.bytes_len(); self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) });
if ptr as u32 % 4 != 0 {
return Err(Error::BufferMisaligned);
}
if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
return Err(Error::DMABufferNotInDataMemory);
}
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
if maxcnt > MAX_DMA_MAXCNT {
return Err(Error::BufferTooLong);
}
let r = T::regs();
let _s = T::state();
// TODO we can not progress until the last buffer written in RXD.PTR
// has started the transmission.
// We can use some sync primitive from `embassy-sync`.
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
Ok(()) Ok(())
} }
#[inline]
fn set_rx_buffer<B>(&self, buffer: B) -> Result<(), Error>
where
B: Buffer,
{
let (ptr, maxcnt) = Self::validate_buffer(buffer)?;
self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) });
self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) });
Ok(())
}
#[inline(always)]
fn is_tx_ptr_updated(&self) -> bool {
self.0.events_txptrupd.read().bits() != 0
}
#[inline(always)]
fn is_rx_ptr_updated(&self) -> bool {
self.0.events_rxptrupd.read().bits() != 0
}
#[inline(always)]
fn reset_tx_ptr_event(&self) {
trace!("TX PTR EVENT: Reset");
self.0.events_txptrupd.reset();
}
#[inline(always)]
fn reset_rx_ptr_event(&self) {
trace!("RX PTR EVENT: Reset");
self.0.events_rxptrupd.reset();
}
#[inline(always)]
fn disable_tx_ptr_interrupt(&self) {
trace!("TX PTR INTERRUPT: Disabled");
self.0.intenclr.write(|w| w.txptrupd().clear());
}
#[inline(always)]
fn disable_rx_ptr_interrupt(&self) {
trace!("RX PTR INTERRUPT: Disabled");
self.0.intenclr.write(|w| w.rxptrupd().clear());
}
#[inline(always)]
fn enable_tx_ptr_interrupt(&self) {
trace!("TX PTR INTERRUPT: Enabled");
self.0.intenset.write(|w| w.txptrupd().set());
}
#[inline(always)]
fn enable_rx_ptr_interrupt(&self) {
trace!("RX PTR INTERRUPT: Enabled");
self.0.intenclr.write(|w| w.rxptrupd().clear());
}
#[inline]
fn on_tx_drop(&self) -> OnDrop<fn()> {
OnDrop::new(move || {
trace!("TX DROP: Stopping");
let device = Device::<T>::new();
device.disable_tx_ptr_interrupt();
device.reset_tx_ptr_event();
device.disable_tx();
// TX is stopped almost instantly, spinning is fine.
while !device.is_tx_ptr_updated() {}
trace!("TX DROP: Stopped");
})
}
fn validate_buffer<B>(buffer: B) -> Result<(u32, u32), Error>
where
B: Buffer,
{
let ptr = buffer.bytes_ptr() as u32;
let len = buffer.bytes_len();
let maxcnt = ((len + core::mem::size_of::<u32>() - 1) / core::mem::size_of::<u32>()) as u32;
trace!("PTR={}, MAXCNT={}", ptr, maxcnt);
// TODO can we avoid repeating all those runtime checks for the same buffer again and again?
if ptr % 4 != 0 {
Err(Error::BufferMisaligned)
} else if len % 4 != 0 {
Err(Error::BufferLengthMisaligned)
} else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER {
Err(Error::BufferNotInDataMemory)
} else if maxcnt > MAX_DMA_MAXCNT {
Err(Error::BufferTooLong)
} else {
Ok((ptr, maxcnt))
}
}
} }
pub trait Buffer: Sized { pub trait Buffer: Sized {
@ -578,10 +637,10 @@ pub trait Buffer: Sized {
fn bytes_len(&self) -> usize; fn bytes_len(&self) -> usize;
} }
impl Buffer for &[u8] { impl Buffer for &[i8] {
#[inline] #[inline]
fn bytes_ptr(&self) -> *const u8 { fn bytes_ptr(&self) -> *const u8 {
self.as_ptr() self.as_ptr() as *const u8
} }
#[inline] #[inline]
@ -610,7 +669,7 @@ impl Buffer for &[i32] {
#[inline] #[inline]
fn bytes_len(&self) -> usize { fn bytes_len(&self) -> usize {
self.len() * core::mem::size_of::<i16>() self.len() * core::mem::size_of::<i32>()
} }
} }
@ -624,8 +683,6 @@ pub(crate) mod sealed {
pub struct State { pub struct State {
pub rx_waker: AtomicWaker, pub rx_waker: AtomicWaker,
pub tx_waker: AtomicWaker, pub tx_waker: AtomicWaker,
pub overruns: AtomicI32,
pub seq: AtomicI32,
} }
impl State { impl State {
@ -633,8 +690,6 @@ pub(crate) mod sealed {
Self { Self {
rx_waker: AtomicWaker::new(), rx_waker: AtomicWaker::new(),
tx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(),
overruns: AtomicI32::new(0),
seq: AtomicI32::new(0),
} }
} }
} }
@ -654,7 +709,7 @@ pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
macro_rules! impl_i2s { macro_rules! impl_i2s {
($type:ident, $pac_type:ident, $irq:ident) => { ($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::i2s::sealed::Instance for peripherals::$type { impl crate::i2s::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::i2s::RegisterBlock { fn regs() -> &'static crate::pac::i2s::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() } unsafe { &*pac::$pac_type::ptr() }
} }
fn state() -> &'static crate::i2s::sealed::State { fn state() -> &'static crate::i2s::sealed::State {

View File

@ -1,14 +1,13 @@
// Example inspired by RTIC's I2S demo: https://github.com/nrf-rs/nrf-hal/blob/master/examples/i2s-controller-demo/src/main.rs
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
use core::f32::consts::PI; use core::f32::consts::PI;
use defmt::{error, info}; use defmt::{error, info, trace};
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000, Channels}; use embassy_nrf::gpio::{Input, Pin, Pull};
use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000};
use embassy_nrf::pac::ficr::info; use embassy_nrf::pac::ficr::info;
use embassy_nrf::{i2s, interrupt}; use embassy_nrf::{i2s, interrupt};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -32,60 +31,79 @@ impl<T> AsMut<T> for AlignedBuffer<T> {
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default()); let p = embassy_nrf::init(Default::default());
let mut config = i2s::Config::default(); let mut config = i2s::Config::default();
// config.mode = MODE_MASTER_16000; config.mode = MODE_MASTER_32000;
config.mode = Mode::Master { // config.mode = Mode::Master {
freq: MckFreq::_32MDiv10, // freq: MckFreq::_32MDiv10,
ratio: Ratio::_256x, // ratio: Ratio::_256x,
}; // 12500 Hz // }; // 12500 Hz
config.channels = Channels::Left; config.channels = Channels::Left;
config.swidth = SampleWidth::_16bit;
let sample_rate = config.mode.sample_rate().expect("I2S Master"); let sample_rate = config.mode.sample_rate().expect("I2S Master");
let inv_sample_rate = 1.0 / sample_rate as f32; let inv_sample_rate = 1.0 / sample_rate as f32;
info!("Sample rate: {}", sample_rate); info!("Sample rate: {}", sample_rate);
let irq = interrupt::take!(I2S); // Wait for a button press
let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); // let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up);
// btn1.wait_for_low().await;
const BUF_SAMPLES: usize = 500; let irq = interrupt::take!(I2S);
const BUF_SIZE: usize = BUF_SAMPLES; let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config).output();
let mut buf = AlignedBuffer([0i16; BUF_SIZE]);
type Sample = i16;
const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample;
const NUM_SAMPLES: usize = 2000;
let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [
AlignedBuffer([0; NUM_SAMPLES]),
AlignedBuffer([0; NUM_SAMPLES]),
AlignedBuffer([0; NUM_SAMPLES]),
];
let mut carrier = SineOsc::new(); let mut carrier = SineOsc::new();
carrier.set_frequency(16.0, inv_sample_rate);
// let mut modulator = SineOsc::new(); let mut freq_mod = SineOsc::new();
// modulator.set_frequency(0.01, inv_sample_rate); freq_mod.set_frequency(8.0, inv_sample_rate);
// modulator.set_amplitude(0.2); freq_mod.set_amplitude(1.0);
let mut generate = |buf: &mut [i16]| { let mut amp_mod = SineOsc::new();
for sample in buf.as_mut() { amp_mod.set_frequency(4.0, inv_sample_rate);
amp_mod.set_amplitude(0.5);
let mut generate = |buf: &mut [Sample]| {
let ptr = buf as *const [Sample] as *const Sample as u32;
trace!("GEN: {}", ptr);
for sample in &mut buf.as_mut().chunks_mut(1) {
let signal = carrier.generate(); let signal = carrier.generate();
// let modulation = bipolar_to_unipolar(modulator.generate()); let freq_modulation = bipolar_to_unipolar(freq_mod.generate());
// carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate); carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate);
// carrier.set_amplitude((modulation); let amp_modulation = bipolar_to_unipolar(amp_mod.generate());
let value = (i16::MAX as f32 * signal) as i16; carrier.set_amplitude(amp_modulation);
*sample = value; let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample;
sample[0] = value;
} }
}; };
generate(buf.as_mut().as_mut_slice()); generate(buffers[0].as_mut().as_mut_slice());
generate(buffers[1].as_mut().as_mut_slice());
if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start");
error!("{}", err);
}
i2s.set_tx_enabled(true);
i2s.start();
let mut index = 1;
loop { loop {
generate(buf.as_mut().as_mut_slice()); if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await {
if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await {
error!("{}", err); error!("{}", err);
} }
index += 1;
if index >= 3 {
index = 0;
}
generate(buffers[index].as_mut().as_mut_slice());
} }
} }
#[derive(Clone)]
struct SineOsc { struct SineOsc {
amplitude: f32, amplitude: f32,
modulo: f32, modulo: f32,