parent
b5c418c10b
commit
08715a0bce
@ -1,51 +0,0 @@ |
|||||||
/*
|
|
||||||
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; |
|
||||||
} |
|
@ -0,0 +1,195 @@ |
|||||||
|
/*
|
||||||
|
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 |
@ -1,563 +0,0 @@ |
|||||||
/*
|
|
||||||
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 |
|
@ -1,145 +0,0 @@ |
|||||||
/*
|
|
||||||
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 |
|
@ -1,50 +0,0 @@ |
|||||||
/* ----------------------------------------------------------------------
|
|
||||||
* 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); |
|
||||||
} |
|
@ -1,370 +0,0 @@ |
|||||||
/*
|
|
||||||
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); |
|
||||||
} |
|
@ -1,117 +0,0 @@ |
|||||||
/*
|
|
||||||
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
@ -1,120 +0,0 @@ |
|||||||
/*
|
|
||||||
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 |
|
@ -1,11 +0,0 @@ |
|||||||
#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 |
|
||||||
}; |
|
@ -1,9 +0,0 @@ |
|||||||
#!/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 |
|
@ -1,100 +0,0 @@ |
|||||||
#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