2019-02-28 14:56:23 -05:00
|
|
|
/* reasonably simple synth
|
2013-10-19 22:31:07 -04:00
|
|
|
*
|
2019-02-28 14:56:23 -05:00
|
|
|
* Copyright (C) 2013, 2019 Robin Gareus <robin@gareus.org>
|
2013-10-19 22:31:07 -04:00
|
|
|
*
|
|
|
|
* 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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE // needed for M_PI
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <math.h>
|
2019-03-01 09:59:41 -05:00
|
|
|
#include <stdint.h>
|
2013-10-19 22:31:07 -04:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2014-11-09 03:11:46 -05:00
|
|
|
|
|
|
|
#ifndef COMPILER_MSVC
|
2013-12-27 08:53:30 -05:00
|
|
|
#include <stdbool.h>
|
2014-11-09 03:11:46 -05:00
|
|
|
#endif
|
|
|
|
|
2013-10-19 22:31:07 -04:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#ifndef BUFFER_SIZE_SAMPLES
|
|
|
|
#define BUFFER_SIZE_SAMPLES 64
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef MIN
|
2019-03-01 09:59:41 -05:00
|
|
|
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
2013-10-19 22:31:07 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* internal MIDI event abstraction */
|
|
|
|
enum RMIDI_EV_TYPE {
|
2019-03-01 09:59:41 -05:00
|
|
|
INVALID = 0,
|
2019-02-28 14:56:23 -05:00
|
|
|
NOTE_ON,
|
|
|
|
NOTE_OFF,
|
|
|
|
PROGRAM_CHANGE,
|
|
|
|
CONTROL_CHANGE,
|
2013-10-19 22:31:07 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct rmidi_event_t {
|
2019-02-28 14:56:23 -05:00
|
|
|
enum RMIDI_EV_TYPE type;
|
|
|
|
uint8_t channel; /**< the MIDI channel number 0-15 */
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
uint8_t note;
|
|
|
|
uint8_t velocity;
|
|
|
|
} tone;
|
|
|
|
struct {
|
|
|
|
uint8_t param;
|
|
|
|
uint8_t value;
|
|
|
|
} control;
|
|
|
|
} d;
|
2013-10-19 22:31:07 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
2019-02-28 14:56:23 -05:00
|
|
|
uint32_t tme[3]; // attack, decay, release times [settings:ms || internal:samples]
|
|
|
|
float vol[2]; // attack, sustain volume [0..1]
|
|
|
|
uint32_t off[3]; // internal use (added attack,decay,release times)
|
2013-10-19 22:31:07 -04:00
|
|
|
} ADSRcfg;
|
|
|
|
|
|
|
|
typedef struct _RSSynthChannel {
|
2019-03-01 09:59:41 -05:00
|
|
|
uint32_t keycomp;
|
|
|
|
uint32_t adsr_cnt[128];
|
|
|
|
float adsr_amp[128];
|
|
|
|
float phase[128]; // various use, zero'ed on note-on
|
|
|
|
int8_t miditable[128]; // internal, note-on/off velocity
|
|
|
|
int8_t midimsgs[128]; // internal, note-off + on in same cycle, sustained-off
|
|
|
|
int8_t sustain; // sustain pedal pressed
|
|
|
|
ADSRcfg adsr;
|
|
|
|
void (*synthesize) (struct _RSSynthChannel* sc,
|
|
|
|
const uint8_t note, const float vol, const float pc,
|
|
|
|
const size_t n_samples, float* left, float* right);
|
2013-10-19 22:31:07 -04:00
|
|
|
} RSSynthChannel;
|
|
|
|
|
|
|
|
typedef void (*SynthFunction) (RSSynthChannel* sc,
|
2019-03-01 09:59:41 -05:00
|
|
|
const uint8_t note,
|
|
|
|
const float vol,
|
|
|
|
const float pc,
|
|
|
|
const size_t n_samples,
|
2019-02-28 14:56:23 -05:00
|
|
|
float* left, float* right);
|
2013-10-19 22:31:07 -04:00
|
|
|
|
|
|
|
typedef struct {
|
2019-02-28 14:56:23 -05:00
|
|
|
uint32_t boffset;
|
2019-03-01 09:59:41 -05:00
|
|
|
float buf[2][BUFFER_SIZE_SAMPLES];
|
2019-02-28 14:56:23 -05:00
|
|
|
RSSynthChannel sc[16];
|
|
|
|
float freqs[128];
|
|
|
|
float kcgain;
|
|
|
|
float kcfilt;
|
|
|
|
double rate;
|
|
|
|
uint32_t xmas_on;
|
|
|
|
uint32_t xmas_off;
|
2013-10-19 22:31:07 -04:00
|
|
|
} RSSynthesizer;
|
|
|
|
|
|
|
|
/* initialize ADSR values
|
|
|
|
*
|
|
|
|
* @param rate sample-rate
|
|
|
|
* @param a attack time in seconds
|
|
|
|
* @param d decay time in seconds
|
|
|
|
* @param r release time in seconds
|
|
|
|
* @param avol attack gain [0..1]
|
|
|
|
* @param svol sustain volume level [0..1]
|
|
|
|
*/
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
init_adsr (ADSRcfg* adsr, const double rate,
|
2019-02-28 14:56:23 -05:00
|
|
|
const uint32_t a, const uint32_t d, const uint32_t r,
|
|
|
|
const float avol, const float svol)
|
|
|
|
{
|
|
|
|
adsr->vol[0] = avol;
|
|
|
|
adsr->vol[1] = svol;
|
|
|
|
adsr->tme[0] = a * rate / 1000.0;
|
|
|
|
adsr->tme[1] = d * rate / 1000.0;
|
|
|
|
adsr->tme[2] = r * rate / 1000.0;
|
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
assert (adsr->tme[0] > 32);
|
|
|
|
assert (adsr->tme[1] > 32);
|
|
|
|
assert (adsr->tme[2] > 32);
|
|
|
|
assert (adsr->vol[0] >= 0 && adsr->vol[1] <= 1.0);
|
|
|
|
assert (adsr->vol[1] >= 0 && adsr->vol[1] <= 1.0);
|
2019-02-28 14:56:23 -05:00
|
|
|
|
|
|
|
adsr->off[0] = adsr->tme[0];
|
|
|
|
adsr->off[1] = adsr->tme[1] + adsr->off[0];
|
|
|
|
adsr->off[2] = adsr->tme[2] + adsr->off[1];
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate per-sample, per-key envelope */
|
2019-02-28 14:56:23 -05:00
|
|
|
static inline float
|
2019-03-01 09:59:41 -05:00
|
|
|
adsr_env (RSSynthChannel* sc, const uint8_t note)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
|
|
|
if (sc->adsr_cnt[note] < sc->adsr.off[0]) {
|
|
|
|
// attack
|
|
|
|
const uint32_t p = ++sc->adsr_cnt[note];
|
|
|
|
if (p == sc->adsr.tme[0]) {
|
|
|
|
sc->adsr_amp[note] = sc->adsr.vol[0];
|
|
|
|
return sc->adsr.vol[0];
|
|
|
|
} else {
|
|
|
|
const float d = sc->adsr.vol[0] - sc->adsr_amp[note];
|
2019-03-01 09:59:41 -05:00
|
|
|
return sc->adsr_amp[note] + (p / (float)sc->adsr.tme[0]) * d;
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
} else if (sc->adsr_cnt[note] < sc->adsr.off[1]) {
|
2019-02-28 14:56:23 -05:00
|
|
|
// decay
|
|
|
|
const uint32_t p = ++sc->adsr_cnt[note] - sc->adsr.off[0];
|
|
|
|
if (p == sc->adsr.tme[1]) {
|
|
|
|
sc->adsr_amp[note] = sc->adsr.vol[1];
|
|
|
|
return sc->adsr.vol[1];
|
|
|
|
} else {
|
|
|
|
const float d = sc->adsr.vol[1] - sc->adsr_amp[note];
|
2019-03-01 09:59:41 -05:00
|
|
|
return sc->adsr_amp[note] + (p / (float)sc->adsr.tme[1]) * d;
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
} else if (sc->adsr_cnt[note] == sc->adsr.off[1]) {
|
2019-02-28 14:56:23 -05:00
|
|
|
// sustain
|
|
|
|
return sc->adsr.vol[1];
|
2019-03-01 09:59:41 -05:00
|
|
|
} else if (sc->adsr_cnt[note] < sc->adsr.off[2]) {
|
2019-02-28 14:56:23 -05:00
|
|
|
// release
|
|
|
|
const uint32_t p = ++sc->adsr_cnt[note] - sc->adsr.off[1];
|
|
|
|
if (p == sc->adsr.tme[2]) {
|
|
|
|
sc->adsr_amp[note] = 0;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
const float d = 0 - sc->adsr_amp[note];
|
2019-03-01 09:59:41 -05:00
|
|
|
return sc->adsr_amp[note] + (p / (float)sc->adsr.tme[2]) * d;
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
} else {
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->adsr_cnt[note] = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* piano like sound w/slight stereo phase */
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
|
|
|
synthesize_sineP (RSSynthChannel* sc,
|
|
|
|
const uint8_t note, const float vol, const float fq,
|
|
|
|
const size_t n_samples, float* left, float* right)
|
|
|
|
{
|
|
|
|
size_t i;
|
2019-03-01 09:59:41 -05:00
|
|
|
float phase = sc->phase[note];
|
2019-02-28 14:56:23 -05:00
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
for (i = 0; i < n_samples; ++i) {
|
|
|
|
float env = adsr_env (sc, note);
|
|
|
|
if (sc->adsr_cnt[note] == 0) {
|
|
|
|
break;
|
|
|
|
}
|
2019-02-28 14:56:23 -05:00
|
|
|
const float amp = vol * env;
|
|
|
|
if (amp > 1e-10) {
|
2019-03-01 09:59:41 -05:00
|
|
|
left[i] += amp * sinf (2.0 * M_PI * phase);
|
|
|
|
left[i] += .300 * amp * sinf (2.0 * M_PI * phase * 2.0);
|
|
|
|
left[i] += .150 * amp * sinf (2.0 * M_PI * phase * 3.0);
|
|
|
|
left[i] += .080 * amp * sinf (2.0 * M_PI * phase * 4.0);
|
|
|
|
//left[i] -= .007 * amp * sinf(2.0 * M_PI * phase * 5.0);
|
|
|
|
//left[i] += .010 * amp * sinf(2.0 * M_PI * phase * 6.0);
|
|
|
|
left[i] += .020 * amp * sinf (2.0 * M_PI * phase * 7.0);
|
2019-02-28 14:56:23 -05:00
|
|
|
phase += fq;
|
2019-03-01 09:59:41 -05:00
|
|
|
right[i] += amp * sinf (2.0 * M_PI * phase);
|
|
|
|
right[i] += .300 * amp * sinf (2.0 * M_PI * phase * 2.0);
|
|
|
|
right[i] += .150 * amp * sinf (2.0 * M_PI * phase * 3.0);
|
|
|
|
right[i] -= .080 * amp * sinf (2.0 * M_PI * phase * 4.0);
|
2019-02-28 14:56:23 -05:00
|
|
|
//right[i] += .007 * amp * sinf(2.0 * M_PI * phase * 5.0);
|
|
|
|
//right[i] += .010 * amp * sinf(2.0 * M_PI * phase * 6.0);
|
2019-03-01 09:59:41 -05:00
|
|
|
right[i] -= .020 * amp * sinf (2.0 * M_PI * phase * 7.0);
|
2019-02-28 14:56:23 -05:00
|
|
|
} else {
|
|
|
|
phase += fq;
|
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
if (phase > 1.0)
|
|
|
|
phase -= 2.0;
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
|
|
|
sc->phase[note] = phase;
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
static const ADSRcfg piano_adsr = { { 5, 800, 100 }, { 1.0, 0.0 }, { 0, 0, 0 } };
|
2013-10-19 22:31:07 -04:00
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
/* process note - move through ADSR states, count active keys,.. */
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
process_key (void* synth,
|
2019-02-28 14:56:23 -05:00
|
|
|
const uint8_t chn, const uint8_t note,
|
|
|
|
const size_t n_samples,
|
2019-03-01 09:59:41 -05:00
|
|
|
float* left, float* right)
|
2013-10-19 22:31:07 -04:00
|
|
|
{
|
2019-03-01 09:59:41 -05:00
|
|
|
RSSynthesizer* rs = (RSSynthesizer*)synth;
|
|
|
|
RSSynthChannel* sc = &rs->sc[chn];
|
|
|
|
const int8_t vel = sc->miditable[note];
|
|
|
|
const int8_t msg = sc->midimsgs[note];
|
|
|
|
const float vol = /* master_volume */ 0.1f * abs (vel) / 127.f;
|
|
|
|
const float phase = sc->phase[note];
|
|
|
|
const int8_t sus = sc->sustain;
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->midimsgs[note] &= ~3;
|
|
|
|
|
|
|
|
if (phase == -10 && vel > 0) {
|
|
|
|
// new note on
|
|
|
|
sc->midimsgs[note] &= ~4;
|
2019-03-01 09:59:41 -05:00
|
|
|
assert (sc->adsr_cnt[note] == 0);
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->adsr_amp[note] = 0;
|
|
|
|
sc->adsr_cnt[note] = 0;
|
2019-03-01 09:59:41 -05:00
|
|
|
sc->phase[note] = 0;
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->keycomp++;
|
|
|
|
//printf("[On] Now %d keys active on chn %d\n", sc->keycomp, chn);
|
2019-03-01 09:59:41 -05:00
|
|
|
} else if (phase >= -1.0 && phase <= 1.0 && vel > 0) {
|
2019-02-28 14:56:23 -05:00
|
|
|
// sustain note or re-start note while adsr in progress:
|
|
|
|
if (sc->adsr_cnt[note] > sc->adsr.off[1] || msg == 3 || msg == 5 || msg == 7) {
|
|
|
|
sc->midimsgs[note] &= ~4;
|
|
|
|
// x-fade to attack
|
2019-03-01 09:59:41 -05:00
|
|
|
sc->adsr_amp[note] = adsr_env (sc, note);
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->adsr_cnt[note] = 0;
|
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
} else if (phase >= -1.0 && phase <= 1.0 && vel < 0) {
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->midimsgs[note] |= 4;
|
|
|
|
// note off
|
|
|
|
if (sc->adsr_cnt[note] <= sc->adsr.off[1] && !sus) {
|
|
|
|
if (sc->adsr_cnt[note] != sc->adsr.off[1]) {
|
|
|
|
// x-fade to release
|
2019-03-01 09:59:41 -05:00
|
|
|
sc->adsr_amp[note] = adsr_env (sc, note);
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
|
|
|
sc->adsr_cnt[note] = sc->adsr.off[1] + 1;
|
2019-03-01 09:59:41 -05:00
|
|
|
} else if (sus && sc->adsr_cnt[note] == sc->adsr.off[1]) {
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->adsr_cnt[note] = sc->adsr.off[1] + 1;
|
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
} else {
|
2019-02-28 14:56:23 -05:00
|
|
|
//printf("FORCE NOTE OFF: %d %d\n", vel, sus);
|
|
|
|
/* note-on + off in same cycle */
|
|
|
|
sc->miditable[note] = 0;
|
2019-03-01 09:59:41 -05:00
|
|
|
sc->adsr_cnt[note] = 0;
|
|
|
|
sc->phase[note] = -10;
|
2019-02-28 14:56:23 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
//printf("NOTE: %d (%d %d %d)\n", sc->adsr_cnt[note], sc->adsr.off[0], sc->adsr.off[1], sc->adsr.off[2]);
|
|
|
|
|
|
|
|
// synthesize actual sound
|
2019-03-01 09:59:41 -05:00
|
|
|
sc->synthesize (sc, note, vol, rs->freqs[note], n_samples, left, right);
|
2019-02-28 14:56:23 -05:00
|
|
|
|
|
|
|
if (sc->adsr_cnt[note] == 0) {
|
|
|
|
//printf("Note %d,%d released\n", chn, note);
|
2019-03-01 09:59:41 -05:00
|
|
|
sc->midimsgs[note] = 0;
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->miditable[note] = 0;
|
2019-03-01 09:59:41 -05:00
|
|
|
sc->adsr_amp[note] = 0;
|
|
|
|
sc->phase[note] = -10;
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->keycomp--;
|
|
|
|
//printf("[off] Now %d keys active on chn %d\n", sc->keycomp, chn);
|
|
|
|
}
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* synthesize a BUFFER_SIZE_SAMPLES's of audio-data */
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_fragment (void* synth, const size_t n_samples, float* left, float* right)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
|
|
|
RSSynthesizer* rs = (RSSynthesizer*)synth;
|
2019-03-01 09:59:41 -05:00
|
|
|
memset (left, 0, n_samples * sizeof (float));
|
|
|
|
memset (right, 0, n_samples * sizeof (float));
|
2019-02-28 14:56:23 -05:00
|
|
|
uint8_t keycomp = 0;
|
2019-03-01 09:59:41 -05:00
|
|
|
int c, k;
|
|
|
|
size_t i;
|
2019-02-28 14:56:23 -05:00
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
for (c = 0; c < 16; ++c) {
|
|
|
|
for (k = 0; k < 128; ++k) {
|
|
|
|
if (rs->sc[c].miditable[k] == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
process_key (synth, c, k, n_samples, left, right);
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
|
|
|
keycomp += rs->sc[c].keycomp;
|
|
|
|
}
|
2013-10-19 22:31:07 -04:00
|
|
|
|
|
|
|
#if 1 // key-compression
|
2019-02-28 14:56:23 -05:00
|
|
|
float kctgt = 8.0 / (float)(keycomp + 7.0);
|
2019-03-01 09:59:41 -05:00
|
|
|
if (kctgt < .5) kctgt = .5;
|
2019-02-28 14:56:23 -05:00
|
|
|
if (kctgt > 1.0) kctgt = 1.0;
|
2019-03-01 09:59:41 -05:00
|
|
|
|
2019-02-28 14:56:23 -05:00
|
|
|
const float _w = rs->kcfilt;
|
2019-03-01 09:59:41 -05:00
|
|
|
for (i = 0; i < n_samples; ++i) {
|
2019-02-28 14:56:23 -05:00
|
|
|
rs->kcgain += _w * (kctgt - rs->kcgain);
|
|
|
|
left[i] *= rs->kcgain;
|
|
|
|
right[i] *= rs->kcgain;
|
|
|
|
}
|
|
|
|
rs->kcgain += 1e-12;
|
2013-10-19 22:31:07 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_reset_channel (RSSynthChannel* sc)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
2019-03-01 09:51:17 -05:00
|
|
|
int k;
|
|
|
|
for (k = 0; k < 128; ++k) {
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->adsr_cnt[k] = 0;
|
|
|
|
sc->adsr_amp[k] = 0;
|
|
|
|
sc->phase[k] = -10;
|
|
|
|
sc->miditable[k] = 0;
|
|
|
|
sc->midimsgs[k] = 0;
|
|
|
|
}
|
|
|
|
sc->keycomp = 0;
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_reset (void* synth)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
|
|
|
RSSynthesizer* rs = (RSSynthesizer*)synth;
|
2019-03-01 09:59:41 -05:00
|
|
|
int c;
|
|
|
|
for (c = 0; c < 16; ++c) {
|
|
|
|
synth_reset_channel (&(rs->sc[c]));
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
|
|
|
rs->kcgain = 0;
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_load (RSSynthChannel* sc, const double rate,
|
|
|
|
SynthFunction synthesize,
|
|
|
|
ADSRcfg const* const adsr)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_reset_channel (sc);
|
|
|
|
init_adsr (&sc->adsr, rate,
|
|
|
|
adsr->tme[0], adsr->tme[1], adsr->tme[2],
|
|
|
|
adsr->vol[0], adsr->vol[1]);
|
2019-02-28 14:56:23 -05:00
|
|
|
sc->synthesize = synthesize;
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* internal abstraction of MIDI data handling
|
|
|
|
*/
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_process_midi_event (void* synth, struct rmidi_event_t* ev)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
|
|
|
RSSynthesizer* rs = (RSSynthesizer*)synth;
|
2019-03-01 09:59:41 -05:00
|
|
|
switch (ev->type) {
|
2019-02-28 14:56:23 -05:00
|
|
|
case NOTE_ON:
|
|
|
|
rs->sc[ev->channel].midimsgs[ev->d.tone.note] |= 1;
|
|
|
|
if (rs->sc[ev->channel].miditable[ev->d.tone.note] <= 0)
|
|
|
|
rs->sc[ev->channel].miditable[ev->d.tone.note] = ev->d.tone.velocity;
|
|
|
|
break;
|
|
|
|
case NOTE_OFF:
|
|
|
|
rs->sc[ev->channel].midimsgs[ev->d.tone.note] |= 2;
|
|
|
|
if (rs->sc[ev->channel].miditable[ev->d.tone.note] > 0)
|
|
|
|
rs->sc[ev->channel].miditable[ev->d.tone.note] *= -1.0;
|
|
|
|
break;
|
|
|
|
case PROGRAM_CHANGE:
|
|
|
|
break;
|
|
|
|
case CONTROL_CHANGE:
|
|
|
|
if (ev->d.control.param == 0x00 || ev->d.control.param == 0x20) {
|
|
|
|
/* 0x00 and 0x20 are used for BANK select */
|
|
|
|
} else if (ev->d.control.param == 64) {
|
|
|
|
/* damper pedal*/
|
2019-03-01 09:59:41 -05:00
|
|
|
rs->sc[ev->channel].sustain = ev->d.control.value < 64 ? 0 : 1;
|
2019-02-28 14:56:23 -05:00
|
|
|
} else if (ev->d.control.param == 121) {
|
|
|
|
/* reset all controllers */
|
|
|
|
} else if (ev->d.control.param == 120 || ev->d.control.param == 123) {
|
|
|
|
/* Midi panic: 120: all sound off, 123: all notes off*/
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_reset_channel (&(rs->sc[ev->channel]));
|
2019-02-28 14:56:23 -05:00
|
|
|
} else if (ev->d.control.param >= 120) {
|
|
|
|
/* params 122-127 are reserved - skip them. */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* PUBLIC API (used by lv2.c)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* align LV2 and internal synth buffers
|
|
|
|
* call synth_fragment as often as needed for the given LV2 buffer size
|
|
|
|
*
|
|
|
|
* @param synth synth-handle
|
|
|
|
* @param written samples written so far (offset in \ref out)
|
|
|
|
* @param nframes total samples to synthesize and write to the \out buffer
|
|
|
|
* @param out pointer to stereo output buffers
|
|
|
|
* @return end of buffer (written + nframes)
|
|
|
|
*/
|
2019-02-28 14:56:23 -05:00
|
|
|
static uint32_t
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_sound (void* synth, uint32_t written, const uint32_t nframes, float** out)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
|
|
|
RSSynthesizer* rs = (RSSynthesizer*)synth;
|
2013-10-19 22:31:07 -04:00
|
|
|
|
2019-02-28 14:56:23 -05:00
|
|
|
while (written < nframes) {
|
|
|
|
uint32_t nremain = nframes - written;
|
2013-10-19 22:31:07 -04:00
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
if (rs->boffset >= BUFFER_SIZE_SAMPLES) {
|
|
|
|
const uint32_t tosynth = MIN (BUFFER_SIZE_SAMPLES, nremain);
|
|
|
|
rs->boffset = BUFFER_SIZE_SAMPLES - tosynth;
|
|
|
|
synth_fragment (rs, tosynth, &(rs->buf[0][rs->boffset]), &(rs->buf[1][rs->boffset]));
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
2013-10-19 22:31:07 -04:00
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
uint32_t nread = MIN (nremain, (BUFFER_SIZE_SAMPLES - rs->boffset));
|
2013-10-19 22:31:07 -04:00
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
memcpy (&out[0][written], &rs->buf[0][rs->boffset], nread * sizeof (float));
|
|
|
|
memcpy (&out[1][written], &rs->buf[1][rs->boffset], nread * sizeof (float));
|
2013-10-19 22:31:07 -04:00
|
|
|
|
2019-02-28 14:56:23 -05:00
|
|
|
written += nread;
|
|
|
|
rs->boffset += nread;
|
|
|
|
}
|
|
|
|
return written;
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse raw midi-data.
|
|
|
|
*
|
|
|
|
* @param synth synth-handle
|
|
|
|
* @param data 8bit midi message
|
|
|
|
* @param size number of bytes in the midi-message
|
|
|
|
*/
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_parse_midi (void* synth, const uint8_t* data, const size_t size)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
2019-03-01 09:59:41 -05:00
|
|
|
if (size < 2 || size > 3)
|
|
|
|
return;
|
2019-02-28 14:56:23 -05:00
|
|
|
// All messages need to be 3 bytes; except program-changes: 2bytes.
|
2019-03-01 09:59:41 -05:00
|
|
|
if (size == 2 && (data[0] & 0xf0) != 0xC0)
|
|
|
|
return;
|
2019-02-28 14:56:23 -05:00
|
|
|
|
|
|
|
struct rmidi_event_t ev;
|
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.channel = data[0] & 0x0f;
|
2019-02-28 14:56:23 -05:00
|
|
|
switch (data[0] & 0xf0) {
|
|
|
|
case 0x80:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = NOTE_OFF;
|
|
|
|
ev.d.tone.note = data[1] & 0x7f;
|
|
|
|
ev.d.tone.velocity = data[2] & 0x7f;
|
2019-02-28 14:56:23 -05:00
|
|
|
break;
|
|
|
|
case 0x90:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = NOTE_ON;
|
|
|
|
ev.d.tone.note = data[1] & 0x7f;
|
|
|
|
ev.d.tone.velocity = data[2] & 0x7f;
|
2019-02-28 14:56:23 -05:00
|
|
|
if (ev.d.tone.velocity == 0) {
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = NOTE_OFF;
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xB0:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = CONTROL_CHANGE;
|
|
|
|
ev.d.control.param = data[1] & 0x7f;
|
|
|
|
ev.d.control.value = data[2] & 0x7f;
|
2019-02-28 14:56:23 -05:00
|
|
|
break;
|
|
|
|
case 0xC0:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = PROGRAM_CHANGE;
|
|
|
|
ev.d.control.value = data[1] & 0x7f;
|
2019-02-28 14:56:23 -05:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_process_midi_event (synth, &ev);
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
static const uint8_t jingle[] = { 71, 71, 71, 71, 71, 71, 71, 74, 67, 69, 71, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 69, 69, 71, 69, 74, 71, 71, 71, 71, 71, 71, 71, 74, 67, 69, 71, 72, 72, 72, 72, 72, 71, 71, 71, 71, 74, 74, 72, 69, 67, 62, 62, 71, 69, 67, 62, 62, 62, 62, 71, 69, 67, 64, 64, 64, 72, 71, 69, 66, 74, 76, 74, 72, 69, 71, 62, 62, 71, 69, 67, 62, 62, 62, 62, 71, 69, 67, 64, 64, 64, 72, 71, 69, 74, 74, 74, 74, 76, 74, 72, 69, 67, 74, 71, 71, 71, 71, 71, 71, 71, 74, 67, 69, 71, 72, 72, 72, 72, 72, 71, 71, 71, 71, 71, 69, 69, 71, 69, 74, 71, 71, 71, 71, 71, 71, 71, 74, 67, 69, 71, 72, 72, 72, 72, 72, 71, 71, 71, 71, 74, 74, 72, 69, 67 };
|
2013-12-13 14:26:30 -05:00
|
|
|
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_parse_xmas (void* synth, const uint8_t* data, const size_t size)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
|
|
|
RSSynthesizer* rs = (RSSynthesizer*)synth;
|
2019-03-01 09:59:41 -05:00
|
|
|
if (size < 2 || size > 3)
|
|
|
|
return;
|
2019-02-28 14:56:23 -05:00
|
|
|
// All messages need to be 3 bytes; except program-changes: 2bytes.
|
2019-03-01 09:59:41 -05:00
|
|
|
if (size == 2 && (data[0] & 0xf0) != 0xC0)
|
|
|
|
return;
|
2019-02-28 14:56:23 -05:00
|
|
|
|
|
|
|
struct rmidi_event_t ev;
|
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.channel = data[0] & 0x0f;
|
2019-02-28 14:56:23 -05:00
|
|
|
switch (data[0] & 0xf0) {
|
|
|
|
case 0x80:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = NOTE_OFF;
|
|
|
|
ev.d.tone.note = jingle[rs->xmas_off++];
|
|
|
|
ev.d.tone.velocity = data[2] & 0x7f;
|
|
|
|
if (rs->xmas_off >= sizeof (jingle))
|
|
|
|
rs->xmas_off = 0;
|
2019-02-28 14:56:23 -05:00
|
|
|
break;
|
|
|
|
case 0x90:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = NOTE_ON;
|
|
|
|
ev.d.tone.note = jingle[rs->xmas_on++];
|
|
|
|
ev.d.tone.velocity = data[2] & 0x7f;
|
|
|
|
if (rs->xmas_on >= sizeof (jingle))
|
|
|
|
rs->xmas_on = 0;
|
2019-02-28 14:56:23 -05:00
|
|
|
break;
|
|
|
|
case 0xB0:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = CONTROL_CHANGE;
|
|
|
|
ev.d.control.param = data[1] & 0x7f;
|
|
|
|
ev.d.control.value = data[2] & 0x7f;
|
2019-02-28 14:56:23 -05:00
|
|
|
break;
|
|
|
|
case 0xC0:
|
2019-03-01 09:59:41 -05:00
|
|
|
ev.type = PROGRAM_CHANGE;
|
|
|
|
ev.d.control.value = data[1] & 0x7f;
|
2019-02-28 14:56:23 -05:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_process_midi_event (synth, &ev);
|
2013-12-13 14:26:30 -05:00
|
|
|
}
|
2013-10-19 22:31:07 -04:00
|
|
|
/**
|
|
|
|
* initialize the synth
|
|
|
|
* This should be called after synth_alloc()
|
|
|
|
* as soon as the sample-rate is known
|
|
|
|
*
|
|
|
|
* @param synth synth-handle
|
|
|
|
* @param rate sample-rate
|
|
|
|
*/
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_init (void* synth, double rate)
|
|
|
|
{
|
|
|
|
RSSynthesizer* rs = (RSSynthesizer*)synth;
|
|
|
|
rs->rate = rate;
|
|
|
|
rs->boffset = BUFFER_SIZE_SAMPLES;
|
2019-02-28 14:56:23 -05:00
|
|
|
const float tuning = 440;
|
2019-03-01 09:59:41 -05:00
|
|
|
int c, k;
|
|
|
|
for (k = 0; k < 128; k++) {
|
|
|
|
rs->freqs[k] = (tuning / 32.0f) * powf (2, (k - 9.0) / 12.0) / rate;
|
|
|
|
assert (rs->freqs[k] < M_PI / 2); // otherwise spatialization may phase out..
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
|
|
|
rs->kcfilt = 12.0 / rate;
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_reset (synth);
|
2019-02-28 14:56:23 -05:00
|
|
|
|
2019-03-01 09:59:41 -05:00
|
|
|
for (c = 0; c < 16; c++) {
|
|
|
|
synth_load (&rs->sc[c], rate, &synthesize_sineP, &piano_adsr);
|
2019-02-28 14:56:23 -05:00
|
|
|
}
|
2019-03-01 09:59:41 -05:00
|
|
|
rs->xmas_on = 0;
|
2019-02-28 14:56:23 -05:00
|
|
|
rs->xmas_off = 0;
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate data-structure, create a handle for all other synth_* functions.
|
|
|
|
*
|
|
|
|
* This data should be freeded with \ref synth_free when the synth is no
|
|
|
|
* longer needed.
|
|
|
|
*
|
|
|
|
* The synth can only be used after calling \rev synth_init as well.
|
|
|
|
*
|
|
|
|
* @return synth-handle
|
|
|
|
*/
|
2019-02-28 14:56:23 -05:00
|
|
|
static void*
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_alloc (void)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
2019-03-01 09:59:41 -05:00
|
|
|
return calloc (1, sizeof (RSSynthesizer));
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* release synth data structure
|
|
|
|
* @param synth synth-handle
|
|
|
|
*/
|
2019-02-28 14:56:23 -05:00
|
|
|
static void
|
2019-03-01 09:59:41 -05:00
|
|
|
synth_free (void* synth)
|
2019-02-28 14:56:23 -05:00
|
|
|
{
|
2019-03-01 09:59:41 -05:00
|
|
|
free (synth);
|
2013-10-19 22:31:07 -04:00
|
|
|
}
|