parent
08715a0bce
commit
9b2cd65be9
@ -0,0 +1,51 @@ |
||||
/*
|
||||
MicroMDAEPiano |
||||
|
||||
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
||||
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
||||
|
||||
(c)2019 H. Wirtz <wirtz@parasitstudio.de> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation; either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
// Idea from: https://playground.arduino.cc/Code/EEPROMWriteAnything/
|
||||
|
||||
#include <EEPROM.h> |
||||
#include <Arduino.h> // for type definitions |
||||
|
||||
uint32_t crc32(uint8_t* calc_start, uint16_t calc_bytes); |
||||
|
||||
template <class T> int EEPROM_writeAnything(int ee, const T& value) |
||||
{ |
||||
uint8_t* p = (uint8_t*)(const void*)&value; |
||||
uint16_t i; |
||||
uint32_t checksum=crc32(p+4,sizeof(value)-4); |
||||
|
||||
*p=checksum; |
||||
|
||||
for (i = 0; i < sizeof(value); i++) |
||||
EEPROM.update(ee++, *p++); |
||||
return i; |
||||
} |
||||
|
||||
template <class T> int EEPROM_readAnything(int ee, T& value) |
||||
{ |
||||
uint8_t* p = (uint8_t*)(void*)&value; |
||||
unsigned int i; |
||||
for (i = 0; i < sizeof(value); i++) |
||||
*p++ = EEPROM.read(ee++); |
||||
return i; |
||||
} |
@ -1,195 +0,0 @@ |
||||
/*
|
||||
LiquidMenuTest |
||||
*/ |
||||
|
||||
#ifndef UI_HPP_INCLUDED |
||||
#define UI_HPP_INCLUDED |
||||
|
||||
#include <LiquidCrystal_I2C.h> |
||||
#include <LiquidMenu.h> |
||||
#include <Bounce.h> |
||||
#include "Encoder4.h" |
||||
#include "config.h" |
||||
|
||||
LiquidCrystal_I2C lcd(LCD_I2C_ADDRESS, LCD_CHARS, LCD_LINES); |
||||
Encoder4 enc[NUM_ENCODER] = {Encoder4(ENC_L_PIN_A, ENC_L_PIN_B), Encoder4(ENC_R_PIN_A, ENC_R_PIN_B)}; |
||||
int32_t encoder_value[NUM_ENCODER]; |
||||
Bounce but[NUM_ENCODER] = {Bounce(BUT_L_PIN, BUT_DEBOUNCE_MS), Bounce(BUT_R_PIN, BUT_DEBOUNCE_MS)}; |
||||
uint8_t main_menu_selector = 0; |
||||
elapsedMillis control_rate; |
||||
|
||||
enum { LEFT_ENCODER, RIGHT_ENCODER }; |
||||
|
||||
// Recycleable objects
|
||||
const char text_back[] PROGMEM = "BACK"; |
||||
LiquidLine back_line(1, 1, text_back); |
||||
|
||||
// Main menu
|
||||
const char text10[] PROGMEM = "Favorites"; |
||||
const char text11[] PROGMEM = "Sound"; |
||||
const char text12[] PROGMEM = "Store"; |
||||
const char text13[] PROGMEM = "Info"; |
||||
LiquidLine main_line1(1, 0, text10); |
||||
LiquidLine main_line2(1, 1, text11); |
||||
LiquidLine main_line3(1, 1, text12); |
||||
LiquidLine main_line4(1, 1, text13); |
||||
LiquidScreen main_screen; |
||||
LiquidMenu main_menu(lcd); |
||||
|
||||
// Sound menu
|
||||
const char text20[] PROGMEM = "Decay"; |
||||
const char text21[] PROGMEM = "Release"; |
||||
const char text22[] PROGMEM = "Hardness"; |
||||
const char text23[] PROGMEM = "Treble"; |
||||
LiquidLine sound_line1(1, 0, text20); |
||||
LiquidLine sound_line2(1, 1, text21); |
||||
LiquidLine sound_line3(1, 1, text22); |
||||
LiquidLine sound_line4(1, 1, text23); |
||||
LiquidScreen sound_screen(sound_line1, sound_line2, sound_line3, sound_line4); |
||||
LiquidMenu sound_menu(lcd, sound_screen); |
||||
|
||||
// Info menu
|
||||
const char text40[] PROGMEM = "INFO"; |
||||
LiquidLine info_line1(0, 0, text40); |
||||
LiquidScreen info_screen(info_line1); |
||||
LiquidMenu info_menu(lcd, info_screen); |
||||
|
||||
// System menu
|
||||
LiquidSystem menu_system(main_menu, sound_menu, info_menu); |
||||
|
||||
void callback_favorites_function() { |
||||
Serial.println(F("callback_favorites_function")); |
||||
} |
||||
|
||||
void callback_sound_function() { |
||||
Serial.println(F("callback_sound_function")); |
||||
} |
||||
|
||||
void callback_store_function() { |
||||
Serial.println(F("callback_store_function")); |
||||
} |
||||
|
||||
void callback_info_function() { |
||||
Serial.println(F("callback_info_function")); |
||||
} |
||||
|
||||
void callback_back_function() { |
||||
Serial.println(F("callback_back_function")); |
||||
} |
||||
|
||||
void setup(void) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
Serial.begin(38400); |
||||
|
||||
pinMode(BUT_L_PIN, INPUT_PULLUP); |
||||
pinMode(BUT_R_PIN, INPUT_PULLUP); |
||||
|
||||
Serial.println(F("<setup start>")); |
||||
|
||||
// Encoder setup
|
||||
enc[0].write(INITIAL_ENC_L_VALUE); |
||||
enc[1].write(INITIAL_ENC_R_VALUE); |
||||
// LCD display setup
|
||||
lcd.init(); |
||||
lcd.blink_off(); |
||||
lcd.cursor_off(); |
||||
lcd.backlight(); |
||||
//lcd.noAutoscroll();
|
||||
|
||||
main_screen.add_line(main_line1); |
||||
main_screen.add_line(main_line2); |
||||
main_screen.add_line(main_line3); |
||||
main_screen.add_line(main_line4); |
||||
main_screen.add_line(back_line); |
||||
|
||||
main_line1.attach_function(1, callback_favorites_function); |
||||
main_line2.attach_function(2, callback_sound_function); |
||||
main_line3.attach_function(3, callback_store_function); |
||||
main_line4.attach_function(4, callback_info_function); |
||||
back_line.attach_function(1, callback_back_function ); |
||||
|
||||
main_screen.set_displayLineCount(2); |
||||
main_menu.add_screen(main_screen); |
||||
|
||||
for (i = 0; i < NUM_ENCODER; i++) |
||||
but[i].update(); |
||||
|
||||
enc[LEFT_ENCODER].write(0, 0, 99, true); |
||||
encoder_value[LEFT_ENCODER] = 0; |
||||
enc[RIGHT_ENCODER].write(0, 0, 4, true); |
||||
encoder_value[RIGHT_ENCODER] = 0; |
||||
|
||||
menu_system.update(); |
||||
menu_system.set_focusPosition(Position::LEFT); |
||||
menu_system.switch_focus(); |
||||
} |
||||
|
||||
void loop(void) |
||||
{ |
||||
// CONTROL-RATE-EVENT-HANDLING
|
||||
if (control_rate > CONTROL_RATE_MS) |
||||
{ |
||||
control_rate = 0; |
||||
|
||||
handle_ui(); |
||||
} |
||||
} |
||||
|
||||
void handle_ui(void) |
||||
{ |
||||
uint8_t i; |
||||
int32_t encoder_tmp; |
||||
|
||||
for (i = 0; i < NUM_ENCODER; i++) |
||||
{ |
||||
but[i].update(); |
||||
|
||||
switch (i) |
||||
{ |
||||
case RIGHT_ENCODER: |
||||
// Encoder handling
|
||||
encoder_tmp = enc[RIGHT_ENCODER].read(); |
||||
if (encoder_tmp > encoder_value[RIGHT_ENCODER]) |
||||
{ |
||||
// move down
|
||||
#ifdef DEBUG |
||||
Serial.println(F("ENC-R-DOWN")); |
||||
#endif |
||||
menu_system.switch_focus(true); |
||||
if (encoder_value[RIGHT_ENCODER] == enc[RIGHT_ENCODER].read_max()) |
||||
encoder_tmp = enc[RIGHT_ENCODER].read_min(); |
||||
} |
||||
else if (encoder_tmp < encoder_value[RIGHT_ENCODER]) |
||||
{ |
||||
// move up
|
||||
#ifdef DEBUG |
||||
Serial.println(F("ENC-R-UP")); |
||||
#endif |
||||
menu_system.switch_focus(false); |
||||
if (encoder_value[RIGHT_ENCODER] == enc[RIGHT_ENCODER].read_min()) |
||||
encoder_tmp = enc[RIGHT_ENCODER].read_max(); |
||||
} |
||||
encoder_value[RIGHT_ENCODER] = encoder_tmp; |
||||
enc[RIGHT_ENCODER].write(encoder_value[RIGHT_ENCODER]); |
||||
|
||||
/*
|
||||
// button handling
|
||||
if (but[i].fallingEdge()) // SELECT
|
||||
{ |
||||
#ifdef DEBUG |
||||
Serial.println(F("SELECT-R")); |
||||
#endif |
||||
menu_system.change_menu(sound_menu); |
||||
}*/ |
||||
break; |
||||
case LEFT_ENCODER: |
||||
// Encoder handling
|
||||
|
||||
// button handling
|
||||
break; |
||||
} |
||||
} |
||||
} |
||||
#endif |
@ -0,0 +1,563 @@ |
||||
/*
|
||||
MicroMDAEPiano |
||||
|
||||
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
||||
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
||||
|
||||
(c)2019 H. Wirtz <wirtz@parasitstudio.de> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation; either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include "config.h" |
||||
#include <Audio.h> |
||||
#include <Wire.h> |
||||
#include <SPI.h> |
||||
#include <MIDI.h> |
||||
#include <EEPROM.h> |
||||
#include "EEPROMAnything.h" |
||||
#include <limits.h> |
||||
#include "mdaEPiano.h" |
||||
#ifdef USE_XFADE_DATA |
||||
#include "mdaEPianoDataXfade.h" |
||||
#else |
||||
#include "mdaEPianoData.h" |
||||
#endif |
||||
#include <Bounce.h> |
||||
#include "Encoder4.h" |
||||
#include <LiquidCrystal_I2C.h> |
||||
#include <LiquidMenu.h> |
||||
#include "UI.hpp" |
||||
#include "midi_devices.hpp" |
||||
|
||||
//*************************************************************************************************
|
||||
//* GLOBAL VARIABLES
|
||||
//*************************************************************************************************
|
||||
|
||||
// Audio configuration
|
||||
AudioPlayQueue queue_r; |
||||
AudioPlayQueue queue_l; |
||||
AudioAnalyzePeak peak_r; |
||||
AudioAnalyzePeak peak_l; |
||||
AudioEffectFreeverb freeverb_r; |
||||
AudioEffectFreeverb freeverb_l; |
||||
AudioMixer4 mixer_r; |
||||
AudioMixer4 mixer_l; |
||||
AudioConnection patchCord0(queue_r, peak_r); |
||||
AudioConnection patchCord1(queue_l, peak_l); |
||||
AudioConnection patchCord4(queue_r, freeverb_r); |
||||
AudioConnection patchCord5(queue_l, freeverb_l); |
||||
AudioConnection patchCord6(queue_r, 0, mixer_r, 0); |
||||
AudioConnection patchCord7(queue_l, 0, mixer_l, 0); |
||||
AudioConnection patchCord8(freeverb_r, 0, mixer_r, 1); |
||||
AudioConnection patchCord9(freeverb_l, 0, mixer_l, 1); |
||||
#if defined(TEENSY_AUDIO_BOARD) |
||||
AudioOutputI2S i2s1; |
||||
AudioConnection patchCord110(mixer_r, 0, i2s1, 0); |
||||
AudioConnection patchCord111(mixer_l, 0, i2s1, 1); |
||||
AudioControlSGTL5000 sgtl5000_1; |
||||
#elif defined(TGA_AUDIO_BOARD) |
||||
AudioOutputI2S i2s1; |
||||
AudioAmplifier volume_r; |
||||
AudioAmplifier volume_l; |
||||
AudioConnection patchCord10(mixer_r, volume_r); |
||||
AudioConnection patchCord11(mixer_l, volume_l); |
||||
AudioConnection patchCord12(volume_r, 0, i2s1, 1); |
||||
AudioConnection patchCord13(volume_l, 0, i2s1, 0); |
||||
AudioControlWM8731master wm8731_1; |
||||
#else |
||||
AudioOutputPT8211 pt8211_1; |
||||
AudioAmplifier volume_r; |
||||
AudioAmplifier volume_l; |
||||
AudioConnection patchCord10(mixer_r, volume_r); |
||||
AudioConnection patchCord11(mixer_l, volume_l); |
||||
AudioConnection patchCord12(volume_r, 0, pt8211_1, 1); |
||||
AudioConnection patchCord13(volume_l, 0, pt8211_1, 0); |
||||
#endif |
||||
|
||||
// Objects
|
||||
mdaEPiano* ep; |
||||
LiquidCrystal_I2C lcd(LCD_I2C_ADDRESS, LCD_CHARS, LCD_LINES); |
||||
Encoder4 enc[2] = {Encoder4(ENC_L_PIN_A, ENC_L_PIN_B), Encoder4(ENC_R_PIN_A, ENC_R_PIN_B)}; |
||||
Bounce but[2] = {Bounce(BUT_L_PIN, BUT_DEBOUNCE_MS), Bounce(BUT_R_PIN, BUT_DEBOUNCE_MS)}; |
||||
|
||||
// Variables
|
||||
uint8_t midi_channel = DEFAULT_MIDI_CHANNEL; |
||||
uint32_t xrun = 0; |
||||
uint32_t overload = 0; |
||||
uint32_t peak = 0; |
||||
uint16_t render_time_max = 0; |
||||
elapsedMicros fill_audio_buffer; |
||||
elapsedMillis control_rate; |
||||
elapsedMillis autostore; |
||||
const uint16_t audio_block_time_us = 1000000 / (SAMPLE_RATE / AUDIO_BLOCK_SAMPLES); |
||||
config_t configuration = {0xffff, 0, VOLUME, 0.5f, DEFAULT_MIDI_CHANNEL}; |
||||
bool eeprom_update_flag = false; |
||||
#ifdef SHOW_CPU_LOAD_MSEC |
||||
elapsedMillis cpu_mem_millis; |
||||
#endif |
||||
|
||||
enum MDA_EP_PARAM { DECAY, RELEASE, HARDNESS, TREBLE, PAN_TREM, LFO_RATE, VELOCITY_SENSE, STEREO, MAX_POLY, TUNE, DETUNE, OVERDRIVE }; |
||||
|
||||
//*************************************************************************************************
|
||||
//* SETUP FUNCTION
|
||||
//*************************************************************************************************
|
||||
|
||||
void setup() |
||||
{ |
||||
//while (!Serial) ; // wait for Arduino Serial Monitor
|
||||
|
||||
pinMode(BUT_L_PIN, INPUT_PULLUP); |
||||
pinMode(BUT_R_PIN, INPUT_PULLUP); |
||||
|
||||
menu_init(); |
||||
|
||||
lcd.clear(); |
||||
lcd.setCursor(1, 0); |
||||
lcd.print(F("MicroMDAEpiano")); |
||||
lcd.setCursor(0, 1); |
||||
lcd.print(F("(c)parasiTstudio")); |
||||
|
||||
Serial.begin(SERIAL_SPEED); |
||||
delay(500); |
||||
|
||||
// Encoder setup
|
||||
enc[0].write(INITIAL_ENC_L_VALUE); |
||||
enc[1].write(INITIAL_ENC_R_VALUE); |
||||
|
||||
// Debug output
|
||||
Serial.println(F("MicroMDAEPiano based on https://sourceforge.net/projects/mda-vst")); |
||||
Serial.println(F("(c)2018/2019 H. Wirtz <wirtz@parasitstudio.de>")); |
||||
Serial.println(F("https://codeberg.org/dcoredump/MicroMDAEPiano")); |
||||
Serial.print(F("Data in PROGMEM: ")); |
||||
Serial.print(sizeof(epianoDataXfade), DEC); |
||||
Serial.println(F(" bytes")); |
||||
Serial.println(); |
||||
Serial.println(F("<setup start>")); |
||||
|
||||
// create EPiano object
|
||||
ep = new mdaEPiano(); |
||||
|
||||
// read initial EEPROM variables
|
||||
initial_values_from_eeprom(); |
||||
|
||||
setup_midi_devices(); |
||||
|
||||
// start audio card
|
||||
AudioNoInterrupts(); |
||||
AudioMemory(AUDIO_MEM); |
||||
|
||||
#ifdef TEENSY_AUDIO_BOARD |
||||
sgtl5000_1.enable(); |
||||
sgtl5000_1.dacVolumeRamp(); |
||||
sgtl5000_1.dacVolume(1.0); |
||||
//sgtl5000_1.dacVolumeRampLinear();
|
||||
sgtl5000_1.unmuteHeadphone(); |
||||
sgtl5000_1.unmuteLineout(); |
||||
sgtl5000_1.autoVolumeDisable(); // turn off AGC
|
||||
sgtl5000_1.unmuteHeadphone(); |
||||
sgtl5000_1.volume(0.5, 0.5); // Headphone volume
|
||||
sgtl5000_1.lineOutLevel(SGTL5000_LINEOUT_LEVEL); |
||||
sgtl5000_1.audioPostProcessorEnable(); |
||||
sgtl5000_1.autoVolumeControl(1, 1, 1, 0.9, 0.01, 0.05); |
||||
sgtl5000_1.autoVolumeEnable(); |
||||
sgtl5000_1.surroundSoundEnable(); |
||||
sgtl5000_1.surroundSound(7, 3); // Configures virtual surround width from 0 (mono) to 7 (widest). select may be set to 1 (disable), 2 (mono input) or 3 (stereo input).
|
||||
sgtl5000_1.enhanceBassEnable(); |
||||
sgtl5000_1.enhanceBass(1.0, 0.2, 1, 2); // Configures the bass enhancement by setting the levels of the original stereo signal and the bass-enhanced mono level which will be mixed together. The high-pass filter may be enabled (0) or bypassed (1).
|
||||
/* The cutoff frequency is specified as follows:
|
||||
value frequency |
||||
0 80Hz |
||||
1 100Hz |
||||
2 125Hz |
||||
3 150Hz |
||||
4 175Hz |
||||
5 200Hz |
||||
6 225Hz |
||||
*/ |
||||
//sgtl5000_1.eqBands(bass, mid_bass, midrange, mid_treble, treble);
|
||||
Serial.println(F("Teensy-Audio-Board enabled.")); |
||||
#elif defined(TGA_AUDIO_BOARD) |
||||
wm8731_1.enable(); |
||||
wm8731_1.volume(1.0); |
||||
Serial.println(F("TGA board enabled.")); |
||||
#else |
||||
Serial.println(F("PT8211 enabled.")); |
||||
#endif |
||||
|
||||
//set_volume(vol, vol_left, vol_right);
|
||||
set_volume(1.0, 0.5); |
||||
|
||||
#if defined (DEBUG) && defined (SHOW_CPU_LOAD_MSEC) |
||||
// Initialize processor and memory measurements
|
||||
AudioProcessorUsageMaxReset(); |
||||
AudioMemoryUsageMaxReset(); |
||||
#endif |
||||
|
||||
Serial.print(F("AUDIO_BLOCK_SAMPLES=")); |
||||
Serial.print(AUDIO_BLOCK_SAMPLES); |
||||
Serial.print(F(" (Time per block=")); |
||||
Serial.print(audio_block_time_us); |
||||
Serial.println(F("ms)")); |
||||
|
||||
ep->setParameter(DECAY, 0.5); |
||||
ep->setParameter(RELEASE, 0.5); |
||||
ep->setParameter(HARDNESS, 0.7); |
||||
ep->setParameter(TREBLE, 0.85); |
||||
ep->setParameter(DETUNE, 0.1); |
||||
ep->setParameter(VELOCITY_SENSE, 1.0); |
||||
ep->setParameter(STEREO, 0.7); |
||||
ep->setParameter(OVERDRIVE, 0.3); |
||||
|
||||
freeverb_r.roomsize(0.2); |
||||
freeverb_l.roomsize(0.2); |
||||
freeverb_r.damping(0.5); |
||||
freeverb_l.damping(0.5); |
||||
mixer_r.gain(0, 0.7); |
||||
mixer_l.gain(0, 0.7); |
||||
mixer_r.gain(1, 0.3); |
||||
mixer_l.gain(1, 0.3); |
||||
|
||||
AudioInterrupts(); |
||||
|
||||
Serial.println(F("<setup end>")); |
||||
|
||||
menu_system.update(); |
||||
menu_system.switch_focus(); |
||||
|
||||
#if defined (DEBUG) && defined (SHOW_CPU_LOAD_MSEC) |
||||
show_cpu_and_mem_usage(); |
||||
cpu_mem_millis = 0; |
||||
#endif |
||||
} |
||||
|
||||
//*************************************************************************************************
|
||||
//* MAIN LOOP
|
||||
//*************************************************************************************************
|
||||
|
||||
void loop() |
||||
{ |
||||
int16_t* audio_buffer_r; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(int16_t)
|
||||
int16_t* audio_buffer_l; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(int16_t)
|
||||
|
||||
// Main sound calculation
|
||||
if (queue_r.available() && queue_l.available() && fill_audio_buffer > audio_block_time_us - 10) |
||||
{ |
||||
fill_audio_buffer = 0; |
||||
|
||||
#if defined (DEBUG) && defined (SHOW_CPU_LOAD_MSEC) |
||||
if (cpu_mem_millis > SHOW_CPU_LOAD_MSEC) |
||||
{ |
||||
show_cpu_and_mem_usage(); |
||||
cpu_mem_millis = 0; |
||||
} |
||||
#endif |
||||
|
||||
audio_buffer_r = queue_r.getBuffer(); |
||||
if (audio_buffer_r == NULL) |
||||
{ |
||||
Serial.println(F("E: audio_buffer_r allocation problems!")); |
||||
} |
||||
audio_buffer_l = queue_l.getBuffer(); |
||||
if (audio_buffer_l == NULL) |
||||
{ |
||||
Serial.println(F("E: audio_buffer_l allocation problems!")); |
||||
} |
||||
|
||||
elapsedMicros t1; |
||||
ep->process(audio_buffer_l, audio_buffer_r); |
||||
uint32_t t2 = t1; |
||||
if (t2 > audio_block_time_us) // everything greater 2.9ms is a buffer underrun!
|
||||
xrun++; |
||||
if (t2 > render_time_max) |
||||
render_time_max = t2; |
||||
if (peak_r.available()) |
||||
{ |
||||
if (peak_r.read() > 1.00) |
||||
peak++; |
||||
} |
||||
if (peak_l.available()) |
||||
{ |
||||
if (peak_l.read() > 1.00) |
||||
peak++; |
||||
} |
||||
|
||||
queue_r.playBuffer(); |
||||
queue_l.playBuffer(); |
||||
} |
||||
|
||||
check_midi_devices(); |
||||
|
||||
// CONTROL-RATE-EVENT-HANDLING
|
||||
if (control_rate > CONTROL_RATE_MS) |
||||
{ |
||||
control_rate = 0; |
||||
|
||||
handle_ui(); |
||||
} |
||||
} |
||||
|
||||
//*************************************************************************************************
|
||||
//* PROGRAM FUNCTIONS
|
||||
//*************************************************************************************************
|
||||
void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity) |
||||
{ |
||||
if (checkMidiChannel(inChannel)) |
||||
{ |
||||
ep->noteOn(inNumber, inVelocity); |
||||
} |
||||
} |
||||
|
||||
void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity) |
||||
{ |
||||
if (checkMidiChannel(inChannel)) |
||||
{ |
||||
ep->noteOn(inNumber, 0); |
||||
} |
||||
} |
||||
|
||||
void handleControlChange(byte inChannel, byte inData1, byte inData2) |
||||
{ |
||||
if (checkMidiChannel(inChannel)) |
||||
{ |
||||
ep->processMidiController(inData1, inData2); |
||||
} |
||||
} |
||||
|
||||
void handleAfterTouch(byte inChannel, byte inPressure) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handlePitchBend(byte inChannel, int inPitch) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleProgramChange(byte inChannel, byte inProgram) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleSystemExclusive(byte *data, uint len) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleSystemExclusiveChunk(const byte *data, uint16_t len, bool last) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleTimeCodeQuarterFrame(byte data) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleAfterTouchPoly(byte inChannel, byte inNumber, byte inVelocity) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleSongSelect(byte inSong) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleTuneRequest(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleClock(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleStart(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleContinue(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleStop(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleActiveSensing(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleSystemReset(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
void handleRealTimeSystem(void) |
||||
{ |
||||
; |
||||
} |
||||
|
||||
bool checkMidiChannel(byte inChannel) |
||||
{ |
||||
// check for MIDI channel
|
||||
if (midi_channel == MIDI_CHANNEL_OMNI) |
||||
{ |
||||
return (true); |
||||
} |
||||
else if (inChannel != midi_channel) |
||||
{ |
||||
#ifdef DEBUG |
||||
Serial.print(F("Ignoring MIDI data on channel ")); |
||||
Serial.print(inChannel); |
||||
Serial.print(F("(listening on ")); |
||||
Serial.print(midi_channel); |
||||
Serial.println(F(")")); |
||||
#endif |
||||
return (false); |
||||
} |
||||
return (true); |
||||
} |
||||
|
||||
void set_volume(float v, float p) |
||||
{ |
||||
configuration.vol = v; |
||||
configuration.pan = p; |
||||
|
||||
#ifdef DEBUG |
||||
Serial.print(F("Setting volume: VOL=")); |
||||
Serial.print(v, DEC); |
||||
Serial.print(F("[")); |
||||
Serial.print(configuration.vol, DEC); |
||||
Serial.print(F("] PAN=")); |
||||
Serial.print(F("[")); |
||||
Serial.print(configuration.pan, DEC); |
||||
Serial.print(F("] ")); |
||||
Serial.print(pow(configuration.vol * sinf(configuration.pan * PI / 2), VOLUME_CURVE), 3); |
||||
Serial.print(F("/")); |
||||
Serial.println(pow(configuration.vol * cosf( configuration.pan * PI / 2), VOLUME_CURVE), 3); |
||||
#endif |
||||
|
||||
// http://files.csound-tutorial.net/floss_manual/Release03/Cs_FM_03_ScrapBook/b-panning-and-spatialization.html
|
||||
mixer_r.gain(0, sinf(p * PI / 2)); |
||||
mixer_l.gain(0, cosf(p * PI / 2)); |
||||
} |
||||
|
||||
/******************************************************************************
|
||||
EEPROM HELPER |
||||
******************************************************************************/ |
||||
|
||||
void initial_values_from_eeprom(void) |
||||
{ |
||||
uint32_t checksum; |
||||
config_t tmp_conf; |
||||
|
||||
EEPROM_readAnything(EEPROM_START_ADDRESS, tmp_conf); |
||||
checksum = crc32((byte*)&tmp_conf + 4, sizeof(tmp_conf) - 4); |
||||
|
||||
#ifdef DEBUG |
||||
Serial.print(F("EEPROM checksum: 0x")); |
||||
Serial.print(tmp_conf.checksum, HEX); |
||||
Serial.print(F(" / 0x")); |
||||
Serial.print(checksum, HEX); |
||||
#endif |
||||
|
||||
if (checksum != tmp_conf.checksum) |
||||
{ |
||||
#ifdef DEBUG |
||||
Serial.print(F(" - mismatch -> initializing EEPROM!")); |
||||
#endif |
||||
eeprom_update(); |
||||
} |
||||
else |
||||
{ |
||||
EEPROM_readAnything(EEPROM_START_ADDRESS, configuration); |
||||
Serial.print(F(" - OK, loading!")); |
||||
} |
||||
#ifdef DEBUG |
||||
Serial.println(); |
||||
#endif |
||||
} |
||||
|
||||
void eeprom_write(void) |
||||
{ |
||||
autostore = 0; |
||||
eeprom_update_flag = true; |
||||
} |
||||
|
||||
void eeprom_update(void) |
||||
{ |
||||
eeprom_update_flag = false; |
||||
configuration.checksum = crc32((byte*)&configuration + 4, sizeof(configuration) - 4); |
||||
EEPROM_writeAnything(EEPROM_START_ADDRESS, configuration); |
||||
Serial.println(F("Updating EEPROM with configuration data")); |
||||
} |
||||
|
||||
uint32_t crc32(byte * calc_start, uint16_t calc_bytes) // base code from https://www.arduino.cc/en/Tutorial/EEPROMCrc
|
||||
{ |
||||
const uint32_t crc_table[16] = |
||||
{ |
||||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, |
||||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, |
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, |
||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c |
||||
}; |
||||
uint32_t crc = ~0L; |
||||
|
||||
for (byte* index = calc_start ; index < (calc_start + calc_bytes) ; ++index) |
||||
{ |
||||
crc = crc_table[(crc ^ *index) & 0x0f] ^ (crc >> 4); |
||||
crc = crc_table[(crc ^ (*index >> 4)) & 0x0f] ^ (crc >> 4); |
||||
crc = ~crc; |
||||
} |
||||
|
||||
return (crc); |
||||
} |
||||
|
||||
//*************************************************************************************************
|
||||
//* DEBUG FUNCTIONS
|
||||
//*************************************************************************************************
|
||||
|
||||
#if defined (DEBUG) && defined (SHOW_CPU_LOAD_MSEC) |
||||
void show_cpu_and_mem_usage(void) |
||||
{ |
||||
Serial.print(F("CPU: ")); |
||||
Serial.print(AudioProcessorUsage(), DEC); |
||||
Serial.print(F(" CPU MAX: ")); |
||||
Serial.print(AudioProcessorUsageMax(), DEC); |
||||
Serial.print(F(" MEM: ")); |
||||
Serial.print(AudioMemoryUsage(), DEC); |
||||
Serial.print(F(" MEM MAX: ")); |
||||
Serial.print(AudioMemoryUsageMax(), DEC); |
||||
Serial.print(F(" RENDER_TIME_MAX: ")); |
||||
Serial.print(render_time_max, DEC); |
||||
Serial.print(F(" XRUN: ")); |
||||
Serial.print(xrun, DEC); |
||||
Serial.print(F(" OVERLOAD: ")); |
||||
Serial.print(overload, DEC); |
||||
Serial.print(F(" PEAK: ")); |
||||
Serial.print(peak, DEC); |
||||
Serial.println(); |
||||
AudioProcessorUsageMaxReset(); |
||||
AudioMemoryUsageMaxReset(); |
||||
render_time_max = 0; |
||||
} |
||||
#endif |
@ -0,0 +1,145 @@ |
||||
/*
|
||||
MicroMDAEPiano |
||||
|
||||
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
||||
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
||||
|
||||
(c)2019 H. Wirtz <wirtz@parasitstudio.de> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation; either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef UI_HPP_INCLUDED |
||||
#define UI_HPP_INCLUDED |
||||
|
||||
#include <LiquidCrystal_I2C.h> |
||||
#include <LiquidMenu.h> |
||||
#include <Bounce.h> |
||||
#include "Encoder4.h" |
||||
|
||||
extern LiquidCrystal_I2C lcd; |
||||
extern Encoder4 enc[2]; |
||||
extern Bounce but[2]; |
||||
|
||||
// Global vars
|
||||
uint8_t main_menu_selector = 0; |
||||
|
||||
enum { BUT_L, BUT_R }; |
||||
|
||||
// Main menu
|
||||
const char text10[] PROGMEM = "Favorites "; |
||||
const char text11[] PROGMEM = "Sound "; |
||||
const char text12[] PROGMEM = "Store "; |
||||
const char text13[] PROGMEM = "Info "; |
||||
LiquidLine main_line1(0, 0, text10); |
||||
LiquidLine main_line2(0, 1, text11); |
||||
LiquidLine main_line3(0, 1, text12); |
||||
LiquidLine main_line4(0, 1, text13); |
||||
LiquidScreen main_screen; |
||||
LiquidMenu main_menu(lcd); |
||||
|
||||
// Sound menu
|
||||
const char text20[] PROGMEM = "Decay "; |
||||
const char text21[] PROGMEM = "Release "; |
||||
const char text22[] PROGMEM = "Hardness "; |
||||
const char text23[] PROGMEM = "Treble "; |
||||
LiquidLine sound_line1(0, 0, text20); |
||||
LiquidLine sound_line2(0, 1, text21); |
||||
LiquidLine sound_line3(0, 1, text22); |
||||
LiquidLine sound_line4(0, 1, text23); |
||||
LiquidScreen sound_screen(sound_line1, sound_line2, sound_line3, sound_line4); |
||||
LiquidMenu sound_menu(lcd, sound_screen); |
||||
|
||||
// Info menu
|
||||
const char text40[] PROGMEM = "INFO "; |
||||
LiquidLine info_line1(0, 0, text40); |
||||
LiquidScreen info_screen(info_line1); |
||||
LiquidMenu info_menu(lcd, info_screen); |
||||
|
||||
// System menu
|
||||
LiquidSystem menu_system(main_menu, sound_menu, info_menu); |
||||
|
||||
void callback_favorites_function() { |
||||
Serial.println(F("callback_favorites_function")); |
||||
} |
||||
|
||||
void callback_sound_function() { |
||||
Serial.println(F("callback_sound_function")); |
||||
} |
||||
|
||||
void callback_store_function() { |
||||
Serial.println(F("callback_store_function")); |
||||
} |
||||
|
||||
void callback_info_function() { |
||||
Serial.println(F("callback_info_function")); |
||||
} |
||||
|
||||
void menu_init(void) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
// LCD display setup
|
||||
lcd.init(); |
||||
lcd.blink_off(); |
||||
lcd.cursor_off(); |
||||
lcd.backlight(); |
||||
//lcd.noAutoscroll();
|
||||
|
||||
main_screen.add_line(main_line1); |
||||
main_screen.add_line(main_line2); |
||||
main_screen.add_line(main_line3); |
||||
main_screen.add_line(main_line4); |
||||
|
||||
main_line1.attach_function(1, callback_favorites_function); |
||||
main_line2.attach_function(2, callback_sound_function); |
||||
main_line3.attach_function(3, callback_store_function); |
||||
main_line4.attach_function(4, callback_info_function); |
||||
|
||||
main_screen.set_displayLineCount(2); |
||||
main_menu.add_screen(main_screen); |
||||
|
||||
menu_system.update(); |
||||
|
||||
for (i = 0; i < NUM_ENCODER; i++) |
||||
but[i].update(); |
||||
|
||||
enc[1].write(0, 0, 99); |
||||
enc[1].write(1, 0, 4); |
||||
} |
||||
|
||||
void handle_ui(void) |
||||
{ |
||||
uint8_t i; |
||||
|
||||
for (i = 0; i < NUM_ENCODER; i++) |
||||
{ |
||||
but[i].update(); |
||||
|
||||
switch (i) |
||||
{ |
||||
case BUT_R: // SELECT
|
||||
if (but[i].fallingEdge()) |
||||
{ |
||||
#ifdef DEBUG |
||||
Serial.println(F("SELECT-R")); |
||||
#endif |
||||
menu_system.next_screen(); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
#endif |
@ -0,0 +1,50 @@ |
||||
/* ----------------------------------------------------------------------
|
||||
* https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621
|
||||
* Fast approximation to the log2() function. It uses a two step |
||||
* process. First, it decomposes the floating-point number into |
||||
* a fractional component F and an exponent E. The fraction component |
||||
* is used in a polynomial approximation and then the exponent added |
||||
* to the result. A 3rd order polynomial is used and the result |
||||
* when computing db20() is accurate to 7.984884e-003 dB. |
||||
** ------------------------------------------------------------------- */ |
||||
|
||||
float log2f_approx_coeff[4] = {1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f}; |
||||
|
||||
float log2f_approx(float X) |
||||
{ |
||||
float *C = &log2f_approx_coeff[0]; |
||||
float Y; |
||||
float F; |
||||
int E; |
||||
|
||||
// This is the approximation to log2()
|
||||
F = frexpf(fabsf(X), &E); |
||||
|
||||
// Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E;
|
||||
Y = *C++; |
||||
Y *= F; |
||||
Y += (*C++); |
||||
Y *= F; |
||||
Y += (*C++); |
||||
Y *= F; |
||||
Y += (*C++); |
||||
Y += E; |
||||
return(Y); |
||||
} |
||||
|
||||
// https://codingforspeed.com/using-faster-exponential-approximation/
|
||||
inline float expf_approx(float x) { |
||||
x = 1.0f + x / 1024; |
||||
x *= x; x *= x; x *= x; x *= x; |
||||
x *= x; x *= x; x *= x; x *= x; |
||||
x *= x; x *= x; |
||||
return x; |
||||
} |
||||
|
||||
inline float unitToDb(float unit) { |
||||
return 6.02f * log2f_approx(unit); |
||||
} |
||||
|
||||
inline float dbToUnit(float db) { |
||||
return expf_approx(db * 2.302585092994046f * 0.05f); |
||||
} |
@ -0,0 +1,370 @@ |
||||
/*
|
||||
MicroMDAEPiano |
||||
|
||||
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
||||
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation; either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include "config.h" |
||||
#ifdef USE_XFADE_DATA |
||||
#include "mdaEPianoDataXfade.h" |
||||
#else |
||||
#include "mdaEPianoData.h" |
||||
#endif |
||||
#include "mdaEPiano.h" |
||||
#include <stdio.h> |
||||
#include <math.h> |
||||
|
||||
mdaEPiano::mdaEPiano() // mdaEPiano::mdaEPiano(audioMasterCallback audioMaster) : AudioEffectX(audioMaster, NPROGS, NPARAMS)
|
||||
{ |
||||
Fs = SAMPLE_RATE; iFs = 1.0f / Fs; //just in case...
|
||||
|
||||
programs = new mdaEPianoProgram[NPROGS]; |
||||
if (programs) |
||||
{ |
||||
//fill patches...
|
||||
int32_t i = 0; |
||||
fillpatch(i++, "Default", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); |
||||
fillpatch(i++, "Bright", 0.500f, 0.500f, 1.000f, 0.800f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.500f); |
||||
fillpatch(i++, "Mellow", 0.500f, 0.500f, 0.000f, 0.000f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); |
||||
fillpatch(i++, "Autopan", 0.500f, 0.500f, 0.500f, 0.500f, 0.250f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); |
||||
fillpatch(i++, "Tremolo", 0.500f, 0.500f, 0.500f, 0.500f, 0.750f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.246f, 0.000f); |
||||
fillpatch(i++, "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); |
||||
fillpatch(i++, "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); |
||||
fillpatch(i++, "(default)", 0.500f, 0.500f, 0.500f, 0.500f, 0.500f, 0.650f, 0.250f, 0.500f, 1.0f, 0.500f, 0.146f, 0.000f); |
||||
setProgram(0); |
||||
} |
||||
|
||||
waves = (short*)epianoDataXfade; |
||||
|
||||
//Waveform data and keymapping
|
||||
kgrp[ 0].root = 36; kgrp[ 0].high = 39; //C1
|
||||
kgrp[ 3].root = 43; kgrp[ 3].high = 45; //G1
|
||||
kgrp[ 6].root = 48; kgrp[ 6].high = 51; //C2
|
||||
kgrp[ 9].root = 55; kgrp[ 9].high = 57; //G2
|
||||
kgrp[12].root = 60; kgrp[12].high = 63; //C3
|
||||
kgrp[15].root = 67; kgrp[15].high = 69; //G3
|
||||
kgrp[18].root = 72; kgrp[18].high = 75; //C4
|
||||
kgrp[21].root = 79; kgrp[21].high = 81; //G4
|
||||
kgrp[24].root = 84; kgrp[24].high = 87; //C5
|
||||
kgrp[27].root = 91; kgrp[27].high = 93; //G5
|
||||
kgrp[30].root = 96; kgrp[30].high = 999; //C6
|
||||
|
||||
kgrp[0].pos = 0; kgrp[0].end = 8476; kgrp[0].loop = 4400; |
||||
kgrp[1].pos = 8477; kgrp[1].end = 16248; kgrp[1].loop = 4903; |
||||
kgrp[2].pos = 16249; kgrp[2].end = 34565; kgrp[2].loop = 6398; |
||||
kgrp[3].pos = 34566; kgrp[3].end = 41384; kgrp[3].loop = 3938; |
||||
kgrp[4].pos = 41385; kgrp[4].end = 45760; kgrp[4].loop = 1633; //was 1636;
|
||||
kgrp[5].pos = 45761; kgrp[5].end = 65211; kgrp[5].loop = 5245; |
||||
kgrp[6].pos = 65212; kgrp[6].end = 72897; kgrp[6].loop = 2937; |
||||
kgrp[7].pos = 72898; kgrp[7].end = 78626; kgrp[7].loop = 2203; //was 2204;
|
||||
kgrp[8].pos = 78627; kgrp[8].end = 100387; kgrp[8].loop = 6368; |
||||
kgrp[9].pos = 100388; kgrp[9].end = 116297; kgrp[9].loop = 10452; |
||||
kgrp[10].pos = 116298; kgrp[10].end = 127661; kgrp[10].loop = 5217; //was 5220;
|
||||
kgrp[11].pos = 127662; kgrp[11].end = 144113; kgrp[11].loop = 3099; |
||||
kgrp[12].pos = 144114; kgrp[12].end = 152863; kgrp[12].loop = 4284; |
||||
kgrp[13].pos = 152864; kgrp[13].end = 173107; kgrp[13].loop = 3916; |
||||
kgrp[14].pos = 173108; kgrp[14].end = 192734; kgrp[14].loop = 2937; |
||||
kgrp[15].pos = 192735; kgrp[15].end = 204598; kgrp[15].loop = 4732; |
||||
kgrp[16].pos = 204599; kgrp[16].end = 218995; kgrp[16].loop = 4733; |
||||
kgrp[17].pos = 218996; kgrp[17].end = 233801; kgrp[17].loop = 2285; |
||||
kgrp[18].pos = 233802; kgrp[18].end = 248011; kgrp[18].loop = 4098; |
||||
kgrp[19].pos = 248012; kgrp[19].end = 265287; kgrp[19].loop = 4099; |
||||
kgrp[20].pos = 265288; kgrp[20].end = 282255; kgrp[20].loop = 3609; |
||||
kgrp[21].pos = 282256; kgrp[21].end = 293776; kgrp[21].loop = 2446; |
||||
kgrp[22].pos = 293777; kgrp[22].end = 312566; kgrp[22].loop = 6278; |
||||
kgrp[23].pos = 312567; kgrp[23].end = 330200; kgrp[23].loop = 2283; |
||||
kgrp[24].pos = 330201; kgrp[24].end = 348889; kgrp[24].loop = 2689; |
||||
kgrp[25].pos = 348890; kgrp[25].end = 365675; kgrp[25].loop = 4370; |
||||
kgrp[26].pos = 365676; kgrp[26].end = 383661; kgrp[26].loop = 5225; |
||||
kgrp[27].pos = 383662; kgrp[27].end = 393372; kgrp[27].loop = 2811; |
||||
kgrp[28].pos = 383662; kgrp[28].end = 393372; kgrp[28].loop = 2811; //ghost
|
||||
kgrp[29].pos = 393373; kgrp[29].end = 406045; kgrp[29].loop = 4522; |
||||
kgrp[30].pos = 406046; kgrp[30].end = 414486; kgrp[30].loop = 2306; |
||||
kgrp[31].pos = 406046; kgrp[31].end = 414486; kgrp[31].loop = 2306; //ghost
|
||||
kgrp[32].pos = 414487; kgrp[32].end = 422408; kgrp[32].loop = 2169; |
||||
|
||||
//initialise...
|
||||
for (int32_t v = 0; v < NVOICES; v++) |
||||
{ |
||||
voice[v].env = 0.0f; |
||||
voice[v].dec = 0.99f; //all notes off
|
||||
} |
||||
volume = 0.2f; |
||||
muff = 160.0f; |
||||
sustain = activevoices = 0; |
||||
tl = tr = lfo0 = dlfo = 0.0f; |
||||
lfo1 = 1.0f; |
||||
|
||||
guiUpdate = 0; |
||||
|
||||
vol = VOLUME; |
||||
update(); |
||||
// suspend();
|
||||
} |
||||
|
||||
void mdaEPiano::update() //parameter change
|
||||
{ |
||||
float * param = programs[curProgram].param; |
||||
size = (int32_t)(12.0f * param[2] - 6.0f); |
||||
|
||||
treb = 4.0f * param[3] * param[3] - 1.0f; //treble gain
|
||||
if (param[3] > 0.5f) tfrq = 14000.0f; else tfrq = 5000.0f; //treble freq
|
||||
tfrq = 1.0f - (float)exp(-iFs * tfrq); |
||||
|
||||
rmod = lmod = param[4] + param[4] - 1.0f; //lfo depth
|
||||
if (param[4] < 0.5f) rmod = -rmod; |
||||
|
||||
dlfo = 6.283f * iFs * (float)exp(6.22f * param[5] - 2.61f); //lfo rate
|
||||
|
||||
velsens = 1.0f + param[6] + param[6]; |
||||
if (param[6] < 0.25f) velsens -= 0.75f - 3.0f * param[6]; |
||||
|
||||
width = 0.03f * param[7]; |
||||
fine = param[9] - 0.5f; |
||||
random = 0.077f * param[10] * param[10]; |
||||
stretch = 0.0f; //0.000434f * (param[11] - 0.5f); parameter re-used for overdrive!
|
||||
overdrive = 1.8f * param[11]; |
||||
} |
||||
|
||||
|
||||
void mdaEPiano::resume() |
||||
{ |
||||
Fs = SAMPLE_RATE; |
||||
iFs = 1.0f / Fs; |
||||
dlfo = 6.283f * iFs * (float)exp(6.22f * programs[curProgram].param[5] - 2.61f); //lfo rate
|
||||
} |
||||
|
||||
|
||||
mdaEPiano::~mdaEPiano () //destroy any buffers...
|
||||
{ |
||||
if (programs) delete [] programs; |
||||
} |
||||
|
||||
|
||||
void mdaEPiano::setProgram(int32_t program) |
||||
{ |
||||
curProgram = program; |
||||
update(); |
||||
} |
||||
|
||||
|
||||
void mdaEPiano::setParameter(int32_t index, float value) |
||||
{ |
||||
programs[curProgram].param[index] = value; |
||||
update(); |
||||
} |
||||
|
||||
|
||||
void mdaEPiano::fillpatch(int32_t p, char *name, float p0, float p1, float p2, float p3, float p4, |
||||
float p5, float p6, float p7, float p8, float p9, float p10, float p11) |
||||
{ |
||||
strcpy(programs[p].name, name); |
||||
programs[p].param[0] = p0; programs[p].param[1] = p1; |
||||
programs[p].param[2] = p2; programs[p].param[3] = p3; |
||||
programs[p].param[4] = p4; programs[p].param[5] = p5; |
||||
programs[p].param[6] = p6; programs[p].param[7] = p7; |
||||
programs[p].param[8] = p8; programs[p].param[9] = p9; |
||||
programs[p].param[10] = p10; programs[p].param[11] = p11; |
||||
} |
||||
|
||||
float mdaEPiano::getParameter(int32_t index) { |
||||
return programs[curProgram].param[index]; |
||||
} |
||||
|
||||
void mdaEPiano::process(int16_t* outputs_r, int16_t* outputs_l) |
||||
{ |
||||
int16_t v; |
||||
float x, l, r, od = overdrive; |
||||
int32_t i; |
||||
int16_t frame; |
||||
|
||||
for (frame = 0; frame < AUDIO_BLOCK_SAMPLES; frame++) |
||||
{ |
||||
VOICE *V = voice; |
||||
l = r = 0.0f; |
||||
for (v = 0; v < activevoices; v++) |
||||
{ |
||||
V->frac += V->delta; //integer-based linear interpolation
|
||||
V->pos += V->frac >> 16; |
||||
V->frac &= 0xFFFF; |
||||
if (V->pos > V->end) |
||||
V->pos -= V->loop; |
||||
i = waves[V->pos] + ((V->frac * (waves[V->pos + 1] - waves[V->pos])) >> 16); |
||||
x = V->env * static_cast<float>(i) / 32768.0f; |
||||
|
||||
V->env = V->env * V->dec; //envelope
|
||||
if (x > 0.0f) |
||||
{ |
||||
x -= od * x * x; //overdrive
|
||||
if (x < -V->env) x = -V->env; |
||||
} |
||||
l += V->outl * x; |
||||
r += V->outr * x; |
||||
|
||||
V++; |
||||
} |
||||
|
||||
tl += tfrq * (l - tl); //treble boost
|
||||
tr += tfrq * (r - tr); |
||||
r += treb * (r - tr); |
||||
l += treb * (l - tl); |
||||
|
||||
lfo0 += dlfo * lfo1; //LFO for tremolo and autopan
|
||||
lfo1 -= dlfo * lfo0; |
||||
l += l * lmod * lfo1; |
||||
r += r * rmod * lfo1; //worth making all these local variables?
|
||||
|
||||
r *= 0.5; |
||||
l *= 0.5; |
||||
|
||||
if (r > 1.0) |
||||
r = 1.0; |
||||
else if (r < -1.0) |
||||
r = -1.0; |
||||
if (l > 1.0) |
||||
l = 1.0; |
||||
else if (l < -1.0) |
||||
l = -1.0; |
||||
outputs_l[frame] = static_cast<int16_t>(l * configuration.vol * 0x7fff) >> REDUCE_LOUDNESS; |
||||
outputs_r[frame] = static_cast<int16_t>(r * configuration.vol * 0x7fff) >> REDUCE_LOUDNESS; |
||||
} |
||||
|
||||
if (fabs(tl) < 1.0e-10) tl = 0.0f; //anti-denormal
|
||||
if (fabs(tr) < 1.0e-10) tr = 0.0f; |
||||
|
||||
for (v = 0; v < activevoices; v++) |
||||
if (voice[v].env < SILENCE) |
||||
voice[v] = voice[--activevoices]; |
||||
} |
||||
|
||||
void mdaEPiano::noteOn(int32_t note, int32_t velocity) |
||||
{ |
||||
float * param = programs[curProgram].param; |
||||
float l = 99.0f; |
||||
int32_t v, vl = 0, k, s; |
||||
|
||||
if (velocity > 0) |
||||
{ |
||||
if (activevoices < NVOICES) //add a note
|
||||
{ |
||||
vl = activevoices; |
||||
activevoices++; |
||||
voice[vl].f0 = voice[vl].f1 = 0.0f; |
||||
} |
||||
else //steal a note
|
||||
{ |
||||
for (v = 0; v < NVOICES; v++) //find quietest voice
|
||||
{ |
||||
if (voice[v].env < l) { |
||||
l = voice[v].env; |
||||
vl = v; |
||||
} |
||||
} |
||||
} |
||||
|
||||
k = (note - 60) * (note - 60); |
||||
l = fine + random * ((float)(k % 13) - 6.5f); //random & fine tune
|
||||
if (note > 60) l += stretch * (float)k; //stretch
|
||||
|
||||
s = size; |
||||
//if(velocity > 40) s += (int32_t)(sizevel * (float)(velocity - 40)); - no velocity to hardness in ePiano
|
||||
|
||||
k = 0; |
||||
while (note > (kgrp[k].high + s)) k += 3; //find keygroup
|
||||
l += (float)(note - kgrp[k].root); //pitch
|
||||
l = 32000.0f * iFs * (float)exp(0.05776226505 * l); |
||||
voice[vl].delta = (int32_t)(65536.0f * l); |
||||
voice[vl].frac = 0; |
||||
|
||||
if (velocity > 48) k++; //mid velocity sample
|
||||
if (velocity > 80) k++; //high velocity sample
|
||||
voice[vl].pos = kgrp[k].pos; |
||||
voice[vl].end = kgrp[k].end - 1; |
||||
voice[vl].loop = kgrp[k].loop; |
||||
|
||||
voice[vl].env = (3.0f + 2.0f * velsens) * (float)pow(0.0078f * velocity, velsens); //velocity
|
||||
|
||||
if (note > 60) voice[vl].env *= (float)exp(0.01f * (float)(60 - note)); //new! high notes quieter
|
||||
|
||||
l = 50.0f + param[4] * param[4] * muff + muffvel * (float)(velocity - 64); //muffle
|
||||
if (l < (55.0f + 0.4f * (float)note)) l = 55.0f + 0.4f * (float)note; |
||||
if (l > 210.0f) l = 210.0f; |
||||
voice[vl].ff = l * l * iFs; |
||||
|
||||
voice[vl].note = note; //note->pan
|
||||
if (note < 12) note = 12; |
||||
if (note > 108) note = 108; |
||||
l = volume; |
||||
voice[vl].outr = l + l * width * (float)(note - 60); |
||||
voice[vl].outl = l + l - voice[vl].outr; |
||||
|
||||
if (note < 44) note = 44; //limit max decay length
|
||||
voice[vl].dec = (float)exp(-iFs * exp(-1.0 + 0.03 * (double)note - 2.0f * param[0])); |
||||
} |
||||
else //note off
|
||||
{ |
||||
for (v = 0; v < NVOICES; v++) if (voice[v].note == note) //any voices playing that note?
|
||||
{ |
||||
if (sustain == 0) |
||||
{ |
||||
voice[v].dec = (float)exp(-iFs * exp(6.0 + 0.01 * (double)note - 5.0 * param[1])); |
||||
} |
||||
else voice[v].note = SUSTAIN; |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool mdaEPiano::processMidiController(uint8_t data1, uint8_t data2) |
||||
{ |
||||
float* param = programs[curProgram].param; |
||||
|
||||
switch (data1) |
||||
{ |
||||
case 0x01: //mod wheel
|
||||
modwhl = 0.0078f * (float)(data2); |
||||
if (modwhl > 0.05f) //over-ride pan/trem depth
|
||||
{ |
||||
rmod = lmod = modwhl; //lfo depth
|
||||
if (param[4] < 0.5f) rmod = -rmod; |
||||
} |
||||
break; |
||||
|
||||
case 0x07: //volume
|
||||
volume = 0.00002f * (float)(data2 * data2); |
||||
break; |
||||
|
||||
case 0x40: //sustain pedal
|
||||
case 0x42: //sustenuto pedal
|
||||
sustain = data2 & 0x40; |
||||
if (sustain == 0) |
||||
{ |
||||
noteOn(SUSTAIN, 0); //end all sustained notes
|
||||
} |
||||
break; |
||||
|
||||
default: //all notes off
|
||||
if (data1 > 0x7A) |
||||
{ |
||||
for (int32_t v = 0; v < NVOICES; v++) voice[v].dec = 0.99f; |
||||
sustain = 0; |
||||
muff = 160.0f; |
||||
} |
||||
break; |
||||
} |
||||
return (true); |
||||
} |
@ -0,0 +1,117 @@ |
||||
/*
|
||||
MicroMDAEPiano |
||||
|
||||
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
||||
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation; either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef __mdaEPiano__ |
||||
#define __mdaEPiano__ |
||||
|
||||
#include <Audio.h> |
||||
#include <Arduino.h> |
||||
#include <string.h> |
||||
#include "config.h" |
||||
|
||||
#define NPARAMS 12 //number of parameters
|
||||
#define NPROGS 8 //number of programs
|
||||
#define NOUTS 2 //number of outputs
|
||||
#define SUSTAIN 128 |
||||
#define SILENCE 0.0001f //voice choking
|
||||
#define WAVELEN 422414 //wave data bytes
|
||||
|
||||
extern config_t configuration; |
||||
|
||||
class mdaEPianoProgram |
||||
{ |
||||
friend class mdaEPiano; |
||||
private: |
||||
float param[NPARAMS]; |
||||
char name[24]; |
||||
}; |
||||
|
||||
struct VOICE //voice state
|
||||
{ |
||||
int32_t delta; //sample playback
|
||||
int32_t frac; |
||||
int32_t pos; |
||||
int32_t end; |
||||
int32_t loop; |
||||
|
||||
float env; //envelope
|
||||
float dec; |
||||
|
||||
float f0; //first-order LPF
|
||||
float f1; |
||||
float ff; |
||||
|
||||
float outl; |
||||
float outr; |
||||
int32_t note; //remember what note triggered this
|
||||
}; |
||||
|
||||
|
||||
struct KGRP //keygroup
|
||||
{ |
||||
int32_t root; //MIDI root note
|
||||
int32_t high; //highest note
|
||||
int32_t pos; |
||||
int32_t end; |
||||
int32_t loop; |
||||
}; |
||||
|
||||
class mdaEPiano |
||||
{ |
||||
public: |
||||
mdaEPiano(); |
||||
~mdaEPiano(); |
||||
|
||||
virtual void process(int16_t *outputs_r, int16_t *outputs_l); |
||||
void noteOn(int32_t note, int32_t velocity); |
||||
virtual bool processMidiController(uint8_t data1, uint8_t data2); |
||||
virtual void setProgram(int32_t program); |
||||
virtual void setParameter(int32_t index, float value); |
||||
virtual float getParameter(int32_t index); |
||||
virtual void resume(); |
||||
|
||||
int32_t guiUpdate; |
||||
void guiGetDisplay(int32_t index, char *label); |
||||
|
||||
private: |
||||
void update(); //my parameter update
|
||||
void fillpatch(int32_t p, char *name, float p0, float p1, float p2, float p3, float p4, |
||||
float p5, float p6, float p7, float p8, float p9, float p10, float p11); |
||||
|
||||
mdaEPianoProgram* programs; |
||||
float Fs, iFs; |
||||
|
||||
///global internal variables
|
||||
KGRP kgrp[34]; |
||||
VOICE voice[NVOICES]; |
||||
int32_t activevoices; |
||||
short *waves; |
||||
float width; |
||||
int32_t size, sustain; |
||||
float lfo0, lfo1, dlfo, lmod, rmod; |
||||
float treb, tfrq, tl, tr; |
||||
float tune, fine, random, stretch, overdrive; |
||||
float muff, muffvel, sizevel, velsens, volume, modwhl; |
||||
float vol; |
||||
uint8_t curProgram; |
||||
}; |
||||
|
||||
#endif |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,120 @@ |
||||
/*
|
||||
MicroMDAEPiano |
||||
|
||||
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
||||
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
||||
|
||||
(c)2019 H. Wirtz <wirtz@parasitstudio.de> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation; either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
/*************************************************
|
||||
* MIDI note values |
||||
*************************************************/ |
||||
|
||||
#ifndef _MIDINOTES_H |
||||
#define _MIDINOTES_H |
||||
|
||||
#define MIDI_A0 21 |
||||
#define MIDI_AIS0 22 |
||||
#define MIDI_B0 23 |
||||
#define MIDI_C1 24 |
||||
#define MIDI_CIS1 25 |
||||
#define MIDI_D1 26 |
||||
#define MIDI_DIS1 27 |
||||
#define MIDI_E1 28 |
||||
#define MIDI_F1 29 |
||||
#define MIDI_FIS1 30 |
||||
#define MIDI_G1 31 |
||||
#define MIDI_GIS1 32 |
||||
#define MIDI_A1 33 |
||||
#define MIDI_AIS1 34 |
||||
#define MIDI_B1 35 |
||||
#define MIDI_C2 36 |
||||
#define MIDI_CIS2 37 |
||||
#define MIDI_D2 38 |
||||
#define MIDI_DIS2 39 |
||||
#define MIDI_E2 40 |
||||
#define MIDI_F2 41 |
||||
#define MIDI_FIS2 42 |
||||
#define MIDI_G2 43 |
||||
#define MIDI_GIS2 44 |
||||
#define MIDI_A2 45 |
||||
#define MIDI_AIS2 46 |
||||
#define MIDI_B2 47 |
||||
#define MIDI_C3 48 |
||||
#define MIDI_CIS3 49 |
||||
#define MIDI_D3 50 |
||||
#define MIDI_DIS3 51 |
||||
#define MIDI_E3 52 |
||||
#define MIDI_F3 53 |
||||
#define MIDI_FIS3 54 |
||||
#define MIDI_G3 55 |
||||
#define MIDI_GIS3 56 |
||||
#define MIDI_A3 57 |
||||
#define MIDI_AIS3 58 |
||||
#define MIDI_B3 59 |
||||
#define MIDI_C4 60 |
||||
#define MIDI_CIS4 61 |
||||
#define MIDI_D4 62 |
||||
#define MIDI_DIS4 63 |
||||
#define MIDI_E4 64 |
||||
#define MIDI_F4 65 |
||||
#define MIDI_FIS4 66 |
||||
#define MIDI_G4 67 |
||||
#define MIDI_GIS4 68 |
||||
#define MIDI_A4 69 |
||||
#define MIDI_AIS4 70 |
||||
#define MIDI_B4 71 |
||||
#define MIDI_C5 72 |
||||
#define MIDI_CIS5 73 |
||||
#define MIDI_D5 74 |
||||
#define MIDI_DIS5 75 |
||||
#define MIDI_E5 76 |
||||
#define MIDI_F5 77 |
||||
#define MIDI_FIS5 78 |
||||
#define MIDI_G5 79 |
||||
#define MIDI_GIS5 80 |
||||
#define MIDI_A5 81 |
||||
#define MIDI_AIS5 82 |
||||
#define MIDI_B5 83 |
||||
#define MIDI_C6 84 |
||||
#define MIDI_CIS6 85 |
||||
#define MIDI_D6 86 |
||||
#define MIDI_DIS6 87 |
||||
#define MIDI_E6 88 |
||||
#define MIDI_F6 89 |
||||
#define MIDI_FIS6 90 |
||||
#define MIDI_G6 91 |
||||
#define MIDI_GIS6 92 |
||||
#define MIDI_A6 93 |
||||
#define MIDI_AIS6 94 |
||||
#define MIDI_B6 95 |
||||
#define MIDI_C7 96 |
||||
#define MIDI_CIS7 97 |
||||
#define MIDI_D7 98 |
||||
#define MIDI_DIS7 99 |
||||
#define MIDI_E7 100 |
||||
#define MIDI_F7 101 |
||||
#define MIDI_FIS7 102 |
||||
#define MIDI_G7 103 |
||||
#define MIDI_GIS7 104 |
||||
#define MIDI_A7 105 |
||||
#define MIDI_AIS7 106 |
||||
#define MIDI_B7 107 |
||||
#define MIDI_C8 108 |
||||
|
||||
#endif |
@ -0,0 +1,11 @@ |
||||
#include "usb_names.h" |
||||
#define MIDI_NAME {'M','i','c','r','o','M','D','A','E','p','i','a','n','o'} |
||||
#define MIDI_NAME_LEN 14 |
||||
|
||||
// Do not change this part. This exact format is required by USB.
|
||||
|
||||
struct usb_string_descriptor_struct usb_string_product_name = { |
||||
2 + MIDI_NAME_LEN * 2, |
||||
3, |
||||
MIDI_NAME |
||||
}; |
@ -0,0 +1,9 @@ |
||||
#!/bin/bash |
||||
|
||||
sed -e 's/^.\+Arduino\.h.\+$/#include <stdint.h>/' ../mdaEPianoData.h >mdaEPianoData.h |
||||
sed -i 's/PROGMEM//' mdaEPianoData.h |
||||
sed -i 's/const//' mdaEPianoData.h |
||||
gcc -o xfade_generator xfade_generator.c |
||||
rm mdaEPianoData.h |
||||
./xfade_generator >../mdaEPianoData_xfade.h |
||||
rm xfade_generator |
@ -0,0 +1,100 @@ |
||||
#include "mdaEPianoData.h" |
||||
#include <stdio.h> |
||||
#include <math.h> |
||||
#include <stdint.h> |
||||
|
||||
struct KGRP //keygroup
|
||||
{ |
||||
int32_t root; //MIDI root note
|
||||
int32_t high; //highest note
|
||||
int32_t pos; |
||||
int32_t end; |
||||
int32_t loop; |
||||
}; |
||||
|
||||
void main(void) |
||||
{ |
||||
struct KGRP kgrp[34]; |
||||
int16_t *waves = (int16_t*)epianoData; |
||||
|
||||
//Waveform data and keymapping
|
||||
kgrp[ 0].root = 36; kgrp[ 0].high = 39; //C1
|
||||
kgrp[ 3].root = 43; kgrp[ 3].high = 45; //G1
|
||||
kgrp[ 6].root = 48; kgrp[ 6].high = 51; //C2
|
||||
kgrp[ 9].root = 55; kgrp[ 9].high = 57; //G2
|
||||
kgrp[12].root = 60; kgrp[12].high = 63; //C3
|
||||
kgrp[15].root = 67; kgrp[15].high = 69; //G3
|
||||
kgrp[18].root = 72; kgrp[18].high = 75; //C4
|
||||
kgrp[21].root = 79; kgrp[21].high = 81; //G4
|
||||
kgrp[24].root = 84; kgrp[24].high = 87; //C5
|
||||
kgrp[27].root = 91; kgrp[27].high = 93; //G5
|
||||
kgrp[30].root = 96; kgrp[30].high = 999; //C6
|
||||
|
||||
kgrp[0].pos = 0; kgrp[0].end = 8476; kgrp[0].loop = 4400; |
||||
kgrp[1].pos = 8477; kgrp[1].end = 16248; kgrp[1].loop = 4903; |
||||
kgrp[2].pos = 16249; kgrp[2].end = 34565; kgrp[2].loop = 6398; |
||||
kgrp[3].pos = 34566; kgrp[3].end = 41384; kgrp[3].loop = 3938; |
||||
kgrp[4].pos = 41385; kgrp[4].end = 45760; kgrp[4].loop = 1633; //was 1636;
|
||||
kgrp[5].pos = 45761; kgrp[5].end = 65211; kgrp[5].loop = 5245; |
||||
kgrp[6].pos = 65212; kgrp[6].end = 72897; kgrp[6].loop = 2937; |
||||
kgrp[7].pos = 72898; kgrp[7].end = 78626; kgrp[7].loop = 2203; //was 2204;
|
||||
kgrp[8].pos = 78627; kgrp[8].end = 100387; kgrp[8].loop = 6368; |
||||
kgrp[9].pos = 100388; kgrp[9].end = 116297; kgrp[9].loop = 10452; |
||||
kgrp[10].pos = 116298; kgrp[10].end = 127661; kgrp[10].loop = 5217; //was 5220;
|
||||
kgrp[11].pos = 127662; kgrp[11].end = 144113; kgrp[11].loop = 3099; |
||||
kgrp[12].pos = 144114; kgrp[12].end = 152863; kgrp[12].loop = 4284; |
||||
kgrp[13].pos = 152864; kgrp[13].end = 173107; kgrp[13].loop = 3916; |
||||
kgrp[14].pos = 173108; kgrp[14].end = 192734; kgrp[14].loop = 2937; |
||||
kgrp[15].pos = 192735; kgrp[15].end = 204598; kgrp[15].loop = 4732; |
||||
kgrp[16].pos = 204599; kgrp[16].end = 218995; kgrp[16].loop = 4733; |
||||
kgrp[17].pos = 218996; kgrp[17].end = 233801; kgrp[17].loop = 2285; |
||||
kgrp[18].pos = 233802; kgrp[18].end = 248011; kgrp[18].loop = 4098; |
||||
kgrp[19].pos = 248012; kgrp[19].end = 265287; kgrp[19].loop = 4099; |
||||
kgrp[20].pos = 265288; kgrp[20].end = 282255; kgrp[20].loop = 3609; |
||||
kgrp[21].pos = 282256; kgrp[21].end = 293776; kgrp[21].loop = 2446; |
||||
kgrp[22].pos = 293777; kgrp[22].end = 312566; kgrp[22].loop = 6278; |
||||
kgrp[23].pos = 312567; kgrp[23].end = 330200; kgrp[23].loop = 2283; |
||||
kgrp[24].pos = 330201; kgrp[24].end = 348889; kgrp[24].loop = 2689; |
||||
kgrp[25].pos = 348890; kgrp[25].end = 365675; kgrp[25].loop = 4370; |
||||
kgrp[26].pos = 365676; kgrp[26].end = 383661; kgrp[26].loop = 5225; |
||||
kgrp[27].pos = 383662; kgrp[27].end = 393372; kgrp[27].loop = 2811; |
||||
kgrp[28].pos = 383662; kgrp[28].end = 393372; kgrp[28].loop = 2811; //ghost
|
||||
kgrp[29].pos = 393373; kgrp[29].end = 406045; kgrp[29].loop = 4522; |
||||
kgrp[30].pos = 406046; kgrp[30].end = 414486; kgrp[30].loop = 2306; |
||||
kgrp[31].pos = 406046; kgrp[31].end = 414486; kgrp[31].loop = 2306; //ghost
|
||||
kgrp[32].pos = 414487; kgrp[32].end = 422408; kgrp[32].loop = 2169; |
||||
|
||||
//extra xfade looping...
|
||||
for (int32_t k = 0; k < 28; k++) |
||||
{ |
||||
int32_t p0 = kgrp[k].end; |
||||
int32_t p1 = kgrp[k].end - kgrp[k].loop; |
||||
float xf = 1.0f; |
||||
float dxf = -0.02f; |
||||
|
||||
while (xf > 0.0f) |
||||
{ |
||||
waves[p0] = (int16_t)((1.0f - xf) * (float)waves[p0] + xf * (float)waves[p1]); |
||||
|
||||
p0--; |
||||
p1--; |
||||
xf += dxf; |
||||
} |
||||
} |
||||
|
||||
// output
|
||||
printf("#include <Arduino.h>\n\n"); |
||||
printf("const int16_t epianoDataXfade[] PROGMEM = {\n"); |
||||
for(uint32_t z=0; z<(844836/sizeof(int16_t)); z=z+20) |
||||
{ |
||||
for(uint32_t i=0;i<20;i++) |
||||
{ |
||||
if(z+i>=(uint32_t)(844836/sizeof(int16_t))) |
||||
break;
|
||||
printf("%d,",waves[z+i]); |
||||
if(i>=19) |
||||
printf("\n"); |
||||
} |
||||
} |
||||
printf("\n};\n"); |
||||
} |
Loading…
Reference in new issue