Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

* 'master' of https://github.com/tildearrow/furnace: (77 commits)
  GUI: use pattern font for input latch
  GUI: partially implement note input latch UI
  GUI: transpose value does not transpose effect ID
  GUI: prepare for note input latch UI
  GUI: min ins selector width fix
  GUI: separate operation masks
  GUI: add missing FDS preset
  GUI: prepare for separate operation mask
  GUI: "none" instrument should not remove inscolumn
  GUI: fix ins 0 being inserted on blank song
  GUI: add a "none" option to instrument list
  update to-do list
  GUI: add "absorb" instrument input mode
  use good default instrument when adding ins
  better default instrument for OPL/OPLL
  GUI: fix selection being visible in dummy row area
  Further fix looped sample preview
  GUI: fix about screen in power saving mode
  VRC6: saw volume 63
  GUI: add an effect list window
  ...

# Conflicts:
#	src/engine/platform/amiga.cpp
#	src/engine/vgmOps.cpp
#	src/gui/dataList.cpp
#	src/gui/guiConst.cpp
#	src/gui/insEdit.cpp
#	src/gui/sampleEdit.cpp
This commit is contained in:
cam900 2022-04-26 12:04:23 +09:00
commit 49a41ff862
116 changed files with 2535 additions and 993 deletions

View file

@ -187,6 +187,8 @@ jobs:
export USE_WAE=ON
export CMAKE_EXTRA_ARGS=()
if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then
# 1. Go to hell
export USE_WAE=OFF
CMAKE_EXTRA_ARGS+=('-DCMAKE_GENERATOR_PLATFORM=${{ steps.windows-identify.outputs.msvc-target }}')
# Force static linking

View file

@ -401,6 +401,7 @@ src/gui/debugWindow.cpp
src/gui/doAction.cpp
src/gui/editing.cpp
src/gui/editControls.cpp
src/gui/effectList.cpp
src/gui/insEdit.cpp
src/gui/log.cpp
src/gui/mixer.cpp

View file

@ -11,6 +11,8 @@ this is a multi-system chiptune tracker.
check out the [Releases](https://github.com/tildearrow/furnace/releases) page. available for Windows, macOS and Linux (AppImage).
[see here](https://nightly.link/tildearrow/furnace/workflows/build/master) for unstable developer builds.
## features
- supports the following systems:
@ -64,6 +66,8 @@ some people have provided packages for Unix/Unix-like distributions. here's a li
[![Build furnace](https://github.com/tildearrow/furnace/actions/workflows/build.yml/badge.svg)](https://github.com/tildearrow/furnace/actions/workflows/build.yml)
if you can't download these artifacts (because GitHub requires you to be logged in), [go here](https://nightly.link/tildearrow/furnace/workflows/build/master) instead.
**NOTE: do not download the project's source as a .zip or .tar.gz as these do not include the project's submodules which are necessary to proceed with building. please instead use Git as shown below.**
## dependencies

View file

@ -9,7 +9,10 @@
# to-do for 0.6pre1
- panning macro
- QSound?
- pitch macro
- relative mode
- test
- piano/input pad
- note input via piano
- input pad
@ -26,21 +29,17 @@
- ability to customize startup system
- store system presets in new file
- Game Boy envelope macro/sequence
- Game Boy envelope view
- option to display chip names instead of "multi-system" on title bar
- rewrite the system name detection function anyway
- add nightly.link
- scroll instrument/wave/sample list when selecting item
- "absorb" mode for instrument input - when this happens, current instrument is set to the input value
- unified data view
- separate "transpose note" and "transpose value" - see next point
- volume commands should work on Game Boy
- macro editor menu
- refactor sysDef.cpp
- add another FM editor layout
- try to find out why does VSlider not accept keyboard input
- finish lock layout
- note input latch! and separate edit masks
- if macros have release, note off should release them
- add "don't scroll on cursor movement" option
- add ability to select entire row when clicking on row number
@ -51,3 +50,4 @@
- settings: OK/Cancel buttons should be always visible
- Apply button in settings
- better FM chip names (number and codename)
- find and replace

View file

@ -274,6 +274,19 @@ size | description
# instrument
notes:
- the entire instrument is stored, regardless of instrument type.
- the macro range varies depending on the instrument type.
- "macro open" indicates whether the macro is collapsed or not in the instrument editor.
- FM operator order is:
- 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op
- 1/2/?/? (? = unused) for OPL 2-op and OPLL
- meaning of extended macros varies depending on instrument type.
- meaning of panning macros varies depending on instrument type:
- for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right)
- otherwise both left and right panning macros are used
```
size | description
-----|------------------------------------
@ -282,17 +295,39 @@ size | description
2 | format version (see header)
1 | instrument type
| - 0: standard
| - 1: FM
| - 1: FM (OPM/OPN)
| - 2: Game Boy
| - 3: C64
| - 4: Amiga/sample
| - 5: PC Engine
| - 6: AY-3-8910
| - 7: AY8930
| - 8: TIA
| - 9: SAA1099
| - 10: VIC
| - 11: PET
| - 12: VRC6
| - 13: OPLL
| - 14: OPL
| - 15: FDS
| - 16: Virtual Boy
| - 17: Namco 163
| - 18: SCC
| - 19: OPZ
| - 20: POKEY
| - 21: PC Speaker
| - 22: WonderSwan
| - 23: Lynx
| - 24: VERA
| - 25: X1-010
| - 26: VRC6 (saw)
1 | reserved
STR | instrument name
--- | **FM instrument data**
1 | alg
1 | alg (SUS on OPLL)
1 | feedback
1 | fms
1 | ams
1 | fms (DC on OPLL)
1 | ams (DM on OPLL)
1 | operator count
| - this is either 2 or 4, and is ignored on non-OPL systems.
| - always read 4 ops regardless of this value.
@ -314,10 +349,12 @@ size | description
1 | dt
1 | d2r
1 | ssgEnv
1 | dam
1 | dvb
1 | egt
1 | ksl
| - bit 4: on (EG-S on OPLL)
| - bit 0-3: envelope type
1 | dam (for YMU759 compat; REV on OPZ)
1 | dvb (for YMU759 compat; FINE on OPZ)
1 | egt (for YMU759 compat; FixedFreq on OPZ)
1 | ksl (EGShift on OPZ)
1 | sus
1 | vib
1 | ws
@ -690,15 +727,18 @@ size | description
| - 10: A#
| - 11: B
| - 12: C (of next octave)
| - this is actually a leftover of the .dmf format.
| - 100: note off
| - 100: note release
| - 100: macro release
| - octave
| - this is an signed char stored in a short.
| - therefore octave value 255 is actually octave -1.
| - yep, another leftover of the .dmf format...
| - instrument
| - volume
| - effect and effect data...
| - effect and effect data (× effect columns)
| - for note/octave, if both values are 0 then it means empty.
| - for instrument, volume, effect and effect data, a value of -1 means empty.
STR | pattern name (>=51)
```

View file

@ -146,6 +146,15 @@ enum DivDispatchCmds {
DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,
DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,
DIV_CMD_ES5506_FILTER_MODE,
DIV_CMD_ES5506_FILTER_K1,
DIV_CMD_ES5506_FILTER_K2,
DIV_CMD_ES5506_ENVELOPE_COUNT,
DIV_CMD_ES5506_ENVELOPE_LVRAMP,
DIV_CMD_ES5506_ENVELOPE_RVRAMP,
DIV_CMD_ES5506_ENVELOPE_K1RAMP,
DIV_CMD_ES5506_ENVELOPE_K2RAMP,
DIV_ALWAYS_SET_VOLUME,
DIV_CMD_MAX
@ -245,8 +254,9 @@ class DivDispatch {
/**
* ticks this dispatch.
* @param sysTick whether the engine has ticked (if not then this may be a sub-tick used in low-latency mode).
*/
virtual void tick();
virtual void tick(bool sysTick=true);
/**
* get the state of a channel.

View file

@ -41,7 +41,7 @@ void process(void* u, float** in, float** out, int inChans, int outChans, unsign
((DivEngine*)u)->nextBuf(in,out,inChans,outChans,size);
}
const char* DivEngine::getEffectDesc(unsigned char effect, int chan) {
const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNull) {
switch (effect) {
case 0x00:
return "00xy: Arpeggio";
@ -116,14 +116,13 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan) {
default:
if ((effect&0xf0)==0x90) {
return "9xxx: Set sample offset*256";
}
else if (chan>=0 && chan<chans) {
} else if (chan>=0 && chan<chans) {
const char* ret=disCont[dispatchOfChan[chan]].dispatch->getEffectName(effect);
if (ret!=NULL) return ret;
}
break;
}
return "Invalid effect";
return notNull?"Invalid effect":NULL;
}
void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
@ -397,7 +396,7 @@ void DivEngine::runExportThread() {
if (getChannelType(i)==5) {
i++;
while (true) {
if (++i>=chans) break;
if (i>=chans) break;
if (getChannelType(i)!=5) break;
}
i--;
@ -635,10 +634,10 @@ void DivEngine::renderSamples() {
break;
}
if (memPos+length>=16777216) {
memcpy(es5506Mem+memPos,s->data16,16777216-memPos);
memcpy(es5506Mem+(memPos/sizeof(short)),s->data16,16777216-memPos);
logW("out of ES5506 memory for sample %d!",i);
} else {
memcpy(es5506Mem+memPos,s->data16,length);
memcpy(es5506Mem+(memPos/sizeof(short)),s->data16,length);
}
s->offES5506=memPos;
memPos+=length;
@ -770,8 +769,20 @@ String DivEngine::getWarnings() {
return warnings;
}
DivInstrument* DivEngine::getIns(int index) {
if (index<0 || index>=song.insLen) return &song.nullIns;
DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) {
if (index<0 || index>=song.insLen) {
switch (fallbackType) {
case DIV_INS_OPLL:
return &song.nullInsOPLL;
break;
case DIV_INS_OPL:
return &song.nullInsOPL;
break;
default:
break;
}
return &song.nullIns;
}
return song.ins[index];
}
@ -884,6 +895,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
}
if (!preserveDrift) {
ticks=1;
subticks=1;
}
skipping=false;
cmdStream.clear();
@ -1338,8 +1350,19 @@ int DivEngine::addInstrument(int refChan) {
BUSY_BEGIN;
DivInstrument* ins=new DivInstrument;
int insCount=(int)song.ins.size();
DivInstrumentType prefType=getPreferInsType(refChan);
switch (prefType) {
case DIV_INS_OPLL:
*ins=song.nullInsOPLL;
break;
case DIV_INS_OPL:
*ins=song.nullInsOPL;
break;
default:
break;
}
ins->name=fmt::sprintf("Instrument %d",insCount);
ins->type=getPreferInsType(refChan);
ins->type=prefType;
saveLock.lock();
song.ins.push_back(ins);
song.insLen=insCount+1;
@ -2014,7 +2037,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
}
do {
if ((ins==-1 || getPreferInsType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) {
if ((ins<0 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) {
chan[finalChan].midiNote=note;
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
break;
@ -2040,6 +2063,20 @@ void DivEngine::autoNoteOff(int ch, int note, int vol) {
}
}
void DivEngine::autoNoteOffAll() {
if (!playing) {
reset();
freelance=true;
playing=true;
}
for (int i=0; i<chans; i++) {
if (chan[i].midiNote!=-1) {
pendingNotes.push(DivNoteEvent(i,-1,-1,-1,false));
chan[i].midiNote=-1;
}
}
}
void DivEngine::setOrder(unsigned char order) {
BUSY_BEGIN_SOFT;
curOrder=order;
@ -2281,6 +2318,8 @@ bool DivEngine::initAudioBackend() {
if (metroVol<0.0f) metroVol=0.0f;
if (metroVol>2.0f) metroVol=2.0f;
if (lowLatency) logI("using low latency mode.");
switch (audioEngine) {
case DIV_AUDIO_JACK:
#ifndef HAVE_JACK

View file

@ -19,6 +19,7 @@
#ifndef _ENGINE_H
#define _ENGINE_H
#include "instrument.h"
#include "song.h"
#include "dispatch.h"
#include "dataErrors.h"
@ -42,8 +43,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev83"
#define DIV_ENGINE_VERSION 83
#define DIV_VERSION "dev84"
#define DIV_ENGINE_VERSION 84
// for imports
#define DIV_VERSION_MOD 0xff01
@ -273,7 +274,7 @@ class DivEngine {
void nextRow();
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond);
// returns true if end of song.
bool nextTick(bool noAccum=false);
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
bool perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal);
void recalcChans();
@ -289,6 +290,7 @@ class DivEngine {
void loadVGI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadS3I(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
@ -310,7 +312,7 @@ class DivEngine {
void runExportThread();
void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size);
DivInstrument* getIns(int index);
DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM);
DivWavetable* getWave(int index);
DivSample* getSample(int index);
// start fresh
@ -403,7 +405,7 @@ class DivEngine {
int getTotalChannelCount();
// get effect description
const char* getEffectDesc(unsigned char effect, int chan);
const char* getEffectDesc(unsigned char effect, int chan, bool notNull=false);
// get channel type
// - 0: FM
@ -573,6 +575,7 @@ class DivEngine {
void autoNoteOn(int chan, int ins, int note, int vol=-1);
void autoNoteOff(int chan, int note, int vol=-1);
void autoNoteOffAll();
// go to order
void setOrder(unsigned char order);

View file

@ -990,6 +990,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
ds.ignoreDACModeOutsideIntendedChannel=true;
ds.e1e2AlsoTakePriority=false;
}
if (ds.version<84) {
ds.newSegaPCM=false;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -1334,7 +1337,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
reader.readC();
reader.readC();
}
for (int i=0; i<23; i++) {
if (ds.version>=84) {
ds.newSegaPCM=reader.readC();
} else {
reader.readC();
}
for (int i=0; i<22; i++) {
reader.readC();
}
}
@ -2287,7 +2295,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.sharedExtStat);
w->writeC(song.ignoreDACModeOutsideIntendedChannel);
w->writeC(song.e1e2AlsoTakePriority);
for (int i=0; i<23; i++) {
w->writeC(song.newSegaPCM);
for (int i=0; i<22; i++) {
w->writeC(0);
}

View file

@ -30,10 +30,66 @@ enum DivInsFormats {
DIV_INSFORMAT_BTI,
DIV_INSFORMAT_S3I,
DIV_INSFORMAT_SBI,
DIV_INSFORMAT_BNK,
DIV_INSFORMAT_OPM,
DIV_INSFORMAT_FF,
};
// Patch data structures
// SBI and some other OPL containers
struct sbi_t {
uint8_t Mcharacteristics,
Ccharacteristics,
Mscaling_output,
Cscaling_output,
Meg_AD,
Ceg_AD,
Meg_SR,
Ceg_SR,
Mwave,
Cwave,
FeedConnect;
};
// Adlib Visual Composer BNK
struct bnkop_t {
uint8_t ksl,
multiple,
feedback, // op1 only
attack,
sustain,
eg,
decay,
releaseRate,
totalLevel,
am,
vib,
ksr,
con; // op1 only
};
struct bnktimbre_t {
uint8_t mode,
percVoice;
bnkop_t op[2];
uint8_t wave0,
wave1;
};
auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) {
sbi.Mcharacteristics = reader.readC();
sbi.Ccharacteristics = reader.readC();
sbi.Mscaling_output = reader.readC();
sbi.Cscaling_output = reader.readC();
sbi.Meg_AD = reader.readC();
sbi.Ceg_AD = reader.readC();
sbi.Meg_SR = reader.readC();
sbi.Ceg_SR = reader.readC();
sbi.Mwave = reader.readC();
sbi.Cwave = reader.readC();
sbi.FeedConnect = reader.readC();
};
void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
DivInstrument* ins=new DivInstrument;
// this is a ridiculous mess
@ -45,7 +101,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
logD(".dmp version %d",version);
} catch (EndOfFileException& e) {
lastError="premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
return;
}
@ -104,7 +160,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
}
} catch (EndOfFileException& e) {
lastError="premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
return;
}
@ -296,7 +352,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
}
} catch (EndOfFileException& e) {
lastError="premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
return;
}
@ -331,7 +387,7 @@ void DivEngine::loadTFI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
}
} catch (EndOfFileException& e) {
lastError="premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
return;
}
@ -373,7 +429,7 @@ void DivEngine::loadVGI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
}
} catch (EndOfFileException& e) {
lastError="premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
return;
}
@ -390,71 +446,70 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector<DivInstrument*>& ret, St
if (s3i_type >= 2) {
ins->type = DIV_INS_OPL;
if (s3i_type > 2 && s3i_type <= 7) {
ins->fm.opllPreset = (uint8_t)(1<<4); // Flag as Drum preset.
}
// skip internal filename - we'll use the long name description
reader.seek(12, SEEK_CUR);
// skip reserved bytes
reader.seek(3, SEEK_CUR);
// 12-byte opl value
uint8_t s3i_Mcharacteristics = reader.readC();
uint8_t s3i_Ccharacteristics = reader.readC();
uint8_t s3i_Mscaling_output = reader.readC();
uint8_t s3i_Cscaling_output = reader.readC();
uint8_t s3i_Meg_AD = reader.readC();
uint8_t s3i_Ceg_AD = reader.readC();
uint8_t s3i_Meg_SR = reader.readC();
uint8_t s3i_Ceg_SR = reader.readC();
uint8_t s3i_Mwave = reader.readC();
uint8_t s3i_Cwave = reader.readC();
uint8_t s3i_FeedConnect = reader.readC();
// 12-byte opl value - identical to SBI format
sbi_t s3i;
readSbiOpData(s3i, reader);
DivInstrumentFM::Operator& opM = ins->fm.op[0];
DivInstrumentFM::Operator& opC = ins->fm.op[1];
ins->fm.ops = 2;
opM.mult = s3i_Mcharacteristics & 0xF;
opM.ksr = ((s3i_Mcharacteristics >> 4) & 0x1);
opM.sus = ((s3i_Mcharacteristics >> 5) & 0x1);
opM.vib = ((s3i_Mcharacteristics >> 6) & 0x1);
opM.am = ((s3i_Mcharacteristics >> 7) & 0x1);
opM.tl = s3i_Mscaling_output & 0x3F;
opM.ksl = ((s3i_Mscaling_output >> 6) & 0x3);
opM.ar = ((s3i_Meg_AD >> 4) & 0xF);
opM.dr = (s3i_Meg_AD & 0xF);
opM.rr = (s3i_Meg_SR & 0xF);
opM.sl = ((s3i_Meg_SR >> 4) & 0xF);
opM.ws = s3i_Mwave;
opM.mult = s3i.Mcharacteristics & 0xF;
opM.ksr = ((s3i.Mcharacteristics >> 4) & 0x1);
opM.sus = ((s3i.Mcharacteristics >> 5) & 0x1);
opM.vib = ((s3i.Mcharacteristics >> 6) & 0x1);
opM.am = ((s3i.Mcharacteristics >> 7) & 0x1);
opM.tl = s3i.Mscaling_output & 0x3F;
opM.ksl = ((s3i.Mscaling_output >> 6) & 0x3);
opM.ar = ((s3i.Meg_AD >> 4) & 0xF);
opM.dr = (s3i.Meg_AD & 0xF);
opM.rr = (s3i.Meg_SR & 0xF);
opM.sl = ((s3i.Meg_SR >> 4) & 0xF);
opM.ws = s3i.Mwave;
ins->fm.alg = (s3i_FeedConnect & 0x1);
ins->fm.fb = ((s3i_FeedConnect >> 1) & 0x7);
ins->fm.alg = (s3i.FeedConnect & 0x1);
ins->fm.fb = ((s3i.FeedConnect >> 1) & 0x7);
opC.mult = s3i_Ccharacteristics & 0xF;
opC.ksr = ((s3i_Ccharacteristics >> 4) & 0x1);
opC.sus = ((s3i_Ccharacteristics >> 5) & 0x1);
opC.vib = ((s3i_Ccharacteristics >> 6) & 0x1);
opC.am = ((s3i_Ccharacteristics >> 7) & 0x1);
opC.tl = s3i_Cscaling_output & 0x3F;
opC.ksl = ((s3i_Cscaling_output >> 6) & 0x3);
opC.ar = ((s3i_Ceg_AD >> 4) & 0xF);
opC.dr = (s3i_Ceg_AD & 0xF);
opC.rr = (s3i_Ceg_SR & 0xF);
opC.sl = ((s3i_Ceg_SR >> 4) & 0xF);
opC.ws = s3i_Cwave;
opC.mult = s3i.Ccharacteristics & 0xF;
opC.ksr = ((s3i.Ccharacteristics >> 4) & 0x1);
opC.sus = ((s3i.Ccharacteristics >> 5) & 0x1);
opC.vib = ((s3i.Ccharacteristics >> 6) & 0x1);
opC.am = ((s3i.Ccharacteristics >> 7) & 0x1);
opC.tl = s3i.Cscaling_output & 0x3F;
opC.ksl = ((s3i.Cscaling_output >> 6) & 0x3);
opC.ar = ((s3i.Ceg_AD >> 4) & 0xF);
opC.dr = (s3i.Ceg_AD & 0xF);
opC.rr = (s3i.Ceg_SR & 0xF);
opC.sl = ((s3i.Ceg_SR >> 4) & 0xF);
opC.ws = s3i.Cwave;
// Skip more stuff we don't need
reader.seek(21, SEEK_CUR);
} else {
lastError = "S3I PCM samples currently not supported.";
logE("S3I PCM samples currently not supported.");
}
ins->name = reader.readString(28);
ins->name = (ins->name.length() == 0) ? stripPath : ins->name;
int s3i_signature = reader.readI();
if (s3i_signature != 0x49524353) {
addWarning("S3I signature invalid.");
logW("S3I signature invalid.");
};
} catch (EndOfFileException& e) {
lastError = "premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
return;
}
@ -470,77 +525,69 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
int sbi_header = reader.readI();
// SBI header determines format
bool is_2op = (sbi_header == 0x1A494253); // SBI\x1A
bool is_2op = (sbi_header == 0x1A494253 || sbi_header == 0x1A504F32); // SBI\x1A or 2OP\x1A
bool is_4op = (sbi_header == 0x1A504F34); // 4OP\x1A
bool is_6op = (sbi_header == 0x1A504F36); // 6OP\x1A - Freq Monster 801-specific
// 32-byte null terminated instrument name
ins->name = reader.readString(32);
String patchName = reader.readString(32);
patchName = (patchName.length() == 0) ? stripPath : patchName;
// 2op SBI
uint8_t sbi_Mcharacteristics = reader.readC();
uint8_t sbi_Ccharacteristics = reader.readC();
uint8_t sbi_Mscaling_output = reader.readC();
uint8_t sbi_Cscaling_output = reader.readC();
uint8_t sbi_Meg_AD = reader.readC();
uint8_t sbi_Ceg_AD = reader.readC();
uint8_t sbi_Meg_SR = reader.readC();
uint8_t sbi_Ceg_SR = reader.readC();
uint8_t sbi_Mwave = reader.readC();
uint8_t sbi_Cwave = reader.readC();
uint8_t sbi_FeedConnect = reader.readC();
auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) {
opM.mult = sbi.Mcharacteristics & 0xF;
opM.ksr = ((sbi.Mcharacteristics >> 4) & 0x1);
opM.sus = ((sbi.Mcharacteristics >> 5) & 0x1);
opM.vib = ((sbi.Mcharacteristics >> 6) & 0x1);
opM.am = ((sbi.Mcharacteristics >> 7) & 0x1);
opM.tl = sbi.Mscaling_output & 0x3F;
opM.ksl = ((sbi.Mscaling_output >> 6) & 0x3);
opM.ar = ((sbi.Meg_AD >> 4) & 0xF);
opM.dr = (sbi.Meg_AD & 0xF);
opM.rr = (sbi.Meg_SR & 0xF);
opM.sl = ((sbi.Meg_SR >> 4) & 0xF);
opM.ws = sbi.Mwave;
// 4op SBI
uint8_t sbi_M4characteristics;
uint8_t sbi_C4characteristics;
uint8_t sbi_M4scaling_output;
uint8_t sbi_C4scaling_output;
uint8_t sbi_M4eg_AD;
uint8_t sbi_C4eg_AD;
uint8_t sbi_M4eg_SR;
uint8_t sbi_C4eg_SR;
uint8_t sbi_M4wave;
uint8_t sbi_C4wave;
uint8_t sbi_4opConnect;
opC.mult = sbi.Ccharacteristics & 0xF;
opC.ksr = ((sbi.Ccharacteristics >> 4) & 0x1);
opC.sus = ((sbi.Ccharacteristics >> 5) & 0x1);
opC.vib = ((sbi.Ccharacteristics >> 6) & 0x1);
opC.am = ((sbi.Ccharacteristics >> 7) & 0x1);
opC.tl = sbi.Cscaling_output & 0x3F;
opC.ksl = ((sbi.Cscaling_output >> 6) & 0x3);
opC.ar = ((sbi.Ceg_AD >> 4) & 0xF);
opC.dr = (sbi.Ceg_AD & 0xF);
opC.rr = (sbi.Ceg_SR & 0xF);
opC.sl = ((sbi.Ceg_SR >> 4) & 0xF);
opC.ws = sbi.Cwave;
};
sbi_t sbi_op12; // 2op (+6op portion)
sbi_t sbi_op34; // 4op
readSbiOpData(sbi_op12, reader);
if (is_2op) {
DivInstrumentFM::Operator& opM = ins->fm.op[0];
DivInstrumentFM::Operator& opC = ins->fm.op[1];
ins->fm.ops = 2;
opM.mult = sbi_Mcharacteristics & 0xF;
opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1);
opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1);
opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1);
opM.am = ((sbi_Mcharacteristics >> 7) & 0x1);
opM.tl = sbi_Mscaling_output & 0x3F;
opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3);
opM.ar = ((sbi_Meg_AD >> 4) & 0xF);
opM.dr = (sbi_Meg_AD & 0xF);
opM.rr = (sbi_Meg_SR & 0xF);
opM.sl = ((sbi_Meg_SR >> 4) & 0xF);
opM.ws = sbi_Mwave;
ins->name = patchName;
writeOp(sbi_op12, opM, opC);
ins->fm.alg = (sbi_op12.FeedConnect & 0x1);
ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7);
ins->fm.alg = (sbi_FeedConnect & 0x1);
ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7);
opC.mult = sbi_Ccharacteristics & 0xF;
opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1);
opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1);
opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1);
opC.am = ((sbi_Ccharacteristics >> 7) & 0x1);
opC.tl = sbi_Cscaling_output & 0x3F;
opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3);
opC.ar = ((sbi_Ceg_AD >> 4) & 0xF);
opC.dr = (sbi_Ceg_AD & 0xF);
opC.rr = (sbi_Ceg_SR & 0xF);
opC.sl = ((sbi_Ceg_SR >> 4) & 0xF);
opC.ws = sbi_Cwave;
// SBTimbre extensions
uint8_t perc_voc = reader.readC();
if (perc_voc >= 6) {
ins->fm.opllPreset = (uint8_t)(1 << 4);
}
// Ignore rest of file - rest is 'reserved padding'.
reader.seek(0, SEEK_END);
}
reader.seek(4, SEEK_CUR);
ret.push_back(ins);
if (is_4op || is_6op) {
} else if (is_4op || is_6op) {
readSbiOpData(sbi_op34, reader);
// Operator placement is different so need to place in correct registers.
// Note: 6op is an unofficial extension of 4op SBIs by Darron Broad (Freq Monster 801).
// We'll only use the 4op portion here for pure OPL3.
@ -549,87 +596,144 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector<DivInstrument*>& ret, St
DivInstrumentFM::Operator& opM4 = ins->fm.op[1];
DivInstrumentFM::Operator& opC4 = ins->fm.op[3];
ins->fm.ops = 4;
ins->name = patchName;
ins->fm.alg = (sbi_op12.FeedConnect & 0x1) | ((sbi_op34.FeedConnect & 0x1) << 1);
ins->fm.fb = ((sbi_op34.FeedConnect >> 1) & 0x7);
writeOp(sbi_op12, opM, opC);
writeOp(sbi_op34, opM4, opC4);
sbi_M4characteristics = reader.readC();
sbi_C4characteristics = reader.readC();
sbi_M4scaling_output = reader.readC();
sbi_C4scaling_output = reader.readC();
sbi_M4eg_AD = reader.readC();
sbi_C4eg_AD = reader.readC();
sbi_M4eg_SR = reader.readC();
sbi_C4eg_SR = reader.readC();
sbi_M4wave = reader.readC();
sbi_C4wave = reader.readC();
sbi_4opConnect = reader.readC();
ins->fm.alg = (sbi_FeedConnect & 0x1) | ((sbi_4opConnect & 0x1) << 1);
ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7);
if (is_6op) {
// Freq Monster 801 6op SBIs use a 4+2op layout
// Save the 4op portion before reading the 2op part
ins->name = fmt::format("{0} (4op)", ins->name);
ret.push_back(ins);
opM.mult = sbi_Mcharacteristics & 0xF;
opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1);
opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1);
opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1);
opM.am = ((sbi_Mcharacteristics >> 7) & 0x1);
opM.tl = sbi_Mscaling_output & 0x3F;
opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3);
opM.ar = ((sbi_Meg_AD >> 4) & 0xF);
opM.dr = (sbi_Meg_AD & 0xF);
opM.rr = (sbi_Meg_SR & 0xF);
opM.sl = ((sbi_Meg_SR >> 4) & 0xF);
opM.ws = sbi_Mwave;
readSbiOpData(sbi_op12, reader);
opC.mult = sbi_Ccharacteristics & 0xF;
opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1);
opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1);
opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1);
opC.am = ((sbi_Ccharacteristics >> 7) & 0x1);
opC.tl = sbi_Cscaling_output & 0x3F;
opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3);
opC.ar = ((sbi_Ceg_AD >> 4) & 0xF);
opC.dr = (sbi_Ceg_AD & 0xF);
opC.rr = (sbi_Ceg_SR & 0xF);
opC.sl = ((sbi_Ceg_SR >> 4) & 0xF);
opC.ws = sbi_Cwave;
opM4.mult = sbi_M4characteristics & 0xF;
opM4.ksr = ((sbi_M4characteristics >> 4) & 0x1);
opM4.sus = ((sbi_M4characteristics >> 5) & 0x1);
opM4.vib = ((sbi_M4characteristics >> 6) & 0x1);
opM4.am = ((sbi_M4characteristics >> 7) & 0x1);
opM4.tl = sbi_M4scaling_output & 0x3F;
opM4.ksl = ((sbi_M4scaling_output >> 6) & 0x3);
opM4.ar = ((sbi_M4eg_AD >> 4) & 0xF);
opM4.dr = (sbi_M4eg_AD & 0xF);
opM4.rr = (sbi_M4eg_SR & 0xF);
opM4.sl = ((sbi_M4eg_SR >> 4) & 0xF);
opM4.ws = sbi_M4wave;
opC4.mult = sbi_C4characteristics & 0xF;
opC4.ksr = ((sbi_C4characteristics >> 4) & 0x1);
opC4.sus = ((sbi_C4characteristics >> 5) & 0x1);
opC4.vib = ((sbi_C4characteristics >> 6) & 0x1);
opC4.am = ((sbi_C4characteristics >> 7) & 0x1);
opC4.tl = sbi_C4scaling_output & 0x3F;
opC4.ksl = ((sbi_C4scaling_output >> 6) & 0x3);
opC4.ar = ((sbi_C4eg_AD >> 4) & 0xF);
opC4.dr = (sbi_C4eg_AD & 0xF);
opC4.rr = (sbi_C4eg_SR & 0xF);
opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF);
opC4.ws = sbi_C4wave;
ins = new DivInstrument;
DivInstrumentFM::Operator& opM6 = ins->fm.op[0];
DivInstrumentFM::Operator& opC6 = ins->fm.op[1];
ins->type = DIV_INS_OPL;
ins->fm.ops = 2;
ins->name = fmt::format("{0} (2op)", patchName);
writeOp(sbi_op12, opM6, opC6);
ins->fm.alg = (sbi_op12.FeedConnect & 0x1);
ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7);
}
// Ignore rest of file once we've read in all we need.
// Note: Freq Monster 801 adds a ton of other additional fields irrelevant to chip registers.
// If instrument transpose is ever supported, we can read it in maybe?
reader.seek(0, SEEK_END);
ret.push_back(ins);
}
} catch (EndOfFileException& e) {
lastError = "premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
return;
}
}
void DivEngine::loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
std::vector<DivInstrument*> insList;
std::vector<String*> instNames;
reader.seek(0, SEEK_SET);
// First distinguish between GEMS BNK and Adlib BNK
uint64_t header = reader.readL();
bool is_adlib = ((header>>8) == 0x2d42494c444100L);
bool is_failed = false;
int readCount = 0;
if (is_adlib) {
try {
reader.seek(0x0c, SEEK_SET);
uint32_t name_offset = reader.readI();
reader.seek(0x10, SEEK_SET);
uint32_t data_offset = reader.readI();
// Seek to BNK patch names
reader.seek(name_offset, SEEK_SET);
while (reader.tell() < data_offset) {
reader.seek(3, SEEK_CUR);
instNames.push_back(new String(reader.readString(9)));
++readCount;
}
// Seek to BNK data
reader.seek(data_offset, SEEK_SET);
// Read until EOF
for (int i = 0; i < readCount; ++i) {
bnktimbre_t timbre;
insList.push_back(new DivInstrument);
auto& ins = insList[i];
ins->type = DIV_INS_OPL;
timbre.mode = reader.readC();
timbre.percVoice = reader.readC();
if (timbre.mode == 1) {
ins->fm.opllPreset = (uint8_t)(1<<4);
}
ins->fm.op[0].ksl = reader.readC();
ins->fm.op[0].mult = reader.readC();
ins->fm.fb = reader.readC();
ins->fm.op[0].ar = reader.readC();
ins->fm.op[0].sl = reader.readC();
ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0;
ins->fm.op[0].dr = reader.readC();
ins->fm.op[0].rr = reader.readC();
ins->fm.op[0].tl = reader.readC();
ins->fm.op[0].am = reader.readC();
ins->fm.op[0].vib = reader.readC();
ins->fm.op[0].ksr = reader.readC();
ins->fm.alg = (reader.readC() == 0) ? 1 : 0;
ins->fm.op[1].ksl = reader.readC();
ins->fm.op[1].mult = reader.readC();
reader.readC(); // skip
ins->fm.op[1].ar = reader.readC();
ins->fm.op[1].sl = reader.readC();
ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0;
ins->fm.op[1].dr = reader.readC();
ins->fm.op[1].rr = reader.readC();
ins->fm.op[1].tl = reader.readC();
ins->fm.op[1].am = reader.readC();
ins->fm.op[1].vib = reader.readC();
ins->fm.op[1].ksr = reader.readC();
reader.readC(); // skip
ins->fm.op[0].ws = reader.readC();
ins->fm.op[1].ws = reader.readC();
ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::format("{0}[{1}]", stripPath, i);
}
reader.seek(0, SEEK_END);
} catch (EndOfFileException& e) {
lastError = "premature end of file";
logE("premature end of file");
for (int i = readCount; i >= 0; --i) {
delete insList[i];
}
is_failed = true;
}
} else {
// assume GEMS BNK for now.
lastError = "GEMS BNK currently not supported.";
logE("GEMS BNK currently not supported.");
}
ret.push_back(ins);
if (!is_failed) {
for (int i = 0; i < readCount; ++i) {
ret.push_back(insList[i]);
}
}
for (auto& name : instNames) {
delete name;
}
}
void DivEngine::loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath) {
@ -693,7 +797,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, Str
}
} catch (EndOfFileException& e) {
lastError = "premature end of file";
logE("premature end of file!\n");
logE("premature end of file");
for (int i = readCount; i >= 0; --i) {
delete insList[i];
}
@ -714,7 +818,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, St
} catch (EndOfFileException& e) {
lastError="premature end of file";
logE("premature end of file!");
logE("premature end of file");
return;
}
}
@ -812,7 +916,7 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
}
} catch (EndOfFileException& e) {
lastError="premature end of file";
logE("premature end of file!");
logE("premature end of file");
delete ins;
delete[] buf;
return ret;
@ -843,6 +947,8 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
format=DIV_INSFORMAT_S3I;
} else if (extS==String(".sbi")) {
format=DIV_INSFORMAT_SBI;
} else if (extS==String(".bnk")) {
format=DIV_INSFORMAT_BNK;
} else if (extS==String(".opm")) {
format=DIV_INSFORMAT_OPM;
} else if (extS==String(".ff")) {
@ -873,6 +979,9 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
case DIV_INSFORMAT_SBI:
loadSBI(reader,ret,stripPath);
break;
case DIV_INSFORMAT_BNK:
loadBNK(reader, ret, stripPath);
break;
case DIV_INSFORMAT_FF:
loadFF(reader,ret,stripPath);
break;

View file

@ -486,6 +486,27 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeC(ws.param2);
w->writeC(ws.param3);
w->writeC(ws.param4);
// other macro modes
w->writeC(std.volMacro.mode);
w->writeC(std.dutyMacro.mode);
w->writeC(std.waveMacro.mode);
w->writeC(std.pitchMacro.mode);
w->writeC(std.ex1Macro.mode);
w->writeC(std.ex2Macro.mode);
w->writeC(std.ex3Macro.mode);
w->writeC(std.algMacro.mode);
w->writeC(std.fbMacro.mode);
w->writeC(std.fmsMacro.mode);
w->writeC(std.amsMacro.mode);
w->writeC(std.panLMacro.mode);
w->writeC(std.panRMacro.mode);
w->writeC(std.phaseResetMacro.mode);
w->writeC(std.ex4Macro.mode);
w->writeC(std.ex5Macro.mode);
w->writeC(std.ex6Macro.mode);
w->writeC(std.ex7Macro.mode);
w->writeC(std.ex8Macro.mode);
}
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
@ -962,6 +983,29 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
ws.param4=reader.readC();
}
// other macro modes
if (version>=84) {
std.volMacro.mode=reader.readC();
std.dutyMacro.mode=reader.readC();
std.waveMacro.mode=reader.readC();
std.pitchMacro.mode=reader.readC();
std.ex1Macro.mode=reader.readC();
std.ex2Macro.mode=reader.readC();
std.ex3Macro.mode=reader.readC();
std.algMacro.mode=reader.readC();
std.fbMacro.mode=reader.readC();
std.fmsMacro.mode=reader.readC();
std.amsMacro.mode=reader.readC();
std.panLMacro.mode=reader.readC();
std.panRMacro.mode=reader.readC();
std.phaseResetMacro.mode=reader.readC();
std.ex4Macro.mode=reader.readC();
std.ex5Macro.mode=reader.readC();
std.ex6Macro.mode=reader.readC();
std.ex7Macro.mode=reader.readC();
std.ex8Macro.mode=reader.readC();
}
return DIV_DATA_SUCCESS;
}

View file

@ -302,6 +302,7 @@ struct DivInstrumentAmiga {
};
short initSample;
bool reversed;
bool useNoteMap;
bool useWave;
unsigned char waveLen;
@ -309,6 +310,7 @@ struct DivInstrumentAmiga {
DivInstrumentAmiga():
initSample(0),
reversed(false),
useNoteMap(false),
useWave(false),
waveLen(31) {}

View file

@ -21,14 +21,19 @@
#include "instrument.h"
#include "engine.h"
void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released) {
void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tick) {
if (!tick) {
had=false;
return;
}
if (finished) {
finished=false;
}
if (had!=has) {
if (actualHad!=has) {
finished=true;
}
had=has;
actualHad=has;
had=actualHad;
if (has) {
val=source.val[pos++];
if (source.rel>=0 && pos>source.rel && !released) {
@ -52,17 +57,18 @@ void DivMacroInt::next() {
if (ins==NULL) return;
// run macros
// TODO: potentially get rid of list to avoid allocations
if (--subTick<=0) {
subTick--;
for (size_t i=0; i<macroListLen; i++) {
if (macroList[i]!=NULL && macroSource[i]!=NULL) {
macroList[i]->doMacro(*macroSource[i],released,subTick==0);
}
}
if (subTick<=0) {
if (e==NULL) {
subTick=1;
} else {
subTick=e->tickMult;
}
for (size_t i=0; i<macroListLen; i++) {
if (macroList[i]!=NULL && macroSource[i]!=NULL) {
macroList[i]->doMacro(*macroSource[i],released);
}
}
}
}
@ -85,6 +91,7 @@ void DivMacroInt::init(DivInstrument* which) {
if (macroList[i]!=NULL) macroList[i]->init();
}
macroListLen=0;
subTick=1;
released=false;

View file

@ -27,17 +27,17 @@ class DivEngine;
struct DivMacroStruct {
int pos;
int val;
bool has, had, finished, will;
bool has, had, actualHad, finished, will;
unsigned int mode;
void doMacro(DivInstrumentMacro& source, bool released);
void doMacro(DivInstrumentMacro& source, bool released, bool tick);
void init() {
pos=mode=0;
has=had=will=false;
has=had=actualHad=will=false;
// TODO: test whether this breaks anything?
val=0;
}
void prepare(DivInstrumentMacro& source) {
has=had=will=true;
has=had=actualHad=will=true;
mode=source.mode;
}
DivMacroStruct():
@ -45,6 +45,7 @@ struct DivMacroStruct {
val(0),
has(false),
had(false),
actualHad(false),
finished(false),
will(false),
mode(0) {}
@ -128,7 +129,7 @@ class DivMacroInt {
e(NULL),
ins(NULL),
macroListLen(0),
subTick(0),
subTick(1),
released(false),
vol(),
arp(),

View file

@ -22,7 +22,7 @@
void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) {
}
void DivDispatch::tick() {
void DivDispatch::tick(bool sysTick) {
}
void* DivDispatch::getChanState(int chan) {

View file

@ -108,7 +108,9 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
} else {
DivSample* s=parent->getSample(chan[i].sample);
if (s->samples>0) {
writeAudDat(s->data8[chan[i].audPos++]);
if (chan[i].audPos<s->samples) {
writeAudDat(s->data8[chan[i].audPos++]);
}
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[i].audPos>=s->loopEnd) || (chan[i].audPos>=s->samples) || (chan[i].audPos>=131071)) {
if (s->isLoopable()) {
chan[i].audPos=s->loopStart;
@ -155,7 +157,7 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
}
}
void DivPlatformAmiga::tick() {
void DivPlatformAmiga::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -204,7 +206,7 @@ void DivPlatformAmiga::tick() {
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins);
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0;
@ -222,7 +224,7 @@ void DivPlatformAmiga::tick() {
int DivPlatformAmiga::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
double off=1.0;
if (ins->amiga.useWave) {
chan[c.chan].useWave=true;
@ -312,7 +314,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
break;
case DIV_CMD_NOTE_PORTA: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].sample=ins->amiga.initSample;
double off=1.0;
if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
@ -362,7 +364,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -88,7 +88,7 @@ class DivPlatformAmiga: public DivDispatch {
void* getChanState(int chan);
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -219,7 +219,7 @@ inline int hScale(int note) {
return ((note/12)<<4)+(noteMap[note%12]);
}
void DivPlatformArcade::tick() {
void DivPlatformArcade::tick(bool sysTick) {
for (int i=0; i<8; i++) {
chan[i].std.next();
@ -264,6 +264,20 @@ void DivPlatformArcade::tick() {
rWrite(0x1b,chan[i].std.wave.val&3);
}
if (chan[i].std.panL.had) {
chan[i].chVolL=(chan[i].std.panL.val&2)>>1;
chan[i].chVolR=chan[i].std.panL.val&1;
if (isMuted[i]) {
rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
} else {
rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|((chan[i].chVolL&1)<<6)|((chan[i].chVolR&1)<<7));
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -432,7 +446,7 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) {
int DivPlatformArcade::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;

View file

@ -107,7 +107,7 @@ class DivPlatformArcade: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyInsChange(int ins);
void setFlags(unsigned int flags);

View file

@ -172,7 +172,7 @@ void DivPlatformAY8910::updateOutSel(bool immediate) {
}
}
void DivPlatformAY8910::tick() {
void DivPlatformAY8910::tick(bool sysTick) {
// PSG
for (int i=0; i<3; i++) {
chan[i].std.next();
@ -215,6 +215,9 @@ void DivPlatformAY8910::tick() {
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
oldWrites[0x08+i]=-1;
@ -291,7 +294,7 @@ void DivPlatformAY8910::tick() {
int DivPlatformAY8910::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AY);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
@ -454,7 +457,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -90,7 +90,7 @@ class DivPlatformAY8910: public DivDispatch {
void flushWrites();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setFlags(unsigned int flags);
bool isStereo();

View file

@ -187,7 +187,7 @@ const unsigned char regMode[3]={
0x0d, 0x14, 0x15
};
void DivPlatformAY8930::tick() {
void DivPlatformAY8930::tick(bool sysTick) {
// PSG
for (int i=0; i<3; i++) {
chan[i].std.next();
@ -226,6 +226,9 @@ void DivPlatformAY8930::tick() {
rWrite(0x08+i,(chan[i].outVol&31)|((chan[i].psgMode&4)<<3));
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
oldWrites[0x08+i]=-1;
@ -315,7 +318,7 @@ void DivPlatformAY8930::tick() {
int DivPlatformAY8930::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AY8930);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
@ -479,7 +482,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -78,7 +78,7 @@ class DivPlatformAY8930: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setFlags(unsigned int flags);
bool isStereo();

View file

@ -81,7 +81,7 @@ void DivPlatformBubSysWSG::updateWave(int ch) {
}
}
void DivPlatformBubSysWSG::tick() {
void DivPlatformBubSysWSG::tick(bool sysTick) {
for (int i=0; i<2; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -110,13 +110,16 @@ void DivPlatformBubSysWSG::tick() {
if (!chan[i].keyOff) chan[i].keyOn=true;
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].active) {
if (chan[i].ws.tick()) {
updateWave(i);
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins);
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SCC);
chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>4095) chan[i].freq=4095;
@ -139,7 +142,7 @@ void DivPlatformBubSysWSG::tick() {
int DivPlatformBubSysWSG::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SCC);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
@ -225,7 +228,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SCC));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -67,7 +67,7 @@ class DivPlatformBubSysWSG: public DivDispatch {
int getRegisterPoolDepth();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -122,11 +122,11 @@ void DivPlatformC64::updateFilter() {
rWrite(0x18,(filtControl<<4)|vol);
}
void DivPlatformC64::tick() {
void DivPlatformC64::tick(bool sysTick) {
for (int i=0; i<3; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
DivInstrument* ins=parent->getIns(chan[i].ins);
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
if (ins->c64.volIsCutoff) {
if (ins->c64.filterIsAbs) {
filtCut=MIN(2047,chan[i].std.vol.val);
@ -157,7 +157,7 @@ void DivPlatformC64::tick() {
}
}
if (chan[i].std.duty.had) {
DivInstrument* ins=parent->getIns(chan[i].ins);
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
if (ins->c64.dutyIsAbs) {
chan[i].duty=chan[i].std.duty.val;
} else {
@ -166,12 +166,14 @@ void DivPlatformC64::tick() {
rWrite(i*7+2,chan[i].duty&0xff);
rWrite(i*7+3,chan[i].duty>>8);
}
if (chan[i].testWhen>0) {
if (--chan[i].testWhen<1) {
if (!chan[i].resetMask) {
rWrite(i*7+5,0);
rWrite(i*7+6,0);
rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1));
if (sysTick) {
if (chan[i].testWhen>0) {
if (--chan[i].testWhen<1) {
if (!chan[i].resetMask && !chan[i].inPorta) {
rWrite(i*7+5,0);
rWrite(i*7+6,0);
rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1));
}
}
}
}
@ -179,6 +181,9 @@ void DivPlatformC64::tick() {
chan[i].wave=chan[i].std.wave.val;
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
filtControl=chan[i].std.ex1.val&15;
updateFilter();
@ -218,7 +223,7 @@ void DivPlatformC64::tick() {
int DivPlatformC64::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].freqChanged=true;
@ -339,8 +344,8 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) {
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta || !chan[c.chan].inPorta) {
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_C64));
chan[c.chan].keyOn=true;
}
}
@ -378,7 +383,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_C64_FILTER_RESET:
if (c.value&15) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64);
if (ins->c64.initFilter) {
filtCut=ins->c64.cut;
updateFilter();
@ -388,7 +393,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_C64_DUTY_RESET:
if (c.value&15) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64);
chan[c.chan].duty=ins->c64.duty;
rWrite(c.chan*7+2,chan[c.chan].duty&0xff);
rWrite(c.chan*7+3,chan[c.chan].duty>>8);

View file

@ -83,7 +83,7 @@ class DivPlatformC64: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setFlags(unsigned int flags);
void notifyInsChange(int ins);

View file

@ -38,10 +38,12 @@ void DivPlatformDummy::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
}
void DivPlatformDummy::tick() {
void DivPlatformDummy::tick(bool sysTick) {
for (unsigned char i=0; i<chans; i++) {
chan[i].amp-=3;
if (chan[i].amp<16) chan[i].amp=16;
if (sysTick) {
chan[i].amp-=3;
if (chan[i].amp<16) chan[i].amp=16;
}
if (chan[i].freqChanged) {
chan[i].freqChanged=false;
@ -115,4 +117,4 @@ void DivPlatformDummy::quit() {
}
DivPlatformDummy::~DivPlatformDummy() {
}
}

View file

@ -41,7 +41,7 @@ class DivPlatformDummy: public DivDispatch {
int dispatch(DivCommand c);
void* getChanState(int chan);
void reset();
void tick();
void tick(bool sysTick=true);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformDummy();

View file

@ -23,7 +23,7 @@
#include <math.h>
#include <map>
#define CHIP_FREQBASE (16*2048)
#define CHIP_FREQBASE (16*2048*(chanMax+1))
#define NOTE_ES5506(c,note) (chan[c].pcm.freqOffs*NOTE_FREQUENCY(note))
#define rWrite(a,...) {if(!skipRegisterWrites) {hostIntf32.emplace(4,(a),__VA_ARGS__); }}
@ -106,14 +106,40 @@ const char** DivPlatformES5506::getRegisterSheet() {
const char* DivPlatformES5506::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set echo feedback level (00 to FF)";
return "10xx: Change waveform";
break;
case 0x11:
return "11xx: Set channel echo level (00 to FF)";
return "11xx: Set filter mode (00 to 03)";
break;
case 0x20:
return "20xx: Set envelope count (000 to 0FF)";
break;
case 0x21:
return "21xx: Set envelope count (100 to 1FF)";
break;
case 0x22:
return "22xx: Set envelope left volume ramp (signed)";
break;
case 0x23:
return "23xx: Set envelope right volume ramp (signed)";
break;
case 0x24:
return "24xx: Set envelope k1 ramp (signed)";
break;
case 0x25:
return "25xx: Set envelope k1 ramp (signed, slower)";
break;
case 0x26:
return "26xx: Set envelope k2 ramp (signed)";
break;
case 0x27:
return "27xx: Set envelope k2 ramp (signed, slower)";
break;
default:
if ((effect & 0xf0) == 0x30) {
return "3xxx: Set echo delay buffer length (000 to AA5)";
if ((effect&0xf0)==0x30) {
return "3xxx: Set filter K1";
} else if ((effect&0xf0)==0x40) {
return "4xxx: Set filter K2";
}
}
return NULL;
@ -190,9 +216,9 @@ void DivPlatformES5506::e(bool state)
irqTrigger=false;
if ((irqv&0x80)==0) {
unsigned char ch=irqv&0x1f;
if (chan[ch].isReversed) { // Reversed loop
pageWriteMask(0x00|ch,0x5f,0x00,0x48,0x78);
chan[ch].isReversed=false;
if (chan[ch].isReverseLoop) { // Reversed loop
pageWriteMask(0x00|ch,0x5f,0x00,(chan[ch].pcm.reversed?0x0040:0x0000)|0x08,0x78);
chan[ch].isReverseLoop=false;
}
}
}
@ -204,7 +230,7 @@ void DivPlatformES5506::irqb(bool state) {
irqTrigger=true;
}
void DivPlatformES5506::tick() {
void DivPlatformES5506::tick(bool sysTick) {
for (int i=0; i<=chanMax; i++) {
chan[i].std.next();
DivInstrument* ins=parent->getIns(chan[i].ins);
@ -252,6 +278,104 @@ void DivPlatformES5506::tick() {
chan[i].keyOn=true;
}
}
// filter macros
if (chan[i].std.duty.had) {
if (chan[i].filter.mode!=DivInstrumentES5506::Filter::FilterMode(chan[i].std.duty.val&3)) {
chan[i].filter.mode=DivInstrumentES5506::Filter::FilterMode(chan[i].std.duty.val&3);
chan[i].filterChanged.mode=1;
}
}
if (chan[i].std.ex1.had) {
switch (chan[i].std.ex1.mode) {
case 0: // relative
if (chan[i].k1Offs!=chan[i].std.ex1.val) {
chan[i].k1Offs=chan[i].std.ex1.val;
chan[i].filterChanged.k1=1;
}
case 1: // absolute
if (chan[i].filter.k1!=(chan[i].std.ex1.val&0xffff)) {
chan[i].filter.k1=chan[i].std.ex1.val&0xffff;
chan[i].filterChanged.k1=1;
}
break;
case 2: { // delta
signed int next_k1=MAX(0,MIN(65535,chan[i].filter.k1+chan[i].std.ex1.val));
if (chan[i].filter.k1!=next_k1) {
chan[i].filter.k1=next_k1;
chan[i].filterChanged.k1=1;
}
break;
}
default:
break;
}
}
if (chan[i].std.ex2.had) {
switch (chan[i].std.ex2.mode) {
case 0: // relative
if (chan[i].k2Offs!=chan[i].std.ex1.val) {
chan[i].k2Offs=chan[i].std.ex1.val;
chan[i].filterChanged.k2=1;
}
case 1: // absolute
if (chan[i].filter.k2!=(chan[i].std.ex2.val&0xffff)) {
chan[i].filter.k2=chan[i].std.ex2.val&0xffff;
chan[i].filterChanged.k2=1;
}
break;
case 2: { // delta
signed int next_k2=MAX(0,MIN(65535,chan[i].filter.k2+chan[i].std.ex2.val));
if (chan[i].filter.k2!=next_k2) {
chan[i].filter.k2=next_k2;
chan[i].filterChanged.k2=1;
}
break;
}
default:
break;
}
}
// envelope macros
if (chan[i].std.ex3.had) {
if (chan[i].envelope.ecount!=(chan[i].std.ex3.val&0x1ff)) {
chan[i].envelope.ecount=chan[i].std.ex3.val&0x1ff;
chan[i].envChanged.ecount=1;
}
}
if (chan[i].std.ex4.had) {
if (chan[i].envelope.lVRamp!=chan[i].std.ex4.val) {
chan[i].envelope.lVRamp=chan[i].std.ex4.val;
chan[i].envChanged.lVRamp=1;
}
}
if (chan[i].std.ex5.had) {
if (chan[i].envelope.rVRamp!=chan[i].std.ex5.val) {
chan[i].envelope.rVRamp=chan[i].std.ex5.val;
chan[i].envChanged.rVRamp=1;
}
}
if (chan[i].std.ex6.had) {
if (chan[i].envelope.k1Ramp!=chan[i].std.ex6.val) {
chan[i].envelope.k1Ramp=chan[i].std.ex6.val;
chan[i].envChanged.k1Ramp=1;
}
}
if (chan[i].std.ex7.had) {
if (chan[i].envelope.k2Ramp!=chan[i].std.ex7.val) {
chan[i].envelope.k2Ramp=chan[i].std.ex7.val;
chan[i].envChanged.k2Ramp=1;
}
}
if (chan[i].std.ex8.had) {
if (chan[i].envelope.k1Slow!=(chan[i].std.ex8.val&1)) {
chan[i].envelope.k1Slow=chan[i].std.ex8.val&1;
chan[i].envChanged.k1Ramp=1;
}
if (chan[i].envelope.k2Slow!=(chan[i].std.ex8.val&2)) {
chan[i].envelope.k2Slow=chan[i].std.ex8.val&2;
chan[i].envChanged.k2Ramp=1;
}
}
// update registers
if (chan[i].volChanged) {
if (!isMuted[i]) { // calculate volume (16 bit)
@ -267,72 +391,96 @@ void DivPlatformES5506::tick() {
}
chan[i].volChanged=false;
}
if (chan[i].filterChanged) {
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].filter.mode<<8),0x0300);
if (chan[i].filterChanged.changed) {
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x07,chan[i].filter.k2);
pageWrite(0x00|i,0x09,chan[i].filter.k1);
if (chan[i].filterChanged.mode) {
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].filter.mode<<8),0x0300);
}
if (chan[i].filterChanged.k2) {
if (chan[i].std.ex2.mode==0) { // Relative
pageWrite(0x00|i,0x07,MAX(0,MIN(65535,chan[i].filter.k2+chan[i].k2Offs)));
} else {
pageWrite(0x00|i,0x07,chan[i].filter.k2);
}
}
if (chan[i].filterChanged.k1) {
if (chan[i].std.ex1.mode==0) { // Relative
pageWrite(0x00|i,0x09,MAX(0,MIN(65535,chan[i].filter.k1+chan[i].k1Offs)));
} else {
pageWrite(0x00|i,0x09,chan[i].filter.k1);
}
}
}
chan[i].filterChanged=false;
chan[i].filterChanged.changed=0;
}
if (chan[i].envChanged) {
if (chan[i].envChanged.changed) {
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x06,chan[i].envelope.ecount);
if (chan[i].envChanged.lVRamp) {
pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8);
}
if (chan[i].envChanged.rVRamp) {
pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8);
}
if (chan[i].envChanged.ecount) {
pageWrite(0x00|i,0x06,chan[i].envelope.ecount);
}
if (chan[i].envChanged.k2Ramp) {
pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0));
}
if (chan[i].envChanged.k1Ramp) {
pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0));
}
}
chan[i].envChanged=false;
}
if (chan[i].rampChanged) {
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8);
pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8);
pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0));
pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0));
}
chan[i].rampChanged=false;
chan[i].envChanged.changed=0;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq*(chanMax+1),chan[i].pitch,false)+chan[i].std.pitch.val;
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>0x1ffff) chan[i].freq=0x1ffff;
if (chan[i].keyOn) {
if (chan[i].pcm.index>=0) {
pageWriteMask(0x00|i,0x5f,0x00,0x0303); // Wipeout CR
pageWrite(0x00|i,0x06,0); // Clear ECOUNT
pageWrite(0x20|i,0x03,chan[i].pcm.base); // Set ACCUM to start address
pageWrite(0x00|i,0x09,0xffff); // Set K1 and K2 to 0xffff
pageWrite(0x00|i,0x07,0xffff,~0,(chanMax+1)*4*2); // needs to 4 sample period delay
pageWrite(0x20|i,0x03,chan[i].pcm.reversed?chan[i].pcm.end:chan[i].pcm.start); // Set ACCUM to start address
pageWrite(0x00|i,0x07,0xffff); // Set K1 and K2 to 0xffff
pageWrite(0x00|i,0x09,0xffff);
pageWrite(0x00|i,0x01,chan[i].freq);
if (chan[i].pcm.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
pageWrite(0x20|i,0x01,chan[i].pcm.loopStart);
}
pageWrite(0x20|i,0x02,chan[i].pcm.loopEnd);
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.start:chan[i].pcm.loopStart);
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.end:chan[i].pcm.loopEnd);
// initialize envelope
pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8);
pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8);
pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0));
pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0));
pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0),~0,(chanMax+1)*4*2); // needs to 4 sample period delay
// initialize filter
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].pcm.bank<<14)|(chan[i].filter.mode<<8),0xc300);
pageWrite(0x00|i,0x09,chan[i].filter.k1);
pageWrite(0x00|i,0x07,chan[i].filter.k2);
if ((chan[i].std.ex2.mode==0) && (chan[i].std.ex2.had)) {
pageWrite(0x00|i,0x07,MAX(0,MIN(65535,chan[i].filter.k2+chan[i].k2Offs)));
} else {
pageWrite(0x00|i,0x07,chan[i].filter.k2);
}
if ((chan[i].std.ex1.mode==0) && (chan[i].std.ex1.had)) {
pageWrite(0x00|i,0x09,MAX(0,MIN(65535,chan[i].filter.k1+chan[i].k1Offs)));
} else {
pageWrite(0x00|i,0x09,chan[i].filter.k1);
}
pageWrite(0x00|i,0x02,chan[i].resLVol);
pageWrite(0x00|i,0x04,chan[i].resRVol);
unsigned int loopFlag=0x0000;
chan[i].isReversed=false;
unsigned int loopFlag=chan[i].pcm.reversed?0x0040:0x0000;
chan[i].isReverseLoop=false;
switch (chan[i].pcm.loopMode) {
case DIV_SAMPLE_LOOPMODE_ONESHOT: // One shot (no loop)
default:
loopFlag=0x0000;
break;
case DIV_SAMPLE_LOOPMODE_FOWARD: // Foward loop
loopFlag=0x0008;
loopFlag|=0x0008;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD: // Backward loop: IRQ enable
loopFlag=0x0038;
chan[i].isReversed=true;
loopFlag|=0x0038;
chan[i].isReverseLoop=true;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG: // Pingpong loop: Hardware support
loopFlag=0x0018;
loopFlag|=0x0018;
break;
}
// Run sample
@ -341,7 +489,7 @@ void DivPlatformES5506::tick() {
}
}
if (chan[i].keyOff) {
pageWriteMask(0x00|i,0x5f,0x00,0x0003); // Wipeout CR
pageWriteMask(0x00|i,0x5f,0x00,0x0303); // Wipeout CR
} else if (chan[i].active) {
pageWrite(0x00|i,0x01,chan[i].freq);
}
@ -356,30 +504,38 @@ int DivPlatformES5506::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
chan[c.chan].sample=ins->amiga.useNoteMap?ins->amiga.noteMap[c.value].ind:ins->amiga.initSample;
double off=1.0;
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
chan[c.chan].pcm.index=chan[c.chan].sample;
DivSample* s=parent->getSample(chan[c.chan].sample);
if (s->centerRate<1) {
off=1.0;
if (chan[c.chan].insChanged) {
chan[c.chan].sample=ins->amiga.useNoteMap?ins->amiga.noteMap[c.value].ind:ins->amiga.initSample;
double off=1.0;
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
chan[c.chan].pcm.index=chan[c.chan].sample;
DivSample* s=parent->getSample(chan[c.chan].sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=ins->amiga.useNoteMap?((double)ins->amiga.noteMap[c.value].freq/((double)s->centerRate*pow(2.0,((double)c.value-48.0)/12.0))):((double)s->centerRate/8363.0);
}
const unsigned int start=s->offES5506<<10;
const unsigned int length=s->samples-1;
const unsigned int end=start+(length<<11);
chan[c.chan].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
chan[c.chan].pcm.freqOffs=off;
chan[c.chan].pcm.reversed=ins->amiga.reversed;
chan[c.chan].pcm.bank=(s->offES5506>>22)&3;
chan[c.chan].pcm.start=start;
chan[c.chan].pcm.end=end;
chan[c.chan].pcm.length=length;
chan[c.chan].pcm.loopStart=(start+(s->loopStart<<11))&0xfffff800;
chan[c.chan].pcm.loopEnd=(start+((s->loopEnd-1)<<11))&0xffffff80;
chan[c.chan].filter=ins->es5506.filter;
chan[c.chan].envelope=ins->es5506.envelope;
} else {
off=ins->amiga.useNoteMap?((double)ins->amiga.noteMap[c.value].freq/((double)s->centerRate*pow(2.0,((double)c.value-48.0)/12.0))):((double)s->centerRate/8363.0);
chan[c.chan].sample=-1;
chan[c.chan].pcm.index=-1;
chan[c.chan].filter=DivInstrumentES5506::Filter();
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
}
unsigned int base=s->offES5506<<10;
chan[c.chan].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
chan[c.chan].pcm.freqOffs=off;
chan[c.chan].pcm.bank=(s->offES5506>>22)&3;
chan[c.chan].pcm.base=base;
chan[c.chan].pcm.loopStart=(base+(s->loopStart<<11))&0xfffff800;
chan[c.chan].pcm.loopEnd=((base+(s->loopEnd<<11))-0x800)&0xffffff80;
chan[c.chan].filter=ins->es5506.filter;
chan[c.chan].envelope=ins->es5506.envelope;
} else {
chan[c.chan].sample=-1;
chan[c.chan].pcm.index=-1;
chan[c.chan].filter=DivInstrumentES5506::Filter();
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
chan[c.chan].insChanged=false;
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_ES5506(c.chan,c.value);
@ -466,6 +622,45 @@ int DivPlatformES5506::dispatch(DivCommand c) {
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_WAVE:
// reserved for useWave
break;
// Filter commands
case DIV_CMD_ES5506_FILTER_MODE:
chan[c.chan].filter.mode=DivInstrumentES5506::Filter::FilterMode(c.value&3);
chan[c.chan].filterChanged.mode=1;
break;
case DIV_CMD_ES5506_FILTER_K1:
chan[c.chan].filter.k1=(chan[c.chan].filter.k1&0xf)|((c.value&0xfff)<<4);
chan[c.chan].filterChanged.k1=1;
break;
case DIV_CMD_ES5506_FILTER_K2:
chan[c.chan].filter.k2=(chan[c.chan].filter.k2&0xf)|((c.value&0xfff)<<4);
chan[c.chan].filterChanged.k2=1;
break;
// Envelope commands
case DIV_CMD_ES5506_ENVELOPE_COUNT:
chan[c.chan].envelope.ecount=c.value&0x1ff;
chan[c.chan].envChanged.ecount=1;
break;
case DIV_CMD_ES5506_ENVELOPE_LVRAMP:
chan[c.chan].envelope.lVRamp=(signed char)(c.value&0xff);
chan[c.chan].envChanged.lVRamp=1;
break;
case DIV_CMD_ES5506_ENVELOPE_RVRAMP:
chan[c.chan].envelope.rVRamp=(signed char)(c.value&0xff);
chan[c.chan].envChanged.rVRamp=1;
break;
case DIV_CMD_ES5506_ENVELOPE_K1RAMP:
chan[c.chan].envelope.k1Ramp=(signed char)(c.value&0xff);
chan[c.chan].envelope.k1Slow=c.value2&1;
chan[c.chan].envChanged.k1Ramp=1;
break;
case DIV_CMD_ES5506_ENVELOPE_K2RAMP:
chan[c.chan].envelope.k2Ramp=(signed char)(c.value&0xff);
chan[c.chan].envelope.k2Slow=c.value2&1;
chan[c.chan].envChanged.k2Ramp=1;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_ES5506(c.chan,c.value2);
bool return2=false;
@ -501,6 +696,16 @@ int DivPlatformES5506::dispatch(DivCommand c) {
}
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS: {
if (chan[c.chan].useWave) break;
if (chan[c.chan].active) {
unsigned int pos=chan[c.chan].pcm.reversed?(chan[c.chan].pcm.length-c.value):c.value;
if ((chan[c.chan].pcm.reversed && pos>0) || ((!chan[c.chan].pcm.reversed) && pos<chan[c.chan].pcm.length)) {
pageWrite(0x20|c.chan,0x03,chan[c.chan].pcm.start+(pos<<11));
}
}
break;
}
case DIV_CMD_GET_VOLMAX:
return 255;
break;
@ -523,6 +728,8 @@ void DivPlatformES5506::forceIns() {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].volChanged=true;
chan[i].filterChanged.changed=(unsigned char)(~0);
chan[i].envChanged.changed=(unsigned char)(~0);
chan[i].sample=-1;
}
}
@ -629,7 +836,7 @@ int DivPlatformES5506::init(DivEngine* p, int channels, int sugRate, unsigned in
setFlags(flags);
chipClock=16000000;
rate=chipClock/16;
rate=chipClock/16; // 2 E clock tick (16 CLKIN tick) per voice
reset();
return 32;
}

View file

@ -32,33 +32,69 @@
class DivPlatformES5506: public DivDispatch, public es550x_intf {
struct Channel {
struct PCM {
double freqOffs;
int index;
double freqOffs;
bool reversed;
unsigned int bank;
unsigned int base;
unsigned int start;
unsigned int end;
unsigned int length;
unsigned int loopStart;
unsigned int loopEnd;
DivSampleLoopMode loopMode;
PCM():
freqOffs(1.0),
index(-1),
freqOffs(1.0),
reversed(false),
bank(0),
base(0),
start(0),
end(0),
length(0),
loopStart(0),
loopEnd(0),
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
} pcm;
int freq, baseFreq, pitch;
unsigned short audLen;
unsigned int audPos;
int sample, wave;
unsigned char ins;
int note;
int panning;
bool active, insChanged, freqChanged, volChanged, filterChanged, envChanged, rampChanged, keyOn, keyOff, inPorta, useWave, isReversed;
int vol, outVol;
int lVol, outLVol;
int rVol, outRVol;
bool active, insChanged, freqChanged, volChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
struct FilterChanged { // Filter changed flags
union { // pack flag bits in single byte
struct { // flag bits
unsigned char mode: 1; // Filter mode
unsigned char k1: 1; // K1
unsigned char k2: 1; // K2
unsigned char dummy: 5; // dummy for bit padding
};
unsigned char changed; // Packed flags are stored here
};
FilterChanged():
changed(0) {}
} filterChanged;
struct EnvChanged { // Envelope changed flags
union { // pack flag bits in single byte
struct { // flag bits
unsigned char ecount: 1; // Envelope count
unsigned char lVRamp: 1; // Left volume Ramp
unsigned char rVRamp: 1; // Right volume Ramp
unsigned char k1Ramp: 1; // K1 Ramp w/Slow flag
unsigned char k2Ramp: 1; // K2 Ramp w/Slow flag
unsigned char dummy: 3; // dummy for bit padding
};
unsigned char changed; // Packed flags are stored here
};
EnvChanged():
changed(0) {}
} envChanged;
signed int k1Offs, k2Offs;
int vol, lVol, rVol;
int outVol, outLVol, outRVol;
int resLVol, resRVol;
DivInstrumentES5506::Filter filter;
DivInstrumentES5506::Envelope envelope;
@ -67,27 +103,23 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
freq(0),
baseFreq(0),
pitch(0),
audLen(0),
audPos(0),
sample(-1),
ins(-1),
note(0),
panning(0x10),
active(false),
insChanged(true),
freqChanged(false),
volChanged(false),
filterChanged(false),
envChanged(false),
rampChanged(false),
keyOn(false),
keyOff(false),
inPorta(false),
vol(0xffff),
k1Offs(0),
k2Offs(0),
vol(0xff),
lVol(0xf),
rVol(0xf),
outVol(0xffff),
lVol(0xffff),
outLVol(0xffff),
rVol(0xffff),
outRVol(0xffff),
resLVol(0xffff),
resRVol(0xffff) {}
@ -150,7 +182,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
virtual int getRegisterPoolSize() override;
virtual void reset() override;
virtual void forceIns() override;
virtual void tick() override;
virtual void tick(bool sysTick=true) override;
virtual void muteChannel(int ch, bool mute) override;
virtual bool isStereo() override;
virtual bool keyOffAffectsArp(int ch) override;

View file

@ -97,7 +97,7 @@ void DivPlatformFDS::updateWave() {
rWrite(0x4089,0);
}
void DivPlatformFDS::tick() {
void DivPlatformFDS::tick(bool sysTick) {
for (int i=0; i<1; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -107,21 +107,11 @@ void DivPlatformFDS::tick() {
rWrite(0x4080,0x80|chan[i].outVol);
}
if (chan[i].std.arp.had) {
if (i==3) { // noise
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=chan[i].std.arp.val;
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=chan[i].note+chan[i].std.arp.val;
}
if (chan[i].baseFreq>255) chan[i].baseFreq=255;
if (chan[i].baseFreq<0) chan[i].baseFreq=0;
} else {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
}
chan[i].freqChanged=true;
@ -155,6 +145,9 @@ void DivPlatformFDS::tick() {
//if (!chan[i].keyOff) chan[i].keyOn=true;
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].active) {
if (ws.tick()) {
updateWave();
@ -205,7 +198,7 @@ void DivPlatformFDS::tick() {
int DivPlatformFDS::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FDS);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].freqChanged=true;
@ -365,7 +358,7 @@ int DivPlatformFDS::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FDS));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -77,7 +77,7 @@ class DivPlatformFDS: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void setFlags(unsigned int flags);

View file

@ -146,7 +146,7 @@ static unsigned char noiseTable[256]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void DivPlatformGB::tick() {
void DivPlatformGB::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.arp.had) {
@ -176,7 +176,7 @@ void DivPlatformGB::tick() {
}
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
DivInstrument* ins=parent->getIns(chan[i].ins);
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB);
if (i!=2) {
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
} else {
@ -192,6 +192,14 @@ void DivPlatformGB::tick() {
if (!chan[i].keyOff) chan[i].keyOn=true;
}
}
if (chan[i].std.panL.had) {
lastPan&=~(0x11<<i);
lastPan|=((chan[i].std.panL.val&1)<<i)|((chan[i].std.panL.val&2)<<(i+3));
rWrite(0x25,procMute());
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -212,7 +220,7 @@ void DivPlatformGB::tick() {
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
DivInstrument* ins=parent->getIns(chan[i].ins);
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB);
if (i==3) { // noise
int ntPos=chan[i].baseFreq;
if (ntPos<0) ntPos=0;
@ -261,7 +269,7 @@ void DivPlatformGB::muteChannel(int ch, bool mute) {
int DivPlatformGB::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_GB);
if (c.value!=DIV_NOTE_NULL) {
if (c.chan==3) { // noise
chan[c.chan].baseFreq=c.value;
@ -298,7 +306,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
chan[c.chan].insChanged=true;
if (c.chan!=2) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_GB);
chan[c.chan].vol=ins->gb.envVol;
if (parent->song.gbInsAffectsEnvelope) {
rWrite(16+c.chan*5+2,((chan[c.chan].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3));
@ -352,7 +360,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
chan[c.chan].duty=c.value;
if (c.chan!=2) {
chan[c.chan].freqChanged=true;
rWrite(16+c.chan*5+1,((chan[c.chan].duty&3)<<6)|(63-(parent->getIns(chan[c.chan].ins)->gb.soundLen&63)));
rWrite(16+c.chan*5+1,((chan[c.chan].duty&3)<<6)|(63-(parent->getIns(chan[c.chan].ins,DIV_INS_GB)->gb.soundLen&63)));
}
break;
case DIV_CMD_PANNING: {
@ -371,7 +379,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_GB));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -70,7 +70,7 @@ class DivPlatformGB: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
void notifyInsChange(int ins);

View file

@ -222,7 +222,7 @@ void DivPlatformGenesis::acquire(short* bufL, short* bufR, size_t start, size_t
}
}
void DivPlatformGenesis::tick() {
void DivPlatformGenesis::tick(bool sysTick) {
for (int i=0; i<6; i++) {
if (i==2 && extMode) continue;
chan[i].std.next();
@ -260,6 +260,15 @@ void DivPlatformGenesis::tick() {
}
}
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -477,7 +486,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==5) {
if (ins->type==DIV_INS_AMIGA) {
dacMode=1;

View file

@ -109,7 +109,7 @@ class DivPlatformGenesis: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
void setYMFM(bool use);

View file

@ -37,7 +37,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
int ordch=orderedOps[ch];
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (opChan[ch].insChanged) {
chan[2].state.alg=ins->fm.alg;
@ -266,7 +266,7 @@ static int opChanOffsH[4]={
0xad, 0xae, 0xac, 0xa6
};
void DivPlatformGenesisExt::tick() {
void DivPlatformGenesisExt::tick(bool sysTick) {
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -283,7 +283,7 @@ void DivPlatformGenesisExt::tick() {
}
}
DivPlatformGenesis::tick();
DivPlatformGenesis::tick(sysTick);
bool writeNoteOn=false;
unsigned char writeMask=2;

View file

@ -41,7 +41,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
void* getChanState(int chan);
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
bool keyOffAffectsPorta(int ch);

View file

@ -145,7 +145,7 @@ void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len
mikey->sampleAudio( bufL + start, bufR + start, len );
}
void DivPlatformLynx::tick() {
void DivPlatformLynx::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -171,6 +171,24 @@ void DivPlatformLynx::tick() {
}
}
if (chan[i].std.panL.had) {
chan[i].pan&=0x0f;
chan[i].pan|=(chan[i].std.panL.val&15)<<4;
}
if (chan[i].std.panR.had) {
chan[i].pan&=0xf0;
chan[i].pan|=chan[i].std.panR.val&15;
}
if (chan[i].std.panL.had || chan[i].std.panR.had) {
WRITE_ATTEN(i,chan[i].pan);
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged) {
if (chan[i].lfsr >= 0) {
WRITE_LFSR(i, (chan[i].lfsr&0xff));
@ -184,8 +202,8 @@ void DivPlatformLynx::tick() {
}
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
WRITE_BACKUP( i, chan[i].fd.backup );
}
else if (chan[i].std.duty.had) {
chan[i].freqChanged=false;
} else if (chan[i].std.duty.had) {
chan[i].duty = chan[i].std.duty.val;
WRITE_FEEDBACK(i, chan[i].duty.feedback);
WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7));
@ -206,7 +224,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
@ -223,7 +241,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
break;
case DIV_CMD_INSTRUMENT:
chan[c.chan].ins=c.value;
//chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
//chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
@ -279,7 +297,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -80,7 +80,7 @@ class DivPlatformLynx: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -96,7 +96,7 @@ void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len
}
}
void DivPlatformMMC5::tick() {
void DivPlatformMMC5::tick(bool sysTick) {
for (int i=0; i<2; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -124,6 +124,9 @@ void DivPlatformMMC5::tick() {
chan[i].duty=chan[i].std.duty.val;
rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].freqChanged=true;
@ -175,7 +178,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
if (c.chan==2) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) {
dacSample=ins->amiga.initSample;
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
@ -225,7 +228,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
rWrite(0x5000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
break;
case DIV_CMD_NOTE_OFF:
@ -303,7 +306,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -71,7 +71,7 @@ class DivPlatformMMC5: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
float getPostAmp();

View file

@ -214,7 +214,7 @@ void DivPlatformN163::updateWaveCh(int ch) {
}
}
void DivPlatformN163::tick() {
void DivPlatformN163::tick(bool sysTick) {
for (int i=0; i<=chanMax; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -261,6 +261,9 @@ void DivPlatformN163::tick() {
}
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].waveLen=chan[i].std.ex1.val&0xfc;
@ -371,7 +374,7 @@ void DivPlatformN163::tick() {
int DivPlatformN163::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_N163);
if (chan[c.chan].insChanged) {
chan[c.chan].wave=ins->n163.wave;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
@ -543,7 +546,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) {
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_N163));
chan[c.chan].keyOn=true;
}
}

View file

@ -93,7 +93,7 @@ class DivPlatformN163: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setFlags(unsigned int flags);
void notifyWaveChange(int wave);

View file

@ -141,7 +141,7 @@ static unsigned char noiseTable[253]={
15
};
void DivPlatformNES::tick() {
void DivPlatformNES::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -196,6 +196,9 @@ void DivPlatformNES::tick() {
chan[i].freqChanged=true;
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].sweepChanged) {
chan[i].sweepChanged=false;
if (i==0) {
@ -269,7 +272,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
if (c.chan==4) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) {
dacSample=ins->amiga.initSample;
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
@ -323,7 +326,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (c.chan==2) {
rWrite(0x4000+c.chan*4,0xff);
} else {
@ -426,7 +429,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -72,7 +72,7 @@ class DivPlatformNES: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
float getPostAmp();

View file

@ -228,7 +228,7 @@ void DivPlatformOPL::acquire(short* bufL, short* bufR, size_t start, size_t len)
//}
}
void DivPlatformOPL::tick() {
void DivPlatformOPL::tick(bool sysTick) {
for (int i=0; i<totalChans; i++) {
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
chan[i].std.next();
@ -269,6 +269,14 @@ void DivPlatformOPL::tick() {
}
}
if (oplType==3 && chan[i].std.panL.had) {
chan[i].pan=((chan[i].std.panL.val&1)<<1)|((chan[i].std.panL.val&2)>>1);
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -282,7 +290,7 @@ void DivPlatformOPL::tick() {
chan[i].state.fb=chan[i].std.fb.val;
}
if (chan[i].std.alg.had || chan[i].std.fb.had) {
if (chan[i].std.alg.had || chan[i].std.fb.had || (oplType==3 && chan[i].std.panL.had)) {
if (isMuted[i]) {
rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1));
if (ops==4) {
@ -527,7 +535,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
}
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;

View file

@ -104,7 +104,7 @@ class DivPlatformOPL: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
void setYMFM(bool use);

View file

@ -111,7 +111,7 @@ void DivPlatformOPLL::acquire(short* bufL, short* bufR, size_t start, size_t len
acquire_nuked(bufL,bufR,start,len);
}
void DivPlatformOPLL::tick() {
void DivPlatformOPLL::tick(bool sysTick) {
for (int i=0; i<11; i++) {
chan[i].std.next();
@ -145,6 +145,10 @@ void DivPlatformOPLL::tick() {
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -361,7 +365,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan>=9 && !properDrums) return 0;
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
}

View file

@ -100,7 +100,7 @@ class DivPlatformOPLL: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setYMFM(bool use);
bool keyOffAffectsArp(int ch);

View file

@ -146,7 +146,7 @@ static unsigned char noiseFreq[12]={
4,13,15,18,21,23,25,27,29,31,0,2
};
void DivPlatformPCE::tick() {
void DivPlatformPCE::tick(bool sysTick) {
for (int i=0; i<6; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -196,13 +196,27 @@ void DivPlatformPCE::tick() {
if (!chan[i].keyOff) chan[i].keyOn=true;
}
}
if (chan[i].std.panL.had) {
chan[i].pan&=0x0f;
chan[i].pan|=(chan[i].std.panL.val&15)<<4;
}
if (chan[i].std.panR.had) {
chan[i].pan&=0xf0;
chan[i].pan|=chan[i].std.panR.val&15;
}
if (chan[i].std.panL.had || chan[i].std.panR.had) {
chWrite(i,0x05,isMuted[i]?0:chan[i].pan);
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].active) {
if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) {
updateWave(i);
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins);
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
if (chan[i].furnaceDac) {
double off=1.0;
@ -237,7 +251,7 @@ void DivPlatformPCE::tick() {
int DivPlatformPCE::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].pcm=true;
} else if (chan[c.chan].furnaceDac) {
@ -415,7 +429,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -87,7 +87,7 @@ class DivPlatformPCE: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -164,7 +164,7 @@ void DivPlatformPCSpeaker::acquire(short* bufL, short* bufR, size_t start, size_
}
}
void DivPlatformPCSpeaker::tick() {
void DivPlatformPCSpeaker::tick(bool sysTick) {
for (int i=0; i<1; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -186,6 +186,9 @@ void DivPlatformPCSpeaker::tick() {
chan[i].freqChanged=true;
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val;
if (chan[i].freq<0) chan[i].freq=0;
@ -214,7 +217,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
@ -279,7 +282,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -77,7 +77,7 @@ class DivPlatformPCSpeaker: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void setFlags(unsigned int flags);

View file

@ -85,7 +85,7 @@ void DivPlatformPET::writeOutVol() {
}
}
void DivPlatformPET::tick() {
void DivPlatformPET::tick(bool sysTick) {
chan.std.next();
if (chan.std.vol.had) {
chan.outVol=chan.std.vol.val&chan.vol;
@ -112,6 +112,9 @@ void DivPlatformPET::tick() {
rWrite(10,chan.wave);
}
}
if (chan.std.pitch.had) {
chan.freqChanged=true;
}
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true)+chan.std.pitch.val;
if (chan.freq>257) chan.freq=257;
@ -135,7 +138,7 @@ void DivPlatformPET::tick() {
int DivPlatformPET::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan.ins);
DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_PET);
if (c.value!=DIV_NOTE_NULL) {
chan.baseFreq=NOTE_PERIODIC(c.value);
chan.freqChanged=true;
@ -210,7 +213,7 @@ int DivPlatformPET::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan.active && c.value2) {
if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins));
if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins,DIV_INS_PET));
}
chan.inPorta=c.value;
break;

View file

@ -65,7 +65,7 @@ class DivPlatformPET: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyInsDeletion(void* ins);
bool isStereo();

View file

@ -274,7 +274,7 @@ void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t l
}
}
void DivPlatformQSound::tick() {
void DivPlatformQSound::tick(bool sysTick) {
for (int i=0; i<16; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -326,8 +326,11 @@ void DivPlatformQSound::tick() {
chan[i].freqChanged=true;
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins);
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val;
if (chan[i].freq>0xffff) chan[i].freq=0xffff;
if (chan[i].keyOn) {
@ -360,7 +363,7 @@ void DivPlatformQSound::tick() {
int DivPlatformQSound::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].sample=ins->amiga.initSample;
double off=1.0;
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
@ -484,7 +487,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -74,7 +74,7 @@ class DivPlatformQSound: public DivDispatch {
int getRegisterPoolDepth();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -132,7 +132,7 @@ inline unsigned char applyPan(unsigned char vol, unsigned char pan) {
return ((vol*(pan>>4))/15)|(((vol*(pan&15))/15)<<4);
}
void DivPlatformSAA1099::tick() {
void DivPlatformSAA1099::tick(bool sysTick) {
for (int i=0; i<6; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -166,6 +166,29 @@ void DivPlatformSAA1099::tick() {
if (chan[i].std.wave.had) {
chan[i].psgMode=chan[i].std.wave.val&3;
}
if (chan[i].std.panL.had) {
chan[i].pan&=0x0f;
chan[i].pan|=(chan[i].std.panL.val&15)<<4;
}
if (chan[i].std.panR.had) {
chan[i].pan&=0xf0;
chan[i].pan|=chan[i].std.panR.val&15;
}
if (chan[i].std.panL.had || chan[i].std.panR.had) {
if (isMuted[i]) {
rWrite(i,0);
} else {
if (chan[i].std.vol.had) {
if (chan[i].active) rWrite(i,applyPan(chan[i].outVol&15,chan[i].pan));
} else {
if (chan[i].active) rWrite(i,applyPan(chan[i].vol&15,chan[i].pan));
}
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
saaEnv[i/3]=chan[i].std.ex1.val;
rWrite(0x18+(i/3),saaEnv[i/3]);
@ -226,7 +249,7 @@ void DivPlatformSAA1099::tick() {
int DivPlatformSAA1099::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
@ -333,7 +356,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -90,7 +90,7 @@ class DivPlatformSAA1099: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setCore(DivSAACores core);
void setFlags(unsigned int flags);

View file

@ -76,13 +76,14 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t
}
}
void DivPlatformSegaPCM::tick() {
void DivPlatformSegaPCM::tick(bool sysTick) {
for (int i=0; i<16; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
// TODO: fix
/*if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
}
}*/
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
@ -99,6 +100,24 @@ void DivPlatformSegaPCM::tick() {
chan[i].freqChanged=true;
}
}
if (chan[i].std.panL.had) {
chan[i].chVolL=chan[i].std.panL.val&127;
if (dumpWrites) {
addWrite(0x10002+(i<<3),chan[i].chVolL);
}
}
if (chan[i].std.panR.had) {
chan[i].chVolR=chan[i].std.panR.val&127;
if (dumpWrites) {
addWrite(0x10003+(i<<3),chan[i].chVolR);
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
/*if (chan[i].keyOn || chan[i].keyOff) {
chan[i].keyOff=false;
}*/
@ -130,7 +149,7 @@ void DivPlatformSegaPCM::muteChannel(int ch, bool mute) {
int DivPlatformSegaPCM::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].pcm.sample=ins->amiga.initSample;
@ -139,14 +158,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
if (dumpWrites) {
addWrite(0x10086+(c.chan<<3),3);
}
chan[c.chan].std.init(NULL);
break;
}
chan[c.chan].pcm.pos=0;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].baseFreq=(c.value<<6);
chan[c.chan].freqChanged=true;
}
chan[c.chan].furnacePCM=true;
chan[c.chan].std.init(ins);
if (dumpWrites) { // Sega PCM writes
DivSample* s=parent->getSample(chan[c.chan].pcm.sample);
addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
@ -163,6 +185,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
}
}
} else {
chan[c.chan].std.init(NULL);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
}
@ -204,6 +227,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
chan[c.chan].std.init(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;

View file

@ -78,7 +78,7 @@ class DivPlatformSegaPCM: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyInsChange(int ins);
void setFlags(unsigned int flags);

View file

@ -23,8 +23,6 @@
#define rWrite(v) {if (!skipRegisterWrites) {sn->write(v); if (dumpWrites) {addWrite(0x200,v);}}}
#define CHIP_DIVIDER 64
const char* regCheatSheetSN[]={
"DATA", "0",
NULL
@ -53,8 +51,10 @@ int DivPlatformSMS::acquireOne() {
return v;
}
void DivPlatformSMS::tick() {
void DivPlatformSMS::tick(bool sysTick) {
for (int i=0; i<4; i++) {
int CHIP_DIVIDER=64;
if (i==3 && isRealSN) CHIP_DIVIDER=60;
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
@ -98,6 +98,9 @@ void DivPlatformSMS::tick() {
}
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
}
for (int i=0; i<3; i++) {
if (chan[i].freqChanged) {
@ -116,8 +119,7 @@ void DivPlatformSMS::tick() {
}
}
if (chan[3].freqChanged || updateSNMode) {
// seems arbitrary huh?
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch-1-(isRealSN?127:0),true)+chan[3].std.pitch.val;
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true)+chan[3].std.pitch.val;
if (chan[3].freq>1023) chan[3].freq=1023;
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
if (snNoiseMode&2) { // take period from channel 3
@ -161,6 +163,8 @@ void DivPlatformSMS::tick() {
}
int DivPlatformSMS::dispatch(DivCommand c) {
int CHIP_DIVIDER=64;
if (c.chan==3 && isRealSN) CHIP_DIVIDER=60;
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
if (c.value!=DIV_NOTE_NULL) {
@ -171,7 +175,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
rWrite(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
@ -184,7 +188,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
break;
case DIV_CMD_INSTRUMENT:
chan[c.chan].ins=c.value;
//chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
//chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
@ -240,7 +244,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -63,7 +63,7 @@ class DivPlatformSMS: public DivDispatch {
void* getChanState(int chan);
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
bool keyOffAffectsPorta(int ch);

View file

@ -21,6 +21,7 @@ namespace es550x
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
@ -250,12 +251,14 @@ protected:
: m_integer(integer)
, m_fraction(fraction)
, m_total_bits(integer + fraction)
, m_accum_mask(u32(std::min<u64>(~0, u64(u64(1) << u64(integer + fraction)) - 1)))
, m_transwave(transwave)
{}
const u8 m_integer;
const u8 m_fraction;
const u8 m_total_bits;
const u32 m_accum_mask;
const bool m_transwave;
void reset();

View file

@ -29,15 +29,14 @@ bool es550x_shared_core::es550x_alu_t::busy()
bool es550x_shared_core::es550x_alu_t::tick()
{
if (m_cr.dir)
{
m_accum = bitfield(m_accum - m_fc, 0, m_total_bits);
return ((!m_cr.lei) && (m_accum < m_start)) ? true : false;
}
m_accum -= m_fc;
else
{
m_accum = bitfield(m_accum + m_fc, 0, m_total_bits);
return ((!m_cr.lei) && (m_accum > m_end)) ? true : false;
}
m_accum += m_fc;
m_accum &= m_accum_mask;
return ((!m_cr.lei)
&& ((( m_cr.dir) && (m_accum < m_start))
|| ((!m_cr.dir) && (m_accum > m_end)))) ? true : false;
}
void es550x_shared_core::es550x_alu_t::loop_exec()

View file

@ -141,13 +141,13 @@ void DivPlatformSwan::writeOutVol(int ch) {
}
}
void DivPlatformSwan::tick() {
void DivPlatformSwan::tick(bool sysTick) {
unsigned char sndCtrl=(pcm?0x20:0)|(sweep?0x40:0)|((noise>0)?0x80:0);
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
int env=chan[i].std.vol.val;
if(parent->getIns(chan[i].ins)->type==DIV_INS_AMIGA) {
if(parent->getIns(chan[i].ins,DIV_INS_SWAN)->type==DIV_INS_AMIGA) {
env=MIN(env/4,15);
}
calcAndWriteOutVol(i,env);
@ -173,6 +173,20 @@ void DivPlatformSwan::tick() {
chan[i].ws.changeWave1(chan[i].wave);
}
}
if (chan[i].std.panL.had) {
chan[i].pan&=0x0f;
chan[i].pan|=(chan[i].std.panL.val&15)<<4;
}
if (chan[i].std.panR.had) {
chan[i].pan&=0xf0;
chan[i].pan|=chan[i].std.panR.val&15;
}
if (chan[i].std.panL.had || chan[i].std.panR.had) {
calcAndWriteOutVol(i,chan[i].std.vol.will?chan[i].std.vol.val:15);
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].active) {
sndCtrl|=(1<<i);
if (chan[i].ws.tick()) {
@ -226,7 +240,7 @@ void DivPlatformSwan::tick() {
int DivPlatformSwan::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SWAN);
if (c.chan==1) {
if (ins->type==DIV_INS_AMIGA) {
pcm=true;
@ -401,7 +415,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -77,7 +77,7 @@ class DivPlatformSwan: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);

View file

@ -84,7 +84,7 @@ unsigned char DivPlatformTIA::dealWithFreq(unsigned char shape, int base, int pi
return 0;
}
void DivPlatformTIA::tick() {
void DivPlatformTIA::tick(bool sysTick) {
for (int i=0; i<2; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -116,6 +116,9 @@ void DivPlatformTIA::tick() {
rWrite(0x15+i,chan[i].shape);
chan[i].freqChanged=true;
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (chan[i].insChanged) {
if (!chan[i].std.wave.will) {
@ -151,7 +154,7 @@ void DivPlatformTIA::tick() {
int DivPlatformTIA::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_TIA);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=c.value<<8;
chan[c.chan].freqChanged=true;
@ -245,7 +248,7 @@ int DivPlatformTIA::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_TIA));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -51,7 +51,7 @@ class DivPlatformTIA: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setFlags(unsigned int flags);
bool isStereo();

View file

@ -183,7 +183,7 @@ inline int hScale(int note) {
return ((note/12)<<4)+(noteMap[note%12]);
}
void DivPlatformTX81Z::tick() {
void DivPlatformTX81Z::tick(bool sysTick) {
for (int i=0; i<8; i++) {
chan[i].std.next();
@ -228,6 +228,10 @@ void DivPlatformTX81Z::tick() {
rWrite(0x1b,chan[i].std.wave.val&3);
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -420,7 +424,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) {
int DivPlatformTX81Z::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;

View file

@ -102,7 +102,7 @@ class DivPlatformTX81Z: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyInsChange(int ins);
void setFlags(unsigned int flags);

View file

@ -114,8 +114,8 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len
psg_render(psg,buf[0],buf[1],curLen);
pcm_render(pcm,buf[2],buf[3],curLen);
for (int i=0; i<curLen; i++) {
bufL[pos]=(short)(((int)buf[0][i]+(buf[2][i]>>1))/2);
bufR[pos]=(short)(((int)buf[1][i]+(buf[3][i]>>1))/2);
bufL[pos]=(short)(((int)buf[0][i]+buf[2][i])/2);
bufR[pos]=(short)(((int)buf[1][i]+buf[3][i])/2);
pos++;
}
len-=curLen;
@ -156,7 +156,7 @@ int DivPlatformVERA::calcNoteFreq(int ch, int note) {
}
}
void DivPlatformVERA::tick() {
void DivPlatformVERA::tick(bool sysTick) {
for (int i=0; i<16; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -184,6 +184,15 @@ void DivPlatformVERA::tick() {
if (chan[i].std.wave.had) {
rWriteHi(i,3,chan[i].std.wave.val);
}
if (i<16) {
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
rWriteHi(i,2,isMuted[i]?0:chan[i].pan);
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val;
if (chan[i].freq>65535) chan[i].freq=65535;
@ -228,7 +237,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
if(c.chan<16) {
rWriteLo(c.chan,2,chan[c.chan].vol)
} else {
chan[16].pcm.sample=parent->getIns(chan[16].ins)->amiga.initSample;
chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.initSample;
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
chan[16].pcm.sample=-1;
}
@ -250,7 +259,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
chan[c.chan].note=c.value;
}
chan[c.chan].active=true;
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA));
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
@ -318,7 +327,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA));
}
chan[c.chan].inPorta=c.value;
break;
@ -374,7 +383,7 @@ void DivPlatformVERA::muteChannel(int ch, bool mute) {
}
float DivPlatformVERA::getPostAmp() {
return 4.0f;
return 8.0f;
}
bool DivPlatformVERA::isStereo() {

View file

@ -63,7 +63,7 @@ class DivPlatformVERA: public DivDispatch {
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void notifyInsDeletion(void* ins);
float getPostAmp();

View file

@ -91,7 +91,7 @@ void DivPlatformVIC20::writeOutVol(int ch) {
}
}
void DivPlatformVIC20::tick() {
void DivPlatformVIC20::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -119,6 +119,9 @@ void DivPlatformVIC20::tick() {
chan[i].keyOn=true;
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val;
if (i<3) {
@ -155,7 +158,7 @@ void DivPlatformVIC20::tick() {
int DivPlatformVIC20::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_VIC);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
@ -229,7 +232,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VIC));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -66,7 +66,7 @@ class DivPlatformVIC20: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setFlags(unsigned int flags);
void notifyInsDeletion(void* ins);

View file

@ -135,7 +135,7 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len
}
}
void DivPlatformVRC6::tick() {
void DivPlatformVRC6::tick(bool sysTick) {
for (int i=0; i<3; i++) {
// 16 for pulse; 14 for saw
int CHIP_DIVIDER=(i==2)?14:16;
@ -178,6 +178,9 @@ void DivPlatformVRC6::tick() {
chWrite(i,0,(chan[i].outVol&0xf)|((chan[i].duty&7)<<4));
}
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (i==2) { // sawtooth
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val;
@ -217,7 +220,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
if (c.chan!=2) { // pulse wave
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_VRC6);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].pcm=true;
} else if (chan[c.chan].furnaceDac) {
@ -281,7 +284,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6));
if (!isMuted[c.chan]) {
if (c.chan==2) { // sawtooth
chWrite(c.chan,0,chan[c.chan].vol);
@ -377,7 +380,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6));
}
chan[c.chan].inPorta=c.value;
break;
@ -431,9 +434,9 @@ void DivPlatformVRC6::reset() {
chan[i]=DivPlatformVRC6::Channel();
chan[i].std.setEngine(parent);
}
// a poll may be necessary to decide the default
chan[2].vol=30;
chan[2].outVol=30;
// HELP
chan[2].vol=63;
chan[2].outVol=63;
if (dumpWrites) {
addWrite(0xffffffff,0);
}

View file

@ -82,7 +82,7 @@ class DivPlatformVRC6: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void setFlags(unsigned int flags);

View file

@ -336,7 +336,7 @@ void DivPlatformX1_010::updateEnvelope(int ch) {
}
}
void DivPlatformX1_010::tick() {
void DivPlatformX1_010::tick(bool sysTick) {
for (int i=0; i<16; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@ -372,6 +372,19 @@ void DivPlatformX1_010::tick() {
}
}
}
if (chan[i].std.panL.had) {
chan[i].pan&=0x0f;
chan[i].pan|=(chan[i].std.panL.val&15)<<4;
chan[i].envChanged=true;
}
if (chan[i].std.panR.had) {
chan[i].pan&=0xf0;
chan[i].pan|=chan[i].std.panR.val&15;
chan[i].envChanged=true;
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
bool nextEnable=(chan[i].std.ex1.val&1);
if (nextEnable!=(chan[i].env.flag.envEnable)) {
@ -512,7 +525,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
chWrite(c.chan,0,0); // reset previous note
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_X1_010);
if ((ins->type==DIV_INS_AMIGA) || chan[c.chan].pcm) {
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].furnacePCM=true;
@ -690,7 +703,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -126,7 +126,7 @@ class DivPlatformX1_010: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -365,9 +365,9 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l
}
}
void DivPlatformYM2610::tick() {
void DivPlatformYM2610::tick(bool sysTick) {
// PSG
ay->tick();
ay->tick(sysTick);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
immWrite(i.addr&15,i.val);
@ -408,6 +408,15 @@ void DivPlatformYM2610::tick() {
}
}
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -626,7 +635,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan>12) { // ADPCM-B
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].furnacePCM=true;
} else {
@ -709,7 +718,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
immWrite(0x100,0x00|(1<<(c.chan-7)));
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
chan[c.chan].std.init(ins);
if (c.chan<4) {
if (!chan[c.chan].std.vol.will) {
@ -972,7 +981,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
case DIV_CMD_PRE_PORTA:
if (c.chan>3) {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
}
}
chan[c.chan].inPorta=c.value;

View file

@ -116,7 +116,7 @@ class DivPlatformYM2610: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -429,9 +429,9 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t
}
}
void DivPlatformYM2610B::tick() {
void DivPlatformYM2610B::tick(bool sysTick) {
// PSG
ay->tick();
ay->tick(sysTick);
ay->flushWrites();
for (DivRegWrite& i: ay->getRegisterWrites()) {
immWrite(i.addr&15,i.val);
@ -472,6 +472,15 @@ void DivPlatformYM2610B::tick() {
}
}
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.pitch.had) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].keyOn=true;
@ -689,7 +698,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan>14) { // ADPCM-B
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].furnacePCM=true;
} else {
@ -772,7 +781,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
immWrite(0x100,0x00|(1<<(c.chan-9)));
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
chan[c.chan].std.init(ins);
if (c.chan<6) {
if (!chan[c.chan].std.vol.will) {
@ -1035,7 +1044,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
case DIV_CMD_PRE_PORTA:
if (c.chan>5) {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
}
}
chan[c.chan].inPorta=c.value;

View file

@ -107,7 +107,7 @@ class DivPlatformYM2610B: public DivDispatch {
int getRegisterPoolSize();
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);

View file

@ -36,7 +36,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
int ordch=orderedOps[ch];
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
@ -78,7 +78,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
break;
case DIV_CMD_VOLUME: {
opChan[ch].vol=c.value;
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch]) {
@ -104,7 +104,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
} else {
opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
}
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (parent->song.sharedExtStat) {
for (int i=0; i<4; i++) {
if (ch==i) continue;
@ -159,14 +159,14 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
}
case DIV_CMD_FM_MULT: { // TODO
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
break;
}
case DIV_CMD_FM_TL: { // TODO
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (isOutput[ins->fm.alg][c.value]) {
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
} else {
@ -175,7 +175,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AR: {
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator op=ins->fm.op[i];
@ -212,7 +212,7 @@ static int opChanOffsH[4]={
0xad, 0xae, 0xac, 0xa6
};
void DivPlatformYM2610BExt::tick() {
void DivPlatformYM2610BExt::tick(bool sysTick) {
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -229,7 +229,7 @@ void DivPlatformYM2610BExt::tick() {
}
}
DivPlatformYM2610B::tick();
DivPlatformYM2610B::tick(sysTick);
bool writeNoteOn=false;
unsigned char writeMask=2;
@ -268,7 +268,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
isOpMuted[ch-2]=mute;
int ordch=orderedOps[ch-2];
DivInstrument* ins=parent->getIns(opChan[ch-2].ins);
DivInstrument* ins=parent->getIns(opChan[ch-2].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch-2]) {

View file

@ -41,7 +41,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
void* getChanState(int chan);
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);

View file

@ -36,7 +36,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
int ordch=orderedOps[ch];
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
@ -78,7 +78,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
break;
case DIV_CMD_VOLUME: {
opChan[ch].vol=c.value;
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch]) {
@ -104,7 +104,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
} else {
opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1);
}
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (parent->song.sharedExtStat) {
for (int i=0; i<4; i++) {
if (ch==i) continue;
@ -159,14 +159,14 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
}
case DIV_CMD_FM_MULT: { // TODO
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
break;
}
case DIV_CMD_FM_TL: { // TODO
unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (isOutput[ins->fm.alg][c.value]) {
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
} else {
@ -175,7 +175,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AR: {
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator op=ins->fm.op[i];
@ -212,7 +212,7 @@ static int opChanOffsH[4]={
0xad, 0xae, 0xac, 0xa6
};
void DivPlatformYM2610Ext::tick() {
void DivPlatformYM2610Ext::tick(bool sysTick) {
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -229,7 +229,7 @@ void DivPlatformYM2610Ext::tick() {
}
}
DivPlatformYM2610::tick();
DivPlatformYM2610::tick(sysTick);
bool writeNoteOn=false;
unsigned char writeMask=2;
@ -268,7 +268,7 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
isOpMuted[ch-1]=mute;
int ordch=orderedOps[ch-1];
DivInstrument* ins=parent->getIns(opChan[ch].ins);
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
unsigned short baseAddr=chanOffs[1]|opOffs[ordch];
DivInstrumentFM::Operator op=ins->fm.op[ordch];
if (isOpMuted[ch]) {

View file

@ -41,7 +41,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
void* getChanState(int chan);
void reset();
void forceIns();
void tick();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);

View file

@ -40,7 +40,7 @@ const char* notes[12]={
};
// update this when adding new commands.
const char* cmdName[DIV_CMD_MAX]={
const char* cmdName[]={
"NOTE_ON",
"NOTE_OFF",
"NOTE_OFF_ENV",
@ -144,15 +144,27 @@ const char* cmdName[DIV_CMD_MAX]={
"N163_WAVE_LOAD",
"N163_WAVE_LOADPOS",
"N163_WAVE_LOADLEN",
"N163_WAVE_LOADMODE",
"N163_CHANNEL_LIMIT",
"N163_GLOBAL_WAVE_LOAD",
"N163_GLOBAL_WAVE_LOADPOS",
"N163_GLOBAL_WAVE_LOADLEN",
"N163_GLOBAL_WAVE_LOADMODE",
"ES5506_FILTER_MODE",
"ES5506_FILTER_K1",
"ES5506_FILTER_K2",
"ES5506_ENVELOPE_COUNT",
"ES5506_ENVELOPE_LVRAMP",
"ES5506_ENVELOPE_RVRAMP",
"ES5506_ENVELOPE_K1RAMP",
"ES5506_ENVELOPE_K2RAMP",
"ALWAYS_SET_VOLUME"
};
static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!");
const char* formatNote(unsigned char note, unsigned char octave) {
static char ret[4];
if (note==100) {
@ -513,19 +525,38 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
break;
case DIV_SYSTEM_ES5506:
switch (effect) {
case 0x10: // echo feedback
dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_FEEDBACK,ch,effectVal));
case 0x10: // select waveform
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
break;
case 0x11: // echo level
dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal));
case 0x11: // filter mode
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_MODE,ch,effectVal&3));
break;
case 0x20:
case 0x21: // envelope ECOUNT
dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_COUNT,ch,((effect&0x01)<<8)|effectVal));
break;
case 0x22: // envelope LVRAMP
dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_LVRAMP,ch,effectVal));
break;
case 0x23: // envelope RVRAMP
dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_RVRAMP,ch,effectVal));
break;
case 0x24:
case 0x25: // envelope K1RAMP
dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_K1RAMP,ch,effectVal,effect&0x01));
break;
case 0x26:
case 0x27: // envelope K2RAMP
dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_K2RAMP,ch,effectVal,effect&0x01));
break;
default:
if ((effect&0xf0)==0x30) {
dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal));
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K1,ch,((effect&0x0f)<<8)|effectVal));
} else if ((effect&0xf0)==0x40) {
dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K2,ch,((effect&0x0f)<<8)|effectVal));
} else {
return false;
}
break;
}
break;
default:
@ -1487,19 +1518,21 @@ void DivEngine::nextRow() {
firstTick=true;
}
bool DivEngine::nextTick(bool noAccum) {
bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
bool ret=false;
if (divider<10) divider=10;
if (lowLatency) {
if (lowLatency && !skipping && !inhibitLowLat) {
tickMult=1000/divider;
if (tickMult<1) tickMult=1;
} else {
tickMult=1;
}
cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/(divider*tickMult);
clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)divider);
if (clockDrift>=divider) {
clockDrift-=divider;
clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(divider*tickMult));
if (clockDrift>=(divider*tickMult)) {
clockDrift-=(divider*tickMult);
cycles++;
}
@ -1656,7 +1689,7 @@ bool DivEngine::nextTick(bool noAccum) {
firstTick=false;
// system tick
for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->tick();
for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->tick(subticks==tickMult);
if (!freelance) {
if (stepPlay!=1) {
@ -1672,7 +1705,7 @@ bool DivEngine::nextTick(bool noAccum) {
}
}
if (consoleMode) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond);
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond);
}
if (haltOn==DIV_HALT_TICK) halted=true;

View file

@ -595,8 +595,8 @@ bool DivSample::resampleSinc(double r) {
result+=s[j]*t2[7-j];
result+=s[8+j]*t1[j];
}
if (result<-32768) result=-32768;
if (result>32767) result=32767;
if (result<-128) result=-128;
if (result>127) result=127;
if (i>=8) {
data8[i-8]=result;
}

View file

@ -324,6 +324,7 @@ struct DivSong {
bool sharedExtStat;
bool ignoreDACModeOutsideIntendedChannel;
bool e1e2AlsoTakePriority;
bool newSegaPCM;
DivOrders orders;
std::vector<DivInstrument*> ins;
@ -334,7 +335,7 @@ struct DivSong {
bool chanShow[DIV_MAX_CHANS];
bool chanCollapse[DIV_MAX_CHANS];
DivInstrument nullIns;
DivInstrument nullIns, nullInsOPLL, nullInsOPL;
DivWavetable nullWave;
DivSample nullSample;
@ -405,7 +406,8 @@ struct DivSong {
gbInsAffectsEnvelope(true),
sharedExtStat(true),
ignoreDACModeOutsideIntendedChannel(false),
e1e2AlsoTakePriority(false) {
e1e2AlsoTakePriority(false),
newSegaPCM(true) {
for (int i=0; i<32; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=64;
@ -418,6 +420,23 @@ struct DivSong {
}
system[0]=DIV_SYSTEM_YM2612;
system[1]=DIV_SYSTEM_SMS;
nullInsOPLL.fm.opllPreset=7;
nullInsOPLL.fm.op[1].tl=0;
nullInsOPLL.name="This is a bug! Report!";
nullInsOPL.fm.alg=0;
nullInsOPL.fm.fb=7;
nullInsOPL.fm.op[0].dr=2;
nullInsOPL.fm.op[0].rr=7;
nullInsOPL.fm.op[0].tl=22;
nullInsOPL.fm.op[0].ksl=1;
nullInsOPL.fm.op[0].mult=3;
nullInsOPL.fm.op[1].tl=0;
nullInsOPL.fm.op[1].dr=3;
nullInsOPL.fm.op[1].rr=12;
nullInsOPL.fm.op[1].mult=1;
nullInsOPL.name="This is a bug! Report!";
}
};

View file

@ -1133,7 +1133,7 @@ const DivInstrumentType chanPrefType[48][32]={
{DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC}, // VIC-20
{DIV_INS_PET}, // PET
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // SNES/N163/RF5C68
{DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6}, // VRC6
{DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6_SAW}, // VRC6
{DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, // OPLL/VRC7
{DIV_INS_FDS}, // FDS
{DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA}, // MMC5

View file

@ -752,11 +752,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
bool writeDACSamples=false;
bool writeNESSamples=false;
bool writePCESamples=false;
bool writeADPCM=false;
bool writeSegaPCM=false;
bool writeX1010=false;
bool writeQSound=false;
bool writeES5506=false;
unsigned char writeADPCM=0;
unsigned char writeSegaPCM=0;
unsigned char writeX1010=0;
unsigned char writeQSound=0;
unsigned char writeES5506=0;
for (int i=0; i<song.systemLen; i++) {
willExport[i]=false;
@ -832,10 +832,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
if (!hasSegaPCM) {
hasSegaPCM=4000000;
willExport[i]=true;
writeSegaPCM=true;
writeSegaPCM=1;
} else if (!(hasSegaPCM&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
writeSegaPCM=2;
hasSegaPCM|=0x40000000;
howManyChips++;
}
@ -844,10 +845,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
if (!hasX1) {
hasX1=disCont[i].dispatch->chipClock;
willExport[i]=true;
writeX1010=true;
writeX1010=1;
} else if (!(hasX1&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
writeX1010=2;
hasX1|=0x40000000;
howManyChips++;
}
@ -861,10 +863,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
if (!hasOPNB) {
hasOPNB=disCont[i].dispatch->chipClock;
willExport[i]=true;
writeADPCM=true;
writeADPCM=1;
} else if (!(hasOPNB&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
writeADPCM=2;
hasOPNB|=0x40000000;
howManyChips++;
}
@ -966,10 +969,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
// not be able to handle the 64kb sample bank trick
hasQSound=disCont[i].dispatch->chipClock;
willExport[i]=true;
writeQSound=true;
writeQSound=1;
} else if (!(hasQSound&0x40000000)) {
isSecond[i]=true;
willExport[i]=false;
writeQSound=2;
addWarning("dual QSound is not supported by the VGM format");
}
break;
@ -993,11 +997,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
// VGM identifies ES5506 if highest bit sets, otherwise ES5505
hasES5505=0x80000000|disCont[i].dispatch->chipClock;
willExport[i]=true;
writeES5506=true;
writeES5506=1;
} else if (!(hasES5505&0x40000000)) {
isSecond[i]=true;
willExport[i]=false;
hasES5505|=0xc0000000;
writeES5506=2;
howManyChips++;
}
break;
@ -1253,7 +1258,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
}
}
if (writeSegaPCM) {
if (writeSegaPCM>0) {
unsigned char* pcmMem=new unsigned char[16777216];
size_t memPos=0;
@ -1285,70 +1290,82 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
if (memPos>=16777216) break;
}
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x80);
w->writeI(memPos+8);
w->writeI(memPos);
w->writeI(0);
w->write(pcmMem,memPos);
for (unsigned char i=0; i<writeSegaPCM; i++) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x80);
w->writeI((memPos+8)|(i*0x80000000));
w->writeI(memPos);
w->writeI(0);
w->write(pcmMem,memPos);
}
delete[] pcmMem;
}
if (writeADPCM && adpcmAMemLen>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x82);
w->writeI(adpcmAMemLen+8);
w->writeI(adpcmAMemLen);
w->writeI(0);
w->write(adpcmAMem,adpcmAMemLen);
if (adpcmAMemLen>0) {
for (unsigned char i=0; i<writeADPCM; i++) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x82);
w->writeI((adpcmAMemLen+8)|(i*0x80000000));
w->writeI(adpcmAMemLen);
w->writeI(0);
w->write(adpcmAMem,adpcmAMemLen);
}
}
if (writeADPCM && adpcmBMemLen>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x83);
w->writeI(adpcmBMemLen+8);
w->writeI(adpcmBMemLen);
w->writeI(0);
w->write(adpcmBMem,adpcmBMemLen);
if (adpcmBMemLen>0) {
for (unsigned char i=0; i<writeADPCM; i++) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x83);
w->writeI((adpcmBMemLen+8)|(i*0x80000000));
w->writeI(adpcmBMemLen);
w->writeI(0);
w->write(adpcmBMem,adpcmBMemLen);
}
}
if (writeQSound && qsoundMemLen>0) {
if (qsoundMemLen>0) {
// always write a whole bank
unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff);
if (blockSize > 0x1000000) {
blockSize = 0x1000000;
}
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x8F);
w->writeI(blockSize+8);
w->writeI(0x1000000);
w->writeI(0);
w->write(qsoundMem,blockSize);
for (unsigned char i=0; i<writeQSound; i++) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x8F);
w->writeI((blockSize+8)|(i*0x80000000));
w->writeI(0x1000000);
w->writeI(0);
w->write(qsoundMem,blockSize);
}
}
if (writeX1010 && x1_010MemLen>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x91);
w->writeI(x1_010MemLen+8);
w->writeI(x1_010MemLen);
w->writeI(0);
w->write(x1_010Mem,x1_010MemLen);
if (x1_010MemLen>0) {
for (unsigned char i=0; i<writeX1010; i++) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x91);
w->writeI((x1_010MemLen+8)|(i*0x80000000));
w->writeI(x1_010MemLen);
w->writeI(0);
w->write(x1_010Mem,x1_010MemLen);
}
}
if (writeES5506 && es5506MemLen>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x8F);
w->writeI(es5506MemLen+8);
w->writeI(es5506MemLen);
w->writeI(0);
w->write(es5506Mem,es5506MemLen);
if (es5506MemLen>0) {
for (unsigned char i=0; i<writeES5506; i++) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x8F);
w->writeI((es5506MemLen+8)|(i*0x80000000));
w->writeI(es5506MemLen);
w->writeI(0);
w->write(es5506Mem,es5506MemLen);
}
}
// initialize streams
@ -1447,7 +1464,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
writeLoop=true;
}
}
if (nextTick() || !playing) {
if (nextTick(false,true) || !playing) {
done=true;
if (!loop) {
for (int i=0; i<song.systemLen; i++) {

View file

@ -30,8 +30,11 @@ bool DivWaveSynth::activeChanged() {
}
bool DivWaveSynth::tick() {
if (--subDivCounter>0) return false;
bool updated=first;
first=false;
subDivCounter=e->tickMult;
if (!state.enabled) return updated;
if (width<1) return false;
@ -167,6 +170,7 @@ void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) {
pos=0;
stage=0;
divCounter=1+state.rateDivider;
subDivCounter=0;
first=true;
changeWave1(state.wave1);

View file

@ -28,7 +28,7 @@ class DivEngine;
class DivWaveSynth {
DivEngine* e;
DivInstrumentWaveSynth state;
int pos, stage, divCounter, width, height;
int pos, stage, divCounter, width, height, subDivCounter;
bool first, activeChangedB;
unsigned char wave1[256];
unsigned char wave2[256];
@ -78,6 +78,7 @@ class DivWaveSynth {
divCounter(0),
width(32),
height(31),
subDivCounter(0),
first(false),
activeChangedB(false) {
memset(wave1,0,256);

View file

@ -214,6 +214,8 @@ void FurnaceGUI::drawAbout() {
while (aboutHue>1) aboutHue--;
while (aboutSin>=2400) aboutSin-=2400;
if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20;
WAKE_UP;
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT;
ImGui::End();

View file

@ -166,6 +166,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
ImGui::Checkbox("New SegaPCM features (macros and better panning)",&e->song.newSegaPCM);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6");
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS;
ImGui::End();

View file

@ -72,130 +72,136 @@ void FurnaceGUI::drawInsList() {
ImGui::Indent();
}
for (int i=0; i<(int)e->song.ins.size(); i++) {
DivInstrument* ins=e->song.ins[i];
String name;
switch (ins->type) {
case DIV_INS_FM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_STD:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_GB:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]);
name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_C64:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AMIGA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]);
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_PCE:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]);
name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AY8930:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_TIA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SAA1099:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VIC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_PET:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]);
name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VRC6:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VRC6_SAW:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPLL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_FDS:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]);
name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VBOY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]);
name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_N163:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]);
name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SCC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]);
name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPZ:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_POKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_BEEPER:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]);
name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SWAN:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]);
name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_MIKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VERA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_X1_010:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_ES5506:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]);
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i);
break;
for (int i=-1; i<(int)e->song.ins.size(); i++) {
String name=ICON_FA_CIRCLE_O " - None -";
const char* insType="Bug!";
if (i>=0) {
DivInstrument* ins=e->song.ins[i];
insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type];
switch (ins->type) {
case DIV_INS_FM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_STD:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_GB:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]);
name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_C64:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AMIGA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]);
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_PCE:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]);
name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_AY8930:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_TIA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SAA1099:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VIC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_PET:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]);
name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VRC6:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VRC6_SAW:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPLL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_FDS:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]);
name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VBOY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]);
name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_N163:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]);
name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SCC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]);
name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_OPZ:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]);
name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_POKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_BEEPER:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]);
name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_SWAN:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]);
name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_MIKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_VERA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_X1_010:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]);
name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i);
break;
case DIV_INS_ES5506:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]);
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i);
break;
}
} else {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(name.c_str(),curIns==i)) {
if (ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i))) {
curIns=i;
}
if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) {
@ -203,8 +209,8 @@ void FurnaceGUI::drawInsList() {
curIns=i;
}
ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]);
if (ImGui::IsItemHovered() && i>=0) {
ImGui::SetTooltip("%s",insType);
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
insEditOpen=true;
nextWindow=GUI_WINDOW_INS_EDIT;

View file

@ -39,6 +39,7 @@
#include "../engine/platform/x1_010.h"
#include "../engine/platform/n163.h"
#include "../engine/platform/vrc6.h"
#include "../engine/platform/es5506.h"
#include "../engine/platform/dummy.h"
#define GENESIS_DEBUG \
@ -333,6 +334,66 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::TextColored(ch->furnaceDac?colorOn:colorOff,">> FurnaceDAC");
break;
}
case DIV_SYSTEM_ES5506: {
DivPlatformES5506::Channel* ch=(DivPlatformES5506::Channel*)data;
ImGui::Text("> ES5506");
ImGui::Text("* freq: %.4x",ch->freq);
ImGui::Text(" - base: %d",ch->baseFreq);
ImGui::Text(" - pitch: %d",ch->pitch);
ImGui::Text("- note: %d",ch->note);
ImGui::Text("- wave: %d",ch->wave);
ImGui::Text("- sample: %d",ch->sample);
ImGui::Text("- ins: %d",ch->ins);
ImGui::Text("* PCM:");
ImGui::Text(" - index: %d",ch->pcm.index);
ImGui::Text(" - freqOffs: %.6f",ch->pcm.freqOffs);
ImGui::Text(" - bank: %.2x",ch->pcm.bank);
ImGui::Text(" - start: %.8x",ch->pcm.start);
ImGui::Text(" - end: %.8x",ch->pcm.end);
ImGui::Text(" - length: %.8x",ch->pcm.length);
ImGui::Text(" - loopStart: %.8x",ch->pcm.loopStart);
ImGui::Text(" - loopEnd: %.8x",ch->pcm.loopEnd);
ImGui::Text(" - loopMode: %d",ch->pcm.loopMode);
ImGui::Text("* Filter:");
ImGui::Text(" - Mode: %d",ch->filter.mode);
ImGui::Text(" - K1: %.4x",ch->filter.k1);
ImGui::Text(" - K2: %.4x",ch->filter.k2);
ImGui::Text("* Envelope:");
ImGui::Text(" - EnvCount: %.3x",ch->envelope.ecount);
ImGui::Text(" - LVRamp: %d",ch->envelope.lVRamp);
ImGui::Text(" - RVRamp: %d",ch->envelope.rVRamp);
ImGui::Text(" - K1Ramp: %d",ch->envelope.k1Ramp);
ImGui::Text(" - K2Ramp: %d",ch->envelope.k2Ramp);
ImGui::Text("- vol: %.2x",ch->vol);
ImGui::Text("- LVol: %.2x",ch->lVol);
ImGui::Text("- RVol: %.2x",ch->rVol);
ImGui::Text("- outVol: %.2x",ch->outVol);
ImGui::Text("- outLVol: %.2x",ch->outLVol);
ImGui::Text("- outRVol: %.2x",ch->outRVol);
ImGui::Text("- ResLVol: %.2x",ch->resLVol);
ImGui::Text("- ResRVol: %.2x",ch->resRVol);
ImGui::TextColored(ch->active?colorOn:colorOff,">> Active");
ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged");
ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged");
ImGui::TextColored(ch->volChanged?colorOn:colorOff,">> VolChanged");
ImGui::TextColored(ch->filterChanged.mode?colorOn:colorOff,">> FilterModeChanged");
ImGui::TextColored(ch->filterChanged.k1?colorOn:colorOff,">> FilterK1Changed");
ImGui::TextColored(ch->filterChanged.k2?colorOn:colorOff,">> FilterK2Changed");
ImGui::TextColored(ch->envChanged.ecount?colorOn:colorOff,">> EnvECountChanged");
ImGui::TextColored(ch->envChanged.lVRamp?colorOn:colorOff,">> EnvLVRampChanged");
ImGui::TextColored(ch->envChanged.rVRamp?colorOn:colorOff,">> EnvRVRampChanged");
ImGui::TextColored(ch->envChanged.k1Ramp?colorOn:colorOff,">> EnvK1RampChanged");
ImGui::TextColored(ch->envChanged.k2Ramp?colorOn:colorOff,">> EnvK2RampChanged");
ImGui::TextColored(ch->keyOn?colorOn:colorOff,">> KeyOn");
ImGui::TextColored(ch->keyOff?colorOn:colorOff,">> KeyOff");
ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta");
ImGui::TextColored(ch->useWave?colorOn:colorOff,">> UseWave");
ImGui::TextColored(ch->isReverseLoop?colorOn:colorOff,">> IsReverseLoop");
ImGui::TextColored(ch->pcm.reversed?colorOn:colorOff,">> PCMReversed");
ImGui::TextColored(ch->envelope.k1Slow?colorOn:colorOff,">> EnvK1Slow");
ImGui::TextColored(ch->envelope.k2Slow?colorOn:colorOff,">> EnvK2Slow");
break;
}
default:
ImGui::Text("Unknown system! Help!");
break;

Some files were not shown because too many files have changed in this diff Show more