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

This commit is contained in:
cam900 2023-07-22 10:39:28 +09:00
commit 0889a8fe92
559 changed files with 31100 additions and 15654 deletions

View file

@ -160,6 +160,7 @@ bool TAMidiInRtMidi::quit() {
bool TAMidiOutRtMidi::send(const TAMidiMessage& what) {
if (!isOpen) return false;
if (!isWorking) return false;
if (what.type<0x80) return false;
size_t len=0;
switch (what.type&0xf0) {
@ -190,6 +191,7 @@ bool TAMidiOutRtMidi::send(const TAMidiMessage& what) {
port->sendMessage(what.sysExData.get(),len);
} catch (RtMidiError& e) {
logE("MIDI output error! %s",e.what());
isWorking=false;
return false;
}
return true;
@ -209,6 +211,7 @@ bool TAMidiOutRtMidi::send(const TAMidiMessage& what) {
port->sendMessage((const unsigned char*)&what.type,len);
} catch (RtMidiError& e) {
logE("MIDI output error! %s",e.what());
isWorking=false;
return false;
}
return true;
@ -237,17 +240,20 @@ bool TAMidiOutRtMidi::openDevice(String name) {
}
isOpen=portOpen;
if (!portOpen) logW("could not find MIDI out device...");
isWorking=true;
return portOpen;
} catch (RtMidiError& e) {
logW("could not open MIDI out device! %s",e.what());
return false;
}
isWorking=true;
return true;
}
bool TAMidiOutRtMidi::closeDevice() {
if (port==NULL) return false;
if (!isOpen) return false;
isWorking=false;
try {
port->closePort();
} catch (RtMidiError& e) {

View file

@ -38,7 +38,7 @@ class TAMidiInRtMidi: public TAMidiIn {
class TAMidiOutRtMidi: public TAMidiOut {
RtMidiOut* port;
bool isOpen;
bool isOpen, isWorking;
public:
bool send(const TAMidiMessage& what);
bool isDeviceOpen();
@ -49,5 +49,6 @@ class TAMidiOutRtMidi: public TAMidiOut {
bool init();
TAMidiOutRtMidi():
port(NULL),
isOpen(false) {}
isOpen(false),
isWorking(false) {}
};

View file

@ -127,14 +127,20 @@ bool TAAudioSDL::init(TAAudioDesc& request, TAAudioDesc& response) {
ac.callback=taSDLProcess;
ac.userdata=this;
ai=SDL_OpenAudioDevice(request.deviceName.empty()?NULL:request.deviceName.c_str(),0,&ac,&ar,SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
ai=SDL_OpenAudioDevice(request.deviceName.empty()?NULL:request.deviceName.c_str(),0,&ac,&ar,0);
if (ai==0) {
logE("could not open audio device: %s",SDL_GetError());
return false;
}
const char* backendName=SDL_GetCurrentAudioDriver();
desc.deviceName=request.deviceName;
desc.name="";
if (backendName==NULL) {
desc.name="";
} else {
desc.name=backendName;
}
desc.rate=ar.freq;
desc.inChans=0;
desc.outChans=ar.channels;

View file

@ -148,9 +148,13 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundanc
}
for (size_t j=0; j<readBufLen; j++) {
if (readBuf[j]==0) {
viable=false;
logW("a zero?");
break;
}
if (readBuf[j]!='\r' && readBuf[j]!='\n' && readBuf[j]!=' ') {
viable=true;
break;
}
}

View file

@ -173,16 +173,16 @@ enum DivDispatchCmds {
DIV_CMD_N163_WAVE_POSITION,
DIV_CMD_N163_WAVE_LENGTH,
DIV_CMD_N163_WAVE_MODE,
DIV_CMD_N163_WAVE_LOAD,
DIV_CMD_N163_WAVE_UNUSED1,
DIV_CMD_N163_WAVE_UNUSED2,
DIV_CMD_N163_WAVE_LOADPOS,
DIV_CMD_N163_WAVE_LOADLEN,
DIV_CMD_N163_WAVE_LOADMODE,
DIV_CMD_N163_WAVE_UNUSED3,
DIV_CMD_N163_CHANNEL_LIMIT,
DIV_CMD_N163_GLOBAL_WAVE_LOAD,
DIV_CMD_N163_GLOBAL_WAVE_LOADPOS,
DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,
DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,
DIV_CMD_N163_UNUSED4,
DIV_CMD_N163_UNUSED5,
DIV_CMD_SU_SWEEP_PERIOD_LOW, // (which, val)
DIV_CMD_SU_SWEEP_PERIOD_HIGH, // (which, val)
@ -236,6 +236,8 @@ enum DivDispatchCmds {
DIV_CMD_NES_LINEAR_LENGTH,
DIV_CMD_EXTERNAL, // (value)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_MAX
@ -284,10 +286,17 @@ struct DivRegWrite {
* - xx is the instance ID
* - 0xffffxx03: set sample playback direction
* - x is the instance ID
* - 0xffffxx04: switch sample bank
* - for use in VGM export
* - 0xffffxx05: set sample position
* - xx is the instance ID
* - data is the sample position
* - 0xffffffff: reset
*/
unsigned int addr;
unsigned int val;
DivRegWrite():
addr(0), val(0) {}
DivRegWrite(unsigned int a, unsigned int v):
addr(a), val(v) {}
};

View file

@ -78,6 +78,7 @@
#include "platform/ga20.h"
#include "platform/sm8521.h"
#include "platform/pv1000.h"
#include "platform/k053260.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@ -172,6 +173,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
if (bbIn[i]==NULL) continue;
if (bb[i]==NULL) continue;
for (size_t j=0; j<runtotal; j++) {
if (bbIn[i][j]==temp[i]) continue;
temp[i]=bbIn[i][j];
blip_add_delta_fast(bb[i],j,temp[i]-prevSample[i]);
prevSample[i]=temp[i];
@ -182,6 +184,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
if (bbIn[i]==NULL) continue;
if (bb[i]==NULL) continue;
for (size_t j=0; j<runtotal; j++) {
if (bbIn[i][j]==temp[i]) continue;
temp[i]=bbIn[i][j];
blip_add_delta(bb[i],j,temp[i]-prevSample[i]);
prevSample[i]=temp[i];
@ -273,12 +276,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
break;
case DIV_SYSTEM_C64_6581:
dispatch=new DivPlatformC64;
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0));
((DivPlatformC64*)dispatch)->setChipModel(true);
break;
case DIV_SYSTEM_C64_8580:
dispatch=new DivPlatformC64;
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0));
((DivPlatformC64*)dispatch)->setChipModel(false);
break;
case DIV_SYSTEM_YM2151:
@ -501,6 +504,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_PV1000:
dispatch=new DivPlatformPV1000;
break;
case DIV_SYSTEM_K053260:
dispatch=new DivPlatformK053260;
break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;

189
src/engine/effect.h Normal file
View file

@ -0,0 +1,189 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 _EFFECT_H
#define _EFFECT_H
#include <stdlib.h>
#include "../ta-utils.h"
class DivEngine;
class DivEffect {
protected:
DivEngine* parent;
public:
/**
* fill a buffer with sound data.
* @param in pointers to input buffers.
* @param out pointers to output buffers.
* @param len the amount of samples in input and output.
*/
virtual void acquire(float** in, float** out, size_t len);
/**
* reset the state of this effect.
*/
virtual void reset();
/**
* get the number of inputs this effect requests.
* @return number of inputs. SHALL NOT be less than zero.
*/
virtual int getInputCount();
/**
* get the number of outputs this effect provides.
* @return number of outputs. SHALL NOT be less than one.
*/
virtual int getOutputCount();
/**
* called when the sample rate changes.
* @param rate the new sample rate.
*/
virtual void rateChanged(double rate);
/**
* get the value of a parameter.
* @param param parameter ID.
* @return a String with the value.
* @throws std::out_of_range if the parameter ID is out of range.
*/
virtual String getParam(size_t param);
/**
* set the value of a parameter.
* @param param parameter ID.
* @param value the value.
* @return whether the parameter was set successfully.
*/
virtual bool setParam(size_t param, String value);
/**
* get a list of parameters.
* @return a C string with a list of parameters, or NULL if there are none.
* PARAMETER TYPES
*
* Parameter
* id:type:name:description:[...]
* type may be one of the following:
* - s: string
* - i: integer
* - I: integer slider
* - r: integer list (radio button)
* - R: integer list (combo box)
* - h: integer hex
* - f: float
* - F: float slider
* - d: double
* - D: double slider
* - b: boolean (checkbox)
* - t: boolean (toggle button)
* - x: X/Y integer
* - X: X/Y float
* - c: color (RGB)
* - C: color (RGBA)
* - B: button
*
* SameLine
* !s
*
* Separator
* ---
*
* Indent/Unindent
* > Indent
* < Unindent
*
* TreeNode
* >> Begin
* << End
*
* Tabs
* >TABBAR BeginTabBar
* >TAB:name Begin
* <TAB End
* <TABBAR EndTabBar
*
* Text
* TEXT:text (static text)
* TEXTF:id (dynamic text)
*
* NOTES
*
* use a new line to separate parameters.
* use `\:` if you need a colon.
*/
virtual const char* getParams();
/**
* get the number of parameters.
* @return count.
*/
virtual size_t getParamCount();
/**
* get a dynamic text.
* @param id the text ID.
* @return a String with the text.
* @throws std::out_of_range if the text ID is out of range.
*/
virtual String getDynamicText(size_t id);
/**
* load effect data.
* @param version effect data version. may be zero.
* @param data effect data. may be NULL.
* @param len effect data length. may be zero.
* @return whether loading was successful.
*/
virtual bool load(unsigned short version, const unsigned char* data, size_t len);
/**
* save effect data.
* @param version effect data version.
* @param len effect data length.
* @return a pointer to effect data. this must be de-allocated manually.
* may also return NULL if it can't save.
*/
virtual unsigned char* save(unsigned short* version, size_t* len);
/**
* initialize this DivEffect.
* @param parent the parent DivEngine.
* @param version effect data version. may be zero.
* @param data effect data. may be NULL.
* @param len effect data length. may be zero.
* @return whether initialization was successful.
*/
virtual bool init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len);
/**
* quit the DivEffect.
*/
virtual void quit();
virtual ~DivEffect();
};
// additional notes:
// - we don't have a GUI API yet, but that will be added when I make the plugin bridge.
#endif

View file

@ -0,0 +1,85 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "../effect.h"
#include "../../ta-log.h"
#include <stdexcept>
void DivEffect::acquire(float** in, float** out, size_t len) {
}
void DivEffect::reset() {
}
int DivEffect::getInputCount() {
return 0;
}
int DivEffect::getOutputCount() {
return 0;
}
void DivEffect::rateChanged(double rate) {
}
String DivEffect::getParam(size_t param) {
throw std::out_of_range("param");
// unreachable
return "";
}
bool DivEffect::setParam(size_t param, String value) {
return false;
}
const char* DivEffect::getParams() {
return NULL;
}
size_t DivEffect::getParamCount() {
return 0;
}
String DivEffect::getDynamicText(size_t id) {
throw std::out_of_range("param");
// unreachable
return "";
}
bool DivEffect::load(unsigned short version, const unsigned char* data, size_t len) {
return false;
}
unsigned char* DivEffect::save(unsigned short* version, size_t* len) {
*len=0;
*version=0;
return NULL;
}
bool DivEffect::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
return false;
}
void DivEffect::quit() {
}
DivEffect::~DivEffect() {
}

View file

@ -0,0 +1,60 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "dummy.h"
void DivEffectDummy::acquire(float** in, float** out, size_t len) {
memcpy(out[0],in[0],len*sizeof(float));
}
void DivEffectDummy::reset() {
}
int DivEffectDummy::getInputCount() {
return 1;
}
int DivEffectDummy::getOutputCount() {
return 1;
}
const char* DivEffectDummy::getParams() {
return NULL;
}
size_t DivEffectDummy::getParamCount() {
return 0;
}
bool DivEffectDummy::load(unsigned short version, const unsigned char* data, size_t len) {
return true;
}
unsigned char* DivEffectDummy::save(unsigned short* version, size_t* len) {
*len=0;
*version=0;
return NULL;
}
bool DivEffectDummy::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
return false;
}
void DivEffectDummy::quit() {
}

34
src/engine/effect/dummy.h Normal file
View file

@ -0,0 +1,34 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "../effect.h"
class DivEffectDummy: public DivEffect {
public:
void acquire(float** in, float** out, size_t len);
void reset();
int getInputCount();
int getOutputCount();
const char* getParams();
size_t getParamCount();
bool load(unsigned short version, const unsigned char* data, size_t len);
unsigned char* save(unsigned short* version, size_t* len);
bool init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len);
void quit();
};

View file

@ -0,0 +1,95 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "engine.h"
#include "effect/dummy.h"
void DivEffectContainer::preAcquire(size_t count) {
if (!count) return;
int inCount=effect->getInputCount();
if (inLen<count) {
for (int i=0; i<inCount; i++) {
if (in[i]!=NULL) {
delete[] in[i];
in[i]=new float[count];
}
}
inLen=count;
}
for (int i=0; i<inCount; i++) {
if (in[i]==NULL) {
in[i]=new float[count];
}
}
}
void DivEffectContainer::acquire(size_t count) {
if (!count) return;
int outCount=effect->getOutputCount();
if (outLen<count) {
for (int i=0; i<outCount; i++) {
if (out[i]!=NULL) {
delete[] out[i];
out[i]=new float[count];
}
}
outLen=count;
}
for (int i=0; i<outCount; i++) {
if (out[i]==NULL) {
out[i]=new float[count];
}
}
effect->acquire(in,out,count);
}
bool DivEffectContainer::init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len) {
switch (effectType) {
case DIV_EFFECT_DUMMY:
default:
effect=new DivEffectDummy;
}
return effect->init(eng,rate,version,data,len);
}
void DivEffectContainer::quit() {
effect->quit();
delete effect;
effect=NULL;
for (int i=0; i<DIV_MAX_OUTPUTS; i++) {
if (in[i]) {
delete[] in[i];
in[i]=NULL;
}
if (out[i]) {
delete[] out[i];
out[i]=NULL;
}
}
inLen=0;
outLen=0;
}

View file

@ -1444,9 +1444,6 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64, bool ol
// extra attributes
song.subsong[0]->hz=c.getDouble("tickRate",60.0);
if (song.subsong[0]->hz!=60.0) {
song.subsong[0]->customTempo=true;
}
}
void DivEngine::createNew(const char* description, String sysName, bool inBase64) {
@ -1572,6 +1569,35 @@ void DivEngine::changeSong(size_t songIndex) {
prevRow=0;
}
void DivEngine::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 DivEngine::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 DivEngine::checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries) {
bool* inAssetDir=new bool[entries];
memset(inAssetDir,0,entries*sizeof(bool));
@ -1584,9 +1610,16 @@ void DivEngine::checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries) {
j--;
continue;
}
// erase duplicate entry
if (inAssetDir[i.entries[j]]) {
i.entries.erase(i.entries.begin()+j);
j--;
continue;
}
// mark entry as present
inAssetDir[j]=true;
inAssetDir[i.entries[j]]=true;
}
}
@ -1599,15 +1632,14 @@ void DivEngine::checkAssetDir(std::vector<DivAssetDir>& dir, size_t entries) {
}
}
// create unsorted directory if it doesn't exist
if (unsortedDir==NULL) {
dir.push_back(DivAssetDir(""));
unsortedDir=&(*dir.rbegin());
}
// 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);
}
}
@ -1646,6 +1678,51 @@ int DivEngine::addSubSong() {
return song.subsong.size()-1;
}
int DivEngine::duplicateSubSong(int index) {
if (song.subsong.size()>=127) return -1;
BUSY_BEGIN;
saveLock.lock();
DivSubSong* theCopy=new DivSubSong;
DivSubSong* theOrig=song.subsong[index];
theCopy->name=theOrig->name;
theCopy->notes=theOrig->notes;
theCopy->hilightA=theOrig->hilightA;
theCopy->hilightB=theOrig->hilightB;
theCopy->timeBase=theOrig->timeBase;
theCopy->arpLen=theOrig->arpLen;
theCopy->speeds=theOrig->speeds;
theCopy->virtualTempoN=theOrig->virtualTempoN;
theCopy->virtualTempoD=theOrig->virtualTempoD;
theCopy->hz=theOrig->hz;
theCopy->patLen=theOrig->patLen;
theCopy->ordersLen=theOrig->ordersLen;
theCopy->orders=theOrig->orders;
memcpy(theCopy->chanShow,theOrig->chanShow,DIV_MAX_CHANS*sizeof(bool));
memcpy(theCopy->chanCollapse,theOrig->chanCollapse,DIV_MAX_CHANS);
for (int i=0; i<DIV_MAX_CHANS; i++) {
theCopy->chanName[i]=theOrig->chanName[i];
theCopy->chanShortName[i]=theOrig->chanShortName[i];
theCopy->pat[i].effectCols=theOrig->pat[i].effectCols;
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
if (theOrig->pat[i].data[j]==NULL) continue;
DivPattern* origPat=theOrig->pat[i].getPattern(j,false);
DivPattern* copyPat=theCopy->pat[i].getPattern(j,true);
origPat->copyOn(copyPat);
}
}
song.subsong.push_back(theCopy);
saveLock.unlock();
BUSY_END;
return song.subsong.size()-1;
}
bool DivEngine::removeSubSong(int index) {
if (song.subsong.size()<=1) return false;
stop();
@ -2172,6 +2249,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
prevOrder=0;
prevRow=0;
stepPlay=0;
if (curSubSong!=NULL) curSubSong->arpLen=1;
int prevDrift, prevMidiClockDrift, prevMidiTimeDrift;
prevDrift=clockDrift;
prevMidiClockDrift=midiClockDrift;
@ -2520,6 +2598,8 @@ void DivEngine::stepOne(int row) {
}
stepPlay=2;
ticks=1;
prevOrder=curOrder;
prevRow=curRow;
BUSY_END;
}
@ -2660,16 +2740,7 @@ void DivEngine::reset() {
elapsedBars=0;
elapsedBeats=0;
nextSpeed=speeds.val[0];
divider=60;
if (curSubSong->customTempo) {
divider=curSubSong->hz;
} else {
if (curSubSong->pal) {
divider=60;
} else {
divider=50;
}
}
divider=curSubSong->hz;
globalPitch=0;
for (int i=0; i<song.systemLen; i++) {
disCont[i].dispatch->reset();
@ -2884,14 +2955,7 @@ const DivGroovePattern& DivEngine::getSpeeds() {
}
float DivEngine::getHz() {
if (curSubSong->customTempo) {
return curSubSong->hz;
} else if (curSubSong->pal) {
return 60.0;
} else {
return 50.0;
}
return 60.0;
return curSubSong->hz;
}
float DivEngine::getCurHz() {
@ -3032,6 +3096,7 @@ int DivEngine::addInstrument(int refChan, DivInstrumentType fallbackType) {
saveLock.lock();
song.ins.push_back(ins);
song.insLen=insCount+1;
checkAssetDir(song.insDir,song.ins.size());
saveLock.unlock();
BUSY_END;
return insCount;
@ -3046,6 +3111,7 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) {
saveLock.lock();
song.ins.push_back(which);
song.insLen=song.ins.size();
checkAssetDir(song.insDir,song.ins.size());
saveLock.unlock();
BUSY_END;
return song.insLen;
@ -3082,6 +3148,8 @@ void DivEngine::delInstrument(int index) {
}
}
}
removeAsset(song.insDir,index);
checkAssetDir(song.insDir,song.ins.size());
}
saveLock.unlock();
BUSY_END;
@ -3098,6 +3166,7 @@ int DivEngine::addWave() {
int waveCount=(int)song.wave.size();
song.wave.push_back(wave);
song.waveLen=waveCount+1;
checkAssetDir(song.waveDir,song.wave.size());
saveLock.unlock();
BUSY_END;
return waveCount;
@ -3114,6 +3183,7 @@ int DivEngine::addWavePtr(DivWavetable* which) {
int waveCount=(int)song.wave.size();
song.wave.push_back(which);
song.waveLen=waveCount+1;
checkAssetDir(song.waveDir,song.wave.size());
saveLock.unlock();
BUSY_END;
return song.waveLen;
@ -3268,6 +3338,8 @@ void DivEngine::delWave(int index) {
delete song.wave[index];
song.wave.erase(song.wave.begin()+index);
song.waveLen=song.wave.size();
removeAsset(song.waveDir,index);
checkAssetDir(song.waveDir,song.wave.size());
}
saveLock.unlock();
BUSY_END;
@ -3288,6 +3360,7 @@ int DivEngine::addSample() {
sPreview.sample=-1;
sPreview.pos=0;
sPreview.dir=false;
checkAssetDir(song.sampleDir,song.sample.size());
saveLock.unlock();
renderSamples();
BUSY_END;
@ -3305,6 +3378,7 @@ int DivEngine::addSamplePtr(DivSample* which) {
saveLock.lock();
song.sample.push_back(which);
song.sampleLen=sampleCount+1;
checkAssetDir(song.sampleDir,song.sample.size());
saveLock.unlock();
renderSamples();
BUSY_END;
@ -3774,6 +3848,8 @@ void DivEngine::delSample(int index) {
delete song.sample[index];
song.sample.erase(song.sample.begin()+index);
song.sampleLen=song.sample.size();
removeAsset(song.sampleDir,index);
checkAssetDir(song.sampleDir,song.sample.size());
renderSamples();
}
saveLock.unlock();
@ -3972,6 +4048,7 @@ bool DivEngine::moveInsUp(int which) {
saveLock.lock();
song.ins[which]=song.ins[which-1];
song.ins[which-1]=prev;
moveAsset(song.insDir,which,which-1);
exchangeIns(which,which-1);
saveLock.unlock();
BUSY_END;
@ -3985,6 +4062,7 @@ bool DivEngine::moveWaveUp(int which) {
saveLock.lock();
song.wave[which]=song.wave[which-1];
song.wave[which-1]=prev;
moveAsset(song.waveDir,which,which-1);
saveLock.unlock();
BUSY_END;
return true;
@ -4000,6 +4078,7 @@ bool DivEngine::moveSampleUp(int which) {
saveLock.lock();
song.sample[which]=song.sample[which-1];
song.sample[which-1]=prev;
moveAsset(song.sampleDir,which,which-1);
saveLock.unlock();
renderSamples();
BUSY_END;
@ -4014,6 +4093,7 @@ bool DivEngine::moveInsDown(int which) {
song.ins[which]=song.ins[which+1];
song.ins[which+1]=prev;
exchangeIns(which,which+1);
moveAsset(song.insDir,which,which+1);
saveLock.unlock();
BUSY_END;
return true;
@ -4026,6 +4106,7 @@ bool DivEngine::moveWaveDown(int which) {
saveLock.lock();
song.wave[which]=song.wave[which+1];
song.wave[which+1]=prev;
moveAsset(song.waveDir,which,which+1);
saveLock.unlock();
BUSY_END;
return true;
@ -4041,6 +4122,7 @@ bool DivEngine::moveSampleDown(int which) {
saveLock.lock();
song.sample[which]=song.sample[which+1];
song.sample[which+1]=prev;
moveAsset(song.sampleDir,which,which+1);
saveLock.unlock();
renderSamples();
BUSY_END;
@ -4085,6 +4167,10 @@ void DivEngine::autoPatchbayP() {
BUSY_END;
}
void DivEngine::recalcPatchbay() {
}
bool DivEngine::patchConnect(unsigned int src, unsigned int dest) {
unsigned int armed=(src<<16)|(dest&0xffff);
for (unsigned int i: song.patchbay) {
@ -4236,9 +4322,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
void DivEngine::autoNoteOff(int ch, int note, int vol) {
if (!playing) {
reset();
freelance=true;
playing=true;
return;
}
//if (ch<0 || ch>=chans) return;
for (int i=0; i<chans; i++) {
@ -4251,9 +4335,7 @@ void DivEngine::autoNoteOff(int ch, int note, int vol) {
void DivEngine::autoNoteOffAll() {
if (!playing) {
reset();
freelance=true;
playing=true;
return;
}
for (int i=0; i<chans; i++) {
if (chan[i].midiNote!=-1) {
@ -4296,23 +4378,11 @@ void DivEngine::updateSysFlags(int system, bool restart) {
BUSY_END;
}
void DivEngine::setSongRate(float hz, bool pal) {
void DivEngine::setSongRate(float hz) {
BUSY_BEGIN;
saveLock.lock();
curSubSong->pal=!pal;
curSubSong->hz=hz;
// what?
curSubSong->customTempo=true;
divider=60;
if (curSubSong->customTempo) {
divider=curSubSong->hz;
} else {
if (curSubSong->pal) {
divider=60;
} else {
divider=50;
}
}
divider=curSubSong->hz;
saveLock.unlock();
BUSY_END;
}
@ -4514,6 +4584,15 @@ bool DivEngine::initAudioBackend() {
}
}
#ifdef HAVE_SDL2
if (audioEngine==DIV_AUDIO_SDL) {
String audioDriver=getConfString("sdlAudioDriver","");
if (!audioDriver.empty()) {
SDL_SetHint("SDL_HINT_AUDIODRIVER",audioDriver.c_str());
}
}
#endif
lowQuality=getConfInt("audioQuality",0);
forceMono=getConfInt("forceMono",0);
clampSamples=getConfInt("clampSamples",0);
@ -4522,7 +4601,7 @@ bool DivEngine::initAudioBackend() {
midiOutClock=getConfInt("midiOutClock",0);
midiOutTime=getConfInt("midiOutTime",0);
midiOutTimeRate=getConfInt("midiOutTimeRate",0);
midiOutProgramChange = getConfInt("midiOutProgramChange",0);
midiOutProgramChange=getConfInt("midiOutProgramChange",0);
midiOutMode=getConfInt("midiOutMode",DIV_MIDI_MODE_NOTE);
if (metroVol<0.0f) metroVol=0.0f;
if (metroVol>2.0f) metroVol=2.0f;

View file

@ -23,6 +23,7 @@
#include "instrument.h"
#include "song.h"
#include "dispatch.h"
#include "effect.h"
#include "export.h"
#include "dataErrors.h"
#include "safeWriter.h"
@ -53,17 +54,16 @@
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
#define DIV_VERSION "dev155"
#define DIV_ENGINE_VERSION 155
#define DIV_UNSTABLE
#define DIV_VERSION "dev164"
#define DIV_ENGINE_VERSION 164
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
#define DIV_VERSION_S3M 0xff03
#define DIV_VERSION_FTM 0xff04
// "Namco C163"
#define DIV_C163_DEFAULT_NAME "Namco 163"
enum DivStatusView {
DIV_STATUS_NOTHING=0,
DIV_STATUS_PATTERN,
@ -222,6 +222,25 @@ struct DivDispatchContainer {
}
};
struct DivEffectContainer {
DivEffect* effect;
float* in[DIV_MAX_OUTPUTS];
float* out[DIV_MAX_OUTPUTS];
size_t inLen, outLen;
void preAcquire(size_t count);
void acquire(size_t count);
bool init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len);
void quit();
DivEffectContainer():
effect(NULL),
inLen(0),
outLen(0) {
memset(in,0,DIV_MAX_OUTPUTS*sizeof(float*));
memset(out,0,DIV_MAX_OUTPUTS*sizeof(float*));
}
};
typedef int EffectValConversion(unsigned char,unsigned char);
struct EffectHandler {
@ -411,6 +430,7 @@ class DivEngine {
std::vector<String> midiOuts;
std::vector<DivCommand> cmdStream;
std::vector<DivInstrumentType> possibleInsTypes;
std::vector<DivEffectContainer> effectInst;
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
@ -441,7 +461,7 @@ class DivEngine {
short tremTable[128];
int reversePitchTable[4096];
int pitchTable[4096];
char c163NameCS[1024];
short effectSlotMap[4096];
int midiBaseChan;
bool midiPoly;
size_t midiAgeCounter;
@ -467,7 +487,7 @@ class DivEngine {
void processRow(int i, bool afterDelay);
void nextOrder();
void nextRow();
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, bool directStream);
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream);
// returns true if end of song.
bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
@ -514,11 +534,21 @@ class DivEngine {
void swapChannels(int src, int dest);
void stompChannel(int ch);
// recalculate patchbay (UNSAFE)
void recalcPatchbay();
// change song (UNSAFE)
void changeSong(size_t songIndex);
// check whether an asset directory is complete
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);
// add every export method here
friend class DivROMExport;
@ -538,6 +568,7 @@ class DivEngine {
float oscSize;
int oscReadPos, oscWritePos;
int tickMult;
int lastNBIns, lastNBOuts, lastNBSize;
std::atomic<size_t> processTime;
void runExportThread();
@ -559,7 +590,7 @@ class DivEngine {
SafeWriter* saveDMF(unsigned char version);
// save as .fur.
// if notPrimary is true then the song will not be altered
SafeWriter* saveFur(bool notPrimary=false);
SafeWriter* saveFur(bool notPrimary=false, bool newPatternFormat=true);
// build a ROM file (TODO).
// specify system to build ROM for.
std::vector<DivROMExportOutput> buildROM(DivROMExportOptions sys);
@ -597,6 +628,8 @@ 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();
@ -920,7 +953,7 @@ class DivEngine {
void updateSysFlags(int system, bool restart);
// set Hz
void setSongRate(float hz, bool pal);
void setSongRate(float hz);
// set remaining loops. -1 means loop forever.
void setLoops(int loops);
@ -1018,6 +1051,9 @@ class DivEngine {
// add subsong
int addSubSong();
// duplicate subsong
int duplicateSubSong(int index);
// remove subsong
bool removeSubSong(int index);
@ -1040,6 +1076,12 @@ class DivEngine {
// move system
bool swapSystem(int src, int dest, bool preserveOrder=true);
// add effect
bool addEffect(DivEffectType which);
// remove effect
bool removeEffect(int index);
// write to register on system
void poke(int sys, unsigned int addr, unsigned short val);
@ -1209,6 +1251,9 @@ class DivEngine {
oscReadPos(0),
oscWritePos(0),
tickMult(1),
lastNBIns(0),
lastNBOuts(0),
lastNBSize(0),
processTime(0),
yrw801ROM(NULL),
tg100ROM(NULL),
@ -1222,6 +1267,7 @@ class DivEngine {
memset(tremTable,0,128*sizeof(short));
memset(reversePitchTable,0,4096*sizeof(int));
memset(pitchTable,0,4096*sizeof(int));
memset(effectSlotMap,-1,4096*sizeof(short));
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
memset(walked,0,8192);
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));

View file

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dataErrors.h"
#include "engine.h"
#include "../ta-log.h"
#include "instrument.h"
@ -219,20 +220,22 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.subsong[0]->hilightB=reader.readC();
}
bool customTempo=false;
ds.subsong[0]->timeBase=reader.readC();
ds.subsong[0]->speeds.len=2;
ds.subsong[0]->speeds.val[0]=reader.readC();
if (ds.version>0x07) {
ds.subsong[0]->speeds.val[1]=reader.readC();
ds.subsong[0]->pal=reader.readC();
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
ds.subsong[0]->customTempo=reader.readC();
bool pal=reader.readC();
ds.subsong[0]->hz=pal?60:50;
customTempo=reader.readC();
} else {
ds.subsong[0]->speeds.len=1;
}
if (ds.version>0x0a) {
String hz=reader.readString(3);
if (ds.subsong[0]->customTempo) {
if (customTempo) {
try {
ds.subsong[0]->hz=std::stoi(hz);
} catch (std::exception& e) {
@ -303,7 +306,6 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.subsong[0]->hz=248;
break;
}
ds.subsong[0]->customTempo=true;
ds.subsong[0]->timeBase=0;
addWarning("Yamaha YMU759 emulation is incomplete! please migrate your song to the OPL3 system.");
}
@ -1045,6 +1047,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.systemFlags[0].set("dpcmMode",false);
}
// C64 no key priority
if (ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) {
ds.systemFlags[0].set("keyPriority",false);
}
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
if (active) quitDispatch();
@ -1649,18 +1656,58 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
}
}
short newFormatNotes[180]={
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -5
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -4
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -3
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -2
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -1
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 0
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 1
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 2
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 3
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 4
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 5
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 6
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 7
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 8
12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 // 9
};
short newFormatOctaves[180]={
250, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, // -5
251, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, // -4
252, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, // -3
253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, // -2
254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // -1
255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 2
2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 3
3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 4
4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 5
5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 6
6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 7
7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 8
8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 9
};
bool DivEngine::loadFur(unsigned char* file, size_t len) {
unsigned int insPtr[256];
unsigned int wavePtr[256];
unsigned int samplePtr[256];
unsigned int subSongPtr[256];
unsigned int sysFlagsPtr[DIV_MAX_CHIPS];
std::vector<int> patPtr;
unsigned int assetDirPtr[3];
std::vector<unsigned int> patPtr;
int numberOfSubSongs=0;
char magic[5];
memset(magic,0,5);
SafeReader reader=SafeReader(file,len);
warnings="";
assetDirPtr[0]=0;
assetDirPtr[1]=0;
assetDirPtr[2]=0;
try {
DivSong ds;
DivSubSong* subSong=ds.subsong[0];
@ -1823,8 +1870,6 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
subSong->speeds.val[1]=reader.readC();
subSong->arpLen=reader.readC();
subSong->hz=reader.readF();
subSong->pal=(subSong->hz>=53);
subSong->customTempo=true;
subSong->patLen=reader.readS();
subSong->ordersLen=reader.readS();
@ -2332,6 +2377,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
if (ds.version>=156) {
assetDirPtr[0]=reader.readI();
assetDirPtr[1]=reader.readI();
assetDirPtr[2]=reader.readI();
}
// read system flags
if (ds.version>=119) {
logD("reading chip flags...");
@ -2366,6 +2417,53 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
// read asset directories
if (ds.version>=156) {
logD("reading asset directories...");
if (!reader.seek(assetDirPtr[0],SEEK_SET)) {
logE("couldn't seek to ins dir!");
lastError=fmt::sprintf("couldn't read instrument directory");
ds.unload();
delete[] file;
return false;
}
if (readAssetDirData(reader,ds.insDir)!=DIV_DATA_SUCCESS) {
lastError="invalid instrument directory data!";
ds.unload();
delete[] file;
return false;
}
if (!reader.seek(assetDirPtr[1],SEEK_SET)) {
logE("couldn't seek to wave dir!");
lastError=fmt::sprintf("couldn't read wavetable directory");
ds.unload();
delete[] file;
return false;
}
if (readAssetDirData(reader,ds.waveDir)!=DIV_DATA_SUCCESS) {
lastError="invalid wavetable directory data!";
ds.unload();
delete[] file;
return false;
}
if (!reader.seek(assetDirPtr[2],SEEK_SET)) {
logE("couldn't seek to sample dir!");
lastError=fmt::sprintf("couldn't read sample directory");
ds.unload();
delete[] file;
return false;
}
if (readAssetDirData(reader,ds.sampleDir)!=DIV_DATA_SUCCESS) {
lastError="invalid sample directory data!";
ds.unload();
delete[] file;
return false;
}
}
// read subsongs
if (ds.version>=95) {
for (int i=0; i<numberOfSubSongs; i++) {
@ -2395,8 +2493,6 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
subSong->speeds.val[1]=reader.readC();
subSong->arpLen=reader.readC();
subSong->hz=reader.readF();
subSong->pal=(subSong->hz>=53);
subSong->customTempo=true;
subSong->patLen=reader.readS();
subSong->ordersLen=reader.readS();
@ -2523,7 +2619,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
// read patterns
for (int i: patPtr) {
for (unsigned int i: patPtr) {
bool isNewFormat=false;
if (!reader.seek(i,SEEK_SET)) {
logE("couldn't seek to pattern in %x!",i);
lastError=fmt::sprintf("couldn't seek to pattern in %x!",i);
@ -2534,62 +2631,151 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
reader.read(magic,4);
logD("reading pattern in %x...",i);
if (strcmp(magic,"PATR")!=0) {
logE("%x: invalid pattern header!",i);
lastError="invalid pattern header!";
ds.unload();
delete[] file;
return false;
if (strcmp(magic,"PATN")!=0 || ds.version<157) {
logE("%x: invalid pattern header!",i);
lastError="invalid pattern header!";
ds.unload();
delete[] file;
return false;
} else {
isNewFormat=true;
}
}
reader.readI();
int chan=reader.readS();
int index=reader.readS();
int subs=0;
if (ds.version>=95) {
subs=reader.readS();
} else {
reader.readS();
}
reader.readS();
if (isNewFormat) {
int subs=(unsigned char)reader.readC();
int chan=(unsigned char)reader.readC();
int index=reader.readS();
logD("- %d, %d, %d",subs,chan,index);
logD("- %d, %d, %d (new)",subs,chan,index);
if (chan<0 || chan>=tchans) {
logE("pattern channel out of range!",i);
lastError="pattern channel out of range!";
ds.unload();
delete[] file;
return false;
}
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
logE("pattern index out of range!",i);
lastError="pattern index out of range!";
ds.unload();
delete[] file;
return false;
}
if (subs<0 || subs>=(int)ds.subsong.size()) {
logE("pattern subsong out of range!",i);
lastError="pattern subsong out of range!";
ds.unload();
delete[] file;
return false;
}
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
pat->data[j][0]=reader.readS();
pat->data[j][1]=reader.readS();
pat->data[j][2]=reader.readS();
pat->data[j][3]=reader.readS();
for (int k=0; k<ds.subsong[subs]->pat[chan].effectCols; k++) {
pat->data[j][4+(k<<1)]=reader.readS();
pat->data[j][5+(k<<1)]=reader.readS();
if (chan<0 || chan>=tchans) {
logE("pattern channel out of range!",i);
lastError="pattern channel out of range!";
ds.unload();
delete[] file;
return false;
}
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
logE("pattern index out of range!",i);
lastError="pattern index out of range!";
ds.unload();
delete[] file;
return false;
}
if (subs<0 || subs>=(int)ds.subsong.size()) {
logE("pattern subsong out of range!",i);
lastError="pattern subsong out of range!";
ds.unload();
delete[] file;
return false;
}
}
if (ds.version>=51) {
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
pat->name=reader.readString();
// read new pattern
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
unsigned char mask=reader.readC();
unsigned short effectMask=0;
if (mask==0xff) break;
if (mask&128) {
j+=(mask&127)+1;
continue;
}
if (mask&32) {
effectMask|=(unsigned char)reader.readC();
}
if (mask&64) {
effectMask|=((unsigned short)reader.readC()&0xff)<<8;
}
if (mask&8) effectMask|=1;
if (mask&16) effectMask|=2;
if (mask&1) { // note
unsigned char note=reader.readC();
if (note==180) {
pat->data[j][0]=100;
pat->data[j][1]=0;
} else if (note==181) {
pat->data[j][0]=101;
pat->data[j][1]=0;
} else if (note==182) {
pat->data[j][0]=102;
pat->data[j][1]=0;
} else if (note<180) {
pat->data[j][0]=newFormatNotes[note];
pat->data[j][1]=newFormatOctaves[note];
} else {
pat->data[j][0]=0;
pat->data[j][1]=0;
}
}
if (mask&2) { // instrument
pat->data[j][2]=(unsigned char)reader.readC();
}
if (mask&4) { // volume
pat->data[j][3]=(unsigned char)reader.readC();
}
for (unsigned char k=0; k<16; k++) {
if (effectMask&(1<<k)) {
pat->data[j][4+k]=(unsigned char)reader.readC();
}
}
}
} else {
int chan=reader.readS();
int index=reader.readS();
int subs=0;
if (ds.version>=95) {
subs=reader.readS();
} else {
reader.readS();
}
reader.readS();
logD("- %d, %d, %d (old)",subs,chan,index);
if (chan<0 || chan>=tchans) {
logE("pattern channel out of range!",i);
lastError="pattern channel out of range!";
ds.unload();
delete[] file;
return false;
}
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
logE("pattern index out of range!",i);
lastError="pattern index out of range!";
ds.unload();
delete[] file;
return false;
}
if (subs<0 || subs>=(int)ds.subsong.size()) {
logE("pattern subsong out of range!",i);
lastError="pattern subsong out of range!";
ds.unload();
delete[] file;
return false;
}
DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
for (int j=0; j<ds.subsong[subs]->patLen; j++) {
pat->data[j][0]=reader.readS();
pat->data[j][1]=reader.readS();
pat->data[j][2]=reader.readS();
pat->data[j][3]=reader.readS();
for (int k=0; k<ds.subsong[subs]->pat[chan].effectCols; k++) {
pat->data[j][4+(k<<1)]=reader.readS();
pat->data[j][5+(k<<1)]=reader.readS();
}
}
if (ds.version>=51) {
pat->name=reader.readString();
}
}
}
@ -2746,6 +2932,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
// C64 key priority compat
if (ds.version<160) {
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_C64_8580 || ds.system[i]==DIV_SYSTEM_C64_6581) {
ds.systemFlags[i].set("keyPriority",false);
}
}
}
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
@ -3138,9 +3333,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
ds.subsong[0]->pat[ch].effectCols=fxCols;
}
ds.subsong[0]->pal=false;
ds.subsong[0]->hz=50;
ds.subsong[0]->customTempo=false;
ds.systemLen=(chCount+3)/4;
for(int i=0; i<ds.systemLen; i++) {
ds.system[i]=DIV_SYSTEM_AMIGA;
@ -3363,7 +3556,6 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
ds.subsong[0]->speeds.val[0]=(unsigned char)reader.readC();
ds.subsong[0]->hz=((double)reader.readC())/2.5;
ds.subsong[0]->customTempo=true;
unsigned char masterVol=reader.readC();
@ -3809,8 +4001,6 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.subsong[0]->ordersLen=seqLen;
ds.subsong[0]->patLen=32;
ds.subsong[0]->hz=50;
ds.subsong[0]->pal=true;
ds.subsong[0]->customTempo=true;
ds.subsong[0]->pat[3].effectCols=3;
ds.subsong[0]->speeds.val[0]=3;
ds.subsong[0]->speeds.len=1;
@ -4843,7 +5033,57 @@ struct PatToWrite {
pat(p) {}
};
SafeWriter* DivEngine::saveFur(bool notPrimary) {
void DivEngine::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 DivEngine::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();
for (unsigned int i=0; i<numDirs; i++) {
DivAssetDir d;
d.name=reader.readString();
unsigned short numEntries=reader.readS();
for (unsigned short j=0; j<numEntries; j++) {
d.entries.push_back(((unsigned char)reader.readC()));
}
dir.push_back(d);
}
return DIV_DATA_SUCCESS;
}
SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
saveLock.lock();
std::vector<int> subSongPtr;
std::vector<int> sysFlagsPtr;
@ -4851,7 +5091,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
std::vector<int> wavePtr;
std::vector<int> samplePtr;
std::vector<int> patPtr;
size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek;
int assetDirPtr[3];
size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek, assetDirPtrSeek;
size_t subSongIndex=0;
DivSubSong* subSong=song.subsong[subSongIndex];
warnings="";
@ -5150,6 +5391,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
}
}
// asset dir pointers (we'll seek here later)
assetDirPtrSeek=w->tell();
w->writeI(0);
w->writeI(0);
w->writeI(0);
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
@ -5237,6 +5484,14 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->seek(0,SEEK_END);
}
/// ASSET DIRECTORIES
assetDirPtr[0]=w->tell();
putAssetDirData(w,song.insDir);
assetDirPtr[1]=w->tell();
putAssetDirData(w,song.waveDir);
assetDirPtr[2]=w->tell();
putAssetDirData(w,song.sampleDir);
/// INSTRUMENT
for (int i=0; i<song.insLen; i++) {
DivInstrument* ins=song.ins[i];
@ -5262,31 +5517,133 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
for (PatToWrite& i: patsToWrite) {
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
patPtr.push_back(w->tell());
w->write("PATR",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeS(i.chan);
w->writeS(i.pat);
w->writeS(i.subsong);
if (newPatternFormat) {
w->write("PATN",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeS(0); // reserved
w->writeC(i.subsong);
w->writeC(i.chan);
w->writeS(i.pat);
w->writeString(pat->name,false);
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
w->writeS(pat->data[j][0]); // note
w->writeS(pat->data[j][1]); // octave
w->writeS(pat->data[j][2]); // instrument
w->writeS(pat->data[j][3]); // volume
#ifdef TA_BIG_ENDIAN
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k++) {
w->writeS(pat->data[j][4+k]);
unsigned char emptyRows=0;
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
unsigned char mask=0;
unsigned char finalNote=255;
unsigned short effectMask=0;
if (pat->data[j][0]==100) {
finalNote=180;
} else if (pat->data[j][0]==101) { // note release
finalNote=181;
} else if (pat->data[j][0]==102) { // macro release
finalNote=182;
} else if (pat->data[j][1]==0 && pat->data[j][0]==0) {
finalNote=255;
} else {
int seek=(pat->data[j][0]+(signed char)pat->data[j][1]*12)+60;
if (seek<0 || seek>=180) {
finalNote=255;
} else {
finalNote=seek;
}
}
if (finalNote!=255) mask|=1; // note
if (pat->data[j][2]!=-1) mask|=2; // instrument
if (pat->data[j][3]!=-1) mask|=4; // volume
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k+=2) {
if (k==0) {
if (pat->data[j][4+k]!=-1) mask|=8;
if (pat->data[j][5+k]!=-1) mask|=16;
} else if (k<8) {
if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=32;
} else {
if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=64;
}
if (pat->data[j][4+k]!=-1) effectMask|=(1<<k);
if (pat->data[j][5+k]!=-1) effectMask|=(2<<k);
}
if (mask==0) {
emptyRows++;
if (emptyRows>127) {
w->writeC(128|(emptyRows-2));
emptyRows=0;
}
} else {
if (emptyRows>1) {
w->writeC(128|(emptyRows-2));
emptyRows=0;
} else if (emptyRows) {
w->writeC(0);
emptyRows=0;
}
w->writeC(mask);
if (mask&32) w->writeC(effectMask&0xff);
if (mask&64) w->writeC((effectMask>>8)&0xff);
if (mask&1) w->writeC(finalNote);
if (mask&2) w->writeC(pat->data[j][2]);
if (mask&4) w->writeC(pat->data[j][3]);
if (mask&8) w->writeC(pat->data[j][4]);
if (mask&16) w->writeC(pat->data[j][5]);
if (mask&32) {
if (effectMask&4) w->writeC(pat->data[j][6]);
if (effectMask&8) w->writeC(pat->data[j][7]);
if (effectMask&16) w->writeC(pat->data[j][8]);
if (effectMask&32) w->writeC(pat->data[j][9]);
if (effectMask&64) w->writeC(pat->data[j][10]);
if (effectMask&128) w->writeC(pat->data[j][11]);
}
if (mask&64) {
if (effectMask&256) w->writeC(pat->data[j][12]);
if (effectMask&512) w->writeC(pat->data[j][13]);
if (effectMask&1024) w->writeC(pat->data[j][14]);
if (effectMask&2048) w->writeC(pat->data[j][15]);
if (effectMask&4096) w->writeC(pat->data[j][16]);
if (effectMask&8192) w->writeC(pat->data[j][17]);
if (effectMask&16384) w->writeC(pat->data[j][18]);
if (effectMask&32768) w->writeC(pat->data[j][19]);
}
}
}
#else
w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
#endif
}
w->writeString(pat->name,false);
// stop
w->writeC(0xff);
} else {
w->write("PATR",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeS(i.chan);
w->writeS(i.pat);
w->writeS(i.subsong);
w->writeS(0); // reserved
for (int j=0; j<song.subsong[i.subsong]->patLen; j++) {
w->writeS(pat->data[j][0]); // note
w->writeS(pat->data[j][1]); // octave
w->writeS(pat->data[j][2]); // instrument
w->writeS(pat->data[j][3]); // volume
#ifdef TA_BIG_ENDIAN
for (int k=0; k<song.subsong[i.subsong]->pat[i.chan].effectCols*2; k++) {
w->writeS(pat->data[j][4+k]);
}
#else
w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
#endif
}
w->writeString(pat->name,false);
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
@ -5328,13 +5685,20 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(sysFlagsPtr[i]);
}
// asset dir pointers
w->seek(assetDirPtrSeek,SEEK_SET);
for (size_t i=0; i<3; i++) {
w->writeI(assetDirPtr[i]);
}
saveLock.unlock();
return w;
}
SafeWriter* DivEngine::saveDMF(unsigned char version) {
// fail if version is not supported
if (version<24 || version>26) {
if (version>26) version=26;
if (version<24) {
logE("cannot save in this version!");
lastError="invalid version to save in! this is a bug!";
return NULL;
@ -5462,12 +5826,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeString(song.author,true);
w->writeC(curSubSong->hilightA);
w->writeC(curSubSong->hilightB);
int intHz=curSubSong->hz;
w->writeC(curSubSong->timeBase);
w->writeC(curSubSong->speeds.val[0]);
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
w->writeC(curSubSong->pal);
w->writeC(curSubSong->customTempo);
w->writeC((intHz<=53)?0:1);
w->writeC((intHz!=60 && intHz!=50));
char customHz[4];
memset(customHz,0,4);
snprintf(customHz,4,"%d",(int)curSubSong->hz);

140
src/engine/fixedQueue.h Normal file
View file

@ -0,0 +1,140 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 _FIXED_QUEUE_H
#define _FIXED_QUEUE_H
#include <stdint.h>
#include "../ta-log.h"
template<typename T, size_t items> struct FixedQueue {
size_t readPos, writePos;
T data[items];
T& front();
T& back();
bool pop();
bool push(const T& item);
bool pop_front();
bool pop_back();
bool push_front(const T& item);
bool push_back(const T& item);
void clear();
bool empty();
size_t size();
FixedQueue():
readPos(0),
writePos(0) {}
};
template <typename T, size_t items> T& FixedQueue<T,items>::front() {
return data[readPos];
}
template <typename T, size_t items> T& FixedQueue<T,items>::back() {
if (writePos==0) return data[items-1];
return data[writePos-1];
}
template <typename T, size_t items> bool FixedQueue<T,items>::pop() {
if (readPos==writePos) return false;
if (++readPos>=items) readPos=0;
return true;
}
template <typename T, size_t items> bool FixedQueue<T,items>::push(const T& item) {
if (writePos==(readPos-1)) {
logW("queue overflow!");
return false;
}
if (writePos==items-1 && readPos==0) {
logW("queue overflow!");
return false;
}
data[writePos]=item;
if (++writePos>=items) writePos=0;
return true;
}
template <typename T, size_t items> bool FixedQueue<T,items>::pop_front() {
if (readPos==writePos) return false;
if (++readPos>=items) readPos=0;
return true;
}
template <typename T, size_t items> bool FixedQueue<T,items>::push_back(const T& item) {
if (writePos==(readPos-1)) {
logW("queue overflow!");
return false;
}
if (writePos==items-1 && readPos==0) {
logW("queue overflow!");
return false;
}
data[writePos]=item;
if (++writePos>=items) writePos=0;
return true;
}
template <typename T, size_t items> bool FixedQueue<T,items>::pop_back() {
if (readPos==writePos) return false;
if (writePos>0) {
writePos--;
} else {
writePos=items-1;
}
return true;
}
template <typename T, size_t items> bool FixedQueue<T,items>::push_front(const T& item) {
if (readPos==(writePos+1)) {
logW("stack overflow!");
return false;
}
if (readPos==0 && writePos==items-1) {
logW("stack overflow!");
return false;
}
if (readPos>0) {
readPos--;
} else {
readPos=items-1;
}
data[readPos]=item;
return true;
}
template <typename T, size_t items> void FixedQueue<T,items>::clear() {
readPos=0;
writePos=0;
}
template <typename T, size_t items> bool FixedQueue<T,items>::empty() {
return (readPos==writePos);
}
template <typename T, size_t items> size_t FixedQueue<T,items>::size() {
if (readPos>writePos) {
return items+writePos-readPos;
}
return writePos-readPos;
}
#endif

View file

@ -134,7 +134,24 @@ bool DivInstrumentN163::operator==(const DivInstrumentN163& other) {
_C(wave) &&
_C(wavePos) &&
_C(waveLen) &&
_C(waveMode)
_C(waveMode) &&
_C(perChanPos) &&
_C(wavePosCh[0]) &&
_C(wavePosCh[1]) &&
_C(wavePosCh[2]) &&
_C(wavePosCh[3]) &&
_C(wavePosCh[4]) &&
_C(wavePosCh[5]) &&
_C(wavePosCh[6]) &&
_C(wavePosCh[7]) &&
_C(waveLenCh[0]) &&
_C(waveLenCh[1]) &&
_C(waveLenCh[2]) &&
_C(waveLenCh[3]) &&
_C(waveLenCh[4]) &&
_C(waveLenCh[5]) &&
_C(waveLenCh[6]) &&
_C(waveLenCh[7])
);
}
@ -519,6 +536,17 @@ void DivInstrument::writeFeatureN1(SafeWriter* w) {
w->writeC(n163.waveLen);
w->writeC(n163.waveMode);
w->writeC(n163.perChanPos);
if (n163.perChanPos) {
for (int i=0; i<8; i++) {
w->writeC(n163.wavePosCh[i]);
}
for (int i=0; i<8; i++) {
w->writeC(n163.waveLenCh[i]);
}
}
FEATURE_END;
}
@ -929,6 +957,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break;
case DIV_INS_PV1000:
break;
case DIV_INS_K053260:
featureSM=true;
featureSL=true;
break;
case DIV_INS_MAX:
break;
@ -2276,6 +2308,18 @@ void DivInstrument::readFeatureN1(SafeReader& reader, short version) {
n163.waveLen=(unsigned char)reader.readC();
n163.waveMode=(unsigned char)reader.readC();
if (version>=164) {
n163.perChanPos=reader.readC();
if (n163.perChanPos) {
for (int i=0; i<8; i++) {
n163.wavePosCh[i]=(unsigned char)reader.readC();
}
for (int i=0; i<8; i++) {
n163.waveLenCh[i]=(unsigned char)reader.readC();
}
}
}
READ_FEAT_END;
}

View file

@ -80,6 +80,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_POKEMINI=47,
DIV_INS_SM8521=48,
DIV_INS_PV1000=49,
DIV_INS_K053260=50,
DIV_INS_MAX,
DIV_INS_NULL
};
@ -445,6 +446,9 @@ struct DivInstrumentX1_010 {
struct DivInstrumentN163 {
int wave, wavePos, waveLen;
unsigned char waveMode;
bool perChanPos;
int wavePosCh[8];
int waveLenCh[8];
bool operator==(const DivInstrumentN163& other);
bool operator!=(const DivInstrumentN163& other) {
@ -455,7 +459,13 @@ struct DivInstrumentN163 {
wave(-1),
wavePos(0),
waveLen(32),
waveMode(3) {}
waveMode(3),
perChanPos(false) {
for (int i=0; i<8; i++) {
wavePosCh[i]=(i&3)<<5;
waveLenCh[i]=32;
}
}
};
struct DivInstrumentFDS {

View file

@ -168,7 +168,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
outL+=(output*sep2)>>7;
outR+=(output*sep1)>>7;
}
oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]))<<2;
oscBuf[i]->data[oscBuf[i]->needle++]=(amiga.nextOut[i]*MIN(64,amiga.audVol[i]))<<1;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
}

View file

@ -21,7 +21,6 @@
#define _AMIGA_H
#include "../dispatch.h"
#include <queue>
#include "../waveSynth.h"
class DivPlatformAmiga: public DivDispatch {

View file

@ -76,7 +76,8 @@ void DivPlatformArcade::acquire_nuked(short** buf, size_t len) {
}
for (int i=0; i<8; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i];
int chOut=(int16_t)fm.ch_out[i];
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767);
}
if (o[0]<-32768) o[0]=-32768;
@ -111,7 +112,8 @@ void DivPlatformArcade::acquire_ymfm(short** buf, size_t len) {
fm_ymfm->generate(&out_ymfm);
for (int i=0; i<8; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1));
int chOut=fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1);
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut,-32768,32767);
}
os[0]=out_ymfm.data[0];
@ -876,7 +878,7 @@ void DivPlatformArcade::poke(std::vector<DivRegWrite>& wlist) {
}
void DivPlatformArcade::reset() {
while (!writes.empty()) writes.pop_front();
writes.clear();
memset(regPool,0,256);
if (useYMFM) {
fm_ymfm->reset();

View file

@ -20,7 +20,6 @@
#ifndef _ARCADE_H
#define _ARCADE_H
#include "fmshared_OPM.h"
#include <queue>
#include "../../../extern/opm/opm.h"
#include "sound/ymfm/ymfm_opm.h"

View file

@ -25,7 +25,7 @@
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} }
#define immWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(regRemap(a),v)); if (dumpWrites) {addWrite(regRemap(a),v);} }
#define CHIP_DIVIDER (extMode?extDiv:((sunsoft||clockSel)?16:8))
@ -187,9 +187,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
buf[0][i]=ayBuf[0][0];
buf[1][i]=buf[0][i];
oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]>>3;
oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]>>3;
oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]>>3;
oscBuf[0]->data[oscBuf[0]->needle++]=CLAMP(sunsoftVolTable[31-(ay->lastIndx&31)]<<3,-32768,32767);
oscBuf[1]->data[oscBuf[1]->needle++]=CLAMP(sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3,-32768,32767);
oscBuf[2]->data[oscBuf[2]->needle++]=CLAMP(sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3,-32768,32767);
}
} else {
for (size_t i=0; i<len; i++) {

View file

@ -20,7 +20,7 @@
#ifndef _AY_H
#define _AY_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "sound/ay8910.h"
class DivPlatformAY8910: public DivDispatch {
@ -89,9 +89,10 @@ class DivPlatformAY8910: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,128> writes;
ay8910_device* ay;
DivDispatchOscBuffer* oscBuf[3];
unsigned char regPool[16];

View file

@ -25,7 +25,7 @@
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define immWrite2(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER (clockSel?8:4)

View file

@ -20,7 +20,7 @@
#ifndef _AY8930_H
#define _AY8930_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "sound/ay8910.h"
class DivPlatformAY8930: public DivDispatch {
@ -99,9 +99,10 @@ class DivPlatformAY8930: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,128> writes;
ay8930_device* ay;
DivDispatchOscBuffer* oscBuf[3];
unsigned char regPool[32];

View file

@ -21,7 +21,6 @@
#define _K005289_H
#include "../dispatch.h"
#include <queue>
#include "../waveSynth.h"
#include "vgsound_emu/src/k005289/k005289.hpp"

View file

@ -21,8 +21,9 @@
#include "../engine.h"
#include "sound/c64_fp/siddefs-fp.h"
#include <math.h>
#include "../../ta-log.h"
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_FREQBASE 524288
@ -63,35 +64,81 @@ const char** DivPlatformC64::getRegisterSheet() {
return regCheatSheetSID;
}
short DivPlatformC64::runFakeFilter(unsigned char ch, int in) {
if (!(regPool[0x17]&(1<<ch))) {
if (regPool[0x18]&0x80 && ch==2) return 0;
float fin=in;
fin*=(float)(regPool[0x18]&15)/20.0f;
return CLAMP(fin,-32768,32767);
}
// taken from dSID
float fin=in;
float fout=0;
float ctf=fakeCutTable[((regPool[0x15]&7)|(regPool[0x16]<<3))&0x7ff];
float reso=(sidIs6581?
((regPool[0x17]>0x5F)?8.0/(float)(regPool[0x17]>>4):1.41):
(pow(2,((float)(4-(float)(regPool[0x17]>>4))/8)))
);
float tmp=fin+fakeBand[ch]*reso+fakeLow[ch];
if (regPool[0x18]&0x40) {
fout-=tmp;
}
tmp=fakeBand[ch]-tmp*ctf;
fakeBand[ch]=tmp;
if (regPool[0x18]&0x20) {
fout-=tmp;
}
tmp=fakeLow[ch]+tmp*ctf;
fakeLow[ch]=tmp;
if (regPool[0x18]&0x10) {
fout+=tmp;
}
fout*=(float)(regPool[0x18]&15)/20.0f;
return CLAMP(fout,-32768,32767);
}
void DivPlatformC64::acquire(short** buf, size_t len) {
int dcOff=isFP?0:sid.get_dc(0);
int dcOff=(sidCore)?0:sid->get_dc(0);
for (size_t i=0; i<len; i++) {
if (!writes.empty()) {
QueuedWrite w=writes.front();
if (isFP) {
sid_fp.write(w.addr,w.val);
if (sidCore==2) {
dSID_write(sid_d,w.addr,w.val);
} else if (sidCore==1) {
sid_fp->write(w.addr,w.val);
} else {
sid.write(w.addr,w.val);
};
sid->write(w.addr,w.val);
}
regPool[w.addr&0x1f]=w.val;
writes.pop();
}
if (isFP) {
sid_fp.clock(4,&buf[0][i]);
if (sidCore==2) {
double o=dSID_render(sid_d);
buf[0][i]=32767*CLAMP(o,-1.0,1.0);
if (++writeOscBuf>=4) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5;
oscBuf[0]->data[oscBuf[0]->needle++]=sid_d->lastOut[0];
oscBuf[1]->data[oscBuf[1]->needle++]=sid_d->lastOut[1];
oscBuf[2]->data[oscBuf[2]->needle++]=sid_d->lastOut[2];
}
} else if (sidCore==1) {
sid_fp->clock(4,&buf[0][i]);
if (++writeOscBuf>=4) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=runFakeFilter(0,(sid_fp->lastChanOut[0]-dcOff)>>5);
oscBuf[1]->data[oscBuf[1]->needle++]=runFakeFilter(1,(sid_fp->lastChanOut[1]-dcOff)>>5);
oscBuf[2]->data[oscBuf[2]->needle++]=runFakeFilter(2,(sid_fp->lastChanOut[2]-dcOff)>>5);
}
} else {
sid.clock();
buf[0][i]=sid.output();
sid->clock();
buf[0][i]=sid->output();
if (++writeOscBuf>=16) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5;
oscBuf[0]->data[oscBuf[0]->needle++]=runFakeFilter(0,(sid->last_chan_out[0]-dcOff)>>5);
oscBuf[1]->data[oscBuf[1]->needle++]=runFakeFilter(1,(sid->last_chan_out[1]-dcOff)>>5);
oscBuf[2]->data[oscBuf[2]->needle++]=runFakeFilter(2,(sid->last_chan_out[2]-dcOff)>>5);
}
}
}
@ -105,7 +152,10 @@ void DivPlatformC64::updateFilter() {
}
void DivPlatformC64::tick(bool sysTick) {
for (int i=0; i<3; i++) {
bool willUpdateFilter=false;
for (int _i=0; _i<3; _i++) {
int i=chanOrder[_i];
chan[i].std.next();
if (chan[i].std.vol.had) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
@ -117,10 +167,10 @@ void DivPlatformC64::tick(bool sysTick) {
if (filtCut>2047) filtCut=2047;
if (filtCut<0) filtCut=0;
}
updateFilter();
willUpdateFilter=true;
} else {
vol=MIN(15,chan[i].std.vol.val);
updateFilter();
willUpdateFilter=true;
}
}
if (NEW_ARP_STRAT) {
@ -156,11 +206,11 @@ void DivPlatformC64::tick(bool sysTick) {
}
if (chan[i].std.ex1.had) {
filtControl=chan[i].std.ex1.val&15;
updateFilter();
willUpdateFilter=true;
}
if (chan[i].std.ex2.had) {
filtRes=chan[i].std.ex2.val&15;
updateFilter();
willUpdateFilter=true;
}
if (chan[i].std.ex3.had) {
chan[i].sync=chan[i].std.ex3.val&1;
@ -178,8 +228,8 @@ void DivPlatformC64::tick(bool sysTick) {
if (--chan[i].testWhen<1) {
if (!chan[i].resetMask && !chan[i].inPorta) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
rWrite(i*7+5,0);
rWrite(i*7+6,0);
rWrite(i*7+5,testAD);
rWrite(i*7+6,testSR);
rWrite(i*7+4,(chan[i].wave<<4)|(ins->c64.noTest?0:8)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1));
}
}
@ -207,9 +257,11 @@ void DivPlatformC64::tick(bool sysTick) {
chan[i].freqChanged=false;
}
}
if (willUpdateFilter) updateFilter();
}
int DivPlatformC64::dispatch(DivCommand c) {
if (c.chan>2) return 0;
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64);
@ -247,6 +299,16 @@ int DivPlatformC64::dispatch(DivCommand c) {
if (chan[c.chan].insChanged) {
chan[c.chan].insChanged=false;
}
if (keyPriority) {
if (chanOrder[1]==c.chan) {
chanOrder[1]=chanOrder[2];
chanOrder[2]=c.chan;
} else if (chanOrder[0]==c.chan) {
chanOrder[0]=chanOrder[1];
chanOrder[1]=chanOrder[2];
chanOrder[2]=c.chan;
}
}
chan[c.chan].macroInit(ins);
break;
}
@ -350,7 +412,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
case DIV_CMD_C64_CUTOFF:
if (c.value>100) c.value=100;
filtCut=c.value*2047/100;
filtCut=(c.value+2)*2047/102;
updateFilter();
break;
case DIV_CMD_C64_FINE_CUTOFF:
@ -436,10 +498,17 @@ int DivPlatformC64::dispatch(DivCommand c) {
void DivPlatformC64::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (isFP) {
sid_fp.mute(ch,mute);
if (sidCore==2) {
dSID_setMuteMask(
sid_d,
(isMuted[0]?0:1)|
(isMuted[1]?0:2)|
(isMuted[2]?0:4)
);
} else if (sidCore==1) {
sid_fp->mute(ch,mute);
} else {
sid.set_is_muted(ch,mute);
sid->set_is_muted(ch,mute);
}
}
@ -498,7 +567,7 @@ bool DivPlatformC64::getWantPreNote() {
}
float DivPlatformC64::getPostAmp() {
return isFP?3.0f:1.0f;
return (sidCore==1)?3.0f:1.0f;
}
void DivPlatformC64::reset() {
@ -506,12 +575,24 @@ void DivPlatformC64::reset() {
for (int i=0; i<3; i++) {
chan[i]=DivPlatformC64::Channel();
chan[i].std.setEngine(parent);
fakeLow[i]=0;
fakeBand[i]=0;
}
if (isFP) {
sid_fp.reset();
if (sidCore==2) {
dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,needInitTables);
dSID_setMuteMask(
sid_d,
(isMuted[0]?0:1)|
(isMuted[1]?0:2)|
(isMuted[2]?0:4)
);
needInitTables=false;
} else if (sidCore==1) {
sid_fp->reset();
sid_fp->clockSilent(16000);
} else {
sid.reset();
sid->reset();
}
memset(regPool,0,32);
@ -522,6 +603,10 @@ void DivPlatformC64::reset() {
filtCut=2047;
resetTime=1;
vol=15;
chanOrder[0]=0;
chanOrder[1]=1;
chanOrder[2]=2;
}
void DivPlatformC64::poke(unsigned int addr, unsigned short val) {
@ -533,23 +618,11 @@ void DivPlatformC64::poke(std::vector<DivRegWrite>& wlist) {
}
void DivPlatformC64::setChipModel(bool is6581) {
if (is6581) {
if (isFP) {
sid_fp.setChipModel(reSIDfp::MOS6581);
} else {
sid.set_chip_model(MOS6581);
}
} else {
if (isFP) {
sid_fp.setChipModel(reSIDfp::MOS8580);
} else {
sid.set_chip_model(MOS8580);
}
}
sidIs6581=is6581;
}
void DivPlatformC64::setFP(bool fp) {
isFP=fp;
void DivPlatformC64::setCore(unsigned char which) {
sidCore=which;
}
void DivPlatformC64::setFlags(const DivConfig& flags) {
@ -570,9 +643,30 @@ void DivPlatformC64::setFlags(const DivConfig& flags) {
for (int i=0; i<3; i++) {
oscBuf[i]->rate=rate/16;
}
if (isFP) {
if (sidCore>0) {
rate/=4;
sid_fp.setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
if (sidCore==1) sid_fp->setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
}
keyPriority=flags.getBool("keyPriority",true);
testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15);
testSR=((flags.getInt("testSustain",0)&15)<<4)|(flags.getInt("testRelease",0)&15);
// init fake filter table
// taken from dSID
double cutRatio=-2.0*3.14*(sidIs6581?(((double)oscBuf[0]->rate/44100.0)*(20000.0/256.0)):(12500.0/256.0))/(double)oscBuf[0]->rate;
for (int i=0; i<2048; i++) {
double c=(double)i/8.0+0.2;
if (sidIs6581) {
if (c<24) {
c=2.0*sin(771.78/(double)oscBuf[0]->rate);
} else {
c=(44100.0/(double)oscBuf[0]->rate)-1.263*(44100.0/(double)oscBuf[0]->rate)*exp(c*cutRatio);
}
} else {
c=1-exp(c*cutRatio);
}
fakeCutTable[i]=c;
}
}
@ -580,11 +674,45 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, const DivConfi
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
needInitTables=true;
writeOscBuf=0;
for (int i=0; i<3; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
if (sidCore==2) {
sid=NULL;
sid_fp=NULL;
sid_d=new struct SID_chip;
} else if (sidCore==1) {
sid=NULL;
sid_fp=new reSIDfp::SID;
sid_d=NULL;
} else {
sid=new SID;
sid_fp=NULL;
sid_d=NULL;
}
if (sidIs6581) {
if (sidCore==2) {
// do nothing
} else if (sidCore==1) {
sid_fp->setChipModel(reSIDfp::MOS6581);
} else {
sid->set_chip_model(MOS6581);
}
} else {
if (sidCore==2) {
// do nothing
} else if (sidCore==1) {
sid_fp->setChipModel(reSIDfp::MOS8580);
} else {
sid->set_chip_model(MOS8580);
}
}
setFlags(flags);
reset();
@ -596,6 +724,9 @@ void DivPlatformC64::quit() {
for (int i=0; i<3; i++) {
delete oscBuf[i];
}
if (sid!=NULL) delete sid;
if (sid_fp!=NULL) delete sid_fp;
if (sid_d!=NULL) delete sid_d;
}
DivPlatformC64::~DivPlatformC64() {

View file

@ -21,9 +21,10 @@
#define _C64_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "sound/c64/sid.h"
#include "sound/c64_fp/SID.h"
#include "sound/c64_d/dsid.h"
class DivPlatformC64: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
@ -55,25 +56,36 @@ class DivPlatformC64: public DivDispatch {
Channel chan[3];
DivDispatchOscBuffer* oscBuf[3];
bool isMuted[3];
float fakeLow[3];
float fakeBand[3];
float fakeCutTable[2048];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,128> writes;
unsigned char filtControl, filtRes, vol;
unsigned char writeOscBuf;
unsigned char sidCore;
int filtCut, resetTime;
bool isFP;
SID sid;
reSIDfp::SID sid_fp;
bool keyPriority, sidIs6581, needInitTables;
unsigned char chanOrder[3];
unsigned char testAD, testSR;
SID* sid;
reSIDfp::SID* sid_fp;
struct SID_chip* sid_d;
unsigned char regPool[32];
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
inline short runFakeFilter(unsigned char ch, int in);
void acquire_classic(short* bufL, short* bufR, size_t start, size_t len);
void acquire_fp(short* bufL, short* bufR, size_t start, size_t len);
@ -101,7 +113,7 @@ class DivPlatformC64: public DivDispatch {
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void setChipModel(bool is6581);
void setFP(bool fp);
void setCore(unsigned char which);
void quit();
~DivPlatformC64();
};

View file

@ -32,7 +32,7 @@ void DivPlatformDummy::acquire(short** buf, size_t len) {
if (chan[j].active) {
if (!isMuted[j]) {
chanOut=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12;
oscBuf[j]->data[oscBuf[j]->needle++]=chanOut;
oscBuf[j]->data[oscBuf[j]->needle++]=chanOut<<1;
out+=chanOut;
} else {
oscBuf[j]->data[oscBuf[j]->needle++]=0;

View file

@ -26,9 +26,8 @@
#define PITCH_OFFSET ((double)(16*2048*(chanMax+1)))
#define NOTE_ES5506(c,note) (parent->calcBaseFreq(chipClock,chan[c].pcm.freqOffs,note,false))
#define rWrite(a,...) {if(!skipRegisterWrites) {hostIntf32.emplace(4,(a),__VA_ARGS__); }}
//#define rRead(a,st,...) {hostIntf32.emplace(st,4,(a),__VA_ARGS__);}
#define immWrite(a,...) {hostIntf32.emplace(4,(a),__VA_ARGS__);}
#define rWrite(a,...) {if(!skipRegisterWrites) {hostIntf32.push_back(QueuedHostIntf(4,(a),__VA_ARGS__)); }}
#define immWrite(a,...) {hostIntf32.push_back(QueuedHostIntf(4,(a),__VA_ARGS__));}
#define pageWrite(p,a,...) \
if (!skipRegisterWrites) { \
if (curPage!=(p)) { \
@ -118,15 +117,15 @@ void DivPlatformES5506::acquire(short** buf, size_t len) {
while (!hostIntf32.empty()) {
QueuedHostIntf w=hostIntf32.front();
if (w.isRead && (w.read!=NULL)) {
hostIntf8.emplace(w.state,0,w.addr,w.read,w.mask);
hostIntf8.emplace(w.state,1,w.addr,w.read,w.mask);
hostIntf8.emplace(w.state,2,w.addr,w.read,w.mask);
hostIntf8.emplace(w.state,3,w.addr,w.read,w.mask,w.delay);
hostIntf8.push(QueuedHostIntf(w.state,0,w.addr,w.read,w.mask));
hostIntf8.push(QueuedHostIntf(w.state,1,w.addr,w.read,w.mask));
hostIntf8.push(QueuedHostIntf(w.state,2,w.addr,w.read,w.mask));
hostIntf8.push(QueuedHostIntf(w.state,3,w.addr,w.read,w.mask,w.delay));
} else {
hostIntf8.emplace(0,w.addr,w.val,w.mask);
hostIntf8.emplace(1,w.addr,w.val,w.mask);
hostIntf8.emplace(2,w.addr,w.val,w.mask);
hostIntf8.emplace(3,w.addr,w.val,w.mask,w.delay);
hostIntf8.push(QueuedHostIntf(0,w.addr,w.val,w.mask));
hostIntf8.push(QueuedHostIntf(1,w.addr,w.val,w.mask));
hostIntf8.push(QueuedHostIntf(2,w.addr,w.val,w.mask));
hostIntf8.push(QueuedHostIntf(3,w.addr,w.val,w.mask,w.delay));
}
hostIntf32.pop();
}
@ -168,7 +167,7 @@ void DivPlatformES5506::acquire(short** buf, size_t len) {
buf[(o<<1)|1][h]=es5506.rout(o);
}
for (int i=chanMax; i>=0; i--) {
oscBuf[i]->data[oscBuf[i]->needle++]=(es5506.voice_lout(i)+es5506.voice_rout(i))>>5;
oscBuf[i]->data[oscBuf[i]->needle++]=(es5506.voice_lout(i)+es5506.voice_rout(i))>>6;
}
}
}
@ -1095,8 +1094,6 @@ DivMacroInt* DivPlatformES5506::getChanMacroInt(int ch) {
void DivPlatformES5506::reset() {
while (!hostIntf32.empty()) hostIntf32.pop();
while (!hostIntf8.empty()) hostIntf8.pop();
while (!queuedRead.empty()) queuedRead.pop();
while (!queuedReadState.empty()) queuedReadState.pop();
for (int i=0; i<32; i++) {
chan[i]=DivPlatformES5506::Channel();
chan[i].std.setEngine(parent);

View file

@ -22,7 +22,7 @@
#include "../dispatch.h"
#include "../engine.h"
#include <queue>
#include "../fixedQueue.h"
#include "../macroInt.h"
#include "../sample.h"
#include "vgsound_emu/src/es550x/es5506.hpp"
@ -238,6 +238,15 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
unsigned int* read;
unsigned short delay;
bool isRead;
QueuedHostIntf():
state(0),
step(0),
addr(0),
val(0),
mask(0),
read(NULL),
delay(0),
isRead(false) {}
QueuedHostIntf(unsigned char s, unsigned char a, unsigned int v, unsigned int m=(unsigned int)(~0), unsigned short d=0):
state(0),
step(s),
@ -257,17 +266,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
delay(d),
isRead(true) {}
};
struct QueuedReadState {
unsigned int* read;
unsigned char state;
QueuedReadState(unsigned int* r, unsigned char s):
read(r),
state(s) {}
};
std::queue<QueuedHostIntf> hostIntf32;
std::queue<QueuedHostIntf> hostIntf8;
std::queue<unsigned char> queuedRead;
std::queue<QueuedReadState> queuedReadState;
FixedQueue<QueuedHostIntf,2048> hostIntf32;
FixedQueue<QueuedHostIntf,2048> hostIntf8;
int cycle, curPage, volScale;
unsigned char maskedVal;
unsigned int irqv;

View file

@ -64,7 +64,7 @@ void DivPlatformFDS::acquire_puNES(short* buf, size_t len) {
buf[i]=sample;
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf->data[oscBuf->needle++]=sample<<1;
oscBuf->data[oscBuf->needle++]=sample*3;
}
}
}
@ -80,7 +80,7 @@ void DivPlatformFDS::acquire_NSFPlay(short* buf, size_t len) {
buf[i]=sample;
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf->data[oscBuf->needle++]=sample<<1;
oscBuf->data[oscBuf->needle++]=sample*3;
}
}
}

View file

@ -130,14 +130,15 @@ class DivPlatformOPN: public DivPlatformFMBase {
unsigned char freqH, freqL;
int portaPauseFreq;
signed char konCycles;
bool mask;
bool mask, hardReset;
OPNOpChannel():
SharedChannel<int>(0),
freqH(0),
freqL(0),
portaPauseFreq(0),
konCycles(0),
mask(true) {}
mask(true),
hardReset(false) {}
};
struct OPNOpChannelStereo: public OPNOpChannel {

View file

@ -22,7 +22,7 @@
#include "../dispatch.h"
#include "../instrument.h"
#include <deque>
#include "../fixedQueue.h"
#define KVS(x,y) ((chan[x].state.op[y].kvs==2 && isOutput[chan[x].state.alg][y]) || chan[x].state.op[y].kvs==1)
@ -79,9 +79,10 @@ class DivPlatformFMBase: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::deque<QueuedWrite> writes;
FixedQueue<QueuedWrite,2048> writes;
unsigned char lastBusy;
int delay;

View file

@ -22,7 +22,7 @@
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if(!skipRegisterWrites) {writes.emplace(a,v); if(dumpWrites) addWrite(a,v);}}
#define rWrite(a,v) {if(!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 64
@ -68,14 +68,19 @@ void DivPlatformGA20::acquire(short** buf, size_t len) {
ga20.write(w.addr,w.val);
regPool[w.addr]=w.val;
writes.pop();
delay=w.delay;
delay=1;
}
}
short *buffer[4] = {&ga20Buf[0][h],&ga20Buf[1][h],&ga20Buf[2][h],&ga20Buf[3][h]};
ga20.sound_stream_update(buffer, 1);
short *buffer[4]={
&ga20Buf[0][h],
&ga20Buf[1][h],
&ga20Buf[2][h],
&ga20Buf[3][h]
};
ga20.sound_stream_update(buffer,1);
buf[0][h]=(signed int)(ga20Buf[0][h]+ga20Buf[1][h]+ga20Buf[2][h]+ga20Buf[3][h])>>2;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h];
oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h]>>1;
}
}
}
@ -356,9 +361,7 @@ DivDispatchOscBuffer* DivPlatformGA20::getOscBuffer(int ch) {
}
void DivPlatformGA20::reset() {
while (!writes.empty()) {
writes.pop();
}
writes.clear();
memset(regPool,0,32);
ga20.device_reset();
delay=0;

View file

@ -21,7 +21,7 @@
#define _GA20_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../macroInt.h"
#include "sound/ga20/iremga20.h"
@ -47,15 +47,14 @@ class DivPlatformGA20: public DivDispatch, public iremga20_intf {
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
struct QueuedWrite {
unsigned short addr;
unsigned char addr;
unsigned char val;
unsigned short delay;
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=1):
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v):
addr(a),
val(v),
delay(d) {}
val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
unsigned int sampleOffGA20[256];
bool sampleLoaded[256];

View file

@ -19,10 +19,11 @@
#include "gb.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define immWrite(a,v) {writes.emplace(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define immWrite(a,v) {writes.push(QueuedWrite(a,v)); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 16
@ -80,6 +81,7 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
}
void DivPlatformGB::updateWave() {
logV("WAVE UPDATE");
rWrite(0x1a,0);
for (int i=0; i<16; i++) {
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];
@ -299,6 +301,7 @@ void DivPlatformGB::tick(bool sysTick) {
}
if (chan[i].keyOn) {
if (i==2) { // wave
rWrite(16+i*5,0x00);
rWrite(16+i*5,0x80);
rWrite(16+i*5+2,gbVolMap[chan[i].outVol]);
} else {
@ -319,7 +322,7 @@ void DivPlatformGB::tick(bool sysTick) {
rWrite(16+i*5+4,((chan[i].keyOn||chan[i].keyOff)?0x80:0x00)|((chan[i].soundLen<64)<<6));
} else {
rWrite(16+i*5+3,(2048-chan[i].freq)&0xff);
rWrite(16+i*5+4,(((2048-chan[i].freq)>>8)&7)|((chan[i].keyOn||chan[i].keyOff)?0x80:0x00)|((chan[i].soundLen<63)<<6));
rWrite(16+i*5+4,(((2048-chan[i].freq)>>8)&7)|((chan[i].keyOn||(chan[i].keyOff && i!=2))?0x80:0x00)|((chan[i].soundLen<63)<<6));
}
if (enoughAlready) { // more compat garbage
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(chan[i].soundLen&63)));
@ -394,6 +397,14 @@ int DivPlatformGB::dispatch(DivCommand c) {
chan[c.chan].vol=chan[c.chan].envVol;
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) {
chan[c.chan].outVol=chan[c.chan].vol;
chan[c.chan].envVol=chan[c.chan].outVol;
}
chan[c.chan].envLen=0;
chan[c.chan].envDir=1;
chan[c.chan].soundLen=64;
}
if (c.chan==2 && chan[c.chan].softEnv) {
chan[c.chan].soundLen=64;
@ -463,7 +474,9 @@ int DivPlatformGB::dispatch(DivCommand c) {
if (c.chan!=2) break;
chan[c.chan].wave=c.value;
ws.changeWave1(chan[c.chan].wave);
chan[c.chan].keyOn=true;
if (chan[c.chan].active) {
chan[c.chan].keyOn=true;
}
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_PERIODIC(c.value2);
@ -664,22 +677,27 @@ void DivPlatformGB::setFlags(const DivConfig& flags) {
}
invertWave=flags.getBool("invertWave",true);
enoughAlready=flags.getBool("enoughAlready",false);
}
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
chipClock=4194304;
CHECK_CUSTOM_CLOCK;
rate=chipClock/16;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
oscBuf[i]->rate=rate;
}
}
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
model=GB_MODEL_DMG_B;
gb=new GB_gameboy_t;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
reset();
return 4;

View file

@ -23,7 +23,7 @@
#include "../dispatch.h"
#include "../waveSynth.h"
#include "sound/gb/gb.h"
#include <queue>
#include "../fixedQueue.h"
class DivPlatformGB: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
@ -62,11 +62,12 @@ class DivPlatformGB: public DivDispatch {
unsigned char lastPan;
DivWaveSynth ws;
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
int antiClickPeriodCount, antiClickWavePos;

View file

@ -63,7 +63,7 @@ void DivPlatformGenesis::processDAC(int iRate) {
for (int i=5; i<7; i++) {
if (chan[i].dacSample!=-1) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0) {
if (!isMuted[i] && s->samples>0 && chan[i].dacPos<s->samples) {
if (parent->song.noOPN2Vol) {
chan[i].dacOutput=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
@ -110,7 +110,7 @@ void DivPlatformGenesis::processDAC(int iRate) {
chan[5].dacPeriod+=chan[5].dacRate;
if (chan[5].dacPeriod>=iRate) {
DivSample* s=parent->getSample(chan[5].dacSample);
if (s->samples>0) {
if (s->samples>0 && chan[5].dacPos<s->samples) {
if (!isMuted[5]) {
if (chan[5].dacReady && writes.size()<16) {
int sample;
@ -184,16 +184,18 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
if (i==5) {
if (fm.dacen) {
if (softPCM) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7;
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7;
oscBuf[i]->data[oscBuf[i]->needle++]=((fm.dacdata^0x100)-0x100)<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=0;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<(chipType==2?0:7);
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?1:6),-32768,32767);
oscBuf[6]->data[oscBuf[6]->needle++]=0;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<(chipType==2?0:7);
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fm.ch_out[i]<<(chipType==2?1:6),-32768,32767);
}
}
@ -241,19 +243,21 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
//OPN2_Write(&fm,0,0);
for (int i=0; i<6; i++) {
int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<5;
if (chOut<-32768) chOut=-32768;
if (chOut>32767) chOut=32767;
if (i==5) {
if (fm_ymfm->debug_dac_enable()) {
if (softPCM) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7;
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7;
oscBuf[i]->data[oscBuf[i]->needle++]=((fm_ymfm->debug_dac_data()^0x100)-0x100)<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=0;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=chOut;
oscBuf[6]->data[oscBuf[6]->needle++]=0;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=chOut;
@ -280,7 +284,7 @@ void DivPlatformGenesis::acquire(short** buf, size_t len) {
}
void DivPlatformGenesis::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
while (!writes.empty()) writes.pop_front();
writes.clear();
for (size_t i=0; i<len; i++) {
processDAC(sRate);
@ -589,6 +593,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>6) return;
if (ch<6) {
if (ch==5) immWrite(0x2a,0x80);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
@ -696,7 +701,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
addWrite(0xffff0003,chan[c.chan].dacDirection);
}
}
chan[c.chan].dacPos=0;
if (chan[c.chan].setPos) {
chan[c.chan].setPos=false;
} else {
chan[c.chan].dacPos=0;
}
chan[c.chan].dacPeriod=0;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
@ -919,6 +928,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (dumpWrites) addWrite(0xffff0003,chan[c.chan].dacDirection);
break;
}
case DIV_CMD_SAMPLE_POS:
if (c.chan<5) c.chan=5;
chan[c.chan].dacPos=c.value;
chan[c.chan].setPos=true;
if (dumpWrites) addWrite(0xffff0005,chan[c.chan].dacPos);
break;
case DIV_CMD_LEGATO: {
if (c.chan==csmChan) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
@ -1160,6 +1175,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
return 1;
}
#define DRSLD2R(x) \
if (chan[i].state.op[x].dr<dr) dr=chan[i].state.op[x].dr; \
if (chan[i].state.op[x].sl<sl) sl=chan[i].state.op[x].sl; \
if (chan[i].state.op[x].d2r<d2r) d2r=chan[i].state.op[x].d2r;
void DivPlatformGenesis::forceIns() {
for (int i=0; i<6; i++) {
for (int j=0; j<4; j++) {
@ -1184,7 +1204,29 @@ void DivPlatformGenesis::forceIns() {
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
if (chan[i].active) {
if (i<5 || !chan[i].dacMode) {
bool sustained=false;
unsigned char dr=chan[i].state.op[3].dr;
unsigned char sl=chan[i].state.op[3].sl;
unsigned char d2r=chan[i].state.op[3].d2r;
switch (chan[i].state.alg&7) {
case 4:
DRSLD2R(2);
break;
case 5:
case 6:
DRSLD2R(2);
DRSLD2R(1);
break;
case 7:
DRSLD2R(2);
DRSLD2R(1);
DRSLD2R(3);
break;
}
if (dr<2 || (sl<15 && d2r<2)) sustained=true;
if ((i<5 || !chan[i].dacMode) && sustained) {
chan[i].keyOn=true;
chan[i].freqChanged=true;
}
@ -1240,7 +1282,7 @@ float DivPlatformGenesis::getPostAmp() {
}
void DivPlatformGenesis::reset() {
while (!writes.empty()) writes.pop_front();
writes.clear();
memset(regPool,0,512);
if (useYMFM) {
fm_ymfm->reset();

View file

@ -57,6 +57,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
int dacDelay;
bool dacReady;
bool dacDirection;
bool setPos;
unsigned char sampleBank;
signed char dacOutput;
Channel():
@ -70,6 +71,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
dacDelay(0),
dacReady(true),
dacDirection(false),
setPos(false),
sampleBank(0),
dacOutput(0) {}
};

View file

@ -396,6 +396,9 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_FM_HARD_RESET:
opChan[ch].hardReset=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 127;
break;
@ -449,6 +452,9 @@ static int opChanOffsH[4]={
};
void DivPlatformGenesisExt::tick(bool sysTick) {
int hardResetElapsed=0;
bool mustHardReset=false;
if (extMode) {
bool writeSomething=false;
unsigned char writeMask=2;
@ -459,6 +465,12 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
writeMask&=~(1<<(4+i));
opChan[i].keyOff=false;
}
if (opChan[i].hardReset && opChan[i].keyOn) {
mustHardReset=true;
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
immWrite(baseAddr+ADDR_SL_RR,0x0f);
hardResetElapsed++;
}
}
if (writeSomething) {
if (chan[csmChan].active) { // CSM
@ -627,6 +639,22 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
(writeMask&0x80)?'4':'-'
);*/
immWrite(0x28,writeMask);
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0xf0,i&0xff);
}
for (int i=0; i<4; i++) {
if (opChan[i].keyOn && opChan[i].hardReset) {
// restore SL/RR
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
}
immWrite(0x28,writeMask);
}
}
if (extMode) {

View file

@ -22,7 +22,7 @@
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if(!skipRegisterWrites) {writes.emplace(a,v); if(dumpWrites) addWrite(a,v);}}
#define rWrite(a,v) {if(!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 64
@ -79,14 +79,14 @@ void DivPlatformK007232::acquire(short** buf, size_t len) {
buf[0][h]=(lout[0]+lout[1])<<4;
buf[1][h]=(rout[0]+rout[1])<<4;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(lout[i]+rout[i])<<4;
oscBuf[i]->data[oscBuf[i]->needle++]=(lout[i]+rout[i])<<3;
}
} else {
const unsigned char vol=regPool[0xc];
const signed int out[2]={(k007232.output(0)*(vol&0xf)),(k007232.output(1)*((vol>>4)&0xf))};
buf[0][h]=(out[0]+out[1])<<4;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=out[i]<<5;
oscBuf[i]->data[oscBuf[i]->needle++]=out[i]<<4;
}
}
}

View file

@ -21,7 +21,7 @@
#define _K007232_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../macroInt.h"
#include "vgsound_emu/src/k007232/k007232.hpp"
@ -57,12 +57,13 @@ class DivPlatformK007232: public DivDispatch, public k007232_intf {
unsigned short addr;
unsigned char val;
unsigned short delay;
QueuedWrite(): addr(0), val(0), delay(1) {}
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=1):
addr(a),
val(v),
delay(d) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
unsigned int sampleOffK007232[256];
bool sampleLoaded[256];

View file

@ -0,0 +1,513 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "k053260.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if(!skipRegisterWrites) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 16
#define TICK_DIVIDER 64 // for match to YM3012 output rate
const char* regCheatSheetK053260[]={
"MainToSub0", "00",
"MainToSub1", "01",
"SubToMain0", "02",
"SubToMain1", "03",
"CHx_FreqL", "08+x*8",
"CHx_FreqH", "09+x*8",
"CHx_LengthL", "0A+x*8",
"CHx_LengthH", "0B+x*8",
"CHx_StartL", "0C+x*8",
"CHx_StartM", "0D+x*8",
"CHx_StartH", "0E+x*8",
"CHx_Volume", "0F+x*8",
"KeyOn", "28",
"Status", "29",
"LoopFormat", "2A",
"Test", "2B",
"CH01_Pan", "2C",
"CH23_Pan", "2D",
"ROMReadback", "2E",
"Control", "2F",
NULL
};
const char** DivPlatformK053260::getRegisterSheet() {
return regCheatSheetK053260;
}
inline void DivPlatformK053260::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
if (!skipRegisterWrites) {
rWrite(8+((ch<<3)|(addr&7)),val);
}
}
u8 DivPlatformK053260::read_sample(u32 address) {
if ((sampleMem!=NULL) && (address<getSampleMemCapacity())) {
return sampleMem[address&0x1fffff];
}
return 0;
}
void DivPlatformK053260::acquire(short** buf, size_t len) {
for (size_t i=0; i<len; i++) {
k053260.tick(TICK_DIVIDER);
int lout=(k053260.output(0)); // scale to 16 bit
int rout=(k053260.output(1)); // scale to 16 bit
if (lout>32767) lout=32767;
if (lout<-32768) lout=-32768;
if (rout>32767) rout=32767;
if (rout<-32768) rout=-32768;
buf[0][i]=lout;
buf[1][i]=rout;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(k053260.voice_out(i,0)+k053260.voice_out(i,1))>>2;
}
}
}
void DivPlatformK053260::tick(bool sysTick) {
unsigned char panMask=0;
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&0x7f)*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
chWrite(i,7,chan[i].outVol);
}
if (NEW_ARP_STRAT) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
}
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) { // panning
chan[i].panning=4+chan[i].std.panL.val;
if (!isMuted[i]) {
panMask|=1<<i;
}
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].audPos=0;
chan[i].setPos=true;
}
}
if (chan[i].setPos) {
// force keyon
chan[i].keyOn=true;
chan[i].setPos=false;
} else {
chan[i].audPos=0;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
unsigned char keyon=regPool[0x28]|(1<<i);
unsigned char keyoff=keyon&~(17<<i);
unsigned char loopon=regPool[0x2a]|(1<<i);
unsigned char loopoff=loopon&~(1<<i);
double off=1.0;
int sample=chan[i].sample;
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=8363.0/s->centerRate;
}
}
DivSample* s=parent->getSample(chan[i].sample);
chan[i].freq=0x1000-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER));
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {
unsigned int start=0;
unsigned int length=0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
start=sampleOffK053260[chan[i].sample];
length=s->length8;
if (chan[i].reverse) {
start+=length;
keyon|=(16<<i);
}
}
if (chan[i].audPos>0) {
if (chan[i].reverse) {
start=start-MIN(chan[i].audPos,s->length8);
}
else {
start=start+MIN(chan[i].audPos,s->length8);
}
length=MAX(1,length-chan[i].audPos);
}
start=MIN(start,getSampleMemCapacity());
length=MIN(65535,MIN(length,getSampleMemCapacity()));
rWrite(0x28,keyoff); // force keyoff first
rWrite(0x2a,loopoff);
chWrite(i,2,length&0xff);
chWrite(i,3,length>>8);
chWrite(i,4,start&0xff);
chWrite(i,5,start>>8);
chWrite(i,6,start>>16);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
chWrite(i,7,chan[i].outVol);
}
rWrite(0x28,keyon);
if (s->isLoopable()) {
rWrite(0x2a,loopon);
}
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
rWrite(0x28,keyoff);
rWrite(0x2a,loopoff);
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
chWrite(i,0,chan[i].freq&0xff);
chWrite(i,1,chan[i].freq>>8);
chan[i].freqChanged=false;
}
}
}
if (panMask) {
updatePanning(panMask);
}
}
void DivPlatformK053260::updatePanning(unsigned char mask) {
if (mask&3) {
rWrite(0x2c,
(isMuted[0]?0:chan[0].panning)|
(isMuted[1]?0:chan[1].panning<<3));
}
if (mask&0xc) {
rWrite(0x2d,
(isMuted[2]?0:chan[2].panning)|
(isMuted[3]?0:chan[3].panning<<3));
}
}
int DivPlatformK053260::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1;
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
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) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
}
case DIV_CMD_NOTE_OFF:
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[c.chan].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].ins=c.value;
}
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
chWrite(c.chan,7,chan[c.chan].outVol);
}
}
break;
case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.vol.has) {
return chan[c.chan].vol;
}
return chan[c.chan].outVol;
break;
case DIV_CMD_PANNING:
chan[c.chan].panning=MIN(parent->convertPanSplitToLinearLR(c.value,c.value2,7)+1,7);
if (!isMuted[c.chan]) {
updatePanning(1<<c.chan);
}
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
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[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_LEGATO: {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
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_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);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
case DIV_CMD_SAMPLE_DIR: {
if (chan[c.chan].reverse!=(bool)(c.value&1)) {
chan[c.chan].reverse=c.value&1;
}
break;
}
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformK053260::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
updatePanning(1<<ch);
}
void DivPlatformK053260::forceIns() {
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
chWrite(i,1,isMuted[i]?0:chan[i].panning);
}
}
void* DivPlatformK053260::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformK053260::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformK053260::getOscBuffer(int ch) {
return oscBuf[ch];
}
void DivPlatformK053260::reset() {
memset(regPool,0,64);
k053260.reset();
rWrite(0x28,0); // keyoff all channels
for (int i=0; i<4; i++) {
chan[i]=DivPlatformK053260::Channel();
chan[i].std.setEngine(parent);
}
updatePanning(0xf);
rWrite(0x2f,2); // sound enable
}
int DivPlatformK053260::getOutputCount() {
return 2;
}
void DivPlatformK053260::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformK053260::notifyWaveChange(int wave) {
// TODO when wavetables are added
// TODO they probably won't be added unless the samples reside in RAM
}
void DivPlatformK053260::notifyInsDeletion(void* ins) {
for (int i=0; i<4; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformK053260::setFlags(const DivConfig& flags) {
switch (flags.getInt("clockSel",0)) {
case 1: chipClock=4000000; break;
default: chipClock=COLOR_NTSC; break;
}
CHECK_CUSTOM_CLOCK;
rate=chipClock/TICK_DIVIDER;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformK053260::poke(unsigned int addr, unsigned short val) {
rWrite(addr&0x3f,val);
}
void DivPlatformK053260::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr&0x3f,i.val);
}
unsigned char* DivPlatformK053260::getRegisterPool() {
regPool[0x29]=k053260.read(0x29); // dynamically updated
return regPool;
}
int DivPlatformK053260::getRegisterPoolSize() {
return 64;
}
const void* DivPlatformK053260::getSampleMem(int index) {
return index == 0 ? sampleMem : NULL;
}
size_t DivPlatformK053260::getSampleMemCapacity(int index) {
return index == 0 ? 2097152 : 0;
}
size_t DivPlatformK053260::getSampleMemUsage(int index) {
return index == 0 ? sampleMemLen : 0;
}
bool DivPlatformK053260::isSampleLoaded(int index, int sample) {
if (index!=0) return false;
if (sample<0 || sample>255) return false;
return sampleLoaded[sample];
}
void DivPlatformK053260::renderSamples(int sysID) {
memset(sampleMem,0,getSampleMemCapacity());
memset(sampleOffK053260,0,256*sizeof(unsigned int));
memset(sampleLoaded,0,256*sizeof(bool));
size_t memPos=1; // for avoid silence
for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i];
if (!s->renderOn[0][sysID]) {
sampleOffK053260[i]=0;
continue;
}
int length=MIN(65535,s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT));
int actualLength=MIN((int)(getSampleMemCapacity()-memPos-1),length);
if (actualLength>0) {
sampleOffK053260[i]=memPos-1;
for (int j=0; j<actualLength; j++) {
sampleMem[memPos++]=s->data8[j];
}
sampleMem[memPos++]=0; // Silence for avoid popping noise
}
if (actualLength<length) {
logW("out of K053260 PCM memory for sample %d!",i);
break;
}
sampleLoaded[i]=true;
}
sampleMemLen=memPos;
}
int DivPlatformK053260::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
sampleMem=new unsigned char[getSampleMemCapacity()];
sampleMemLen=0;
setFlags(flags);
reset();
return 4;
}
void DivPlatformK053260::quit() {
delete[] sampleMem;
for (int i=0; i<4; i++) {
delete oscBuf[i];
}
}

View file

@ -0,0 +1,97 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 _K053260_H
#define _K053260_H
#include "../dispatch.h"
#include <queue>
#include "vgsound_emu/src/k053260/k053260.hpp"
class DivPlatformK053260: public DivDispatch, public k053260_intf {
struct Channel: public SharedChannel<int> {
unsigned int audPos;
int sample, wave;
int panning;
bool setPos, reverse;
int macroVolMul;
Channel():
SharedChannel<int>(127),
audPos(0),
sample(-1),
wave(-1),
panning(4),
setPos(false),
reverse(false),
macroVolMul(64) {}
};
Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
int chipType;
unsigned char curChan;
unsigned int sampleOffK053260[256];
bool sampleLoaded[256];
unsigned char* sampleMem;
size_t sampleMemLen;
k053260_core k053260;
unsigned char regPool[64];
void updatePanning(unsigned char mask);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
public:
virtual u8 read_sample(u32 address) override;
virtual void acquire(short** buf, size_t len) override;
virtual int dispatch(DivCommand c) override;
virtual void* getChanState(int chan) override;
virtual DivMacroInt* getChanMacroInt(int ch) override;
virtual DivDispatchOscBuffer* getOscBuffer(int chan) override;
virtual unsigned char* getRegisterPool() override;
virtual int getRegisterPoolSize() override;
virtual void reset() override;
virtual void forceIns() override;
virtual void tick(bool sysTick=true) override;
virtual void muteChannel(int ch, bool mute) override;
virtual int getOutputCount() override;
virtual void notifyInsChange(int ins) override;
virtual void notifyWaveChange(int wave) override;
virtual void notifyInsDeletion(void* ins) override;
virtual void setFlags(const DivConfig& flags) override;
virtual void poke(unsigned int addr, unsigned short val) override;
virtual void poke(std::vector<DivRegWrite>& wlist) override;
virtual const char** getRegisterSheet() override;
virtual const void* getSampleMem(int index = 0) override;
virtual size_t getSampleMemCapacity(int index = 0) override;
virtual size_t getSampleMemUsage(int index = 0) override;
virtual bool isSampleLoaded(int index, int sample) override;
virtual void renderSamples(int chipID) override;
virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override;
virtual void quit() override;
DivPlatformK053260():
DivDispatch(),
k053260_intf(),
k053260(*this) {}
private:
void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
};
#endif

View file

@ -85,9 +85,9 @@ void DivPlatformMMC5::acquire(short** buf, size_t len) {
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output*10)<<7);
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output*10)<<7);
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output*2)<<6);
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output)<<11);
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output)<<11);
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output)<<7);
}
}
}

View file

@ -23,7 +23,7 @@
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define NOTE_LINEAR(x) ((x)<<7)
@ -60,7 +60,7 @@ void DivPlatformMSM5232::acquire(short** buf, size_t len) {
((regPool[12+(i>>4)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+
((regPool[12+(i>>4)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+
((regPool[12+(i>>4)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0)
)<<3;
)<<2;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(o,-32768,32767);
}

View file

@ -21,7 +21,7 @@
#define _MSM5232_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "sound/oki/msm5232.h"
class DivPlatformMSM5232: public DivDispatch {
@ -46,11 +46,12 @@ class DivPlatformMSM5232: public DivDispatch {
unsigned char groupAR[2];
unsigned char groupDR[2];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
int cycles, curChan, delay, detune, clockDriftAccum;
unsigned int clockDriftLFOPos, clockDriftLFOSpeed;

View file

@ -24,7 +24,7 @@
#include <string.h>
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
const char** DivPlatformMSM6258::getRegisterSheet() {
return NULL;
@ -84,7 +84,7 @@ void DivPlatformMSM6258::acquire(short** buf, size_t len) {
} else {
buf[0][h]=(msmPan&2)?msmOut:0;
buf[1][h]=(msmPan&1)?msmOut:0;
oscBuf[0]->data[oscBuf[0]->needle++]=msmPan?msmOut:0;
oscBuf[0]->data[oscBuf[0]->needle++]=msmPan?(msmOut>>1):0;
}
}
}

View file

@ -21,7 +21,7 @@
#define _MSM6258_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "sound/oki/okim6258.h"
class DivPlatformMSM6258: public DivDispatch {
@ -42,9 +42,10 @@ class DivPlatformMSM6258: public DivDispatch {
struct QueuedWrite {
unsigned short addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
okim6258_device* msm;
unsigned char lastBusy;

View file

@ -23,8 +23,8 @@
#include <string.h>
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.emplace(a,v,d); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,d)); if (dumpWrites) {addWrite(a,v);} }
const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL;
@ -79,9 +79,8 @@ void DivPlatformMSM6295::acquire(short** buf, size_t len) {
if (++updateOsc>=22) {
updateOsc=0;
// TODO: per-channel osc
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=msm.voice_out(i)<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=msm.voice_out(i)<<5;
}
}
}

View file

@ -21,7 +21,7 @@
#define _MSM6295_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "vgsound_emu/src/msm6295/msm6295.hpp"
class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
@ -41,12 +41,13 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
unsigned short addr;
unsigned char val;
unsigned short delay;
QueuedWrite(): addr(0), val(0), delay(96) {}
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=96):
addr(a),
val(v),
delay(d) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
msm6295_core msm;
unsigned char lastBusy;

View file

@ -22,9 +22,8 @@
#include "../../ta-log.h"
#include <math.h>
#define rRead(a,v) n163.addr_w(a); n163.data_r(v);
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWriteMask(a,v,m) if (!skipRegisterWrites) {writes.emplace(a,v,m); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define rWriteMask(a,v,m) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,m)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) \
if (c<=chanMax) { \
rWrite(0x78-(c<<3)+(a&7),v) \
@ -147,7 +146,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) {
} else {
// load from custom
DivWavetable* wt=parent->getWave(wave);
for (int i=0; i<len; i++) {
for (int i=0; i<wt->len; i++) {
unsigned char addr=(pos+i); // address (nibble each)
if (addr>=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area
break;
@ -156,7 +155,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) {
if (wt->max<1 || wt->len<1) {
rWriteMask(addr>>1,0,mask);
} else {
int data=wt->data[i*wt->len/len]*15/wt->max;
int data=wt->data[i]*15/wt->max;
if (data<0) data=0;
if (data>15) data=15;
rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask);
@ -167,7 +166,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) {
void DivPlatformN163::updateWaveCh(int ch) {
if (ch<=chanMax) {
logV("updateWave with pos %d and len %d",chan[ch].wavePos,chan[ch].waveLen);
//logV("updateWave with pos %d and len %d",chan[ch].wavePos,chan[ch].waveLen);
updateWave(ch,-1,chan[ch].wavePos,chan[ch].waveLen);
if (chan[ch].active && !isMuted[ch]) {
chan[ch].volumeChanged=true;
@ -198,19 +197,16 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].std.duty.had) {
if (chan[i].wavePos!=chan[i].std.duty.val) {
chan[i].wavePos=chan[i].std.duty.val;
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
}
if (chan[i].curWavePos!=chan[i].std.duty.val) {
chan[i].curWavePos=chan[i].std.duty.val;
chan[i].waveChanged=true;
}
}
if (chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val) {
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].waveMode&0x2) {
if (chan[i].waveMode) {
chan[i].waveUpdated=true;
}
}
@ -225,60 +221,11 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].std.ex1.had) {
if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].waveLen=chan[i].std.ex1.val&0xfc;
chan[i].ws.setWidth(chan[i].waveLen);
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
}
if (chan[i].curWaveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].curWaveLen=chan[i].std.ex1.val&0xfc;
chan[i].freqChanged=true;
}
}
if (chan[i].std.ex2.had) {
if ((chan[i].waveMode&0x2)!=(chan[i].std.ex2.val&0x2)) { // update when every waveform changed
chan[i].waveMode=(chan[i].waveMode&~0x2)|(chan[i].std.ex2.val&0x2);
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
chan[i].waveChanged=true;
}
}
if ((chan[i].waveMode&0x1)!=(chan[i].std.ex2.val&0x1)) { // update waveform now
chan[i].waveMode=(chan[i].waveMode&~0x1)|(chan[i].std.ex2.val&0x1);
if (chan[i].waveMode&0x1) { // rising edge
chan[i].waveUpdated=true;
chan[i].waveChanged=true;
}
}
}
if (chan[i].std.ex3.had) {
if (chan[i].loadWave!=chan[i].std.ex3.val) {
chan[i].loadWave=chan[i].std.ex3.val;
if (chan[i].loadMode&0x2) {
updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
}
}
}
if (chan[i].std.alg.had) {
if (chan[i].loadPos!=chan[i].std.alg.val) {
chan[i].loadPos=chan[i].std.alg.val;
}
}
if (chan[i].std.fb.had) {
if (chan[i].loadLen!=(chan[i].std.fb.val&0xfc)) {
chan[i].loadLen=chan[i].std.fb.val&0xfc;
}
}
if (chan[i].std.fms.had) {
if ((chan[i].loadMode&0x2)!=(chan[i].std.fms.val&0x2)) { // load when every waveform changes
chan[i].loadMode=(chan[i].loadMode&~0x2)|(chan[i].std.fms.val&0x2);
}
if ((chan[i].loadMode&0x1)!=(chan[i].std.fms.val&0x1)) { // load now
chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms.val&0x1);
if (chan[i].loadMode&0x1) { // rising edge
updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
}
}
}
if (chan[i].volumeChanged) {
if (chan[i].active && !isMuted[i]) {
chWriteMask(i,0x7,chan[i].resVol&0xf,0xf);
@ -288,7 +235,7 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].volumeChanged=false;
}
if (chan[i].waveChanged) {
chWrite(i,0x6,chan[i].wavePos);
chWrite(i,0x6,chan[i].curWavePos);
if (chan[i].active) {
chan[i].freqChanged=true;
}
@ -309,23 +256,17 @@ void DivPlatformN163::tick(bool sysTick) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
// TODO: what is this mess?
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);
chan[i].freq=(((chan[i].freq*chan[i].waveLen)*(chanMax+1))/16);
chan[i].freq=(((chan[i].freq*chan[i].curWaveLen)*(chanMax+1))/16);
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
if (chan[i].keyOn) {
if (chan[i].wave<0) {
chan[i].wave=0;
if (chan[i].waveMode&0x2) {
updateWaveCh(i);
}
}
}
if (chan[i].keyOff && !isMuted[i]) {
chWriteMask(i,0x7,0,0xf);
}
chWrite(i,0x0,chan[i].freq&0xff);
chWrite(i,0x2,chan[i].freq>>8);
chWrite(i,0x4,((256-chan[i].waveLen)&0xfc)|((chan[i].freq>>16)&3));
chWrite(i,0x4,((256-chan[i].curWaveLen)&0xfc)|((chan[i].freq>>16)&3));
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
@ -338,14 +279,21 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_N163);
if (chan[c.chan].insChanged) {
chan[c.chan].wave=ins->n163.wave;
chan[c.chan].wavePos=ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.waveLen;
if (ins->n163.wave>=0) {
chan[c.chan].wave=ins->n163.wave;
}
chan[c.chan].wavePos=ins->n163.perChanPos?ins->n163.wavePosCh[c.chan&7]:ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.perChanPos?ins->n163.waveLenCh[c.chan&7]:ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode;
chan[c.chan].ws.init(NULL,chan[c.chan].waveLen,15,false);
chan[c.chan].curWavePos=chan[c.chan].wavePos;
chan[c.chan].curWaveLen=chan[c.chan].waveLen;
chan[c.chan].ws.init(NULL,chan[c.chan].waveLen,15,true);
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
}
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
chan[c.chan].waveChanged=true;
if (chan[c.chan].waveMode&0x3 || ins->ws.enabled) {
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
}
@ -369,7 +317,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
//chan[c.chan].macroInit(NULL);
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].active=false;
@ -432,68 +380,40 @@ int DivPlatformN163::dispatch(DivCommand c) {
}
case DIV_CMD_WAVE:
chan[c.chan].wave=c.value;
if (chan[c.chan].waveMode&0x2) {
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
chan[c.chan].keyOn=true;
break;
case DIV_CMD_N163_WAVE_POSITION:
chan[c.chan].wavePos=c.value;
if (chan[c.chan].waveMode&0x2) {
chan[c.chan].waveUpdated=true;
}
chan[c.chan].curWavePos=c.value;
chan[c.chan].waveChanged=true;
break;
case DIV_CMD_N163_WAVE_LENGTH:
chan[c.chan].waveLen=c.value&0xfc;
if (chan[c.chan].waveMode&0x2) {
chan[c.chan].waveUpdated=true;
}
chan[c.chan].curWaveLen=c.value&0xfc;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_N163_WAVE_MODE:
chan[c.chan].waveMode=c.value&0x3;
if (chan[c.chan].waveMode&0x3) { // update now
chan[c.chan].waveUpdated=true;
chan[c.chan].waveChanged=true;
}
break;
case DIV_CMD_N163_WAVE_LOAD:
chan[c.chan].loadWave=c.value;
if (chan[c.chan].loadMode&0x2) { // load when every waveform changes
updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
}
break;
case DIV_CMD_N163_WAVE_LOADPOS:
chan[c.chan].loadPos=c.value;
chan[c.chan].wavePos=c.value;
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
break;
case DIV_CMD_N163_WAVE_LOADLEN:
chan[c.chan].loadLen=c.value&0xfc;
break;
case DIV_CMD_N163_WAVE_LOADMODE:
chan[c.chan].loadMode=c.value&0x3;
if (chan[c.chan].loadMode&0x1) { // load now
updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
chan[c.chan].waveLen=c.value&0xfc;
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOAD:
loadWave=c.value;
if (loadMode&0x2) { // load when every waveform changes
updateWave(c.chan,loadWave,loadPos,loadLen);
if (loadWave>=0 && loadWave<parent->song.waveLen) {
updateWave(-1,loadWave,loadPos,-1);
}
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS:
loadPos=c.value;
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADLEN:
loadLen=c.value&0xfc;
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE:
loadMode=c.value&0x3;
if (loadMode&0x3) { // load now
updateWave(c.chan,loadWave,loadPos,loadLen);
}
break;
case DIV_CMD_N163_CHANNEL_LIMIT:
if (chanMax!=(c.value&0x7)) {
chanMax=c.value&0x7;
@ -547,9 +467,6 @@ void DivPlatformN163::forceIns() {
chan[i].freqChanged=true;
chan[i].volumeChanged=true;
chan[i].waveChanged=true;
if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true;
}
}
}
}
@ -557,7 +474,7 @@ void DivPlatformN163::forceIns() {
void DivPlatformN163::notifyWaveChange(int wave) {
for (int i=0; i<8; i++) {
if (chan[i].wave==wave) {
if (chan[i].waveMode&0x2) {
if (chan[i].waveMode) {
chan[i].ws.changeWave1(wave);
chan[i].waveUpdated=true;
}
@ -619,8 +536,6 @@ void DivPlatformN163::reset() {
chanMax=initChanMax;
loadWave=-1;
loadPos=0;
loadLen=0;
loadMode=0;
rWrite(0x7f,initChanMax<<4);
}

View file

@ -21,7 +21,7 @@
#define _N163_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../waveSynth.h"
#include "vgsound_emu/src/n163/n163.hpp"
@ -29,9 +29,8 @@ class DivPlatformN163: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
signed char resVol;
short wave, wavePos, waveLen;
unsigned char waveMode;
short loadWave, loadPos, loadLen;
unsigned char loadMode;
short curWavePos, curWaveLen;
bool waveMode;
bool volumeChanged;
bool waveChanged, waveUpdated;
DivWaveSynth ws;
@ -41,11 +40,9 @@ class DivPlatformN163: public DivDispatch {
wave(-1),
wavePos(0),
waveLen(0),
curWavePos(0),
curWaveLen(0),
waveMode(0),
loadWave(-1),
loadPos(0),
loadLen(0),
loadMode(0),
volumeChanged(false),
waveChanged(false),
waveUpdated(false) {}
@ -54,16 +51,16 @@ class DivPlatformN163: public DivDispatch {
DivDispatchOscBuffer* oscBuf[8];
bool isMuted[8];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
unsigned char mask;
QueuedWrite(unsigned char a, unsigned char v, unsigned char m=~0): addr(a), val(v), mask(m) {}
unsigned char addr;
unsigned char val;
unsigned char mask;
QueuedWrite(): addr(0), val(0), mask(~0) {}
QueuedWrite(unsigned char a, unsigned char v, unsigned char m=~0): addr(a), val(v), mask(m) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,2048> writes;
unsigned char initChanMax;
unsigned char chanMax;
short loadWave, loadPos, loadLen;
unsigned char loadMode;
short loadWave, loadPos;
bool multiplex;
n163_core n163;

View file

@ -22,7 +22,7 @@
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_FREQBASE 4194304
@ -177,7 +177,7 @@ void DivPlatformNamcoWSG::acquire(short** buf, size_t len) {
};
namco->sound_stream_update(bufC,1);
for (int i=0; i<chans; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=namco->m_channel_list[i].last_out*chans;
oscBuf[i]->data[oscBuf[i]->needle++]=(namco->m_channel_list[i].last_out*chans)>>1;
}
}
}

View file

@ -21,7 +21,7 @@
#define _NAMCOWSG_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../waveSynth.h"
#include "sound/namco.h"
@ -41,11 +41,12 @@ class DivPlatformNamcoWSG: public DivDispatch {
DivDispatchOscBuffer* oscBuf[8];
bool isMuted[8];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
unsigned short addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
namco_audio_device* namco;
int devType, chans;

View file

@ -145,7 +145,7 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11;
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11;
oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<11;
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<11;
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<12;
oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8;
}
}
@ -346,7 +346,11 @@ void DivPlatformNES::tick(bool sysTick) {
rWrite(0x4012,(dpcmAddr>>6)&0xff);
rWrite(0x4013,dpcmLen&0xff);
rWrite(0x4015,31);
dpcmBank=dpcmAddr>>14;
if (dpcmBank!=(dpcmAddr>>14)) {
dpcmBank=dpcmAddr>>14;
logV("switching bank to %d",dpcmBank);
if (dumpWrites) addWrite(0xffff0004,dpcmBank);
}
}
} else {
if (nextDPCMFreq>=0) {
@ -425,7 +429,11 @@ int DivPlatformNES::dispatch(DivCommand c) {
rWrite(0x4012,(dpcmAddr>>6)&0xff);
rWrite(0x4013,dpcmLen&0xff);
rWrite(0x4015,31);
dpcmBank=dpcmAddr>>14;
if (dpcmBank!=(dpcmAddr>>14)) {
dpcmBank=dpcmAddr>>14;
logV("switching bank to %d",dpcmBank);
if (dumpWrites) addWrite(0xffff0004,dpcmBank);
}
}
}
break;
@ -854,6 +862,7 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, const DivConfi
dpcmMem=new unsigned char[262144];
dpcmMemLen=0;
dpcmBank=0;
if (dumpWrites) addWrite(0xffff0004,dpcmBank);
init_nla_table(500,500);
reset();

View file

@ -24,7 +24,7 @@
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define immWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define KVSL(x,y) ((chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==2 && isOutputL[ops==4][chan[x].state.alg][y]) || chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==1)
@ -211,7 +211,7 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
if (!isMuted[adpcmChan]) {
os[0]-=aOut.data[0]>>3;
os[1]-=aOut.data[0]>>3;
oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=aOut.data[0];
oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=aOut.data[0]>>1;
} else {
oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=0;
}
@ -220,47 +220,45 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
if (fm.rhy&0x20) {
for (int i=0; i<melodicChans+1; i++) {
unsigned char ch=outChanMap[i];
int chOut=0;
if (ch==255) continue;
oscBuf[i]->data[oscBuf[i]->needle]=0;
if (fm.channel[i].out[0]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
chOut+=*fm.channel[ch].out[0];
}
if (fm.channel[i].out[1]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
chOut+=*fm.channel[ch].out[1];
}
if (fm.channel[i].out[2]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[2];
chOut+=*fm.channel[ch].out[2];
}
if (fm.channel[i].out[3]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
chOut+=*fm.channel[ch].out[3];
}
oscBuf[i]->data[oscBuf[i]->needle]<<=1;
oscBuf[i]->needle++;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<(i==melodicChans?1:2),-32768,32767);
}
// special
oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*6;
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*6;
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*6;
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*6;
oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*4;
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*4;
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*4;
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*4;
} else {
for (int i=0; i<chans; i++) {
unsigned char ch=outChanMap[i];
int chOut=0;
if (ch==255) continue;
oscBuf[i]->data[oscBuf[i]->needle]=0;
if (fm.channel[i].out[0]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
chOut+=*fm.channel[ch].out[0];
}
if (fm.channel[i].out[1]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
chOut+=*fm.channel[ch].out[1];
}
if (fm.channel[i].out[2]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[2];
chOut+=*fm.channel[ch].out[2];
}
if (fm.channel[i].out[3]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
chOut+=*fm.channel[ch].out[3];
}
oscBuf[i]->data[oscBuf[i]->needle]<<=1;
oscBuf[i]->needle++;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<2,-32768,32767);
}
}

View file

@ -21,7 +21,7 @@
#define _OPL_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../../../extern/opl/opl3.h"
#include "sound/ymfm/ymfm_adpcm.h"
@ -64,9 +64,10 @@ class DivPlatformOPL: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,2048> writes;
opl3_chip fm;
unsigned char* adpcmBMem;
size_t adpcmBMemLen;

View file

@ -24,7 +24,7 @@
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define immWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_FREQBASE 1180068
@ -101,8 +101,16 @@ void DivPlatformOPLL::tick(bool sysTick) {
if (chan[i].std.vol.had) {
chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].vol,MIN(15,chan[i].std.vol.val),15);
if (i<9) {
rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
if (i>=6 && properDrums) {
drumVol[i-6]=15-chan[i].outVol;
rWrite(0x36,drumVol[0]);
rWrite(0x37,drumVol[1]|(drumVol[4]<<4));
rWrite(0x38,drumVol[3]|(drumVol[2]<<4));
} else if (i<6 || !drums) {
if (i<9) {
rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
}
}
@ -409,10 +417,6 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
case 8: case 9:
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
break;
default:
chan[c.chan].fixedFreq=0;
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
break;
}
} else {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);

View file

@ -21,7 +21,7 @@
#define _OPLL_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
extern "C" {
#include "../../../extern/Nuked-OPLL/opll.h"
@ -50,9 +50,10 @@ class DivPlatformOPLL: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,512> writes;
opll_t fm;
int delay, lastCustomMemory;
unsigned char lastBusy;

View file

@ -22,7 +22,7 @@
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) \
if (!skipRegisterWrites) { \
if (curChan!=c) { \
@ -101,7 +101,7 @@ void DivPlatformPCE::acquire(short** buf, size_t len) {
pce->ResetTS(0);
for (int i=0; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP((pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1])<<1,-32768,32767);
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1],-32768,32767);
}
tempL[0]=(tempL[0]>>1)+(tempL[0]>>2);
@ -531,7 +531,7 @@ int DivPlatformPCE::getRegisterPoolSize() {
}
void DivPlatformPCE::reset() {
while (!writes.empty()) writes.pop();
writes.clear();
memset(regPool,0,128);
for (int i=0; i<6; i++) {
chan[i]=DivPlatformPCE::Channel();

View file

@ -21,7 +21,7 @@
#define _PCE_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../waveSynth.h"
#include "sound/pce_psg.h"
@ -60,11 +60,12 @@ class DivPlatformPCE: public DivDispatch {
bool antiClickEnabled;
bool updateLFO;
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(9) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,512> writes;
unsigned char lastPan;
int cycles, curChan, delay;

View file

@ -42,12 +42,65 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) {
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;
}
if (chan[0].audPos>=(int)chan[0].audLen) {
chan[0].audPos%=chan[0].audLen;
chan[0].audDir=false;
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+((s7-s6)*(chan[0].audSub&0xffff)>>16);
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;
}
output=(chan[0].ws.output[chan[0].audPos]-0x80)<<8;
} else {
DivSample* s=parent->getSample(chan[0].sample);
if (s->samples>0) {
@ -176,7 +229,7 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) {
} else {
output=output*chan[0].vol*chan[0].envVol/16384;
}
oscBuf->data[oscBuf->needle++]=output;
oscBuf->data[oscBuf->needle++]=output>>1;
if (outStereo) {
buf[0][h]=((output*chan[0].panL)>>(depthScale+8))<<depthScale;
buf[1][h]=((output*chan[0].panR)>>(depthScale+8))<<depthScale;

View file

@ -21,7 +21,6 @@
#define _PCM_DAC_H
#include "../dispatch.h"
#include <queue>
#include "../waveSynth.h"
class DivPlatformPCMDAC: public DivDispatch {

View file

@ -366,8 +366,10 @@ void DivPlatformPCSpeaker::tick(bool sysTick) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>65535) chan[i].freq=65535;
if (chan[i].keyOn) {
on=true;
if (!chan[i].std.vol.had) {
if (chan[i].keyOn) {
on=true;
}
}
if (chan[i].keyOff) {
on=false;

View file

@ -21,7 +21,7 @@
#define _PCSPKR_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include <thread>
#include <mutex>
#include <condition_variable>
@ -40,12 +40,16 @@ class DivPlatformPCSpeaker: public DivDispatch {
struct RealQueueVal {
int tv_sec, tv_nsec;
unsigned short val;
RealQueueVal():
tv_sec(0),
tv_nsec(0),
val(0) {}
RealQueueVal(int sec, int nsec, unsigned short v):
tv_sec(sec),
tv_nsec(nsec),
val(v) {}
};
std::queue<RealQueueVal> realQueue;
FixedQueue<RealQueueVal,2048> realQueue;
std::mutex realQueueLock;
bool isMuted[1];
bool on, flip, lastOn, realOutEnabled;

View file

@ -21,7 +21,7 @@
#include "../engine.h"
#include "../../ta-log.h"
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 1
@ -35,6 +35,7 @@ const char* regCheatSheetPOKEY[]={
"AUDF4", "6",
"AUDC4", "7",
"AUDCTL", "8",
"SKCTL", "F",
NULL
};
@ -85,10 +86,10 @@ void DivPlatformPOKEY::acquireMZ(short* buf, size_t len) {
if (++oscBufDelay>=14) {
oscBufDelay=0;
oscBuf[0]->data[oscBuf[0]->needle++]=pokey.outvol_0<<11;
oscBuf[1]->data[oscBuf[1]->needle++]=pokey.outvol_1<<11;
oscBuf[2]->data[oscBuf[2]->needle++]=pokey.outvol_2<<11;
oscBuf[3]->data[oscBuf[3]->needle++]=pokey.outvol_3<<11;
oscBuf[0]->data[oscBuf[0]->needle++]=pokey.outvol_0<<10;
oscBuf[1]->data[oscBuf[1]->needle++]=pokey.outvol_1<<10;
oscBuf[2]->data[oscBuf[2]->needle++]=pokey.outvol_2<<10;
oscBuf[3]->data[oscBuf[3]->needle++]=pokey.outvol_3<<10;
}
}
}
@ -154,9 +155,14 @@ void DivPlatformPOKEY::tick(bool sysTick) {
}
}
if (skctlChanged) {
skctlChanged=false;
rWrite(15,skctl);
}
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,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
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);
if ((i==0 && !(audctl&64)) || (i==2 && !(audctl&32)) || i==1 || i==3) {
chan[i].freq/=7;
@ -194,6 +200,11 @@ void DivPlatformPOKEY::tick(bool sysTick) {
chan[i].freq>>=2;
}
// non-linear pitch
if (parent->song.linearPitch==0) {
chan[i].freq-=chan[i].pitch;
}
if (--chan[i].freq<0) chan[i].freq=0;
// snap buzz periods
@ -315,6 +326,10 @@ int DivPlatformPOKEY::dispatch(DivCommand c) {
audctl=c.value&0xff;
audctlChanged=true;
break;
case DIV_CMD_STD_NOISE_FREQ:
skctl=c.value?0x8b:0x03;
skctlChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
@ -380,6 +395,7 @@ void DivPlatformPOKEY::forceIns() {
chan[i].freqChanged=true;
}
audctlChanged=true;
skctlChanged=true;
}
void* DivPlatformPOKEY::getChanState(int ch) {
@ -403,7 +419,7 @@ unsigned char* DivPlatformPOKEY::getRegisterPool() {
}
int DivPlatformPOKEY::getRegisterPoolSize() {
return 9;
return 16;
}
void DivPlatformPOKEY::reset() {
@ -425,6 +441,8 @@ void DivPlatformPOKEY::reset() {
audctl=0;
audctlChanged=true;
skctl=3;
skctlChanged=true;
}
bool DivPlatformPOKEY::keyOffAffectsArp(int ch) {

View file

@ -21,7 +21,7 @@
#define _POKEY_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
extern "C" {
#include "sound/pokey/mzpokeysnd.h"
@ -43,13 +43,14 @@ class DivPlatformPOKEY: public DivDispatch {
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
unsigned char audctl;
bool audctlChanged;
FixedQueue<QueuedWrite,128> writes;
unsigned char audctl, skctl;
bool audctlChanged, skctlChanged;
unsigned char oscBufDelay;
PokeyState pokey;
AltASAP::Pokey altASAP;

View file

@ -42,7 +42,7 @@ void DivPlatformPV1000::acquire(short** buf, size_t len) {
short samp=d65010g031_sound_tick(&d65010g031,1);
buf[0][h]=samp;
for (int i=0; i<3; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(d65010g031.out[i]);
oscBuf[i]->data[oscBuf[i]->needle++]=MAX(d65010g031.out[i]<<2,0);
}
}
}

View file

@ -22,7 +22,6 @@
#include "../dispatch.h"
#include "sound/d65modified.h"
#include <queue>
class DivPlatformPV1000: public DivDispatch {
struct Channel: public SharedChannel<int> {

View file

@ -272,7 +272,7 @@ void DivPlatformQSound::acquire(short** buf, size_t len) {
buf[1][h]=chip.out[1];
for (int i=0; i<19; i++) {
int data=chip.voice_output[i]<<2;
int data=chip.voice_output[i]<<1;
if (data<-32768) data=-32768;
if (data>32767) data=32767;
oscBuf[i]->data[oscBuf[i]->needle++]=data;
@ -319,7 +319,7 @@ void DivPlatformQSound::tick(bool sysTick) {
if (length > 65536 - 16) {
length = 65536 - 16;
}
if (loopStart == -1 || loopStart >= length) {
if (!s->isLoopable()) {
if (i<16) {
qsound_end = offPCM[chan[i].sample] + length + 15;
} else {
@ -466,6 +466,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
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) {
chan[c.chan].outVol=chan[c.chan].vol;
@ -609,6 +610,7 @@ void DivPlatformQSound::forceIns() {
for (int i=0; i<19; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].keyOff=true;
//chan[i].sample=-1;
}
}

View file

@ -21,7 +21,6 @@
#define _QSOUND_H
#include "../dispatch.h"
#include <queue>
#include "sound/qsound.h"
class DivPlatformQSound: public DivDispatch {

View file

@ -74,7 +74,7 @@ void DivPlatformRF5C68::acquire(short** buf, size_t len) {
rf5c68.sound_stream_update(bufPtrs,chBufPtrs,blockLen);
for (int i=0; i<8; i++) {
for (size_t j=0; j<blockLen; j++) {
oscBuf[i]->data[oscBuf[i]->needle++]=bufC[i*2][j]+bufC[i*2+1][j];
oscBuf[i]->data[oscBuf[i]->needle++]=(bufC[i*2][j]+bufC[i*2+1][j])>>1;
}
}
pos+=blockLen;

View file

@ -21,7 +21,6 @@
#define _RF5C68_H
#include "../dispatch.h"
#include <queue>
#include "sound/rf5c68.h"
class DivPlatformRF5C68: public DivDispatch {

View file

@ -23,7 +23,7 @@
#include <string.h>
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 2

View file

@ -21,7 +21,7 @@
#define _SAA_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../../../extern/SAASound/src/SAASound.h"
class DivPlatformSAA1099: public DivDispatch {
@ -44,9 +44,10 @@ class DivPlatformSAA1099: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
CSAASound* saa_saaSound;
unsigned char regPool[32];
unsigned char lastBusy;

View file

@ -21,7 +21,6 @@
#define _SCC_H
#include "../dispatch.h"
#include <queue>
#include "../waveSynth.h"
#include "vgsound_emu/src/scc/scc.hpp"

View file

@ -23,7 +23,7 @@
#include <string.h>
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) rWrite(((c)<<3)+(a),v)
void DivPlatformSegaPCM::acquire(short** buf, size_t len) {
@ -49,7 +49,7 @@ void DivPlatformSegaPCM::acquire(short** buf, size_t len) {
buf[1][h]=os[1];
for (int i=0; i<16; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=pcm.lastOut[i][0]+pcm.lastOut[i][1];
oscBuf[i]->data[oscBuf[i]->needle++]=(pcm.lastOut[i][0]+pcm.lastOut[i][1])>>1;
}
}
}
@ -138,7 +138,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
rWrite(6+(i<<3),sampleEndSegaPCM[chan[i].pcm.sample]);
if (loopStart<0 || loopStart>=actualLength) {
if (!s->isLoopable()) {
rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
} else {
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart;
@ -156,7 +156,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
rWrite(6+(i<<3),sampleEndSegaPCM[chan[i].pcm.sample]);
if (loopStart<0 || loopStart>=actualLength) {
if (!s->isLoopable()) {
rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
} else {
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart;
@ -195,9 +195,6 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].pcm.sample=-1;
rWrite(0x86+(c.chan<<3),3);
chan[c.chan].macroInit(NULL);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
}
if (c.value!=DIV_NOTE_NULL) {
@ -207,6 +204,16 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
}
chan[c.chan].furnacePCM=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (parent->song.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);
rWrite(3+(c.chan<<3),chan[c.chan].chVolR);
}
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
} else {
@ -423,7 +430,7 @@ const void* DivPlatformSegaPCM::getSampleMem(int index) {
}
size_t DivPlatformSegaPCM::getSampleMemCapacity(int index) {
return index == 0 ? 16777216 : 0;
return index == 0 ? 2097152 : 0;
}
size_t DivPlatformSegaPCM::getSampleMemUsage(int index) {
@ -465,7 +472,7 @@ void DivPlatformSegaPCM::reset() {
void DivPlatformSegaPCM::renderSamples(int sysID) {
size_t memPos=0;
memset(sampleMem,0,16777216);
memset(sampleMem,0,2097152);
memset(sampleLoaded,0,256*sizeof(bool));
memset(sampleOffSegaPCM,0,256*sizeof(unsigned int));
memset(sampleEndSegaPCM,0,256);
@ -482,7 +489,7 @@ void DivPlatformSegaPCM::renderSamples(int sysID) {
}
logV("- sample %d will be at %x with length %x",i,memPos,alignedSize);
sampleLoaded[i]=true;
if (memPos>=16777216) break;
if (memPos>=2097152) break;
sampleOffSegaPCM[i]=memPos;
for (unsigned int j=0; j<alignedSize; j++) {
if (j>=sample->samples) {
@ -491,10 +498,10 @@ void DivPlatformSegaPCM::renderSamples(int sysID) {
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
}
sampleEndSegaPCM[i]=((memPos+0xff)>>8)-1;
if (memPos>=16777216) break;
if (memPos>=2097152) break;
}
logV(" and it ends in %d",sampleEndSegaPCM[i]);
if (memPos>=16777216) break;
if (memPos>=2097152) break;
}
sampleMemLen=memPos;
}
@ -522,10 +529,10 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, const DivC
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
sampleMem=new unsigned char[16777216];
sampleMem=new unsigned char[2097152];
pcm.set_bank(segapcm_device::BANK_12M|segapcm_device::BANK_MASKF8);
pcm.set_read([this](unsigned int addr) -> unsigned char {
return sampleMem[addr&0xffffff];
return sampleMem[addr&0x1fffff];
});
setFlags(flags);
reset();

View file

@ -23,7 +23,7 @@
#include "../dispatch.h"
#include "../instrument.h"
#include "sound/segapcm.h"
#include <queue>
#include "../fixedQueue.h"
class DivPlatformSegaPCM: public DivDispatch {
protected:
@ -59,9 +59,10 @@ class DivPlatformSegaPCM: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,1024> writes;
segapcm_device pcm;
int delay;
int pcmL, pcmR, pcmCycles;

View file

@ -22,7 +22,7 @@
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 64
@ -58,9 +58,9 @@ void DivPlatformSM8521::acquire(short** buf, size_t len) {
sm8521_sound_tick(&sm8521,8);
buf[0][h]=sm8521.out<<6;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<7;
}
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<6;
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<7;
}
}

View file

@ -21,7 +21,7 @@
#define _SM8521_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "../waveSynth.h"
#include "sound/sm8521.h"
@ -46,11 +46,12 @@ class DivPlatformSM8521: public DivDispatch {
DivDispatchOscBuffer* oscBuf[3];
bool isMuted[3];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
unsigned short addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,128> writes;
bool antiClickEnabled;
struct sm8521_t sm8521;

View file

@ -22,7 +22,7 @@
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);}}}
#define rWrite(a,v) {if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);}}}
const char* regCheatSheetSN[]={
"DATA", "0",

View file

@ -25,7 +25,7 @@
extern "C" {
#include "../../../extern/Nuked-PSG/ympsg.h"
}
#include <queue>
#include "../fixedQueue.h"
class DivPlatformSMS: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
@ -59,9 +59,10 @@ class DivPlatformSMS: public DivDispatch {
unsigned short addr;
unsigned char val;
bool addrOrVal;
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,128> writes;
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);

View file

@ -91,7 +91,7 @@ void DivPlatformSNES::acquire(short** buf, size_t len) {
next=(next*254)/MAX(1,globalVolL+globalVolR);
if (next<-32768) next=-32768;
if (next>32767) next=32767;
oscBuf[i]->data[oscBuf[i]->needle++]=next;
oscBuf[i]->data[oscBuf[i]->needle++]=next>>1;
}
}
}
@ -222,7 +222,7 @@ void DivPlatformSNES::tick(bool sysTick) {
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,s->lengthBRR-1)/16*9;
}
if (s->loopStart>=0) {
if (s->isLoopable()) {
loop=((s->depth!=DIV_SAMPLE_DEPTH_BRR)?9:0)+start+((s->loopStart/16)*9);
}
} else {

View file

@ -22,7 +22,7 @@
#include "../dispatch.h"
#include "../waveSynth.h"
#include <queue>
#include "../fixedQueue.h"
#include "sound/snes/SPC_DSP.h"
class DivPlatformSNES: public DivDispatch {
@ -81,9 +81,10 @@ class DivPlatformSNES: public DivDispatch {
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,256> writes;
signed char sampleMem[65536];
signed char copyOfSampleMem[65536];

View file

@ -0,0 +1,19 @@
Copyright (c) 2021 DefleMask Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,11 @@
dSID
===
This is the SID core used in DefleMask.
The project started as a very careful port from jsSID, comparing the wave
output from both, ensuring they were exactly the same.
## License
MIT License

View file

@ -0,0 +1,370 @@
#include "dsid.h"
#include <stdio.h>
#include <math.h> // INFINITY
#include <stdlib.h>
#include <string.h> // memset, memcpy
#define SID_OUT_SCALE (0x10000 * 3 * 16)
// CONTROL
#define GAT 0x01
#define SYN 0x02
#define RNG 0x04
#define TST 0x08
#define TRI 0x10
#define SAW 0x20
#define PUL 0x40
#define NOI 0x80
#define _HZ 0x10
#define DECSUS 0x40
#define ATK 0x80
// filter mode (high)
#define LP 0x10
#define BP 0x20
#define HP 0x40
#define OFF3 0x80
#define waveforms_add_sample(_id,_s) \
sid->lastOut[_id]=(_s);
const int Aexp[256] = {
1, 30, 30, 30, 30, 30, 30, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
double cmbWF(int chn, int *wfa, int index, int differ6581, struct SID_globals *g) {
if (differ6581 && g->model == 6581)
index &= 0x7FF;
return wfa[index];
}
void cCmbWF(int *wfa, double bitmul, double bstr, double trh) {
for (int i = 0; i < 4096; i++) {
wfa[i] = 0;
for (int j = 0; j < 12; j++) {
double blvl = 0;
for (int k = 0; k < 12; k++) {
blvl += (bitmul / pow(bstr, abs(k - j))) * (((i >> k) & 1) - 0.5);
}
wfa[i] += (blvl >= trh) ? pow(2, j) : 0;
}
wfa[i] *= 12;
}
}
void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf) {
if (model == 6581) {
sid->g.model = 6581;
} else {
sid->g.model = 8580;
}
memset(sid->M,0,MemLen);
memset(sid->SIDct, 0, sizeof(sid->SIDct));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
sid->SIDct[i].ch[j].Ast = _HZ;
sid->SIDct[i].ch[j].nLFSR = 0x7FFFF8;
sid->SIDct[i].ch[j].prevwfout = 0;
}
sid->SIDct[i].ch[0].FSW = 1;
sid->SIDct[i].ch[1].FSW = 2;
sid->SIDct[i].ch[2].FSW = 4;
}
sid->g.ctfr = -2.0 * 3.14 * (12500.0 / 256.0) / samplingRate,
sid->g.ctf_ratio_6581 = -2.0 * 3.14 * (samplingRate / 44100.0) * (20000.0 / 256.0) / samplingRate;
sid->g.ckr = clockRate / samplingRate;
const double bAprd[16] = {9, 32 * 1, 63 * 1, 95 * 1, 149 * 1, 220 * 1,
267 * 1, 313 * 1, 392 * 1, 977 * 1, 1954 * 1, 3126 * 1,
3907 * 1, 11720 * 1, 19532 * 1, 31251 * 1};
const int bAstp[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
memcpy(&sid->g.Aprd, &bAprd, sizeof(bAprd));
memcpy(&sid->g.Astp, &bAstp, sizeof(bAstp));
if (init_wf) {
cCmbWF(sid->g.trsaw, 0.8, 2.4, 0.64);
cCmbWF(sid->g.pusaw, 1.4, 1.9, 0.68);
cCmbWF(sid->g.Pulsetrsaw, 0.8, 2.5, 0.64);
for (int i = 0; i < 2048; i++) {
double ctf = (double) i / 8.0 + 0.2;
if (model == 8580) {
ctf = 1 - exp(ctf * sid->g.ctfr);
} else {
if (ctf < 24) {
ctf = 2.0 * sin(771.78 / samplingRate);
} else {
ctf = (44100.0 / samplingRate) - 1.263 * (44100.0 / samplingRate) * exp(ctf * sid->g.ctf_ratio_6581);
}
}
sid->g.ctf_table[i] = ctf;
}
}
double prd0 = sid->g.ckr > 9 ? sid->g.ckr : 9;
sid->g.Aprd[0] = prd0;
sid->g.Astp[0] = ceil(prd0 / 9);
for (int i=0; i<3; i++) {
sid->fakeplp[i]=0;
sid->fakepbp[i]=0;
}
}
double dSID_render(struct SID_chip* sid) {
double flin = 0, output = 0;
double wfout = 0;
double step = 0;
for (int chn = 0; chn < 3; chn++) {
struct SIDVOICE *voic = &((struct SIDMEM *) (sid->M))->v[chn];
double pgt = (sid->SIDct->ch[chn].Ast & GAT);
uint8_t ctrl = voic->control;
uint8_t wf = ctrl & 0xF0;
uint8_t test = ctrl & TST;
uint8_t SR = voic->susres;
double tmp = 0;
if (pgt != (ctrl & GAT)) {
if (pgt) {
sid->SIDct->ch[chn].Ast &= 0xFF - (GAT | ATK | DECSUS);
} else {
sid->SIDct->ch[chn].Ast = (GAT | ATK | DECSUS);
if ((SR & 0xF) > (sid->SIDct->ch[chn].pSR & 0xF))
tmp = 1;
}
}
sid->SIDct->ch[chn].pSR = SR;
sid->SIDct->ch[chn].rcnt += sid->g.ckr;
if (sid->SIDct->ch[chn].rcnt >= 0x8000)
sid->SIDct->ch[chn].rcnt -= 0x8000;
double prd;
if (sid->SIDct->ch[chn].Ast & ATK) {
step = voic->attack;
prd = sid->g.Aprd[(int) step];
} else if (sid->SIDct->ch[chn].Ast & DECSUS) {
step = voic->decay;
prd = sid->g.Aprd[(int) step];
} else {
step = SR & 0xF;
prd = sid->g.Aprd[(int) step];
}
step = sid->g.Astp[(int) step];
if (sid->SIDct->ch[chn].rcnt >= prd && sid->SIDct->ch[chn].rcnt < prd + sid->g.ckr &&
tmp == 0) {
sid->SIDct->ch[chn].rcnt -= prd;
if ((sid->SIDct->ch[chn].Ast & ATK) ||
++sid->SIDct->ch[chn].expcnt == Aexp[(int) sid->SIDct->ch[chn].envcnt]) {
if (!(sid->SIDct->ch[chn].Ast & _HZ)) {
if (sid->SIDct->ch[chn].Ast & ATK) {
sid->SIDct->ch[chn].envcnt += step;
if (sid->SIDct->ch[chn].envcnt >= 0xFF) {
sid->SIDct->ch[chn].envcnt = 0xFF;
sid->SIDct->ch[chn].Ast &= 0xFF - ATK;
}
} else if (!(sid->SIDct->ch[chn].Ast & DECSUS) ||
sid->SIDct->ch[chn].envcnt > (SR >> 4) + (SR & 0xF0)) {
sid->SIDct->ch[chn].envcnt -= step;
if (sid->SIDct->ch[chn].envcnt <= 0 &&
sid->SIDct->ch[chn].envcnt + step != 0) {
sid->SIDct->ch[chn].envcnt = 0;
sid->SIDct->ch[chn].Ast |= _HZ;
}
}
}
sid->SIDct->ch[chn].expcnt = 0;
} else {
}
}
sid->SIDct->ch[chn].envcnt = (int) sid->SIDct->ch[chn].envcnt & 0xFF;
double aAdd = (voic->freq_low + voic->freq_high * 256) * sid->g.ckr;
if (test || ((ctrl & SYN) && sid->SIDct->sMSBrise)) {
sid->SIDct->ch[chn].pacc = 0;
} else {
sid->SIDct->ch[chn].pacc += aAdd;
if (sid->SIDct->ch[chn].pacc > 0xFFFFFF)
sid->SIDct->ch[chn].pacc -= 0x1000000;
}
double MSB = (int) sid->SIDct->ch[chn].pacc & 0x800000;
sid->SIDct->sMSBrise = (MSB > ((int) sid->SIDct->ch[chn].pracc & 0x800000)) ? 1 : 0;
if (wf & NOI) {
tmp = sid->SIDct->ch[chn].nLFSR;
if ((((int) sid->SIDct->ch[chn].pacc & 0x100000) !=
((int) sid->SIDct->ch[chn].pracc & 0x100000)) ||
aAdd >= 0x100000) {
step = ((int) tmp & 0x400000) ^ (((int) tmp & 0x20000) << 5);
tmp = (((int) tmp << 1) + (step > 0 || test)) & 0x7FFFFF;
sid->SIDct->ch[chn].nLFSR = tmp;
}
wfout = (wf & 0x70) ? 0
: (((int) tmp & 0x100000) >> 5) + (((int) tmp & 0x40000) >> 4) +
(((int) tmp & 0x4000) >> 1) + (((int) tmp & 0x800) << 1) +
(((int) tmp & 0x200) << 2) + (((int) tmp & 0x20) << 5) +
(((int) tmp & 0x04) << 7) + (((int) tmp & 0x01) << 8);
} else if (wf & PUL) {
double pw = (voic->pw_low + (voic->pw_high) * 256) * 16;
tmp = (int) aAdd >> 9;
if (0 < pw && pw < tmp)
pw = tmp;
tmp = (int) tmp ^ 0xFFFF;
if (pw > tmp)
pw = tmp;
tmp = (int) sid->SIDct->ch[chn].pacc >> 8;
if (wf == PUL) {
int lel = ((int) aAdd >> 16);
if (lel > 0) {
step = 256.0 / (double) lel;
} else {
step = INFINITY;
}
if (test)
wfout = 0xFFFF;
else if (tmp < pw) {
double lim = (0xFFFF - pw) * step;
if (lim > 0xFFFF)
lim = 0xFFFF;
wfout = lim - (pw - tmp) * step;
if (wfout < 0)
wfout = 0;
} else {
double lim = pw * step;
if (lim > 0xFFFF)
lim = 0xFFFF;
wfout = (0xFFFF - tmp) * step - lim;
if (wfout >= 0)
wfout = 0xFFFF;
wfout = (int) wfout & 0xFFFF;
}
} else {
wfout = (tmp >= pw || test) ? 0xFFFF : 0;
if (wf & TRI) {
if (wf & SAW) {
wfout =
(wfout) ? cmbWF(chn, sid->g.Pulsetrsaw, (int) tmp >> 4, 1, &sid->g) : 0;
} else {
tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0);
wfout =
(wfout)
? cmbWF(chn, sid->g.pusaw,
((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 11,
0, &sid->g)
: 0;
}
} else if (wf & SAW)
wfout = (wfout) ? cmbWF(chn, sid->g.pusaw, (int) tmp >> 4, 1, &sid->g) : 0;
}
} else if (wf & SAW) {
wfout = (int) sid->SIDct->ch[chn].pacc >> 8;
if (wf & TRI)
wfout = cmbWF(chn, sid->g.trsaw, (int) wfout >> 4, 1, &sid->g);
else {
step = aAdd / 0x1200000;
wfout += wfout * step;
if (wfout > 0xFFFF)
wfout = 0xFFFF - (wfout - 0x10000) / step;
}
} else if (wf & TRI) {
tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0);
wfout = ((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 7;
}
if (wf)
sid->SIDct->ch[chn].prevwfout = wfout;
else {
wfout = sid->SIDct->ch[chn].prevwfout;
}
sid->SIDct->ch[chn].pracc = sid->SIDct->ch[chn].pacc;
sid->SIDct->sMSB = MSB;
// double preflin = flin;
if ((sid->mute_mask & (1 << chn))) {
if (sid->M[0x17] & sid->SIDct->ch[chn].FSW) {
double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256);
flin += chnout;
// fake filter for solo waveform ahead
// mostly copypasted from below
double fakeflin = chnout;
double fakeflout = 0;
double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff];
double reso;
if (sid->g.model == 8580) {
reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8));
} else {
reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41;
}
double tmp = fakeflin + sid->fakepbp[chn] * reso + sid->fakeplp[chn];
if (sid->M[0x18] & HP)
fakeflout -= tmp;
tmp = sid->fakepbp[chn] - tmp * ctf;
sid->fakepbp[chn] = tmp;
if (sid->M[0x18] & BP)
fakeflout -= tmp;
tmp = sid->fakeplp[chn] + tmp * ctf;
sid->fakeplp[chn] = tmp;
if (sid->M[0x18] & LP)
fakeflout += tmp;
double wf_out = (fakeflout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535;
waveforms_add_sample(chn, wf_out);
} else if ((chn % 3) != 2 || !(sid->M[0x18] & OFF3)) {
double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256);
output += chnout;
double wf_out = (chnout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535;
waveforms_add_sample(chn, wf_out);
}
} else {
waveforms_add_sample(chn, 0);
}
}
int M1 = 0;
if (M1 & 3)
sid->M[0x1B] = (int) wfout >> 8;
sid->M[0x1C] = sid->SIDct->ch[2].envcnt;
double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff];
double reso;
if (sid->g.model == 8580) {
reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8));
} else {
reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41;
}
double tmp = flin + sid->SIDct->pbp * reso + sid->SIDct->plp;
if (sid->M[0x18] & HP)
output -= tmp;
tmp = sid->SIDct->pbp - tmp * ctf;
sid->SIDct->pbp = tmp;
if (sid->M[0x18] & BP)
output -= tmp;
tmp = sid->SIDct->plp + tmp * ctf;
sid->SIDct->plp = tmp;
if (sid->M[0x18] & LP)
output += tmp;
return (output / SID_OUT_SCALE) * (sid->M[0x18] & 0xF);
}
void dSID_setMuteMask(struct SID_chip* sid, int mute_mask) {
sid->mute_mask = mute_mask;
}
float dSID_getVolume(struct SID_chip* sid, int channel) {
if ((sid->M[0x18] & 0xF) == 0)
return 0;
return sid->SIDct[0].ch[channel].envcnt / 256.0f;
}
void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val) {
sid->M[addr&0x1f]=val;
}

View file

@ -0,0 +1,99 @@
#ifndef DSID_H
#define DSID_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
struct SID_ctx_chan {
double rcnt;
double envcnt;
double expcnt;
double pacc;
double pracc;
int FSW;
int nLFSR;
double prevwfout;
uint8_t pSR;
int Ast;
};
struct SID_ctx {
int sMSBrise;
int sMSB;
double plp;
double pbp;
struct SID_ctx_chan ch[3];
};
struct SIDVOICE {
uint8_t freq_low;
uint8_t freq_high;
uint8_t pw_low;
uint8_t pw_high : 4;
uint8_t UNUSED : 4;
uint8_t control;
uint8_t decay : 4;
uint8_t attack : 4;
uint8_t susres;
// uint8_t release : 4;
// uint8_t sustain : 4;
};
struct SIDMEM {
struct SIDVOICE v[3];
uint8_t UNUSED : 4;
uint8_t cutoff_low : 4;
uint8_t cutoff_high;
uint8_t reso_rt : 4;
uint8_t reso : 4;
uint8_t volume : 4;
uint8_t filter_mode : 4;
uint8_t paddlex;
uint8_t paddley;
uint8_t osc3;
uint8_t env3;
};
struct SID_globals {
double ckr;
double ctfr;
double ctf_ratio_6581;
double ctf_table[2048];
int trsaw[4096];
int pusaw[4096];
int Pulsetrsaw[4096];
double Aprd[16];
int Astp[16];
int model;
};
#define MemLen 65536
struct SID_chip {
struct SID_globals g;
struct SID_ctx SIDct[3];
uint8_t M[MemLen];
int16_t lastOut[3];
int mute_mask;
double fakeplp[3];
double fakepbp[3];
};
double dSID_render(struct SID_chip* sid);
void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf);
float dSID_getVolume(struct SID_chip* sid, int channel);
void dSID_setMuteMask(struct SID_chip* sid, int mute_mask);
void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -61,7 +61,6 @@ const unsigned int EnvelopeGenerator::adsrtable[16] =
void EnvelopeGenerator::reset()
{
// counter is not changed on reset
envelope_pipeline = 0;
state_pipeline = 0;
@ -73,7 +72,7 @@ void EnvelopeGenerator::reset()
gate = false;
resetLfsr = true;
resetLfsr = false;
exponential_counter = 0;
exponential_counter_period = 1;
@ -81,7 +80,11 @@ void EnvelopeGenerator::reset()
state = RELEASE;
counter_enabled = true;
rate = adsrtable[release];
rate = 0;
envelope_counter = 0;
env3 = 0;
lfsr = 0x7fff;
}
void EnvelopeGenerator::writeCONTROL_REG(unsigned char control)

View file

@ -146,7 +146,7 @@ public:
counter_enabled(true),
gate(false),
resetLfsr(false),
envelope_counter(0xaa),
envelope_counter(0),
attack(0),
decay(0),
sustain(0),

View file

@ -351,7 +351,7 @@ void SID::write(int offset, unsigned char value)
break;
case 0x04: // Voice #1 control register
voice[0]->writeCONTROL_REG(muted[0] ? 0 : value);
voice[0]->writeCONTROL_REG(value);
break;
case 0x05: // Voice #1 Attack and Decay length
@ -379,7 +379,7 @@ void SID::write(int offset, unsigned char value)
break;
case 0x0b: // Voice #2 control register
voice[1]->writeCONTROL_REG(muted[1] ? 0 : value);
voice[1]->writeCONTROL_REG(value);
break;
case 0x0c: // Voice #2 Attack and Decay length
@ -407,7 +407,7 @@ void SID::write(int offset, unsigned char value)
break;
case 0x12: // Voice #3 control register
voice[2]->writeCONTROL_REG(muted[2] ? 0 : value);
voice[2]->writeCONTROL_REG(value);
break;
case 0x13: // Voice #3 Attack and Decay length

View file

@ -320,11 +320,11 @@ int SID::output()
const int v2 = voice[1]->output(voice[0]->wave());
const int v3 = voice[2]->output(voice[1]->wave());
lastChanOut[0]=v1;
lastChanOut[1]=v2;
lastChanOut[2]=v3;
lastChanOut[0]=muted[0]?0:v1;
lastChanOut[1]=muted[1]?0:v2;
lastChanOut[2]=muted[2]?0:v3;
return externalFilter->clock(filter->clock(v1, v2, v3));
return externalFilter->clock(filter->clock(muted[0]?0:v1, muted[1]?0:v2, muted[2]?0:v3));
}

View file

@ -137,7 +137,7 @@ void okim6258_device::device_reset()
void okim6258_device::sound_stream_update(short** outputs, int len)
{
auto &buffer = outputs[0];
short* buffer = outputs[0];
if (m_status & STATUS_PLAYING)
{

View file

@ -39,7 +39,7 @@ static constexpr int MuteInit = 2;
static constexpr int MuteSerialInput = 8;
//just some magick value to match the audio level of mzpokeysnd
static constexpr int16_t MAGICK_VOLUME_BOOSTER = 160;
static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 4;
static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 6;
struct PokeyBase
{

View file

@ -141,6 +141,7 @@ template<bool IsOpnA>
bool opn_registers_base<IsOpnA>::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
{
assert(index < REGISTERS);
if (index >= REGISTERS) return false;
// writes in the 0xa0-af/0x1a0-af region are handled as latched pairs
// borrow unused registers 0xb8-bf/0x1b8-bf as temporary holding locations

View file

@ -23,7 +23,7 @@
#include <math.h>
//#define rWrite(a,v) pendingWrites[a]=v;
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) rWrite(((c)<<5)|(a),v);
#define CHIP_DIVIDER 2

View file

@ -21,7 +21,7 @@
#define _SU_H
#include "../dispatch.h"
#include <queue>
#include "../fixedQueue.h"
#include "sound/su.h"
class DivPlatformSoundUnit: public DivDispatch {
@ -72,11 +72,12 @@ class DivPlatformSoundUnit: public DivDispatch {
DivDispatchOscBuffer* oscBuf[8];
bool isMuted[8];
struct QueuedWrite {
unsigned char addr;
unsigned char val;
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
unsigned char addr;
unsigned char val;
QueuedWrite(): addr(0), val(0) {}
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
FixedQueue<QueuedWrite,512> writes;
unsigned char lastPan;
bool sampleMemSize;
unsigned char ilCtrl, ilSize, fil1;

View file

@ -21,8 +21,8 @@
#include "../engine.h"
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);}}
#define postWrite(a,v) postDACWrites.emplace(a,v);
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);}}
#define postWrite(a,v) postDACWrites.push(DivRegWrite(a,v));
#define CHIP_DIVIDER 32
@ -548,19 +548,25 @@ void DivPlatformSwan::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
}
int DivPlatformSwan::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
void DivPlatformSwan::setFlags(const DivConfig& flags) {
chipClock=3072000;
CHECK_CUSTOM_CLOCK;
rate=chipClock/16; // = 192000kHz, should be enough
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
oscBuf[i]->rate=rate;
}
}
int DivPlatformSwan::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
ws=new WSwan();
setFlags(flags);
reset();
return 4;
}

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