dynamic sequencer support
This commit is contained in:
parent
7d93cd8977
commit
9e67026345
3 changed files with 144 additions and 22 deletions
|
|
@ -93,6 +93,84 @@ pub mod sequencer {
|
||||||
self.sequence[self.index]
|
self.sequence[self.index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SequenceEntry {
|
||||||
|
pub frequency_hz: u16,
|
||||||
|
pub duration_ms: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DynamicSequence<'a> {
|
||||||
|
sequence: &'a [SequenceEntry],
|
||||||
|
index: usize,
|
||||||
|
|
||||||
|
// timer stuff
|
||||||
|
system_tick_rate_hz: usize,
|
||||||
|
ticks_remaining: usize,
|
||||||
|
|
||||||
|
// playback stuff
|
||||||
|
num_loops: usize,
|
||||||
|
loop_count: usize,
|
||||||
|
|
||||||
|
// misc
|
||||||
|
enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DynamicSequence<'a> {
|
||||||
|
pub fn new(sequence: &'a [SequenceEntry], system_tick_rate_hz: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
sequence,
|
||||||
|
index: 0,
|
||||||
|
system_tick_rate_hz,
|
||||||
|
ticks_remaining: 0,
|
||||||
|
num_loops: 1,
|
||||||
|
loop_count: 0,
|
||||||
|
enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_sequence(&mut self, sequence: &'a [SequenceEntry], num_loops: usize) {
|
||||||
|
self.sequence = sequence;
|
||||||
|
self.num_loops = num_loops;
|
||||||
|
self.loop_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
if self.enabled {
|
||||||
|
self.ticks_remaining = self.ticks_remaining.saturating_sub(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn need_service(&self) -> bool {
|
||||||
|
self.ticks_remaining == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service(&mut self) -> Option<u16> {
|
||||||
|
let entry = &self.sequence[self.index];
|
||||||
|
self.ticks_remaining = self.system_tick_rate_hz * (entry.duration_ms as usize) / 1000;
|
||||||
|
self.index = self.index.saturating_add(1);
|
||||||
|
|
||||||
|
let out = if self.loop_count > self.num_loops {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(entry.frequency_hz)
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.index > self.sequence.len() - 1 {
|
||||||
|
self.index = 0;
|
||||||
|
self.loop_count = self.loop_count.saturating_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::insert_coin::{
|
use crate::insert_coin::{
|
||||||
|
|
@ -194,12 +272,14 @@ pub struct Services {
|
||||||
pub led2: LedService,
|
pub led2: LedService,
|
||||||
pub synth0: SynthesizerService,
|
pub synth0: SynthesizerService,
|
||||||
pub sample_player: DacService<'static>,
|
pub sample_player: DacService<'static>,
|
||||||
|
pub sequencer: sequencer::DynamicSequence<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Services {
|
impl Services {
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self) {
|
||||||
self.synth0.tick();
|
self.synth0.tick();
|
||||||
self.sample_player.tick();
|
self.sample_player.tick();
|
||||||
|
self.sequencer.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,19 +434,31 @@ impl App {
|
||||||
self.services.led2.service();
|
self.services.led2.service();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.services.sequencer.need_service() {
|
||||||
|
if let Some(out) = self.services.sequencer.service() {
|
||||||
|
self.services.synth0.set_freq(out.into());
|
||||||
|
} else {
|
||||||
|
self.services.sequencer.disable();
|
||||||
|
self.services.synth0.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.services.synth0.need_service() {
|
if self.services.synth0.need_service() {
|
||||||
let out = self.services.synth0.service() / self.settings.volume.as_volume_divisor();
|
let out = match self.services.synth0.service() {
|
||||||
// self.interfaces
|
Some(value) => value / self.settings.volume.as_volume_divisor(),
|
||||||
// .pwm_core
|
None => 0,
|
||||||
// .write_amplitude(ch32_hal::timer::Channel::Ch4, out);
|
};
|
||||||
|
self.interfaces
|
||||||
|
.pwm_core
|
||||||
|
.write_amplitude(ch32_hal::timer::Channel::Ch4, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.services.sample_player.need_service() {
|
if self.services.sample_player.need_service() {
|
||||||
self.services.sample_player.service();
|
self.services.sample_player.service();
|
||||||
let out = self.services.sample_player.get_amplitude();
|
// let out = self.services.sample_player.get_amplitude();
|
||||||
self.interfaces
|
// self.interfaces
|
||||||
.pwm_core
|
// .pwm_core
|
||||||
.write_amplitude(ch32_hal::timer::Channel::Ch4, out as u8);
|
// .write_amplitude(ch32_hal::timer::Channel::Ch4, out as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,19 @@ use hal::println;
|
||||||
|
|
||||||
use qingke::riscv;
|
use qingke::riscv;
|
||||||
|
|
||||||
|
use crate::app::sequencer::{DynamicSequence, SequenceEntry};
|
||||||
|
|
||||||
static LED0_SEQ: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
static LED0_SEQ: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
||||||
|
static TEST_SEQ: [app::sequencer::SequenceEntry; 2] = [
|
||||||
|
SequenceEntry {
|
||||||
|
frequency_hz: 440,
|
||||||
|
duration_ms: 1000,
|
||||||
|
},
|
||||||
|
SequenceEntry {
|
||||||
|
frequency_hz: 220,
|
||||||
|
duration_ms: 1000,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Flag {
|
struct Flag {
|
||||||
|
|
@ -86,8 +98,8 @@ static mut INPUT_FLAGS: InputFlags = InputFlags {
|
||||||
struct Test {}
|
struct Test {}
|
||||||
impl Handler<hal::interrupt::typelevel::EXTI7_0> for Test {
|
impl Handler<hal::interrupt::typelevel::EXTI7_0> for Test {
|
||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
#[cfg(feature = "enable_print")]
|
// #[cfg(feature = "enable_print")]
|
||||||
println!("on_interrupt()");
|
// println!("on_interrupt()");
|
||||||
critical_section::with(|_| unsafe {
|
critical_section::with(|_| unsafe {
|
||||||
let flags = system::clear_interrupt(2, 6);
|
let flags = system::clear_interrupt(2, 6);
|
||||||
if flags[0] {
|
if flags[0] {
|
||||||
|
|
@ -206,8 +218,8 @@ fn app_main(mut p: hal::Peripherals) -> ! {
|
||||||
// println!("ADC_PIN CHANNEL: {}", adc_pin.channel().channel());
|
// println!("ADC_PIN CHANNEL: {}", adc_pin.channel().channel());
|
||||||
let adc_cal = adc.calibrate();
|
let adc_cal = adc.calibrate();
|
||||||
|
|
||||||
#[cfg(feature = "enable_print")]
|
// #[cfg(feature = "enable_print")]
|
||||||
println!("ADC calibration value: {}", adc_cal);
|
// println!("ADC calibration value: {}", adc_cal);
|
||||||
|
|
||||||
// definitions
|
// definitions
|
||||||
let sense_coin_pin = p.PC2;
|
let sense_coin_pin = p.PC2;
|
||||||
|
|
@ -260,12 +272,15 @@ fn app_main(mut p: hal::Peripherals) -> ! {
|
||||||
let sample_player = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data);
|
let sample_player = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data);
|
||||||
sample_player.load_data(coin_sound);
|
sample_player.load_data(coin_sound);
|
||||||
|
|
||||||
|
let sequencer = app::sequencer::DynamicSequence::new(&TEST_SEQ, tick_rate_hz);
|
||||||
|
|
||||||
let app_services = Services {
|
let app_services = Services {
|
||||||
led0: LedService::new(led0_ch),
|
led0: LedService::new(led0_ch),
|
||||||
led1: LedService::new(led1_ch),
|
led1: LedService::new(led1_ch),
|
||||||
led2: LedService::new(led2_ch),
|
led2: LedService::new(led2_ch),
|
||||||
synth0: SynthesizerService::new(tick_rate_hz),
|
synth0: SynthesizerService::new(tick_rate_hz),
|
||||||
sample_player,
|
sample_player,
|
||||||
|
sequencer,
|
||||||
};
|
};
|
||||||
|
|
||||||
let app_sequences = Sequences {
|
let app_sequences = Sequences {
|
||||||
|
|
@ -303,9 +318,8 @@ fn app_main(mut p: hal::Peripherals) -> ! {
|
||||||
// -depress the big button for approx 2s and it puts the led into low light mode. Just making it dimmer than usual.
|
// -depress the big button for approx 2s and it puts the led into low light mode. Just making it dimmer than usual.
|
||||||
// -depress the big button for 5s and it goes into deep sleep, everything off with the low sleep current draw
|
// -depress the big button for 5s and it goes into deep sleep, everything off with the low sleep current draw
|
||||||
|
|
||||||
#[cfg(feature = "enable_print")]
|
// #[cfg(feature = "enable_print")]
|
||||||
println!("begin");
|
// println!("begin");
|
||||||
|
|
||||||
let mut volume_btn_prev = volume_btn_input.is_high_immediate();
|
let mut volume_btn_prev = volume_btn_input.is_high_immediate();
|
||||||
let mut light_ctrl_btn_prev = light_ctrl_btn_input.is_high_immediate();
|
let mut light_ctrl_btn_prev = light_ctrl_btn_input.is_high_immediate();
|
||||||
|
|
||||||
|
|
@ -324,8 +338,8 @@ fn app_main(mut p: hal::Peripherals) -> ! {
|
||||||
|
|
||||||
volume_btn_input.service();
|
volume_btn_input.service();
|
||||||
if volume_btn_input.ready() {
|
if volume_btn_input.ready() {
|
||||||
#[cfg(feature = "enable_print")]
|
// #[cfg(feature = "enable_print")]
|
||||||
println!("volume btn value: {}", volume_btn_input.value());
|
// println!("volume btn value: {}", volume_btn_input.value());
|
||||||
if !volume_btn_input.value() {
|
if !volume_btn_input.value() {
|
||||||
app.volume_button();
|
app.volume_button();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ const SQUARE_WAVETABLE: [u8; 2] = [0, 100];
|
||||||
|
|
||||||
pub struct SynthesizerService {
|
pub struct SynthesizerService {
|
||||||
pub synth: SimpleWavetableSynthesizer<SimpleWavetable<'static, u8>>,
|
pub synth: SimpleWavetableSynthesizer<SimpleWavetable<'static, u8>>,
|
||||||
|
pub enabled: bool,
|
||||||
|
pub need_service: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SynthesizerService {
|
impl SynthesizerService {
|
||||||
|
|
@ -18,19 +20,29 @@ impl SynthesizerService {
|
||||||
let square_wt = SimpleWavetable::new(&SQUARE_WAVETABLE);
|
let square_wt = SimpleWavetable::new(&SQUARE_WAVETABLE);
|
||||||
let synth = SimpleWavetableSynthesizer::new(square_wt, clock_freq_hz);
|
let synth = SimpleWavetableSynthesizer::new(square_wt, clock_freq_hz);
|
||||||
|
|
||||||
Self { synth }
|
Self {
|
||||||
|
synth,
|
||||||
|
enabled: true,
|
||||||
|
need_service: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self) {
|
||||||
|
if self.enabled {
|
||||||
self.synth.tick();
|
self.synth.tick();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn need_service(&self) -> bool {
|
pub fn need_service(&self) -> bool {
|
||||||
self.synth.has_new_output()
|
self.need_service || self.synth.has_new_output()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn service(&mut self) -> u8 {
|
pub fn service(&mut self) -> Option<u8> {
|
||||||
self.synth.get_output()
|
if self.enabled {
|
||||||
|
Some(self.synth.get_output())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,10 +52,14 @@ impl SynthesizerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable(&mut self) {
|
pub fn enable(&mut self) {
|
||||||
|
self.enabled = true;
|
||||||
|
self.need_service = true;
|
||||||
// TODO: write the enable function
|
// TODO: write the enable function
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable(&mut self) {
|
pub fn disable(&mut self) {
|
||||||
|
self.enabled = false;
|
||||||
|
self.need_service = true;
|
||||||
// TODO: write the disable function
|
// TODO: write the disable function
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue