diff -Nru yoshimi-0.060.10/0.060.10.notes yoshimi-0.060.12/0.060.10.notes --- yoshimi-0.060.10/0.060.10.notes 2011-04-11 23:38:55.000000000 +0000 +++ yoshimi-0.060.12/0.060.10.notes 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -yoshimi-0.060.10 - -COPYING file updated - -yoshimi-0.060.9 - -licence anomalies corrected. - -yoshimi-0.060.8 - -Nothing too adventurous, just an incremental advance on 0.058.1 adding Paul's -Unison and Reverb enhancements, jack session support, panning inversion -corrections and assorted other tweaks and bug fixes. diff -Nru yoshimi-0.060.10/0.060.12.notes yoshimi-0.060.12/0.060.12.notes --- yoshimi-0.060.10/0.060.12.notes 1970-01-01 00:00:00.000000000 +0000 +++ yoshimi-0.060.12/0.060.12.notes 2012-01-03 20:15:18.000000000 +0000 @@ -0,0 +1,25 @@ +yoshimi-0.060.12 + +* Fix for the keyshift/microtonal issue, patch provided by RPD (Rob). Thanks! + +yoshimi-0.060.11 + +* Fix empty functions, fluid no longer produces (empty) implementations for totally empty functions in .fl files. More info: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=633476 and http://www.fltk.org/str.php?L2259 Thanks to Frank Kober for pointing out this issue and thanks for the patch. +* Dynamically switching to legato mode using a footswitch has been made possible by Kristian Amlie. Thanks for the patch! +* Renamed Yoshimi icon file in the desktop directory and modified desktop file accordingly. Packagers, please use this icon file, it has been designed specifically for Yoshimi. If anything needs to be changed in order to have this icon included please let me know + +Jeremy Jongepier + +yoshimi-0.060.10 + +COPYING file updated + +yoshimi-0.060.9 + +licence anomalies corrected. + +yoshimi-0.060.8 + +Nothing too adventurous, just an incremental advance on 0.058.1 adding Paul's +Unison and Reverb enhancements, jack session support, panning inversion +corrections and assorted other tweaks and bug fixes. diff -Nru yoshimi-0.060.10/debian/changelog yoshimi-0.060.12/debian/changelog --- yoshimi-0.060.10/debian/changelog 2011-07-11 13:33:49.000000000 +0000 +++ yoshimi-0.060.12/debian/changelog 2012-01-05 14:12:56.000000000 +0000 @@ -1,3 +1,10 @@ +yoshimi (0.060.12-1) unstable; urgency=low + + * New upstream release. + * Remove 02-fluid_1.3.patch, applied upstream. + + -- Alessio Treglia Thu, 05 Jan 2012 15:12:50 +0100 + yoshimi (0.060.10-3) unstable; urgency=low * Refresh 01-gcc_flags.patch. diff -Nru yoshimi-0.060.10/debian/patches/02-fluid_1.3.patch yoshimi-0.060.12/debian/patches/02-fluid_1.3.patch --- yoshimi-0.060.10/debian/patches/02-fluid_1.3.patch 2011-07-11 13:31:10.000000000 +0000 +++ yoshimi-0.060.12/debian/patches/02-fluid_1.3.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -Description: Since the 1.3.x series, Fluid no longer produces (empty) - implementations for totally empty functions in .fl files. -Author: Aaron M. Ucko -Bug-Debian: http://bugs.debian.org/633476 -Forwarded: no ---- - src/UI/BankUI.fl | 5 ++++- - src/UI/OscilGenUI.fl | 6 +++++- - src/UI/PartUI.fl | 5 ++++- - src/UI/PresetsUI.fl | 6 ++++-- - 4 files changed, 17 insertions(+), 5 deletions(-) - ---- yoshimi.orig/src/UI/BankUI.fl -+++ yoshimi/src/UI/BankUI.fl -@@ -51,7 +51,10 @@ decl {\#include "Misc/SynthEngine.h"} {p - class BankProcess_ {open - } { - Function {process(void)} {open return_type {virtual void} -- } {} -+ } { -+ comment {This body intentionally left blank.} {in_source not_in_header -+ } -+ } - decl {Bank *bank;} {public - } - } ---- yoshimi.orig/src/UI/OscilGenUI.fl -+++ yoshimi/src/UI/OscilGenUI.fl -@@ -166,7 +166,11 @@ class OscilSpectrum {open : {public Fl_B - - class PSlider {open : {public Fl_Slider} - } { -- Function {PSlider(int x,int y, int w, int h, const char *label=0):Fl_Slider(x,y,w,h,label)} {} {} -+ Function {PSlider(int x,int y, int w, int h, const char *label=0):Fl_Slider(x,y,w,h,label)} { -+ } { -+ comment {This body intentionally left blank.} {in_source not_in_header -+ } -+ } - Function {handle(int event)} {return_type int - } { - code {int X=x(),Y=y(),W=w(),H=h(); ---- yoshimi.orig/src/UI/PartUI.fl -+++ yoshimi/src/UI/PartUI.fl -@@ -89,7 +89,10 @@ class PartSysEffSend {open : {public Fl_ - class PartUI_ {open - } { - Function {showparameters(int kititem,int engine)} {open return_type virtual -- } {} -+ } { -+ comment {This body intentionally left blank.} {in_source not_in_header -+ } -+ } - } - - class PartKitItem {: {public Fl_Group} ---- yoshimi.orig/src/UI/PresetsUI.fl -+++ yoshimi/src/UI/PresetsUI.fl -@@ -44,8 +44,10 @@ decl {\#include "Params/Presets.h"} {pub - - class PresetsUI_ {} { - Function {refresh()} {return_type {virtual void} -- } {} -- Function {~PresetsUI_()} {} {} -+ } { -+ comment {This body intentionally left blank.} {in_source not_in_header -+ } -+ } - } - - class PresetsUI {} { diff -Nru yoshimi-0.060.10/debian/patches/series yoshimi-0.060.12/debian/patches/series --- yoshimi-0.060.10/debian/patches/series 2011-07-11 13:29:01.000000000 +0000 +++ yoshimi-0.060.12/debian/patches/series 2012-01-05 14:04:19.000000000 +0000 @@ -1,2 +1 @@ 01-gcc_flags.patch -02-fluid_1.3.patch Binary files /tmp/VJAfB0C5ig/yoshimi-0.060.10/desktop/pink_robot_icon.png and /tmp/TVRgemIjA3/yoshimi-0.060.12/desktop/pink_robot_icon.png differ diff -Nru yoshimi-0.060.10/desktop/yoshimi.desktop.in yoshimi-0.060.12/desktop/yoshimi.desktop.in --- yoshimi-0.060.10/desktop/yoshimi.desktop.in 2010-11-26 09:10:12.000000000 +0000 +++ yoshimi-0.060.12/desktop/yoshimi.desktop.in 2012-01-02 20:31:54.000000000 +0000 @@ -9,4 +9,4 @@ TryExec=@CMAKE_INSTALL_PREFIX@/bin/yoshimi Terminal=false StartupNotify=true -Icon=@CMAKE_INSTALL_PREFIX@/share/yoshimi/pink_robot_icon.png +Icon=@CMAKE_INSTALL_PREFIX@/share/yoshimi/yoshimi.png Binary files /tmp/VJAfB0C5ig/yoshimi-0.060.10/desktop/yoshimi.png and /tmp/TVRgemIjA3/yoshimi-0.060.12/desktop/yoshimi.png differ diff -Nru yoshimi-0.060.10/src/CMakeLists.txt yoshimi-0.060.12/src/CMakeLists.txt --- yoshimi-0.060.10/src/CMakeLists.txt 2011-04-11 23:28:16.000000000 +0000 +++ yoshimi-0.060.12/src/CMakeLists.txt 2012-01-03 20:16:34.000000000 +0000 @@ -20,7 +20,7 @@ cmake_minimum_required (VERSION 2.6) cmake_policy (VERSION 2.6) cmake_policy (SET CMP0004 OLD) -set (YOSHIMI_VERSION "0.060.10") +set (YOSHIMI_VERSION "0.060.12") file (WRITE version.txt "${YOSHIMI_VERSION}") set (CMAKE_INCLUDE_CURRENT_DIR ON) diff -Nru yoshimi-0.060.10/src/Misc/Part.cpp yoshimi-0.060.12/src/Misc/Part.cpp --- yoshimi-0.060.10/src/Misc/Part.cpp 2011-04-11 23:20:29.000000000 +0000 +++ yoshimi-0.060.12/src/Misc/Part.cpp 2012-01-03 19:57:16.000000000 +0000 @@ -68,7 +68,7 @@ kit[n].padpars = NULL; } - kit[0].adpars = new ADnoteParameters(microtonal, fft); + kit[0].adpars = new ADnoteParameters(fft); kit[0].subpars = new SUBnoteParameters(); kit[0].padpars = new PADnoteParameters(fft); @@ -242,7 +242,7 @@ int lastnotecopy = lastnote; // Useful after lastnote has been changed. // MonoMem stuff: - if (!Ppolymode) // if Poly is off + if (!Ppolymode || ctl->legato.legato) // if Poly is off { monomemnotes.push_back(note); // Add note to the list. monomem[note].velocity = velocity; // Store this note's velocity. @@ -268,9 +268,9 @@ break; } } - if (Plegatomode && !Pdrummode) + if ((Plegatomode || ctl->legato.legato) && !Pdrummode) { - if (Ppolymode) + if (Ppolymode && Plegatomode) { Runtime.Log("Warning, poly and legato modes are both on."); Runtime.Log("That should not happen, so disabling legato mode"); @@ -366,7 +366,7 @@ // still held down or sustained for the Portamento to activate // (that's like Legato). int portamento = 0; - if (Ppolymode || !ismonofirstnote) + if ((Ppolymode && !ctl->legato.legato) || !ismonofirstnote) { // I added a third argument to the // ctl->initportamento(...) function to be able @@ -500,7 +500,8 @@ partnote[pos].kititem[0].sendtoparteffect = 0; if (kit[0].Padenabled) partnote[pos].kititem[0].adnote = - new ADnote(kit[0].adpars, ctl, vel, portamento, note, false ); // not silent + new ADnote(kit[0].adpars, ctl, notebasefreq, vel, + portamento, note, false ); // not silent if (kit[0].Psubenabled) partnote[pos].kititem[0].subnote = new SUBnote(kit[0].subpars, ctl, notebasefreq, vel, @@ -518,7 +519,8 @@ partnote[posb].kititem[0].sendtoparteffect = 0; if (kit[0].Padenabled) partnote[posb].kititem[0].adnote = - new ADnote(kit[0].adpars, ctl, vel, portamento, note, true); // silent + new ADnote(kit[0].adpars, ctl, notebasefreq, vel, + portamento, note, true); // silent if (kit[0].Psubenabled) partnote[posb].kititem[0].subnote = new SUBnote(kit[0].subpars, ctl, notebasefreq, vel, @@ -551,11 +553,12 @@ if (kit[item].adpars && kit[item].Padenabled) { partnote[pos].kititem[ci].adnote = - new ADnote(kit[item].adpars, ctl, vel, portamento, note, false); // not silent + new ADnote(kit[item].adpars, ctl, notebasefreq, vel, + portamento, note, false); // not silent } if (kit[item].subpars && kit[item].Psubenabled) partnote[pos].kititem[ci].subnote = - new SUBnote(kit[item].subpars, ctl,notebasefreq, vel, + new SUBnote(kit[item].subpars, ctl, notebasefreq, vel, portamento, note, false); if (kit[item].padpars && kit[item].Ppadenabled) @@ -574,7 +577,8 @@ if (kit[item].adpars && kit[item].Padenabled) { partnote[posb].kititem[ci].adnote = - new ADnote(kit[item].adpars, ctl, vel, portamento, note, true); // silent + new ADnote(kit[item].adpars, ctl, notebasefreq, + vel, portamento, note, true); // silent } if (kit[item].subpars && kit[item].Psubenabled) partnote[posb].kititem[ci].subnote = @@ -620,7 +624,7 @@ { if (!ctl->sustain.sustain) { //the sustain pedal is not pushed - if (!Ppolymode && (not monomemnotes.empty())) + if ((!Ppolymode || ctl->legato.legato) && (not monomemnotes.empty())) MonoMemRenote(); // To play most recent still held note. else RelaseNotePos(i); @@ -679,6 +683,9 @@ if (!ctl->sustain.sustain) RelaseSustainedKeys(); break; + case C_legatofootswitch: + ctl->setlegato(par); + break; case C_allsoundsoff: AllNotesOff(); // Panic break; @@ -725,7 +732,7 @@ void Part::RelaseSustainedKeys(void) { // Let's call MonoMemRenote() on some conditions: - if (Ppolymode == 0 && (not monomemnotes.empty())) + if ((Ppolymode == 0 || ctl->legato.legato) && (not monomemnotes.empty())) if (monomemnotes.back() != lastnote) // Sustain controller manipulation would cause repeated same note // respawn without this check. @@ -828,7 +835,7 @@ keylimit = POLIPHONY - 5; // release old keys if the number of notes>keylimit - if (Ppolymode) + if (Ppolymode && !ctl->legato.legato) { int notecount = 0; for (int i = 0; i < POLIPHONY; ++i) @@ -1057,7 +1064,7 @@ else { if (!kit[kititem].adpars) - kit[kititem].adpars = new ADnoteParameters(microtonal, fft); + kit[kititem].adpars = new ADnoteParameters(fft); if (!kit[kititem].subpars) kit[kititem].subpars = new SUBnoteParameters(); if (!kit[kititem].padpars) diff -Nru yoshimi-0.060.10/src/Misc/Part.cpp.orig yoshimi-0.060.12/src/Misc/Part.cpp.orig --- yoshimi-0.060.10/src/Misc/Part.cpp.orig 1970-01-01 00:00:00.000000000 +0000 +++ yoshimi-0.060.12/src/Misc/Part.cpp.orig 2012-01-02 20:08:33.000000000 +0000 @@ -0,0 +1,1345 @@ +/* + Part.cpp - Part implementation + + Original ZynAddSubFX author Nasca Octavian Paul + Copyright (C) 2002-2005 Nasca Octavian Paul + Copyright 2009, James Morris + Copyright 2009-2011, Alan Calvert + + This file is part of yoshimi, which is free software: you can redistribute + it and/or modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + yoshimi 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 (version 2 or + later) for more details. + + You should have received a copy of the GNU General Public License along with + yoshimi; if not, write to the Free Software Foundation, Inc., 51 Franklin + Street, Fifth Floor, Boston, MA 02110-1301, USA. + + This file is derivative of ZynAddSubFX original code, modified April 2011 +*/ + +#include +#include + +using namespace std; + +#include "Params/ADnoteParameters.h" +#include "Params/SUBnoteParameters.h" +#include "Params/PADnoteParameters.h" +#include "Synth/ADnote.h" +#include "Synth/SUBnote.h" +#include "Synth/PADnote.h" +#include "Params/Controller.h" +#include "Effects/EffectMgr.h" +#include "DSP/FFTwrapper.h" +#include "Misc/Microtonal.h" +#include "Misc/XMLwrapper.h" +#include "Misc/SynthEngine.h" +#include "Synth/Resonance.h" +#include "Synth/BodyDisposal.h" +#include "Misc/Part.h" + +Part::Part(Microtonal *microtonal_, FFTwrapper *fft_) : + microtonal(microtonal_), + fft(fft_), + partMuted(0), + killallnotes(false) +{ + ctl = new Controller(); + partoutl = (float*)fftwf_malloc(synth->bufferbytes); + memset(partoutl, 0, synth->bufferbytes); + partoutr = (float*)fftwf_malloc(synth->bufferbytes); + memset(partoutr, 0, synth->bufferbytes); + tmpoutl = (float*)fftwf_malloc(synth->bufferbytes); + memset(tmpoutl, 0, synth->bufferbytes); + tmpoutr = (float*)fftwf_malloc(synth->bufferbytes); + memset(tmpoutr, 0, synth->bufferbytes); + + for (int n = 0; n < NUM_KIT_ITEMS; ++n) + { + kit[n].Pname.clear(); + kit[n].adpars = NULL; + kit[n].subpars = NULL; + kit[n].padpars = NULL; + } + + kit[0].adpars = new ADnoteParameters(microtonal, fft); + kit[0].subpars = new SUBnoteParameters(); + kit[0].padpars = new PADnoteParameters(fft); + + // Part's Insertion Effects init + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + partefx[nefx] = new EffectMgr(1); + + for (int n = 0; n < NUM_PART_EFX + 1; ++n) + { + partfxinputl[n] = (float*)fftwf_malloc(synth->bufferbytes); + memset(partfxinputl[n], 0, synth->bufferbytes); + partfxinputr[n] = (float*)fftwf_malloc(synth->bufferbytes); + memset(partfxinputr[n], 0, synth->bufferbytes); + Pefxbypass[n] = false; + } + + oldfreq = -1.0f; + + int i, j; + for (i = 0; i < POLIPHONY; ++i) + { + partnote[i].status = KEY_OFF; + partnote[i].note = -1; + partnote[i].itemsplaying = 0; + for (j = 0; j < NUM_KIT_ITEMS; ++j) + { + partnote[i].kititem[j].adnote = NULL; + partnote[i].kititem[j].subnote = NULL; + partnote[i].kititem[j].padnote = NULL; + } + partnote[i].time = 0; + } + cleanup(); + Pname.clear(); + + oldvolumel = oldvolumer = 0.5f; + lastnote = -1; + lastpos = 0; // lastpos will store previously used NoteOn(...)'s pos. + lastlegatomodevalid = false; // To store previous legatomodevalid value. + defaults(); +} + + +void Part::defaults(void) +{ + Penabled = 0; + Pminkey = 0; + Pmaxkey = 127; + Pnoteon = 1; + Ppolymode = 1; + Plegatomode = 0; + setVolume(96); + Pkeyshift = 64; + Prcvchn = 0; + setPan(Ppanning = 64); + Pvelsns = 64; + Pveloffs = 64; + Pkeylimit = 15; + defaultsinstrument(); + ctl->defaults(); +} + +void Part::defaultsinstrument(void) +{ + Pname.clear(); + + info.Ptype = 0; + info.Pauthor.clear(); + info.Pcomments.clear(); + + Pkitmode = 0; + Pdrummode = 0; + + for (int n = 0; n < NUM_KIT_ITEMS; ++n) + { + kit[n].Penabled = 0; + kit[n].Pmuted = 0; + kit[n].Pminkey = 0; + kit[n].Pmaxkey = 127; + kit[n].Padenabled = 0; + kit[n].Psubenabled = 0; + kit[n].Ppadenabled = 0; + kit[n].Pname.clear(); + kit[n].Psendtoparteffect = 0; + if (n != 0) + setkititemstatus(n, 0); + } + kit[0].Penabled = 1; + kit[0].Padenabled = 1; + kit[0].adpars->defaults(); + kit[0].subpars->defaults(); + kit[0].padpars->defaults(); + + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + { + partefx[nefx]->defaults(); + Pefxroute[nefx] = 0; // route to next effect + } +} + + +// Cleanup the part +void Part::cleanup(void) +{ + Mute(); + for (int k = 0; k < POLIPHONY; ++k) + KillNotePos(k); + memset(partoutl, 0, synth->bufferbytes); + memset(partoutr, 0, synth->bufferbytes); + memset(tmpoutl, 0, synth->bufferbytes); + memset(tmpoutr, 0, synth->bufferbytes); + + ctl->resetall(); + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + partefx[nefx]->cleanup(); + for (int n = 0; n < NUM_PART_EFX + 1; ++n) + { + memset(partfxinputl[n], 0, synth->bufferbytes); + memset(partfxinputr[n], 0, synth->bufferbytes); + + } + Unmute(); +} + + +Part::~Part() +{ + cleanup(); + for (int n = 0; n < NUM_KIT_ITEMS; ++n) + { + if (kit[n].adpars) + delete kit[n].adpars; + if (kit[n].subpars) + delete kit[n].subpars; + if (kit[n].padpars) + delete kit[n].padpars; + } + fftwf_free(partoutl); + fftwf_free(partoutr); + fftwf_free(tmpoutl); + fftwf_free(tmpoutr); + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + { + if (partefx[nefx]) + delete partefx[nefx]; + } + for (int n = 0; n < NUM_PART_EFX + 1; ++n) + { + if (partfxinputl[n]) + fftwf_free(partfxinputl[n]); + if (partfxinputr[n]) + fftwf_free(partfxinputr[n]); + } + if (ctl) + delete ctl; +} + + +// Note On Messages +void Part::NoteOn(int note, int velocity, int masterkeyshift) +{ + if (!Pnoteon || note < Pminkey || note > Pmaxkey) + return; + + // Legato and MonoMem used vars: + int posb = POLIPHONY - 1; // Just a dummy initial value. + bool legatomodevalid = false; // true when legato mode is determined applicable. + bool doinglegato = false; // true when we determined we do a legato note. + bool ismonofirstnote = false; // (In Mono/Legato) true when we determined + // no other notes are held down or sustained.*/ + int lastnotecopy = lastnote; // Useful after lastnote has been changed. + + // MonoMem stuff: + if (!Ppolymode || ctl->legato.legato) // if Poly is off + { + monomemnotes.push_back(note); // Add note to the list. + monomem[note].velocity = velocity; // Store this note's velocity. + monomem[note].mkeyshift = masterkeyshift; + if (partnote[lastpos].status != KEY_PLAYING + && partnote[lastpos].status != KEY_RELASED_AND_SUSTAINED) + { + ismonofirstnote = true; // No other keys are held or sustained. + } + } + else // Poly mode is On, so just make sure the list is empty. + { + if (not monomemnotes.empty()) + monomemnotes.clear(); + } + lastnote = note; + int pos = -1; + for (int i = 0; i < POLIPHONY; ++i) + { + if (partnote[i].status == KEY_OFF) + { + pos = i; + break; + } + } + if ((Plegatomode || ctl->legato.legato) && !Pdrummode) + { + if (Ppolymode && Plegatomode) + { + Runtime.Log("Warning, poly and legato modes are both on."); + Runtime.Log("That should not happen, so disabling legato mode"); + Plegatomode = 0; + } + else + { + // Legato mode is on and applicable. + legatomodevalid = true; + if ((not ismonofirstnote) && lastlegatomodevalid) + { + // At least one other key is held or sustained, and the + // previous note was played while in valid legato mode. + doinglegato = true; // So we'll do a legato note. + pos = lastpos; // A legato note uses same pos as previous.. + posb = lastposb; // .. same goes for posb. + } + else + { + // Legato mode is valid, but this is only a first note. + for (int i = 0; i < POLIPHONY; ++i) + if (partnote[i].status == KEY_PLAYING + || partnote[i].status == KEY_RELASED_AND_SUSTAINED) + RelaseNotePos(i); + + // Set posb + posb = (pos + 1) % POLIPHONY; // We really want it (if the following fails) + for (int i = 0; i < POLIPHONY; ++i) + { + if (partnote[i].status == KEY_OFF && pos != i) + { + posb = i; + break; + } + } + } + lastposb = posb;// Keep a trace of used posb + } + } + else + { + // Legato mode is either off or non-applicable. + if (Ppolymode == 0) + { // if the mode is 'mono' turn off all other notes + for (int i = 0; i < POLIPHONY; ++i) + if (partnote[i].status == KEY_PLAYING) + RelaseNotePos(i); + RelaseSustainedKeys(); + } + } + lastlegatomodevalid = legatomodevalid; + + if (pos == -1) + { + // test + Runtime.Log("Too may notes - notes > poliphony, PartNoteOn()"); + } + else + { + // start the note + partnote[pos].status = KEY_PLAYING; + partnote[pos].note = note; + if (legatomodevalid) { + partnote[posb].status = KEY_PLAYING; + partnote[posb].note = note; + } + + // compute the velocity offset + float vel = velF(velocity / 127.0f, Pvelsns) + (Pveloffs - 64.0f) / 64.0f; + vel = (vel < 0.0f) ? 0.0f : vel; + vel = (vel > 1.0f) ? 1.0f : vel; + + // compute the keyshift + int partkeyshift = (int)Pkeyshift - 64; + int keyshift = masterkeyshift + partkeyshift; + + // initialise note frequency + float notebasefreq; + if (!Pdrummode) + { + if ((notebasefreq = microtonal->getNoteFreq(note, keyshift)) < 0.0f) + return; // the key is not mapped + } + else + notebasefreq = microtonal->getNoteFreq(note); + + // Portamento + if (oldfreq < 1.0f) + oldfreq = notebasefreq; // this is only the first note is played + + // For Mono/Legato: Force Portamento Off on first + // notes. That means it is required that the previous note is + // still held down or sustained for the Portamento to activate + // (that's like Legato). + int portamento = 0; + if ((Ppolymode && !ctl->legato.legato) || !ismonofirstnote) + { + // I added a third argument to the + // ctl->initportamento(...) function to be able + // to tell it if we're doing a legato note. + portamento = ctl->initportamento(oldfreq, notebasefreq, doinglegato); + } + + if (portamento) + ctl->portamento.noteusing = pos; + oldfreq = notebasefreq; + lastpos = pos; // Keep a trace of used pos. + if (doinglegato) + { + // Do Legato note + if (!Pkitmode) + { // "normal mode" legato note + if ((kit[0].Padenabled) + && (partnote[pos].kititem[0].adnote) + && (partnote[posb].kititem[0].adnote)) + { + partnote[pos].kititem[0].adnote-> + ADlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[0].adnote-> + ADlegatonote(notebasefreq, vel, portamento, note, true); + // 'true' is to tell it it's being called from here. + } + + if ((kit[0].Psubenabled) + && (partnote[pos].kititem[0].subnote) + && (partnote[posb].kititem[0].subnote)) + { + partnote[pos].kititem[0].subnote-> + SUBlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[0].subnote-> + SUBlegatonote(notebasefreq, vel, portamento, note, true); + } + + if ((kit[0].Ppadenabled) + && (partnote[pos].kititem[0].padnote) + && (partnote[posb].kititem[0].padnote)) + { + partnote[pos].kititem[0].padnote-> + PADlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[0].padnote-> + PADlegatonote(notebasefreq, vel, portamento, note, true); + } + + } + else + { // "kit mode" legato note + int ci = 0; + for (int item = 0; item < NUM_KIT_ITEMS; ++item) + { + if (kit[item].Pmuted) + continue; + if ((note < kit[item].Pminkey) || (note > kit[item].Pmaxkey)) + continue; + + if ((lastnotecopy < kit[item].Pminkey) + || (lastnotecopy > kit[item].Pmaxkey)) + continue; // We will not perform legato across 2 key regions. + + partnote[pos].kititem[ci].sendtoparteffect = + ( kit[item].Psendtoparteffect < NUM_PART_EFX) + ? kit[item].Psendtoparteffect + : NUM_PART_EFX; // if this parameter is 127 for "unprocessed" + partnote[posb].kititem[ci].sendtoparteffect = + ( kit[item].Psendtoparteffect < NUM_PART_EFX) + ? kit[item].Psendtoparteffect + : NUM_PART_EFX; + + if ((kit[item].Padenabled) + && (kit[item].adpars) + && (partnote[pos].kititem[ci].adnote) + && (partnote[posb].kititem[ci].adnote)) + { + partnote[pos].kititem[ci].adnote-> + ADlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[ci].adnote-> + ADlegatonote(notebasefreq, vel, portamento, note, true); + } + if ((kit[item].Psubenabled) + && (kit[item].subpars) + && (partnote[pos].kititem[ci].subnote) + && (partnote[posb].kititem[ci].subnote)) + { + partnote[pos].kititem[ci].subnote-> + SUBlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[ci].subnote-> + SUBlegatonote(notebasefreq, vel, portamento, note, true); + } + if ((kit[item].Ppadenabled) + && (kit[item].padpars) + && (partnote[pos].kititem[ci].padnote) + && (partnote[posb].kititem[ci].padnote)) + { + partnote[pos].kititem[ci].padnote-> + PADlegatonote(notebasefreq, vel, portamento, note, true); + partnote[posb].kititem[ci].padnote-> + PADlegatonote(notebasefreq, vel, portamento, note, true); + } + + if ((kit[item].adpars) + || (kit[item].subpars) + || (kit[item].padpars)) + { + ci++; + if (Pkitmode == 2 + && (kit[item].Padenabled + || kit[item].Psubenabled + || kit[item].Ppadenabled)) + break; + } + } + if (ci == 0) + { + // No legato were performed at all, so pretend nothing happened: + monomemnotes.pop_back(); // Remove last note from the list. + lastnote = lastnotecopy; // Set lastnote back to previous value. + } + } + return; // Ok, Legato note done, return. + } + + partnote[pos].itemsplaying = 0; + if (legatomodevalid) + partnote[posb].itemsplaying = 0; + + if (!Pkitmode) + { // init the notes for the "normal mode" + partnote[pos].kititem[0].sendtoparteffect = 0; + if (kit[0].Padenabled) + partnote[pos].kititem[0].adnote = + new ADnote(kit[0].adpars, ctl, vel, portamento, note, false ); // not silent + if (kit[0].Psubenabled) + partnote[pos].kititem[0].subnote = + new SUBnote(kit[0].subpars, ctl, notebasefreq, vel, + portamento, note, false); + if (kit[0].Ppadenabled) + partnote[pos].kititem[0].padnote = + new PADnote(kit[0].padpars, ctl, notebasefreq, vel, + portamento, note, false); + if (kit[0].Padenabled || kit[0].Psubenabled || kit[0].Ppadenabled) + partnote[pos].itemsplaying++; + + // Spawn another note (but silent) if legatomodevalid==true + if (legatomodevalid) + { + partnote[posb].kititem[0].sendtoparteffect = 0; + if (kit[0].Padenabled) + partnote[posb].kititem[0].adnote = + new ADnote(kit[0].adpars, ctl, vel, portamento, note, true); // silent + if (kit[0].Psubenabled) + partnote[posb].kititem[0].subnote = + new SUBnote(kit[0].subpars, ctl, notebasefreq, vel, + portamento, note, true); + if (kit[0].Ppadenabled) + partnote[posb].kititem[0].padnote = + new PADnote(kit[0].padpars, ctl, notebasefreq, vel, + portamento, note, true); + if (kit[0].Padenabled || kit[0].Psubenabled || kit[0].Ppadenabled) + partnote[posb].itemsplaying++; + } + } + else + { // init the notes for the "kit mode" + for (int item = 0; item < NUM_KIT_ITEMS; ++item) + { + if (kit[item].Pmuted) + continue; + if (note < kit[item].Pminkey + || note>kit[item].Pmaxkey) + continue; + + int ci = partnote[pos].itemsplaying; // ci=current item + + partnote[pos].kititem[ci].sendtoparteffect = + (kit[item].Psendtoparteffect < NUM_PART_EFX) + ? kit[item].Psendtoparteffect + : NUM_PART_EFX; // if this parameter is 127 for "unprocessed" + + if (kit[item].adpars && kit[item].Padenabled) + { + partnote[pos].kititem[ci].adnote = + new ADnote(kit[item].adpars, ctl, vel, portamento, note, false); // not silent + } + if (kit[item].subpars && kit[item].Psubenabled) + partnote[pos].kititem[ci].subnote = + new SUBnote(kit[item].subpars, ctl,notebasefreq, vel, + portamento, note, false); + + if (kit[item].padpars && kit[item].Ppadenabled) + partnote[pos].kititem[ci].padnote = + new PADnote(kit[item].padpars, ctl, notebasefreq, vel, + portamento, note, false); + + // Spawn another note (but silent) if legatomodevalid==true + if (legatomodevalid) + { + partnote[posb].kititem[ci].sendtoparteffect = + (kit[item].Psendtoparteffect < NUM_PART_EFX) + ? kit[item].Psendtoparteffect + : NUM_PART_EFX; // if this parameter is 127 for "unprocessed" + + if (kit[item].adpars && kit[item].Padenabled) + { + partnote[posb].kititem[ci].adnote = + new ADnote(kit[item].adpars, ctl, vel, portamento, note, true); // silent + } + if (kit[item].subpars && kit[item].Psubenabled) + partnote[posb].kititem[ci].subnote = + new SUBnote(kit[item].subpars, ctl, notebasefreq, + vel, portamento, note, true); + if (kit[item].padpars && kit[item].Ppadenabled) + partnote[posb].kititem[ci].padnote = + new PADnote(kit[item].padpars, ctl, notebasefreq, + vel, portamento, note, true); + + if (kit[item].adpars || kit[item].subpars) + partnote[posb].itemsplaying++; + } + + if (kit[item].adpars || kit[item].subpars) + { + partnote[pos].itemsplaying++; + if (Pkitmode == 2 && (kit[item].Padenabled + || kit[item].Psubenabled + || kit[item].Ppadenabled)) + break; + } + } + } + } + + // this only relase the keys if there is maximum number of keys allowed + setkeylimit(Pkeylimit); +} + +// Note Off Messages +void Part::NoteOff(int note) //relase the key +{ + int i; + + // This note is released, so we remove it from the list. + if (not monomemnotes.empty()) + monomemnotes.remove(note); + + for ( i = POLIPHONY - 1; i >= 0; i--) + { //first note in, is first out if there are same note multiple times + if (partnote[i].status == KEY_PLAYING && partnote[i].note == note) + { + if (!ctl->sustain.sustain) + { //the sustain pedal is not pushed + if ((!Ppolymode || ctl->legato.legato) && (not monomemnotes.empty())) + MonoMemRenote(); // To play most recent still held note. + else + RelaseNotePos(i); + } + else + { // the sustain pedal is pushed + partnote[i].status = KEY_RELASED_AND_SUSTAINED; + } + } + } +} + + +// Controllers +void Part::SetController(unsigned int type, int par) +{ + switch (type) + { + case C_pitchwheel: + ctl->setpitchwheel(par); + break; + case C_expression: + ctl->setexpression(par); + setVolume(Pvolume); + break; + case C_portamento: + ctl->setportamento(par); + break; + case C_panning: + setPan(par); + break; + case C_filtercutoff: + ctl->setfiltercutoff(par); + break; + case C_filterq: + ctl->setfilterq(par); + break; + case C_bandwidth: + ctl->setbandwidth(par); + break; + case C_modwheel: + ctl->setmodwheel(par); + break; + case C_fmamp: + ctl->setfmamp(par); + break; + case C_volume: + ctl->setvolume(par); + if (ctl->volume.receive) + volume = ctl->volume.volume; + else + setVolume(Pvolume); + break; + case C_sustain: + ctl->setsustain(par); + if (!ctl->sustain.sustain) + RelaseSustainedKeys(); + break; + case C_legatofootswitch: + ctl->setlegato(par); + break; + case C_allsoundsoff: + AllNotesOff(); // Panic + break; + case C_resetallcontrollers: + ctl->resetall(); + RelaseSustainedKeys(); + if (ctl->volume.receive) + volume = ctl->volume.volume; + setVolume(Pvolume); + setPan(Ppanning); + + for (int item = 0; item < NUM_KIT_ITEMS; ++item) + { + if (!kit[item].adpars) + continue; + kit[item].adpars->GlobalPar.Reson->sendcontroller(C_resonance_center, 1.0); + kit[item].adpars->GlobalPar.Reson->sendcontroller(C_resonance_bandwidth, 1.0); + } + // more update to add here if I add controllers + break; + case C_allnotesoff: + RelaseAllKeys(); + break; + case C_resonance_center: + ctl->setresonancecenter(par); + for (int item = 0; item < NUM_KIT_ITEMS; ++item) + { + if (!kit[item].adpars) + continue; + kit[item].adpars->GlobalPar.Reson->sendcontroller(C_resonance_center, + ctl->resonancecenter.relcenter); + } + break; + case C_resonance_bandwidth: + ctl->setresonancebw(par); + kit[0].adpars->GlobalPar.Reson->sendcontroller(C_resonance_bandwidth, + ctl->resonancebandwidth.relbw); + break; + } +} + + +// Relase the sustained keys +void Part::RelaseSustainedKeys(void) +{ + // Let's call MonoMemRenote() on some conditions: + if ((Ppolymode == 0 || ctl->legato.legato) && (not monomemnotes.empty())) + if (monomemnotes.back() != lastnote) + // Sustain controller manipulation would cause repeated same note + // respawn without this check. + MonoMemRenote(); // To play most recent still held note. + + for (int i = 0; i < POLIPHONY; ++i) + if (partnote[i].status == KEY_RELASED_AND_SUSTAINED) + RelaseNotePos(i); +} + + +// Relase all keys +void Part::RelaseAllKeys(void) +{ + for (int i = 0; i < POLIPHONY; ++i) + { + if (partnote[i].status != KEY_RELASED + && partnote[i].status != KEY_OFF) //thanks to Frank Neumann + RelaseNotePos(i); + } +} + + +// Call NoteOn(...) with the most recent still held key as new note +// (Made for Mono/Legato). +void Part::MonoMemRenote(void) +{ + unsigned char mmrtempnote = monomemnotes.back(); // Last list element. + monomemnotes.pop_back(); // We remove it, will be added again in NoteOn(...). + if (Pnoteon == 0) + RelaseNotePos(lastpos); + else + NoteOn(mmrtempnote, monomem[mmrtempnote].velocity, + monomem[mmrtempnote].mkeyshift); +} + + +// Release note at position +void Part::RelaseNotePos(int pos) +{ + + for (int j = 0; j < NUM_KIT_ITEMS; ++j) + { + if (partnote[pos].kititem[j].adnote) + if (partnote[pos].kititem[j].adnote) + partnote[pos].kititem[j].adnote->relasekey(); + + if (partnote[pos].kititem[j].subnote) + if (partnote[pos].kititem[j].subnote) + partnote[pos].kititem[j].subnote->relasekey(); + + if (partnote[pos].kititem[j].padnote) + if (partnote[pos].kititem[j].padnote) + partnote[pos].kititem[j].padnote->relasekey(); + } + partnote[pos].status = KEY_RELASED; +} + + +// Kill note at position +void Part::KillNotePos(int pos) +{ + partnote[pos].status = KEY_OFF; + partnote[pos].note = -1; + partnote[pos].time = 0; + partnote[pos].itemsplaying = 0; + + for (int j = 0; j < NUM_KIT_ITEMS; ++j) + { + if (partnote[pos].kititem[j].adnote) + { + Runtime.deadObjects->addBody(partnote[pos].kititem[j].adnote); + partnote[pos].kititem[j].adnote = NULL; + } + if (partnote[pos].kititem[j].subnote) + { + Runtime.deadObjects->addBody(partnote[pos].kititem[j].subnote); + partnote[pos].kititem[j].subnote = NULL; + } + if (partnote[pos].kititem[j].padnote) + { + Runtime.deadObjects->addBody(partnote[pos].kititem[j].padnote); + partnote[pos].kititem[j].padnote = NULL; + } + } + if (pos == ctl->portamento.noteusing) + { + ctl->portamento.noteusing = -1; + ctl->portamento.used = 0; + } +} + + +// Set Part's key limit +void Part::setkeylimit(unsigned char Pkeylimit_) +{ + Pkeylimit = Pkeylimit_; + int keylimit = Pkeylimit; + if (!keylimit) + keylimit = POLIPHONY - 5; + + // release old keys if the number of notes>keylimit + if (Ppolymode && !ctl->legato.legato) + { + int notecount = 0; + for (int i = 0; i < POLIPHONY; ++i) + { + if (partnote[i].status == KEY_PLAYING + || partnote[i].status == KEY_RELASED_AND_SUSTAINED) + notecount++; + } + int oldestnotepos = -1, maxtime = 0; + if (notecount > keylimit) + { // find out the oldest note + for (int i = 0; i < POLIPHONY; ++i) + { + if ((partnote[i].status == KEY_PLAYING + || partnote[i].status == KEY_RELASED_AND_SUSTAINED) + && partnote[i].time > maxtime) + { + maxtime = partnote[i].time; + oldestnotepos = i; + } + } + } + if (oldestnotepos != -1) + RelaseNotePos(oldestnotepos); + } +} + + +// Compute Part samples and store them in the partoutl[] and partoutr[] +void Part::ComputePartSmps(void) +{ + if (isMuted()) + { + memset(partoutl, 0, synth->bufferbytes); + memset(partoutr, 0, synth->bufferbytes); + return; + } + + int k; + int noteplay; // 0 if there is nothing activated + for (int nefx = 0; nefx < NUM_PART_EFX + 1; ++nefx) + { + memset(partfxinputl[nefx], 0, synth->bufferbytes); + memset(partfxinputr[nefx], 0, synth->bufferbytes); + } + + for (k = 0; k < POLIPHONY; ++k) + { + if (partnote[k].status == KEY_OFF) + continue; + noteplay = 0; + partnote[k].time++; + // get the sampledata of the note and kill it if it's finished + for (int item = 0; item < partnote[k].itemsplaying; ++item) + { + int sendcurrenttofx = partnote[k].kititem[item].sendtoparteffect; + ADnote *adnote = partnote[k].kititem[item].adnote; + SUBnote *subnote = partnote[k].kititem[item].subnote; + PADnote *padnote = partnote[k].kititem[item].padnote; + // get from the ADnote + if (adnote) + { + noteplay++; + if (adnote->ready) + adnote->noteout(tmpoutl, tmpoutr); + else + { + memset(tmpoutl, 0, synth->bufferbytes); + memset(tmpoutr, 0, synth->bufferbytes); + } + if (adnote->finished()) + { + Runtime.deadObjects->addBody(partnote[k].kititem[item].adnote); + partnote[k].kititem[item].adnote = NULL; + } + for (int i = 0; i < synth->buffersize; ++i) + { // add the ADnote to part(mix) + partfxinputl[sendcurrenttofx][i] += tmpoutl[i]; + partfxinputr[sendcurrenttofx][i] += tmpoutr[i]; + } + } + // get from the SUBnote + if (subnote) + { + noteplay++; + if (subnote->ready) + subnote->noteout(tmpoutl, tmpoutr); + else + { + memset(tmpoutl, 0, synth->bufferbytes); + memset(tmpoutr, 0, synth->bufferbytes); + } + for (int i = 0; i < synth->buffersize; ++i) + { // add the SUBnote to part(mix) + partfxinputl[sendcurrenttofx][i] += tmpoutl[i]; + partfxinputr[sendcurrenttofx][i] += tmpoutr[i]; + } + if (subnote->finished()) + { + Runtime.deadObjects->addBody(partnote[k].kititem[item].subnote); + partnote[k].kititem[item].subnote = NULL; + } + } + // get from the PADnote + if (padnote) + { + noteplay++; + if (padnote->ready) + { + padnote->noteout(tmpoutl, tmpoutr); + } + else + { + memset(tmpoutl, 0, synth->bufferbytes); + memset(tmpoutr, 0, synth->bufferbytes); + } + if (padnote->finished()) + { + Runtime.deadObjects->addBody(partnote[k].kititem[item].padnote); + partnote[k].kititem[item].padnote = NULL; + } + for (int i = 0 ; i < synth->buffersize; ++i) + { // add the PADnote to part(mix) + partfxinputl[sendcurrenttofx][i] += tmpoutl[i]; + partfxinputr[sendcurrenttofx][i] += tmpoutr[i]; + } + } + } + // Kill note if there is no synth on that note + if (noteplay == 0) + KillNotePos(k); + } + + // Apply part's effects and mix them + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + { + if (!Pefxbypass[nefx]) + { + partefx[nefx]->out(partfxinputl[nefx], partfxinputr[nefx]); + if (Pefxroute[nefx] == 2) + { + for (int i = 0; i < synth->buffersize; ++i) + { + partfxinputl[nefx + 1][i] += partefx[nefx]->efxoutl[i]; + partfxinputr[nefx + 1][i] += partefx[nefx]->efxoutr[i]; + } + } + } + int routeto = (Pefxroute[nefx] == 0) ? nefx + 1 : NUM_PART_EFX; + for (int i = 0; i < synth->buffersize; ++i) + { + partfxinputl[routeto][i] += partfxinputl[nefx][i]; + partfxinputr[routeto][i] += partfxinputr[nefx][i]; + } + } + memcpy(partoutl, partfxinputl[NUM_PART_EFX], synth->bufferbytes); + memcpy(partoutr, partfxinputr[NUM_PART_EFX], synth->bufferbytes); + + // Kill All Notes if killallnotes true + if (killallnotes) + { + for (int i = 0; i < synth->buffersize; ++i) + { + float tmp = (synth->buffersize - i) / synth->buffersize_f; + partoutl[i] *= tmp; + partoutr[i] *= tmp; + } + memset(tmpoutl, 0, synth->bufferbytes); + memset(tmpoutr, 0, synth->bufferbytes); + + for (int k = 0; k < POLIPHONY; ++k) + KillNotePos(k); + killallnotes = 0; + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + partefx[nefx]->cleanup(); + } + ctl->updateportamento(); +} + + +// Parameter control +void Part::setVolume(char value) +{ + Pvolume = value; + volume = dB2rap((Pvolume - 96.0f) / 96.0f * 40.0f) * ctl->expression.relvolume; +} + + +void Part::setPan(char value) +{ + Ppanning = value; + float t = ((Ppanning > 0) ? (float)(Ppanning - 1) : 0.0f) / 126.0f; + pangainL = cosf(t * PI / 2.0f); + pangainR = cosf((1.0f - t) * PI / 2.0f); +} + + +// Enable or disable a kit item +void Part::setkititemstatus(int kititem, int Penabled_) +{ + if (kititem == 0 && kititem >= NUM_KIT_ITEMS) + return; // nonexistent kit item and the first kit item is always enabled + kit[kititem].Penabled = Penabled_; + + bool resetallnotes = false; + if (!Penabled_) + { + kit[kititem].Pname.clear(); + if (kit[kititem].adpars) + { + delete kit[kititem].adpars; + kit[kititem].adpars = NULL; + } + if (kit[kititem].subpars) + { + delete kit[kititem].subpars; + kit[kititem].subpars = NULL; + } + if (kit[kititem].padpars) + { + delete kit[kititem].padpars; + kit[kititem].padpars = NULL; + resetallnotes = true; + } + } + else + { + if (!kit[kititem].adpars) + kit[kititem].adpars = new ADnoteParameters(microtonal, fft); + if (!kit[kititem].subpars) + kit[kititem].subpars = new SUBnoteParameters(); + if (!kit[kititem].padpars) + kit[kititem].padpars = new PADnoteParameters(fft); + } + + if (resetallnotes) + for (int k = 0; k < POLIPHONY; ++k) + KillNotePos(k); +} + + +void Part::add2XMLinstrument(XMLwrapper *xml) +{ + xml->beginbranch("INFO"); + xml->addparstr("name", Pname); + xml->addparstr("author", info.Pauthor); + xml->addparstr("comments", info.Pcomments); + xml->addpar("type",info.Ptype); + xml->endbranch(); + + + xml->beginbranch("INSTRUMENT_KIT"); + xml->addpar("kit_mode", Pkitmode); + xml->addparbool("drum_mode", Pdrummode); + + for (int i = 0; i < NUM_KIT_ITEMS; ++i) + { + xml->beginbranch("INSTRUMENT_KIT_ITEM",i); + xml->addparbool("enabled", kit[i].Penabled); + if (kit[i].Penabled) + { + xml->addparstr("name", kit[i].Pname.c_str()); + + xml->addparbool("muted", kit[i].Pmuted); + xml->addpar("min_key", kit[i].Pminkey); + xml->addpar("max_key", kit[i].Pmaxkey); + + xml->addpar("send_to_instrument_effect", kit[i].Psendtoparteffect); + + xml->addparbool("add_enabled", kit[i].Padenabled); + if (kit[i].Padenabled && kit[i].adpars) + { + xml->beginbranch("ADD_SYNTH_PARAMETERS"); + kit[i].adpars->add2XML(xml); + xml->endbranch(); + } + + xml->addparbool("sub_enabled", kit[i].Psubenabled); + if (kit[i].Psubenabled && kit[i].subpars) + { + xml->beginbranch("SUB_SYNTH_PARAMETERS"); + kit[i].subpars->add2XML(xml); + xml->endbranch(); + } + + xml->addparbool("pad_enabled", kit[i].Ppadenabled); + if (kit[i].Ppadenabled && kit[i].padpars) + { + xml->beginbranch("PAD_SYNTH_PARAMETERS"); + kit[i].padpars->add2XML(xml); + xml->endbranch(); + } + } + xml->endbranch(); + } + xml->endbranch(); + + xml->beginbranch("INSTRUMENT_EFFECTS"); + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + { + xml->beginbranch("INSTRUMENT_EFFECT",nefx); + xml->beginbranch("EFFECT"); + partefx[nefx]->add2XML(xml); + xml->endbranch(); + + xml->addpar("route", Pefxroute[nefx]); + partefx[nefx]->setdryonly(Pefxroute[nefx] == 2); + xml->addparbool("bypass",Pefxbypass[nefx]); + xml->endbranch(); + } + xml->endbranch(); +} + + +void Part::add2XML(XMLwrapper *xml) +{ + // parameters + xml->addparbool("enabled", Penabled); + if (!Penabled && xml->minimal) + return; + + xml->addpar("volume", Pvolume); + xml->addpar("panning", Ppanning); + + xml->addpar("min_key", Pminkey); + xml->addpar("max_key", Pmaxkey); + xml->addpar("key_shift", Pkeyshift); + xml->addpar("rcv_chn", Prcvchn); + + xml->addpar("velocity_sensing", Pvelsns); + xml->addpar("velocity_offset", Pveloffs); + + xml->addparbool("note_on", Pnoteon); + xml->addparbool("poly_mode", Ppolymode); + xml->addpar("legato_mode", Plegatomode); + xml->addpar("key_limit", Pkeylimit); + + xml->beginbranch("INSTRUMENT"); + add2XMLinstrument(xml); + xml->endbranch(); + + xml->beginbranch("CONTROLLER"); + ctl->add2XML(xml); + xml->endbranch(); +} + + +bool Part::saveXML(string filename) +{ + XMLwrapper *xml = new XMLwrapper(); + if (!xml) + { + Runtime.Log("Error, Part::saveXML failed to instantiate new XMLwrapper"); + return false; + } + xml->beginbranch("INSTRUMENT"); + add2XMLinstrument(xml); + xml->endbranch(); + bool result = xml->saveXMLfile(filename); + delete xml; + return result; +} + + +bool Part::loadXMLinstrument(string filename) +{ + XMLwrapper *xml = new XMLwrapper(); + if (!xml) + { + Runtime.Log("Error, Part failed to instantiate new XMLwrapper"); + return false; + } + + if (!xml->loadXMLfile(filename)) + { + Runtime.Log("Error, Part failed to load instrument file " + filename); + delete xml; + return false; + } + if (xml->enterbranch("INSTRUMENT") == 0) + { + Runtime.Log(filename + " is not an instrument file"); + return false; + } + Mute(); + defaultsinstrument(); + getfromXMLinstrument(xml); + applyparameters(); + Unmute(); + xml->exitbranch(); + delete xml; + return true; +} + + +void Part::applyparameters(void) +{ + for (int n = 0; n < NUM_KIT_ITEMS; ++n) + if (kit[n].Ppadenabled && kit[n].padpars != NULL) + kit[n].padpars->applyparameters(true); +} + + +void Part::getfromXMLinstrument(XMLwrapper *xml) +{ + if (xml->enterbranch("INFO")) + { + Pname = xml->getparstr("name"); + info.Pauthor = xml->getparstr("author"); + info.Pcomments = xml->getparstr("comments"); + info.Ptype = xml->getpar("type", info.Ptype, 0, 16); + xml->exitbranch(); + } + + if (xml->enterbranch("INSTRUMENT_KIT")) + { + Pkitmode = xml->getpar127("kit_mode", Pkitmode); + Pdrummode = xml->getparbool("drum_mode", Pdrummode); + setkititemstatus(0, 0); + for (int i = 0; i < NUM_KIT_ITEMS; ++i) + { + if (!xml->enterbranch("INSTRUMENT_KIT_ITEM", i)) + continue; + setkititemstatus(i, xml->getparbool("enabled", kit[i].Penabled)); + if (!kit[i].Penabled) + { + xml->exitbranch(); + continue; + } + kit[i].Pname = xml->getparstr("name"); + kit[i].Pmuted = xml->getparbool("muted", kit[i].Pmuted); + kit[i].Pminkey = xml->getpar127("min_key", kit[i].Pminkey); + kit[i].Pmaxkey = xml->getpar127("max_key", kit[i].Pmaxkey); + kit[i].Psendtoparteffect = xml->getpar127("send_to_instrument_effect", + kit[i].Psendtoparteffect); + kit[i].Padenabled = xml->getparbool("add_enabled", kit[i].Padenabled); + if (xml->enterbranch("ADD_SYNTH_PARAMETERS")) + { + kit[i].adpars->getfromXML(xml); + xml->exitbranch(); + } + kit[i].Psubenabled = xml->getparbool("sub_enabled", kit[i].Psubenabled); + if (xml->enterbranch("SUB_SYNTH_PARAMETERS")) + { + kit[i].subpars->getfromXML(xml); + xml->exitbranch(); + } + kit[i].Ppadenabled = xml->getparbool("pad_enabled", kit[i].Ppadenabled); + if (xml->enterbranch("PAD_SYNTH_PARAMETERS")) + { + kit[i].padpars->getfromXML(xml); + xml->exitbranch(); + } + xml->exitbranch(); + } + xml->exitbranch(); + } + if (xml->enterbranch("INSTRUMENT_EFFECTS")) + { + for (int nefx = 0; nefx < NUM_PART_EFX; ++nefx) + { + if (!xml->enterbranch("INSTRUMENT_EFFECT", nefx)) + continue; + if (xml->enterbranch("EFFECT")) + { + partefx[nefx]->getfromXML(xml); + xml->exitbranch(); + } + Pefxroute[nefx] = xml->getpar("route", Pefxroute[nefx], 0, NUM_PART_EFX); + partefx[nefx]->setdryonly(Pefxroute[nefx] == 2); + Pefxbypass[nefx] = xml->getparbool("bypass", Pefxbypass[nefx]); + xml->exitbranch(); + } + xml->exitbranch(); + } +} + + +void Part::getfromXML(XMLwrapper *xml) +{ + Penabled = xml->getparbool("enabled", Penabled); + + setVolume(xml->getpar127("volume", Pvolume)); + setPan(xml->getpar127("panning", Ppanning)); + + Pminkey = xml->getpar127("min_key", Pminkey); + Pmaxkey = xml->getpar127("max_key", Pmaxkey); + Pkeyshift = xml->getpar127("key_shift", Pkeyshift); + Prcvchn = xml->getpar127("rcv_chn", Prcvchn); + + Pvelsns = xml->getpar127("velocity_sensing", Pvelsns); + Pveloffs = xml->getpar127("velocity_offset", Pveloffs); + + Pnoteon = xml->getparbool("note_on", Pnoteon); + Ppolymode = xml->getparbool("poly_mode", Ppolymode); + Plegatomode = xml->getparbool("legato_mode", Plegatomode); // older versions + if (!Plegatomode) + Plegatomode = xml->getpar127("legato_mode", Plegatomode); + Pkeylimit = xml->getpar127("key_limit", Pkeylimit); + if (xml->enterbranch("INSTRUMENT")) + { + getfromXMLinstrument(xml); + xml->exitbranch(); + applyparameters(); + } + if (xml->enterbranch("CONTROLLER")) + { + ctl->getfromXML(xml); + xml->exitbranch(); + } +} diff -Nru yoshimi-0.060.10/src/MusicIO/MidiControl.h yoshimi-0.060.12/src/MusicIO/MidiControl.h --- yoshimi-0.060.10/src/MusicIO/MidiControl.h 2011-04-11 23:20:29.000000000 +0000 +++ yoshimi-0.060.12/src/MusicIO/MidiControl.h 2012-01-02 20:08:33.000000000 +0000 @@ -33,6 +33,7 @@ C_fmamp = 76, C_volume = 7, C_sustain = 64, + C_legatofootswitch = 68, C_allnotesoff = 123, C_allsoundsoff = 120, C_resetallcontrollers = 121, diff -Nru yoshimi-0.060.10/src/Params/ADnoteParameters.cpp yoshimi-0.060.12/src/Params/ADnoteParameters.cpp --- yoshimi-0.060.10/src/Params/ADnoteParameters.cpp 2011-04-11 23:20:29.000000000 +0000 +++ yoshimi-0.060.12/src/Params/ADnoteParameters.cpp 2012-01-03 19:57:16.000000000 +0000 @@ -35,9 +35,8 @@ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 0 }; -ADnoteParameters::ADnoteParameters(Microtonal *micro_, FFTwrapper *fft_) : +ADnoteParameters::ADnoteParameters(FFTwrapper *fft_) : Presets(), - microtonal(micro_), fft(fft_) { setpresettype("ADnoteParameters"); diff -Nru yoshimi-0.060.10/src/Params/ADnoteParameters.h yoshimi-0.060.12/src/Params/ADnoteParameters.h --- yoshimi-0.060.10/src/Params/ADnoteParameters.h 2011-04-11 01:30:58.000000000 +0000 +++ yoshimi-0.060.12/src/Params/ADnoteParameters.h 2012-01-03 19:57:16.000000000 +0000 @@ -34,8 +34,6 @@ #include "DSP/FFTwrapper.h" #include "Params/Presets.h" -class Microtonal; - enum FMTYPE { NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PITCH_MOD }; extern int ADnote_unison_sizes[]; @@ -171,7 +169,7 @@ class ADnoteParameters : public Presets { public: - ADnoteParameters(Microtonal *micro_, FFTwrapper *fft_); + ADnoteParameters(FFTwrapper *fft_); ~ADnoteParameters(); void defaults(void); void add2XML(XMLwrapper *xml); @@ -187,7 +185,6 @@ ADnoteGlobalParam GlobalPar; ADnoteVoiceParam VoicePar[NUM_VOICES]; - Microtonal *microtonal; static int ADnote_unison_sizes[15]; private: diff -Nru yoshimi-0.060.10/src/Params/Controller.cpp yoshimi-0.060.12/src/Params/Controller.cpp --- yoshimi-0.060.10/src/Params/Controller.cpp 2011-04-11 23:20:29.000000000 +0000 +++ yoshimi-0.060.12/src/Params/Controller.cpp 2012-01-02 20:08:33.000000000 +0000 @@ -80,6 +80,7 @@ setfmamp(127); setvolume(127); setsustain(0); + setlegato(0); setresonancecenter(64); setresonancebw(64); } @@ -194,6 +195,12 @@ } +void Controller::setlegato(int value) +{ + legato.data = value; + legato.legato = (value < 64) ? 0 : 1; +} + void Controller::setportamento(int value) { portamento.data = value; diff -Nru yoshimi-0.060.10/src/Params/Controller.h yoshimi-0.060.12/src/Params/Controller.h --- yoshimi-0.060.10/src/Params/Controller.h 2011-04-11 01:31:16.000000000 +0000 +++ yoshimi-0.060.12/src/Params/Controller.h 2012-01-02 20:08:33.000000000 +0000 @@ -50,6 +50,7 @@ void setfmamp(int value); void setvolume(int value); void setsustain(int value); + void setlegato(int value); void setportamento(int value); void setresonancecenter(int value); void setresonancebw(int value); @@ -119,6 +120,11 @@ unsigned char receive; } sustain; + struct { // Legato + int data; + int legato; + } legato; + struct { // Portamento // parameters int data; diff -Nru yoshimi-0.060.10/src/Synth/ADnote.cpp yoshimi-0.060.12/src/Synth/ADnote.cpp --- yoshimi-0.060.10/src/Synth/ADnote.cpp 2011-04-11 23:20:29.000000000 +0000 +++ yoshimi-0.060.12/src/Synth/ADnote.cpp 2012-01-03 19:57:16.000000000 +0000 @@ -35,14 +35,14 @@ #include "Misc/SynthEngine.h" #include "Synth/ADnote.h" -ADnote::ADnote(ADnoteParameters *adpars_, Controller *ctl_, float velocity_, - int portamento_, int midinote_, bool besilent) : +ADnote::ADnote(ADnoteParameters *adpars_, Controller *ctl_, float freq_, + float velocity_, int portamento_, int midinote_, bool besilent) : ready(0), adpars(adpars_), stereo(adpars->GlobalPar.PStereo), midinote(midinote_), velocity(velocity_), - basefreq(adpars->microtonal->getNoteFreq(midinote_)), + basefreq(freq_), NoteEnabled(true), ctl(ctl_), time(0.0f), @@ -1039,7 +1039,7 @@ + NoteGlobalPar.Detune / 100.0f; if (!NoteVoicePar[nvoice].fixedfreq) - return adpars->microtonal->getNoteFreq(midinote) * powf(2.0f, detune / 12.0f); + return basefreq * powf(2.0f, detune / 12.0f); else // fixed freq is enabled { float fixedfreq = 440.0f; diff -Nru yoshimi-0.060.10/src/Synth/ADnote.h yoshimi-0.060.12/src/Synth/ADnote.h --- yoshimi-0.060.10/src/Synth/ADnote.h 2011-04-11 23:20:29.000000000 +0000 +++ yoshimi-0.060.12/src/Synth/ADnote.h 2012-01-03 19:57:16.000000000 +0000 @@ -45,7 +45,7 @@ class ADnote : public Carcass, private SynthHelper, private Float2Int { public: - ADnote(ADnoteParameters *adpars_, Controller *ctl_, float velocity_, + ADnote(ADnoteParameters *adpars_, Controller *ctl_, float freq_, float velocity_, int portamento_, int midinote_, bool besilent); ~ADnote(); diff -Nru yoshimi-0.060.10/src/UI/BankUI.fl yoshimi-0.060.12/src/UI/BankUI.fl --- yoshimi-0.060.10/src/UI/BankUI.fl 2011-04-11 23:20:30.000000000 +0000 +++ yoshimi-0.060.12/src/UI/BankUI.fl 2012-01-02 20:01:20.000000000 +0000 @@ -51,7 +51,7 @@ class BankProcess_ {open } { Function {process(void)} {open return_type {virtual void} - } {} + } {code { ; } {} } decl {Bank *bank;} {public } } diff -Nru yoshimi-0.060.10/src/UI/OscilGenUI.fl yoshimi-0.060.12/src/UI/OscilGenUI.fl --- yoshimi-0.060.10/src/UI/OscilGenUI.fl 2011-04-11 23:20:30.000000000 +0000 +++ yoshimi-0.060.12/src/UI/OscilGenUI.fl 2012-01-02 20:01:20.000000000 +0000 @@ -166,7 +166,7 @@ class PSlider {open : {public Fl_Slider} } { - Function {PSlider(int x,int y, int w, int h, const char *label=0):Fl_Slider(x,y,w,h,label)} {} {} + Function {PSlider(int x,int y, int w, int h, const char *label=0):Fl_Slider(x,y,w,h,label)} {} {code { ; } {} } Function {handle(int event)} {return_type int } { code {int X=x(),Y=y(),W=w(),H=h(); diff -Nru yoshimi-0.060.10/src/UI/PartUI.fl yoshimi-0.060.12/src/UI/PartUI.fl --- yoshimi-0.060.10/src/UI/PartUI.fl 2011-04-11 23:20:30.000000000 +0000 +++ yoshimi-0.060.12/src/UI/PartUI.fl 2012-01-02 20:01:21.000000000 +0000 @@ -89,7 +89,7 @@ class PartUI_ {open } { Function {showparameters(int kititem,int engine)} {open return_type virtual - } {} + } {code { ; } {} } } class PartKitItem {: {public Fl_Group} diff -Nru yoshimi-0.060.10/src/UI/PresetsUI.fl yoshimi-0.060.12/src/UI/PresetsUI.fl --- yoshimi-0.060.10/src/UI/PresetsUI.fl 2011-04-11 23:20:30.000000000 +0000 +++ yoshimi-0.060.12/src/UI/PresetsUI.fl 2012-01-02 20:01:21.000000000 +0000 @@ -44,8 +44,8 @@ class PresetsUI_ {} { Function {refresh()} {return_type {virtual void} - } {} - Function {~PresetsUI_()} {} {} + } {code { ; } {} } + Function {~PresetsUI_()} {} {code { ; } {} } } class PresetsUI {} { diff -Nru yoshimi-0.060.10/src/version.txt yoshimi-0.060.12/src/version.txt --- yoshimi-0.060.10/src/version.txt 2011-04-11 23:35:10.000000000 +0000 +++ yoshimi-0.060.12/src/version.txt 2012-01-03 20:16:06.000000000 +0000 @@ -1 +1 @@ -0.060.10 \ No newline at end of file +0.060.12