pitch computation
This commit is contained in:
parent
88ca7a5037
commit
af49dcf7b6
@ -38,6 +38,10 @@ void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) {
|
||||
void Chassis::activate() {
|
||||
// calculate filter coefficients and stuff
|
||||
printf("called activate()\n");
|
||||
|
||||
for (uint8_t i=0; i<104; i++) {
|
||||
s.pitchCV[i] = (261.63 * powf(2, (i-24) / 12.0f)) / sampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
void Chassis::deactivate() {
|
||||
@ -126,6 +130,15 @@ void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
||||
s.runLFO();
|
||||
|
||||
for (uint32_t i = 0; i < NUM_VOICES; i++) {
|
||||
s.voice[i].calcPitch(s);
|
||||
|
||||
switch(s.patchRam.switch1 & 0x03) {
|
||||
case 1: s.voice[i].omega /= 4; break;
|
||||
case 2: s.voice[i].omega /= 2;
|
||||
default: break;
|
||||
}
|
||||
|
||||
//printf("voice %d note = %02x ff71 = %04x\n",i, s.voice[i].note, s.voice[i].ff71 );
|
||||
s.voice[i].gate(s);
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,119 @@ bool Voice::isFree() {
|
||||
return ff10 == false;
|
||||
}
|
||||
|
||||
void Voice::calcPitch(Synth &s) {
|
||||
uint32_t bc, de, ea, a;
|
||||
|
||||
// 03ad
|
||||
ea = 0x1818;
|
||||
|
||||
// add in tuning value from ff61, not implemented
|
||||
|
||||
// 03ba
|
||||
bc = s.ff51; // computed pitch LFO
|
||||
if (s.ff4a & 0x02) {
|
||||
ea -= bc;
|
||||
} else {
|
||||
ea += bc;
|
||||
}
|
||||
|
||||
// 03c6
|
||||
// add in bender from ff68
|
||||
|
||||
// 03d2
|
||||
s.ff6f = ea;
|
||||
|
||||
ea = ff71;
|
||||
a = note;
|
||||
|
||||
// 03e3
|
||||
bc = a << 8;
|
||||
|
||||
// 03e6
|
||||
a = 0; // set from porta coefficient ff7d
|
||||
|
||||
if (a != 0) goto h03f5;
|
||||
|
||||
// 3eb
|
||||
ea = bc;
|
||||
h03ec:
|
||||
ff71 = ea; // STEAX (DE++)
|
||||
|
||||
// we're not looping so we can ignore until
|
||||
// 03ee inrw ff0f voice counter
|
||||
// 03f0 eqiw ff0f, 06
|
||||
// 03f3 jr 03e0
|
||||
|
||||
goto h0407;
|
||||
|
||||
h03f5:
|
||||
if (ea == bc) goto h03ec; // DNE EA, BC; JR 03EC store value
|
||||
if (!(ea > bc)) goto h0401; // DGT EA, BC; JR 0401
|
||||
ea -= a;
|
||||
if (!(ea > bc)) ea = bc;
|
||||
goto h03ec;
|
||||
h0401:
|
||||
ea += a;
|
||||
if (!(ea < bc)) ea = bc;
|
||||
goto h03ec;
|
||||
|
||||
h0407:
|
||||
// this outputs the Sub Osc CV
|
||||
// ignore until
|
||||
// 0413 mviw ff0f, 0 reset voice counter
|
||||
s.ff34 = 1; // unsure what this is used for
|
||||
|
||||
// 0419
|
||||
ea = ff71; // pitch + fraction per voice
|
||||
bc = s.ff6f; // tune + lfo + bend
|
||||
ea += bc;
|
||||
// 0424
|
||||
s.ff6e = ea & 0xff; // MOV A, EAL; STAW 006e
|
||||
a = ea >> 8; // mov a, EAH
|
||||
|
||||
// 0428
|
||||
if (a <= 0x2f) goto h04a5; // GTI A,$2F; JRE $04A5
|
||||
if (a >= 0x97) goto h04ac; // LTI A,$97; JRE $04AC
|
||||
|
||||
a -= 0x30;
|
||||
|
||||
h0432:
|
||||
// printf("setting omega for note %d \n", a);
|
||||
omega = ((s.pitchCV[a + 1] - s.pitchCV[a]) * (s.ff6e / 256.0)) + s.pitchCV[a];
|
||||
// 0432 onwards calculates the address for the CV
|
||||
// table at E60 and stacks it
|
||||
// 043a onwards fetches the value from the divider
|
||||
// table and computes a linear interpolation with the next one up
|
||||
// using the fractional value stored in ff6e
|
||||
|
||||
// 045a onwards decides which divider to program
|
||||
|
||||
// 0471 unstacks the CV table address and calculates a linear
|
||||
// interpolation of this and the next CV value using ff6e
|
||||
// 048b onwards sends it to the correct DAC
|
||||
|
||||
// 0496 onwards works out which vocie to do next and loops
|
||||
|
||||
// 04a3
|
||||
goto h04d5;
|
||||
|
||||
h04a5: // pitch too low
|
||||
s.ff6e = 0;
|
||||
a = 0;
|
||||
goto h0432;
|
||||
|
||||
h04ac: // pitch too high
|
||||
s.ff6e = 0;
|
||||
a = 0x66;
|
||||
goto h0432;
|
||||
|
||||
// 04b3 programs the dividers somehow
|
||||
// ignore until 04d5
|
||||
|
||||
h04d5:
|
||||
return;
|
||||
}
|
||||
|
||||
void Voice::on(uint32_t key, bool reset = 0) {
|
||||
// what's with the crazy private variables and all the gotos with crazy labels?
|
||||
// this code emulates the 78C11 code directly (probably inefficiently)
|
||||
@ -37,6 +150,8 @@ void Voice::on(uint32_t key, bool reset = 0) {
|
||||
// this current implementation doesn't reset the voice
|
||||
(void)reset;
|
||||
|
||||
// printf("called with key=%d\n", note);
|
||||
|
||||
ff10 = true; // note held from keyboard
|
||||
ff07 = true; // attack phase
|
||||
if (note == key) goto h0144;
|
||||
@ -57,7 +172,9 @@ h0144:
|
||||
h0149:
|
||||
// this is in the wrong place really but is the equivalent of programming the counter
|
||||
// and VCO ramp DAC
|
||||
omega = (261.63 * powf(2, (note - 60) / 12.0f)) / 48000.0f;
|
||||
// omega = (261.63 * powf(2, (note - 60) / 12.0f)) / 48000.0f;
|
||||
// printf("note, key = %d, %d\n", note, key);
|
||||
return;
|
||||
}
|
||||
|
||||
void Voice::off() {
|
||||
@ -152,7 +269,7 @@ h0323:
|
||||
// 032b
|
||||
bc |= 0xff00; // initial scaling value?
|
||||
|
||||
//printf("0323 ");
|
||||
// printf("0323 ");
|
||||
|
||||
h032d:
|
||||
tos = bc; // push bc
|
||||
@ -178,7 +295,8 @@ h032d:
|
||||
ea = (bc & 0xff) * a; // MUL C
|
||||
d = a; // MOV D,A
|
||||
a = (ea >> 8); // MOV A,EAH
|
||||
bc &= 0xff00; bc |= a; // MOV C,A
|
||||
bc &= 0xff00;
|
||||
bc |= a; // MOV C,A
|
||||
a = d; // MOV A,D
|
||||
|
||||
// 0345
|
||||
@ -203,7 +321,8 @@ h032d:
|
||||
ea = (bc & 0xff) * a; // MUL C
|
||||
d = a; // MOV D,A
|
||||
a = ea >> 8; // MOV A, EAH
|
||||
bc &= 0xff00; bc |= a; // MOV C,A
|
||||
bc &= 0xff00;
|
||||
bc |= a; // MOV C,A
|
||||
a = d; // MOV A,D
|
||||
ea = (bc >> 8) * a; // MUL B
|
||||
ea += (bc & 0xff); // EADD EA,C
|
||||
@ -216,7 +335,7 @@ h036b:
|
||||
goto h0323;
|
||||
|
||||
h0370: // calculate holdoff time
|
||||
//printf("0370 ");
|
||||
// printf("0370 ");
|
||||
ea = ff56; // holdoff time
|
||||
bc = attackTable[patchRam.lfoDelay]; // stored at ff58
|
||||
// 0379
|
||||
@ -231,13 +350,13 @@ h0385:
|
||||
ff1e |= 0x02; // stop predelay flag
|
||||
|
||||
h0388:
|
||||
//printf("0388 ");
|
||||
// printf("0388 ");
|
||||
ea = ff5a; // envelope speed
|
||||
|
||||
// 038d
|
||||
bc = lfoDelayTable[patchRam.lfoDelay >> 4]; // delay setting divided by 8 and saved at ff6c
|
||||
|
||||
//printf("---------------------------------------- %04x %04x\n", ea, bc);
|
||||
// printf("---------------------------------------- %04x %04x\n", ea, bc);
|
||||
|
||||
// 0391 DADDNC EA, BC
|
||||
if ((ea + bc) > 0xffff) goto h039a;
|
||||
@ -255,7 +374,7 @@ h039a:
|
||||
goto h032d;
|
||||
|
||||
h03a1:
|
||||
//printf("LFO=%04x VCF=%04x flags=%02x holdoff=%04x envelope=%04x\n", ff51, ff53, ff1e, ff56, ff5a);
|
||||
// printf("LFO=%04x VCF=%04x flags=%02x holdoff=%04x envelope=%04x\n", ff51, ff53, ff1e, ff56, ff5a);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -19,10 +19,11 @@
|
||||
// contains the actual sound generation code
|
||||
|
||||
#include "voice.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
static inline float poly3blep0(float t) {
|
||||
float t2 = t * t;
|
||||
return 2 * (t * t2 - 0.5f * t2 * t2);
|
||||
@ -40,7 +41,6 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
|
||||
// there's a resistor on the panel board to sprag the range
|
||||
float pw = s.ff4f / 32768.0f;
|
||||
|
||||
|
||||
float sqr = 0.175;
|
||||
float saw = (s.patchRam.switch1 & 0x10) ? 0.220 : 0;
|
||||
float sub = (s.patchRam.sub / 127.0f) * 0.275;
|
||||
|
@ -29,14 +29,14 @@ class Voice {
|
||||
uint8_t note = 60; // per-voice note, set to middle C at 02b1h
|
||||
|
||||
void on(uint32_t key, bool reset);
|
||||
|
||||
void off();
|
||||
|
||||
bool isFree();
|
||||
|
||||
void run(Synth &s, float *buffer, uint32_t samples);
|
||||
|
||||
void gate(Synth &s);
|
||||
void calcPitch(Synth &s);
|
||||
uint16_t ff71 = 0; // stores pitch + fraction
|
||||
|
||||
float omega;
|
||||
|
||||
private:
|
||||
enum { ATTACK,
|
||||
@ -57,7 +57,7 @@ class Voice {
|
||||
|
||||
uint16_t env;
|
||||
|
||||
float phase = 0, omega = 260 / 48000.0, subosc = 1;
|
||||
float phase = 0, subosc = 1;
|
||||
// float env, target;
|
||||
float delay;
|
||||
uint8_t pulseStage = 0;
|
||||
@ -111,6 +111,7 @@ class Synth {
|
||||
// RAM from ff00h to ffffh is cleared to zero
|
||||
// this is in the startup routine at 0280h
|
||||
uint8_t ff1e = 0;
|
||||
uint8_t ff34 = 0;
|
||||
uint8_t ff4a = 0; // LFO flags
|
||||
uint16_t ff4d = 0; // LFO output value
|
||||
uint16_t ff4f = 0; // computed PWM LFO
|
||||
@ -119,6 +120,9 @@ class Synth {
|
||||
uint16_t ff56 = 0; // LFO Delay envelope
|
||||
uint16_t ff5a = 0; // LFO Delay holdoff
|
||||
uint8_t ff64 = 0; // LFO mod sens amount
|
||||
uint8_t ff6e = 0; // fractional pitch temp
|
||||
uint16_t ff6f = 0; // computed pitch amount
|
||||
//uint16_t ff71 = 0; // unsure, to do with pitch
|
||||
|
||||
// okay, not the greatest, this right here
|
||||
// this struct contains the bytes that make up a Juno 106 patch in
|
||||
@ -200,6 +204,8 @@ class Synth {
|
||||
uint16_t lfoDelayTable[8] = {
|
||||
0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100};
|
||||
|
||||
float pitchCV[104];
|
||||
|
||||
void runLFO();
|
||||
void lfoDelay();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user