From ec66b7a21bce6ff9326d3d4eace6601eb688a28f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 28 Jan 2022 00:55:51 -0500 Subject: [PATCH] add song tuning soon: system clock settings --- demos/bruno_time.fur | Bin 10272 -> 10302 bytes papers/format.md | 3 +- src/engine/dispatch.h | 12 ++++++ src/engine/engine.cpp | 23 +++++++++-- src/engine/engine.h | 7 +++- src/engine/platform/amiga.cpp | 19 ++++----- src/engine/platform/arcade.cpp | 2 +- src/engine/platform/ay.cpp | 19 ++++----- src/engine/platform/ay8930.cpp | 19 ++++----- src/engine/platform/c64.cpp | 21 +++++----- src/engine/platform/gb.cpp | 17 ++++---- src/engine/platform/genesis.cpp | 22 ++++++----- src/engine/platform/genesisext.cpp | 8 ++-- src/engine/platform/nes.cpp | 24 ++++++------ src/engine/platform/nes.h | 1 - src/engine/platform/pce.cpp | 23 +++++------ src/engine/platform/saa.cpp | 21 +++++----- src/engine/platform/sms.cpp | 21 +++++----- src/engine/platform/tia.cpp | 4 +- src/engine/platform/tia.h | 2 + src/engine/platform/ym2610.cpp | 61 ++++++++++++++--------------- src/engine/platform/ym2610ext.cpp | 6 +-- src/engine/platform/ym2610shared.h | 3 +- src/engine/playback.cpp | 6 ++- src/engine/song.h | 2 + src/gui/gui.cpp | 21 +++++++--- 26 files changed, 211 insertions(+), 156 deletions(-) diff --git a/demos/bruno_time.fur b/demos/bruno_time.fur index 7905bed61cc1191e19121886d513f4ce984204f3..351a6e1f9de3701ad899e12d2c452bfdf6d6e823 100644 GIT binary patch literal 10302 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,CHIP_DIVIDER,x,true) +#define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false) + +#define COLOR_NTSC (315000000.0/88.0) +#define COLOR_PAL (283.75*15625.0+25.0) + #endif diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 6fdc39444..7156f1d2f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1355,8 +1355,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.author=reader.readString(); logI("%s by %s\n",ds.name.c_str(),ds.author.c_str()); + if (ds.version>=33) { + ds.tuning=reader.readF(); + } else { + reader.readI(); + } + // reserved - for (int i=0; i<24; i++) reader.readC(); + for (int i=0; i<20; i++) reader.readC(); // pointers reader.read(insPtr,ds.insLen*4); @@ -1699,9 +1705,11 @@ SafeWriter* DivEngine::saveFur() { w->writeString(song.name,false); // song author w->writeString(song.author,false); + + w->writeF(song.tuning); // reserved - for (int i=0; i<24; i++) { + for (int i=0; i<20; i++) { w->writeC(0); } @@ -3886,6 +3894,13 @@ void DivEngine::playSub(bool preserveDrift) { } } +int DivEngine::calcBaseFreq(double clock, double divider, int note, bool period) { + double base=(period?(song.tuning*0.0625):song.tuning)*pow(2.0,(float)(note+3)/12.0); + return period? + round((clock/base)/divider): + base*(divider/clock); +} + int DivEngine::calcFreq(int base, int pitch, bool period) { return period? int(base*pow(2,-(double)pitch/(12.0*128.0))/(98.0+globalPitch*6.0)*98.0): @@ -4014,7 +4029,7 @@ void DivEngine::previewSample(int sample, int note) { blip_clear(samp_bb); double rate=song.sample[sample]->rate; if (note>=0) { - rate=(440.0*pow(2.0,(double)(note+3)/12.0)); + rate=(song.tuning*pow(2.0,(double)(note+3)/12.0)); if (rate<=0) rate=song.sample[sample]->rate; } blip_set_rates(samp_bb,rate,got.rate); @@ -4045,7 +4060,7 @@ void DivEngine::previewWave(int wave, int note) { return; } blip_clear(samp_bb); - blip_set_rates(samp_bb,song.wave[wave]->len*(27.5*pow(2.0,(double)(note+3)/12.0)),got.rate); + blip_set_rates(samp_bb,song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0)),got.rate); samp_prevSample=0; sPreview.pos=0; sPreview.sample=-1; diff --git a/src/engine/engine.h b/src/engine/engine.h index 3cdef106d..a0ff5678c 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -11,8 +11,8 @@ #include #include -#define DIV_VERSION "0.5pre3" -#define DIV_ENGINE_VERSION 32 +#define DIV_VERSION "0.5pre4" +#define DIV_ENGINE_VERSION 33 enum DivStatusView { DIV_STATUS_NOTHING=0, @@ -260,6 +260,9 @@ class DivEngine { void setConf(String key, double value); void setConf(String key, String value); + // calculate base frequency/period + int calcBaseFreq(double clock, double divider, int note, bool period); + // calculate frequency/period int calcFreq(int base, int pitch, bool period=false); diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 80a06483a..6ed9f6c84 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -2,8 +2,8 @@ #include "../engine.h" #include -#define FREQ_BASE 6843.0f #define AMIGA_DIVIDER 8 +#define CHIP_DIVIDER 16 void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t len) { for (size_t h=start; hgetIns(chan[c.chan].ins); if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); } chan[c.chan].sample=ins->amiga.initSample; if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { @@ -151,7 +151,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].keyOn=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=round(FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -174,7 +174,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp-12):(0)))/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp-12):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; @@ -243,10 +243,11 @@ void DivPlatformAmiga::notifyInsDeletion(void* ins) { void DivPlatformAmiga::setPAL(bool pal) { if (pal) { - rate=3546895/AMIGA_DIVIDER; + chipClock=COLOR_PAL*4.0/5.0; } else { - rate=3579545/AMIGA_DIVIDER; + chipClock=COLOR_NTSC; } + rate=chipClock/AMIGA_DIVIDER; } int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 43e3090a6..2823f32fe 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -330,7 +330,7 @@ void DivPlatformArcade::tick() { if (chan[i].freqChanged) { chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64; if (chan[i].furnacePCM) { - chan[i].pcm.freq=MIN(255,((440.0*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250); + chan[i].pcm.freq=MIN(255,((parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250); if (dumpWrites && i>=8) { addWrite(0x10007+((i-8)<<3),chan[i].pcm.freq); } diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 4bc3328ff..0a422f144 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -7,7 +7,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define PSG_FREQ_BASE 6848.0f +#define CHIP_DIVIDER 8 void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t len) { if (ayBufLengetIns(chan[c.chan].ins); if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -193,7 +193,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -216,7 +216,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; break; } @@ -357,10 +357,11 @@ void DivPlatformAY8910::notifyInsDeletion(void* ins) { void DivPlatformAY8910::setPAL(bool pal) { if (pal) { - rate=221681; + chipClock=COLOR_PAL*2.0/5.0; } else { - rate=223722; + chipClock=COLOR_NTSC/2.0; } + rate=chipClock/8; } int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 5641c7a5d..bdae663c0 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -7,7 +7,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define PSG_FREQ_BASE 6848.0f +#define CHIP_DIVIDER 8 void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t len) { if (ayBufLengetIns(chan[c.chan].ins); if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -220,7 +220,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -243,7 +243,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; break; } @@ -392,10 +392,11 @@ void DivPlatformAY8930::notifyInsDeletion(void* ins) { void DivPlatformAY8930::setPAL(bool pal) { if (pal) { - rate=221681; + chipClock=COLOR_PAL*2.0/5.0; } else { - rate=223722; + chipClock=COLOR_NTSC/2.0; } + rate=chipClock/8; } int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index b5760a3a5..b86ed32b5 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -2,10 +2,10 @@ #include "../engine.h" #include -#define FREQ_BASE 277.0f - #define rWrite(a,v) if (!skipRegisterWrites) {sid.write(a,v); if (dumpWrites) {addWrite(a,v);} } +#define CHIP_FREQBASE 524288 + void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) { for (size_t i=start; igetIns(chan[c.chan].ins); if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(FREQ_BASE*pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -170,7 +170,7 @@ int DivPlatformC64::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=round(FREQ_BASE*pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_FREQUENCY(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -207,7 +207,7 @@ int DivPlatformC64::dispatch(DivCommand c) { rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|chan[c.chan].active); break; case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=round(FREQ_BASE*pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)))/12.0f))); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; @@ -357,10 +357,11 @@ void DivPlatformC64::setChipModel(bool is6581) { void DivPlatformC64::setPAL(bool pal) { if (pal) { - rate=985248; + rate=COLOR_PAL*2.0/9.0; } else { - rate=1022727; + rate=COLOR_NTSC*2.0/7.0; } + chipClock=rate; } int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 702139f30..069150b4c 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -5,7 +5,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {GB_apu_write(gb,a,v); if (dumpWrites) {addWrite(a,v);} } #define immWrite(a,v) {GB_apu_write(gb,a,v); if (dumpWrites) {addWrite(a,v);} } -#define FREQ_BASE 8015.85f +#define CHIP_DIVIDER 16 void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len) { for (size_t i=start; ichan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -261,7 +261,7 @@ int DivPlatformGB::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: if (c.chan==3) break; - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)))/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; @@ -356,7 +356,8 @@ int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, bool pal) { parent=p; dumpWrites=false; skipRegisterWrites=false; - rate=262144; + chipClock=4194304; + rate=chipClock/16; gb=new GB_gameboy_t; reset(); return 4; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index d45205778..28667ebee 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -9,6 +9,8 @@ static unsigned char konOffs[6]={ 0, 1, 2, 4, 5, 6 }; +#define CHIP_FREQBASE 9440540 + void DivPlatformGenesis::acquire(short* bufL, short* bufR, size_t start, size_t len) { static short o[2]; static int os[2]; @@ -104,15 +106,15 @@ void DivPlatformGenesis::tick() { if (chan[i].std.hadArp) { if (!chan[i].inPorta) { if (chan[i].std.arpMode) { - chan[i].baseFreq=644.0f*pow(2.0f,((float)chan[i].std.arp/12.0f)); + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp); } else { - chan[i].baseFreq=644.0f*pow(2.0f,((float)(chan[i].note+(signed char)chan[i].std.arp)/12.0f)); + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp); } } chan[i].freqChanged=true; } else { if (chan[i].std.arpMode && chan[i].std.finishedArp) { - chan[i].baseFreq=644.0f*pow(2.0f,((float)chan[i].note/12.0f)); + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); chan[i].freqChanged=true; } } @@ -307,7 +309,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } dacPos=0; dacPeriod=0; - chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; chan[c.chan].furnaceDac=true; } else { // compatible mode @@ -365,7 +367,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; } @@ -428,7 +430,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=644.0f*pow(2.0f,((float)c.value2/12.0f)); + int destFreq=NOTE_FREQUENCY(c.value2); int newFreq; bool return2=false; if (destFreq>chan[c.chan].baseFreq) { @@ -471,7 +473,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; break; @@ -638,10 +640,12 @@ void DivPlatformGenesis::notifyInsDeletion(void* ins) { void DivPlatformGenesis::setPAL(bool pal) { if (pal) { - rate=211125; + chipClock=COLOR_PAL*12.0/7.0; } else { - rate=213068; + chipClock=COLOR_NTSC*15.0/7.0; } + psg.setPAL(pal); + rate=chipClock/36; } int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 7f354d452..078ca38bf 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -4,6 +4,8 @@ #include "genesisshared.h" +#define CHIP_FREQBASE 9440540 + int DivPlatformGenesisExt::dispatch(DivCommand c) { if (c.chan<2) { return DivPlatformGenesis::dispatch(c); @@ -54,7 +56,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - opChan[ch].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].freqChanged=true; } opChan[ch].keyOn=true; @@ -108,7 +110,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=644.0f*pow(2.0f,((float)c.value2/12.0f)); + int destFreq=NOTE_FREQUENCY(c.value2); int newFreq; bool return2=false; if (destFreq>opChan[ch].baseFreq) { @@ -141,7 +143,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - opChan[ch].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].freqChanged=true; break; } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index e5c8c8673..a02a72b62 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -4,8 +4,7 @@ #include #include -#define FREQ_BASE 3424.0f -#define FREQ_BASE_PAL 3180.0f +#define CHIP_DIVIDER 16 #define rWrite(a,v) if (!skipRegisterWrites) {apu_wr_reg(nes,a,v); if (dumpWrites) {addWrite(a,v);} } @@ -97,16 +96,16 @@ void DivPlatformNES::tick() { } else { if (!chan[i].inPorta) { if (chan[i].std.arpMode) { - chan[i].baseFreq=round(freqBase/pow(2.0f,((float)(chan[i].std.arp)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp); } else { - chan[i].baseFreq=round(freqBase/pow(2.0f,((float)(chan[i].note+chan[i].std.arp)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp); } } } chan[i].freqChanged=true; } else { if (chan[i].std.arpMode && chan[i].std.finishedArp) { - chan[i].baseFreq=round(freqBase/pow(2.0f,((float)(chan[i].note)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note); chan[i].freqChanged=true; } } @@ -190,7 +189,7 @@ int DivPlatformNES::dispatch(DivCommand c) { } dacPos=0; dacPeriod=0; - chan[c.chan].baseFreq=440.0f*pow(2.0f,((float)(c.value+3)/12.0f)); + chan[c.chan].baseFreq=parent->song.tuning*pow(2.0f,((float)(c.value+3)/12.0f)); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; @@ -220,7 +219,7 @@ int DivPlatformNES::dispatch(DivCommand c) { } } else { if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(freqBase/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); } } if (c.value!=DIV_NOTE_NULL) { @@ -273,7 +272,7 @@ int DivPlatformNES::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=round(freqBase/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -309,7 +308,7 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: if (c.chan==3) break; - chan[c.chan].baseFreq=round(freqBase/pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)))/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; @@ -377,16 +376,15 @@ bool DivPlatformNES::keyOffAffectsArp(int ch) { void DivPlatformNES::setPAL(bool pal) { if (pal) { - rate=1662607; - freqBase=FREQ_BASE_PAL; + rate=COLOR_PAL*3.0/8.0; apuType=1; nes->apu.type=apuType; } else { - rate=1789773; - freqBase=FREQ_BASE; + rate=COLOR_NTSC/2.0; apuType=0; nes->apu.type=apuType; } + chipClock=rate; } void DivPlatformNES::notifyInsDeletion(void* ins) { diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 3249da985..52814fee0 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -41,7 +41,6 @@ class DivPlatformNES: public DivDispatch { unsigned char apuType; struct NESAPU* nes; - float freqBase; friend void putDispatchChan(void*,int,int); public: diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 2b957928d..06a1b2fd2 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -13,7 +13,7 @@ rWrite(a,v); \ } -#define FREQ_BASE 1712.0f*2 +#define CHIP_DIVIDER 32 void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) { for (size_t h=start; hgetIns(chan[i].ins); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); if (chan[i].furnaceDac) { - chan[i].dacRate=1789773/chan[i].freq; + chan[i].dacRate=chipClock/chan[i].freq; if (dumpWrites) addWrite(0xffff0001+(i<<8),chan[i].dacRate); } if (chan[i].freq>4095) chan[i].freq=4095; @@ -186,7 +186,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { chan[c.chan].dacPos=0; chan[c.chan].dacPeriod=0; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -215,7 +215,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { break; } if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|noiseFreq[chan[c.chan].note%12]):0); @@ -270,7 +270,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { rWrite(0x08,c.value); break; case DIV_CMD_NOTE_PORTA: { - int destFreq=round(FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -311,7 +311,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)))/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; @@ -398,10 +398,11 @@ void DivPlatformPCE::notifyInsDeletion(void* ins) { void DivPlatformPCE::setPAL(bool pal) { if (pal) { // technically there is no PAL PC Engine but oh well... - rate=1773448/6; + chipClock=COLOR_PAL*4.0/5.0; } else { - rate=1789773/6; + chipClock=COLOR_NTSC; } + rate=chipClock/12; } int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 6cc341a1e..fb678ac81 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -6,7 +6,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define PSG_FREQ_BASE 122240.0f +#define CHIP_DIVIDER 2 void DivPlatformSAA1099::acquire(short* bufL, short* bufR, size_t start, size_t len) { if (saaBufLengetIns(chan[c.chan].ins); if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -171,7 +171,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value*(8-chan[c.chan].freqH); @@ -202,7 +202,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; break; } @@ -308,11 +308,8 @@ void DivPlatformSAA1099::notifyInsDeletion(void* ins) { } void DivPlatformSAA1099::setPAL(bool pal) { - if (pal) { - rate=250000; - } else { - rate=250000; - } + chipClock=8000000; + rate=chipClock/32; } int DivPlatformSAA1099::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 70d3d4bf7..4a0e03d7d 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -2,10 +2,10 @@ #include "../engine.h" #include -#define FREQ_BASE 1712.0f - #define rWrite(v) {if (!skipRegisterWrites) {sn->write(v); if (dumpWrites) {addWrite(0x200,v);}}} +#define CHIP_DIVIDER 64 + void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) { sn->sound_stream_update(bufL+start,len); } @@ -25,14 +25,14 @@ void DivPlatformSMS::tick() { } if (chan[i].std.hadArp) { if (chan[i].std.arpMode) { - chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].std.arp)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp); } else { - chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].note+chan[i].std.arp)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp); } chan[i].freqChanged=true; } else { if (chan[i].std.arpMode && chan[i].std.finishedArp) { - chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].note)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note); chan[i].freqChanged=true; } } @@ -93,7 +93,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -130,7 +130,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=round(FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -154,7 +154,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { updateSNMode=true; break; case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)))/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; @@ -223,10 +223,11 @@ void DivPlatformSMS::notifyInsDeletion(void* ins) { void DivPlatformSMS::setPAL(bool pal) { if (pal) { - rate=221681; + chipClock=COLOR_PAL*4.0/5.0; } else { - rate=223722; + chipClock=COLOR_NTSC; } + rate=chipClock/16; } int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, bool pal) { diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index ebd587cc0..fb39940fa 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -9,12 +9,12 @@ void DivPlatformTIA::acquire(short* bufL, short* bufR, size_t start, size_t len) tia.process(bufL+start,len); } -unsigned char dealWithFreq(unsigned char shape, int base, int pitch) { +unsigned char DivPlatformTIA::dealWithFreq(unsigned char shape, int base, int pitch) { if (base&0x80000000 && ((base&0x7fffffff)<32)) { return base&0x1f; } int bp=base+pitch; - double mult=0.25*27.5*pow(2.0,double(768+bp)/(256.0*12.0)); + double mult=0.25*(parent->song.tuning*0.0625)*pow(2.0,double(768+bp)/(256.0*12.0)); switch (shape) { case 1: // buzzy return ceil(31400/(30.6*mult))-1; diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index 180e20351..2828b00e8 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -20,6 +20,8 @@ class DivPlatformTIA: public DivDispatch { bool isMuted[2]; TIASound tia; friend void putDispatchChan(void*,int,int); + + unsigned char dealWithFreq(unsigned char shape, int base, int pitch); public: void acquire(short* bufL, short* bufR, size_t start, size_t len); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 0ffd4775e..4196a1e44 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -11,6 +11,8 @@ static unsigned char konOffs[4]={ 1, 2, 5, 6 }; +#define CHIP_DIVIDER 32 + void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; @@ -57,15 +59,15 @@ void DivPlatformYM2610::tick() { if (chan[i].std.hadArp) { if (!chan[i].inPorta) { if (chan[i].std.arpMode) { - chan[i].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)(chan[i].std.arp)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp); } else { - chan[i].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)(chan[i].note+chan[i].std.arp)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp); } } chan[i].freqChanged=true; } else { if (chan[i].std.arpMode && chan[i].std.finishedArp) { - chan[i].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)(chan[i].note)/12.0f))); + chan[i].baseFreq=NOTE_PERIODIC(chan[i].note); chan[i].freqChanged=true; } } @@ -165,15 +167,15 @@ void DivPlatformYM2610::tick() { if (chan[i].std.hadArp) { if (!chan[i].inPorta) { if (chan[i].std.arpMode) { - chan[i].baseFreq=FM_FREQ_BASE*pow(2.0f,((float)chan[i].std.arp/12.0f)); + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp); } else { - chan[i].baseFreq=FM_FREQ_BASE*pow(2.0f,((float)(chan[i].note+(signed char)chan[i].std.arp)/12.0f)); + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp); } } chan[i].freqChanged=true; } else { if (chan[i].std.arpMode && chan[i].std.finishedArp) { - chan[i].baseFreq=FM_FREQ_BASE*pow(2.0f,((float)chan[i].note/12.0f)); + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); chan[i].freqChanged=true; } } @@ -278,19 +280,19 @@ void DivPlatformYM2610::tick() { } int DivPlatformYM2610::octave(int freq) { - if (freq>=FM_FREQ_BASE*128) { + if (freq>=622.0f*128) { return 128; - } else if (freq>=FM_FREQ_BASE*64) { + } else if (freq>=622.0f*64) { return 64; - } else if (freq>=FM_FREQ_BASE*32) { + } else if (freq>=622.0f*32) { return 32; - } else if (freq>=FM_FREQ_BASE*16) { + } else if (freq>=622.0f*16) { return 16; - } else if (freq>=FM_FREQ_BASE*8) { + } else if (freq>=622.0f*8) { return 8; - } else if (freq>=FM_FREQ_BASE*4) { + } else if (freq>=622.0f*4) { return 4; - } else if (freq>=FM_FREQ_BASE*2) { + } else if (freq>=622.0f*2) { return 2; } else { return 1; @@ -299,19 +301,19 @@ int DivPlatformYM2610::octave(int freq) { } int DivPlatformYM2610::toFreq(int freq) { - if (freq>=FM_FREQ_BASE*128) { + if (freq>=622.0f*128) { return 0x3800|((freq>>7)&0x7ff); - } else if (freq>=FM_FREQ_BASE*64) { + } else if (freq>=622.0f*64) { return 0x3000|((freq>>6)&0x7ff); - } else if (freq>=FM_FREQ_BASE*32) { + } else if (freq>=622.0f*32) { return 0x2800|((freq>>5)&0x7ff); - } else if (freq>=FM_FREQ_BASE*16) { + } else if (freq>=622.0f*16) { return 0x2000|((freq>>4)&0x7ff); - } else if (freq>=FM_FREQ_BASE*8) { + } else if (freq>=622.0f*8) { return 0x1800|((freq>>3)&0x7ff); - } else if (freq>=FM_FREQ_BASE*4) { + } else if (freq>=622.0f*4) { return 0x1000|((freq>>2)&0x7ff); - } else if (freq>=FM_FREQ_BASE*2) { + } else if (freq>=622.0f*2) { return 0x800|((freq>>1)&0x7ff); } else { return freq&0x7ff; @@ -351,7 +353,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { if (c.chan>3) { // PSG if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -397,7 +399,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=FM_FREQ_BASE*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -479,7 +481,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } case DIV_CMD_NOTE_PORTA: { if (c.chan>3) { // PSG - int destFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + int destFreq=NOTE_PERIODIC(c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -502,7 +504,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { break; } - int destFreq=FM_FREQ_BASE*pow(2.0f,((float)c.value2/12.0f)); + int destFreq=NOTE_FREQUENCY(c.value2); int newFreq; bool return2=false; if (destFreq>chan[c.chan].baseFreq) { @@ -539,9 +541,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { if (c.chan>3) { // PSG - chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); } else { - chan[c.chan].baseFreq=FM_FREQ_BASE*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); } chan[c.chan].freqChanged=true; break; @@ -794,11 +796,8 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) { for (int i=0; i<13; i++) { isMuted[i]=false; } - if (pal) { - rate=500000; - } else { - rate=500000; - } + chipClock=8000000; + rate=chipClock/16; iface.parent=parent; iface.sampleBank=0; fm=new ymfm::ym2610(iface); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 71072edad..055dfeda3 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -46,7 +46,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - opChan[ch].baseFreq=FM_FREQ_BASE*pow(2.0f,((float)c.value/12.0f)); + opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].freqChanged=true; } opChan[ch].keyOn=true; @@ -102,7 +102,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=FM_FREQ_BASE*pow(2.0f,((float)c.value2/12.0f)); + int destFreq=NOTE_FREQUENCY(c.value2); int newFreq; bool return2=false; if (destFreq>opChan[ch].baseFreq) { @@ -135,7 +135,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - opChan[ch].baseFreq=FM_FREQ_BASE*pow(2.0f,((float)c.value/12.0f)); + opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].freqChanged=true; break; } diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 169db0d79..d7c792d07 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -26,5 +26,4 @@ static int orderedOps[4]={ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define FM_FREQ_BASE 622.0f -#define PSG_FREQ_BASE 7640.0f +#define CHIP_FREQBASE 9509775 \ No newline at end of file diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 22aed29b4..a8a2052fa 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1005,6 +1005,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } if (!playing) { + if (out!=NULL) { + memcpy(oscBuf[0],out[0],size*sizeof(float)); + memcpy(oscBuf[1],out[1],size*sizeof(float)); + oscSize=size; + } isBusy.unlock(); return; } @@ -1139,7 +1144,6 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi while (metroPos>=1) metroPos--; } - // TODO: improve memcpy(oscBuf[0],out[0],size*sizeof(float)); memcpy(oscBuf[1],out[1],size*sizeof(float)); oscSize=size; diff --git a/src/engine/song.h b/src/engine/song.h index d05975a83..e19469467 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -106,6 +106,7 @@ struct DivSong { bool pal; bool customTempo; int hz, patLen, ordersLen, insLen, waveLen, sampleLen; + float tuning; // compatibility flags bool limitSlides; // limit slide range @@ -151,6 +152,7 @@ struct DivSong { insLen(0), waveLen(0), sampleLen(0), + tuning(440.0f), limitSlides(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f76e270e5..04342ec2c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -569,6 +569,16 @@ void FurnaceGUI::drawSongInfo() { ImGui::SameLine(); ImGui::Text("NTSC"); } + + ImGui::Text("Tuning (A-4)"); + ImGui::SameLine(); + float tune=e->song.tuning; + ImGui::SetNextItemWidth(120.0f*dpiScale); + if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { + if (tune<220.0f) tune=220.0f; + if (tune>660.0f) tune=660.0f; + e->song.tuning=tune; + } } if (ImGui::IsWindowFocused()) curWindow=GUI_WINDOW_SONG_INFO; ImGui::End(); @@ -1980,19 +1990,20 @@ void FurnaceGUI::drawMixer() { void FurnaceGUI::drawOsc() { if (!oscOpen) return; ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); if (ImGui::Begin("Oscilloscope",&oscOpen)) { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); float values[512]; for (int i=0; i<512; i++) { int pos=i*e->oscSize/512; values[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; } + //ImGui::SetCursorPos(ImVec2(0,0)); ImGui::PlotLines("##SingleOsc",values,512,0,NULL,-1.0f,1.0f,ImGui::GetContentRegionAvail()); - ImGui::PopStyleVar(4); } + ImGui::PopStyleVar(4); ImGui::End(); }