Merge branch 'master' of https://github.com/tildearrow/furnace into ymf289b
This commit is contained in:
commit
0889a8fe92
559 changed files with 31100 additions and 15654 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
189
src/engine/effect.h
Normal 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
|
||||
85
src/engine/effect/abstract.cpp
Normal file
85
src/engine/effect/abstract.cpp
Normal 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() {
|
||||
}
|
||||
60
src/engine/effect/dummy.cpp
Normal file
60
src/engine/effect/dummy.cpp
Normal 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
34
src/engine/effect/dummy.h
Normal 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();
|
||||
};
|
||||
95
src/engine/effectContainer.cpp
Normal file
95
src/engine/effectContainer.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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*)));
|
||||
|
|
|
|||
|
|
@ -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
140
src/engine/fixedQueue.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#define _AMIGA_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../waveSynth.h"
|
||||
|
||||
class DivPlatformAmiga: public DivDispatch {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#define _K005289_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../waveSynth.h"
|
||||
#include "vgsound_emu/src/k005289/k005289.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
513
src/engine/platform/k053260.cpp
Normal file
513
src/engine/platform/k053260.cpp
Normal 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];
|
||||
}
|
||||
}
|
||||
97
src/engine/platform/k053260.h
Normal file
97
src/engine/platform/k053260.h
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#define _PCM_DAC_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../waveSynth.h"
|
||||
|
||||
class DivPlatformPCMDAC: public DivDispatch {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include "../dispatch.h"
|
||||
#include "sound/d65modified.h"
|
||||
#include <queue>
|
||||
|
||||
class DivPlatformPV1000: public DivDispatch {
|
||||
struct Channel: public SharedChannel<int> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#define _QSOUND_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "sound/qsound.h"
|
||||
|
||||
class DivPlatformQSound: public DivDispatch {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#define _RF5C68_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "sound/rf5c68.h"
|
||||
|
||||
class DivPlatformRF5C68: public DivDispatch {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#define _SCC_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../waveSynth.h"
|
||||
#include "vgsound_emu/src/scc/scc.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
19
src/engine/platform/sound/c64_d/LICENSE
Normal file
19
src/engine/platform/sound/c64_d/LICENSE
Normal 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.
|
||||
11
src/engine/platform/sound/c64_d/README.md
Normal file
11
src/engine/platform/sound/c64_d/README.md
Normal 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
|
||||
370
src/engine/platform/sound/c64_d/dsid.c
Normal file
370
src/engine/platform/sound/c64_d/dsid.c
Normal 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;
|
||||
}
|
||||
99
src/engine/platform/sound/c64_d/dsid.h
Normal file
99
src/engine/platform/sound/c64_d/dsid.h
Normal 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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public:
|
|||
counter_enabled(true),
|
||||
gate(false),
|
||||
resetLfsr(false),
|
||||
envelope_counter(0xaa),
|
||||
envelope_counter(0),
|
||||
attack(0),
|
||||
decay(0),
|
||||
sustain(0),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue