diff --git a/patch_sm/WavPlayer/Makefile b/patch_sm/WavPlayer/Makefile new file mode 100644 index 000000000..50c1f09ba --- /dev/null +++ b/patch_sm/WavPlayer/Makefile @@ -0,0 +1,17 @@ +# Project Name +TARGET = WavPlayer + +# Sources +CPP_SOURCES = WavPlayer.cpp + +# Library Locations +LIBDAISY_DIR = ../../libDaisy +DAISYSP_DIR = ../../DaisySP + +# Includes FatFS source files within project. +USE_FATFS = 1 + +# Core location, and generic makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile + diff --git a/patch_sm/WavPlayer/WavPlayer.cpp b/patch_sm/WavPlayer/WavPlayer.cpp new file mode 100644 index 000000000..50ce7fbab --- /dev/null +++ b/patch_sm/WavPlayer/WavPlayer.cpp @@ -0,0 +1,163 @@ +// # WavPlayer +// ## Description +// Fairly simply sample player. +// Loads the first file it encounters on the SD card ( probably with alphabetic sort ). +// +// Play .wav file from the SD Card. +// +// ozh Feb 2022 - port to Patch_sm hardware, specifically the Patch.init() Eurorack module +// Controls: +// Toggle switch will 1) start playing the selected .WAV file +// 2) control whether the file loops at its end +// Push button will restart playing the sample (upon release of the button). +// Gate 1 input will restart playing the sample (upon the rising edge of the gate). +// +// limitations: no file selection mechanism. Only the first file (ASCII name order) will be played. +// +// TODO: +// use a knob to select between (upto) 16 (mono 48kHz) .WAV files on the SD card (refernce USE_ENCODER code) + +#include +#include + +#include "daisy_patch_sm.h" + +using namespace daisy; +using namespace patch_sm; + +DaisyPatchSM hw; + +SdmmcHandler sdcard; +FatFSInterface fsi; +WavPlayer sampler; + +/** Switch objects */ +Switch toggle; // toggle switch +Switch button; // push button + +// switch off encoder because the Patch.init() does not have one +#ifndef USE_ENCODER +//#define USE_ENCODER +#endif +static bool gate_state = false, prev_gate_state = false; + +void AudioCallback(AudioHandle::InterleavingInputBuffer in, + AudioHandle::InterleavingOutputBuffer out, + size_t size) +{ +#ifdef USE_ENCODER + int32_t inc; +#endif + + // Debounce digital controls + hw.ProcessDigitalControls(); + +// no encoder on Patch_sm, so none of this encoder code applies +// ozh - only the first file encountered on the SD card will be played. +#ifdef USE_ENCODER + // Change file with encoder. + inc = hw.encoder.Increment(); + if(inc > 0) + { + size_t curfile; + curfile = sampler.GetCurrentFile(); + if(curfile < sampler.GetNumberFiles() - 1) + { + sampler.Open(curfile + 1); + } + } + else if(inc < 0) + { + size_t curfile; + curfile = sampler.GetCurrentFile(); + if(curfile > 0) + { + sampler.Open(curfile - 1); + } + } +#endif // USE_ENCODER + +#define AUDIO_CHANNELS 2 + for(size_t i = 0; i < size; i += AUDIO_CHANNELS) + { + out[i] = out[i + 1] = s162f(sampler.Stream()) * 0.5f; + } +} + + +int main(void) +{ + // Init hardware + size_t blocksize = 4; // could make this 4 or 48, not critical here, + // cuz callback is very lightweight relative to the SD card read ( .Prepare() ) in the "while" loop below + + hw.Init(); + + /* Initialize the switch + - We'll read the switch on pin B8 + */ + + static bool state, prev_state; + toggle.Init(hw.B8); + + /* Initialize the switch + - We'll read the switch on pin B7 + */ + button.Init(hw.B7); + + SdmmcHandler::Config sd_cfg; + sd_cfg.Defaults(); + sd_cfg.speed = SdmmcHandler::Speed::MEDIUM_SLOW; //MEDIUM_SLOW; SLOW;// OZH set it to slower speed for debugging + sdcard.Init(sd_cfg); + fsi.Init(FatFSInterface::Config::MEDIA_SD); + f_mount(&fsi.GetSDFileSystem(), "/", 1); + + sampler.Init(fsi.GetSDPath()); + sampler.SetLooping(false); // set to false to play just once + + // SET LED to indicate Looping status. // ozh this works to show Looping status on the "boot" led on the Daisy Seed + hw.SetLed(sampler.GetLooping()); + + // Init Audio + hw.SetAudioBlockSize(blocksize); + hw.StartAudio(AudioCallback); + + // Loop forever... + for(;;) + { + // Prepare buffers for sampler as needed + sampler.Prepare(); + + /** Debounce the button */ + button.Debounce(); + if(button.FallingEdge()) + { + sampler.Restart(); + } + else + { + // detect Rising Edge + prev_gate_state = gate_state; + gate_state = hw.gate_in_1.State(); + /** Get the current gate in state */ + if((gate_state) + && (gate_state != prev_gate_state)) // gate is high, prev was low + { + sampler.Restart(); + } + } + /** Debounce the switch */ + toggle.Debounce(); + + /** Get the current toggle state */ + prev_state = state; // see if it changed + state = toggle.Pressed(); // flipped up + + if(state != prev_state) + { // toggle detected + sampler.SetLooping(state); // loops if Patch.init() toggle switch is up + /** Set the onboard led to the current state */ + hw.SetLed(state); + } + } +} diff --git a/seed/WavPlayer/WavPlayer.cpp b/seed/WavPlayer/WavPlayer.cpp index 165021d6e..3314a4949 100644 --- a/seed/WavPlayer/WavPlayer.cpp +++ b/seed/WavPlayer/WavPlayer.cpp @@ -7,26 +7,71 @@ // #include #include -#include "daisy_pod.h" -//#include "daisy_patch.h" + +// switch between Patch and Pod - the encoders are different +#ifndef USE_PATCH +#define USE_PATCH +#endif // USE_PATCH +#ifndef USE_PATCH_SM +//#define USE_PATCH_SM +// Patch_sm is like the POD in its Audio (2 channel) and lack of OLED display +// unlike the POD, it has no encoder, so we would have to use other controls like a knob and a pushbutton +#endif // USE_PATCH_SM + +#ifdef USE_PATCH +#include "daisy_patch.h" +#else + #ifdef USE_PATCH_SM + #include "daisy_patch_sm.h" + #else + #include "daisy_pod.h" + #endif //USE_PATCH_SM +#endif // USE_PATCH using namespace daisy; +#ifdef USE_PATCH_SM +using namespace patch_sm; +#endif //USE_PATCH_SM + +#ifdef USE_PATCH +DaisyPatch hw; +#else + #ifdef USE_PATCH_SM + DaisyPatchSM hw; + #else + DaisyPod hw; + #endif //USE_PATCH_SM +#endif // USE_PATCH -//DaisyPatch hw; -DaisyPod hw; SdmmcHandler sdcard; FatFSInterface fsi; WavPlayer sampler; +#ifdef USE_PATCH +void AudioCallback(AudioHandle::InputBuffer in, + AudioHandle::OutputBuffer out, + size_t size) +#else // Pod and Patch_sm void AudioCallback(AudioHandle::InterleavingInputBuffer in, AudioHandle::InterleavingOutputBuffer out, size_t size) +#endif // USE_PATCH { int32_t inc; // Debounce digital controls hw.ProcessDigitalControls(); +// turn the encoder support on and off, for debugging + +#ifndef USE_PATCH_SM + // no encoder on Patch_sm +#ifndef USE_ENCODER +#define USE_ENCODER +#endif // USE_ENCODER +#endif // USE_PATCH_SM + +#ifdef USE_ENCODER // Change file with encoder. inc = hw.encoder.Increment(); if(inc > 0) @@ -47,6 +92,7 @@ void AudioCallback(AudioHandle::InterleavingInputBuffer in, sampler.Open(curfile - 1); } } +#endif // USE_ENCODER // if(hw.button1.RisingEdge()) // { @@ -56,14 +102,28 @@ void AudioCallback(AudioHandle::InterleavingInputBuffer in, // if(hw.button2.RisingEdge()) // { // sampler.SetLooping(!sampler.GetLooping()); + // ozh - the DaisyPatch::LED_2_B is a reference for the pre-OLED prototype of the Daisy + // 2/23/2022 use hw.seed.SetLed(bool state); // this is tested below // //hw.SetLed(DaisyPatch::LED_2_B, sampler.GetLooping()); // //dsy_gpio_write(&hw.leds[DaisyPatch::LED_2_B], // // static_cast(!sampler.GetLooping())); // } - for(size_t i = 0; i < size; i += 2) +#ifdef USE_PATCH + // this is a little counter intuitive. + // We have a channel and an index eg out[CHNL][i] + // so, we don't need to move forward 2 for the index + #define AUDIO_CHANNELS 1 +#else // Pod and Patch_sm + #define AUDIO_CHANNELS 2 +#endif // USE_PATCH + for(size_t i = 0; i < size; i += AUDIO_CHANNELS) { +#ifdef USE_PATCH + out[0][i] = out[1][i] = s162f(sampler.Stream()) * 0.5f; +#else // Pod and Patch_sm out[i] = out[i + 1] = s162f(sampler.Stream()) * 0.5f; +#endif } } @@ -72,23 +132,39 @@ int main(void) { // Init hardware size_t blocksize = 4; + //ozh - chg blocksize to match Init blocksize in daisy_patch.cpp + //AudioHandle::Config cfg; + //cfg.blocksize = 48; + //cfg.samplerate = SaiHandle::Config::SampleRate::SAI_48KHZ; + hw.Init(); // hw.ClearLeds(); SdmmcHandler::Config sd_cfg; sd_cfg.Defaults(); + sd_cfg.speed = SdmmcHandler::Speed::MEDIUM_SLOW; //MEDIUM_SLOW; SLOW;// OZH set it to slower speed for debugging sdcard.Init(sd_cfg); fsi.Init(FatFSInterface::Config::MEDIA_SD); f_mount(&fsi.GetSDFileSystem(), "/", 1); sampler.Init(fsi.GetSDPath()); sampler.SetLooping(true); + // test only ozh + //sampler.SetLooping(false); - // SET LED to indicate Looping status. - //hw.SetLed(DaisyPatch::LED_2_B, sampler.GetLooping()); + // SET LED to indicate Looping status. // ozh this works to show Looping status on the "boot" led on the Daisy Seed +// different syntax for ORed conditions +#ifdef USE_PATCH + hw.seed.SetLed(sampler.GetLooping()); +#else + #ifdef USE_PATCH_SM + hw.SetLed(sampler.GetLooping()); + #endif +#endif // USE_PATCH // Init Audio hw.SetAudioBlockSize(blocksize); hw.StartAudio(AudioCallback); + // Loop forever... for(;;) {