pitch computation

This commit is contained in:
Gordon JC Pearce 2024-09-09 13:34:02 +01:00
parent 88ca7a5037
commit af49dcf7b6
4 changed files with 160 additions and 22 deletions

View File

@ -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);
}
}

View File

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

View File

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

View File

@ -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();
};