dev240 - Merge branch 'inf2' - READ

this introduces several major changes to Furnace. I hope this message explains everything.

**File Format Changes**

a new song info header (`INF2`) has been introduced, which:
- cleans up the mess I made when adding sub-songs to Furnace
- allows better forward compatibility and extensibility
- uses 16-bit chip IDs
- moves compatibility flags to another block and is stored as a DivConfig
- stores channel count in the file, allowing chips with dynamic channel count (e.g. Namco 163)

a new sub-song data block has been introduced as well.

check out papers/format.md for information.

**Furnace Changes**

TimeBase ("Divider" in the UI) has been REMOVED. it was a DefleMask leftover.
to compensate, the speeds are now 16-bit. older songs will have their speeds converted, but this may fail if you use grooves or change speed mid-song.

dynamic channel count support has been added. the following chips are currently supported:
- Namco 163 (1-8 channels)
- ES5506 (5-32 channels)
- Generic PCM DAC (1-128 channels, with software mixing!)

channel colors have been added (thanks Eknous!).

SegaPCM (compatible 5-channel mode) and Neo Geo CD have been REMOVED. when loading previous files, including .dmf ones, these will have compatible SegaPCM and Neo Geo CD chips converted to normal SegaPCM and YM2610 respectively.
their channel count remains unaltered though. you can fix this by going into the chip manager and clicking the button next to "irregular channel count" in each chip config section.

**Code Changes**

a couple refactors have been made for the sake of code cleanliness and depending less on DivEngine...

=> Chip Channel Count

two new values, adjacent to the channel count, have been added to DivSysDef.
these specify the minimum and maximum channel count for a chip.

if your chip doesn't require dynamic channels, feel free to set these to the channel count.

for example, here's an old definition:

```
sysDefs[DIV_SYSTEM_NES]=new DivSysDef(
  _("NES (Ricoh 2A03)"), NULL, 0x06, 0x06, 5 /* channel count*/, false, true, 0x161, false, (1U<<DIV_SAMPLE_DEPTH_1BIT_DPCM)|(1U<<DIV_SAMPLE_DEPTH_8BIT), 0, 0,
  ...
```

and here's how it looks now:

```
sysDefs[DIV_SYSTEM_NES]=new DivSysDef(
  _("NES (Ricoh 2A03)"), NULL, 0x06, 0x06, 5 /* nominal channel count*/, 5 /* min channels */, 5 /* max channels */,
  false, true, 0x161, false, (1U<<DIV_SAMPLE_DEPTH_1BIT_DPCM)|(1U<<DIV_SAMPLE_DEPTH_8BIT), 0, 0,
  ...
```

=> New Channel Definition

a new way to define chip channels has been introduced, replacing the old one.
it looks cleaner and is more flexible (even supporting dynamic channel count).

it works by defining a function in the chip definition, which returns a DivChanDef with channel information (name, short name, type and instrument type(s)).
alternatively, a list can be provided in the DivChanDefFunc() constructor, in the event channels differ greatly and/or the number of channels is small.
for example, if your chip's channel definition code looked like this:

```
{_("Pulse 1"), _("Pulse 2"), _("Triangle"), _("Noise"), _("DPCM")}, // names
{"S1", "S2", "TR", "NO", "DMC"}, // short names
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM}, // types
{DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_NES}, // ins types
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, // secondary ins types
```

now they look like this:

```
DivChanDefFunc({
  DivChanDef(_("Pulse 1") , "S1" , DIV_CH_PULSE, DIV_INS_NES),
  DivChanDef(_("Pulse 2") , "S2" , DIV_CH_PULSE, DIV_INS_NES),
  DivChanDef(_("Triangle"), "TR" , DIV_CH_WAVE , DIV_INS_NES),
  DivChanDef(_("Noise")   , "NO" , DIV_CH_NOISE, DIV_INS_NES),
  DivChanDef(_("DPCM")    , "DMC", DIV_CH_PCM  , DIV_INS_NES, DIV_INS_AMIGA)
}),
```

some helper templates, such as stockChanDef and simpleChanDef also exist, which automatically map channel names and types regardless of count.
this is useful if all your channels happen to be named "Channel 1", "Channel 2" and so on, while sharing the same type and instrument type (this is the case for many sampler chips).

if you've been working on a custom chip, make sure to adapt your chip definitions.

=> More Chip Definition Changes

the DivSystem enum is now in src/engine/sysDef.h

channel definitions are cached in the DivSong.

=> Reducing Dependency on DivEngine

many functions have been moved away from DivEngine where possible. these include:

- recalcChans() (now on DivSong)
  - chans, sysOfChan, dispatchOfChan, dispatchChanOfChan and dispatchFirstChan have been moved to DivSong as well.
- asset dir storage/modification/check finctions (now on src/engine/assetDir.cpp)
- DivEngine::getSystemDef() is now a static function, so it can be called without a DivEngine (engine still has to initialize systems first!)

IMPORTANT: hasLoadedSomething is no longer set in recalcChans() because it is part of DivEngine. if you have import code, MAKE SURE TO SET hasLoadedSomething TO true AFTER SWITCHING THE SONG (song=ds).

=> Dynamic Channel Count

a new array called systemChans[] has been added to DivSong, allowing you to define the number of channels for each chip.
if you change this, make sure to call recalcChans() afterwards.

if you use dispatchChanOfChan[], treat `-1` as "no channel". this is set when the channel count is higher than the chip's maximum.

if you're working on import code, and are certain you won't use dynamic channel count, call DivSong::initDefaultSystemChans() before recalcChans(). this will set up default channel counts for you.
otherwise, set systemChans[] appropriately.
This commit is contained in:
tildearrow 2025-11-21 11:26:27 -05:00
commit 6ad8a0893b
131 changed files with 5673 additions and 3997 deletions

152
src/engine/assetDir.cpp Normal file
View file

@ -0,0 +1,152 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2025 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "assetDir.h"
#include "../ta-log.h"
void moveAsset(std::vector<DivAssetDir>& dir, int before, int after) {
if (before<0 || after<0) return;
for (DivAssetDir& i: dir) {
for (size_t j=0; j<i.entries.size(); j++) {
// erase matching entry
if (i.entries[j]==before) {
i.entries[j]=after;
} else if (i.entries[j]==after) {
i.entries[j]=before;
}
}
}
}
void removeAsset(std::vector<DivAssetDir>& dir, int entry) {
if (entry<0) return;
for (DivAssetDir& i: dir) {
for (size_t j=0; j<i.entries.size(); j++) {
// erase matching entry
if (i.entries[j]==entry) {
i.entries.erase(i.entries.begin()+j);
j--;
} else if (i.entries[j]>entry) {
i.entries[j]--;
}
}
}
}
void checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries) {
bool* inAssetDir=new bool[entries];
memset(inAssetDir,0,entries*sizeof(bool));
for (DivAssetDir& i: dir) {
for (size_t j=0; j<i.entries.size(); j++) {
// erase invalid entry
if (i.entries[j]<0 || i.entries[j]>=(int)entries) {
i.entries.erase(i.entries.begin()+j);
j--;
continue;
}
// erase duplicate entry
if (inAssetDir[i.entries[j]]) {
i.entries.erase(i.entries.begin()+j);
j--;
continue;
}
// mark entry as present
inAssetDir[i.entries[j]]=true;
}
}
// get unsorted directory
DivAssetDir* unsortedDir=NULL;
for (DivAssetDir& i: dir) {
if (i.name.empty()) {
unsortedDir=&i;
break;
}
}
// add missing items to unsorted directory
for (size_t i=0; i<entries; i++) {
if (!inAssetDir[i]) {
// create unsorted directory if it doesn't exist
if (unsortedDir==NULL) {
dir.push_back(DivAssetDir(""));
unsortedDir=&(*dir.rbegin());
}
unsortedDir->entries.push_back(i);
}
}
delete[] inAssetDir;
}
void putAssetDirData(SafeWriter* w, std::vector<DivAssetDir>& dir) {
size_t blockStartSeek, blockEndSeek;
w->write("ADIR",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeI(dir.size());
for (DivAssetDir& i: dir) {
w->writeString(i.name,false);
w->writeS(i.entries.size());
for (int j: i.entries) {
w->writeC(j);
}
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END);
}
DivDataErrors readAssetDirData(SafeReader& reader, std::vector<DivAssetDir>& dir) {
char magic[4];
reader.read(magic,4);
if (memcmp(magic,"ADIR",4)!=0) {
logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]);
return DIV_DATA_INVALID_HEADER;
}
reader.readI(); // reserved
unsigned int numDirs=reader.readI();
dir.reserve(numDirs);
for (unsigned int i=0; i<numDirs; i++) {
DivAssetDir d;
d.name=reader.readString();
unsigned short numEntries=reader.readS();
d.entries.reserve(numEntries);
for (unsigned short j=0; j<numEntries; j++) {
d.entries.push_back(((unsigned char)reader.readC()));
}
dir.push_back(d);
}
return DIV_DATA_SUCCESS;
}

52
src/engine/assetDir.h Normal file
View file

@ -0,0 +1,52 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2025 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _ASSET_DIR_H
#define _ASSET_DIR_H
#include "../ta-utils.h"
#include <vector>
#include "dataErrors.h"
#include "safeReader.h"
#include "safeWriter.h"
struct DivAssetDir {
String name;
std::vector<int> entries;
DivAssetDir():
name("New Directory") {}
DivAssetDir(String n):
name(n) {}
};
// check whether an asset directory is complete (UNSAFE)
void checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries);
// move an asset
void moveAsset(std::vector<DivAssetDir>& dir, int before, int after);
// remove an asset
void removeAsset(std::vector<DivAssetDir>& dir, int entry);
// read/write asset dir
void putAssetDirData(SafeWriter* w, std::vector<DivAssetDir>& dir);
DivDataErrors readAssetDirData(SafeReader& reader, std::vector<DivAssetDir>& dir);
#endif

View file

@ -596,7 +596,7 @@ bool DivCSPlayer::tick() {
}
if (chan[i].portaSpeed) {
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(e->song.linearPitch?e->song.pitchSlideSpeed:1),chan[i].portaTarget));
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(e->song.compatFlags.linearPitch?e->song.compatFlags.pitchSlideSpeed:1),chan[i].portaTarget));
}
if (chan[i].arp && !chan[i].portaSpeed) {
if (chan[i].arpTicks==0) {
@ -704,7 +704,12 @@ bool DivCSPlayer::init() {
// initialize state
for (int i=0; i<e->getTotalChannelCount(); i++) {
chan[i].volMax=(e->getDispatch(e->dispatchOfChan[i])->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,e->dispatchChanOfChan[i]))<<8)|0xff;
if (e->song.dispatchChanOfChan[i]>=0) {
chan[i].volMax=(e->getDispatch(e->song.dispatchOfChan[i])->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,e->song.dispatchChanOfChan[i]))<<8)|0xff;
} else {
// fallback
chan[i].volMax=0xfff;
}
chan[i].volume=chan[i].volMax;
}

View file

@ -1303,7 +1303,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
// write header
w->write("FCS",4);
w->writeS(chans);
w->writeS(song.chans);
// flags
w->writeC((options.longPointers?1:0)|(options.bigEndian?2:0));
// reserved
@ -1313,7 +1313,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
w->writeC(0);
}
// offsets
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
chanStream[i]=new SafeWriter;
chanStream[i]->init();
if (options.longPointers) {
@ -1323,7 +1323,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
}
}
// max stack sizes
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
w->writeC(0);
}
@ -1338,7 +1338,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
// PASS 0: play the song and log channel command streams
// song beginning marker
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
chanStream[i]->writeC(0xd0);
chanStream[i]->writeC(i);
chanStream[i]->writeC(0x00);
@ -1350,7 +1350,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
chanStream[i]->writeC(0x00);
}
while (!done) {
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
tickPos[i].push_back(chanStream[i]->tell());
}
if (loopTick==-1) {
@ -1359,7 +1359,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
logI("loop is on tick %d",tick);
loopTick=tick;
// loop marker
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
chanStream[i]->writeC(0xd0);
chanStream[i]->writeC(i);
chanStream[i]->writeC(0x00);
@ -1410,7 +1410,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
}
}
cmdStream.clear();
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
chanStream[i]->writeC(0xde);
// padding
chanStream[i]->writeC(0x00);
@ -1424,7 +1424,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
tick++;
}
if (!playing || loopTick<0) {
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
chanStream[i]->writeC(0xdf);
// padding
chanStream[i]->writeC(0x00);
@ -1436,7 +1436,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
chanStream[i]->writeC(0x00);
}
} else {
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
if ((int)tickPos[i].size()>loopTick) {
chanStream[i]->writeC(0xda);
chanStream[i]->writeI(tickPos[i][loopTick]);
@ -1494,7 +1494,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
}
// set preset instruments
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
for (size_t i=0; i<chanStream[h]->size(); i+=8) {
if (buf[i]==0xb8) {
@ -1536,7 +1536,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
}
// set preset volumes
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
for (size_t i=0; i<chanStream[h]->size(); i+=8) {
if (buf[i]==0xc7) {
@ -1578,7 +1578,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
}
// set speed dial commands
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
for (size_t i=0; i<chanStream[h]->size(); i+=8) {
if (buf[i]==0xd7) {
@ -1601,7 +1601,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
// PASS 2: condense delays
if (!options.noDelayCondense) {
// calculate delay usage
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
int delayCount=0;
for (size_t i=0; i<chanStream[h]->size(); i+=8) {
@ -1639,7 +1639,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
}
// condense delays
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
int delayPos=-1;
int delayCount=0;
@ -1693,7 +1693,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
// PASS 3: note off + one-tick wait
// optimize one-tick gaps sometimes used in songs
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
if (chanStream[h]->size()<8) continue;
for (size_t i=0; i<chanStream[h]->size()-8; i+=8) {
@ -1714,12 +1714,12 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
// PASS 4: remove nop's
// this includes modifying call addresses to compensate
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
chanStream[h]=stripNops(chanStream[h]);
}
// PASS 5: put all channels together
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
chanStreamOff[i]=globalStream->tell();
logI("- %d: off %x size %ld",i,chanStreamOff[i],chanStream[i]->size());
reloc8(chanStream[i]->getFinalBuf(),chanStream[i]->size(),0,globalStream->tell());
@ -1798,7 +1798,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
// also find new offsets
globalStream=stripNopsPacked(globalStream,sortedCmd,chanStreamOff);
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
chanStreamOff[h]+=w->tell();
}
@ -1807,7 +1807,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
w->write(globalStream->getFinalBuf(),globalStream->size());
// calculate max stack sizes
for (int h=0; h<chans; h++) {
for (int h=0; h<song.chans; h++) {
std::stack<unsigned int> callStack;
unsigned int maxStackSize=0;
unsigned char* buf=w->getFinalBuf();
@ -1866,7 +1866,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
delete globalStream;
w->seek(40,SEEK_SET);
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
if (options.longPointers) {
if (options.bigEndian) {
w->writeI_BE(chanStreamOff[i]);
@ -1884,7 +1884,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
logD("maximum stack sizes:");
unsigned int cumulativeStackSize=0;
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
w->writeC(chanStackSize[i]);
logD("- %d: %d",i,chanStackSize[i]);
cumulativeStackSize+=chanStackSize[i];

View file

@ -1082,10 +1082,10 @@ class DivDispatch {
#define NOTE_FNUM_BLOCK(x,bits,blk) parent->calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits,blk)
// this is for volume scaling calculation.
#define VOL_SCALE_LINEAR(x,y,range) ((parent->song.ceilVolumeScaling)?((((x)*(y))+(range-1))/(range)):(((x)*(y))/(range)))
#define VOL_SCALE_LINEAR(x,y,range) ((parent->song.compatFlags.ceilVolumeScaling)?((((x)*(y))+(range-1))/(range)):(((x)*(y))/(range)))
#define VOL_SCALE_LOG(x,y,range) (CLAMP(((x)+(y))-(range),0,(range)))
#define VOL_SCALE_LINEAR_BROKEN(x,y,range) ((parent->song.newVolumeScaling)?(VOL_SCALE_LINEAR(x,y,range)):(VOL_SCALE_LOG(x,y,range)))
#define VOL_SCALE_LOG_BROKEN(x,y,range) ((parent->song.newVolumeScaling)?(VOL_SCALE_LOG(x,y,range)):(VOL_SCALE_LINEAR(x,y,range)))
#define VOL_SCALE_LINEAR_BROKEN(x,y,range) ((parent->song.compatFlags.newVolumeScaling)?(VOL_SCALE_LINEAR(x,y,range)):(VOL_SCALE_LOG(x,y,range)))
#define VOL_SCALE_LOG_BROKEN(x,y,range) ((parent->song.compatFlags.newVolumeScaling)?(VOL_SCALE_LOG(x,y,range)):(VOL_SCALE_LINEAR(x,y,range)))
// these are here for convenience.
// it is encouraged to use these, since you get an exact value this way.
@ -1098,7 +1098,7 @@ class DivDispatch {
if ((x)<(xMin)) (x)=(xMin); \
if ((x)>(xMax)) (x)=(xMax);
#define NEW_ARP_STRAT (parent->song.linearPitch && !parent->song.oldArpStrategy)
#define NEW_ARP_STRAT (parent->song.compatFlags.linearPitch && !parent->song.compatFlags.oldArpStrategy)
#define HACKY_LEGATO_MESS chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode && !NEW_ARP_STRAT
#endif

View file

@ -345,7 +345,6 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
((DivPlatformArcade*)dispatch)->setYMFM(eng->getConfInt("arcadeCore",0)==0);
}
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_FULL:
dispatch=new DivPlatformYM2610;
if (isRender) {
@ -354,7 +353,6 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
((DivPlatformYM2610*)dispatch)->setCombo(eng->getConfInt("opnbCore",1));
}
break;
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL_EXT:
dispatch=new DivPlatformYM2610Ext;
if (isRender) {
@ -599,7 +597,6 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
dispatch=new DivPlatformQSound;
break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
dispatch=new DivPlatformSegaPCM;
break;
case DIV_SYSTEM_X1_010:

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,7 @@
#include "export.h"
#include "dataErrors.h"
#include "safeWriter.h"
#include "sysDef.h"
#include "cmdStream.h"
#include "filePlayer.h"
#include "../audio/taAudio.h"
@ -55,8 +56,8 @@ class DivWorkPool;
#define DIV_UNSTABLE
#define DIV_VERSION "dev239"
#define DIV_ENGINE_VERSION 239
#define DIV_VERSION "dev240"
#define DIV_ENGINE_VERSION 240
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -332,129 +333,6 @@ struct DivEffectContainer {
}
};
typedef int EffectValConversion(unsigned char,unsigned char);
struct EffectHandler {
DivDispatchCmds dispatchCmd;
const char* description;
EffectValConversion* val;
EffectValConversion* val2;
EffectHandler(
DivDispatchCmds dispatchCmd_,
const char* description_,
EffectValConversion val_=NULL,
EffectValConversion val2_=NULL
):
dispatchCmd(dispatchCmd_),
description(description_),
val(val_),
val2(val2_) {}
};
struct DivDoNotHandleEffect {
};
typedef std::unordered_map<unsigned char,const EffectHandler> EffectHandlerMap;
struct DivSysDef {
const char* name;
const char* nameJ;
const char* description;
unsigned char id;
unsigned char id_DMF;
int channels;
bool isFM, isSTD, isCompound;
// width 0: variable
// height 0: no wavetable support
unsigned short waveWidth, waveHeight;
unsigned int vgmVersion;
unsigned int sampleFormatMask;
const char* chanNames[DIV_MAX_CHANS];
const char* chanShortNames[DIV_MAX_CHANS];
int chanTypes[DIV_MAX_CHANS];
// 0: primary
// 1: alternate (usually PCM)
DivInstrumentType chanInsType[DIV_MAX_CHANS][2];
const EffectHandlerMap effectHandlers;
const EffectHandlerMap postEffectHandlers;
const EffectHandlerMap preEffectHandlers;
DivSysDef(
const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans,
bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, unsigned int formatMask, unsigned short waveWid, unsigned short waveHei,
const char* desc,
std::initializer_list<const char*> chNames,
std::initializer_list<const char*> chShortNames,
std::initializer_list<int> chTypes,
std::initializer_list<DivInstrumentType> chInsType1,
std::initializer_list<DivInstrumentType> chInsType2={},
const EffectHandlerMap fxHandlers_={},
const EffectHandlerMap postFxHandlers_={},
const EffectHandlerMap preFxHandlers_={}):
name(sysName),
nameJ(sysNameJ),
description(desc),
id(fileID),
id_DMF(fileID_DMF),
channels(chans),
isFM(isFMChip),
isSTD(isSTDChip),
isCompound(compound),
waveWidth(waveWid),
waveHeight(waveHei),
vgmVersion(vgmVer),
sampleFormatMask(formatMask),
effectHandlers(fxHandlers_),
postEffectHandlers(postFxHandlers_),
preEffectHandlers(preFxHandlers_) {
memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*));
memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*));
memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int));
for (int i=0; i<DIV_MAX_CHANS; i++) {
chanInsType[i][0]=DIV_INS_NULL;
chanInsType[i][1]=DIV_INS_NULL;
}
int index=0;
for (const char* i: chNames) {
chanNames[index++]=i;
if (index>=DIV_MAX_CHANS) break;
}
index=0;
for (const char* i: chShortNames) {
chanShortNames[index++]=i;
if (index>=DIV_MAX_CHANS) break;
}
index=0;
for (int i: chTypes) {
chanTypes[index++]=i;
if (index>=DIV_MAX_CHANS) break;
}
index=0;
for (DivInstrumentType i: chInsType1) {
chanInsType[index++][0]=i;
if (index>=DIV_MAX_CHANS) break;
}
index=0;
for (DivInstrumentType i: chInsType2) {
chanInsType[index++][1]=i;
if (index>=DIV_MAX_CHANS) break;
}
}
};
enum DivChanTypes {
DIV_CH_FM=0,
DIV_CH_PULSE=1,
DIV_CH_NOISE=2,
DIV_CH_WAVE=3,
DIV_CH_PCM=4,
DIV_CH_OP=5
};
extern const char* cmdName[];
class DivEngine {
@ -463,7 +341,6 @@ class DivEngine {
TAAudioDesc want, got;
String exportPath;
std::thread* exportThread;
int chans;
bool configLoaded;
bool active;
bool lowQuality;
@ -546,7 +423,6 @@ class DivEngine {
std::vector<String> midiIns;
std::vector<String> midiOuts;
std::vector<DivCommand> cmdStream;
std::vector<DivInstrumentType> possibleInsTypes;
std::vector<DivEffectContainer> effectInst;
std::vector<int> curChanMask;
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
@ -625,7 +501,6 @@ class DivEngine {
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
bool perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal);
bool perSystemPreEffect(int ch, unsigned char effect, unsigned char effectVal);
void recalcChans();
void reset();
void playSub(bool preserveDrift, int goalRow=0);
void runMidiClock(int totalCycles=1);
@ -688,6 +563,7 @@ class DivEngine {
void copyChannel(int src, int dest);
void swapChannels(int src, int dest);
void stompChannel(int ch);
bool sysChanCountChange(int firstChan, int before, int after);
// recalculate patchbay (UNSAFE)
void recalcPatchbay();
@ -701,16 +577,6 @@ class DivEngine {
void swapSystemUnsafe(int src, int dest, bool preserveOrder=true);
// move an asset
void moveAsset(std::vector<DivAssetDir>& dir, int before, int after);
// remove an asset
void removeAsset(std::vector<DivAssetDir>& dir, int entry);
// read/write asset dir
void putAssetDirData(SafeWriter* w, std::vector<DivAssetDir>& dir);
DivDataErrors readAssetDirData(SafeReader& reader, std::vector<DivAssetDir>& dir);
// add every export method here
friend class DivROMExport;
friend class DivExportAmigaValidation;
@ -726,10 +592,6 @@ class DivEngine {
DivChannelData* curPat;
DivSubSong* curSubSong;
DivInstrument* tempIns;
DivSystem sysOfChan[DIV_MAX_CHANS];
int dispatchOfChan[DIV_MAX_CHANS];
int dispatchChanOfChan[DIV_MAX_CHANS];
int dispatchFirstChan[DIV_MAX_CHANS];
bool keyHit[DIV_MAX_CHANS];
float* oscBuf[DIV_MAX_OUTPUTS];
float oscSize;
@ -819,9 +681,6 @@ class DivEngine {
// convert old flags
static void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys);
// check whether an asset directory is complete (UNSAFE)
void checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries);
// benchmark (returns time in seconds)
double benchmarkPlayback();
double benchmarkSeek();
@ -966,7 +825,7 @@ class DivEngine {
const char* getSystemNameJ(DivSystem sys);
// get sys definition
const DivSysDef* getSystemDef(DivSystem sys);
static const DivSysDef* getSystemDef(DivSystem sys);
// get ROM export definition
const DivROMExportDef* getROMExportDef(DivROMExportOptions opt);
@ -1356,6 +1215,9 @@ class DivEngine {
// change system
bool changeSystem(int index, DivSystem which, bool preserveOrder=true);
// set system channel count
bool setSystemChans(int index, int ch, bool preserveOrder=true);
// add system
bool addSystem(DivSystem which);
@ -1469,7 +1331,6 @@ class DivEngine {
DivEngine():
output(NULL),
exportThread(NULL),
chans(0),
configLoaded(false),
active(false),
lowQuality(false),
@ -1604,10 +1465,6 @@ class DivEngine {
mu5ROM(NULL) {
memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool));
memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool));
memset(dispatchFirstChan,0,DIV_MAX_CHANS*sizeof(int));
memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(vibTable,0,64*sizeof(short));
memset(tremTable,0,128*sizeof(short));
memset(effectSlotMap,-1,4096*sizeof(short));

View file

@ -91,7 +91,7 @@ void DivExportAmigaValidation::run() {
size_t lastTick=0;
//bool writeLoop=false;
int loopPos=-1;
for (int i=0; i<e->chans; i++) {
for (int i=0; i<e->song.chans; i++) {
e->chan[i].wentThroughNote=false;
e->chan[i].goneThroughNote=false;
}

View file

@ -172,56 +172,56 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
// compatibility flags
if (!getConfInt("noDMFCompat",0)) {
ds.limitSlides=true;
ds.linearPitch=1;
ds.loopModality=0;
ds.properNoiseLayout=false;
ds.waveDutyIsVol=false;
ds.compatFlags.limitSlides=true;
ds.compatFlags.linearPitch=1;
ds.compatFlags.loopModality=0;
ds.compatFlags.properNoiseLayout=false;
ds.compatFlags.waveDutyIsVol=false;
// TODO: WHAT?! geodude.dmf fails when this is true
// but isn't that how Defle behaves???
ds.resetMacroOnPorta=false;
ds.legacyVolumeSlides=true;
ds.compatibleArpeggio=true;
ds.noteOffResetsSlides=true;
ds.targetResetsSlides=true;
ds.arpNonPorta=false;
ds.algMacroBehavior=false;
ds.brokenShortcutSlides=false;
ds.ignoreDuplicateSlides=true;
ds.brokenDACMode=true;
ds.oneTickCut=false;
ds.newInsTriggersInPorta=true;
ds.arp0Reset=true;
ds.brokenSpeedSel=true;
ds.noSlidesOnFirstTick=false;
ds.rowResetsArpPos=false;
ds.ignoreJumpAtEnd=true;
ds.buggyPortaAfterSlide=true;
ds.gbInsAffectsEnvelope=true;
ds.ignoreDACModeOutsideIntendedChannel=false;
ds.e1e2AlsoTakePriority=true;
ds.fbPortaPause=true;
ds.snDutyReset=true;
ds.oldOctaveBoundary=false;
ds.noOPN2Vol=true;
ds.newVolumeScaling=false;
ds.volMacroLinger=false;
ds.brokenOutVol=true;
ds.brokenOutVol2=true;
ds.e1e2StopOnSameNote=true;
ds.brokenPortaArp=false;
ds.snNoLowPeriods=true;
ds.disableSampleMacro=true;
ds.preNoteNoEffect=true;
ds.oldDPCM=true;
ds.delayBehavior=0;
ds.jumpTreatment=2;
ds.oldAlwaysSetVolume=true;
ds.compatFlags.resetMacroOnPorta=false;
ds.compatFlags.legacyVolumeSlides=true;
ds.compatFlags.compatibleArpeggio=true;
ds.compatFlags.noteOffResetsSlides=true;
ds.compatFlags.targetResetsSlides=true;
ds.compatFlags.arpNonPorta=false;
ds.compatFlags.algMacroBehavior=false;
ds.compatFlags.brokenShortcutSlides=false;
ds.compatFlags.ignoreDuplicateSlides=true;
ds.compatFlags.brokenDACMode=true;
ds.compatFlags.oneTickCut=false;
ds.compatFlags.newInsTriggersInPorta=true;
ds.compatFlags.arp0Reset=true;
ds.compatFlags.brokenSpeedSel=true;
ds.compatFlags.noSlidesOnFirstTick=false;
ds.compatFlags.rowResetsArpPos=false;
ds.compatFlags.ignoreJumpAtEnd=true;
ds.compatFlags.buggyPortaAfterSlide=true;
ds.compatFlags.gbInsAffectsEnvelope=true;
ds.compatFlags.ignoreDACModeOutsideIntendedChannel=false;
ds.compatFlags.e1e2AlsoTakePriority=true;
ds.compatFlags.fbPortaPause=true;
ds.compatFlags.snDutyReset=true;
ds.compatFlags.oldOctaveBoundary=false;
ds.compatFlags.noOPN2Vol=true;
ds.compatFlags.newVolumeScaling=false;
ds.compatFlags.volMacroLinger=false;
ds.compatFlags.brokenOutVol=true;
ds.compatFlags.brokenOutVol2=true;
ds.compatFlags.e1e2StopOnSameNote=true;
ds.compatFlags.brokenPortaArp=false;
ds.compatFlags.snNoLowPeriods=true;
ds.compatFlags.disableSampleMacro=true;
ds.compatFlags.preNoteNoEffect=true;
ds.compatFlags.oldDPCM=true;
ds.compatFlags.delayBehavior=0;
ds.compatFlags.jumpTreatment=2;
ds.compatFlags.oldAlwaysSetVolume=true;
// 1.1 compat flags
if (ds.version>24) {
ds.waveDutyIsVol=true;
ds.legacyVolumeSlides=false;
ds.compatFlags.waveDutyIsVol=true;
ds.compatFlags.legacyVolumeSlides=false;
}
// Neo Geo detune is caused by Defle running Neo Geo at the wrong clock.
@ -256,11 +256,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
bool customTempo=false;
ds.subsong[0]->timeBase=reader.readC();
unsigned char oldTimeBase=reader.readC();
ds.subsong[0]->speeds.len=2;
ds.subsong[0]->speeds.val[0]=reader.readC();
ds.subsong[0]->speeds.val[0]=(unsigned char)reader.readC();
if (ds.version>0x07) {
ds.subsong[0]->speeds.val[1]=reader.readC();
ds.subsong[0]->speeds.val[1]=(unsigned char)reader.readC();
bool pal=reader.readC();
ds.subsong[0]->hz=pal?60:50;
customTempo=reader.readC();
@ -317,7 +317,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
if (ds.system[0]==DIV_SYSTEM_YMU759) {
switch (ds.subsong[0]->timeBase) {
switch (oldTimeBase) {
case 0:
ds.subsong[0]->hz=248;
break;
@ -340,8 +340,10 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.subsong[0]->hz=248;
break;
}
ds.subsong[0]->timeBase=0;
addWarning("Yamaha YMU759 emulation is incomplete! please migrate your song to the OPL3 system.");
} else {
ds.subsong[0]->speeds.val[0]*=(oldTimeBase+1);
ds.subsong[0]->speeds.val[1]*=(oldTimeBase+1);
}
logV("%x",reader.tell());
@ -395,7 +397,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
if (ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) {
ins->type=DIV_INS_C64;
}
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT
if (ds.system[0]==DIV_SYSTEM_YM2610_CRAP || ds.system[0]==DIV_SYSTEM_YM2610_CRAP_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) {
if (!mode) {
@ -627,7 +629,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
// piece of crap offset by 1
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT) {
if (ds.system[0]==DIV_SYSTEM_YM2610_CRAP || ds.system[0]==DIV_SYSTEM_YM2610_CRAP_EXT) {
ins->std.waveMacro.val[j]++;
}
}
@ -1167,15 +1169,30 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.systemFlags[0].set("brokenPitch",true);
}
ds.initDefaultSystemChans();
// flatten 5-channel SegaPCM and Neo Geo CD
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_SEGAPCM_COMPAT) {
ds.system[i]=DIV_SYSTEM_SEGAPCM;
} else if (ds.system[i]==DIV_SYSTEM_YM2610_CRAP) {
ds.system[i]=DIV_SYSTEM_YM2610_FULL;
} else if (ds.system[i]==DIV_SYSTEM_YM2610_CRAP_EXT) {
ds.system[i]=DIV_SYSTEM_YM2610_FULL_EXT;
}
}
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
ds.recalcChans();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
// always convert to normal sample mode (I have no idea how will I do export)
convertLegacySampleMode();
saveLock.unlock();
@ -1205,6 +1222,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
lastError="invalid version to save in! this is a bug!";
return NULL;
}
int actualChans=song.chans;
// check whether system is compound
bool isFlat=false;
if (song.systemLen==2) {
@ -1214,8 +1232,10 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
if (song.system[0]==DIV_SYSTEM_YM2612_EXT && song.system[1]==DIV_SYSTEM_SMS) {
isFlat=true;
}
if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM_COMPAT) {
if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM) {
isFlat=true;
addWarning("only first 5 channels of SegaPCM.");
actualChans=13;
}
if (song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL) {
isFlat=true;
@ -1231,6 +1251,10 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
addWarning("your song will sound different. I am not going to bother adding further compatibility.");
}
}
if (song.system[0]==DIV_SYSTEM_YM2610_FULL || song.system[0]==DIV_SYSTEM_YM2610_FULL_EXT) {
addWarning("ADPCM-B not supported.");
actualChans--;
}
// fail if more than one system
if (!isFlat && song.systemLen!=1) {
logE("cannot save multiple systems in this format!");
@ -1289,7 +1313,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
lastError="maximum number of wavetables in .dmf is 64";
return NULL;
}
for (int i=0; i<chans; i++) {
for (int i=0; i<actualChans; i++) {
for (int j=0; j<curSubSong->ordersLen; j++) {
if (curOrders->ord[i][j]>0x7f) {
logE("order %d, %d is out of range (0-127)!",i,j);
@ -1316,7 +1340,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
} else if (song.system[0]==DIV_SYSTEM_YM2612_EXT && song.system[1]==DIV_SYSTEM_SMS) {
w->writeC(systemToFileDMF(DIV_SYSTEM_GENESIS_EXT));
sys=DIV_SYSTEM_GENESIS_EXT;
} else if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM_COMPAT) {
} else if (song.system[0]==DIV_SYSTEM_YM2151 && song.system[1]==DIV_SYSTEM_SEGAPCM) {
w->writeC(systemToFileDMF(DIV_SYSTEM_ARCADE));
sys=DIV_SYSTEM_ARCADE;
} else if (song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL) {
@ -1331,6 +1355,12 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
} else if (song.system[0]==DIV_SYSTEM_AY8910 && song.system[1]==DIV_SYSTEM_SCC) {
w->writeC(systemToFileDMF(DIV_SYSTEM_MSX2));
sys=DIV_SYSTEM_MSX2;
} else if (song.system[0]==DIV_SYSTEM_YM2610_FULL) {
w->writeC(systemToFileDMF(DIV_SYSTEM_YM2610_CRAP));
sys=DIV_SYSTEM_YM2610_CRAP;
} else if (song.system[0]==DIV_SYSTEM_YM2610_FULL_EXT) {
w->writeC(systemToFileDMF(DIV_SYSTEM_YM2610_CRAP_EXT));
sys=DIV_SYSTEM_YM2610_CRAP_EXT;
} else {
w->writeC(systemToFileDMF(song.system[0]));
sys=song.system[0];
@ -1344,7 +1374,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
int intHz=curSubSong->hz;
w->writeC(curSubSong->timeBase);
w->writeC(0);
w->writeC(curSubSong->speeds.val[0]);
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
w->writeC((intHz<=53)?0:1);
@ -1356,7 +1386,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeI(curSubSong->patLen);
w->writeC(curSubSong->ordersLen);
for (int i=0; i<chans; i++) {
for (int i=0; i<actualChans; i++) {
for (int j=0; j<curSubSong->ordersLen; j++) {
w->writeC(curOrders->ord[i][j]);
if (version>=25) {
@ -1425,8 +1455,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
case DIV_SYSTEM_PCE:
i->type=DIV_INS_PCE;
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
i->type=DIV_INS_AY;
break;
default:
@ -1578,7 +1608,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(realWaveMacroLen);
for (int j=0; j<realWaveMacroLen; j++) {
// piece of crap offset by 1
if (song.system[0]==DIV_SYSTEM_YM2610 || song.system[0]==DIV_SYSTEM_YM2610_EXT) {
if (song.system[0]==DIV_SYSTEM_YM2610_FULL || song.system[0]==DIV_SYSTEM_YM2610_FULL_EXT) {
w->writeI(i->std.waveMacro.val[j]-1);
} else {
w->writeI(i->std.waveMacro.val[j]);
@ -1642,7 +1672,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
bool relWarning=false;
for (int i=0; i<getChannelCount(sys); i++) {
for (int i=0; i<actualChans; i++) {
short note, octave;
w->writeC(curPat[i].effectCols);
@ -1671,13 +1701,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
alwaysConvert=true;
}
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_CRAP:
if (i>=7) {
convertSampleUsage=true;
alwaysConvert=true;
}
break;
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_CRAP_EXT:
if (i>=10) {
convertSampleUsage=true;
alwaysConvert=true;

View file

@ -147,12 +147,12 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
DivSong ds;
ds.tuning=436.0;
ds.version=DIV_VERSION_FC;
//ds.linearPitch=0;
//ds.pitchMacroIsLinear=false;
//ds.noSlidesOnFirstTick=true;
//ds.rowResetsArpPos=true;
ds.pitchSlideSpeed=8;
ds.ignoreJumpAtEnd=false;
//ds.compatFlags.linearPitch=0;
//ds.compatFlags.pitchMacroIsLinear=false;
//ds.compatFlags.noSlidesOnFirstTick=true;
//ds.compatFlags.rowResetsArpPos=true;
ds.compatFlags.pitchSlideSpeed=8;
ds.compatFlags.ignoreJumpAtEnd=false;
// load here
if (!reader.seek(0,SEEK_SET)) {
@ -664,13 +664,16 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.subsong[0]->optimizePatterns();
ds.subsong[0]->rearrangePatterns();
ds.initDefaultSystemChans();
ds.recalcChans();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {

View file

@ -495,7 +495,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
ds.subsong.clear();
ds.linearPitch = 0;
ds.compatFlags.linearPitch = 0;
unsigned int pal = 0;
@ -654,6 +654,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
int curr_chan = 0;
int map_ch = 0;
ds.systemChans[systemID]=5;
ds.system[systemID++] = DIV_SYSTEM_NES;
ds.systemFlags[0].set("resetSweep",true); // FamiTracker behavior
@ -668,6 +669,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
if (expansions & 1) {
ds.systemChans[systemID]=3;
ds.system[systemID++] = DIV_SYSTEM_VRC6;
for (int ch = 0; ch < 3; ch++) {
@ -685,6 +687,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
vrc6_saw_chan = map_ch - 1;
}
if (expansions & 8) {
ds.systemChans[systemID]=3;
ds.system[systemID++] = DIV_SYSTEM_MMC5;
for (int ch = 0; ch < (eft ? 3 : 2); ch++) {
@ -707,6 +710,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (expansions & 16) {
ds.system[systemID] = DIV_SYSTEM_N163;
ds.systemFlags[systemID].set("channels", (int)n163Chans - 1);
ds.systemChans[systemID]=CLAMP(n163Chans,1,8);
systemID++;
for (int ch = 0; ch < (int)n163Chans; ch++) {
@ -716,12 +720,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
map_ch++;
}
for (int ch = 0; ch < (8 - (int)n163Chans); ch++) {
/*for (int ch = 0; ch < (8 - (int)n163Chans); ch++) {
map_channels[curr_chan] = map_ch; // do not populate and skip the missing N163 channels!
map_ch++;
}
}*/
}
if (expansions & 4) {
ds.systemChans[systemID]=1;
ds.system[systemID++] = DIV_SYSTEM_FDS;
map_channels[curr_chan] = map_ch;
@ -730,6 +735,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
map_ch++;
}
if (expansions & 2) {
ds.systemChans[systemID]=6;
ds.system[systemID++] = DIV_SYSTEM_VRC7;
for (int ch = 0; ch < 6; ch++) {
@ -741,6 +747,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
if (expansions & 32) {
ds.system[systemID] = DIV_SYSTEM_AY8910;
ds.systemChans[systemID]=3;
ds.systemFlags[systemID++].set("chipType", 2); // Sunsoft 5B
for (int ch = 0; ch < 3; ch++) {
@ -751,6 +758,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
}
if (expansions & 64) {
ds.systemChans[systemID]=3;
ds.system[systemID++] = DIV_SYSTEM_AY8930;
for (int ch = 0; ch < 3; ch++) {
@ -761,6 +769,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
}
if (expansions & 128) {
ds.systemChans[systemID]=6;
ds.system[systemID++] = DIV_SYSTEM_SAA1099;
for (int ch = 0; ch < 6; ch++) {
@ -770,6 +779,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
}
if (expansions & 256) {
ds.systemChans[systemID]=5;
ds.system[systemID++] = DIV_SYSTEM_5E01;
for (int ch = 0; ch < 5; ch++) {
@ -779,6 +789,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
}
if (expansions & 512) {
ds.systemChans[systemID]=3;
ds.system[systemID++] = DIV_SYSTEM_C64_6581;
for (int ch = 0; ch < 3; ch++) {
@ -788,6 +799,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
}
if (expansions & 1024) {
ds.systemChans[systemID]=3;
ds.system[systemID++] = DIV_SYSTEM_C64_8580;
for (int ch = 0; ch < 3; ch++) {
@ -797,6 +809,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
}
if (expansions & 2048) {
ds.systemChans[systemID]=4;
ds.system[systemID++] = DIV_SYSTEM_POKEY;
for (int ch = 0; ch < 4; ch++) {
@ -817,13 +830,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
calcChans--; // no PCM channel for MMC5 in famitracker
}
calcChans += getChannelCount(ds.system[i]);
total_chans += getChannelCount(ds.system[i]);
if (ds.system[i] == DIV_SYSTEM_N163) {
calcChans -= getChannelCount(ds.system[i]);
calcChans += (int)n163Chans;
}
calcChans += ds.systemChans[i];
total_chans += ds.systemChans[i];
}
if (calcChans != tchans) {
// TODO: would ignore trigger CVE? too bad if so!
@ -2370,7 +2378,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
CHECK_BLOCK_VERSION(3);
unsigned int linear_pitch = reader.readI();
ds.linearPitch = linear_pitch == 0 ? 0 : 1;
ds.compatFlags.linearPitch = linear_pitch == 0 ? 0 : 1;
if (blockVersion >= 2) {
int fineTuneCents = reader.readC() * 100;
@ -2802,13 +2810,15 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
}
}
ds.recalcChans();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {

File diff suppressed because it is too large Load diff

View file

@ -219,10 +219,10 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
try {
DivSong ds;
ds.version=DIV_VERSION_IT;
ds.noSlidesOnFirstTick=true;
ds.rowResetsArpPos=true;
ds.ignoreJumpAtEnd=false;
ds.pitchSlideSpeed=8;
ds.compatFlags.noSlidesOnFirstTick=true;
ds.compatFlags.rowResetsArpPos=true;
ds.compatFlags.ignoreJumpAtEnd=false;
ds.compatFlags.pitchSlideSpeed=8;
logV("Impulse Tracker module");
@ -277,9 +277,9 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
}
if (flags&8) {
ds.linearPitch=1;
ds.compatFlags.linearPitch=1;
} else {
ds.linearPitch=0;
ds.compatFlags.linearPitch=0;
}
unsigned char globalVol=reader.readC();
@ -1619,12 +1619,6 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
logV("maxChan: %d",maxChan);
// set channel visibility
for (int i=maxChan; i<((maxChan+32)&(~31)); i++) {
ds.subsong[0]->chanShow[i]=false;
ds.subsong[0]->chanShowChanOsc[i]=false;
}
// copy patterns to the rest of subsongs
int copiesMade=0;
for (size_t i=1; i<ds.subsong.size(); i++) {
@ -1664,10 +1658,13 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
}
// set systems
int chansToCount=maxChan;
for (int i=0; i<(maxChan+32)>>5; i++) {
ds.system[i]=DIV_SYSTEM_ES5506;
ds.systemChans[i]=MIN(32,chansToCount);
chansToCount-=ds.systemChans[i];
ds.systemFlags[i].set("amigaVol",true);
if (!ds.linearPitch) {
if (!ds.compatFlags.linearPitch) {
ds.systemFlags[i].set("amigaPitch",true);
}
}
@ -1675,7 +1672,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
ds.systemName="PC";
// find subsongs
ds.findSubSongs(maxChan);
ds.recalcChans();
ds.findSubSongs();
// populate subsongs with default panning values
for (size_t i=0; i<ds.subsong.size(); i++) {
@ -1707,8 +1705,8 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) {
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {

View file

@ -41,11 +41,11 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
DivSong ds;
ds.tuning=436.0;
ds.version=DIV_VERSION_MOD;
ds.linearPitch=0;
ds.noSlidesOnFirstTick=true;
ds.rowResetsArpPos=true;
ds.ignoreJumpAtEnd=false;
ds.delayBehavior=0;
ds.compatFlags.linearPitch=0;
ds.compatFlags.noSlidesOnFirstTick=true;
ds.compatFlags.rowResetsArpPos=true;
ds.compatFlags.ignoreJumpAtEnd=false;
ds.compatFlags.delayBehavior=0;
int insCount=31;
bool bypassLimits=false;
@ -431,15 +431,17 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
ds.insLen=ds.ins.size();
// find subsongs
ds.findSubSongs(chCount);
ds.initDefaultSystemChans();
ds.recalcChans();
ds.findSubSongs();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {

View file

@ -79,12 +79,12 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
try {
DivSong ds;
ds.version=DIV_VERSION_S3M;
ds.linearPitch=0;
ds.pitchMacroIsLinear=false;
ds.noSlidesOnFirstTick=true;
ds.rowResetsArpPos=true;
ds.ignoreJumpAtEnd=false;
ds.pitchSlideSpeed=12;
ds.compatFlags.linearPitch=0;
ds.compatFlags.pitchMacroIsLinear=false;
ds.compatFlags.noSlidesOnFirstTick=true;
ds.compatFlags.rowResetsArpPos=true;
ds.compatFlags.ignoreJumpAtEnd=false;
ds.compatFlags.pitchSlideSpeed=12;
logV("Scream Tracker 3 module");
@ -345,6 +345,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
ds.systemName="PC";
if (hasPCM) {
ds.system[ds.systemLen]=DIV_SYSTEM_ES5506;
ds.systemChans[ds.systemLen]=32; // for now
ds.systemVol[ds.systemLen]=(float)globalVol/64.0;
ds.systemPan[ds.systemLen]=0;
ds.systemFlags[ds.systemLen].set("volScale",3900);
@ -354,6 +355,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
}
if (hasFM) {
ds.system[ds.systemLen]=opl2 ? DIV_SYSTEM_OPL2 : DIV_SYSTEM_OPL3;
ds.systemChans[ds.systemLen]=opl2?9:18; // for now
ds.systemVol[ds.systemLen]=1.0f;
ds.systemPan[ds.systemLen]=0;
ds.systemLen++;
@ -1179,7 +1181,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
}
// find subsongs
ds.findSubSongs(DIV_MAX_CHANS);
ds.recalcChans();
ds.findSubSongs();
// populate subsongs with default panning values
if (masterVol&128) { // only in stereo mode
@ -1214,8 +1217,8 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {

View file

@ -296,13 +296,12 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
}
w->writeText("\n");
w->writeText(fmt::sprintf("- virtual tempo: %d/%d\n",s->virtualTempoN,s->virtualTempoD));
w->writeText(fmt::sprintf("- time base: %d\n",s->timeBase));
w->writeText(fmt::sprintf("- pattern length: %d\n",s->patLen));
w->writeText(fmt::sprintf("\norders:\n```\n"));
for (int j=0; j<s->ordersLen; j++) {
w->writeText(fmt::sprintf("%.2X |",j));
for (int k=0; k<chans; k++) {
for (int k=0; k<song.chans; k++) {
w->writeText(fmt::sprintf(" %.2X",s->orders.ord[k][j]));
}
w->writeText("\n");
@ -318,7 +317,7 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
for (int k=0; k<s->patLen; k++) {
w->writeText(fmt::sprintf("%.2X ",k));
for (int l=0; l<chans; l++) {
for (int l=0; l<song.chans; l++) {
DivPattern* p=s->pat[l].getPattern(s->orders.ord[l][j],false);
short note, octave;
noteToSplitNote(p->newData[k][DIV_PAT_NOTE],note,octave);

View file

@ -551,7 +551,7 @@ bool DivEngine::loadTFMv1(unsigned char* file, size_t len) {
ds.systemLen=1;
ds.system[0]=DIV_SYSTEM_YM2612;
ds.loopModality=1;
ds.compatFlags.loopModality=1;
unsigned char speed=reader.readCNoRLE();
unsigned char interleaveFactor=reader.readCNoRLE();
@ -704,13 +704,15 @@ bool DivEngine::loadTFMv1(unsigned char* file, size_t len) {
info.loopPos=loopPos;
TFMParsePattern(info);
ds.recalcChans();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {
@ -744,7 +746,7 @@ bool DivEngine::loadTFMv2(unsigned char* file, size_t len) {
ds.systemLen=1;
ds.system[0]=DIV_SYSTEM_YM2612;
ds.loopModality=1;
ds.compatFlags.loopModality=1;
unsigned char magic[8]={0};
@ -904,13 +906,16 @@ bool DivEngine::loadTFMv2(unsigned char* file, size_t len) {
info.loopPos=loopPos;
TFMParsePattern(info);
ds.initDefaultSystemChans();
ds.recalcChans();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {

View file

@ -207,12 +207,12 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
try {
DivSong ds;
ds.version=DIV_VERSION_XM;
//ds.linearPitch=0;
//ds.pitchMacroIsLinear=false;
ds.noSlidesOnFirstTick=true;
ds.rowResetsArpPos=true;
ds.ignoreJumpAtEnd=false;
ds.pitchSlideSpeed=8;
//ds.compatFlags.linearPitch=0;
//ds.compatFlags.pitchMacroIsLinear=false;
ds.compatFlags.noSlidesOnFirstTick=true;
ds.compatFlags.rowResetsArpPos=true;
ds.compatFlags.ignoreJumpAtEnd=false;
ds.compatFlags.pitchSlideSpeed=8;
logV("Extended Module");
@ -251,7 +251,7 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
unsigned short totalChans=reader.readS();
unsigned short patCount=reader.readS();
ds.insLen=(unsigned short)reader.readS();
ds.linearPitch=(reader.readS()&1)?1:0;
ds.compatFlags.linearPitch=(reader.readS()&1)?1:0;
ds.subsong[0]->speeds.val[0]=reader.readS();
ds.subsong[0]->speeds.len=1;
double bpm=(unsigned short)reader.readS();
@ -301,10 +301,13 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
}
}
int chansToCount=totalChans;
for (int i=0; i<(totalChans+31)>>5; i++) {
ds.system[i]=DIV_SYSTEM_ES5506;
ds.systemChans[i]=MIN(32,chansToCount);
chansToCount-=ds.systemChans[i];
ds.systemFlags[i].set("amigaVol",true);
ds.systemFlags[i].set("amigaPitch",(ds.linearPitch==0));
ds.systemFlags[i].set("amigaPitch",(ds.compatFlags.linearPitch==0));
ds.systemFlags[i].set("volScale",3900);
}
ds.systemLen=(totalChans+31)>>5;
@ -1370,22 +1373,17 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) {
return false;
}
// set channel visibility
for (int i=totalChans; i<((totalChans+32)&(~31)); i++) {
ds.subsong[0]->chanShow[i]=false;
ds.subsong[0]->chanShowChanOsc[i]=false;
}
// find subsongs
ds.findSubSongs(totalChans);
ds.recalcChans();
ds.findSubSongs();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
song.unload();
song=ds;
hasLoadedSomething=true;
changeSong(0);
recalcChans();
saveLock.unlock();
BUSY_END;
if (active) {

View file

@ -22,6 +22,7 @@
#include "engine.h"
#include <fmt/printf.h>
// TODO: this function could be in DivSong instead
bool DivEngine::convertLegacySampleMode() {
logD("converting legacy sample mode...");
int legacyInsInit=-1;
@ -114,7 +115,7 @@ bool DivEngine::convertLegacySampleMode() {
};
for (DivSubSong* h: song.subsong) {
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
// 0: sample off
// 1: legacy mode
// 2: normal mode
@ -125,11 +126,11 @@ bool DivEngine::convertLegacySampleMode() {
bool noteOffDisablesSampleMode=false;
bool hasLegacyToggle=false;
switch (sysOfChan[i]) {
switch (song.sysOfChan[i]) {
case DIV_SYSTEM_NES:
case DIV_SYSTEM_5E01:
// NES PCM channel (on by default)
if (dispatchChanOfChan[i]!=4) {
if (song.dispatchChanOfChan[i]!=4) {
continue;
}
sampleMode=1;
@ -137,14 +138,14 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_MMC5:
// MMC5 PCM channel
if (dispatchChanOfChan[i]!=2) {
if (song.dispatchChanOfChan[i]!=2) {
continue;
}
sampleMode=1;
break;
case DIV_SYSTEM_YM2612:
// YM2612 DAC channel
if (dispatchChanOfChan[i]!=5) {
if (song.dispatchChanOfChan[i]!=5) {
continue;
}
hasLegacyToggle=true;
@ -152,7 +153,7 @@ bool DivEngine::convertLegacySampleMode() {
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2612_CSM:
// YM2612 DAC channel
if (dispatchChanOfChan[i]!=8) {
if (song.dispatchChanOfChan[i]!=8) {
continue;
}
hasLegacyToggle=true;
@ -167,20 +168,18 @@ bool DivEngine::convertLegacySampleMode() {
case DIV_SYSTEM_AY8930:
// any channel can be DAC'd
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_FULL:
// Neo Geo CD ADPCM channels
if (dispatchChanOfChan[i]<7) {
if (song.dispatchChanOfChan[i]<7) {
continue;
}
sampleMode=1;
preferredInsType=DIV_INS_ADPCMA;
preferredInsType2=DIV_INS_ADPCMB;
break;
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL_EXT:
// Neo Geo CD ADPCM channels
if (dispatchChanOfChan[i]<10) {
if (song.dispatchChanOfChan[i]<10) {
continue;
}
sampleMode=1;
@ -189,7 +188,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2612_DUALPCM:
// DualPCM DAC
if (dispatchChanOfChan[i]<5) {
if (song.dispatchChanOfChan[i]<5) {
continue;
}
sampleMode=1;
@ -197,7 +196,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
// DualPCM DAC
if (dispatchChanOfChan[i]<8 || dispatchChanOfChan[i]>9) {
if (song.dispatchChanOfChan[i]<8 || song.dispatchChanOfChan[i]>9) {
continue;
}
sampleMode=1;
@ -205,7 +204,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2610_CSM:
// Neo Geo CD ADPCM channels
if (dispatchChanOfChan[i]<11) {
if (song.dispatchChanOfChan[i]<11) {
continue;
}
sampleMode=1;
@ -214,7 +213,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2610B:
// ADPCM channels
if (dispatchChanOfChan[i]<9) {
if (song.dispatchChanOfChan[i]<9) {
continue;
}
sampleMode=1;
@ -223,7 +222,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2610B_EXT:
// ADPCM channels
if (dispatchChanOfChan[i]<12) {
if (song.dispatchChanOfChan[i]<12) {
continue;
}
sampleMode=1;
@ -232,7 +231,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2610B_CSM:
// ADPCM channels
if (dispatchChanOfChan[i]<13) {
if (song.dispatchChanOfChan[i]<13) {
continue;
}
sampleMode=1;
@ -241,7 +240,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2608:
// ADPCM channel
if (dispatchChanOfChan[i]!=15) {
if (song.dispatchChanOfChan[i]!=15) {
continue;
}
sampleMode=1;
@ -249,7 +248,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2608_EXT:
// ADPCM channel
if (dispatchChanOfChan[i]!=18) {
if (song.dispatchChanOfChan[i]!=18) {
continue;
}
sampleMode=1;
@ -257,14 +256,13 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_YM2608_CSM:
// ADPCM channel
if (dispatchChanOfChan[i]!=19) {
if (song.dispatchChanOfChan[i]!=19) {
continue;
}
sampleMode=1;
preferredInsType=DIV_INS_ADPCMB;
break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
// all channels can play back samples
sampleMode=1;
preferredInsType=DIV_INS_SEGAPCM;
@ -279,7 +277,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_Y8950:
// Y8950 ADPCM
if (dispatchChanOfChan[i]!=9) {
if (song.dispatchChanOfChan[i]!=9) {
continue;
}
sampleMode=1;
@ -287,14 +285,14 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_Y8950_DRUMS:
// Y8950 ADPCM
if (dispatchChanOfChan[i]!=11) {
if (song.dispatchChanOfChan[i]!=11) {
continue;
}
sampleMode=1;
break;
case DIV_SYSTEM_SWAN:
// PCM channel
if (dispatchChanOfChan[i]!=1) {
if (song.dispatchChanOfChan[i]!=1) {
continue;
}
noteOffDisablesSampleMode=true;
@ -302,7 +300,7 @@ bool DivEngine::convertLegacySampleMode() {
break;
case DIV_SYSTEM_VRC6:
// pulse DAC mode
if (dispatchChanOfChan[i]>=2) {
if (song.dispatchChanOfChan[i]>=2) {
continue;
}
hasLegacyToggle=true;

View file

@ -43,7 +43,7 @@ void DivMacroStruct::prepare(DivInstrumentMacro& source, DivEngine* e) {
mode=source.mode;
type=(source.open>>1)&3;
activeRelease=source.open&8;
linger=(source.macroType==DIV_MACRO_VOL && e->song.volMacroLinger);
linger=(source.macroType==DIV_MACRO_VOL && e->song.compatFlags.volMacroLinger);
lfoPos=LFO_PHASE;
}

View file

@ -668,7 +668,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
chan[c.chan].writeVol=true;
}
@ -752,9 +752,9 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -260,7 +260,7 @@ void DivPlatformArcade::tick(bool sysTick) {
} 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 (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i] || !op.enable) {
@ -379,7 +379,7 @@ void DivPlatformArcade::tick(bool sysTick) {
for (int i=0; i<8; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+chan[i].pitch-128+chan[i].pitch2;
if (!parent->song.oldArpStrategy) {
if (!parent->song.compatFlags.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride<<7)+chan[i].pitch-128+chan[i].pitch2;
} else {
@ -862,7 +862,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
return 127;
break;
case DIV_CMD_PRE_PORTA:
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -716,7 +716,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
//chan[c.chan].keyOn=true;
@ -735,7 +735,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (!(chan[c.chan].nextPSGMode.val&8)) {
@ -942,9 +942,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -544,7 +544,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
//chan[c.chan].keyOn=true;
@ -563,7 +563,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (!(chan[c.chan].nextPSGMode.val&8)) {
@ -764,9 +764,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -176,7 +176,7 @@ int DivPlatformBifurcator::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -250,9 +250,9 @@ int DivPlatformBifurcator::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_BIFURCATOR_STATE_LOAD:

View file

@ -164,7 +164,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
rWrite(2+c.chan,(chan[c.chan].wave<<5)|chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
@ -243,9 +243,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -357,7 +357,7 @@ int DivPlatformC140::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
chan[c.chan].volChangedL=true;
chan[c.chan].volChangedR=true;
@ -445,9 +445,9 @@ int DivPlatformC140::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -574,12 +574,12 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta || parent->song.preNoteNoEffect) {
if (parent->song.compatFlags.resetMacroOnPorta || parent->song.compatFlags.preNoteNoEffect) {
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_C64));
chan[c.chan].keyOn=true;
}
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -358,7 +358,7 @@ int DivPlatformDave::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chan[c.chan].writeVol=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
@ -471,9 +471,9 @@ int DivPlatformDave::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_DAVE));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_DAVE));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -23,7 +23,7 @@
#include <math.h>
#define PITCH_OFFSET ((double)(16*2048*(chanMax+1)))
#define NOTE_ES5506(c,note) ((amigaPitch && !parent->song.linearPitch)?parent->calcBaseFreq(COLOR_NTSC*16,chan[c].pcm.freqOffs,note,true):parent->calcBaseFreq(chipClock,chan[c].pcm.freqOffs,note,false))
#define NOTE_ES5506(c,note) ((amigaPitch && !parent->song.compatFlags.linearPitch)?parent->calcBaseFreq(COLOR_NTSC*16,chan[c].pcm.freqOffs,note,true):parent->calcBaseFreq(chipClock,chan[c].pcm.freqOffs,note,false))
#define rWrite(a,...) {if(!skipRegisterWrites) {hostIntf32.push_back(QueuedHostIntf(4,(a),__VA_ARGS__)); }}
#define immWrite(a,...) {hostIntf32.push_back(QueuedHostIntf(4,(a),__VA_ARGS__));}
@ -603,7 +603,7 @@ void DivPlatformES5506::tick(bool sysTick) {
const unsigned int length=s->samples-1;
const unsigned int end=start+(length<<11);
const unsigned int nextBank=(offES5506>>22)&3;
const double nextFreqOffs=((amigaPitch && !parent->song.linearPitch)?16:PITCH_OFFSET)*off;
const double nextFreqOffs=((amigaPitch && !parent->song.compatFlags.linearPitch)?16:PITCH_OFFSET)*off;
chan[i].pcm.loopMode=loopMode;
chan[i].pcm.bank=nextBank;
chan[i].pcm.start=start;
@ -746,7 +746,7 @@ void DivPlatformES5506::tick(bool sysTick) {
chan[i].pcm.nextPos=0;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (amigaPitch && !parent->song.linearPitch) {
if (amigaPitch && !parent->song.compatFlags.linearPitch) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch*16,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,2,chan[i].pitch2*16,16*COLOR_NTSC,chan[i].pcm.freqOffs);
chan[i].freq=PITCH_OFFSET*(COLOR_NTSC/chan[i].freq)/(chipClock/16.0);
chan[i].freq=CLAMP(chan[i].freq,0,0x1ffff);
@ -767,7 +767,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
chan[i].pcm.loopStart=(chan[i].pcm.start+(s->loopStart<<11))&0xfffff800;
chan[i].pcm.loopEnd=(chan[i].pcm.start+((s->loopEnd)<<11))&0xffffff80;
chan[i].pcm.freqOffs=((amigaPitch && !parent->song.linearPitch)?16:PITCH_OFFSET)*off;
chan[i].pcm.freqOffs=((amigaPitch && !parent->song.compatFlags.linearPitch)?16:PITCH_OFFSET)*off;
unsigned int startPos=chan[i].pcm.direction?chan[i].pcm.end:chan[i].pcm.start;
if (chan[i].pcm.nextPos) {
const unsigned int start=chan[i].pcm.start;
@ -1212,7 +1212,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
int nextFreq=chan[c.chan].baseFreq;
int destFreq=NOTE_ES5506(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
if (amigaPitch && !parent->song.linearPitch) {
if (amigaPitch && !parent->song.compatFlags.linearPitch) {
c.value*=16;
}
if (destFreq>nextFreq) {
@ -1244,9 +1244,9 @@ int DivPlatformES5506::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_ES5506));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_ES5506));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
chan[c.chan].nextNote=chan[c.chan].note;
chan[c.chan].noteChanged.note=1;
}

View file

@ -337,7 +337,7 @@ void DivPlatformESFM::tick(bool sysTick) {
if (chan[i].freqChanged) {
int mul=2;
int fixedBlock=chan[i].state.fm.block;
if (!parent->song.linearPitch) {
if (!parent->song.compatFlags.linearPitch) {
mul=octave(chan[i].baseFreq,fixedBlock)*2;
}
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,mul,chan[i].pitch2,chipClock,CHIP_FREQBASE);
@ -569,7 +569,7 @@ int DivPlatformESFM::dispatch(DivCommand c) {
bool return2=false;
int mul=1;
int fixedBlock=0;
if (!parent->song.linearPitch) {
if (!parent->song.compatFlags.linearPitch) {
fixedBlock=chan[c.chan].state.fm.block;
mul=octave(chan[c.chan].baseFreq,fixedBlock);
}
@ -586,7 +586,7 @@ int DivPlatformESFM::dispatch(DivCommand c) {
return2=true;
}
}
if (!chan[c.chan].portaPause && !parent->song.linearPitch) {
if (!chan[c.chan].portaPause && !parent->song.compatFlags.linearPitch) {
if (mul!=octave(newFreq,fixedBlock)) {
chan[c.chan].portaPause=true;
break;
@ -987,7 +987,7 @@ int DivPlatformESFM::dispatch(DivCommand c) {
return 63;
break;
case DIV_CMD_PRE_PORTA:
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
}
chan[c.chan].inPorta=c.value;

View file

@ -132,7 +132,7 @@ void DivPlatformFDS::tick(bool sysTick) {
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
if (i==3) {
if (parent->song.properNoiseLayout) {
if (parent->song.compatFlags.properNoiseLayout) {
chan[i].duty&=1;
} else if (chan[i].duty>1) {
chan[i].duty=1;
@ -264,7 +264,7 @@ int DivPlatformFDS::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
@ -390,9 +390,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FDS));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FDS));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -31,7 +31,7 @@
int newFreq; \
bool return2=false; \
if (_targetChan.portaPause) { \
if (parent->song.oldOctaveBoundary) { \
if (parent->song.compatFlags.oldOctaveBoundary) { \
if ((_targetChan.portaPauseFreq&0xf800)>(_targetChan.baseFreq&0xf800)) { \
_targetChan.baseFreq=((_targetChan.baseFreq&0x7ff)>>1)|(_targetChan.portaPauseFreq&0xf800); \
} else { \
@ -59,7 +59,7 @@
/* what the heck! */ \
if (!_targetChan.portaPause) { \
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) { \
if (parent->song.fbPortaPause) { \
if (parent->song.compatFlags.fbPortaPause) { \
_targetChan.portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800); \
_targetChan.portaPause=true; \
break; \
@ -68,7 +68,7 @@
} \
} \
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) { \
if (parent->song.fbPortaPause) { \
if (parent->song.compatFlags.fbPortaPause) { \
_targetChan.portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800); \
_targetChan.portaPause=true; \
break; \

View file

@ -261,7 +261,7 @@ int DivPlatformGA20::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
chan[c.chan].volumeChanged=true;
}
@ -332,9 +332,9 @@ int DivPlatformGA20::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -224,7 +224,7 @@ void DivPlatformGB::tick(bool sysTick) {
if (i!=2) {
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(chan[i].soundLen&63)));
} else if (!chan[i].softEnv) {
if (parent->song.waveDutyIsVol) {
if (parent->song.compatFlags.waveDutyIsVol) {
rWrite(16+i*5+2,(model==GB_MODEL_AGB_NATIVE?gbVolMapEx:gbVolMap)[(chan[i].std.duty.val&3)<<2]);
}
}
@ -439,7 +439,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].envVol;
}
} else if (chan[c.chan].softEnv && c.chan!=2) {
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
chan[c.chan].envVol=chan[c.chan].outVol;
}
@ -478,7 +478,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
chan[c.chan].soundLen=ins->gb.soundLen;
chan[c.chan].vol=chan[c.chan].envVol;
chan[c.chan].outVol=chan[c.chan].vol;
if (parent->song.gbInsAffectsEnvelope) {
if (parent->song.compatFlags.gbInsAffectsEnvelope) {
rWrite(16+c.chan*5+2,((chan[c.chan].vol<<4))|(chan[c.chan].envLen&7)|((chan[c.chan].envDir&1)<<3));
}
}
@ -567,9 +567,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_GB));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_GB));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GB_SWEEP_DIR:

View file

@ -237,7 +237,7 @@ int DivPlatformGBADMA::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].envVol=2;
}
if (chan[c.chan].useWave) {
@ -321,7 +321,7 @@ int DivPlatformGBADMA::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -417,7 +417,7 @@ int DivPlatformGBAMinMod::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -503,9 +503,9 @@ int DivPlatformGBAMinMod::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -68,7 +68,7 @@ void DivPlatformGenesis::processDAC(int iRate) {
if (chan[i].dacSample!=-1) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0 && chan[i].dacPos<s->samples) {
if (parent->song.noOPN2Vol) {
if (parent->song.compatFlags.noOPN2Vol) {
chan[i].dacOutput=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
chan[i].dacOutput=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
@ -110,7 +110,7 @@ void DivPlatformGenesis::processDAC(int iRate) {
if (s->samples>0 && chan[5].dacPos<s->samples) {
if (!isMuted[5]) {
int sample;
if (parent->song.noOPN2Vol) {
if (parent->song.compatFlags.noOPN2Vol) {
sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
} else {
sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
@ -122,7 +122,7 @@ void DivPlatformGenesis::processDAC(int iRate) {
chan[5].dacPos=s->loopStart;
} else if (chan[5].dacPos>=s->samples) {
chan[5].dacSample=-1;
if (parent->song.brokenDACMode) {
if (parent->song.compatFlags.brokenDACMode) {
rWrite(0x2b,0);
}
}
@ -768,7 +768,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
if (chan[i].std.alg.had) {
chan[i].state.alg=chan[i].std.alg.val;
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i] || !op.enable) {
@ -863,7 +863,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
for (int i=0; i<512; i++) {
if (pendingWrites[i]!=oldWrites[i]) {
if (i==0x2b && pendingWrites[i]!=0 && !parent->song.brokenDACMode) {
if (i==0x2b && pendingWrites[i]!=0 && !parent->song.compatFlags.brokenDACMode) {
if (chan[5].keyOn) chan[5].keyOn=false;
chan[5].keyOff=true;
}
@ -894,7 +894,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
for (int i=0; i<csmChan; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[i].state.block);
} else {
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE,11);
@ -1131,7 +1131,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.chan>=5 && c.chan<csmChan) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
if (parent->song.brokenDACMode) {
if (parent->song.compatFlags.brokenDACMode) {
rWrite(0x2b,0);
if (chan[c.chan].dacMode) break;
}
@ -1200,7 +1200,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {

View file

@ -153,7 +153,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
} else {
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int i=0; i<4; i++) {
if (ch==i) continue;
opChan[i].pan=opChan[ch].pan;
@ -169,7 +169,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>opChan[ch].baseFreq) {
@ -197,7 +197,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
}
case DIV_CMD_SAMPLE_MODE: {
// not ignored actually!
if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
if (!parent->song.compatFlags.ignoreDACModeOutsideIntendedChannel) {
chan[5].dacMode=c.value;
rWrite(0x2b,c.value<<7);
}
@ -547,7 +547,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
@ -578,7 +578,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
if (opChan[i].std.panL.had) {
opChan[i].pan=opChan[i].std.panL.val&3;
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int j=0; j<4; j++) {
if (i==j) continue;
opChan[j].pan=opChan[i].pan;
@ -648,7 +648,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,2,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[extChanOffs].state.block);
} else {
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,2,opChan[i].pitch2);

View file

@ -324,7 +324,7 @@ int DivPlatformK007232::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
@ -405,9 +405,9 @@ int DivPlatformK007232::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -255,7 +255,7 @@ int DivPlatformK053260::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -331,9 +331,9 @@ int DivPlatformK053260::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -361,7 +361,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].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -429,7 +429,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
}
}
chan[c.chan].freqChanged=true;
if (chan[c.chan].pcm && parent->song.linearPitch) {
if (chan[c.chan].pcm && parent->song.compatFlags.linearPitch) {
chan[c.chan].sampleBaseFreq=chan[c.chan].baseFreq;
}
if (return2) {
@ -451,9 +451,9 @@ int DivPlatformLynx::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -229,7 +229,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
rWrite(0x5000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
@ -313,9 +313,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -157,7 +157,7 @@ void DivPlatformMSM5232::tick(bool sysTick) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
chan[i].freq=chan[i].baseFreq+chan[i].pitch+chan[i].pitch2-(12<<7);
if (!parent->song.oldArpStrategy) {
if (!parent->song.compatFlags.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride<<7)+(chan[i].pitch)-(12<<7);
} else {
@ -206,7 +206,7 @@ int DivPlatformMSM5232::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
@ -249,13 +249,13 @@ int DivPlatformMSM5232::dispatch(DivCommand c) {
int destFreq=NOTE_LINEAR(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*parent->song.pitchSlideSpeed;
chan[c.chan].baseFreq+=c.value*parent->song.compatFlags.pitchSlideSpeed;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value*parent->song.pitchSlideSpeed;
chan[c.chan].baseFreq-=c.value*parent->song.compatFlags.pitchSlideSpeed;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
@ -291,9 +291,9 @@ int DivPlatformMSM5232::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -101,7 +101,7 @@ void DivPlatformMSM6258::acquire(short** buf, size_t len) {
void DivPlatformMSM6258::tick(bool sysTick) {
for (int i=0; i<1; i++) {
if (!parent->song.disableSampleMacro) {
if (!parent->song.compatFlags.disableSampleMacro) {
chan[i].std.next();
if (chan[i].std.duty.had) {
if (rateSel!=(chan[i].std.duty.val&3)) {

View file

@ -122,7 +122,7 @@ void DivPlatformMSM6295::acquire(short** buf, size_t len) {
void DivPlatformMSM6295::tick(bool sysTick) {
for (int i=0; i<4; i++) {
if (!parent->song.disableSampleMacro) {
if (!parent->song.compatFlags.disableSampleMacro) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].std.vol.val,chan[i].vol,8);

View file

@ -389,9 +389,9 @@ int DivPlatformMultiPCM::dispatch(DivCommand c) {
return 127;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MULTIPCM));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MULTIPCM));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
}
chan[c.chan].inPorta=c.value;

View file

@ -386,13 +386,13 @@ int DivPlatformN163::dispatch(DivCommand c) {
int destFreq=destFreqD;
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch)?1:16);
chan[c.chan].baseFreq+=c.value*((parent->song.compatFlags.linearPitch)?1:16);
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch)?1:16);
chan[c.chan].baseFreq-=c.value*((parent->song.compatFlags.linearPitch)?1:16);
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
@ -456,12 +456,12 @@ int DivPlatformN163::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) {
if (parent->song.compatFlags.resetMacroOnPorta) {
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_N163));
chan[c.chan].keyOn=true;
}
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
@ -593,7 +593,7 @@ void DivPlatformN163::setFlags(const DivConfig& flags) {
break;
}
CHECK_CUSTOM_CLOCK;
initChanMax=chanMax=flags.getInt("channels",0)&7;
initChanMax=chanMax=flags.getInt("channels",7)&7;
multiplex=!flags.getBool("multiplex",false); // not accurate in real hardware
rate=chipClock;
rate/=15;
@ -602,6 +602,7 @@ void DivPlatformN163::setFlags(const DivConfig& flags) {
for (int i=0; i<8; i++) {
oscBuf[i]->setRate(rate);//=rate/(initChanMax+1);
}
logV("N163: initChanMax: %d",initChanMax);
lenCompensate=flags.getBool("lenCompensate",false);
@ -618,6 +619,8 @@ int DivPlatformN163::init(DivEngine* p, int channels, int sugRate, const DivConf
oscBuf[i]=new DivDispatchOscBuffer;
}
logV("N163: init(%d)",channels);
memCompo.used=0;
memCompo.capacity=128;
memCompo.memory=regPool;

View file

@ -353,7 +353,7 @@ int DivPlatformNamcoWSG::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
@ -406,13 +406,13 @@ int DivPlatformNamcoWSG::dispatch(DivCommand c) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch)?1:8);
chan[c.chan].baseFreq+=c.value*((parent->song.compatFlags.linearPitch)?1:8);
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch)?1:8);
chan[c.chan].baseFreq-=c.value*((parent->song.compatFlags.linearPitch)?1:8);
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
@ -440,9 +440,9 @@ int DivPlatformNamcoWSG::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -334,7 +334,7 @@ int DivPlatformNDS::dispatch(DivCommand c) {
chan[c.chan].busy=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -422,9 +422,9 @@ int DivPlatformNDS::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NDS));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NDS));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -326,7 +326,7 @@ void DivPlatformNES::tick(bool sysTick) {
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
if (i==3) {
if (parent->song.properNoiseLayout) {
if (parent->song.compatFlags.properNoiseLayout) {
chan[i].duty&=1;
} else if (chan[i].duty>1) {
chan[i].duty=1;
@ -372,7 +372,7 @@ void DivPlatformNES::tick(bool sysTick) {
ntPos+=chan[i].pitch2;
if (isE) {
chan[i].freq=31-(ntPos&31);
} else if (parent->song.properNoiseLayout) {
} else if (parent->song.compatFlags.properNoiseLayout) {
chan[i].freq=15-(ntPos&15);
} else {
if (ntPos<0) ntPos=0;
@ -578,12 +578,12 @@ int DivPlatformNES::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NES));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (c.chan==2) {
rWrite(0x4000+c.chan*4,linearCount);
} else if (!parent->song.brokenOutVol2) {
} else if (!parent->song.compatFlags.brokenOutVol2) {
rWrite(0x4000+c.chan*4,(chan[c.chan].envMode<<4)|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
}
if (resetSweep && c.chan<2) {
@ -752,9 +752,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NES));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NES));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -1515,7 +1515,7 @@ void DivPlatformOPL::tick(bool sysTick) {
if (chan[i].freqChanged) {
int mul=2;
int fixedBlock=chan[i].state.block;
if (!parent->song.linearPitch) {
if (!parent->song.compatFlags.linearPitch) {
mul=octave(chan[i].baseFreq,fixedBlock)*2;
}
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,mul,chan[i].pitch2,chipClock,CHIP_FREQBASE);
@ -2082,7 +2082,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
bool return2=false;
int mul=1;
int fixedBlock=0;
if (!parent->song.linearPitch) {
if (!parent->song.compatFlags.linearPitch) {
fixedBlock=chan[c.chan].state.block;
mul=octave(chan[c.chan].baseFreq,fixedBlock);
}
@ -2099,7 +2099,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
return2=true;
}
}
if (!chan[c.chan].portaPause && !parent->song.linearPitch) {
if (!chan[c.chan].portaPause && !parent->song.compatFlags.linearPitch) {
if (mul!=octave(newFreq,fixedBlock)) {
chan[c.chan].portaPause=true;
break;
@ -2583,9 +2583,9 @@ int DivPlatformOPL::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (PCM_CHECK(c.chan) && chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MULTIPCM));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MULTIPCM));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
chan[c.chan].baseFreq=(PCM_CHECK(c.chan))?NOTE_PCM(chan[c.chan].note):
((c.chan==adpcmChan)?(NOTE_ADPCMB(chan[c.chan].note)):(NOTE_FREQUENCY(chan[c.chan].note)));
}

View file

@ -341,7 +341,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
if (chan[i].freqChanged) {
int mul=2;
int fixedBlock=chan[i].state.block;
if (!parent->song.linearPitch) {
if (!parent->song.compatFlags.linearPitch) {
mul=octave(chan[i].baseFreq,fixedBlock)*2;
}
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,mul,chan[i].pitch2,chipClock,CHIP_FREQBASE);
@ -684,7 +684,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
bool return2=false;
int mul=1;
int fixedBlock=0;
if (!parent->song.linearPitch) {
if (!parent->song.compatFlags.linearPitch) {
fixedBlock=chan[c.chan].state.block;
mul=octave(chan[c.chan].baseFreq,fixedBlock);
}
@ -956,7 +956,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (c.chan>=9 && !properDrums) return 0;
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -103,7 +103,7 @@ void DivPlatformPCE::acquireDirect(blip_buffer_t** bb, size_t len) {
signed char dacData=((signed char)((unsigned char)s->data8[chan[i].dacPos]^0x80))>>3;
chan[i].dacOut=CLAMP(dacData,-16,15);
if (!isMuted[i]) {
chWrite(i,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[i].outVol));
chWrite(i,0x04,parent->song.compatFlags.disableSampleMacro?0xdf:(0xc0|chan[i].outVol));
chWrite(i,0x06,chan[i].dacOut&0x1f);
} else {
chWrite(i,0x04,0xc0);
@ -233,7 +233,7 @@ void DivPlatformPCE::tick(bool sysTick) {
chan[i].dacPos=0;
}
chan[i].dacPeriod=0;
chWrite(i,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[i].vol));
chWrite(i,0x04,parent->song.compatFlags.disableSampleMacro?0xdf:(0xc0|chan[i].vol));
addWrite(0xffff0000+(i<<8),chan[i].dacSample);
chan[i].keyOn=true;
}
@ -269,11 +269,11 @@ void DivPlatformPCE::tick(bool sysTick) {
if (i>=4) {
int noiseSeek=(chan[i].fixedArp?chan[i].baseNoteOverride:(chan[i].note+chan[i].arpOff))+chan[i].pitch2;
if (!parent->song.properNoiseLayout && noiseSeek<0) noiseSeek=0;
if (!parent->song.compatFlags.properNoiseLayout && noiseSeek<0) noiseSeek=0;
if (!NEW_ARP_STRAT) {
noiseSeek=chan[i].noiseSeek;
}
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.compatFlags.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
}
if (chan[i].keyOn) {
//rWrite(16+i*5,0x80);
@ -324,7 +324,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
break;
} else {
if (dumpWrites) {
chWrite(c.chan,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[c.chan].vol));
chWrite(c.chan,0x04,parent->song.compatFlags.disableSampleMacro?0xdf:(0xc0|chan[c.chan].vol));
addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dacSample);
}
}
@ -341,7 +341,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -359,7 +359,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chWrite(c.chan,0x04,0x80|chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
@ -476,9 +476,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
@ -503,7 +503,7 @@ void DivPlatformPCE::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
chWrite(ch,0x05,isMuted[ch]?0:chan[ch].pan);
if (!isMuted[ch] && (chan[ch].pcm && chan[ch].dacSample!=-1)) {
chWrite(ch,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[ch].outVol));
chWrite(ch,0x04,parent->song.compatFlags.disableSampleMacro?0xdf:(0xc0|chan[ch].outVol));
chWrite(ch,0x06,chan[ch].dacOut&0x1f);
}
}

View file

@ -29,310 +29,342 @@
void DivPlatformPCMDAC::acquire(short** buf, size_t len) {
const int depthScale=(15-outDepth);
int output=0;
int outSum[2];
oscBuf->begin(len);
for (size_t h=0; h<len; h++) {
if (!chan[0].active) {
// do not process if our channels are null
if (chan==NULL) {
for (size_t h=0; h<len; h++) {
buf[0][h]=0;
buf[1][h]=0;
oscBuf->putSample(h,0);
continue;
}
if (chan[0].useWave || (chan[0].sample>=0 && chan[0].sample<parent->song.sampleLen)) {
chan[0].audSub+=chan[0].freq;
if (chan[0].useWave) {
while (chan[0].audSub>=0x10000) {
chan[0].audSub-=0x10000;
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
if (chan[0].audPos>=(int)chan[0].audLen) {
chan[0].audPos%=chan[0].audLen;
chan[0].audDir=false;
}
chan[0].audDat[0]=chan[0].audDat[1];
chan[0].audDat[1]=chan[0].audDat[2];
chan[0].audDat[2]=chan[0].audDat[3];
chan[0].audDat[3]=chan[0].audDat[4];
chan[0].audDat[4]=chan[0].audDat[5];
chan[0].audDat[5]=chan[0].audDat[6];
chan[0].audDat[6]=chan[0].audDat[7];
chan[0].audDat[7]=(chan[0].ws.output[chan[0].audPos]-0x80)<<8;
}
const short s0=chan[0].audDat[0];
const short s1=chan[0].audDat[1];
const short s2=chan[0].audDat[2];
const short s3=chan[0].audDat[3];
const short s4=chan[0].audDat[4];
const short s5=chan[0].audDat[5];
const short s6=chan[0].audDat[6];
const short s7=chan[0].audDat[7];
switch (interp) {
case 1: // linear
output=s6+(((int)((int)s7-(int)s6)*((chan[0].audSub>>1)&0x7fff))>>15);
break;
case 2: { // cubic
float* cubicTable=DivFilterTables::getCubicTable();
float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2];
float result=(float)s4*t[0]+(float)s5*t[1]+(float)s6*t[2]+(float)s7*t[3];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
case 3: { // sinc
float* sincTable=DivFilterTables::getSincTable8();
float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2];
float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2];
float result=(
s0*t2[3]+
s1*t2[2]+
s2*t2[1]+
s3*t2[0]+
s4*t1[0]+
s5*t1[1]+
s6*t1[2]+
s7*t1[3]
);
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
default: // none
output=s7;
break;
}
} else {
DivSample* s=parent->getSample(chan[0].sample);
if (s->samples>0) {
while (chan[0].audSub>=0x10000) {
chan[0].audSub-=0x10000;
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
if (chan[0].audDir) {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopStart+(s->loopStart-chan[0].audPos);
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopEnd-1-(s->loopStart-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan[0].audPos<0) {
chan[0].sample=-1;
}
break;
}
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
} else {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=(chan[0].audPos+s->loopStart)-s->loopEnd;
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=s->loopEnd-1-(s->loopEnd-1-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
break;
}
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
}
chan[0].audDat[0]=chan[0].audDat[1];
chan[0].audDat[1]=chan[0].audDat[2];
chan[0].audDat[2]=chan[0].audDat[3];
chan[0].audDat[3]=chan[0].audDat[4];
chan[0].audDat[4]=chan[0].audDat[5];
chan[0].audDat[5]=chan[0].audDat[6];
chan[0].audDat[6]=chan[0].audDat[7];
if (chan[0].audPos>=0 && chan[0].audPos<(int)s->samples) {
chan[0].audDat[7]=s->data16[chan[0].audPos];
} else {
chan[0].audDat[7]=0;
}
}
} else {
chan[0].sample=-1;
chan[0].audSub=0;
chan[0].audPos=0;
}
const short s0=chan[0].audDat[0];
const short s1=chan[0].audDat[1];
const short s2=chan[0].audDat[2];
const short s3=chan[0].audDat[3];
const short s4=chan[0].audDat[4];
const short s5=chan[0].audDat[5];
const short s6=chan[0].audDat[6];
const short s7=chan[0].audDat[7];
switch (interp) {
case 1: // linear
output=s6+(((int)((int)s7-(int)s6)*((chan[0].audSub>>1)&0x7fff))>>15);
break;
case 2: { // cubic
float* cubicTable=DivFilterTables::getCubicTable();
float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2];
float result=(float)s4*t[0]+(float)s5*t[1]+(float)s6*t[2]+(float)s7*t[3];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
case 3: { // sinc
float* sincTable=DivFilterTables::getSincTable8();
float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2];
float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2];
float result=(
s0*t2[3]+
s1*t2[2]+
s2*t2[1]+
s3*t2[0]+
s4*t1[0]+
s5*t1[1]+
s6*t1[2]+
s7*t1[3]
);
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
default: // none
output=s7;
break;
}
}
}
if (isMuted) {
output=0;
} else {
output=((output*MIN(volMax,chan[0].vol)*MIN(chan[0].envVol,64))>>6)/volMax;
}
oscBuf->putSample(h,((output>>depthScale)<<depthScale)>>1);
if (outStereo) {
buf[0][h]=((output*chan[0].panL)>>(depthScale+8))<<depthScale;
buf[1][h]=((output*chan[0].panR)>>(depthScale+8))<<depthScale;
} else {
output=(output>>depthScale)<<depthScale;
buf[0][h]=output;
buf[1][h]=output;
}
return;
}
oscBuf->end(len);
for (int i=0; i<chans; i++) {
oscBuf[i].begin(len);
}
for (size_t h=0; h<len; h++) {
outSum[0]=0;
outSum[1]=0;
for (int i=0; i<chans; i++) {
output=0;
if (!chan[i].active) {
oscBuf[i].putSample(h,0);
continue;
}
if (chan[i].useWave || (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen)) {
chan[i].audSub+=chan[i].freq;
if (chan[i].useWave) {
while (chan[i].audSub>=0x10000) {
chan[i].audSub-=0x10000;
chan[i].audPos+=((!chan[i].useWave) && chan[i].audDir)?-1:1;
if (chan[i].audPos>=(int)chan[i].audLen) {
chan[i].audPos%=chan[i].audLen;
chan[i].audDir=false;
}
chan[i].audDat[0]=chan[i].audDat[1];
chan[i].audDat[1]=chan[i].audDat[2];
chan[i].audDat[2]=chan[i].audDat[3];
chan[i].audDat[3]=chan[i].audDat[4];
chan[i].audDat[4]=chan[i].audDat[5];
chan[i].audDat[5]=chan[i].audDat[6];
chan[i].audDat[6]=chan[i].audDat[7];
chan[i].audDat[7]=(chan[i].ws.output[chan[i].audPos]-0x80)<<8;
}
const short s0=chan[i].audDat[0];
const short s1=chan[i].audDat[1];
const short s2=chan[i].audDat[2];
const short s3=chan[i].audDat[3];
const short s4=chan[i].audDat[4];
const short s5=chan[i].audDat[5];
const short s6=chan[i].audDat[6];
const short s7=chan[i].audDat[7];
switch (interp) {
case 1: // linear
output=s6+(((int)((int)s7-(int)s6)*((chan[i].audSub>>1)&0x7fff))>>15);
break;
case 2: { // cubic
float* cubicTable=DivFilterTables::getCubicTable();
float* t=&cubicTable[((chan[i].audSub&0xffff)>>6)<<2];
float result=(float)s4*t[0]+(float)s5*t[1]+(float)s6*t[2]+(float)s7*t[3];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
case 3: { // sinc
float* sincTable=DivFilterTables::getSincTable8();
float* t1=&sincTable[(8191-((chan[i].audSub&0xffff)>>3))<<2];
float* t2=&sincTable[((chan[i].audSub&0xffff)>>3)<<2];
float result=(
s0*t2[3]+
s1*t2[2]+
s2*t2[1]+
s3*t2[0]+
s4*t1[0]+
s5*t1[1]+
s6*t1[2]+
s7*t1[3]
);
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
default: // none
output=s7;
break;
}
} else {
DivSample* s=parent->getSample(chan[i].sample);
if (s->samples>0) {
while (chan[i].audSub>=0x10000) {
chan[i].audSub-=0x10000;
chan[i].audPos+=((!chan[i].useWave) && chan[i].audDir)?-1:1;
if (chan[i].audDir) {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[i].audPos<s->loopStart) {
chan[i].audPos=s->loopStart+(s->loopStart-chan[i].audPos);
chan[i].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
if (chan[i].audPos<s->loopStart) {
chan[i].audPos=s->loopEnd-1-(s->loopStart-chan[i].audPos);
chan[i].audDir=true;
}
break;
default:
if (chan[i].audPos<0) {
chan[i].sample=-1;
}
break;
}
} else if (chan[i].audPos>=(int)s->samples) {
chan[i].sample=-1;
}
} else {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
if (chan[i].audPos>=s->loopEnd) {
chan[i].audPos=(chan[i].audPos+s->loopStart)-s->loopEnd;
chan[i].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan[i].audPos>=s->loopEnd) {
chan[i].audPos=s->loopEnd-1-(s->loopEnd-1-chan[i].audPos);
chan[i].audDir=true;
}
break;
default:
if (chan[i].audPos>=(int)s->samples) {
chan[i].sample=-1;
}
break;
}
} else if (chan[i].audPos>=(int)s->samples) {
chan[i].sample=-1;
}
}
chan[i].audDat[0]=chan[i].audDat[1];
chan[i].audDat[1]=chan[i].audDat[2];
chan[i].audDat[2]=chan[i].audDat[3];
chan[i].audDat[3]=chan[i].audDat[4];
chan[i].audDat[4]=chan[i].audDat[5];
chan[i].audDat[5]=chan[i].audDat[6];
chan[i].audDat[6]=chan[i].audDat[7];
if (chan[i].audPos>=0 && chan[i].audPos<(int)s->samples) {
chan[i].audDat[7]=s->data16[chan[i].audPos];
} else {
chan[i].audDat[7]=0;
}
}
} else {
chan[i].sample=-1;
chan[i].audSub=0;
chan[i].audPos=0;
}
const short s0=chan[i].audDat[0];
const short s1=chan[i].audDat[1];
const short s2=chan[i].audDat[2];
const short s3=chan[i].audDat[3];
const short s4=chan[i].audDat[4];
const short s5=chan[i].audDat[5];
const short s6=chan[i].audDat[6];
const short s7=chan[i].audDat[7];
switch (interp) {
case 1: // linear
output=s6+(((int)((int)s7-(int)s6)*((chan[i].audSub>>1)&0x7fff))>>15);
break;
case 2: { // cubic
float* cubicTable=DivFilterTables::getCubicTable();
float* t=&cubicTable[((chan[i].audSub&0xffff)>>6)<<2];
float result=(float)s4*t[0]+(float)s5*t[1]+(float)s6*t[2]+(float)s7*t[3];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
case 3: { // sinc
float* sincTable=DivFilterTables::getSincTable8();
float* t1=&sincTable[(8191-((chan[i].audSub&0xffff)>>3))<<2];
float* t2=&sincTable[((chan[i].audSub&0xffff)>>3)<<2];
float result=(
s0*t2[3]+
s1*t2[2]+
s2*t2[1]+
s3*t2[0]+
s4*t1[0]+
s5*t1[1]+
s6*t1[2]+
s7*t1[3]
);
if (result<-32768) result=-32768;
if (result>32767) result=32767;
output=result;
break;
}
default: // none
output=s7;
break;
}
}
}
if (isMuted[i]) {
output=0;
} else {
output=((output*MIN(volMax,chan[i].vol)*MIN(chan[i].envVol,64))>>6)/volMax;
}
oscBuf[i].putSample(h,((output>>depthScale)<<depthScale)>>1);
if (outStereo) {
outSum[0]+=((output*chan[i].panL)>>(depthScale+8))<<depthScale;
outSum[1]+=((output*chan[i].panR)>>(depthScale+8))<<depthScale;
} else {
output=(output>>depthScale)<<depthScale;
outSum[0]+=output;
outSum[1]+=output;
}
}
outSum[0]=(int)((float)outSum[0]*volMult);
outSum[1]=(int)((float)outSum[1]*volMult);
if (outSum[0]<-32768) outSum[0]=-32768;
if (outSum[0]>32767) outSum[0]=32767;
if (outSum[1]<-32768) outSum[1]=-32768;
if (outSum[1]>32767) outSum[1]=32767;
buf[0][h]=outSum[0];
buf[1][h]=outSum[1];
}
for (int i=0; i<chans; i++) {
oscBuf[i].end(len);
}
}
void DivPlatformPCMDAC::tick(bool sysTick) {
chan[0].std.next();
if (chan[0].std.vol.had) {
chan[0].envVol=chan[0].std.vol.val;
}
if (NEW_ARP_STRAT) {
chan[0].handleArp();
} else if (chan[0].std.arp.had) {
if (!chan[0].inPorta) {
chan[0].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[0].note,chan[0].std.arp.val));
for (int i=0; i<chans; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].envVol=chan[i].std.vol.val;
}
chan[0].freqChanged=true;
}
if (chan[0].useWave && chan[0].std.wave.had) {
if (chan[0].wave!=chan[0].std.wave.val || chan[0].ws.activeChanged()) {
chan[0].wave=chan[0].std.wave.val;
chan[0].ws.changeWave1(chan[0].wave);
if (!chan[0].keyOff) chan[0].keyOn=true;
}
}
if (chan[0].useWave && chan[0].active) {
chan[0].ws.tick();
}
if (chan[0].std.pitch.had) {
if (chan[0].std.pitch.mode) {
chan[0].pitch2+=chan[0].std.pitch.val;
CLAMP_VAR(chan[0].pitch2,-32768,32767);
} else {
chan[0].pitch2=chan[0].std.pitch.val;
}
chan[0].freqChanged=true;
}
if (chan[0].std.panL.had) {
int val=chan[0].std.panL.val&0x7f;
chan[0].panL=val*2;
}
if (chan[0].std.panR.had) {
int val=chan[0].std.panR.val&0x7f;
chan[0].panR=val*2;
}
if (chan[0].std.phaseReset.had) {
if (chan[0].std.phaseReset.val==1) {
chan[0].audDir=false;
chan[0].audPos=0;
}
}
if (chan[0].freqChanged || chan[0].keyOn || chan[0].keyOff) {
//DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA);
double off=1.0;
if (!chan[0].useWave && chan[0].sample>=0 && chan[0].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[0].sample);
off=(s->centerRate>=1)?((double)s->centerRate/parent->getCenterRate()):1.0;
}
chan[0].freq=off*parent->calcFreq(chan[0].baseFreq,chan[0].pitch,chan[0].fixedArp?chan[0].baseNoteOverride:chan[0].arpOff,chan[0].fixedArp,false,2,chan[0].pitch2,chipClock,CHIP_FREQBASE);
if (chan[0].freq>16777215) chan[0].freq=16777215;
if (chan[0].keyOn) {
if (!chan[0].std.vol.had) {
chan[0].envVol=64;
if (NEW_ARP_STRAT) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[0].keyOn=false;
chan[i].freqChanged=true;
}
if (chan[0].keyOff) {
chan[0].keyOff=false;
if (chan[i].useWave && chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
chan[i].wave=chan[i].std.wave.val;
chan[i].ws.changeWave1(chan[i].wave);
if (!chan[i].keyOff) chan[i].keyOn=true;
}
}
if (chan[i].useWave && chan[i].active) {
chan[i].ws.tick();
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
if (chan[i].std.panL.had) {
int val=chan[i].std.panL.val&0x7f;
chan[i].panL=val*2;
}
if (chan[i].std.panR.had) {
int val=chan[i].std.panR.val&0x7f;
chan[i].panR=val*2;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chan[i].audDir=false;
chan[i].audPos=0;
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
double off=1.0;
if (!chan[i].useWave && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[i].sample);
off=(s->centerRate>=1)?((double)s->centerRate/parent->getCenterRate()):1.0;
}
chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
if (chan[i].freq>16777215) chan[i].freq=16777215;
if (chan[i].keyOn) {
if (!chan[i].std.vol.had) {
chan[i].envVol=64;
}
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
chan[i].keyOff=false;
}
chan[i].freqChanged=false;
}
chan[0].freqChanged=false;
}
}
int DivPlatformPCMDAC::dispatch(DivCommand c) {
if (c.chan>=chans) return 0;
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
if (ins->amiga.useWave) {
chan[c.chan].sampleNote=DIV_NOTE_NULL;
chan[c.chan].sampleNoteDelta=0;
chan[0].useWave=true;
chan[0].audLen=ins->amiga.waveLen+1;
if (chan[0].insChanged) {
if (chan[0].wave<0) {
chan[0].wave=0;
chan[0].ws.setWidth(chan[0].audLen);
chan[0].ws.changeWave1(chan[0].wave);
chan[c.chan].useWave=true;
chan[c.chan].audLen=ins->amiga.waveLen+1;
if (chan[c.chan].insChanged) {
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.setWidth(chan[c.chan].audLen);
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
}
}
} else {
if (c.value!=DIV_NOTE_NULL) {
chan[0].sample=ins->amiga.getSample(c.value);
chan[c.chan].sample=ins->amiga.getSample(c.value);
chan[c.chan].sampleNote=c.value;
c.value=ins->amiga.getFreq(c.value);
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
@ -340,120 +372,120 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
chan[c.chan].sample=ins->amiga.getSample(chan[c.chan].sampleNote);
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
}
chan[0].useWave=false;
chan[c.chan].useWave=false;
}
if (c.value!=DIV_NOTE_NULL) {
chan[0].baseFreq=round(NOTE_FREQUENCY(c.value));
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value));
}
if (chan[0].useWave || chan[0].sample<0 || chan[0].sample>=parent->song.sampleLen) {
chan[0].sample=-1;
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1;
chan[c.chan].sampleNote=DIV_NOTE_NULL;
chan[c.chan].sampleNoteDelta=0;
}
if (chan[0].setPos) {
chan[0].setPos=false;
if (chan[c.chan].setPos) {
chan[c.chan].setPos=false;
} else {
chan[0].audDir=false;
chan[0].audPos=0;
chan[c.chan].audDir=false;
chan[c.chan].audPos=0;
}
chan[0].audSub=0;
memset(chan[0].audDat,0,8*sizeof(short));
chan[c.chan].audSub=0;
memset(chan[c.chan].audDat,0,8*sizeof(short));
if (c.value!=DIV_NOTE_NULL) {
chan[0].freqChanged=true;
chan[0].note=c.value;
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
chan[0].active=true;
chan[0].keyOn=true;
chan[0].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[0].std.vol.will) {
chan[0].envVol=64;
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].envVol=64;
}
if (chan[0].useWave) {
chan[0].ws.init(ins,chan[0].audLen,255,chan[0].insChanged);
if (chan[c.chan].useWave) {
chan[c.chan].ws.init(ins,chan[c.chan].audLen,255,chan[c.chan].insChanged);
}
chan[0].insChanged=false;
chan[c.chan].insChanged=false;
break;
}
case DIV_CMD_NOTE_OFF:
chan[0].sample=-1;
chan[0].active=false;
chan[0].keyOff=true;
chan[0].macroInit(NULL);
chan[c.chan].sample=-1;
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[0].std.release();
chan[c.chan].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan[0].ins!=c.value || c.value2==1) {
chan[0].ins=c.value;
chan[0].insChanged=true;
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].ins=c.value;
chan[c.chan].insChanged=true;
}
break;
case DIV_CMD_VOLUME:
if (chan[0].vol!=c.value) {
chan[0].vol=c.value;
if (!chan[0].std.vol.has) {
chan[0].envVol=64;
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].envVol=64;
}
}
break;
case DIV_CMD_GET_VOLUME:
return chan[0].vol;
return chan[c.chan].vol;
break;
case DIV_CMD_PANNING:
chan[0].panL=c.value;
chan[0].panR=c.value2;
chan[c.chan].panL=c.value;
chan[c.chan].panR=c.value2;
break;
case DIV_CMD_PITCH:
chan[0].pitch=c.value;
chan[0].freqChanged=true;
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_WAVE:
if (!chan[0].useWave) break;
chan[0].wave=c.value;
chan[0].keyOn=true;
chan[0].ws.changeWave1(chan[0].wave);
if (!chan[c.chan].useWave) break;
chan[c.chan].wave=c.value;
chan[c.chan].keyOn=true;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=round(NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta));
bool return2=false;
if (destFreq>chan[0].baseFreq) {
chan[0].baseFreq+=c.value;
if (chan[0].baseFreq>=destFreq) {
chan[0].baseFreq=destFreq;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[0].baseFreq-=c.value;
if (chan[0].baseFreq<=destFreq) {
chan[0].baseFreq=destFreq;
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[0].freqChanged=true;
chan[c.chan].freqChanged=true;
if (return2) {
chan[0].inPorta=false;
chan[c.chan].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_LEGATO: {
chan[0].baseFreq=round(NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[0].std.arp.val):(0))));
chan[0].freqChanged=true;
chan[0].note=c.value;
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
}
case DIV_CMD_PRE_PORTA:
if (chan[0].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[0].macroInit(parent->getIns(chan[0].ins,DIV_INS_AMIGA));
if (chan[c.chan].active && c.value2) {
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
chan[0].inPorta=c.value;
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:
if (chan[0].useWave) break;
chan[0].audPos=c.value;
chan[0].setPos=true;
if (chan[c.chan].useWave) break;
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
case DIV_CMD_GET_VOLMAX:
return volMax;
@ -474,31 +506,38 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
}
void DivPlatformPCMDAC::muteChannel(int ch, bool mute) {
isMuted=mute;
if (ch>=chans) return;
isMuted[ch]=mute;
}
void DivPlatformPCMDAC::forceIns() {
chan[0].insChanged=true;
chan[0].freqChanged=true;
chan[0].audDir=false;
chan[0].audPos=0;
chan[0].sample=-1;
for (int i=0; i<chans; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].audDir=false;
chan[i].audPos=0;
chan[i].sample=-1;
}
}
void* DivPlatformPCMDAC::getChanState(int ch) {
return &chan;
if (ch>=chans) return NULL;
return &chan[ch];
}
DivDispatchOscBuffer* DivPlatformPCMDAC::getOscBuffer(int ch) {
return oscBuf;
if (ch>=chans) return NULL;
return &oscBuf[ch];
}
void DivPlatformPCMDAC::reset() {
chan[0]=DivPlatformPCMDAC::Channel();
chan[0].std.setEngine(parent);
chan[0].ws.setEngine(parent);
chan[0].ws.init(NULL,32,255);
memset(chan[0].audDat,0,8*sizeof(short));
for (int i=0; i<chans; i++) {
chan[i]=DivPlatformPCMDAC::Channel();
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,255);
memset(chan[i].audDat,0,8*sizeof(short));
}
}
int DivPlatformPCMDAC::getOutputCount() {
@ -506,15 +545,17 @@ int DivPlatformPCMDAC::getOutputCount() {
}
DivMacroInt* DivPlatformPCMDAC::getChanMacroInt(int ch) {
return &chan[0].std;
if (ch>=chans) return NULL;
return &chan[ch].std;
}
unsigned short DivPlatformPCMDAC::getPan(int ch) {
return (chan[0].panL<<8)|chan[0].panR;
if (ch>=chans) return 0;
return (chan[ch].panL<<8)|chan[ch].panR;
}
DivSamplePos DivPlatformPCMDAC::getSamplePos(int ch) {
if (ch>=1) return DivSamplePos();
if (ch>=chans) return DivSamplePos();
return DivSamplePos(
chan[ch].sample,
chan[ch].audPos,
@ -523,19 +564,25 @@ DivSamplePos DivPlatformPCMDAC::getSamplePos(int ch) {
}
void DivPlatformPCMDAC::notifyInsChange(int ins) {
if (chan[0].ins==ins) {
chan[0].insChanged=true;
for (int i=0; i<chans; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformPCMDAC::notifyWaveChange(int wave) {
if (chan[0].useWave && chan[0].wave==wave) {
chan[0].ws.changeWave1(wave);
for (int i=0; i<chans; i++) {
if (chan[i].useWave && chan[i].wave==wave) {
chan[i].ws.changeWave1(wave);
}
}
}
void DivPlatformPCMDAC::notifyInsDeletion(void* ins) {
chan[0].std.notifyInsDeletion((DivInstrument*)ins);
for (int i=0; i<chans; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformPCMDAC::setFlags(const DivConfig& flags) {
@ -547,22 +594,38 @@ void DivPlatformPCMDAC::setFlags(const DivConfig& flags) {
outDepth=(flags.getInt("outDepth",15))&15;
outStereo=flags.getBool("stereo",true);
interp=flags.getInt("interpolation",0);
oscBuf->setRate(rate);
for (int i=0; i<chans; i++) {
oscBuf[i].setRate(rate);
}
volMax=flags.getInt("volMax",255);
if (volMax<1) volMax=1;
volMult=flags.getFloat("volMult",1.0f);
if (volMult<0.0f) volMult=0.0f;
if (volMult>1.0f) volMult=1.0f;
}
int DivPlatformPCMDAC::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
oscBuf=new DivDispatchOscBuffer;
isMuted=false;
oscBuf=new DivDispatchOscBuffer[channels];
chan=new Channel[channels];
isMuted=new bool[channels];
chans=channels;
for (int i=0; i<channels; i++) {
isMuted[i]=false;
}
setFlags(flags);
reset();
return 1;
}
void DivPlatformPCMDAC::quit() {
delete oscBuf;
delete[] chan;
delete[] isMuted;
delete[] oscBuf;
chan=NULL;
isMuted=NULL;
oscBuf=NULL;
chans=0;
}

View file

@ -52,9 +52,10 @@ class DivPlatformPCMDAC: public DivDispatch {
setPos(false),
envVol(64) {}
};
Channel chan[1];
Channel* chan;
bool* isMuted;
int chans;
DivDispatchOscBuffer* oscBuf;
bool isMuted;
int outDepth;
// valid values:
// - 0: none
@ -64,6 +65,7 @@ class DivPlatformPCMDAC: public DivDispatch {
int interp;
int volMax;
bool outStereo;
float volMult;
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
@ -87,6 +89,8 @@ class DivPlatformPCMDAC: public DivDispatch {
void notifyInsDeletion(void* ins);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
DivPlatformPCMDAC():
chan(NULL), isMuted(NULL), chans(0) {}
};
#endif

View file

@ -470,7 +470,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -537,9 +537,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -158,7 +158,7 @@ int DivPlatformPET::dispatch(DivCommand c) {
chan[0].active=true;
chan[0].keyOn=true;
chan[0].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[0].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[0].std.vol.will) {
chan[0].outVol=chan[0].vol;
}
break;
@ -227,9 +227,9 @@ int DivPlatformPET::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[0].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[0].macroInit(parent->getIns(chan[0].ins,DIV_INS_PET));
if (parent->song.compatFlags.resetMacroOnPorta) chan[0].macroInit(parent->getIns(chan[0].ins,DIV_INS_PET));
}
if (!chan[0].inPorta && c.value && !parent->song.brokenPortaArp && chan[0].std.arp.will && !NEW_ARP_STRAT) chan[0].baseFreq=NOTE_PERIODIC(chan[0].note);
if (!chan[0].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[0].std.arp.will && !NEW_ARP_STRAT) chan[0].baseFreq=NOTE_PERIODIC(chan[0].note);
chan[0].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -175,7 +175,7 @@ int DivPlatformPokeMini::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -241,9 +241,9 @@ int DivPlatformPokeMini::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -184,7 +184,7 @@ void DivPlatformPOKEY::tick(bool sysTick) {
for (int i=0; i<4; i++) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,parent->song.linearPitch?chan[i].pitch:0,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,parent->song.linearPitch?chan[i].pitch2:0,chipClock,CHIP_DIVIDER);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,parent->song.compatFlags.linearPitch?chan[i].pitch:0,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,parent->song.compatFlags.linearPitch?chan[i].pitch2:0,chipClock,CHIP_DIVIDER);
if ((i==0 && !(audctl&64)) || (i==2 && !(audctl&32)) || i==1 || i==3) {
chan[i].freq/=7;
@ -223,7 +223,7 @@ void DivPlatformPOKEY::tick(bool sysTick) {
}
// non-linear pitch
if (!parent->song.linearPitch) {
if (!parent->song.compatFlags.linearPitch) {
chan[i].freq-=chan[i].pitch;
}
@ -297,7 +297,7 @@ int DivPlatformPOKEY::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
chan[c.chan].ctlChanged=true;
}
@ -383,9 +383,9 @@ int DivPlatformPOKEY::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEY));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEY));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -101,7 +101,7 @@ int DivPlatformPong::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -168,9 +168,9 @@ int DivPlatformPong::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -288,7 +288,7 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].keyOn=true;
@ -372,9 +372,9 @@ int DivPlatformPowerNoise::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA: {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POWERNOISE));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
chan[c.chan].inPorta=c.value;

View file

@ -182,9 +182,9 @@ int DivPlatformPV1000::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PV1000));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PV1000));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -488,7 +488,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chan[c.chan].keyOff=false;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (chan[c.chan].isNewQSound) {
chan[c.chan].resVol=(chan[c.chan].outVol*16383)/255;
@ -594,9 +594,9 @@ int DivPlatformQSound::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -210,7 +210,7 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -284,9 +284,9 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -211,7 +211,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (isMuted[c.chan]) {
@ -261,13 +261,13 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch)?1:(8-chan[c.chan].freqH));
chan[c.chan].baseFreq+=c.value*((parent->song.compatFlags.linearPitch)?1:(8-chan[c.chan].freqH));
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch)?1:(8-chan[c.chan].freqH));
chan[c.chan].baseFreq-=c.value*((parent->song.compatFlags.linearPitch)?1:(8-chan[c.chan].freqH));
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
@ -318,9 +318,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -172,7 +172,7 @@ int DivPlatformSCC::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (!isMuted[c.chan]) {
@ -253,9 +253,9 @@ int DivPlatformSCC::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -100,7 +100,7 @@ void DivPlatformSCV::tick(bool sysTick) {
} else {
chan[i].freq=(chan[i].baseFreq+chan[i].pitch+chan[i].pitch2+143);
}
if (!parent->song.oldArpStrategy) {
if (!parent->song.compatFlags.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride)+chan[i].pitch+chan[i].pitch2;
} else {
@ -186,7 +186,7 @@ int DivPlatformSCV::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
//chwrite(c.chan,0x04,0x80|chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
@ -267,9 +267,9 @@ int DivPlatformSCV::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_UPD1771C));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_UPD1771C));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) {
chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
}
chan[c.chan].inPorta=c.value;

View file

@ -66,7 +66,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
for (int i=0; i<16; i++) {
chan[i].std.next();
if (parent->song.newSegaPCM) {
if (parent->song.compatFlags.newSegaPCM) {
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
@ -89,13 +89,13 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
chan[i].pcm.freq=-1;
}
if (parent->song.newSegaPCM) if (chan[i].std.panL.had) {
if (parent->song.compatFlags.newSegaPCM) if (chan[i].std.panL.had) {
chan[i].chPanL=chan[i].std.panL.val&127;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
rWrite(2+(i<<3),chan[i].chVolL);
}
if (parent->song.newSegaPCM) if (chan[i].std.panR.had) {
if (parent->song.compatFlags.newSegaPCM) if (chan[i].std.panR.had) {
chan[i].chPanR=chan[i].std.panR.val&127;
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
rWrite(3+(i<<3),chan[i].chVolR);
@ -120,7 +120,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch)-128+(oldSlides?0:chan[i].pitch2);
if (!parent->song.oldArpStrategy) {
if (!parent->song.compatFlags.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride<<7)+chan[i].pitch-128+(chan[i].pitch2<<(oldSlides?1:0));
} else {
@ -204,10 +204,10 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].pcm.freq=-1;
}
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (parent->song.newSegaPCM) {
if (parent->song.compatFlags.newSegaPCM) {
chan[c.chan].chVolL=(chan[c.chan].outVol*chan[c.chan].chPanL)/127;
chan[c.chan].chVolR=(chan[c.chan].outVol*chan[c.chan].chPanR)/127;
rWrite(2+(c.chan<<3),chan[c.chan].chVolL);
@ -240,7 +240,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
if (parent->song.newSegaPCM) {
if (parent->song.compatFlags.newSegaPCM) {
chan[c.chan].chVolL=(c.value*chan[c.chan].chPanL)/127;
chan[c.chan].chVolR=(c.value*chan[c.chan].chPanR)/127;
} else {
@ -262,7 +262,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (parent->song.newSegaPCM) {
if (parent->song.compatFlags.newSegaPCM) {
chan[c.chan].chPanL=c.value>>1;
chan[c.chan].chPanR=c.value2>>1;
chan[c.chan].chVolL=(chan[c.chan].outVol*chan[c.chan].chPanL)/127;
@ -284,7 +284,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
case DIV_CMD_NOTE_PORTA: {
int destFreq=((c.value2+chan[c.chan].sampleNoteDelta)<<7);
int newFreq;
int mul=(oldSlides || !parent->song.linearPitch)?8:1;
int mul=(oldSlides || !parent->song.compatFlags.linearPitch)?8:1;
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value*mul;
@ -334,7 +334,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
return 127;
break;
case DIV_CMD_PRE_PORTA:
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=(chan[c.chan].note<<7);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=(chan[c.chan].note<<7);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -445,12 +445,12 @@ int DivPlatformSID2::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta || parent->song.preNoteNoEffect) {
if (parent->song.compatFlags.resetMacroOnPorta || parent->song.compatFlags.preNoteNoEffect) {
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SID2));
chan[c.chan].keyOn=true;
}
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -845,7 +845,7 @@ int DivPlatformSID3::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
//chan[c.chan].keyOn=true;
@ -997,12 +997,12 @@ int DivPlatformSID3::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta || parent->song.preNoteNoEffect) {
if (parent->song.compatFlags.resetMacroOnPorta || parent->song.compatFlags.preNoteNoEffect) {
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SID3));
chan[c.chan].keyOn=true;
}
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PANNING: {

View file

@ -208,7 +208,7 @@ int DivPlatformSM8521::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
@ -267,13 +267,13 @@ int DivPlatformSM8521::dispatch(DivCommand c) {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch)?1:8);
chan[c.chan].baseFreq+=c.value*((parent->song.compatFlags.linearPitch)?1:8);
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch)?1:8);
chan[c.chan].baseFreq-=c.value*((parent->song.compatFlags.linearPitch)?1:8);
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
@ -293,9 +293,9 @@ int DivPlatformSM8521::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SM8521));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SM8521));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -190,7 +190,7 @@ void DivPlatformSMS::acquireDirect(blip_buffer_t** bb, size_t len) {
double DivPlatformSMS::NOTE_SN(int ch, int note) {
double CHIP_DIVIDER=toneDivider;
if (ch==3) CHIP_DIVIDER=noiseDivider;
if (parent->song.linearPitch || !easyNoise) {
if (parent->song.compatFlags.linearPitch || !easyNoise) {
return NOTE_PERIODIC(note);
}
int easyStartingPeriod=16;
@ -210,7 +210,7 @@ int DivPlatformSMS::snCalcFreq(int ch) {
if (chan[ch].fixedArp) {
curFreq=chan[ch].baseNoteOverride<<7;
}
if (parent->song.linearPitch && easyNoise && curFreq>easyThreshold) {
if (parent->song.compatFlags.linearPitch && easyNoise && curFreq>easyThreshold) {
int ret=(((easyStartingPeriod<<7))-(curFreq-(easyThreshold)))>>7;
if (ret<0) ret=0;
return ret;
@ -242,7 +242,7 @@ void DivPlatformSMS::tick(bool sysTick) {
}
if (i==3) {
if (chan[i].std.duty.had) {
if (chan[i].std.duty.val!=snNoiseMode || parent->song.snDutyReset) {
if (chan[i].std.duty.val!=snNoiseMode || parent->song.compatFlags.snDutyReset) {
snNoiseMode=chan[i].std.duty.val;
if (chan[i].std.duty.val<2) {
chan[3].freqChanged=false;
@ -277,7 +277,7 @@ void DivPlatformSMS::tick(bool sysTick) {
if (chan[i].freqChanged) {
chan[i].freq=snCalcFreq(i);
if (chan[i].freq>1023) chan[i].freq=1023;
if (parent->song.snNoLowPeriods) {
if (parent->song.compatFlags.snNoLowPeriods) {
if (chan[i].freq<8) chan[i].freq=1;
} else {
if (chan[i].freq<0) chan[i].freq=0;
@ -297,7 +297,7 @@ void DivPlatformSMS::tick(bool sysTick) {
chan[3].freq=snCalcFreq(3);
//parent->calcFreq(chan[3].baseFreq,chan[3].pitch,chan[3].fixedArp?chan[3].baseNoteOverride:chan[3].arpOff,chan[3].fixedArp,true,0,chan[3].pitch2,chipClock,noiseDivider);
if (chan[3].freq>1023) chan[3].freq=1023;
if (parent->song.snNoLowPeriods) {
if (parent->song.compatFlags.snNoLowPeriods) {
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
}
if (chan[3].freq<0) chan[3].freq=0;
@ -359,13 +359,13 @@ int DivPlatformSMS::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOff=false;
//if (!parent->song.brokenOutVol2) {
//if (!parent->song.compatFlags.brokenOutVol2) {
chan[c.chan].writeVol=true;
chan[c.chan].outVol=chan[c.chan].vol;
//rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
//}
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -451,9 +451,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_SN(c.chan,chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_SN(c.chan,chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -390,7 +390,7 @@ int DivPlatformSNES::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
// this is the fix. it needs testing.
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
if (chan[c.chan].outVol!=chan[c.chan].vol) chan[c.chan].shallWriteVol=true;
chan[c.chan].outVol=chan[c.chan].vol;
}
@ -482,7 +482,7 @@ int DivPlatformSNES::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SNES));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SNES));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -327,7 +327,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
chan[c.chan].hwSeqDelay=0;
chWrite(c.chan,0x02,chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
@ -484,13 +484,13 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
int destFreq=NOTE_SU(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch)?1:(1+(chan[c.chan].baseFreq>>9)));
chan[c.chan].baseFreq+=c.value*((parent->song.compatFlags.linearPitch)?1:(1+(chan[c.chan].baseFreq>>9)));
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch)?1:(1+(chan[c.chan].baseFreq>>9)));
chan[c.chan].baseFreq-=c.value*((parent->song.compatFlags.linearPitch)?1:(1+(chan[c.chan].baseFreq>>9)));
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
@ -519,9 +519,9 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SU));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SU));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_SU(c.chan,chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_SU(c.chan,chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_C64_PW_SLIDE:

View file

@ -297,7 +297,7 @@ int DivPlatformSupervision::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
//chwrite(c.chan,0x04,0x80|chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
@ -384,9 +384,9 @@ int DivPlatformSupervision::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SUPERVISION));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SUPERVISION));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -405,7 +405,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
@ -538,9 +538,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -95,7 +95,7 @@ void DivPlatformT6W28::writeOutVol(int ch) {
double DivPlatformT6W28::NOTE_SN(int ch, int note) {
double CHIP_DIVIDER=16;
if (ch==3) CHIP_DIVIDER=15;
if (parent->song.linearPitch || !easyNoise) {
if (parent->song.compatFlags.linearPitch || !easyNoise) {
return NOTE_PERIODIC(note);
}
if (note>107) {
@ -105,7 +105,7 @@ double DivPlatformT6W28::NOTE_SN(int ch, int note) {
}
int DivPlatformT6W28::snCalcFreq(int ch) {
if (parent->song.linearPitch && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(107<<7)) {
if (parent->song.compatFlags.linearPitch && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(107<<7)) {
int ret=(((13<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(107<<7)))>>7;
if (ret<0) ret=0;
return ret;
@ -187,7 +187,7 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
writeOutVol(c.chan);
}
@ -270,9 +270,9 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_T6W28));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_T6W28));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_SN(c.chan,chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_SN(c.chan,chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -149,7 +149,7 @@ int DivPlatformTED::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
@ -230,9 +230,9 @@ int DivPlatformTED::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TED));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TED));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -295,7 +295,7 @@ int DivPlatformTIA::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
rWrite(0x15+c.chan,chan[c.chan].shape);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].insChanged) {
@ -395,7 +395,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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TIA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TIA));
}
chan[c.chan].inPorta=c.value;
break;

View file

@ -229,7 +229,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
if (chan[i].std.alg.had) {
chan[i].state.alg=chan[i].std.alg.val;
immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0x40:0)|(chan[i].chVolR<<7));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i] || !op.enable) {
@ -321,7 +321,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
}
// fixed pitch
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
bool freqChangeOp=false;
if (op.egt) {
@ -416,7 +416,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
for (int i=0; i<8; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+chan[i].pitch-128+chan[i].pitch2;
if (!parent->song.oldArpStrategy) {
if (!parent->song.compatFlags.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride<<7)+chan[i].pitch-128+chan[i].pitch2;
} else {
@ -1006,7 +1006,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
return 127;
break;
case DIV_CMD_PRE_PORTA:
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_LINEAR(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:

View file

@ -297,7 +297,7 @@ int DivPlatformVB::dispatch(DivCommand c) {
}
chWrite(4,0x00,0x80);
}
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
writeEnv(c.chan);
}
@ -445,9 +445,9 @@ int DivPlatformVB::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -347,7 +347,7 @@ int DivPlatformVERA::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -421,7 +421,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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA));
}
if (!chan[c.chan].inPorta && c.value && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=calcNoteFreq(c.chan,chan[c.chan].note);
chan[c.chan].inPorta=c.value;

View file

@ -247,9 +247,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VIC));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VIC));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -297,7 +297,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
//chan[c.chan].keyOn=true;
@ -418,9 +418,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -614,7 +614,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chan[c.chan].envChanged=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
@ -729,9 +729,9 @@ 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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NoteX1_010(c.chan,chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_FREQ:

View file

@ -551,7 +551,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
if (chan[i].std.alg.had) {
chan[i].state.alg=chan[i].std.alg.val;
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i] || !op.enable) {
@ -661,7 +661,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
for (int i=0; i<3; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[i].state.block);
} else {
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2);
@ -797,7 +797,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
if (parent->song.compatFlags.brokenFMOff) chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
@ -867,7 +867,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
}
break;
}
if (c.chan>(psgChanOffs-1) || parent->song.linearPitch) { // PSG
if (c.chan>(psgChanOffs-1) || parent->song.compatFlags.linearPitch) { // PSG
int destFreq=NOTE_FNUM_BLOCK(c.value2,11,chan[c.chan].state.block);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -1156,7 +1156,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
case DIV_CMD_PRE_PORTA:
if (c.chan>(2+isCSM)) {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
}
}
chan[c.chan].inPorta=c.value;

View file

@ -146,7 +146,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>opChan[ch].baseFreq) {
@ -472,7 +472,7 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
@ -551,7 +551,7 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[extChanOffs].state.block);
} else {
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2);

View file

@ -794,7 +794,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
if (chan[i].std.alg.had) {
chan[i].state.alg=chan[i].std.alg.val;
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i] || !op.enable) {
@ -916,7 +916,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
for (int i=0; i<6; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[i].state.block);
} else {
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2);
@ -1238,7 +1238,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
if (parent->song.compatFlags.brokenFMOff) chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
@ -1340,7 +1340,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
}
break;
}
if (c.chan>(5+isCSM) || parent->song.linearPitch) { // PSG, ADPCM-B
if (c.chan>(5+isCSM) || parent->song.compatFlags.linearPitch) { // PSG, ADPCM-B
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -1651,7 +1651,7 @@ int DivPlatformYM2608::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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
}
}
chan[c.chan].inPorta=c.value;

View file

@ -150,7 +150,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
} else {
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int i=0; i<4; i++) {
if (ch==i) continue;
opChan[i].pan=opChan[ch].pan;
@ -166,7 +166,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>opChan[ch].baseFreq) {
@ -510,7 +510,7 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
@ -541,7 +541,7 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
if (opChan[i].std.panL.had) {
opChan[i].pan=opChan[i].std.panL.val&3;
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int j=0; j<4; j++) {
if (i==j) continue;
opChan[j].pan=opChan[i].pan;
@ -612,7 +612,7 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[extChanOffs].state.block);
} else {
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2);

View file

@ -714,7 +714,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
if (chan[i].std.alg.had) {
chan[i].state.alg=chan[i].std.alg.val;
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i] || !op.enable) {
@ -836,7 +836,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==1 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[i].state.block);
} else {
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11);
@ -1178,7 +1178,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
if (parent->song.compatFlags.brokenFMOff) chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
@ -1280,7 +1280,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
}
break;
}
if (c.chan>=psgChanOffs || parent->song.linearPitch) { // PSG, ADPCM-B
if (c.chan>=psgChanOffs || parent->song.compatFlags.linearPitch) { // PSG, ADPCM-B
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -1591,7 +1591,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
case DIV_CMD_PRE_PORTA:
if (c.chan>=psgChanOffs) {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
}
}
chan[c.chan].inPorta=c.value;

View file

@ -783,7 +783,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
if (chan[i].std.alg.had) {
chan[i].state.alg=chan[i].std.alg.val;
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i] || !op.enable) {
@ -905,7 +905,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
for (int i=0; i<(psgChanOffs-isCSM); i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[i].state.block);
} else {
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,4,chan[i].pitch2);
@ -1247,7 +1247,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
if (parent->song.compatFlags.brokenFMOff) chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
@ -1349,7 +1349,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
}
break;
}
if (c.chan>=psgChanOffs || parent->song.linearPitch) { // PSG, ADPCM-B
if (c.chan>=psgChanOffs || parent->song.compatFlags.linearPitch) { // PSG, ADPCM-B
int destFreq=NOTE_OPNB(c.chan,c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -1660,7 +1660,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
case DIV_CMD_PRE_PORTA:
if (c.chan>=psgChanOffs) {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM));
}
}
chan[c.chan].inPorta=c.value;

View file

@ -146,7 +146,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
} else {
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int i=0; i<4; i++) {
if (ch==i) continue;
opChan[i].pan=opChan[ch].pan;
@ -162,7 +162,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>opChan[ch].baseFreq) {
@ -503,7 +503,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
@ -534,7 +534,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
if (opChan[i].std.panL.had) {
opChan[i].pan=opChan[i].std.panL.val&3;
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int j=0; j<4; j++) {
if (i==j) continue;
opChan[j].pan=opChan[i].pan;
@ -604,7 +604,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[extChanOffs].state.block);
} else {
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2);

View file

@ -146,7 +146,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
} else {
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
}
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int i=0; i<4; i++) {
if (ch==i) continue;
opChan[i].pan=opChan[ch].pan;
@ -162,7 +162,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
int destFreq=NOTE_FREQUENCY(c.value2);
bool return2=false;
if (destFreq>opChan[ch].baseFreq) {
@ -503,7 +503,7 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
if (opChan[i].std.alg.had) {
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
if (!parent->song.compatFlags.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
if (isOpMuted[j] || !op.enable) {
@ -534,7 +534,7 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
if (opChan[i].std.panL.had) {
opChan[i].pan=opChan[i].std.panL.val&3;
if (parent->song.sharedExtStat) {
if (parent->song.compatFlags.sharedExtStat) {
for (int j=0; j<4; j++) {
if (i==j) continue;
opChan[j].pan=opChan[i].pan;
@ -604,7 +604,7 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
unsigned char hardResetMask=0;
if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) {
if (parent->song.linearPitch) {
if (parent->song.compatFlags.linearPitch) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11,chan[extChanOffs].state.block);
} else {
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,opChan[i].fixedArp?opChan[i].baseNoteOverride:opChan[i].arpOff,opChan[i].fixedArp,false,4,opChan[i].pitch2);

View file

@ -241,7 +241,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
@ -287,7 +287,7 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_FREQUENCY(c.value2+chan[c.chan].sampleNoteDelta);
bool return2=false;
int multiplier=(parent->song.linearPitch)?1:256;
int multiplier=(parent->song.compatFlags.linearPitch)?1:256;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*multiplier;
if (chan[c.chan].baseFreq>=destFreq) {
@ -316,9 +316,9 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:

View file

@ -212,9 +212,9 @@ int DivPlatformZXBeeper::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -190,7 +190,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
if (!parent->song.compatFlags.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
writeOutVol(c.chan);
}
@ -293,9 +293,9 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
if (parent->song.compatFlags.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.compatFlags.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:

View file

@ -496,18 +496,23 @@ int DivEngine::dispatchCmd(DivCommand c) {
}
}
// don't dispatch if the channel doesn't exist
if (song.dispatchChanOfChan[c.dis]<0) {
return 0;
}
// map the channel to channel of chip
// c.dis is a copy of c.chan because we'll use it in the next call
c.chan=dispatchChanOfChan[c.dis];
c.chan=song.dispatchChanOfChan[c.dis];
// dispatch command to chip dispatch
return disCont[dispatchOfChan[c.dis]].dispatch->dispatch(c);
return disCont[song.dispatchOfChan[c.dis]].dispatch->dispatch(c);
}
// this function handles per-chip normal effects
bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) {
// don't process invalid chips
DivSysDef* sysDef=sysDefs[sysOfChan[ch]];
DivSysDef* sysDef=sysDefs[song.sysOfChan[ch]];
if (sysDef==NULL) return false;
// find the effect handler
auto iter=sysDef->effectHandlers.find(effect);
@ -530,7 +535,7 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
// this handles per-chip post effects...
bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal) {
// don't process invalid chips
DivSysDef* sysDef=sysDefs[sysOfChan[ch]];
DivSysDef* sysDef=sysDefs[song.sysOfChan[ch]];
if (sysDef==NULL) return false;
// find the effect handler
auto iter=sysDef->postEffectHandlers.find(effect);
@ -552,7 +557,7 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
// ...and this handles chip pre-effects
bool DivEngine::perSystemPreEffect(int ch, unsigned char effect, unsigned char effectVal) {
DivSysDef* sysDef=sysDefs[sysOfChan[ch]];
DivSysDef* sysDef=sysDefs[song.sysOfChan[ch]];
if (sysDef==NULL) return false;
auto iter=sysDef->preEffectHandlers.find(effect);
if (iter==sysDef->preEffectHandlers.end()) return false;
@ -659,9 +664,9 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - 2: DefleMask (same as 1)
// in the case of normal, the jump row (changePos) is not reset to 0
// this means that you can do 0Dxx 0Byy and it'll work, taking you to row xx of order yy
if (changeOrd==-1 || song.jumpTreatment==0) {
if (changeOrd==-1 || song.compatFlags.jumpTreatment==0) {
changeOrd=effectVal;
if (song.jumpTreatment==1 || song.jumpTreatment==2) {
if (song.compatFlags.jumpTreatment==1 || song.compatFlags.jumpTreatment==2) {
changePos=0;
}
}
@ -671,23 +676,23 @@ void DivEngine::processRow(int i, bool afterDelay) {
// if there is a 0Dxx effect on the very last order, it is ignored
// COMPAT FLAG: simultaneous jump treatment
if (song.jumpTreatment==2) {
if (song.compatFlags.jumpTreatment==2) {
// - 2: DefleMask (jump to next order unless it is the last one and ignoreJumpAtEnd is on)
if ((curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
if ((curOrder<(curSubSong->ordersLen-1) || !song.compatFlags.ignoreJumpAtEnd)) {
// changeOrd -2 means increase order by 1
// it overrides a previous 0Bxx effect
changeOrd=-2;
changePos=effectVal;
}
} else if (song.jumpTreatment==1) {
} else if (song.compatFlags.jumpTreatment==1) {
// - 1: old Furnace (same as 2 but ignored if 0Bxx is present)
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.compatFlags.ignoreJumpAtEnd)) {
changeOrd=-2;
changePos=effectVal;
}
} else {
// - 0: normal
if (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd) {
if (curOrder<(curSubSong->ordersLen-1) || !song.compatFlags.ignoreJumpAtEnd) {
// set the target order if not set, allowing you to use 0B and 0D regardless of position
if (changeOrd<0) {
changeOrd=-2;
@ -700,13 +705,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal!=0) {
// COMPAT FLAG: cut/delay effect policy (delayBehavior)
// - 0: strict
// - delays equal or greater to the speed * timeBase are ignored
// - delays equal or greater to the speed are ignored (formerly time base was involved but that has been removed now)
// - 1: strict old
// - delays equal or greater to the speed are ignored
// - delays greater to the speed are ignored
// - 2: lax (default)
// - no delay is ever ignored unless overridden by another
bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed*(curSubSong->timeBase+1)));
if (song.delayBehavior==2) comparison=true;
bool comparison=(song.compatFlags.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed));
if (song.compatFlags.delayBehavior==2) comparison=true;
if (comparison) {
// set the delay row, order and timer
chan[i].rowDelay=effectVal;
@ -721,7 +726,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// this hack is disabled due to its dirtiness and the fact I
// don't feel like being compatible with a buggy tracker any further
if (effectVal==nextSpeed) {
//if (sysOfChan[i]!=DIV_SYSTEM_YM2610 && sysOfChan[i]!=DIV_SYSTEM_YM2610_EXT) chan[i].delayLocked=true;
//if (song.sysOfChan[i]!=DIV_SYSTEM_YM2610 && song.sysOfChan[i]!=DIV_SYSTEM_YM2610_EXT) chan[i].delayLocked=true;
} else {
chan[i].delayLocked=false;
}
@ -757,7 +762,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: legacy volume slides
// - sets volume to max once a vol slide down has finished (thus setting volume to volMax+1)
if (song.legacyVolumeSlides && chan[i].volume==chan[i].volMax+1) {
if (song.compatFlags.legacyVolumeSlides && chan[i].volume==chan[i].volMax+1) {
logV("forcing volume");
chan[i].volume=chan[i].volMax;
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
@ -775,7 +780,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: reset slides on note off (inverted in the GUI)
// - a portamento/pitch slide will be halted upon encountering note off
// - this will not occur if the stopPortaOnNoteOff flag is on and this is a portamento
if (chan[i].inPorta && song.noteOffResetsSlides) {
if (chan[i].inPorta && song.compatFlags.noteOffResetsSlides) {
// stopOnOff will be false if stopPortaOnNoteOff flag is off
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
@ -784,15 +789,10 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].stopOnOff=false;
}
// depending on the system, portamento may still be disabled
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
// this here is a now-disabled hack which makes the noise channel also stop when square 3 is
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
}*/
}
// another compatibility hack which schedules a second reset later just in case
chan[i].scheduledSlideReset=true;
@ -805,21 +805,17 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].keyOn=false;
chan[i].keyOff=true;
// same thing here regarding reset slide behavior
if (chan[i].inPorta && song.noteOffResetsSlides) {
if (chan[i].inPorta && song.compatFlags.noteOffResetsSlides) {
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
}*/
}
chan[i].scheduledSlideReset=true;
}
@ -839,7 +835,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// ...unless there's a way to trigger keyOn twice
if (!chan[i].keyOn) {
// the behavior of arpeggio reset upon note off varies per system
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsArp(dispatchChanOfChan[i])) {
if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsArp(song.dispatchChanOfChan[i])) {
chan[i].arp=0;
dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp));
}
@ -847,7 +843,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].doNote=true;
// COMPAT FLAG: compatible arpeggio
// - once a new note plays, arp will not be applied for this tick
if (chan[i].arp!=0 && song.compatibleArpeggio) {
if (chan[i].arp!=0 && song.compatFlags.compatibleArpeggio) {
chan[i].arpYield=true;
}
}
@ -876,7 +872,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: legacy ALWAYS_SET_VOLUME behavior (oldAlwaysSetVolume)
// - prior to its addition, volume changes wouldn't be effective depending on the system if the volume is the same as the current one
// - afterwards, volume change is made regardless in order to set the bottom byte of volume ("subvolume")
if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->newData[whatRow][DIV_PAT_VOL]) {
if (!song.compatFlags.oldAlwaysSetVolume || disCont[song.dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->newData[whatRow][DIV_PAT_VOL]) {
// here we let dispatchCmd() know we can do MIDI aftertouch if there isn't a note
if (pat->newData[whatRow][DIV_PAT_NOTE]==-1) {
chan[i].midiAftertouch=true;
@ -986,7 +982,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - 02xx still works
// - a previous portamento (03xx) will prevent this slide from occurring
// - E1xy/E2xy also will if *another* flag is set
if (song.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break;
if (song.compatFlags.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break;
lastSlide=0x01;
if (effectVal==0) {
chan[i].portaNote=-1;
@ -998,12 +994,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - this prompts dispatch to stop processing arp macros during a slide
// - this only happens if pitch linearity is set to None
// - if we don't let dispatch know, the slide will never occur as arp takes over
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
if (!song.compatFlags.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
// COMPAT FLAG: limit slide range
// - this confines pitch slides from dispatch->getPortaFloor to C-8 (I think)
// - yep, the lowest portamento note depends on the system...
chan[i].portaNote=song.limitSlides?0x60:255;
chan[i].portaNote=song.compatFlags.limitSlides?0x60:255;
chan[i].portaSpeed=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
// most of these are used for compat flag handling
@ -1017,7 +1013,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - this prompts dispatch to stop processing arp macros during a slide
// - this only happens if pitch linearity is set to None
// - if we don't let dispatch know, the slide will never occur as arp takes over
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
if (!song.compatFlags.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
}
break;
case 0x02: // pitch slide down
@ -1026,7 +1022,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - 01xx still works
// - a previous portamento (03xx) will prevent this slide from occurring
// - E1xy/E2xy also will if *another* flag is set
if (song.ignoreDuplicateSlides && (lastSlide==0x02 || lastSlide==0x1337)) break;
if (song.compatFlags.ignoreDuplicateSlides && (lastSlide==0x02 || lastSlide==0x1337)) break;
lastSlide=0x02;
if (effectVal==0) {
chan[i].portaNote=-1;
@ -1034,12 +1030,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].inPorta=false;
// COMPAT FLAG: arpeggio inhibits non-porta slides
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
if (!song.compatFlags.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
// COMPAT FLAG: limit slide range
// - this confines pitch slides from dispatch->getPortaFloor to C-8 (I think)
// - yep, the lowest portamento note depends on the system...
chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60;
chan[i].portaNote=(song.compatFlags.limitSlides && song.dispatchChanOfChan[i]>=0)?disCont[song.dispatchOfChan[i]].dispatch->getPortaFloor(song.dispatchChanOfChan[i]):-60;
chan[i].portaSpeed=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].portaStop=true;
@ -1048,7 +1044,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].wasShorthandPorta=false;
chan[i].inPorta=false;
// COMPAT FLAG: arpeggio inhibits non-porta slides
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
if (!song.compatFlags.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
}
break;
case 0x03: // portamento
@ -1068,7 +1064,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: buggy portamento after sliding
// - you might want to slide up or down and then 03xx to return to the original note
// - if a porta to the same note is attempted after slide, for some reason it does not occur
if (chan[i].note==chan[i].oldNote && !chan[i].inPorta && song.buggyPortaAfterSlide) {
if (chan[i].note==chan[i].oldNote && !chan[i].inPorta && song.compatFlags.buggyPortaAfterSlide) {
chan[i].portaNote=chan[i].note;
chan[i].portaSpeed=-1;
} else {
@ -1088,7 +1084,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: stop portamento on note off
// - if a portamento is called and then a note off occurs, stop portamento before the next note
// - ...unless noteOffResetsSlides is disabled
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
chan[i].stopOnOff=song.compatFlags.stopPortaOnNoteOff; // what?!
chan[i].scheduledSlideReset=false;
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,1));
// this is used to inhibit any other slide commands if the respective compat flag is enabled
@ -1148,7 +1144,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
calledPorta=true;
// COMPAT FLAG: buggy portamento after sliding
// yes, this also affects 06xy.
if (chan[i].note==chan[i].oldNote && !chan[i].inPorta && song.buggyPortaAfterSlide) {
if (chan[i].note==chan[i].oldNote && !chan[i].inPorta && song.compatFlags.buggyPortaAfterSlide) {
chan[i].portaNote=chan[i].note;
chan[i].portaSpeed=-1;
} else {
@ -1161,7 +1157,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// this is the same as 03xx.
chan[i].portaStop=true;
if (chan[i].keyOn) chan[i].doNote=false;
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
chan[i].stopOnOff=song.compatFlags.stopPortaOnNoteOff; // what?!
chan[i].scheduledSlideReset=false;
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,1));
lastSlide=0x1337; // i hate this so much
@ -1228,7 +1224,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].arp=effectVal;
// COMPAT FLAG: reset note to base on arp stop (inverted in the GUI)
// - a 0000 effect resets arpeggio position
if (chan[i].arp==0 && song.arp0Reset) {
if (chan[i].arp==0 && song.compatFlags.arp0Reset) {
chan[i].resetArp=true;
}
dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp));
@ -1250,7 +1246,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: old sample offset effect
// - before 0.6.3 the sample offset effect was 9xxx, where `xxx` is multiplied by 256
// - the effect was then changed to 90xx/91xx/92xx, allowing you to set the low, mid and high bytes of the offset respectively
if (song.oldSampleOffset) {
if (song.compatFlags.oldSampleOffset) {
// send sample position now
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_POS,i,(((effect&0x0f)<<8)|effectVal)*256));
} else {
@ -1283,7 +1279,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - ignore cut if equal or greater than speed
// - 2: lax (default)
// - no cut is ever ignored unless overridden by another
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
if (effectVal>0 && (song.compatFlags.delayBehavior==2 || effectVal<nextSpeed)) {
// the cut timer is ticked after nextRow(), so we set it one tick higher.
chan[i].volCut=effectVal+1;
chan[i].cutType=0;
@ -1324,7 +1320,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// these are for compatibility stuff
chan[i].portaStop=true;
// COMPAT FLAG: stop portamento on note off
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
chan[i].stopOnOff=song.compatFlags.stopPortaOnNoteOff; // what?!
chan[i].scheduledSlideReset=false;
// only enter portamento if the speed is set
if ((effectVal&15)!=0) {
@ -1335,14 +1331,14 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: broken shortcut slides
// - oddly enough, shortcut slides are not communicated to the dispatch
// - this was fixed in 0.5.7
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
if (!song.compatFlags.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
// COMPAT FLAG: E1xy/E2xy also take priority over slides
// - another Defle hack. it places shortcut slides above pitch slides.
if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ...
if (song.compatFlags.e1e2AlsoTakePriority) lastSlide=0x1337; // ...
} else {
chan[i].inPorta=false;
// COMPAT FLAG: broken shortcut slides
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
if (!song.compatFlags.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
}
break;
case 0xe2: // portamento down
@ -1352,7 +1348,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].portaStop=true;
// COMPAT FLAG: stop portamento on note off
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
chan[i].stopOnOff=song.compatFlags.stopPortaOnNoteOff; // what?!
chan[i].scheduledSlideReset=false;
if ((effectVal&15)!=0) {
chan[i].inPorta=true;
@ -1360,13 +1356,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].shorthandPorta=true;
chan[i].wasShorthandPorta=true;
// COMPAT FLAG: broken shortcut slides
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
if (!song.compatFlags.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
// COMPAT FLAG: E1xy/E2xy also take priority over slides
if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ...
if (song.compatFlags.e1e2AlsoTakePriority) lastSlide=0x1337; // ...
} else {
chan[i].inPorta=false;
// COMPAT FLAG: broken shortcut slides
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
if (!song.compatFlags.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
}
break;
case 0xe3: // vibrato shape
@ -1408,7 +1404,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - 2: lax (default)
// - no cut is ever ignored unless overridden by another
// "Bruh"
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
if (effectVal>0 && (song.compatFlags.delayBehavior==2 || effectVal<nextSpeed)) {
// the cut timer is ticked after nextRow(), so we set it one tick higher.
chan[i].cut=effectVal+1;
chan[i].cutType=2;
@ -1452,7 +1448,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - ignore cut if equal or greater than speed
// - 2: lax (default)
// - no cut is ever ignored unless overridden by another
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
if (effectVal>0 && (song.compatFlags.delayBehavior==2 || effectVal<nextSpeed)) {
// the cut timer is ticked after nextRow(), so we set it one tick higher.
chan[i].cut=effectVal+1;
chan[i].cutType=0;
@ -1543,7 +1539,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// - ignore cut if equal or greater than speed
// - 2: lax (default)
// - no cut is ever ignored unless overridden by another
if (song.delayBehavior==2 || effectVal<nextSpeed) {
if (song.compatFlags.delayBehavior==2 || effectVal<nextSpeed) {
// the cut timer is ticked after nextRow(), so we set it one tick higher.
chan[i].cut=effectVal+1;
chan[i].cutType=1;
@ -1577,7 +1573,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: instrument changes triggee on portamento (inverted in the GUI)
// - before 0.6pre1 it was not possible to change instrument during portamento
// - now it is. this sends a "null" note to allow such change
if (insChanged && (chan[i].inPorta || calledPorta) && song.newInsTriggersInPorta) {
if (insChanged && (chan[i].inPorta || calledPorta) && song.compatFlags.newInsTriggersInPorta) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
}
@ -1585,7 +1581,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].doNote) {
// COMPAT FLAG: continuous vibrato
// - when enabled, the vibrato position is not reset on each note
if (!song.continuousVibrato) {
if (!song.compatFlags.continuousVibrato) {
chan[i].vibratoPos=0;
}
// send pitch now (why? didn't we do that already?)
@ -1594,7 +1590,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
// COMPAT FLAG: broken portamento during legato
// - portamento would not occur if legato is on
// - this was fixed in 0.6pre4
if (chan[i].legato && (!chan[i].inPorta || song.brokenPortaLegato)) {
if (chan[i].legato && (!chan[i].inPorta || song.compatFlags.brokenPortaLegato)) {
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note));
} else {
@ -1603,13 +1599,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) {
// COMPAT FLAG: E1xy/E2xy stop on same note
// - if there was a shortcut slide, stop it
if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) {
if (song.compatFlags.e1e2StopOnSameNote && chan[i].wasShorthandPorta) {
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
// COMPAT FLAG: broken shortcut slides
// - oddly enough, shortcut slides are not communicated to the dispatch
// - this was fixed in 0.5.7
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
if (!song.compatFlags.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
chan[i].wasShorthandPorta=false;
chan[i].inPorta=false;
} else {
@ -1624,7 +1620,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].releasing=false;
// COMPAT FLAG: reset arp position on new note
// - this does exactly what it says
if (song.resetArpPhaseOnNewNote) {
if (song.compatFlags.resetArpPhaseOnNewNote) {
chan[i].arpStage=-1;
}
// these are used by VGM/ROM export to determine the duration of loop trail.
@ -1681,10 +1677,10 @@ void DivEngine::processRow(int i, bool afterDelay) {
case 0xf2: // single pitch slide down
if (effect==0xf1) {
// COMPAT FLAG: limit slide range
chan[i].portaNote=song.limitSlides?0x60:255;
chan[i].portaNote=song.compatFlags.limitSlides?0x60:255;
} else {
// COMPAT FLAG: limit slide range
chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60;
chan[i].portaNote=(song.compatFlags.limitSlides && song.dispatchChanOfChan[i]>=0)?disCont[song.dispatchOfChan[i]].dispatch->getPortaFloor(song.dispatchChanOfChan[i]):-60;
}
chan[i].portaSpeed=effectVal;
chan[i].portaStop=true;
@ -1692,13 +1688,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].scheduledSlideReset=false;
chan[i].inPorta=false;
// COMPAT FLAG: arpeggio inhibits non-porta slides
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch?song.pitchSlideSpeed:1),chan[i].portaNote));
if (!song.compatFlags.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.compatFlags.linearPitch?song.compatFlags.pitchSlideSpeed:1),chan[i].portaNote));
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
chan[i].inPorta=false;
// COMPAT FLAG: arpeggio inhibits non-porta slides
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
if (!song.compatFlags.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
break;
}
}
@ -1726,7 +1722,7 @@ void DivEngine::nextRow() {
if (view==DIV_STATUS_PATTERN && !skipping) {
strcpy(pb1,"");
strcpy(pb3,"");
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
// orders
snprintf(pb,4095," %.2x",curOrders->ord[i][curOrder]);
strcat(pb1,pb);
@ -1792,16 +1788,16 @@ void DivEngine::nextRow() {
}
// process row pre on all channels
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
// try to find pre effects
processRowPre(i);
}
// process row on all channels
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
// COMPAT FLAG: cut/delay effect policy (delayBehavior)
// - if not lax, reset the row delay timer so it never happens
if (song.delayBehavior!=2) {
if (song.compatFlags.delayBehavior!=2) {
chan[i].rowDelay=0;
}
processRow(i,false);
@ -1866,22 +1862,22 @@ void DivEngine::nextRow() {
// - DefleMask uses a mandatory two-speed system
// - if the pattern length is odd, the speed to use is determined correctly...
// - ...unless the order count is also odd! in that case the first row of order 0 will always use speed 1, even if the song looped and we should be using speed 2
if (song.brokenSpeedSel) {
if (song.compatFlags.brokenSpeedSel) {
unsigned char speed2=(speeds.len>=2)?speeds.val[1]:speeds.val[0];
unsigned char speed1=speeds.val[0];
// if the pattern length is odd and the current order is odd, use speed 2 for even rows and speed 1 for odd ones
if ((curSubSong->patLen&1) && curOrder&1) {
ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
ticks=((curRow&1)?speed2:speed1);
nextSpeed=(curRow&1)?speed1:speed2;
} else {
ticks=((curRow&1)?speed1:speed2)*(curSubSong->timeBase+1);
ticks=((curRow&1)?speed1:speed2);
nextSpeed=(curRow&1)?speed2:speed1;
}
} else {
// normal speed alternation
// set the number of ticks and cycle to the next speed
ticks=speeds.val[curSpeed]*(curSubSong->timeBase+1);
ticks=speeds.val[curSpeed];
curSpeed++;
if (curSpeed>=speeds.len) curSpeed=0;
// cache the next speed for future operations
@ -1895,7 +1891,7 @@ void DivEngine::nextRow() {
// post row details
// schedule pre-notes and delays (for C64 and/or a compat flag)
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
if (pat->newData[curRow][DIV_PAT_NOTE]!=-1) {
// if there is a note
@ -1904,8 +1900,8 @@ void DivEngine::nextRow() {
if (!chan[i].legato) {
// check whether we should fire a pre-note event
bool wantPreNote=false;
if (disCont[dispatchOfChan[i]].dispatch!=NULL) {
wantPreNote=disCont[dispatchOfChan[i]].dispatch->getWantPreNote();
if (disCont[song.dispatchOfChan[i]].dispatch!=NULL) {
wantPreNote=disCont[song.dispatchOfChan[i]].dispatch->getWantPreNote();
if (wantPreNote) {
bool doPreparePreNote=true;
int addition=0;
@ -1917,7 +1913,7 @@ void DivEngine::nextRow() {
// COMPAT FLAG: pre-note does not take effect into consideration
// - a bug which does not cancel pre-note before a portamento or during legato
// - fixed in 0.6pre9
if (!song.preNoteNoEffect) {
if (!song.compatFlags.preNoteNoEffect) {
// handle portamento
if (pat->newData[curRow][DIV_PAT_FX(j)]==0x03 && pat->newData[curRow][DIV_PAT_FXVAL(j)]!=0 && pat->newData[curRow][DIV_PAT_FXVAL(j)]!=-1) {
doPreparePreNote=false;
@ -1951,7 +1947,7 @@ void DivEngine::nextRow() {
// COMPAT FLAG: auto-insert one tick gap between notes
// - simulates behavior of certain Amiga/C64 sound drivers where a one-tick cut occurred before another note
if (song.oneTickCut) {
if (song.compatFlags.oneTickCut) {
bool doPrepareCut=true;
int addition=0;
@ -2039,7 +2035,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// this is a check that nullifies any note off event that right after a note on
// it prevents a situation where some notes do not play
for (int i=pendingNotes.size()-1; i>=0; i--) {
if (pendingNotes[i].channel<0 || pendingNotes[i].channel>=chans) continue;
if (pendingNotes[i].channel<0 || pendingNotes[i].channel>=song.chans) continue;
if (pendingNotes[i].on) {
isOn[pendingNotes[i].channel]=true;
} else {
@ -2058,7 +2054,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// fetch event
DivNoteEvent& note=pendingNotes.front();
// don't if channel is out of bounds or event is canceled
if (note.nop || note.channel<0 || note.channel>=chans) {
if (note.nop || note.channel<0 || note.channel>=song.chans) {
pendingNotes.pop_front();
continue;
}
@ -2077,10 +2073,13 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
}
// set volume as long as there's one associated with the event
// and the chip has per-channel volume
if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
if (note.volume>=0 && !disCont[song.dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
// map velocity to curve and then to equivalent chip volume
float curvedVol=pow((float)note.volume/127.0f,midiVolExp);
int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol);
int mappedVol=0;
if (song.dispatchChanOfChan[note.channel]>=0) {
mappedVol=disCont[song.dispatchOfChan[note.channel]].dispatch->mapVelocity(song.dispatchChanOfChan[note.channel],curvedVol);
}
// fire command
dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,mappedVol));
}
@ -2094,11 +2093,14 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
chan[note.channel].lastIns=note.ins;
} else {
// note off
DivMacroInt* macroInt=disCont[dispatchOfChan[note.channel]].dispatch->getChanMacroInt(dispatchChanOfChan[note.channel]);
DivMacroInt* macroInt=NULL;
if (song.dispatchChanOfChan[note.channel]>=0) {
macroInt=disCont[song.dispatchOfChan[note.channel]].dispatch->getChanMacroInt(song.dispatchChanOfChan[note.channel]);
}
if (macroInt!=NULL) {
// if the current instrument has a release point in any macros and
// volume is per-channel, send a note release instead of a note off
if (macroInt->hasRelease && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
if (macroInt->hasRelease && !disCont[song.dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,note.channel));
} else {
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,note.channel));
@ -2123,7 +2125,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// delayed row's state before it has a chance to do anything. a typical example would be
// a delay scheduling a note-on to be simultaneous with the next row, and the next row also
// containing a delayed note. if we don't apply the delayed row first, the world explodes.
for (int i=0; i<chans; i++) {
for (int i=0; i<song.chans; i++) {
// delay effects
if (chan[i].rowDelay>0) {
if (--chan[i].rowDelay==0) {
@ -2155,7 +2157,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// - 0: reset channels. call playSub() to seek back to the loop position
// - 1: soft-reset channels. same as 0 for now
// - 2: don't reset
if (song.loopModality!=2) {
if (song.compatFlags.loopModality!=2) {
playSub(true);
}
}
@ -2188,7 +2190,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
}
// process stuff such as effects
if (!shallStop) for (int i=0; i<chans; i++) {
if (!shallStop) for (int i=0; i<song.chans; i++) {
// retrigger
if (chan[i].retrigSpeed) {
if (--chan[i].retrigTick<0) {
@ -2202,7 +2204,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// volume slides and tremolo
// COMPAT FLAG: don't slide on the first tick of a row
// - Amiga/PC tracker behavior where slides and vibrato do not take course during the first tick of a row
if (!song.noSlidesOnFirstTick || !firstTick) {
if (!song.compatFlags.noSlidesOnFirstTick || !firstTick) {
// volume slides
if (chan[i].volSpeed!=0) {
// the call to GET_VOLUME is part of a compatibility process
@ -2252,7 +2254,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// COMPAT FLAG: legacy volume slides
// - sets volume to max once a vol slide down has finished (thus setting volume to volMax+1)
// - there is more to this, such as the first step of volume macro resulting in unpredictable behavior, but I don't feel like implementing THAT...
if (song.legacyVolumeSlides) {
if (song.compatFlags.legacyVolumeSlides) {
chan[i].volume=chan[i].volMax+1;
} else {
chan[i].volume=0;
@ -2415,7 +2417,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// portamento and pitch slides
// COMPAT FLAG: don't slide on the first tick of a row
// - Amiga/PC tracker behavior where slides and vibrato do not take course during the first tick of a row
if (!song.noSlidesOnFirstTick || !firstTick) {
if (!song.compatFlags.noSlidesOnFirstTick || !firstTick) {
// portamento only runs if the channel has been used and the porta speed is higher than 0
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
// send a portamento update command to the dispatch.
@ -2425,7 +2427,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// - 1: full (pitch slides linear... we multiply the portamento speed by a user-defined multiplier)
// COMPAT FLAG: reset pitch slide/portamento upon reaching target (inverted in the GUI)
// - when disabled, portamento remains active after it has finished
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.compatFlags.linearPitch?song.compatFlags.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.compatFlags.targetResetsSlides) {
// if we are here, it means we reached the target and shall stop
chan[i].portaSpeed=0;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
@ -2451,7 +2453,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// COMPAT FLAG: reset slides on note off (inverted in the GUI)
// - a portamento/pitch slide will be halted upon encountering note off
// - this will not occur if the stopPortaOnNoteOff flag is on and this is a portamento
if (chan[i].inPorta && song.noteOffResetsSlides) {
if (chan[i].inPorta && song.compatFlags.noteOffResetsSlides) {
chan[i].keyOff=true;
chan[i].keyOn=false;
// stopOnOff will be false if stopPortaOnNoteOff flag is off
@ -2462,7 +2464,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
chan[i].stopOnOff=false;
}
// depending on the system, portamento may still be disabled
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
if (song.dispatchChanOfChan[i]>=0) if (disCont[song.dispatchOfChan[i]].dispatch->keyOffAffectsPorta(song.dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
@ -2500,7 +2502,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
}
// COMPAT FLAG: reset arp position on row change
// - simulates Amiga/PC tracker behavior where the next row resets arp pos
if (song.rowResetsArpPos && firstTick) {
if (song.compatFlags.rowResetsArpPos && firstTick) {
chan[i].arpStage=-1;
}
// arpeggio (actually)
@ -2563,8 +2565,9 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
shallStop=false;
shallStopSched=false;
// reset all chan oscs
for (int i=0; i<chans; i++) {
DivDispatchOscBuffer* buf=disCont[dispatchOfChan[i]].dispatch->getOscBuffer(dispatchChanOfChan[i]);
for (int i=0; i<song.chans; i++) {
if (song.dispatchChanOfChan[i]<0) continue;
DivDispatchOscBuffer* buf=disCont[song.dispatchOfChan[i]].dispatch->getOscBuffer(song.dispatchChanOfChan[i]);
if (buf!=NULL) {
buf->reset();
}
@ -2629,20 +2632,18 @@ void DivEngine::runMidiClock(int totalCycles) {
output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0));
}
// calculate tempo using highlight, timeBase, tick rate, speeds and virtual tempo
// calculate tempo using highlight, tick rate, speeds and virtual tempo
double hl=curSubSong->hilightA;
if (hl<=0.0) hl=4.0;
double timeBase=curSubSong->timeBase+1;
double speedSum=0;
double vD=virtualTempoD;
for (int i=0; i<MIN(16,speeds.len); i++) {
speedSum+=speeds.val[i];
}
speedSum/=MAX(1,speeds.len);
if (timeBase<1.0) timeBase=1.0;
if (speedSum<1.0) speedSum=1.0;
if (vD<1) vD=1;
double bpm=((24.0*divider)/(timeBase*hl*speedSum))*(double)virtualTempoN/vD;
double bpm=((24.0*divider)/(hl*speedSum))*(double)virtualTempoN/vD;
// avoid a division by zer
if (bpm<1.0) bpm=1.0;
int increment=got.rate/(bpm);
@ -2857,7 +2858,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
case TA_MIDI_NOTE_OFF: {
if (midiIsDirect) {
// in direct mode, map the event directly to the channel
if (chan<0 || chan>=chans) break;
if (chan<0 || chan>=song.chans) break;
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
} else {
// find a suitable channel and add this event to the queue
@ -2876,7 +2877,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
if (msg.data[1]==0) {
if (midiIsDirect) {
// in direct mode, map the event directly to the channel
if (chan<0 || chan>=chans) break;
if (chan<0 || chan>=song.chans) break;
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
} else {
// find a suitable channel and add this event to the queue
@ -2885,7 +2886,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
} else {
if (midiIsDirect) {
// in direct mode, map the event directly to the channel
if (chan<0 || chan>=chans) break;
if (chan<0 || chan>=song.chans) break;
pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true));
} else {
// find a suitable channel and add this event to the queue

View file

@ -17,11 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "song.h"
#include "engine.h"
#include "../ta-log.h"
#include <inttypes.h>
#include <string.h>
#include <chrono>
static DivCompatFlags defaultFlags;
TimeMicros DivSongTimestamps::getTimes(int order, int row) {
if (order<0 || order>=DIV_MAX_PATTERNS) return TimeMicros(-1,0);
if (row<0 || row>=DIV_MAX_ROWS) return TimeMicros(-1,0);
@ -194,12 +197,12 @@ void DivSubSong::calcTimestamps(int chans, std::vector<DivGroovePattern>& groove
if (effectVal!=0) {
// COMPAT FLAG: cut/delay effect policy (delayBehavior)
// - 0: strict
// - delays equal or greater to the speed * timeBase are ignored
// - delays equal or greater to the speed are ignored (formerly time base was involved but that has been removed now)
// - 1: strict old
// - delays equal or greater to the speed are ignored
// - delays greater to the speed are ignored
// - 2: lax (default)
// - no delay is ever ignored unless overridden by another
bool comparison=(delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed*(timeBase+1)));
bool comparison=(delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed));
if (delayBehavior==2) comparison=true;
if (comparison) {
// set the delay row, order and timer
@ -324,16 +327,16 @@ void DivSubSong::calcTimestamps(int chans, std::vector<DivGroovePattern>& groove
// we subtract firstPat from curOrder as firstPat is used by a function which finds sub-songs
// (the beginning of a new sub-song will be in order 0)
if ((patLen&1) && (curOrder-firstPat)&1) {
ticks=((curRow&1)?speed2:speed1)*(timeBase+1);
ticks=((curRow&1)?speed2:speed1);
nextSpeed=(curRow&1)?speed1:speed2;
} else {
ticks=((curRow&1)?speed1:speed2)*(timeBase+1);
ticks=((curRow&1)?speed1:speed2);
nextSpeed=(curRow&1)?speed2:speed1;
}
} else {
// normal speed alternation
// set the number of ticks and cycle to the next speed
ticks=curSpeeds.val[curSpeed]*(timeBase+1);
ticks=curSpeeds.val[curSpeed];
curSpeed++;
if (curSpeed>=curSpeeds.len) curSpeed=0;
// cache the next speed for future operations
@ -448,6 +451,214 @@ void DivSubSong::calcTimestamps(int chans, std::vector<DivGroovePattern>& groove
logV("song length: %s; %" PRIu64 " ticks",ts.totalTime.toString(6,TA_TIME_FORMAT_AUTO),ts.totalTicks);
}
bool DivSubSong::readData(SafeReader& reader, int version, int chans) {
unsigned char magic[4];
reader.read(magic,4);
if (version>=240) {
if (memcmp(magic,"SNG2",4)!=0) {
logE("invalid subsong header!");
return false;
}
reader.readI();
hz=reader.readF();
arpLen=reader.readC();
effectDivider=reader.readC();
patLen=reader.readS();
ordersLen=reader.readS();
hilightA=reader.readC();
hilightB=reader.readC();
virtualTempoN=reader.readS();
virtualTempoD=reader.readS();
speeds.len=reader.readC();
for (int i=0; i<16; i++) {
speeds.val[i]=reader.readS();
}
name=reader.readString();
notes=reader.readString();
logD("reading orders (%d)...",ordersLen);
for (int j=0; j<chans; j++) {
for (int k=0; k<ordersLen; k++) {
orders.ord[j][k]=reader.readC();
}
}
for (int i=0; i<chans; i++) {
pat[i].effectCols=reader.readC();
}
for (int i=0; i<chans; i++) {
unsigned char tempchar=reader.readC();
chanShow[i]=tempchar&1;
chanShowChanOsc[i]=tempchar&2;
}
for (int i=0; i<chans; i++) {
chanCollapse[i]=reader.readC();
}
for (int i=0; i<chans; i++) {
chanName[i]=reader.readString();
}
for (int i=0; i<chans; i++) {
chanShortName[i]=reader.readString();
}
for (int i=0; i<chans; i++) {
chanColor[i]=reader.readI();
}
} else {
if (memcmp(magic,"SONG",4)!=0) {
logE("invalid subsong header!");
return false;
}
reader.readI();
unsigned char oldTimeBase=reader.readC();
speeds.len=2;
speeds.val[0]=(unsigned char)reader.readC();
speeds.val[1]=(unsigned char)reader.readC();
arpLen=reader.readC();
hz=reader.readF();
patLen=reader.readS();
ordersLen=reader.readS();
hilightA=reader.readC();
hilightB=reader.readC();
if (version>=96) {
virtualTempoN=reader.readS();
virtualTempoD=reader.readS();
} else {
reader.readI();
}
name=reader.readString();
notes=reader.readString();
logD("reading orders (%d)...",ordersLen);
for (int j=0; j<chans; j++) {
for (int k=0; k<ordersLen; k++) {
orders.ord[j][k]=reader.readC();
}
}
for (int i=0; i<chans; i++) {
pat[i].effectCols=reader.readC();
}
for (int i=0; i<chans; i++) {
if (version<189) {
chanShow[i]=reader.readC();
chanShowChanOsc[i]=chanShow[i];
} else {
unsigned char tempchar=reader.readC();
chanShow[i]=tempchar&1;
chanShowChanOsc[i]=tempchar&2;
}
}
for (int i=0; i<chans; i++) {
chanCollapse[i]=reader.readC();
}
for (int i=0; i<chans; i++) {
chanName[i]=reader.readString();
}
for (int i=0; i<chans; i++) {
chanShortName[i]=reader.readString();
}
if (version>=139) {
speeds.len=reader.readC();
for (int i=0; i<16; i++) {
speeds.val[i]=(unsigned char)reader.readC();
}
}
for (int i=0; i<16; i++) {
speeds.val[i]*=(oldTimeBase+1);
}
}
return true;
}
void DivSubSong::putData(SafeWriter* w, int chans) {
size_t blockStartSeek, blockEndSeek;
w->write("SNG2",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeF(hz);
w->writeC(arpLen);
w->writeC(effectDivider);
w->writeS(patLen);
w->writeS(ordersLen);
w->writeC(hilightA);
w->writeC(hilightB);
w->writeS(virtualTempoN);
w->writeS(virtualTempoD);
// speeds
w->writeC(speeds.len);
for (int i=0; i<16; i++) {
w->writeS(speeds.val[i]);
}
w->writeString(name,false);
w->writeString(notes,false);
for (int i=0; i<chans; i++) {
for (int j=0; j<ordersLen; j++) {
w->writeC(orders.ord[i][j]);
}
}
for (int i=0; i<chans; i++) {
w->writeC(pat[i].effectCols);
}
for (int i=0; i<chans; i++) {
w->writeC(
(chanShow[i]?1:0)|
(chanShowChanOsc[i]?2:0)
);
}
for (int i=0; i<chans; i++) {
w->writeC(chanCollapse[i]);
}
for (int i=0; i<chans; i++) {
w->writeString(chanName[i],false);
}
for (int i=0; i<chans; i++) {
w->writeString(chanShortName[i],false);
}
for (int i=0; i<chans; i++) {
w->writeI(chanColor[i]);
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END);
}
void DivSubSong::clearData() {
for (int i=0; i<DIV_MAX_CHANS; i++) {
pat[i].wipePatterns();
@ -574,7 +785,7 @@ void DivSubSong::makePatUnique() {
}
}
void DivSong::findSubSongs(int chans) {
void DivSong::findSubSongs() {
std::vector<DivSubSong*> newSubSongs;
for (DivSubSong* i: subsong) {
std::vector<int> subSongStart;
@ -584,7 +795,7 @@ void DivSong::findSubSongs(int chans) {
// find possible subsongs
logD("finding subsongs...");
while (++curStart<i->ordersLen) {
i->calcTimestamps(chans,grooves,jumpTreatment,ignoreJumpAtEnd,brokenSpeedSel,delayBehavior,curStart);
i->calcTimestamps(chans,grooves,compatFlags.jumpTreatment,compatFlags.ignoreJumpAtEnd,compatFlags.brokenSpeedSel,compatFlags.delayBehavior,curStart);
if (!i->ts.isLoopable) break;
// make sure we don't pick the same range twice
@ -620,7 +831,7 @@ void DivSong::findSubSongs(int chans) {
theCopy->notes=i->notes;
theCopy->hilightA=i->hilightA;
theCopy->hilightB=i->hilightB;
theCopy->timeBase=i->timeBase;
theCopy->effectDivider=i->effectDivider;
theCopy->arpLen=i->arpLen;
theCopy->speeds=i->speeds;
theCopy->virtualTempoN=i->virtualTempoN;
@ -642,6 +853,7 @@ void DivSong::findSubSongs(int chans) {
memcpy(theCopy->chanShow,i->chanShow,DIV_MAX_CHANS*sizeof(bool));
memcpy(theCopy->chanShowChanOsc,i->chanShowChanOsc,DIV_MAX_CHANS*sizeof(bool));
memcpy(theCopy->chanCollapse,i->chanCollapse,DIV_MAX_CHANS);
memcpy(theCopy->chanColor,i->chanColor,DIV_MAX_CHANS*sizeof(unsigned int));
for (int k=0; k<DIV_MAX_CHANS; k++) {
theCopy->chanName[k]=i->chanName[k];
@ -689,7 +901,69 @@ void DivSong::findSubSongs(int chans) {
for (DivSubSong* i: newSubSongs) {
subsong.push_back(i);
}
}
void DivSong::initDefaultSystemChans() {
for (int i=0; i<systemLen; i++) {
const DivSysDef* sysDef=DivEngine::getSystemDef(system[i]);
if (sysDef==NULL) {
systemChans[i]=0;
} else {
systemChans[i]=sysDef->channels;
}
}
}
void DivSong::recalcChans() {
logV("DivSong: recalcChans() called");
bool isInsTypePossible[DIV_INS_MAX];
chans=0;
int chanIndex=0;
memset(isInsTypePossible,0,DIV_INS_MAX*sizeof(bool));
for (int i=0; i<systemLen; i++) {
const DivSysDef* sysDef=DivEngine::getSystemDef(system[i]);
int chanCount=systemChans[i];
int firstChan=chans;
chans+=chanCount;
for (int j=0; j<chanCount; j++) {
sysOfChan[chanIndex]=system[i];
dispatchOfChan[chanIndex]=i;
if (sysDef==NULL) {
dispatchChanOfChan[chanIndex]=-1;
} else if (j<sysDef->maxChans) {
dispatchChanOfChan[chanIndex]=j;
} else {
dispatchChanOfChan[chanIndex]=-1;
}
dispatchFirstChan[chanIndex]=firstChan;
if (sysDef!=NULL) {
chanDef[chanIndex]=sysDef->getChanDef(j);
if (chanDef[chanIndex].insType[0]!=DIV_INS_NULL) {
isInsTypePossible[chanDef[chanIndex].insType[0]]=true;
}
if (chanDef[chanIndex].insType[1]!=DIV_INS_NULL) {
isInsTypePossible[chanDef[chanIndex].insType[1]]=true;
}
} else {
chanDef[chanIndex]=DivChanDef();
}
chanIndex++;
}
}
possibleInsTypes.clear();
for (int i=0; i<DIV_INS_MAX; i++) {
if (isInsTypePossible[i]) possibleInsTypes.push_back((DivInstrumentType)i);
}
checkAssetDir(insDir,ins.size());
checkAssetDir(waveDir,wave.size());
checkAssetDir(sampleDir,sample.size());
logV("%d channels (%d chips)",chans,systemLen);
}
void DivSong::clearSongData() {
@ -749,4 +1023,201 @@ void DivSong::unload() {
delete i;
}
subsong.clear();
}
}
bool DivGroovePattern::readData(SafeReader& reader) {
unsigned char magic[4];
reader.read(magic,4);
if (memcmp(magic,"GROV",4)!=0) {
logE("invalid groove header!");
return false;
}
reader.readI();
return true;
}
void DivGroovePattern::putData(SafeWriter* w) {
size_t blockStartSeek, blockEndSeek;
w->write("GROV",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeC(len);
for (int i=0; i<16; i++) {
w->writeS(val[i]);
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END);
}
void DivCompatFlags::setDefaults() {
limitSlides=false;
linearPitch=1;
pitchSlideSpeed=4;
loopModality=2;
delayBehavior=2;
jumpTreatment=0;
properNoiseLayout=true;
waveDutyIsVol=false;
resetMacroOnPorta=false;
legacyVolumeSlides=false;
compatibleArpeggio=false;
noteOffResetsSlides=true;
targetResetsSlides=true;
arpNonPorta=false;
algMacroBehavior=false;
brokenShortcutSlides=false;
ignoreDuplicateSlides=false;
stopPortaOnNoteOff=false;
continuousVibrato=false;
brokenDACMode=false;
oneTickCut=false;
newInsTriggersInPorta=true;
arp0Reset=true;
brokenSpeedSel=false;
noSlidesOnFirstTick=false;
rowResetsArpPos=false;
ignoreJumpAtEnd=false;
buggyPortaAfterSlide=false;
gbInsAffectsEnvelope=true;
sharedExtStat=true;
ignoreDACModeOutsideIntendedChannel=false;
e1e2AlsoTakePriority=false;
newSegaPCM=true;
fbPortaPause=false;
snDutyReset=false;
pitchMacroIsLinear=true;
oldOctaveBoundary=false;
noOPN2Vol=false;
newVolumeScaling=true;
volMacroLinger=true;
brokenOutVol=false;
brokenOutVol2=false;
e1e2StopOnSameNote=false;
brokenPortaArp=false;
snNoLowPeriods=false;
disableSampleMacro=false;
oldArpStrategy=false;
brokenPortaLegato=false;
brokenFMOff=false;
preNoteNoEffect=false;
oldDPCM=false;
resetArpPhaseOnNewNote=false;
ceilVolumeScaling=false;
oldAlwaysSetVolume=false;
oldSampleOffset=false;
oldCenterRate=true;
}
bool DivCompatFlags::areDefaults() {
return (*this==defaultFlags);
}
bool DivCompatFlags::readData(SafeReader& reader) {
DivConfig c;
unsigned char magic[4];
reader.read(magic,4);
if (memcmp(magic,"CFLG",4)!=0) {
return false;
}
reader.readI();
String data=reader.readString();
c.loadFromMemory(data.c_str());
// TODO: this
return true;
}
#define CHECK_AND_STORE_BOOL(_x) \
if (_x!=defaultFlags._x) { \
c.set(#_x,_x); \
}
#define CHECK_AND_STORE_UNSIGNED_CHAR(_x) \
if (_x!=defaultFlags._x) { \
c.set(#_x,(int)_x); \
}
void DivCompatFlags::putData(SafeWriter* w) {
DivConfig c;
size_t blockStartSeek, blockEndSeek;
CHECK_AND_STORE_BOOL(limitSlides);
CHECK_AND_STORE_UNSIGNED_CHAR(linearPitch);
CHECK_AND_STORE_UNSIGNED_CHAR(pitchSlideSpeed);
CHECK_AND_STORE_UNSIGNED_CHAR(loopModality);
CHECK_AND_STORE_UNSIGNED_CHAR(delayBehavior);
CHECK_AND_STORE_UNSIGNED_CHAR(jumpTreatment);
CHECK_AND_STORE_BOOL(properNoiseLayout);
CHECK_AND_STORE_BOOL(waveDutyIsVol);
CHECK_AND_STORE_BOOL(resetMacroOnPorta);
CHECK_AND_STORE_BOOL(legacyVolumeSlides);
CHECK_AND_STORE_BOOL(compatibleArpeggio);
CHECK_AND_STORE_BOOL(noteOffResetsSlides);
CHECK_AND_STORE_BOOL(targetResetsSlides);
CHECK_AND_STORE_BOOL(arpNonPorta);
CHECK_AND_STORE_BOOL(algMacroBehavior);
CHECK_AND_STORE_BOOL(brokenShortcutSlides);
CHECK_AND_STORE_BOOL(ignoreDuplicateSlides);
CHECK_AND_STORE_BOOL(stopPortaOnNoteOff);
CHECK_AND_STORE_BOOL(continuousVibrato);
CHECK_AND_STORE_BOOL(brokenDACMode);
CHECK_AND_STORE_BOOL(oneTickCut);
CHECK_AND_STORE_BOOL(newInsTriggersInPorta);
CHECK_AND_STORE_BOOL(arp0Reset);
CHECK_AND_STORE_BOOL(brokenSpeedSel);
CHECK_AND_STORE_BOOL(noSlidesOnFirstTick);
CHECK_AND_STORE_BOOL(rowResetsArpPos);
CHECK_AND_STORE_BOOL(ignoreJumpAtEnd);
CHECK_AND_STORE_BOOL(buggyPortaAfterSlide);
CHECK_AND_STORE_BOOL(gbInsAffectsEnvelope);
CHECK_AND_STORE_BOOL(sharedExtStat);
CHECK_AND_STORE_BOOL(ignoreDACModeOutsideIntendedChannel);
CHECK_AND_STORE_BOOL(e1e2AlsoTakePriority);
CHECK_AND_STORE_BOOL(newSegaPCM);
CHECK_AND_STORE_BOOL(fbPortaPause);
CHECK_AND_STORE_BOOL(snDutyReset);
CHECK_AND_STORE_BOOL(pitchMacroIsLinear);
CHECK_AND_STORE_BOOL(oldOctaveBoundary);
CHECK_AND_STORE_BOOL(noOPN2Vol);
CHECK_AND_STORE_BOOL(newVolumeScaling);
CHECK_AND_STORE_BOOL(volMacroLinger);
CHECK_AND_STORE_BOOL(brokenOutVol);
CHECK_AND_STORE_BOOL(brokenOutVol2);
CHECK_AND_STORE_BOOL(e1e2StopOnSameNote);
CHECK_AND_STORE_BOOL(brokenPortaArp);
CHECK_AND_STORE_BOOL(snNoLowPeriods);
CHECK_AND_STORE_BOOL(disableSampleMacro);
CHECK_AND_STORE_BOOL(oldArpStrategy);
CHECK_AND_STORE_BOOL(brokenPortaLegato);
CHECK_AND_STORE_BOOL(brokenFMOff);
CHECK_AND_STORE_BOOL(preNoteNoEffect);
CHECK_AND_STORE_BOOL(oldDPCM);
CHECK_AND_STORE_BOOL(resetArpPhaseOnNewNote);
CHECK_AND_STORE_BOOL(ceilVolumeScaling);
CHECK_AND_STORE_BOOL(oldAlwaysSetVolume);
CHECK_AND_STORE_BOOL(oldSampleOffset);
CHECK_AND_STORE_BOOL(oldCenterRate);
String data=c.toString();
w->write("CFLG",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeString(data,false);
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END);
}

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