Compare commits
No commits in common. "f1d5279535fc6246c0e325a0328319a428c68400" and "28b6dcb1ef13bb1be54c85d844100ad1cf3ad210" have entirely different histories.
f1d5279535
...
28b6dcb1ef
|
@ -66,6 +66,8 @@ DistrhoPluginNekobi::DistrhoPluginNekobi()
|
|||
fSynth.nugget_remains = 0;
|
||||
|
||||
fSynth.note_id = 0;
|
||||
fSynth.polyphony = XSYNTH_DEFAULT_POLYPHONY;
|
||||
fSynth.voices = XSYNTH_DEFAULT_POLYPHONY;
|
||||
fSynth.monophonic = XSYNTH_MONO_MODE_ONCE;
|
||||
fSynth.glide = 0;
|
||||
fSynth.last_noteon_pitch = 0.0f;
|
||||
|
@ -95,7 +97,7 @@ DistrhoPluginNekobi::DistrhoPluginNekobi()
|
|||
// Default values
|
||||
fParams.waveform = 0.0f;
|
||||
fParams.tuning = 0.0f;
|
||||
fParams.cutoff = 63.0f;
|
||||
fParams.cutoff = 25.0f;
|
||||
fParams.resonance = 25.0f;
|
||||
fParams.envMod = 50.0f;
|
||||
fParams.decay = 75.0f;
|
||||
|
@ -105,10 +107,10 @@ DistrhoPluginNekobi::DistrhoPluginNekobi()
|
|||
// Internal stuff
|
||||
fSynth.waveform = 0.0f;
|
||||
fSynth.tuning = 1.0f;
|
||||
fSynth.cutoff = 63.0f;
|
||||
fSynth.cutoff = 5.0f;
|
||||
fSynth.resonance = 0.8f;
|
||||
fSynth.envmod = 50.0f;
|
||||
fSynth.decay = 75.0f;
|
||||
fSynth.envmod = 0.3f;
|
||||
fSynth.decay = 0.0002f;
|
||||
fSynth.accent = 0.3f;
|
||||
fSynth.volume = 0.75f;
|
||||
|
||||
|
@ -164,13 +166,13 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter)
|
|||
parameter.midiCC = 75;
|
||||
break;
|
||||
case paramCutoff:
|
||||
parameter.hints = kParameterIsAutomatable;
|
||||
parameter.hints = kParameterIsAutomatable; // modified x2.5
|
||||
parameter.name = "Cutoff";
|
||||
parameter.symbol = "cutoff";
|
||||
parameter.unit = "%";
|
||||
parameter.ranges.def = 63.0f;
|
||||
parameter.ranges.def = 25.0f;
|
||||
parameter.ranges.min = 0.0f;
|
||||
parameter.ranges.max = 127.0f;
|
||||
parameter.ranges.max = 100.0f;
|
||||
parameter.midiCC = 74;
|
||||
break;
|
||||
case paramResonance:
|
||||
|
@ -180,7 +182,7 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter)
|
|||
parameter.unit = "%";
|
||||
parameter.ranges.def = 25.0f;
|
||||
parameter.ranges.min = 0.0f;
|
||||
parameter.ranges.max = 100.0f;
|
||||
parameter.ranges.max = 95.0f;
|
||||
parameter.midiCC = 71;
|
||||
break;
|
||||
case paramEnvMod:
|
||||
|
@ -190,7 +192,7 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter)
|
|||
parameter.unit = "%";
|
||||
parameter.ranges.def = 50.0f;
|
||||
parameter.ranges.min = 0.0f;
|
||||
parameter.ranges.max = 127.0f;
|
||||
parameter.ranges.max = 100.0f;
|
||||
parameter.midiCC = 1; //Mod Wheel
|
||||
break;
|
||||
case paramDecay:
|
||||
|
@ -200,7 +202,7 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter)
|
|||
parameter.unit = "%";
|
||||
parameter.ranges.def = 75.0f;
|
||||
parameter.ranges.min = 0.0f;
|
||||
parameter.ranges.max = 127.0f;
|
||||
parameter.ranges.max = 100.0f;
|
||||
parameter.midiCC = 72;
|
||||
break;
|
||||
case paramAccent:
|
||||
|
@ -270,23 +272,23 @@ void DistrhoPluginNekobi::setParameterValue(uint32_t index, float value)
|
|||
break;
|
||||
case paramCutoff:
|
||||
fParams.cutoff = value;
|
||||
fSynth.cutoff = value;
|
||||
DISTRHO_SAFE_ASSERT(fSynth.cutoff >= 0.0f && fSynth.cutoff <= 127.0f);
|
||||
fSynth.cutoff = value/2.5f;
|
||||
DISTRHO_SAFE_ASSERT(fSynth.cutoff >= 0.0f && fSynth.cutoff <= 40.0f);
|
||||
break;
|
||||
case paramResonance:
|
||||
fParams.resonance = value;
|
||||
fSynth.resonance = value/29.82f;
|
||||
DISTRHO_SAFE_ASSERT(fSynth.resonance >= 0.0f && fSynth.resonance <= 3.4f);
|
||||
fSynth.resonance = value/100.0f;
|
||||
DISTRHO_SAFE_ASSERT(fSynth.resonance >= 0.0f && fSynth.resonance <= 0.95f);
|
||||
break;
|
||||
case paramEnvMod:
|
||||
fParams.envMod = value;
|
||||
fSynth.envmod = value;
|
||||
DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 127.0f);
|
||||
fSynth.envmod = value/100.0f;
|
||||
DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 1.0f);
|
||||
break;
|
||||
case paramDecay:
|
||||
fParams.decay = value;
|
||||
fSynth.decay = value;
|
||||
DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.0f && fSynth.decay <= 127.0f);
|
||||
fSynth.decay = value/100.0f * 0.000491f + 0.000009f; // FIXME: log?
|
||||
DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.000009f && fSynth.decay <= 0.0005f);
|
||||
break;
|
||||
case paramAccent:
|
||||
fParams.accent = value;
|
||||
|
|
|
@ -63,9 +63,9 @@ DistrhoUINekobi::DistrhoUINekobi()
|
|||
fKnobCutoff = new ImageKnob(this, knobImage, ImageKnob::Vertical);
|
||||
fKnobCutoff->setId(DistrhoPluginNekobi::paramCutoff);
|
||||
fKnobCutoff->setAbsolutePos(185, 43);
|
||||
fKnobCutoff->setRange(0.0f, 127.0f);
|
||||
fKnobCutoff->setDefault(50.0f);
|
||||
fKnobCutoff->setValue(50.0f);
|
||||
fKnobCutoff->setRange(0.0f, 100.0f);
|
||||
fKnobCutoff->setDefault(25.0f);
|
||||
fKnobCutoff->setValue(25.0f);
|
||||
fKnobCutoff->setRotationAngle(305);
|
||||
fKnobCutoff->setCallback(this);
|
||||
|
||||
|
@ -83,7 +83,7 @@ DistrhoUINekobi::DistrhoUINekobi()
|
|||
fKnobEnvMod = new ImageKnob(this, knobImage, ImageKnob::Vertical);
|
||||
fKnobEnvMod->setId(DistrhoPluginNekobi::paramEnvMod);
|
||||
fKnobEnvMod->setAbsolutePos(329, 43);
|
||||
fKnobEnvMod->setRange(0.0f, 127.0f);
|
||||
fKnobEnvMod->setRange(0.0f, 100.0f);
|
||||
fKnobEnvMod->setDefault(50.0f);
|
||||
fKnobEnvMod->setValue(50.0f);
|
||||
fKnobEnvMod->setRotationAngle(305);
|
||||
|
@ -93,7 +93,7 @@ DistrhoUINekobi::DistrhoUINekobi()
|
|||
fKnobDecay = new ImageKnob(this, knobImage, ImageKnob::Vertical);
|
||||
fKnobDecay->setId(DistrhoPluginNekobi::paramDecay);
|
||||
fKnobDecay->setAbsolutePos(400, 43);
|
||||
fKnobDecay->setRange(0.0f, 127.0f);
|
||||
fKnobDecay->setRange(0.0f, 100.0f);
|
||||
fKnobDecay->setDefault(75.0f);
|
||||
fKnobDecay->setValue(75.0f);
|
||||
fKnobDecay->setRotationAngle(305);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# --------------------------------------------------------------
|
||||
# Project name, used for binaries
|
||||
|
||||
NAME = Nekobi-v8
|
||||
NAME = Nekobi
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Files to build
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/* nekobee DSSI software synthesizer plugin
|
||||
*
|
||||
* Copyright (C) 2004 Sean Bolton and others.
|
||||
*
|
||||
* Portions of this file may have come from Chris Cannam and Steve
|
||||
* Harris's public domain DSSI example code.
|
||||
*
|
||||
* 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 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., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _XSYNTH_H
|
||||
#define _XSYNTH_H
|
||||
|
||||
/* ==== debugging ==== */
|
||||
|
||||
/* XSYNTH_DEBUG bits */
|
||||
#define XDB_DSSI 1 /* DSSI interface */
|
||||
#define XDB_AUDIO 2 /* audio output */
|
||||
#define XDB_NOTE 4 /* note on/off, voice allocation */
|
||||
#define XDB_DATA 8 /* plugin patchbank handling */
|
||||
#define GDB_MAIN 16 /* GUI main program flow */
|
||||
#define GDB_OSC 32 /* GUI OSC handling */
|
||||
#define GDB_IO 64 /* GUI patch file input/output */
|
||||
#define GDB_GUI 128 /* GUI GUI callbacks, updating, etc. */
|
||||
|
||||
/* If you want debug information, define XSYNTH_DEBUG to the XDB_* bits you're
|
||||
* interested in getting debug information about, bitwise-ORed together.
|
||||
* Otherwise, leave it undefined. */
|
||||
// #define XSYNTH_DEBUG (1+8+16+32+64)
|
||||
|
||||
//#define XSYNTH_DEBUG GDB_GUI + GDB_OSC
|
||||
|
||||
// #define XSYNTH_DEBUG XDB_DSSI
|
||||
#ifdef XSYNTH_DEBUG
|
||||
|
||||
#include <stdio.h>
|
||||
#define XSYNTH_DEBUG_INIT(x)
|
||||
#define XDB_MESSAGE(type, fmt...) { if (XSYNTH_DEBUG & type) fprintf(stderr, "nekobee-dssi.so" fmt); }
|
||||
#define GDB_MESSAGE(type, fmt...) { if (XSYNTH_DEBUG & type) fprintf(stderr, "nekobee_gtk" fmt); }
|
||||
// -FIX-:
|
||||
// #include "message_buffer.h"
|
||||
// #define XSYNTH_DEBUG_INIT(x) mb_init(x)
|
||||
// #define XDB_MESSAGE(type, fmt...) { \-
|
||||
// if (XSYNTH_DEBUG & type) { \-
|
||||
// char _m[256]; \-
|
||||
// snprintf(_m, 255, fmt); \-
|
||||
// add_message(_m); \-
|
||||
// } \-
|
||||
// }
|
||||
|
||||
#else /* !XSYNTH_DEBUG */
|
||||
|
||||
#define XDB_MESSAGE(type, fmt, ...)
|
||||
#define GDB_MESSAGE(type, fmt, ...)
|
||||
#define XSYNTH_DEBUG_INIT(x)
|
||||
|
||||
#endif /* XSYNTH_DEBUG */
|
||||
|
||||
/* ==== end of debugging ==== */
|
||||
|
||||
#define XSYNTH_MAX_POLYPHONY 1
|
||||
#define XSYNTH_DEFAULT_POLYPHONY 1
|
||||
|
||||
#endif /* _XSYNTH_H */
|
|
@ -25,13 +25,13 @@
|
|||
* MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "nekobee_synth.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "nekobee.h"
|
||||
#include "nekobee_synth.h"
|
||||
#include "nekobee_voice.h"
|
||||
|
||||
/*
|
||||
|
@ -39,13 +39,19 @@
|
|||
*
|
||||
* stop processing all notes immediately
|
||||
*/
|
||||
void nekobee_synth_all_voices_off(nekobee_synth_t *synth) {
|
||||
void
|
||||
nekobee_synth_all_voices_off(nekobee_synth_t *synth)
|
||||
{
|
||||
int i;
|
||||
nekobee_voice_t *voice = synth->voice;
|
||||
if (_PLAYING(voice)) {
|
||||
nekobee_voice_off(voice);
|
||||
}
|
||||
nekobee_voice_t *voice;
|
||||
|
||||
for (i = 0; i < synth->voices; i++) {
|
||||
//voice = synth->voice[i];
|
||||
voice = synth->voice;
|
||||
if (_PLAYING(voice)) {
|
||||
nekobee_voice_off(voice);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 8; i++) synth->held_keys[i] = -1;
|
||||
}
|
||||
|
||||
|
@ -54,16 +60,23 @@ void nekobee_synth_all_voices_off(nekobee_synth_t *synth) {
|
|||
*
|
||||
* handle a note off message
|
||||
*/
|
||||
void nekobee_synth_note_off(nekobee_synth_t *synth, unsigned char key,
|
||||
unsigned char rvelocity) {
|
||||
int count = 0;
|
||||
nekobee_voice_t *voice = synth->voice;
|
||||
if (_PLAYING(voice)) {
|
||||
nekobee_voice_note_off(synth, voice, key, 64);
|
||||
count++;
|
||||
void
|
||||
nekobee_synth_note_off(nekobee_synth_t *synth, unsigned char key, unsigned char rvelocity)
|
||||
{
|
||||
int i, count = 0;
|
||||
nekobee_voice_t *voice;
|
||||
|
||||
for (i = 0; i < synth->voices; i++) {
|
||||
voice = synth->voice;
|
||||
if (_PLAYING(voice)) {
|
||||
XDB_MESSAGE(XDB_NOTE, " nekobee_synth_note_off: key %d rvel %d voice %d note id %d\n", key, rvelocity, i, voice->note_id);
|
||||
nekobee_voice_note_off(synth, voice, key, 64);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!count) nekobee_voice_remove_held_key(synth, key);
|
||||
if (!count)
|
||||
nekobee_voice_remove_held_key(synth, key);
|
||||
|
||||
return;
|
||||
(void)rvelocity;
|
||||
|
@ -74,28 +87,34 @@ void nekobee_synth_note_off(nekobee_synth_t *synth, unsigned char key,
|
|||
*
|
||||
* put all notes into the released state
|
||||
*/
|
||||
void nekobee_synth_all_notes_off(nekobee_synth_t *synth) {
|
||||
nekobee_voice_t *voice = synth->voice;
|
||||
void
|
||||
nekobee_synth_all_notes_off(nekobee_synth_t* synth)
|
||||
{
|
||||
int i;
|
||||
nekobee_voice_t *voice;
|
||||
|
||||
/* reset the sustain controller */
|
||||
synth->cc[MIDI_CTL_SUSTAIN] = 0;
|
||||
|
||||
if (_ON(voice) || _SUSTAINED(voice)) {
|
||||
nekobee_voice_release_note(synth, voice);
|
||||
for (i = 0; i < synth->voices; i++) {
|
||||
//voice = synth->voice[i];
|
||||
voice = synth->voice;
|
||||
if (_ON(voice) || _SUSTAINED(voice)) {
|
||||
nekobee_voice_release_note(synth, voice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* nekobee_synth_note_on
|
||||
*/
|
||||
void nekobee_synth_note_on(nekobee_synth_t *synth, unsigned char key,
|
||||
unsigned char velocity) {
|
||||
nekobee_voice_t *voice;
|
||||
void
|
||||
nekobee_synth_note_on(nekobee_synth_t *synth, unsigned char key, unsigned char velocity)
|
||||
{
|
||||
nekobee_voice_t* voice;
|
||||
|
||||
voice = synth->voice;
|
||||
if (_PLAYING(synth->voice)) {
|
||||
// XDB_MESSAGE(XDB_NOTE, " nekobee_synth_note_on: retriggering mono
|
||||
// voice on new key %d\n", key);
|
||||
XDB_MESSAGE(XDB_NOTE, " nekobee_synth_note_on: retriggering mono voice on new key %d\n", key);
|
||||
}
|
||||
|
||||
voice->note_id = synth->note_id++;
|
||||
|
@ -106,114 +125,112 @@ void nekobee_synth_note_on(nekobee_synth_t *synth, unsigned char key,
|
|||
/*
|
||||
* nekobee_synth_update_volume
|
||||
*/
|
||||
void nekobee_synth_update_volume(nekobee_synth_t *synth) {
|
||||
void
|
||||
nekobee_synth_update_volume(nekobee_synth_t* synth)
|
||||
{
|
||||
synth->cc_volume = (float)(synth->cc[MIDI_CTL_MSB_MAIN_VOLUME] * 128 +
|
||||
synth->cc[MIDI_CTL_LSB_MAIN_VOLUME]) /
|
||||
16256.0f;
|
||||
if (synth->cc_volume > 1.0f) synth->cc_volume = 1.0f;
|
||||
/* don't need to check if any playing voices need updating, because it's
|
||||
* global */
|
||||
synth->cc[MIDI_CTL_LSB_MAIN_VOLUME]) / 16256.0f;
|
||||
if (synth->cc_volume > 1.0f)
|
||||
synth->cc_volume = 1.0f;
|
||||
/* don't need to check if any playing voices need updating, because it's global */
|
||||
}
|
||||
|
||||
/*
|
||||
* nekobee_synth_control_change
|
||||
*/
|
||||
void nekobee_synth_control_change(nekobee_synth_t *synth, unsigned int param,
|
||||
signed int value) {
|
||||
void
|
||||
nekobee_synth_control_change(nekobee_synth_t *synth, unsigned int param, signed int value)
|
||||
{
|
||||
synth->cc[param] = value;
|
||||
|
||||
printf("%0d %d\n", param, value);
|
||||
|
||||
switch (param) {
|
||||
case MIDI_CTL_MSB_MAIN_VOLUME:
|
||||
case MIDI_CTL_LSB_MAIN_VOLUME:
|
||||
nekobee_synth_update_volume(synth);
|
||||
break;
|
||||
|
||||
case MIDI_CTL_ALL_SOUNDS_OFF:
|
||||
nekobee_synth_all_voices_off(synth);
|
||||
break;
|
||||
case MIDI_CTL_MSB_MAIN_VOLUME:
|
||||
case MIDI_CTL_LSB_MAIN_VOLUME:
|
||||
nekobee_synth_update_volume(synth);
|
||||
break;
|
||||
|
||||
case MIDI_CTL_RESET_CONTROLLERS:
|
||||
nekobee_synth_init_controls(synth);
|
||||
break;
|
||||
case MIDI_CTL_ALL_SOUNDS_OFF:
|
||||
nekobee_synth_all_voices_off(synth);
|
||||
break;
|
||||
|
||||
case MIDI_CTL_ALL_NOTES_OFF:
|
||||
nekobee_synth_all_notes_off(synth);
|
||||
break;
|
||||
case MIDI_CTL_RESET_CONTROLLERS:
|
||||
nekobee_synth_init_controls(synth);
|
||||
break;
|
||||
|
||||
/* what others should we respond to? */
|
||||
case MIDI_CTL_ALL_NOTES_OFF:
|
||||
nekobee_synth_all_notes_off(synth);
|
||||
break;
|
||||
|
||||
/* these we ignore (let the host handle):
|
||||
* BANK_SELECT_MSB
|
||||
* BANK_SELECT_LSB
|
||||
* DATA_ENTRY_MSB
|
||||
* NRPN_MSB
|
||||
* NRPN_LSB
|
||||
* RPN_MSB
|
||||
* RPN_LSB
|
||||
* -FIX- no! we need RPN (0, 0) Pitch Bend Sensitivity!
|
||||
*/
|
||||
/* what others should we respond to? */
|
||||
|
||||
/* these we ignore (let the host handle):
|
||||
* BANK_SELECT_MSB
|
||||
* BANK_SELECT_LSB
|
||||
* DATA_ENTRY_MSB
|
||||
* NRPN_MSB
|
||||
* NRPN_LSB
|
||||
* RPN_MSB
|
||||
* RPN_LSB
|
||||
* -FIX- no! we need RPN (0, 0) Pitch Bend Sensitivity!
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* nekobee_synth_init_controls
|
||||
*/
|
||||
void nekobee_synth_init_controls(nekobee_synth_t *synth) {
|
||||
void
|
||||
nekobee_synth_init_controls(nekobee_synth_t *synth)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 128; i++) {
|
||||
synth->cc[i] = 0;
|
||||
}
|
||||
|
||||
synth->cc[7] = 127; /* full volume */
|
||||
synth->cc[7] = 127; /* full volume */
|
||||
nekobee_synth_update_volume(synth);
|
||||
}
|
||||
|
||||
/*
|
||||
* nekobee_synth_render_voices
|
||||
*/
|
||||
void nekobee_synth_render_voices(nekobee_synth_t *synth, float *out,
|
||||
unsigned long sample_count,
|
||||
int do_control_update) {
|
||||
void
|
||||
nekobee_synth_render_voices(nekobee_synth_t *synth, float *out, unsigned long sample_count,
|
||||
int do_control_update)
|
||||
{
|
||||
unsigned long i;
|
||||
float res, wow;
|
||||
|
||||
/* clear the buffer */
|
||||
for (i = 0; i < sample_count; i++) out[i] = 0.0f;
|
||||
for (i = 0; i < sample_count; i++)
|
||||
out[i] = 0.0f;
|
||||
|
||||
// we can do anything that must be updated all the time here
|
||||
// this is called even when a voice isn't playing
|
||||
|
||||
// approximate a log scale
|
||||
res = 1 - synth->resonance;
|
||||
wow = res * res;
|
||||
wow = wow / 10.0f;
|
||||
res = 1-synth->resonance;
|
||||
wow = res*res;
|
||||
wow = wow/10.0f;
|
||||
|
||||
|
||||
// FIXME just... fix this
|
||||
// as the resonance is increased, "wow" slows down the accent attack
|
||||
if ((synth->voice->velocity > 90) &&
|
||||
(synth->vcf_accent < synth->voice->vcf_eg)) {
|
||||
synth->vcf_accent = (0.985 - wow) * synth->vcf_accent +
|
||||
(0.015 + wow) * synth->voice->vcf_eg;
|
||||
if ((synth->voice->velocity>90) && (synth->vcf_accent < synth->voice->vcf_eg)) {
|
||||
synth->vcf_accent=(0.985-wow)*synth->vcf_accent+(0.015+wow)*synth->voice->vcf_eg;
|
||||
} else {
|
||||
synth->vcf_accent = (0.985 - wow) * synth->vcf_accent; // or just decay
|
||||
synth->vcf_accent=(0.985-wow)*synth->vcf_accent; // or just decay
|
||||
}
|
||||
|
||||
if (synth->voice->velocity > 90) {
|
||||
synth->vca_accent = 0.95 * synth->vca_accent +
|
||||
0.05; // ramp up accent on with a time constant
|
||||
if (synth->voice->velocity>90) {
|
||||
synth->vca_accent=0.95*synth->vca_accent+0.05; // ramp up accent on with a time constant
|
||||
} else {
|
||||
synth->vca_accent =
|
||||
0.95 * synth->vca_accent; // accent off with time constant
|
||||
synth->vca_accent=0.95*synth->vca_accent; // accent off with time constant
|
||||
}
|
||||
|
||||
//out[0] += 0.10f; /* add a 'buzz' to output so there's something audible even
|
||||
|
||||
#if defined(XSYNTH_DEBUG) && (XSYNTH_DEBUG & XDB_AUDIO)
|
||||
out[0] += 0.10f; /* add a 'buzz' to output so there's something audible even when quiescent */
|
||||
#endif /* defined(XSYNTH_DEBUG) && (XSYNTH_DEBUG & XDB_AUDIO) */
|
||||
if (_PLAYING(synth->voice)) {
|
||||
nekobee_voice_render(synth, out, sample_count);
|
||||
nekobee_voice_render(synth, synth->voice, out, sample_count, do_control_update);
|
||||
}
|
||||
(void)do_control_update;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#ifndef _XSYNTH_SYNTH_H
|
||||
#define _XSYNTH_SYNTH_H
|
||||
|
||||
#include "nekobee.h"
|
||||
#include "nekobee_types.h"
|
||||
|
||||
#define XSYNTH_MONO_MODE_OFF 0
|
||||
|
@ -50,6 +51,8 @@ struct _nekobee_synth_t {
|
|||
|
||||
/* voice tracking and data */
|
||||
unsigned int note_id; /* incremented for every new note, used for voice-stealing prioritization */
|
||||
int polyphony; /* requested polyphony, must be <= XSYNTH_MAX_POLYPHONY */
|
||||
int voices; /* current polyphony, either requested polyphony above or 1 while in monophonic mode */
|
||||
int monophonic; /* true if operating in monophonic mode */
|
||||
int glide; /* current glide mode */
|
||||
float last_noteon_pitch; /* glide start pitch for non-legato modes */
|
||||
|
@ -57,6 +60,7 @@ struct _nekobee_synth_t {
|
|||
float vcf_accent; /* used to emulate the circuit that sweeps the vcf at full resonance */
|
||||
float vca_accent; /* used to smooth the accent pulse, removing the click */
|
||||
|
||||
//nekobee_voice_t *voice[XSYNTH_MAX_POLYPHONY];
|
||||
nekobee_voice_t *voice;
|
||||
|
||||
/* current non-paramter-mapped controller values */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "nekobee_types.h"
|
||||
#include "nekobee.h"
|
||||
#include "nekobee_synth.h"
|
||||
#include "nekobee_voice.h"
|
||||
|
||||
|
@ -56,16 +57,15 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
unsigned char key, unsigned char velocity)
|
||||
{
|
||||
int i;
|
||||
|
||||
voice->key = key;
|
||||
voice->velocity = velocity;
|
||||
voice->vcf_eg = 9.8f;
|
||||
voice->vca_eg = 1.0f;
|
||||
// 1.5M and 1uF
|
||||
voice->vca_tc = 1 / 1.5 * synth->deltat;
|
||||
|
||||
|
||||
if (!synth->monophonic || !(_ON(voice) || _SUSTAINED(voice))) {
|
||||
|
||||
// brand-new voice, or monophonic voice in release phase; set everything up
|
||||
XDB_MESSAGE(XDB_NOTE, " nekobee_voice_note_on in polyphonic/new section: key %d, mono %d, old status %d\n", key, synth->monophonic, voice->status);
|
||||
|
||||
voice->target_pitch = nekobee_pitch[key];
|
||||
|
||||
|
@ -77,16 +77,17 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
}
|
||||
|
||||
if (!_PLAYING(voice)) {
|
||||
//printf("resetting voice\n");
|
||||
voice->lfo_pos = 0.0f;
|
||||
voice->vca_eg = 0.0f;
|
||||
voice->vcf_eg = 9.8f;
|
||||
voice->vcf_eg = 0.0f;
|
||||
voice->delay1 = 0.0f;
|
||||
voice->delay2 = 0.0f;
|
||||
voice->delay3 = 0.0f;
|
||||
voice->delay4 = 0.0f;
|
||||
voice->c5 = 0.0f;
|
||||
voice->osc_index = 0;
|
||||
voice->osc.phase = 0.0f;
|
||||
voice->osc1.last_waveform = -1;
|
||||
voice->osc1.pos = 0.0f;
|
||||
|
||||
}
|
||||
voice->vca_eg_phase = 0;
|
||||
|
@ -96,6 +97,7 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
} else {
|
||||
|
||||
/* synth is monophonic, and we're modifying a playing voice */
|
||||
XDB_MESSAGE(XDB_NOTE, " nekobee_voice_note_on in monophonic section: old key %d => new key %d\n", synth->held_keys[0], key);
|
||||
|
||||
/* set new pitch */
|
||||
voice->target_pitch = nekobee_pitch[key];
|
||||
|
@ -130,10 +132,9 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
}
|
||||
synth->held_keys[0] = key;
|
||||
|
||||
// FIXME check if this mess is necessary
|
||||
if (!_PLAYING(voice)) {
|
||||
voice->status = XSYNTH_VOICE_ON;
|
||||
//nekobee_voice_start_voice(voice);
|
||||
|
||||
nekobee_voice_start_voice(voice);
|
||||
|
||||
} else if (!_ON(voice)) { /* must be XSYNTH_VOICE_SUSTAINED or XSYNTH_VOICE_RELEASED */
|
||||
|
||||
|
@ -183,9 +184,7 @@ nekobee_voice_note_off(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
{
|
||||
unsigned char previous_top_key;
|
||||
|
||||
//printf("voice->vca_tc=%f", voice->vca_tc);
|
||||
voice->vca_tc = (101) * synth->deltat;
|
||||
|
||||
XDB_MESSAGE(XDB_NOTE, " nekobee_set_note_off: called for voice %p, key %d\n", voice, key);
|
||||
|
||||
/* save release velocity */
|
||||
voice->velocity = rvelocity;
|
||||
|
@ -203,6 +202,7 @@ nekobee_voice_note_off(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
|
||||
/* most-recently-played key has changed */
|
||||
voice->key = synth->held_keys[0];
|
||||
XDB_MESSAGE(XDB_NOTE, " note-off in monophonic section: changing pitch to %d\n", voice->key);
|
||||
voice->target_pitch = nekobee_pitch[voice->key];
|
||||
if (synth->glide == XSYNTH_GLIDE_MODE_INITIAL ||
|
||||
synth->glide == XSYNTH_GLIDE_MODE_OFF)
|
||||
|
@ -221,12 +221,14 @@ nekobee_voice_note_off(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
if (XSYNTH_SYNTH_SUSTAINED(synth)) {
|
||||
|
||||
/* no more keys in list, but we're sustained */
|
||||
XDB_MESSAGE(XDB_NOTE, " note-off in monophonic section: sustained with no held keys\n");
|
||||
if (!_RELEASED(voice))
|
||||
voice->status = XSYNTH_VOICE_SUSTAINED;
|
||||
|
||||
} else { /* not sustained */
|
||||
|
||||
/* no more keys in list, so turn off note */
|
||||
XDB_MESSAGE(XDB_NOTE, " note-off in monophonic section: turning off voice %p\n", voice);
|
||||
nekobee_voice_set_release_phase(voice);
|
||||
voice->status = XSYNTH_VOICE_RELEASED;
|
||||
|
||||
|
@ -241,6 +243,7 @@ nekobee_voice_note_off(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
|||
void
|
||||
nekobee_voice_release_note(nekobee_synth_t *synth, nekobee_voice_t *voice)
|
||||
{
|
||||
XDB_MESSAGE(XDB_NOTE, " nekobee_voice_release_note: turning off voice %p\n", voice);
|
||||
if (_ON(voice)) {
|
||||
/* dummy up a release velocity */
|
||||
voice->rvelocity = 64;
|
||||
|
|
|
@ -27,92 +27,131 @@
|
|||
#define _XSYNTH_VOICE_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nekobee_types.h"
|
||||
|
||||
/* maximum size of a rendering burst */
|
||||
#define XSYNTH_NUGGET_SIZE 64
|
||||
#define XSYNTH_NUGGET_SIZE 64
|
||||
|
||||
struct _nekobee_patch_t {
|
||||
float tuning;
|
||||
/* minBLEP constants */
|
||||
/* minBLEP table oversampling factor (must be a power of two): */
|
||||
#define MINBLEP_PHASES 64
|
||||
/* MINBLEP_PHASES minus one: */
|
||||
#define MINBLEP_PHASE_MASK 63
|
||||
/* length in samples of (truncated) step discontinuity delta: */
|
||||
#define STEP_DD_PULSE_LENGTH 72
|
||||
/* length in samples of (truncated) slope discontinuity delta: */
|
||||
#define SLOPE_DD_PULSE_LENGTH 71
|
||||
/* the longer of the two above: */
|
||||
#define LONGEST_DD_PULSE_LENGTH STEP_DD_PULSE_LENGTH
|
||||
/* MINBLEP_BUFFER_LENGTH must be at least XSYNTH_NUGGET_SIZE plus
|
||||
* LONGEST_DD_PULSE_LENGTH, and not less than twice LONGEST_DD_PULSE_LENGTH: */
|
||||
#define MINBLEP_BUFFER_LENGTH 512
|
||||
/* delay between start of DD pulse and the discontinuity, in samples: */
|
||||
#define DD_SAMPLE_DELAY 4
|
||||
|
||||
struct _nekobee_patch_t
|
||||
{
|
||||
float tuning;
|
||||
unsigned char waveform;
|
||||
float cutoff;
|
||||
float resonance;
|
||||
float envmod;
|
||||
float decay;
|
||||
float accent;
|
||||
float volume;
|
||||
float cutoff;
|
||||
float resonance;
|
||||
float envmod;
|
||||
float decay;
|
||||
float accent;
|
||||
float volume;
|
||||
};
|
||||
|
||||
enum nekobee_voice_status {
|
||||
enum nekobee_voice_status
|
||||
{
|
||||
XSYNTH_VOICE_OFF, /* silent: is not processed by render loop */
|
||||
XSYNTH_VOICE_ON, /* has not received a note off event */
|
||||
XSYNTH_VOICE_SUSTAINED, /* has received note off, but sustain controller is
|
||||
on */
|
||||
XSYNTH_VOICE_RELEASED /* had note off, not sustained, in final decay phase
|
||||
of envelopes */
|
||||
XSYNTH_VOICE_SUSTAINED, /* has received note off, but sustain controller is on */
|
||||
XSYNTH_VOICE_RELEASED /* had note off, not sustained, in final decay phase of envelopes */
|
||||
};
|
||||
|
||||
struct blosc_t {
|
||||
float phase; // 0 - 1, with 1 being 2pi radians of phase rotation
|
||||
float delay; // one sample delay for blep
|
||||
struct blosc
|
||||
{
|
||||
int last_waveform, /* persistent */
|
||||
waveform, /* comes from LADSPA port each cycle */
|
||||
bp_high; /* persistent */
|
||||
float pos, /* persistent */
|
||||
pw; /* comes from LADSPA port each cycle */
|
||||
};
|
||||
|
||||
/*
|
||||
* nekobee_voice_t
|
||||
*/
|
||||
struct _nekobee_voice_t {
|
||||
unsigned int note_id;
|
||||
struct _nekobee_voice_t
|
||||
{
|
||||
unsigned int note_id;
|
||||
|
||||
unsigned char status;
|
||||
unsigned char key;
|
||||
unsigned char velocity;
|
||||
unsigned char rvelocity; /* the note-off velocity */
|
||||
unsigned char rvelocity; /* the note-off velocity */
|
||||
|
||||
/* translated controller values */
|
||||
float pressure; /* filter resonance multiplier, off = 1.0, full on = 0.0 */
|
||||
float pressure; /* filter resonance multiplier, off = 1.0, full on = 0.0 */
|
||||
|
||||
/* persistent voice state */
|
||||
float prev_pitch, target_pitch, lfo_pos;
|
||||
struct blosc_t osc;
|
||||
float vca_eg, vcf_eg, accent_slug, delay1, delay2, delay3, delay4;
|
||||
float vca_tc, vcf_tc; // VCA and VCF time constants
|
||||
|
||||
float vca_slug;
|
||||
|
||||
unsigned char vca_eg_phase, vcf_eg_phase;
|
||||
int osc_index; /* shared index into osc_audio */
|
||||
float osc_audio[XSYNTH_NUGGET_SIZE];
|
||||
float delayhp;
|
||||
|
||||
|
||||
float prev_pitch,
|
||||
target_pitch,
|
||||
lfo_pos;
|
||||
struct blosc osc1;
|
||||
float vca_eg,
|
||||
vcf_eg,
|
||||
accent_slug,
|
||||
delay1,
|
||||
delay2,
|
||||
delay3,
|
||||
delay4,
|
||||
c5;
|
||||
unsigned char vca_eg_phase,
|
||||
vcf_eg_phase;
|
||||
int osc_index; /* shared index into osc_audio */
|
||||
float osc_audio[MINBLEP_BUFFER_LENGTH];
|
||||
float freqcut_buf[XSYNTH_NUGGET_SIZE];
|
||||
float vca_buf[XSYNTH_NUGGET_SIZE];
|
||||
};
|
||||
|
||||
#define _PLAYING(voice) ((voice)->status != XSYNTH_VOICE_OFF)
|
||||
#define _ON(voice) ((voice)->status == XSYNTH_VOICE_ON)
|
||||
#define _SUSTAINED(voice) ((voice)->status == XSYNTH_VOICE_SUSTAINED)
|
||||
#define _RELEASED(voice) ((voice)->status == XSYNTH_VOICE_RELEASED)
|
||||
#define _AVAILABLE(voice) ((voice)->status == XSYNTH_VOICE_OFF)
|
||||
#define _PLAYING(voice) ((voice)->status != XSYNTH_VOICE_OFF)
|
||||
#define _ON(voice) ((voice)->status == XSYNTH_VOICE_ON)
|
||||
#define _SUSTAINED(voice) ((voice)->status == XSYNTH_VOICE_SUSTAINED)
|
||||
#define _RELEASED(voice) ((voice)->status == XSYNTH_VOICE_RELEASED)
|
||||
#define _AVAILABLE(voice) ((voice)->status == XSYNTH_VOICE_OFF)
|
||||
|
||||
extern float nekobee_pitch[129];
|
||||
extern float nekobee_pitch[128];
|
||||
|
||||
typedef struct { float value, delta; } float_value_delta;
|
||||
extern float_value_delta step_dd_table[];
|
||||
|
||||
extern float slope_dd_table[];
|
||||
|
||||
/* nekobee_voice.c */
|
||||
nekobee_voice_t *nekobee_voice_new();
|
||||
void nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
||||
unsigned char key, unsigned char velocity);
|
||||
void nekobee_voice_remove_held_key(nekobee_synth_t *synth, unsigned char key);
|
||||
void nekobee_voice_note_off(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
||||
unsigned char key, unsigned char rvelocity);
|
||||
void nekobee_voice_release_note(nekobee_synth_t *synth, nekobee_voice_t *voice);
|
||||
void nekobee_voice_set_ports(nekobee_synth_t *synth, nekobee_patch_t *patch);
|
||||
void nekobee_voice_update_pressure_mod(nekobee_synth_t *synth,
|
||||
nekobee_voice_t *voice);
|
||||
void nekobee_voice_note_on(nekobee_synth_t *synth,
|
||||
nekobee_voice_t *voice,
|
||||
unsigned char key,
|
||||
unsigned char velocity);
|
||||
void nekobee_voice_remove_held_key(nekobee_synth_t *synth,
|
||||
unsigned char key);
|
||||
void nekobee_voice_note_off(nekobee_synth_t *synth,
|
||||
nekobee_voice_t *voice,
|
||||
unsigned char key,
|
||||
unsigned char rvelocity);
|
||||
void nekobee_voice_release_note(nekobee_synth_t *synth,
|
||||
nekobee_voice_t *voice);
|
||||
void nekobee_voice_set_ports(nekobee_synth_t *synth,
|
||||
nekobee_patch_t *patch);
|
||||
void nekobee_voice_update_pressure_mod(nekobee_synth_t *synth,
|
||||
nekobee_voice_t *voice);
|
||||
|
||||
/* nekobee_voice_render.c */
|
||||
void nekobee_init_tables(void);
|
||||
void nekobee_voice_render(nekobee_synth_t *synth, float *out,
|
||||
uint32_t count);
|
||||
void nekobee_voice_render(nekobee_synth_t *synth, nekobee_voice_t *voice,
|
||||
float *out, unsigned long sample_count,
|
||||
int do_control_update);
|
||||
|
||||
/* inline functions */
|
||||
|
||||
|
@ -122,11 +161,23 @@ void nekobee_voice_render(nekobee_synth_t *synth, float *out,
|
|||
* Purpose: Turns off a voice immediately, meaning that it is not processed
|
||||
* anymore by the render loop.
|
||||
*/
|
||||
static inline void nekobee_voice_off(nekobee_voice_t *voice) {
|
||||
static inline void
|
||||
nekobee_voice_off(nekobee_voice_t* voice)
|
||||
{
|
||||
voice->status = XSYNTH_VOICE_OFF;
|
||||
/* silence the oscillator buffer for the next use */
|
||||
memset(voice->osc_audio, 0, XSYNTH_NUGGET_SIZE * sizeof(float));
|
||||
memset(voice->osc_audio, 0, MINBLEP_BUFFER_LENGTH * sizeof(float));
|
||||
/* -FIX- decrement active voice count? */
|
||||
}
|
||||
|
||||
/*
|
||||
* nekobee_voice_start_voice
|
||||
*/
|
||||
static inline void
|
||||
nekobee_voice_start_voice(nekobee_voice_t *voice)
|
||||
{
|
||||
voice->status = XSYNTH_VOICE_ON;
|
||||
/* -FIX- increment active voice count? */
|
||||
}
|
||||
|
||||
#endif /* _XSYNTH_VOICE_H */
|
||||
|
|
|
@ -18,224 +18,3 @@
|
|||
* MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
// complete rewrite of the voice engine
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nekobee_synth.h"
|
||||
#include "nekobee_voice.h"
|
||||
|
||||
// centre oscillator around Middle C
|
||||
// conveniently the middle of the 303's range
|
||||
#define REF_NOTE 60
|
||||
|
||||
float nekobee_pitch[129];
|
||||
float logpot[129];
|
||||
|
||||
void nekobee_init_tables(void) {
|
||||
// create tables used by Nekobee to save on expensive calculations
|
||||
// mostly involving exponentiation!
|
||||
// tables are scaled to 128 values for ease of calculation with MIDI
|
||||
|
||||
// it's worth noting that a real 303 only responds over four octaves
|
||||
// although in theory its DAC could do five
|
||||
|
||||
// it's a bit of a waste defining 128 MIDI notes in the expo scale
|
||||
|
||||
uint8_t i;
|
||||
float x;
|
||||
|
||||
for (i = 0; i < 128; i++) {
|
||||
// expo pitch scale (MIDI note number to VCO control current)
|
||||
nekobee_pitch[i] = powf(2, (i - REF_NOTE) / 12.0f);
|
||||
// log pot scale used for volume, decay, cutoff, and env mod
|
||||
// for a range of "0 to 1" scaled to 0-127, gives a log response
|
||||
// with 50% of "pot rotation" giving 15% output
|
||||
x = i / 127.0f; // pot input from 0 to 1
|
||||
logpot[i] = 0.03225 * powf(32, x) - 0.03225;
|
||||
}
|
||||
// one extra value so we don't need to bounds check the linear interpolator
|
||||
logpot[128] = logpot[127];
|
||||
nekobee_pitch[128] = nekobee_pitch[127];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void vco(nekobee_synth_t *synth, uint32_t count) {
|
||||
// generate a bandlimited oscillator
|
||||
// uses polyblep for bandlimiting
|
||||
// massive and endless thanks to Mystran
|
||||
// https://www.kvraudio.com/forum/viewtopic.php?t=398553
|
||||
|
||||
nekobee_voice_t *voice = synth->voice;
|
||||
struct blosc_t *osc = &voice->osc;
|
||||
|
||||
uint32_t i;
|
||||
|
||||
float phase = osc->phase; // current running phase 0..1
|
||||
float delay = osc->delay; // delay sample for polyblep
|
||||
float out, t; // output sample, temporary value for blep
|
||||
|
||||
// calculate omega for phase shift
|
||||
float w = nekobee_pitch[voice->key] * 261.63 * synth->deltat;
|
||||
|
||||
// FIXME this only does saws
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
phase += w;
|
||||
out = delay;
|
||||
delay = 0;
|
||||
|
||||
if (phase > 1.0f) {
|
||||
t = (phase - 1) / w;
|
||||
out -= 0.5 * t * t; // other polynomials are available
|
||||
t = 1 - t;
|
||||
delay += 0.5 * t * t;
|
||||
phase -= 1.0f;
|
||||
}
|
||||
delay += phase; // save value for next time
|
||||
voice->osc_audio[i] =
|
||||
0.5 - out; // save output in buffer, remove DC offset
|
||||
}
|
||||
osc->phase = phase;
|
||||
osc->delay = delay;
|
||||
}
|
||||
|
||||
void vcf(nekobee_synth_t *synth, float *out, uint32_t count) {
|
||||
// run a 4-pole ladder filter over a block
|
||||
// this is a crude implementation that only approximates the complex
|
||||
// behaviour of the "real" ladder filter
|
||||
|
||||
// to calculate the cutoff frequency we need to solve the expo converter
|
||||
// not as bad as it sounds!
|
||||
// the equation is IcQ11 = IcQ10 * exp(-VbQ10 / 26)
|
||||
// VbQ10 is the voltage on Q10's base, IcQ10 is the collector current
|
||||
// this is supplied from the cutoff pot
|
||||
|
||||
nekobee_voice_t *voice = synth->voice;
|
||||
|
||||
float delay1 = voice->delay1, delay2 = voice->delay2,
|
||||
delay3 = voice->delay3, delay4 = voice->delay4;
|
||||
|
||||
float vcf_eg = voice->vcf_eg;
|
||||
float vca_eg = voice->vca_eg;
|
||||
float vca_slug = voice->vca_slug;
|
||||
|
||||
float delayhp = voice->delayhp;
|
||||
|
||||
// to get the correct cutoff first we need Q10's collector current
|
||||
// The top of VR3 Cutoff is fed from 12V, the bottom from the emitter
|
||||
// of Q9 at around 3.2V through a 10k resistor. So, 8.8V between "rails"
|
||||
// gives us (8.8*10k)/(10k+50k) = 1.47V at the bottom
|
||||
// The wiper of VR3 goes through R73 100k and TM3 470k, which we'll assume
|
||||
// is set to about half, call it 300k in total
|
||||
// So IcQ10 is given by (Vcutoff - Vbias - Vbe) / 300
|
||||
// For ease of working I just assume that Vbias is 0V and that the envelope
|
||||
// can go negative, from about 7V to about -3V the range of Vcutoff is
|
||||
// then 1.47 to 8.88-1.47 so 7.41V max
|
||||
|
||||
float Vcutoff = 1.47 + 7.41 * logpot[(int)floor(synth->cutoff)];
|
||||
|
||||
// similarly the envelope modulation pot is 50k log in series with 10k
|
||||
// but the top of the pot is fed with the envelope voltage, about 10V at
|
||||
// peak
|
||||
|
||||
float Renvmod = .167 + .833 * logpot[(int)floor(synth->envmod)];
|
||||
|
||||
// subtract the 3.2V offset from Q9
|
||||
float Venvmod = (voice->vcf_eg - 3.2) * Renvmod;
|
||||
|
||||
// R63 and R71 form a voltage divider, 2.2k / (220k + 2.2k) = 0.0099
|
||||
// multiply by 1000 to get a voltage in mV
|
||||
|
||||
Venvmod *= 9.901;
|
||||
float Vbe1 = Venvmod;
|
||||
|
||||
// .3 is 300k expressed as MOhm
|
||||
// if we expressed it in Ohms output would be in A
|
||||
float IcQ10 = (Vcutoff - 0.65) / .29; // 100k + TM3, IcQ10 in uA
|
||||
float IcQ11 = IcQ10 * exp(Vbe1 / 26.0); // in uA
|
||||
|
||||
// printf("Vbe1 = %04f, IcQ10 = %04f, IcQ11 = %04f\n", Vbe1, IcQ10, IcQ11);
|
||||
|
||||
float cutoff = IcQ11 * 96.67; // approximate Hz-per-uA
|
||||
|
||||
float ct = 6.2832 * cutoff * synth->deltat;
|
||||
|
||||
ct *= 0.25; // 4x oversampling
|
||||
ct = ct / (1 + ct);
|
||||
// printf("cutoff = %04fHz, ct=%f\n", cutoff, ct);
|
||||
|
||||
float hpc = 6.28 * 50 * synth->deltat;
|
||||
float fout, fb, hp;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
for (uint32_t ovs = 0; ovs < 4; ovs++) {
|
||||
float in = voice->osc_audio[i];
|
||||
|
||||
float clip = 1.0;
|
||||
fb = (in - ((fout - .133*in) * synth->resonance)) / clip;
|
||||
|
||||
//fb = in * synth->resonance*5;
|
||||
|
||||
if (fb > 1) fb=1;
|
||||
if (fb < -1) fb=-1;
|
||||
|
||||
fb = 1.5 * fb - 0.5 * fb*fb*fb;
|
||||
|
||||
fb *= clip;
|
||||
|
||||
//fb *= 0.5;
|
||||
|
||||
delay1 = ((fb - delay1) * ct) + delay1;
|
||||
delay2 = ((delay1 - delay2) * ct) + delay2;
|
||||
delay3 = ((delay2 - delay3) * ct) + delay3;
|
||||
delay4 = ((delay3 - delay4) * ct) + delay4;
|
||||
hp = ((delay4 - delayhp) * hpc ) + delayhp;
|
||||
delayhp = hp;
|
||||
fout = delay4-hp;
|
||||
}
|
||||
vca_slug = ((vca_eg - vca_slug)*(3000*synth->deltat))+vca_slug;
|
||||
out[i] = 0.5*fout * vca_slug;
|
||||
vcf_eg *= 1 - voice->vcf_tc;
|
||||
vca_eg *= 1 - voice->vca_tc;
|
||||
|
||||
}
|
||||
voice->delay1 = delay1;
|
||||
voice->delay2 = delay2;
|
||||
voice->delay3 = delay3;
|
||||
voice->delay4 = delay4;
|
||||
|
||||
voice->vcf_eg = vcf_eg;
|
||||
voice->vca_eg = vca_eg;
|
||||
voice->vca_slug = vca_slug;
|
||||
|
||||
voice->delayhp = delayhp;
|
||||
}
|
||||
|
||||
void nekobee_voice_render(nekobee_synth_t *synth, float *out, uint32_t count) {
|
||||
// generate "count" samples into the buffer at out
|
||||
|
||||
// FIXME factor this out into the control code
|
||||
// Decay is about 2.5 seconds with the pot all the way down, and 200ms with
|
||||
// it all the way up this is set by a 1M log pot in series with a 68k
|
||||
// resistor, a 1uF capacitor, and a bunch of other stuff to give a correct
|
||||
// DC offset and a log tailoff right at the very bottom
|
||||
|
||||
if (synth->voice->velocity < 90) {
|
||||
// printf("accent off\n");
|
||||
synth->voice->vcf_tc =
|
||||
(1 / ((68 + 1000 * logpot[(int)synth->decay]) * 0.001)) * synth->deltat;
|
||||
} else {
|
||||
synth->voice->vcf_tc = 1/(68*0.001)*synth->deltat;
|
||||
//printf("accent on\n");
|
||||
}
|
||||
// printf("tc = %f deltat=%f pot=%f\n",synth->voice->vcf_tc,
|
||||
// synth->deltat,logpot[(int)synth->decay]);
|
||||
|
||||
vco(synth, count);
|
||||
|
||||
vcf(synth, out, count);
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue