#include "daisy_seed.h" #include "daisysp.h" #include "scope.hpp" #include "osclsk.hpp" using namespace daisy; using namespace daisysp; #define DMA_AREA_SIZE (1 << 12) #define SCOPE_RING_BUF_SIZE 4096 #define AUDIO_BLOCK_SIZE 2 /* declarations */ DaisySeed daisy_hw; System sys; #ifdef AUDIOC_DEBUG CpuLoadMeter load_meter; #endif osclsk_scope scope; uint8_t DMA_BUFFER_MEM_SECTION dma_area[DMA_AREA_SIZE]; static void audio_cb(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t sz); Oscillator osc; WhiteNoise noise; AdEnv kickVolEnv, kickPitchEnv, snareEnv; Switch kick, snare; RingBuffer scope_in; void audio_cb(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t sz) { #ifdef AUDIOC_DEBUG load_meter.OnBlockStart(); #endif float osc_out, noise_out, snr_env_out, kck_env_out; float sig[AUDIO_BLOCK_SIZE]; //Get rid of any bouncing snare.Debounce(); kick.Debounce(); //If you press the kick button... if(kick.RisingEdge()) { //Trigger both envelopes! kickVolEnv.Trigger(); kickPitchEnv.Trigger(); } //If press the snare button trigger its envelope if(snare.RisingEdge()) { snareEnv.Trigger(); } //Prepare the audio block for(size_t i = 0; i < sz; i++) { //Get the next volume samples snr_env_out = snareEnv.Process(); kck_env_out = kickVolEnv.Process(); //Apply the pitch envelope to the kick osc.SetFreq(kickPitchEnv.Process()); //Set the kick volume to the envelope's output osc.SetAmp(kck_env_out); //Process the next oscillator sample osc_out = osc.Process(); //Get the next snare sample noise_out = noise.Process(); //Set the sample to the correct volume noise_out *= snr_env_out; //Mix the two signals at half volume sig[i] = .5 * noise_out + .5 * osc_out; } /* write to output */ for (size_t i = 0; i < sz; i++) { out[0][i] = sig[i]; out[1][i] = sig[i]; } scope_in.Overwrite(sig, sz); #ifdef AUDIOC_DEBUG load_meter.OnBlockEnd(); #endif } static void setup_drums(void) { float samplerate; /* kick n snare */ samplerate = daisy_hw.AudioSampleRate(); //Initialize oscillator for kickdrum osc.Init(samplerate); osc.SetWaveform(Oscillator::WAVE_TRI); osc.SetAmp(1); //Initialize noise noise.Init(); //Initialize envelopes, this one's for the snare amplitude snareEnv.Init(samplerate); snareEnv.SetTime(ADENV_SEG_ATTACK, .01); snareEnv.SetTime(ADENV_SEG_DECAY, .2); snareEnv.SetMax(1); snareEnv.SetMin(0); //This envelope will control the kick oscillator's pitch //Note that this envelope is much faster than the volume kickPitchEnv.Init(samplerate); kickPitchEnv.SetTime(ADENV_SEG_ATTACK, .01); kickPitchEnv.SetTime(ADENV_SEG_DECAY, .05); kickPitchEnv.SetMax(400); kickPitchEnv.SetMin(50); //This one will control the kick's volume kickVolEnv.Init(samplerate); kickVolEnv.SetTime(ADENV_SEG_ATTACK, .01); kickVolEnv.SetTime(ADENV_SEG_DECAY, 1); kickVolEnv.SetMax(1); kickVolEnv.SetMin(0); //Initialize the kick and snare buttons on pins 27 and 28 //The callback rate is samplerate / blocksize (48) snare.Init(daisy_hw.GetPin(27), samplerate / (float)AUDIO_BLOCK_SIZE); kick.Init(daisy_hw.GetPin(28), samplerate / (float)AUDIO_BLOCK_SIZE); } #define LOAD_FREQ 5 #define LOAD_PERIOD (1000u / LOAD_FREQ) int main(void) { size_t read; float s[AUDIO_BLOCK_SIZE]; daisy_hw.Configure(); daisy_hw.Init(); daisy_hw.StartLog(false); /* true = wait for usb connection */ daisy_hw.SetAudioBlockSize(AUDIO_BLOCK_SIZE); #ifdef AUDIOC_DEBUG load_meter.Init(daisy_hw.AudioSampleRate(), daisy_hw.AudioBlockSize()); uint32_t load_tick = sys.GetNow(); uint32_t t; #endif if (scope.init(dma_area, sizeof(dma_area)) == -1) daisy_hw.PrintLine("scope.init failed"); scope_in.Init(); setup_drums(); daisy_hw.StartAudio(audio_cb); while (true) { switch (scope.state()) { case osclsk_scope::osc_state::SAMPLE_DONE: scope.render_block(); break; case osclsk_scope::osc_state::RENDER: scope.render_block(); break; case osclsk_scope::osc_state::RENDER_DONE: scope.prep(); break; case osclsk_scope::osc_state::READY: scope.trig(); break; default: break; } read = scope_in.readable(); read = (read <= sizeof(s)/sizeof(*s)) ? read : sizeof(s)/sizeof(*s); scope_in.ImmediateRead(s, read); scope.sample(s, read); #ifdef AUDIOC_DEBUG if ((t = sys.GetNow()) > load_tick + LOAD_PERIOD) { load_tick = t; // get the current load (smoothed value and peak values) const float avgLoad = load_meter.GetAvgCpuLoad(); const float maxLoad = load_meter.GetMaxCpuLoad(); const float minLoad = load_meter.GetMinCpuLoad(); // print it to the serial connection (as percentages) daisy_hw.PrintLine("Processing Load %:"); daisy_hw.PrintLine("Max: " FLT_FMT3, FLT_VAR3(maxLoad * 100.0f)); daisy_hw.PrintLine("Avg: " FLT_FMT3, FLT_VAR3(avgLoad * 100.0f)); daisy_hw.PrintLine("Min: " FLT_FMT3, FLT_VAR3(minLoad * 100.0f)); } #endif } }