#include "daisy_seed.h" #include "daisysp.h" #include "scope.hpp" #include "osclsk.hpp" using namespace daisy; using namespace daisysp; #define DMA_AREA_SIZE (3*(1 << 16)) #define SCOPE_RING_BUF_SIZE 1024 #define AUDIO_BLOCK_SIZE 2 /* declarations */ DaisySeed daisy_hw; CpuLoadMeter load_meter; 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; static void audio_cb(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t sz) { float osc_out, noise_out, snr_env_out, kck_env_out; float sig[AUDIO_BLOCK_SIZE]; load_meter.OnBlockStart(); //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; } for (size_t i = 0; i < sz; i++) { out[0][i] = sig[i]; out[1][i] = sig[i]; } scope_in.Overwrite(sig, sz); load_meter.OnBlockEnd(); } 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_METER_TICKS (1 << 16) int main(void) { size_t read; float s[AUDIO_BLOCK_SIZE]; int load_tick; daisy_hw.Configure(); daisy_hw.Init(); daisy_hw.StartLog(false); /* true = wait for usb connection */ daisy_hw.SetAudioBlockSize(AUDIO_BLOCK_SIZE); load_meter.Init(daisy_hw.AudioSampleRate(), daisy_hw.AudioBlockSize()); load_tick = LOAD_METER_TICKS; 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) { read = scope_in.readable(); read = (read <= sizeof(s)/sizeof(*s)) ? read : sizeof(s)/sizeof(*s); scope_in.ImmediateRead(s, read); scope.sample(s, read); switch (scope.state()) { case osclsk_scope::osc_state::SAMPLE_DONE: 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; } if (--load_tick == 0) { load_tick = LOAD_METER_TICKS; // 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)); } } }