Revert "first commit"

This reverts commit 1ef901f460.
master
Holger Wirtz 5 years ago
parent 08715a0bce
commit 9b2cd65be9
  1. 51
      EEPROMAnything.h
  2. 195
      LiquidMenuTest.ino
  3. 563
      MicroMDAEPiano.ino
  4. 0
      README.md
  5. 145
      UI.hpp
  6. 121
      config.h
  7. 50
      fast_log.h
  8. 370
      mdaEPiano.cpp
  9. 117
      mdaEPiano.h
  10. 21152
      mdaEPianoData.h
  11. 21154
      mdaEPianoDataXfade.h
  12. 1604
      midi_devices.hpp
  13. 120
      midinotes.h
  14. 11
      name.c
  15. 9
      utility/gen_xfade.sh
  16. 100
      utility/xfade_generator.c

@ -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

145
UI.hpp

@ -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

@ -1,6 +1,90 @@
/*
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 CONFIG_H_INCLUDED
#define CONFIG_H_INCLUDED
#include "midinotes.h"
#include <Arduino.h>
// ATTENTION! For better latency you have to redefine AUDIO_BLOCK_SAMPLES from
// 128 to 64 in <ARDUINO-IDE-DIR>/cores/teensy3/AudioStream.h
//*************************************************************************************************
//* DEVICE SETTINGS
//*************************************************************************************************
// MIDI
#define MIDI_DEVICE_DIN Serial1
#define MIDI_DEVICE_USB 1
#define MIDI_DEVICE_USB_HOST 1
// AUDIO
// If nothing is defined PT8211 is used as audio output device!
#define TEENSY_AUDIO_BOARD 1
//#define TGA_AUDIO_BOARD 1
//*************************************************************************************************
//* MIDI SETTINGS
//*************************************************************************************************
#define DEFAULT_MIDI_CHANNEL MIDI_CHANNEL_OMNI
#define MIDI_MERGE_THRU 1
//*************************************************************************************************
//* AUDIO SETTINGS
//*************************************************************************************************
#define VOLUME 0.8
#define VOLUME_CURVE 0.07
#define AUDIO_MEM 128
#define SAMPLE_RATE 44100
#define REDUCE_LOUDNESS 0
#define USE_XFADE_DATA 1
//*************************************************************************************************
//* DEBUG OUTPUT SETTINGS
//*************************************************************************************************
#define DEBUG 1
#define SERIAL_SPEED 38400
#define SHOW_XRUN 1
#define SHOW_CPU_LOAD_MSEC 5000
//*************************************************************************************************
//* HARDWARE SETTINGS
//*************************************************************************************************
// Teensy Audio Shield:
#define SGTL5000_LINEOUT_LEVEL 29
//#define SDCARD_CS_PIN 10
//#define SDCARD_MOSI_PIN 7
//#define SDCARD_SCK_PIN 14
// Teensy 3.5 & 3.6 SD card
#define SDCARD_CS_PIN BUILTIN_SDCARD
#define SDCARD_MOSI_PIN 11 // not actually used
#define SDCARD_SCK_PIN 13 // not actually used
// Encoder with button
#define NUM_ENCODER 2
#define ENC_L_PIN_A 3
@ -20,6 +104,41 @@
#define LCD_CHARS 16
#define LCD_LINES 2
#define CONTROL_RATE_MS 50
// EEPROM address
#define EEPROM_START_ADDRESS 0
//*************************************************************************************************
//* DO NO CHANGE ANYTHING BEYOND IF YOU DON'T KNOW WHAT YOU ARE DOING !!!
//*************************************************************************************************
#define CONTROL_RATE_MS 100
// MIDI
#ifdef MIDI_DEVICE_USB
#define USBCON 1
#endif
#if defined(__MK66FX1M0__)
// Teensy-3.6 settings
#define MIDI_DEVICE_USB_HOST 1
#else
// Teensy-3.5 settings
#undef MIDI_DEVICE_USB_HOST
#endif
// Engine
#if defined(__MK66FX1M0__)
// Teensy-3.6 settings
#define NVOICES 64
#else
#define NVOICES 32
#endif
// struct for holding the current configuration
struct config_t {
uint32_t checksum;
uint8_t voice;
float vol;
float pan;
uint8_t midi_channel;
};
#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…
Cancel
Save