Merge branch 'master' of https://github.com/tildearrow/furnace into k053260
This commit is contained in:
commit
7402575d11
398 changed files with 23113 additions and 12190 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,11 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_HINT_ARP_TIME, // (value)
|
||||
|
||||
DIV_CMD_SNES_GLOBAL_VOL_LEFT,
|
||||
DIV_CMD_SNES_GLOBAL_VOL_RIGHT,
|
||||
|
||||
DIV_CMD_NES_LINEAR_LENGTH,
|
||||
|
||||
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
||||
|
||||
DIV_CMD_MAX
|
||||
|
|
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -2034,11 +2066,17 @@ String DivEngine::getPlaybackDebugInfo() {
|
|||
"divider: %f\n"
|
||||
"cycles: %d\n"
|
||||
"clockDrift: %f\n"
|
||||
"midiClockCycles: %d\n"
|
||||
"midiClockDrift: %f\n"
|
||||
"midiTimeCycles: %d\n"
|
||||
"midiTimeDrift: %f\n"
|
||||
"changeOrd: %d\n"
|
||||
"changePos: %d\n"
|
||||
"totalSeconds: %d\n"
|
||||
"totalTicks: %d\n"
|
||||
"totalTicksR: %d\n"
|
||||
"curMidiClock: %d\n"
|
||||
"curMidiTime: %d\n"
|
||||
"totalCmds: %d\n"
|
||||
"lastCmds: %d\n"
|
||||
"cmdsPerSecond: %d\n"
|
||||
|
|
@ -2048,7 +2086,8 @@ String DivEngine::getPlaybackDebugInfo() {
|
|||
"totalProcessed: %d\n"
|
||||
"bufferPos: %d\n",
|
||||
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
|
||||
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
||||
midiClockCycles,midiClockDrift,midiTimeCycles,midiTimeDrift,changeOrd,changePos,totalSeconds,totalTicks,
|
||||
totalTicksR,curMidiClock,curMidiTime,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
||||
(int)extValue,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
||||
);
|
||||
}
|
||||
|
|
@ -2165,16 +2204,27 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
prevOrder=0;
|
||||
prevRow=0;
|
||||
stepPlay=0;
|
||||
int prevDrift;
|
||||
if (curSubSong!=NULL) curSubSong->arpLen=1;
|
||||
int prevDrift, prevMidiClockDrift, prevMidiTimeDrift;
|
||||
prevDrift=clockDrift;
|
||||
prevMidiClockDrift=midiClockDrift;
|
||||
prevMidiTimeDrift=midiTimeDrift;
|
||||
clockDrift=0;
|
||||
cycles=0;
|
||||
midiClockCycles=0;
|
||||
midiClockDrift=0;
|
||||
midiTimeCycles=0;
|
||||
midiTimeDrift=0;
|
||||
if (!preserveDrift) {
|
||||
ticks=1;
|
||||
tempoAccum=0;
|
||||
totalTicks=0;
|
||||
totalSeconds=0;
|
||||
totalTicksR=0;
|
||||
curMidiClock=0;
|
||||
curMidiTime=0;
|
||||
curMidiTimeCode=0;
|
||||
curMidiTimePiece=0;
|
||||
totalLoops=0;
|
||||
lastLoopPos=-1;
|
||||
}
|
||||
|
|
@ -2191,6 +2241,10 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
skipping=false;
|
||||
return;
|
||||
}
|
||||
if (!preserveDrift) {
|
||||
runMidiClock(cycles);
|
||||
runMidiTime(cycles);
|
||||
}
|
||||
}
|
||||
int oldOrder=curOrder;
|
||||
while (playing && (curRow<goalRow || ticks>1)) {
|
||||
|
|
@ -2198,6 +2252,10 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
skipping=false;
|
||||
return;
|
||||
}
|
||||
if (!preserveDrift) {
|
||||
runMidiClock(cycles);
|
||||
runMidiTime(cycles);
|
||||
}
|
||||
if (oldOrder!=curOrder) break;
|
||||
if (ticks-((tempoAccum+curSubSong->virtualTempoN)/MAX(1,curSubSong->virtualTempoD))<1 && curRow>=goalRow) break;
|
||||
}
|
||||
|
|
@ -2211,9 +2269,22 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
repeatPattern=oldRepeatPattern;
|
||||
if (preserveDrift) {
|
||||
clockDrift=prevDrift;
|
||||
midiClockDrift=prevMidiClockDrift;
|
||||
midiTimeDrift=prevMidiTimeDrift;
|
||||
} else {
|
||||
clockDrift=0;
|
||||
cycles=0;
|
||||
midiClockCycles=0;
|
||||
midiClockDrift=0;
|
||||
midiTimeCycles=0;
|
||||
midiTimeDrift=0;
|
||||
if (curMidiTime>0) {
|
||||
curMidiTime--;
|
||||
}
|
||||
if (curMidiClock>0) {
|
||||
curMidiClock--;
|
||||
}
|
||||
curMidiTimePiece=0;
|
||||
}
|
||||
if (!preserveDrift) {
|
||||
ticks=1;
|
||||
|
|
@ -2382,9 +2453,74 @@ void DivEngine::play() {
|
|||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
keyHit[i]=false;
|
||||
}
|
||||
curMidiTimePiece=0;
|
||||
if (output) if (!skipping && output->midiOut!=NULL) {
|
||||
int pos=totalTicksR/6;
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_POSITION,(pos>>7)&0x7f,pos&0x7f));
|
||||
if (midiOutClock) {
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_POSITION,(curMidiClock>>7)&0x7f,curMidiClock&0x7f));
|
||||
}
|
||||
if (midiOutTime) {
|
||||
TAMidiMessage msg;
|
||||
msg.type=TA_MIDI_SYSEX;
|
||||
msg.sysExData.reset(new unsigned char[10],std::default_delete<unsigned char[]>());
|
||||
msg.sysExLen=10;
|
||||
unsigned char* msgData=msg.sysExData.get();
|
||||
int actualTime=curMidiTime;
|
||||
int timeRate=midiOutTimeRate;
|
||||
int drop=0;
|
||||
if (timeRate<1 || timeRate>4) {
|
||||
if (curSubSong->hz>=47.98 && curSubSong->hz<=48.02) {
|
||||
timeRate=1;
|
||||
} else if (curSubSong->hz>=49.98 && curSubSong->hz<=50.02) {
|
||||
timeRate=2;
|
||||
} else if (curSubSong->hz>=59.9 && curSubSong->hz<=60.11) {
|
||||
timeRate=4;
|
||||
} else {
|
||||
timeRate=4;
|
||||
}
|
||||
}
|
||||
|
||||
switch (timeRate) {
|
||||
case 1: // 24
|
||||
msgData[5]=(actualTime/(60*60*24))%24;
|
||||
msgData[6]=(actualTime/(60*24))%60;
|
||||
msgData[7]=(actualTime/24)%60;
|
||||
msgData[8]=actualTime%24;
|
||||
break;
|
||||
case 2: // 25
|
||||
msgData[5]=(actualTime/(60*60*25))%24;
|
||||
msgData[6]=(actualTime/(60*25))%60;
|
||||
msgData[7]=(actualTime/25)%60;
|
||||
msgData[8]=actualTime%25;
|
||||
break;
|
||||
case 3: // 29.97 (NTSC drop)
|
||||
// drop
|
||||
drop=((actualTime/(30*60))-(actualTime/(30*600)))*2;
|
||||
actualTime+=drop;
|
||||
|
||||
msgData[5]=(actualTime/(60*60*30))%24;
|
||||
msgData[6]=(actualTime/(60*30))%60;
|
||||
msgData[7]=(actualTime/30)%60;
|
||||
msgData[8]=actualTime%30;
|
||||
break;
|
||||
case 4: // 30 (NTSC non-drop)
|
||||
default:
|
||||
msgData[5]=(actualTime/(60*60*30))%24;
|
||||
msgData[6]=(actualTime/(60*30))%60;
|
||||
msgData[7]=(actualTime/30)%60;
|
||||
msgData[8]=actualTime%30;
|
||||
break;
|
||||
}
|
||||
|
||||
msgData[5]|=(timeRate-1)<<5;
|
||||
|
||||
msgData[0]=0xf0;
|
||||
msgData[1]=0x7f;
|
||||
msgData[2]=0x7f;
|
||||
msgData[3]=0x01;
|
||||
msgData[4]=0x01;
|
||||
msgData[9]=0xf7;
|
||||
output->midiOut->send(msg);
|
||||
}
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_MACHINE_PLAY,0,0));
|
||||
}
|
||||
BUSY_END;
|
||||
|
|
@ -2557,16 +2693,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();
|
||||
|
|
@ -2781,14 +2908,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() {
|
||||
|
|
@ -2929,6 +3049,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;
|
||||
|
|
@ -2943,6 +3064,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;
|
||||
|
|
@ -2979,6 +3101,8 @@ void DivEngine::delInstrument(int index) {
|
|||
}
|
||||
}
|
||||
}
|
||||
removeAsset(song.insDir,index);
|
||||
checkAssetDir(song.insDir,song.ins.size());
|
||||
}
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
|
@ -2995,6 +3119,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;
|
||||
|
|
@ -3011,6 +3136,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;
|
||||
|
|
@ -3090,15 +3216,17 @@ DivWavetable* DivEngine::waveFromFile(const char* path, bool addRaw) {
|
|||
// read as .dmw
|
||||
reader.seek(0,SEEK_SET);
|
||||
int len=reader.readI();
|
||||
logD("wave length %d",len);
|
||||
if (len<=0 || len>256) {
|
||||
throw EndOfFileException(&reader,reader.size());
|
||||
}
|
||||
wave->len=len;
|
||||
wave->max=(unsigned char)reader.readC();
|
||||
if (wave->max==255) { // new wavetable format
|
||||
unsigned char waveVersion=reader.readC();
|
||||
logI("reading modern .dmw...");
|
||||
logD("wave version %d",waveVersion);
|
||||
wave->max=reader.readC();
|
||||
wave->max=(unsigned char)reader.readC();
|
||||
for (int i=0; i<len; i++) {
|
||||
wave->data[i]=reader.readI();
|
||||
}
|
||||
|
|
@ -3163,6 +3291,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;
|
||||
|
|
@ -3183,6 +3313,7 @@ int DivEngine::addSample() {
|
|||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
checkAssetDir(song.sampleDir,song.sample.size());
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
BUSY_END;
|
||||
|
|
@ -3200,6 +3331,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;
|
||||
|
|
@ -3669,6 +3801,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();
|
||||
|
|
@ -3867,6 +4001,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;
|
||||
|
|
@ -3880,6 +4015,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;
|
||||
|
|
@ -3895,6 +4031,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;
|
||||
|
|
@ -3909,6 +4046,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;
|
||||
|
|
@ -3921,6 +4059,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;
|
||||
|
|
@ -3936,6 +4075,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;
|
||||
|
|
@ -3980,6 +4120,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) {
|
||||
|
|
@ -4191,23 +4335,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;
|
||||
}
|
||||
|
|
@ -4365,6 +4497,10 @@ void DivEngine::quitDispatch() {
|
|||
}
|
||||
cycles=0;
|
||||
clockDrift=0;
|
||||
midiClockCycles=0;
|
||||
midiClockDrift=0;
|
||||
midiTimeCycles=0;
|
||||
midiTimeDrift=0;
|
||||
chans=0;
|
||||
playing=false;
|
||||
curSpeed=0;
|
||||
|
|
@ -4381,6 +4517,10 @@ void DivEngine::quitDispatch() {
|
|||
totalTicks=0;
|
||||
totalSeconds=0;
|
||||
totalTicksR=0;
|
||||
curMidiClock=0;
|
||||
curMidiTime=0;
|
||||
curMidiTimeCode=0;
|
||||
curMidiTimePiece=0;
|
||||
totalCmds=0;
|
||||
lastCmds=0;
|
||||
cmdsPerSecond=0;
|
||||
|
|
@ -4407,6 +4547,8 @@ bool DivEngine::initAudioBackend() {
|
|||
lowLatency=getConfInt("lowLatency",0);
|
||||
metroVol=(float)(getConfInt("metroVol",100))/100.0f;
|
||||
midiOutClock=getConfInt("midiOutClock",0);
|
||||
midiOutTime=getConfInt("midiOutTime",0);
|
||||
midiOutTimeRate=getConfInt("midiOutTimeRate",0);
|
||||
midiOutProgramChange = getConfInt("midiOutProgramChange",0);
|
||||
midiOutMode=getConfInt("midiOutMode",DIV_MIDI_MODE_NOTE);
|
||||
if (metroVol<0.0f) metroVol=0.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,8 +54,8 @@
|
|||
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
|
||||
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev153"
|
||||
#define DIV_ENGINE_VERSION 153
|
||||
#define DIV_VERSION "dev159"
|
||||
#define DIV_ENGINE_VERSION 159
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -222,6 +223,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 {
|
||||
|
|
@ -369,8 +389,10 @@ class DivEngine {
|
|||
bool systemsRegistered;
|
||||
bool hasLoadedSomething;
|
||||
bool midiOutClock;
|
||||
bool midiOutTime;
|
||||
bool midiOutProgramChange;
|
||||
int midiOutMode;
|
||||
int midiOutTimeRate;
|
||||
int softLockCount;
|
||||
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed;
|
||||
size_t curSubSongIndex;
|
||||
|
|
@ -378,8 +400,13 @@ class DivEngine {
|
|||
double divider;
|
||||
int cycles;
|
||||
double clockDrift;
|
||||
int midiClockCycles;
|
||||
double midiClockDrift;
|
||||
int midiTimeCycles;
|
||||
double midiTimeDrift;
|
||||
int stepPlay;
|
||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, curMidiClock, curMidiTime, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||
int curMidiTimePiece, curMidiTimeCode;
|
||||
unsigned char extValue, pendingMetroTick;
|
||||
DivGroovePattern speeds;
|
||||
short tempoAccum;
|
||||
|
|
@ -404,6 +431,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];
|
||||
|
|
@ -434,6 +462,7 @@ class DivEngine {
|
|||
short tremTable[128];
|
||||
int reversePitchTable[4096];
|
||||
int pitchTable[4096];
|
||||
short effectSlotMap[4096];
|
||||
char c163NameCS[1024];
|
||||
int midiBaseChan;
|
||||
bool midiPoly;
|
||||
|
|
@ -468,6 +497,8 @@ class DivEngine {
|
|||
void recalcChans();
|
||||
void reset();
|
||||
void playSub(bool preserveDrift, int goalRow=0);
|
||||
void runMidiClock(int totalCycles=1);
|
||||
void runMidiTime(int totalCycles=1);
|
||||
|
||||
void testFunction();
|
||||
|
||||
|
|
@ -505,11 +536,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;
|
||||
|
|
@ -550,7 +591,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);
|
||||
|
|
@ -588,6 +629,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();
|
||||
|
|
@ -911,7 +954,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);
|
||||
|
|
@ -1031,6 +1074,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);
|
||||
|
||||
|
|
@ -1124,8 +1173,10 @@ class DivEngine {
|
|||
systemsRegistered(false),
|
||||
hasLoadedSomething(false),
|
||||
midiOutClock(false),
|
||||
midiOutTime(false),
|
||||
midiOutProgramChange(false),
|
||||
midiOutMode(DIV_MIDI_MODE_NOTE),
|
||||
midiOutTimeRate(0),
|
||||
softLockCount(0),
|
||||
subticks(0),
|
||||
ticks(0),
|
||||
|
|
@ -1146,16 +1197,24 @@ class DivEngine {
|
|||
divider(60),
|
||||
cycles(0),
|
||||
clockDrift(0),
|
||||
midiClockCycles(0),
|
||||
midiClockDrift(0),
|
||||
midiTimeCycles(0),
|
||||
midiTimeDrift(0),
|
||||
stepPlay(0),
|
||||
changeOrd(-1),
|
||||
changePos(0),
|
||||
totalSeconds(0),
|
||||
totalTicks(0),
|
||||
totalTicksR(0),
|
||||
curMidiClock(0),
|
||||
curMidiTime(0),
|
||||
totalCmds(0),
|
||||
lastCmds(0),
|
||||
cmdsPerSecond(0),
|
||||
globalPitch(0),
|
||||
curMidiTimePiece(0),
|
||||
curMidiTimeCode(0),
|
||||
extValue(0),
|
||||
pendingMetroTick(0),
|
||||
tempoAccum(0),
|
||||
|
|
@ -1203,6 +1262,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.");
|
||||
}
|
||||
|
|
@ -1040,6 +1042,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.systemFlags[0].set("noEasyNoise",true);
|
||||
}
|
||||
|
||||
// NES PCM
|
||||
if (ds.system[0]==DIV_SYSTEM_NES) {
|
||||
ds.systemFlags[0].set("dpcmMode",false);
|
||||
}
|
||||
|
||||
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
|
||||
|
||||
if (active) quitDispatch();
|
||||
|
|
@ -1644,18 +1651,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];
|
||||
|
|
@ -1787,6 +1834,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<138) {
|
||||
ds.brokenPortaLegato=true;
|
||||
}
|
||||
if (ds.version<155) {
|
||||
ds.brokenFMOff=true;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
|
@ -1815,8 +1865,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();
|
||||
|
|
@ -2295,7 +2343,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
if (ds.version>=138) {
|
||||
ds.brokenPortaLegato=reader.readC();
|
||||
for (int i=0; i<7; i++) {
|
||||
if (ds.version>=155) {
|
||||
ds.brokenFMOff=reader.readC();
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<6; i++) {
|
||||
reader.readC();
|
||||
}
|
||||
}
|
||||
|
|
@ -2319,6 +2372,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...");
|
||||
|
|
@ -2353,6 +2412,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++) {
|
||||
|
|
@ -2382,8 +2488,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();
|
||||
|
|
@ -2510,7 +2614,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);
|
||||
|
|
@ -2521,62 +2626,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2724,6 +2918,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// NES PCM compat
|
||||
if (ds.version<154) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_NES) {
|
||||
ds.systemFlags[i].set("dpcmMode",false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
@ -3116,9 +3319,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;
|
||||
|
|
@ -3341,7 +3542,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();
|
||||
|
||||
|
|
@ -3787,8 +3987,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;
|
||||
|
|
@ -4821,7 +5019,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;
|
||||
|
|
@ -4829,7 +5077,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="";
|
||||
|
|
@ -5128,6 +5377,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);
|
||||
|
|
@ -5215,6 +5470,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];
|
||||
|
|
@ -5240,31 +5503,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);
|
||||
|
|
@ -5306,6 +5671,12 @@ 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;
|
||||
}
|
||||
|
|
@ -5440,12 +5811,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)?1:0);
|
||||
w->writeC((intHz!=60 && intHz!=50));
|
||||
char customHz[4];
|
||||
memset(customHz,0,4);
|
||||
snprintf(customHz,4,"%d",(int)curSubSong->hz);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ 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];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]>>1;
|
||||
}
|
||||
|
||||
if (o[0]<-32768) o[0]=-32768;
|
||||
|
|
@ -111,7 +111,7 @@ 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));
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1;
|
||||
}
|
||||
|
||||
os[0]=out_ymfm.data[0];
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ void DivPlatformAY8910::runDAC() {
|
|||
bool end=false;
|
||||
bool changed=false;
|
||||
int prevOut=chan[i].dac.out;
|
||||
while (chan[i].dac.period>rate && !end) {
|
||||
while (chan[i].dac.period>dacRate && !end) {
|
||||
DivSample* s=parent->getSample(chan[i].dac.sample);
|
||||
if (s->samples<=0) {
|
||||
chan[i].dac.sample=-1;
|
||||
|
|
@ -143,7 +143,7 @@ void DivPlatformAY8910::runDAC() {
|
|||
end=true;
|
||||
break;
|
||||
}
|
||||
chan[i].dac.period-=rate;
|
||||
chan[i].dac.period-=dacRate;
|
||||
}
|
||||
if (changed && !end) {
|
||||
if (!isMuted[i]) {
|
||||
|
|
@ -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++]=sunsoftVolTable[31-(ay->lastIndx&31)]<<2;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<2;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<2;
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
|
|
@ -205,9 +205,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
|
|||
buf[1][i]=buf[0][i];
|
||||
}
|
||||
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<2;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -797,6 +797,7 @@ void DivPlatformAY8910::setFlags(const DivConfig& flags) {
|
|||
chipClock=extClock;
|
||||
rate=chipClock/extDiv;
|
||||
clockSel=false;
|
||||
dacRate=chipClock/dacRateDiv;
|
||||
} else {
|
||||
clockSel=flags.getBool("halfClock",false);
|
||||
switch (flags.getInt("clockSel",0)) {
|
||||
|
|
@ -851,6 +852,7 @@ void DivPlatformAY8910::setFlags(const DivConfig& flags) {
|
|||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/8;
|
||||
dacRate=rate;
|
||||
}
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
|
|
|
|||
|
|
@ -104,7 +104,9 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
|
||||
bool extMode;
|
||||
unsigned int extClock;
|
||||
int dacRate;
|
||||
unsigned char extDiv;
|
||||
unsigned char dacRateDiv;
|
||||
|
||||
bool stereo, sunsoft, intellivision, clockSel;
|
||||
bool ioPortA, ioPortB;
|
||||
|
|
@ -119,7 +121,6 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
short* ayBuf[3];
|
||||
size_t ayBufLen;
|
||||
|
||||
void runDAC();
|
||||
void checkWrites();
|
||||
void updateOutSel(bool immediate=false);
|
||||
|
||||
|
|
@ -127,6 +128,7 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void runDAC();
|
||||
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
|
|
@ -151,10 +153,11 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8):
|
||||
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8, unsigned char ddiv=24):
|
||||
DivDispatch(),
|
||||
extMode(useExtMode),
|
||||
extClock(eclk),
|
||||
extDiv(ediv) {}
|
||||
extDiv(ediv),
|
||||
dacRateDiv(ddiv) {}
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -186,9 +186,9 @@ void DivPlatformAY8930::acquire(short** buf, size_t len) {
|
|||
buf[1][i]=buf[0][i];
|
||||
}
|
||||
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<2;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) {
|
|||
chanOut=chan[i].waveROM[k005289.addr(i)]*(regPool[2+i]&0xf);
|
||||
out+=chanOut;
|
||||
if (writeOscBuf==0) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,18 +80,18 @@ void DivPlatformC64::acquire(short** buf, size_t len) {
|
|||
sid_fp.clock(4,&buf[0][i]);
|
||||
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_fp.lastChanOut[0]-dcOff)>>6;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>6;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>6;
|
||||
}
|
||||
} else {
|
||||
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++]=(sid.last_chan_out[0]-dcOff)>>6;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>6;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +105,7 @@ void DivPlatformC64::updateFilter() {
|
|||
}
|
||||
|
||||
void DivPlatformC64::tick(bool sysTick) {
|
||||
bool willUpdateFilter=false;
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
|
|
@ -117,10 +118,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 +157,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;
|
||||
|
|
@ -207,6 +208,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
if (willUpdateFilter) updateFilter();
|
||||
}
|
||||
|
||||
int DivPlatformC64::dispatch(DivCommand c) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -168,7 +168,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ void DivPlatformGA20::acquire(short** buf, size_t len) {
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
|
|||
buf[1][i]=gb->apu_output.final_sample.right;
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,8 +82,12 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
|
|||
void DivPlatformGB::updateWave() {
|
||||
rWrite(0x1a,0);
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=15-ws.output[((i<<1)+antiClickWavePos-1)&31];
|
||||
int nibble2=15-ws.output[((1+(i<<1))+antiClickWavePos-1)&31];
|
||||
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];
|
||||
int nibble2=ws.output[((1+(i<<1))+antiClickWavePos)&31];
|
||||
if (invertWave) {
|
||||
nibble1^=15;
|
||||
nibble2^=15;
|
||||
}
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
}
|
||||
antiClickWavePos&=31;
|
||||
|
|
@ -658,6 +662,7 @@ void DivPlatformGB::setFlags(const DivConfig& flags) {
|
|||
model=GB_MODEL_AGB;
|
||||
break;
|
||||
}
|
||||
invertWave=flags.getBool("invertWave",true);
|
||||
enoughAlready=flags.getBool("enoughAlready",false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class DivPlatformGB: public DivDispatch {
|
|||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
bool antiClickEnabled;
|
||||
bool invertWave;
|
||||
bool enoughAlready;
|
||||
unsigned char lastPan;
|
||||
DivWaveSynth ws;
|
||||
|
|
|
|||
|
|
@ -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<<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()<<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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ void DivPlatformK053260::acquire(short** buf, size_t len) {
|
|||
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))>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(k053260.voice_out(i,0)+k053260.voice_out(i,1))>>2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*10)<<6);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output*10)<<6);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output*2)<<5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ void DivPlatformN163::acquire(short** buf, size_t len) {
|
|||
buf[0][i]=out;
|
||||
|
||||
if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<7;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<6;
|
||||
}
|
||||
|
||||
// command queue
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,11 +115,11 @@ void DivPlatformNES::acquire_puNES(short** buf, size_t len) {
|
|||
buf[0][i]=sample;
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<11);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<11);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<11);
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<11);
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<8);
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<10);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<10);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<10);
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<10);
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,11 +142,11 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
|
|||
buf[0][i]=sample;
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
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[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<10;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<10;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<10;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<10;
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -211,7 +211,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
if (i==2) { // triangle
|
||||
rWrite(0x4000+i*4,(chan[i].outVol==0)?0:255);
|
||||
rWrite(0x4000+i*4,(chan[i].outVol==0)?0:linearCount);
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
rWrite(0x4000+i*4,(chan[i].envMode<<4)|chan[i].outVol|((chan[i].duty&3)<<6));
|
||||
|
|
@ -262,7 +262,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
//rWrite(16+i*5,chan[i].sweep);
|
||||
}
|
||||
}
|
||||
if (i<2) if (chan[i].std.phaseReset.had) {
|
||||
if (i<3) if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].prevFreq=-1;
|
||||
|
|
@ -337,14 +337,22 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
// write DPCM
|
||||
rWrite(0x4015,15);
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
if (nextDPCMFreq>=0) {
|
||||
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
|
||||
nextDPCMFreq=-1;
|
||||
} else {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
}
|
||||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
dpcmBank=dpcmAddr>>14;
|
||||
}
|
||||
} else {
|
||||
if (dpcmMode) {
|
||||
if (nextDPCMFreq>=0) {
|
||||
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
|
||||
nextDPCMFreq=-1;
|
||||
} else {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
}
|
||||
}
|
||||
|
|
@ -353,6 +361,8 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
if (chan[4].keyOn) chan[4].keyOn=false;
|
||||
chan[4].freqChanged=false;
|
||||
}
|
||||
|
||||
nextDPCMFreq=-1;
|
||||
}
|
||||
|
||||
int DivPlatformNES::dispatch(DivCommand c) {
|
||||
|
|
@ -401,12 +411,17 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].furnaceDac=false;
|
||||
if (dpcmMode && !skipRegisterWrites) {
|
||||
unsigned int dpcmAddr=sampleOffDPCM[dacSample];
|
||||
unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4;
|
||||
unsigned int dpcmLen=parent->getSample(dacSample)->lengthDPCM>>4;
|
||||
if (dpcmLen>255) dpcmLen=255;
|
||||
goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
// write DPCM
|
||||
rWrite(0x4015,15);
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
if (nextDPCMFreq>=0) {
|
||||
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
|
||||
nextDPCMFreq=-1;
|
||||
} else {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
}
|
||||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
|
|
@ -434,7 +449,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
if (c.chan==2) {
|
||||
rWrite(0x4000+c.chan*4,0xff);
|
||||
rWrite(0x4000+c.chan*4,linearCount);
|
||||
} else if (!parent->song.brokenOutVol2) {
|
||||
rWrite(0x4000+c.chan*4,(chan[c.chan].envMode<<4)|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
|
||||
}
|
||||
|
|
@ -466,7 +481,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
}
|
||||
if (chan[c.chan].active) {
|
||||
if (c.chan==2) {
|
||||
rWrite(0x4000+c.chan*4,0xff);
|
||||
rWrite(0x4000+c.chan*4,linearCount);
|
||||
} else {
|
||||
rWrite(0x4000+c.chan*4,(chan[c.chan].envMode<<4)|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
|
||||
}
|
||||
|
|
@ -542,6 +557,16 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
countMode=c.value;
|
||||
rWrite(0x4017,countMode?0x80:0);
|
||||
break;
|
||||
case DIV_CMD_NES_LINEAR_LENGTH:
|
||||
if (c.chan==2) {
|
||||
linearCount=c.value;
|
||||
if (chan[c.chan].active) {
|
||||
rWrite(0x4000+c.chan*4,(chan[c.chan].outVol==0)?0:linearCount);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].prevFreq=-1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_NES_DMC:
|
||||
rWrite(0x4011,c.value&0x7f);
|
||||
break;
|
||||
|
|
@ -555,6 +580,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
rWrite(0x4013,0);
|
||||
rWrite(0x4015,31);
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_FREQ: {
|
||||
bool goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
if (dpcmMode) {
|
||||
nextDPCMFreq=c.value&15;
|
||||
rWrite(0x4010,(c.value&15)|(goingToLoop?0x40:0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||
|
|
@ -655,9 +688,11 @@ void DivPlatformNES::reset() {
|
|||
dacSample=-1;
|
||||
sampleBank=0;
|
||||
dpcmBank=0;
|
||||
dpcmMode=false;
|
||||
dpcmMode=dpcmModeDefault;
|
||||
goingToLoop=false;
|
||||
countMode=false;
|
||||
nextDPCMFreq=-1;
|
||||
linearCount=255;
|
||||
|
||||
if (useNP) {
|
||||
nes1_NP->Reset();
|
||||
|
|
@ -709,6 +744,8 @@ void DivPlatformNES::setFlags(const DivConfig& flags) {
|
|||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->rate=rate/32;
|
||||
}
|
||||
|
||||
dpcmModeDefault=flags.getBool("dpcmMode",true);
|
||||
}
|
||||
|
||||
void DivPlatformNES::notifyInsDeletion(void* ins) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,10 @@ class DivPlatformNES: public DivDispatch {
|
|||
unsigned char sampleBank;
|
||||
unsigned char writeOscBuf;
|
||||
unsigned char apuType;
|
||||
unsigned char linearCount;
|
||||
signed char nextDPCMFreq;
|
||||
bool dpcmMode;
|
||||
bool dpcmModeDefault;
|
||||
bool dacAntiClickOn;
|
||||
bool useNP;
|
||||
bool goingToLoop;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -234,14 +234,13 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
|||
if (fm.channel[i].out[3]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
|
||||
}
|
||||
oscBuf[i]->data[oscBuf[i]->needle]<<=1;
|
||||
oscBuf[i]->needle++;
|
||||
}
|
||||
// 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*3;
|
||||
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*3;
|
||||
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*3;
|
||||
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*3;
|
||||
} else {
|
||||
for (int i=0; i<chans; i++) {
|
||||
unsigned char ch=outChanMap[i];
|
||||
|
|
@ -259,7 +258,6 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
|||
if (fm.channel[i].out[3]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
|
||||
}
|
||||
oscBuf[i]->data[oscBuf[i]->needle]<<=1;
|
||||
oscBuf[i]->needle++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
|
|||
unsigned char nextOut=cycleMapOPLL[fm.cycles];
|
||||
if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) {
|
||||
os+=(o[0]+o[1]);
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6;
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<5;
|
||||
} else {
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0;
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
|
|||
if (!(vrc7 || (fm.rm_enable&0x20))) for (int i=0; i<9; i++) {
|
||||
unsigned char ch=visMapOPLL[i];
|
||||
if ((i>=6 && properDrums) || !isMuted[ch]) {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<6;
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<5;
|
||||
} else {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=0;
|
||||
}
|
||||
|
|
@ -101,8 +101,17 @@ 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));
|
||||
break;
|
||||
} 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 +418,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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -85,10 +85,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ void DivPlatformPOKEY::tick(bool sysTick) {
|
|||
|
||||
for (int i=0; i<4; i++) {
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,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 +194,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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ void DivPlatformSCC::acquire(short** buf, size_t len) {
|
|||
buf[0][h]=out;
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<7;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
|||
DivSample* s=parent->getSample(chan[i].pcm.sample);
|
||||
off=(double)s->centerRate/8363.0;
|
||||
}
|
||||
chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+512)/(128.0*12.0)))*255)/31250)+(oldSlides?chan[i].pitch2:0);
|
||||
chan[i].pcm.freq=MIN(255,((rate*0.5)+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+512)/(128.0*12.0)))*255)/rate)+(oldSlides?chan[i].pitch2:0);
|
||||
rWrite(7+(i<<3),chan[i].pcm.freq);
|
||||
}
|
||||
chan[i].freqChanged=false;
|
||||
|
|
@ -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;
|
||||
|
|
@ -220,7 +220,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
rWrite(0x86+(c.chan<<3),3);
|
||||
break;
|
||||
}
|
||||
chan[c.chan].pcm.freq=MIN(255,(parent->getSample(chan[c.chan].pcm.sample)->rate*255)/31250);
|
||||
chan[c.chan].pcm.freq=MIN(255,(parent->getSample(chan[c.chan].pcm.sample)->rate*255)/rate);
|
||||
chan[c.chan].furnacePCM=false;
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
|
|
|
|||
|
|
@ -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<<5;
|
||||
}
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<6;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<5;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -202,6 +202,7 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
// TODO: if wavetable length is higher than 32, we lose precision!
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
|
||||
|
|
@ -221,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 {
|
||||
|
|
@ -300,6 +301,11 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
rWrite(0x4d,echoBits);
|
||||
writeEcho=false;
|
||||
}
|
||||
if (writeDryVol) {
|
||||
rWrite(0x0c,dryVolL);
|
||||
rWrite(0x1c,dryVolR);
|
||||
writeDryVol=false;
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].shallWriteEnv) {
|
||||
writeEnv(i);
|
||||
|
|
@ -563,6 +569,14 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
rWrite(0x3c,echoVolR);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_SNES_GLOBAL_VOL_LEFT:
|
||||
dryVolL=c.value;
|
||||
writeDryVol=true;
|
||||
break;
|
||||
case DIV_CMD_SNES_GLOBAL_VOL_RIGHT:
|
||||
dryVolR=c.value;
|
||||
writeDryVol=true;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
@ -673,6 +687,7 @@ void DivPlatformSNES::forceIns() {
|
|||
writeNoise=true;
|
||||
writePitchMod=true;
|
||||
writeEcho=true;
|
||||
writeDryVol=true;
|
||||
initEcho();
|
||||
}
|
||||
|
||||
|
|
@ -761,6 +776,10 @@ void DivPlatformSNES::reset() {
|
|||
writeNoise=false;
|
||||
writePitchMod=false;
|
||||
writeEcho=true;
|
||||
writeDryVol=false;
|
||||
|
||||
dryVolL=127;
|
||||
dryVolR=127;
|
||||
|
||||
echoDelay=initEchoDelay;
|
||||
echoFeedback=initEchoFeedback;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
unsigned char noiseFreq;
|
||||
signed char delay;
|
||||
signed char echoVolL, echoVolR, echoFeedback;
|
||||
signed char dryVolL, dryVolR;
|
||||
signed char echoFIR[8];
|
||||
unsigned char echoDelay;
|
||||
size_t sampleTableBase;
|
||||
|
|
@ -66,6 +67,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
bool writeNoise;
|
||||
bool writePitchMod;
|
||||
bool writeEcho;
|
||||
bool writeDryVol;
|
||||
bool echoOn;
|
||||
|
||||
bool initEchoOn;
|
||||
|
|
|
|||
|
|
@ -509,7 +509,7 @@ public:
|
|||
}
|
||||
|
||||
if (oscb!=NULL) {
|
||||
oscb[i]->data[oscb[i]->needle++]=oscbWrite;
|
||||
oscb[i]->data[oscb[i]->needle++]=oscbWrite>>1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = 2;
|
||||
|
||||
struct PokeyBase
|
||||
{
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ void DivPlatformSwan::acquire(short** buf, size_t len) {
|
|||
buf[0][h]=samp[0];
|
||||
buf[1][h]=samp[1];
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ void DivPlatformTIA::acquire(short** buf, size_t len) {
|
|||
}
|
||||
if (++chanOscCounter>=114) {
|
||||
chanOscCounter=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0];
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1];
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0]>>1;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1]>>1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ void DivPlatformTX81Z::acquire(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));
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1;
|
||||
}
|
||||
|
||||
os[0]=out_ymfm.data[0];
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ void DivPlatformVB::acquire(short** buf, size_t len) {
|
|||
tempL=0;
|
||||
tempR=0;
|
||||
for (int i=0; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*8;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*4;
|
||||
tempL+=vb->last_output[i][0];
|
||||
tempR+=vb->last_output[i][1];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ extern "C" {
|
|||
#define rWritePCMCtrl(d) {regPool[64]=(d); pcm_write_ctrl(pcm,d);}
|
||||
#define rWritePCMRate(d) {regPool[65]=(d); pcm_write_rate(pcm,d);}
|
||||
#define rWritePCMData(d) {regPool[66]=(d); pcm_write_fifo(pcm,d);}
|
||||
#define rWritePCMVol(d) rWritePCMCtrl((regPool[64]&(~0x3f))|((d)&0x3f))
|
||||
#define rWritePCMVol(d) rWritePCMCtrl((regPool[64]&(~0x8f))|((d)&15))
|
||||
|
||||
const char* regCheatSheetVERA[]={
|
||||
"CHxFreq", "00+x*4",
|
||||
|
|
@ -107,9 +107,9 @@ void DivPlatformVERA::acquire(short** buf, size_t len) {
|
|||
pos++;
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=psg->channels[i].lastOut<<4;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=psg->channels[i].lastOut<<3;
|
||||
}
|
||||
int pcmOut=whyCallItBuf[2][i]+whyCallItBuf[3][i];
|
||||
int pcmOut=(whyCallItBuf[2][i]+whyCallItBuf[3][i])>>1;
|
||||
if (pcmOut<-32768) pcmOut=-32768;
|
||||
if (pcmOut>32767) pcmOut=32767;
|
||||
oscBuf[16]->data[oscBuf[16]->needle++]=pcmOut;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ void DivPlatformVIC20::acquire(short** buf, size_t len) {
|
|||
vic_sound_machine_calculate_samples(vic,&samp,1,1,0,SAMP_DIVIDER);
|
||||
buf[0][h]=samp;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<11):0;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<10):0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ void DivPlatformVRC6::acquire(short** buf, size_t len) {
|
|||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<10;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<9;
|
||||
}
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<10;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<9;
|
||||
}
|
||||
|
||||
// Command part
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ void DivPlatformX1_010::acquire(short** buf, size_t len) {
|
|||
if (stereo) buf[1][h]=tempR;
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
int vo=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))<<3;
|
||||
int vo=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))<<2;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(vo,-32768,32767);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,6 +169,15 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
|||
static short ignored[2];
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
os=0;
|
||||
// Nuked part
|
||||
for (unsigned int i=0; i<nukedMult; i++) {
|
||||
|
|
@ -222,11 +231,11 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
|||
buf[0][h]=os;
|
||||
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
|
||||
}
|
||||
|
||||
for (int i=3; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -242,6 +251,15 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
os=0;
|
||||
if (!writes.empty()) {
|
||||
if (--delay<1) {
|
||||
|
|
@ -264,11 +282,11 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1));
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
}
|
||||
|
||||
for (int i=3; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -567,7 +585,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].keyOff=true;
|
||||
|
|
|
|||
|
|
@ -321,6 +321,15 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
os[0]=0; os[1]=0;
|
||||
// Nuked part
|
||||
for (int i=0; i<nukedMult; i++) {
|
||||
|
|
@ -393,19 +402,19 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1;
|
||||
}
|
||||
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -427,6 +436,15 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
os[0]=0; os[1]=0;
|
||||
if (!writes.empty()) {
|
||||
if (--delay<1) {
|
||||
|
|
@ -453,19 +471,19 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
|
|||
buf[1][h]=os[1];
|
||||
|
||||
for (int i=0; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1));
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=6; i<9; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]>>1;
|
||||
}
|
||||
|
||||
for (int i=9; i<15; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1))>>1;
|
||||
}
|
||||
|
||||
oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
oscBuf[15]->data[oscBuf[15]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1007,7 +1025,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].keyOff=true;
|
||||
|
|
@ -1675,7 +1693,7 @@ int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, const DivCo
|
|||
fm=new ymfm::ym2608(iface);
|
||||
fm->set_fidelity(ymfm::OPN_FIDELITY_MIN);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910(true,chipClock,ayDiv);
|
||||
ay=new DivPlatformAY8910(true,chipClock,ayDiv,48);
|
||||
ay->init(p,3,sugRate,ayFlags);
|
||||
ay->toggleRegisterDump(true);
|
||||
setFlags(flags);
|
||||
|
|
|
|||
|
|
@ -256,6 +256,15 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
os[0]=0; os[1]=0;
|
||||
// Nuked part
|
||||
for (int i=0; i<24; i++) {
|
||||
|
|
@ -324,19 +333,19 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]>>1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1;
|
||||
}
|
||||
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -360,6 +369,15 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
os[0]=0; os[1]=0;
|
||||
if (!writes.empty()) {
|
||||
if (--delay<1 && !(fm->read(0)&0x80)) {
|
||||
|
|
@ -386,19 +404,19 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
|
|||
buf[1][h]=os[1];
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1));
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1;
|
||||
}
|
||||
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -979,7 +997,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].keyOff=true;
|
||||
|
|
|
|||
|
|
@ -320,7 +320,16 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
os[0]=0; os[1]=0;
|
||||
|
||||
// Nuked part
|
||||
for (int i=0; i<24; i++) {
|
||||
if (!writes.empty()) {
|
||||
|
|
@ -392,19 +401,19 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1;
|
||||
}
|
||||
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -426,6 +435,15 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
os[0]=0; os[1]=0;
|
||||
if (!writes.empty()) {
|
||||
if (--delay<1 && !(fm->read(0)&0x80)) {
|
||||
|
|
@ -453,19 +471,19 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1));
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(adpcmAChan[i-adpcmAChanOffs]->get_last_out(0)+adpcmAChan[i-adpcmAChanOffs]->get_last_out(1))>>1;
|
||||
}
|
||||
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
|
||||
oscBuf[adpcmBChanOffs]->data[oscBuf[adpcmBChanOffs]->needle++]=(abe->get_last_out(0)+abe->get_last_out(1))>>1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1046,7 +1064,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
if (parent->song.brokenFMOff) chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].keyOff=true;
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
fm->set_fidelity(ymfm::OPN_FIDELITY_MED);
|
||||
setFlags(flags);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910(true,chipClock,32);
|
||||
ay=new DivPlatformAY8910(true,chipClock,32,144);
|
||||
ay->init(p,3,sugRate,ayFlags);
|
||||
ay->toggleRegisterDump(true);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ void DivPlatformYMZ280B::acquire(short** buf, size_t len) {
|
|||
for (int j=0; j<8; j++) {
|
||||
dataL+=why[j*2][i];
|
||||
dataR+=why[j*2+1][i];
|
||||
oscBuf[j]->data[oscBuf[j]->needle++]=(short)(((int)why[j*2][i]+why[j*2+1][i])/2);
|
||||
oscBuf[j]->data[oscBuf[j]->needle++]=(short)(((int)why[j*2][i]+why[j*2+1][i])/4);
|
||||
}
|
||||
buf[0][pos]=(short)(dataL/8);
|
||||
buf[1][pos]=(short)(dataR/8);
|
||||
|
|
|
|||
|
|
@ -231,6 +231,11 @@ const char* cmdName[]={
|
|||
|
||||
"HINT_ARP_TIME",
|
||||
|
||||
"SNES_GLOBAL_VOL_LEFT",
|
||||
"SNES_GLOBAL_VOL_RIGHT",
|
||||
|
||||
"NES_LINEAR_LENGTH",
|
||||
|
||||
"ALWAYS_SET_VOLUME"
|
||||
};
|
||||
|
||||
|
|
@ -412,6 +417,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
if (effectVal==-1) effectVal=0;
|
||||
effectVal&=255;
|
||||
|
||||
switch (effect) {
|
||||
case 0x09: // select groove pattern/speed 1
|
||||
|
|
@ -592,6 +598,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
if (effectVal==-1) effectVal=0;
|
||||
effectVal&=255;
|
||||
|
||||
// per-system effect
|
||||
if (!perSystemEffect(i,effect,effectVal)) switch (effect) {
|
||||
|
|
@ -774,21 +781,17 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed));
|
||||
break;
|
||||
case 0x07: // tremolo
|
||||
// TODO
|
||||
// this effect is really weird. i thought it would alter the tremolo depth but turns out it's completely different
|
||||
// this is how it works:
|
||||
// - 07xy enables tremolo
|
||||
// - when enabled, a "low" boundary is calculated based on the current volume
|
||||
// - then a volume slide down starts to the low boundary, and then when this is reached a volume slide up begins
|
||||
// - this process repeats until 0700 or 0Axy are found
|
||||
// - note that a volume value does not stop tremolo - instead it glitches this whole thing up
|
||||
if (chan[i].tremoloDepth==0) {
|
||||
chan[i].tremoloPos=0;
|
||||
}
|
||||
chan[i].tremoloDepth=effectVal&15;
|
||||
chan[i].tremoloRate=effectVal>>4;
|
||||
// tremolo and vol slides are incompatiblw
|
||||
chan[i].volSpeed=0;
|
||||
if (chan[i].tremoloDepth!=0) {
|
||||
chan[i].volSpeed=0;
|
||||
} else {
|
||||
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
||||
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
|
||||
}
|
||||
break;
|
||||
case 0x0a: // volume ramp
|
||||
// TODO: non-0x-or-x0 value should be treated as 00
|
||||
|
|
@ -1059,6 +1062,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
short effectVal=pat->data[whatRow][5+(j<<1)];
|
||||
|
||||
if (effectVal==-1) effectVal=0;
|
||||
effectVal&=255;
|
||||
perSystemPostEffect(i,effect,effectVal);
|
||||
}
|
||||
}
|
||||
|
|
@ -1198,7 +1202,7 @@ void DivEngine::nextRow() {
|
|||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0xed) {
|
||||
if (pat->data[curRow][5+(j<<1)]>0) {
|
||||
addition=pat->data[curRow][5+(j<<1)];
|
||||
addition=pat->data[curRow][5+(j<<1)]&255;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1228,7 +1232,7 @@ void DivEngine::nextRow() {
|
|||
}
|
||||
if (pat->data[curRow][4+(j<<1)]==0xed) {
|
||||
if (pat->data[curRow][5+(j<<1)]>0) {
|
||||
addition=pat->data[curRow][5+(j<<1)];
|
||||
addition=pat->data[curRow][5+(j<<1)]&255;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1309,11 +1313,6 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
if (--subticks<=0) {
|
||||
subticks=tickMult;
|
||||
|
||||
// MIDI clock
|
||||
if (output) if (!skipping && output->midiOut!=NULL && midiOutClock) {
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0));
|
||||
}
|
||||
|
||||
if (stepPlay!=1) {
|
||||
tempoAccum+=curSubSong->virtualTempoN;
|
||||
while (tempoAccum>=curSubSong->virtualTempoD) {
|
||||
|
|
@ -1383,10 +1382,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
if (chan[i].vibratoDepth>0) {
|
||||
chan[i].vibratoPos+=chan[i].vibratoRate;
|
||||
if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
|
||||
while (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
|
||||
|
||||
chan[i].vibratoPosGiant+=chan[i].vibratoRate;
|
||||
if (chan[i].vibratoPos>=512) chan[i].vibratoPos-=512;
|
||||
while (chan[i].vibratoPos>=512) chan[i].vibratoPos-=512;
|
||||
|
||||
switch (chan[i].vibratoDir) {
|
||||
case 1: // up
|
||||
|
|
@ -1543,6 +1542,147 @@ int DivEngine::getBufferPos() {
|
|||
return bufferPos>>MASTER_CLOCK_PREC;
|
||||
}
|
||||
|
||||
void DivEngine::runMidiClock(int totalCycles) {
|
||||
if (freelance) return;
|
||||
midiClockCycles-=totalCycles;
|
||||
while (midiClockCycles<=0) {
|
||||
curMidiClock++;
|
||||
if (output) if (!skipping && output->midiOut!=NULL && midiOutClock) {
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0));
|
||||
}
|
||||
|
||||
double hl=curSubSong->hilightA;
|
||||
if (hl<=0.0) hl=4.0;
|
||||
double timeBase=curSubSong->timeBase+1;
|
||||
double speedSum=0;
|
||||
double vD=curSubSong->virtualTempoD;
|
||||
for (int i=0; i<MIN(16,speeds.len); i++) {
|
||||
speedSum+=speeds.val[i];
|
||||
}
|
||||
speedSum/=MAX(1,speeds.len);
|
||||
if (timeBase<1.0) timeBase=1.0;
|
||||
if (speedSum<1.0) speedSum=1.0;
|
||||
if (vD<1) vD=1;
|
||||
double bpm=((24.0*divider)/(timeBase*hl*speedSum))*(double)curSubSong->virtualTempoN/vD;
|
||||
|
||||
midiClockCycles+=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm);
|
||||
midiClockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(bpm));
|
||||
if (midiClockDrift>=(bpm)) {
|
||||
midiClockDrift-=(bpm);
|
||||
midiClockCycles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivEngine::runMidiTime(int totalCycles) {
|
||||
if (freelance) return;
|
||||
midiTimeCycles-=totalCycles;
|
||||
while (midiTimeCycles<=0) {
|
||||
if (curMidiTimePiece==0) {
|
||||
curMidiTimeCode=curMidiTime;
|
||||
}
|
||||
if (!(curMidiTimePiece&3)) curMidiTime++;
|
||||
|
||||
double frameRate=96.0;
|
||||
int timeRate=midiOutTimeRate;
|
||||
if (timeRate<1 || timeRate>4) {
|
||||
if (curSubSong->hz>=47.98 && curSubSong->hz<=48.02) {
|
||||
timeRate=1;
|
||||
} else if (curSubSong->hz>=49.98 && curSubSong->hz<=50.02) {
|
||||
timeRate=2;
|
||||
} else if (curSubSong->hz>=59.9 && curSubSong->hz<=60.11) {
|
||||
timeRate=4;
|
||||
} else {
|
||||
timeRate=4;
|
||||
}
|
||||
}
|
||||
|
||||
int hour=0;
|
||||
int minute=0;
|
||||
int second=0;
|
||||
int frame=0;
|
||||
int drop=0;
|
||||
int actualTime=curMidiTimeCode;
|
||||
|
||||
switch (timeRate) {
|
||||
case 1: // 24
|
||||
frameRate=96.0;
|
||||
hour=(actualTime/(60*60*24))%24;
|
||||
minute=(actualTime/(60*24))%60;
|
||||
second=(actualTime/24)%60;
|
||||
frame=actualTime%24;
|
||||
break;
|
||||
case 2: // 25
|
||||
frameRate=100.0;
|
||||
hour=(actualTime/(60*60*25))%24;
|
||||
minute=(actualTime/(60*25))%60;
|
||||
second=(actualTime/25)%60;
|
||||
frame=actualTime%25;
|
||||
break;
|
||||
case 3: // 29.97 (NTSC drop)
|
||||
frameRate=120.0*(1000.0/1001.0);
|
||||
|
||||
// drop
|
||||
drop=((actualTime/(30*60))-(actualTime/(30*600)))*2;
|
||||
actualTime+=drop;
|
||||
|
||||
hour=(actualTime/(60*60*30))%24;
|
||||
minute=(actualTime/(60*30))%60;
|
||||
second=(actualTime/30)%60;
|
||||
frame=actualTime%30;
|
||||
break;
|
||||
case 4: // 30 (NTSC non-drop)
|
||||
default:
|
||||
frameRate=120.0;
|
||||
hour=(actualTime/(60*60*30))%24;
|
||||
minute=(actualTime/(60*30))%60;
|
||||
second=(actualTime/30)%60;
|
||||
frame=actualTime%30;
|
||||
break;
|
||||
}
|
||||
|
||||
if (output) if (!skipping && output->midiOut!=NULL && midiOutTime) {
|
||||
unsigned char val=0;
|
||||
switch (curMidiTimePiece) {
|
||||
case 0:
|
||||
val=frame&15;
|
||||
break;
|
||||
case 1:
|
||||
val=frame>>4;
|
||||
break;
|
||||
case 2:
|
||||
val=second&15;
|
||||
break;
|
||||
case 3:
|
||||
val=second>>4;
|
||||
break;
|
||||
case 4:
|
||||
val=minute&15;
|
||||
break;
|
||||
case 5:
|
||||
val=minute>>4;
|
||||
break;
|
||||
case 6:
|
||||
val=hour&15;
|
||||
break;
|
||||
case 7:
|
||||
val=(hour>>4)|((timeRate-1)<<1);
|
||||
break;
|
||||
}
|
||||
val|=curMidiTimePiece<<4;
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_MTC_FRAME,val,0));
|
||||
}
|
||||
curMidiTimePiece=(curMidiTimePiece+1)&7;
|
||||
|
||||
midiTimeCycles+=got.rate*pow(2,MASTER_CLOCK_PREC)/(frameRate);
|
||||
midiTimeDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(frameRate));
|
||||
if (midiTimeDrift>=(frameRate)) {
|
||||
midiTimeDrift-=(frameRate);
|
||||
midiTimeCycles++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size) {
|
||||
lastLoopPos=-1;
|
||||
|
||||
|
|
@ -1827,7 +1967,18 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
pendingMetroTick=0;
|
||||
}
|
||||
} else {
|
||||
// 3. tick the clock and fill buffers as needed
|
||||
// 3. run MIDI clock
|
||||
int midiTotal=MIN(cycles,runLeftG);
|
||||
for (int i=0; i<midiTotal; i++) {
|
||||
runMidiClock();
|
||||
}
|
||||
|
||||
// 4. run MIDI timecode
|
||||
for (int i=0; i<midiTotal; i++) {
|
||||
runMidiTime();
|
||||
}
|
||||
|
||||
// 5. tick the clock and fill buffers as needed
|
||||
if (cycles<runLeftG) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
int total=(cycles*disCont[i].runtotal)/(size<<MASTER_CLOCK_PREC);
|
||||
|
|
@ -1969,7 +2120,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
for (size_t i=0; i<size; i++) {
|
||||
float chanSum=out[0][i];
|
||||
for (int j=1; j<outChans; j++) {
|
||||
chanSum=out[j][i];
|
||||
chanSum+=out[j][i];
|
||||
}
|
||||
out[0][i]=chanSum/outChans;
|
||||
for (int j=1; j<outChans; j++) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "sample.h"
|
||||
#include "../ta-log.h"
|
||||
#include "../fileutils.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_SNDFILE
|
||||
|
|
@ -53,7 +54,7 @@ void DivSample::putSampleData(SafeWriter* w) {
|
|||
w->writeC(depth);
|
||||
w->writeC(loopMode);
|
||||
w->writeC(brrEmphasis);
|
||||
w->writeC(0); // reserved
|
||||
w->writeC(dither);
|
||||
w->writeI(loop?loopStart:-1);
|
||||
w->writeI(loop?loopEnd:-1);
|
||||
|
||||
|
|
@ -130,8 +131,11 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
|
|||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
// reserved
|
||||
reader.readC();
|
||||
if (version>=159) {
|
||||
dither=reader.readC()&1;
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
|
||||
loopStart=reader.readI();
|
||||
loopEnd=reader.readI();
|
||||
|
|
@ -444,6 +448,35 @@ bool DivSample::save(const char* path) {
|
|||
#endif
|
||||
}
|
||||
|
||||
bool DivSample::saveRaw(const char* path) {
|
||||
if (samples<1) {
|
||||
logE("sample is empty though!");
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* f=ps_fopen(path,"wb");
|
||||
if (f==NULL) {
|
||||
logE("could not save sample: %s!",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
if (isLoopable()) {
|
||||
unsigned short loopPos=getLoopStartPosition(DIV_SAMPLE_DEPTH_BRR);
|
||||
fputc(loopPos&0xff,f);
|
||||
fputc(loopPos>>8,f);
|
||||
} else {
|
||||
fputc(0,f);
|
||||
fputc(0,f);
|
||||
}
|
||||
}
|
||||
|
||||
if (fwrite(getCurBuf(),1,getCurBufLen(),f)!=getCurBufLen()) {
|
||||
logW("did not write entire instrument!");
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.
|
||||
bool DivSample::initInternal(DivSampleDepth d, int count) {
|
||||
switch (d) {
|
||||
|
|
@ -455,9 +488,9 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
|
|||
break;
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM: // DPCM
|
||||
if (dataDPCM!=NULL) delete[] dataDPCM;
|
||||
lengthDPCM=(count+7)/8;
|
||||
lengthDPCM=1+((((count+7)/8)+15)&(~15));
|
||||
dataDPCM=new unsigned char[lengthDPCM];
|
||||
memset(dataDPCM,0,lengthDPCM);
|
||||
memset(dataDPCM,0xaa,lengthDPCM);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: // YMZ ADPCM
|
||||
if (dataZ!=NULL) delete[] dataZ;
|
||||
|
|
@ -554,7 +587,34 @@ bool DivSample::strip(unsigned int begin, unsigned int end) {
|
|||
if (begin>samples) begin=samples;
|
||||
if (end>samples) end=samples;
|
||||
int count=samples-(end-begin);
|
||||
if (count<=0) return resize(0);
|
||||
if (count<=0) {
|
||||
loopStart=-1;
|
||||
loopEnd=-1;
|
||||
loop=false;
|
||||
return resize(0);
|
||||
}
|
||||
if (loopStart>(int)begin && loopEnd<(int)end) {
|
||||
loopStart=-1;
|
||||
loopEnd=-1;
|
||||
loop=false;
|
||||
} else {
|
||||
if (loopStart<(int)end && loopStart>(int)begin) {
|
||||
loopStart=end;
|
||||
}
|
||||
if (loopStart>(int)begin && loopEnd>(int)begin) {
|
||||
loopStart-=end-begin;
|
||||
loopEnd-=end-begin;
|
||||
if (loopEnd<0) loopEnd=0;
|
||||
if (loopStart<0) loopStart=0;
|
||||
} else if (loopEnd>(int)begin) {
|
||||
loopEnd=begin;
|
||||
}
|
||||
}
|
||||
if (loopStart>loopEnd) {
|
||||
loopStart=-1;
|
||||
loopEnd=-1;
|
||||
loop=false;
|
||||
}
|
||||
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||
if (data8!=NULL) {
|
||||
signed char* oldData8=data8;
|
||||
|
|
@ -599,6 +659,16 @@ bool DivSample::trim(unsigned int begin, unsigned int end) {
|
|||
int count=end-begin;
|
||||
if (count==0) return true;
|
||||
if (begin==0 && end==samples) return true;
|
||||
if (((int)begin<loopStart && (int)end<loopStart) || ((int)begin>loopEnd && (int)end>loopEnd)) {
|
||||
loopStart=-1;
|
||||
loopEnd=-1;
|
||||
loop=false;
|
||||
} else {
|
||||
loopStart-=begin;
|
||||
loopEnd-=begin;
|
||||
if (loopStart<0) loopStart=0;
|
||||
if (loopEnd>count) loopEnd=count;
|
||||
}
|
||||
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||
if (data8!=NULL) {
|
||||
signed char* oldData8=data8;
|
||||
|
|
@ -669,9 +739,43 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void DivSample::convert(DivSampleDepth newDepth) {
|
||||
render();
|
||||
depth=newDepth;
|
||||
switch (depth) {
|
||||
case DIV_SAMPLE_DEPTH_1BIT:
|
||||
setSampleCount((samples+7)&(~7));
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
|
||||
setSampleCount((1+((((samples+7)/8)+15)&(~15)))<<3);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
|
||||
setSampleCount(((lengthZ+3)&(~0x03))*2);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM
|
||||
setSampleCount((samples+1)&(~1));
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_A: // ADPCM-A
|
||||
setSampleCount((samples+1)&(~1));
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_ADPCM_B: // ADPCM-B
|
||||
setSampleCount((samples+1)&(~1));
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_BRR: // BRR
|
||||
setSampleCount(16*(lengthBRR/9));
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_VOX: // VOX
|
||||
setSampleCount((samples+1)&(~1));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
#define RESAMPLE_BEGIN \
|
||||
if (samples<1) return true; \
|
||||
int finalCount=(double)samples*(r/(double)rate); \
|
||||
int finalCount=(double)samples*(tRate/sRate); \
|
||||
signed char* oldData8=data8; \
|
||||
short* oldData16=data16; \
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) { \
|
||||
|
|
@ -689,10 +793,10 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
|
|||
}
|
||||
|
||||
#define RESAMPLE_END \
|
||||
if (loopStart>=0) loopStart=(double)loopStart*(r/(double)rate); \
|
||||
if (loopEnd>=0) loopEnd=(double)loopEnd*(r/(double)rate); \
|
||||
centerRate=(int)((double)centerRate*(r/(double)rate)); \
|
||||
rate=r; \
|
||||
if (loopStart>=0) loopStart=(double)loopStart*(tRate/sRate); \
|
||||
if (loopEnd>=0) loopEnd=(double)loopEnd*(tRate/sRate); \
|
||||
centerRate=(int)((double)centerRate*(tRate/sRate)); \
|
||||
rate=(int)((double)rate*(tRate/sRate)); \
|
||||
samples=finalCount; \
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) { \
|
||||
delete[] oldData16; \
|
||||
|
|
@ -700,12 +804,12 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
|
|||
delete[] oldData8; \
|
||||
}
|
||||
|
||||
bool DivSample::resampleNone(double r) {
|
||||
bool DivSample::resampleNone(double sRate, double tRate) {
|
||||
RESAMPLE_BEGIN;
|
||||
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
for (int i=0; i<finalCount; i++) {
|
||||
unsigned int pos=(unsigned int)((double)i*((double)rate/r));
|
||||
unsigned int pos=(unsigned int)((double)i*(sRate/tRate));
|
||||
if (pos>=samples) {
|
||||
data16[i]=0;
|
||||
} else {
|
||||
|
|
@ -714,7 +818,7 @@ bool DivSample::resampleNone(double r) {
|
|||
}
|
||||
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||
for (int i=0; i<finalCount; i++) {
|
||||
unsigned int pos=(unsigned int)((double)i*((double)rate/r));
|
||||
unsigned int pos=(unsigned int)((double)i*(sRate/tRate));
|
||||
if (pos>=samples) {
|
||||
data8[i]=0;
|
||||
} else {
|
||||
|
|
@ -727,12 +831,12 @@ bool DivSample::resampleNone(double r) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivSample::resampleLinear(double r) {
|
||||
bool DivSample::resampleLinear(double sRate, double tRate) {
|
||||
RESAMPLE_BEGIN;
|
||||
|
||||
double posFrac=0;
|
||||
unsigned int posInt=0;
|
||||
double factor=(double)rate/r;
|
||||
double factor=sRate/tRate;
|
||||
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
for (int i=0; i<finalCount; i++) {
|
||||
|
|
@ -766,12 +870,12 @@ bool DivSample::resampleLinear(double r) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivSample::resampleCubic(double r) {
|
||||
bool DivSample::resampleCubic(double sRate, double tRate) {
|
||||
RESAMPLE_BEGIN;
|
||||
|
||||
double posFrac=0;
|
||||
unsigned int posInt=0;
|
||||
double factor=(double)rate/r;
|
||||
double factor=sRate/tRate;
|
||||
float* cubicTable=DivFilterTables::getCubicTable();
|
||||
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
|
|
@ -820,12 +924,12 @@ bool DivSample::resampleCubic(double r) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivSample::resampleBlep(double r) {
|
||||
bool DivSample::resampleBlep(double sRate, double tRate) {
|
||||
RESAMPLE_BEGIN;
|
||||
|
||||
double posFrac=0;
|
||||
unsigned int posInt=0;
|
||||
double factor=r/(double)rate;
|
||||
double factor=tRate/sRate;
|
||||
float* sincITable=DivFilterTables::getSincIntegralTable();
|
||||
|
||||
float* floatData=new float[finalCount];
|
||||
|
|
@ -904,12 +1008,12 @@ bool DivSample::resampleBlep(double r) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivSample::resampleSinc(double r) {
|
||||
bool DivSample::resampleSinc(double sRate, double tRate) {
|
||||
RESAMPLE_BEGIN;
|
||||
|
||||
double posFrac=0;
|
||||
unsigned int posInt=0;
|
||||
double factor=(double)rate/r;
|
||||
double factor=sRate/tRate;
|
||||
float* sincTable=DivFilterTables::getSincTable();
|
||||
float s[16];
|
||||
|
||||
|
|
@ -971,29 +1075,29 @@ bool DivSample::resampleSinc(double r) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivSample::resample(double r, int filter) {
|
||||
bool DivSample::resample(double sRate, double tRate, int filter) {
|
||||
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) return false;
|
||||
switch (filter) {
|
||||
case DIV_RESAMPLE_NONE:
|
||||
return resampleNone(r);
|
||||
return resampleNone(sRate,tRate);
|
||||
break;
|
||||
case DIV_RESAMPLE_LINEAR:
|
||||
return resampleLinear(r);
|
||||
return resampleLinear(sRate,tRate);
|
||||
break;
|
||||
case DIV_RESAMPLE_CUBIC:
|
||||
return resampleCubic(r);
|
||||
return resampleCubic(sRate,tRate);
|
||||
break;
|
||||
case DIV_RESAMPLE_BLEP:
|
||||
return resampleBlep(r);
|
||||
return resampleBlep(sRate,tRate);
|
||||
break;
|
||||
case DIV_RESAMPLE_SINC:
|
||||
return resampleSinc(r);
|
||||
return resampleSinc(sRate,tRate);
|
||||
break;
|
||||
case DIV_RESAMPLE_BEST:
|
||||
if (r>rate) {
|
||||
return resampleSinc(r);
|
||||
if (tRate>sRate) {
|
||||
return resampleSinc(sRate,tRate);
|
||||
} else {
|
||||
return resampleBlep(r);
|
||||
return resampleBlep(sRate,tRate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1062,12 +1166,14 @@ void DivSample::render(unsigned int formatMask) {
|
|||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_1BIT_DPCM)) { // DPCM
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_1BIT_DPCM,samples)) return;
|
||||
int accum=63;
|
||||
int next=63;
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
int next=((unsigned short)(data16[i]^0x8000))>>9;
|
||||
next=((unsigned short)(data16[i]^0x8000))>>9;
|
||||
if (next>accum) {
|
||||
dataDPCM[i>>3]|=1<<(i&7);
|
||||
accum++;
|
||||
} else {
|
||||
dataDPCM[i>>3]&=~(1<<(i&7));
|
||||
accum--;
|
||||
}
|
||||
if (accum<0) accum=0;
|
||||
|
|
@ -1093,13 +1199,29 @@ void DivSample::render(unsigned int formatMask) {
|
|||
}
|
||||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_8BIT)) { // 8-bit PCM
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_8BIT,samples)) return;
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
data8[i]=data16[i]>>8;
|
||||
if (dither) {
|
||||
unsigned short lfsr=0x6438;
|
||||
unsigned short lfsr1=0x1283;
|
||||
signed char errorLast=0;
|
||||
signed char errorCur=0;
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
signed char val=CLAMP(data16[i]+128,-32768,32767)>>8;
|
||||
errorLast=errorCur;
|
||||
errorCur=(val<<8)-data16[i];
|
||||
data8[i]=CLAMP(val-((((errorLast+errorCur)>>1)+(lfsr&0xff))>>8),-128,127);
|
||||
lfsr=(lfsr<<1)|(((lfsr>>1)^(lfsr>>2)^(lfsr>>4)^(lfsr>>15))&1);
|
||||
lfsr1=(lfsr1<<1)|(((lfsr1>>1)^(lfsr1>>2)^(lfsr1>>4)^(lfsr1>>15))&1);
|
||||
}
|
||||
} else {
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
data8[i]=data16[i]>>8;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_BRR)) { // BRR
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_BRR,samples)) return;
|
||||
brrEncode(data16,dataBRR,samples,loop?loopStart:-1,brrEmphasis);
|
||||
int sampleCount=loop?loopEnd:samples;
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_BRR,sampleCount)) return;
|
||||
brrEncode(data16,dataBRR,sampleCount,loop?loopStart:-1,brrEmphasis);
|
||||
}
|
||||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_VOX)) { // VOX
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_VOX,samples)) return;
|
||||
|
|
@ -1173,9 +1295,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
|
|||
duplicate=new unsigned char[getCurBufLen()];
|
||||
memcpy(duplicate,getCurBuf(),getCurBufLen());
|
||||
}
|
||||
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,loopMode);
|
||||
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,dither,loopMode);
|
||||
} else {
|
||||
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,loopMode);
|
||||
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,dither,loopMode);
|
||||
}
|
||||
if (!doNotPush) {
|
||||
while (!redoHist.empty()) {
|
||||
|
|
@ -1208,6 +1330,8 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
|
|||
loopStart=h->loopStart; \
|
||||
loopEnd=h->loopEnd; \
|
||||
loop=h->loop; \
|
||||
brrEmphasis=h->brrEmphasis; \
|
||||
dither=h->dither; \
|
||||
loopMode=h->loopMode;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ struct DivSampleHistory {
|
|||
unsigned int length, samples;
|
||||
DivSampleDepth depth;
|
||||
int rate, centerRate, loopStart, loopEnd;
|
||||
bool loop, brrEmphasis;
|
||||
bool loop, brrEmphasis, dither;
|
||||
DivSampleLoopMode loopMode;
|
||||
bool hasSample;
|
||||
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, DivSampleLoopMode lm):
|
||||
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool di, DivSampleLoopMode lm):
|
||||
data((unsigned char*)d),
|
||||
length(l),
|
||||
samples(s),
|
||||
|
|
@ -75,9 +75,10 @@ struct DivSampleHistory {
|
|||
loopEnd(le),
|
||||
loop(lp),
|
||||
brrEmphasis(be),
|
||||
dither(di),
|
||||
loopMode(lm),
|
||||
hasSample(true) {}
|
||||
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, DivSampleLoopMode lm):
|
||||
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool di, DivSampleLoopMode lm):
|
||||
data(NULL),
|
||||
length(0),
|
||||
samples(0),
|
||||
|
|
@ -88,6 +89,7 @@ struct DivSampleHistory {
|
|||
loopEnd(le),
|
||||
loop(lp),
|
||||
brrEmphasis(be),
|
||||
dither(di),
|
||||
loopMode(lm),
|
||||
hasSample(false) {}
|
||||
~DivSampleHistory();
|
||||
|
|
@ -108,7 +110,7 @@ struct DivSample {
|
|||
// - 10: VOX ADPCM
|
||||
// - 16: 16-bit PCM
|
||||
DivSampleDepth depth;
|
||||
bool loop, brrEmphasis;
|
||||
bool loop, brrEmphasis, dither;
|
||||
// valid values are:
|
||||
// - 0: Forward loop
|
||||
// - 1: Backward loop
|
||||
|
|
@ -188,11 +190,11 @@ struct DivSample {
|
|||
/**
|
||||
* @warning DO NOT USE - internal functions
|
||||
*/
|
||||
bool resampleNone(double rate);
|
||||
bool resampleLinear(double rate);
|
||||
bool resampleCubic(double rate);
|
||||
bool resampleBlep(double rate);
|
||||
bool resampleSinc(double rate);
|
||||
bool resampleNone(double sRate, double tRate);
|
||||
bool resampleLinear(double sRate, double tRate);
|
||||
bool resampleCubic(double sRate, double tRate);
|
||||
bool resampleBlep(double sRate, double tRate);
|
||||
bool resampleSinc(double sRate, double tRate);
|
||||
|
||||
/**
|
||||
* save this sample to a file.
|
||||
|
|
@ -201,6 +203,13 @@ struct DivSample {
|
|||
*/
|
||||
bool save(const char* path);
|
||||
|
||||
/**
|
||||
* save this sample to a file (raw).
|
||||
* @param path a path.
|
||||
* @return whether saving succeeded or not.
|
||||
*/
|
||||
bool saveRaw(const char* path);
|
||||
|
||||
/**
|
||||
* @warning DO NOT USE - internal function
|
||||
* initialize sample data.
|
||||
|
|
@ -255,11 +264,19 @@ struct DivSample {
|
|||
/**
|
||||
* change the sample rate.
|
||||
* @warning do not attempt to resample outside of a synchronized block!
|
||||
* @param rate number of samples.
|
||||
* @param sRate source rate.
|
||||
* @param tRate target rate.
|
||||
* @param filter the interpolation filter.
|
||||
* @return whether it was successful.
|
||||
*/
|
||||
bool resample(double rate, int filter);
|
||||
bool resample(double sRate, double tRate, int filter);
|
||||
|
||||
/**
|
||||
* convert sample depth.
|
||||
* @warning do not attempt to do this outside of a synchronized block!
|
||||
* @param newDepth the new depth.
|
||||
*/
|
||||
void convert(DivSampleDepth newDepth);
|
||||
|
||||
/**
|
||||
* initialize the rest of sample formats for this sample.
|
||||
|
|
@ -308,6 +325,7 @@ struct DivSample {
|
|||
depth(DIV_SAMPLE_DEPTH_16BIT),
|
||||
loop(false),
|
||||
brrEmphasis(true),
|
||||
dither(false),
|
||||
loopMode(DIV_SAMPLE_LOOP_FORWARD),
|
||||
data8(NULL),
|
||||
data16(NULL),
|
||||
|
|
|
|||
|
|
@ -131,6 +131,14 @@ enum DivSystem {
|
|||
DIV_SYSTEM_K053260
|
||||
};
|
||||
|
||||
enum DivEffectType: unsigned short {
|
||||
DIV_EFFECT_NULL=0,
|
||||
DIV_EFFECT_DUMMY,
|
||||
DIV_EFFECT_EXTERNAL,
|
||||
DIV_EFFECT_VOLUME,
|
||||
DIV_EFFECT_FILTER
|
||||
};
|
||||
|
||||
struct DivGroovePattern {
|
||||
unsigned char val[16];
|
||||
unsigned char len;
|
||||
|
|
@ -146,8 +154,6 @@ struct DivSubSong {
|
|||
unsigned char timeBase, arpLen;
|
||||
DivGroovePattern speeds;
|
||||
short virtualTempoN, virtualTempoD;
|
||||
bool pal;
|
||||
bool customTempo;
|
||||
float hz;
|
||||
int patLen, ordersLen;
|
||||
|
||||
|
|
@ -170,8 +176,6 @@ struct DivSubSong {
|
|||
arpLen(1),
|
||||
virtualTempoN(150),
|
||||
virtualTempoD(150),
|
||||
pal(true),
|
||||
customTempo(false),
|
||||
hz(60.0),
|
||||
patLen(64),
|
||||
ordersLen(1) {
|
||||
|
|
@ -192,6 +196,21 @@ struct DivAssetDir {
|
|||
name(n) {}
|
||||
};
|
||||
|
||||
struct DivEffectStorage {
|
||||
DivEffectType id;
|
||||
unsigned short slot, storageVer;
|
||||
float dryWet;
|
||||
unsigned char* storage;
|
||||
size_t storageLen;
|
||||
DivEffectStorage():
|
||||
id(DIV_EFFECT_NULL),
|
||||
slot(0),
|
||||
storageVer(0),
|
||||
dryWet(1.0f),
|
||||
storage(NULL),
|
||||
storageLen(0) {}
|
||||
};
|
||||
|
||||
struct DivSong {
|
||||
// version number used for saving the song.
|
||||
// Furnace will save using the latest possible version,
|
||||
|
|
@ -353,6 +372,7 @@ struct DivSong {
|
|||
bool oldArpStrategy;
|
||||
bool patchbayAuto;
|
||||
bool brokenPortaLegato;
|
||||
bool brokenFMOff;
|
||||
|
||||
std::vector<DivInstrument*> ins;
|
||||
std::vector<DivWavetable*> wave;
|
||||
|
|
@ -366,6 +386,8 @@ struct DivSong {
|
|||
std::vector<DivAssetDir> waveDir;
|
||||
std::vector<DivAssetDir> sampleDir;
|
||||
|
||||
std::vector<DivEffectStorage> effects;
|
||||
|
||||
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound;
|
||||
DivWavetable nullWave;
|
||||
DivSample nullSample;
|
||||
|
|
@ -468,7 +490,8 @@ struct DivSong {
|
|||
autoSystem(true),
|
||||
oldArpStrategy(false),
|
||||
patchbayAuto(true),
|
||||
brokenPortaLegato(false) {
|
||||
brokenPortaLegato(false),
|
||||
brokenFMOff(false) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
system[i]=DIV_SYSTEM_NULL;
|
||||
systemVol[i]=1.0;
|
||||
|
|
|
|||
|
|
@ -699,8 +699,8 @@ void DivEngine::registerSystems() {
|
|||
sysDefs[DIV_SYSTEM_NES]=new DivSysDef(
|
||||
"NES (Ricoh 2A03)", NULL, 0x06, 0x06, 5, false, true, 0x161, false, (1U<<DIV_SAMPLE_DEPTH_1BIT_DPCM)|(1U<<DIV_SAMPLE_DEPTH_8BIT),
|
||||
"also known as Famicom in Japan, it's the most well-known game console of the '80's.",
|
||||
{"Pulse 1", "Pulse 2", "Triangle", "Noise", "PCM"},
|
||||
{"S1", "S2", "TR", "NO", "PCM"},
|
||||
{"Pulse 1", "Pulse 2", "Triangle", "Noise", "DPCM"},
|
||||
{"S1", "S2", "TR", "NO", "DMC"},
|
||||
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM},
|
||||
{DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_AMIGA},
|
||||
{},
|
||||
|
|
@ -712,7 +712,9 @@ void DivEngine::registerSystems() {
|
|||
{0x15, {DIV_CMD_NES_ENV_MODE, "15xx: Set envelope mode (0: envelope, 1: length, 2: looping, 3: constant)"}},
|
||||
{0x16, {DIV_CMD_NES_LENGTH, "16xx: Set length counter (refer to manual for a list of values)"}},
|
||||
{0x17, {DIV_CMD_NES_COUNT_MODE, "17xx: Set frame counter mode (0: 4-step, 1: 5-step)"}},
|
||||
{0x18, {DIV_CMD_SAMPLE_MODE, "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)"}}
|
||||
{0x18, {DIV_CMD_SAMPLE_MODE, "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)"}},
|
||||
{0x19, {DIV_CMD_NES_LINEAR_LENGTH, "19xx: Set triangle linear counter (0 to 7F; 80 and higher halt)"}},
|
||||
{0x20, {DIV_CMD_SAMPLE_FREQ, "20xx: Set DPCM frequency (0 to F)"}}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -911,6 +913,8 @@ void DivEngine::registerSystems() {
|
|||
{0x1a, {DIV_CMD_SNES_ECHO_VOL_LEFT, "1Axx: Set left echo volume"}},
|
||||
{0x1b, {DIV_CMD_SNES_ECHO_VOL_RIGHT, "1Bxx: Set right echo volume"}},
|
||||
{0x1c, {DIV_CMD_SNES_ECHO_FEEDBACK, "1Cxx: Set echo feedback"}},
|
||||
{0x1e, {DIV_CMD_SNES_GLOBAL_VOL_LEFT, "1Exx: Set dry output volume (left)"}},
|
||||
{0x1f, {DIV_CMD_SNES_GLOBAL_VOL_RIGHT, "1Fxx: Set dry output volume (right)"}},
|
||||
{0x30, {DIV_CMD_SNES_ECHO_FIR, "30xx: Set echo filter coefficient 0",constVal<0>,effectVal}},
|
||||
{0x31, {DIV_CMD_SNES_ECHO_FIR, "31xx: Set echo filter coefficient 1",constVal<1>,effectVal}},
|
||||
{0x32, {DIV_CMD_SNES_ECHO_FIR, "32xx: Set echo filter coefficient 2",constVal<2>,effectVal}},
|
||||
|
|
@ -1848,7 +1852,7 @@ void DivEngine::registerSystems() {
|
|||
|
||||
sysDefs[DIV_SYSTEM_SFX_BEEPER_QUADTONE]=new DivSysDef(
|
||||
"ZX Spectrum Beeper (QuadTone Engine)", NULL, 0xca, 0, 5, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"Another ZX Spectrum beeper system with full PWM pulses and 3-level volume per channel. It also has a pitchable overlay sample channel.",
|
||||
"another ZX Spectrum beeper system with full PWM pulses and 3-level volume per channel. it also has a pitchable overlay sample channel.",
|
||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "PCM"},
|
||||
{"CH1", "CH2", "CH3", "CH4", "PCM"},
|
||||
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM},
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0x95);
|
||||
w->writeC(streamID);
|
||||
w->writeS(write.val); // sample number
|
||||
w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags
|
||||
w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags
|
||||
if (sample->isLoopable() && !sampleDir[streamID]) {
|
||||
loopTimer[streamID]=sample->length8;
|
||||
loopSample[streamID]=write.val;
|
||||
|
|
@ -620,7 +620,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0x95);
|
||||
w->writeC(streamID);
|
||||
w->writeS(pendingFreq[streamID]); // sample number
|
||||
w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags
|
||||
w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags
|
||||
if (sample->isLoopable() && !sampleDir[streamID]) {
|
||||
loopTimer[streamID]=sample->length8;
|
||||
loopSample[streamID]=pendingFreq[streamID];
|
||||
|
|
@ -1657,6 +1657,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
}
|
||||
}
|
||||
|
||||
// variable set but not used?
|
||||
logV("howManyChips: %d",howManyChips);
|
||||
|
||||
// write chips and stuff
|
||||
w->writeI(hasSN);
|
||||
w->writeI(hasOPLL);
|
||||
|
|
@ -2135,6 +2138,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
if (nextTick(false,true)) {
|
||||
if (trailing) beenOneLoopAlready=true;
|
||||
trailing=true;
|
||||
if (!loop) countDown=0;
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (!willExport[dispatchOfChan[i]]) continue;
|
||||
chan[i].wentThroughNote=false;
|
||||
|
|
@ -2246,7 +2250,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
int delay=i.second.time-lastOne;
|
||||
if (delay>16) {
|
||||
w->writeC(0x61);
|
||||
w->writeS(totalWait);
|
||||
w->writeS(delay);
|
||||
} else if (delay>0) {
|
||||
w->writeC(0x70+delay-1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "song.h"
|
||||
|
||||
DivZSM::DivZSM() {
|
||||
w = NULL;
|
||||
w=NULL;
|
||||
init();
|
||||
}
|
||||
|
||||
|
|
@ -31,8 +31,8 @@ DivZSM::~DivZSM() {
|
|||
}
|
||||
|
||||
void DivZSM::init(unsigned int rate) {
|
||||
if (w != NULL) delete w;
|
||||
w = new SafeWriter;
|
||||
if (w!=NULL) delete w;
|
||||
w=new SafeWriter;
|
||||
w->init();
|
||||
// write default ZSM data header
|
||||
w->write("zm",2); // magic header
|
||||
|
|
@ -50,7 +50,7 @@ void DivZSM::init(unsigned int rate) {
|
|||
w->writeS((unsigned short)rate);
|
||||
// 2 reserved bytes (set to zero)
|
||||
w->writeS(0x00);
|
||||
tickRate = rate;
|
||||
tickRate=rate;
|
||||
loopOffset=-1;
|
||||
numWrites=0;
|
||||
memset(&ymState,-1,sizeof(ymState));
|
||||
|
|
@ -63,44 +63,43 @@ int DivZSM::getoffset() {
|
|||
}
|
||||
|
||||
void DivZSM::writeYM(unsigned char a, unsigned char v) {
|
||||
int lastMask = ymMask;
|
||||
int lastMask=ymMask;
|
||||
if (a==0x19 && v>=0x80) a=0x1a; // AMD/PSD use same reg addr. store PMD as 0x1a
|
||||
if (a==0x08 && (v&0xf8)) ymMask |= (1 << (v & 0x07)); // mark chan as in-use if keyDN
|
||||
if (a!=0x08) ymState[ym_NEW][a] = v; // cache the newly-written value
|
||||
if (a==0x08 && (v&0xf8)) ymMask|=(1<<(v&0x07)); // mark chan as in-use if keyDN
|
||||
if (a!=0x08) ymState[ym_NEW][a]=v; // cache the newly-written value
|
||||
bool writeit=false; // used to suppress spurious writes to unused channels
|
||||
if (a < 0x20) {
|
||||
if (a == 0x08) {
|
||||
if (a<0x20) {
|
||||
if (a==0x08) {
|
||||
// write keyUPDN messages if channel is active.
|
||||
writeit = (ymMask & (1 << (v & 0x07))) > 0;
|
||||
}
|
||||
else {
|
||||
writeit=(ymMask&(1<<(v&0x07)))>0;
|
||||
} else {
|
||||
// do not suppress global registers
|
||||
writeit = true;
|
||||
writeit=true;
|
||||
}
|
||||
} else {
|
||||
writeit = (ymMask & (1 << (a & 0x07))) > 0; // a&0x07 = chan ID for regs >=0x20
|
||||
writeit=(ymMask&(1<<(a&0x07)))>0; // a&0x07 = chan ID for regs >=0x20
|
||||
}
|
||||
if (lastMask != ymMask) {
|
||||
if (lastMask!=ymMask) {
|
||||
// if the ymMask just changed, then the channel has become active.
|
||||
// This can only happen on a KeyDN event, so voice = v & 0x07
|
||||
// This can only happen on a KeyDN event, so voice=v&0x07
|
||||
// insert a keyUP just to be safe.
|
||||
ymwrites.push_back(DivRegWrite(0x08,v&0x07));
|
||||
numWrites++;
|
||||
// flush the ym_NEW cached states for this channel into the ZSM....
|
||||
for ( int i=0x20 + (v&0x07); i <= 0xff ; i+=8) {
|
||||
if (ymState[ym_NEW][i] != ymState[ym_PREV][i]) {
|
||||
for (int i=0x20+(v&0x07); i<=0xff; i+=8) {
|
||||
if (ymState[ym_NEW][i]!=ymState[ym_PREV][i]) {
|
||||
ymwrites.push_back(DivRegWrite(i,ymState[ym_NEW][i]));
|
||||
numWrites++;
|
||||
// ...and update the shadow
|
||||
ymState[ym_PREV][i] = ymState[ym_NEW][i];
|
||||
ymState[ym_PREV][i]=ymState[ym_NEW][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle the current write if channel is active
|
||||
if (writeit && ((ymState[ym_NEW][a] != ymState[ym_PREV][a])||a==0x08) ) {
|
||||
if (writeit && ((ymState[ym_NEW][a]!=ymState[ym_PREV][a]) || a==0x08)) {
|
||||
// update YM shadow if not the KeyUPDN register.
|
||||
if (a!=0x008) ymState[ym_PREV][a] = ymState[ym_NEW][a];
|
||||
// if reg = PMD, then change back to real register 0x19
|
||||
if (a!=8) ymState[ym_PREV][a]=ymState[ym_NEW][a];
|
||||
// if reg=PMD, then change back to real register 0x19
|
||||
if (a==0x1a) a=0x19;
|
||||
ymwrites.push_back(DivRegWrite(a,v));
|
||||
numWrites++;
|
||||
|
|
@ -109,24 +108,26 @@ void DivZSM::writeYM(unsigned char a, unsigned char v) {
|
|||
|
||||
void DivZSM::writePSG(unsigned char a, unsigned char v) {
|
||||
// TODO: suppress writes to PSG voice that is not audible (volume=0)
|
||||
if (a >= 64) {
|
||||
if (a>=64) {
|
||||
logD ("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v);
|
||||
return;
|
||||
}
|
||||
if(psgState[psg_PREV][a] == v) {
|
||||
if (psgState[psg_NEW][a] != v)
|
||||
if (psgState[psg_PREV][a]==v) {
|
||||
if (psgState[psg_NEW][a]!=v) {
|
||||
// NEW value is being reset to the same as PREV value
|
||||
// so it is no longer a new write.
|
||||
numWrites--;
|
||||
}
|
||||
} else {
|
||||
if (psgState[psg_PREV][a] == psgState[psg_NEW][a])
|
||||
if (psgState[psg_PREV][a]==psgState[psg_NEW][a]) {
|
||||
// if this write changes the NEW cached value to something other
|
||||
// than the PREV value, then this is a new write.
|
||||
numWrites++;
|
||||
}
|
||||
}
|
||||
psgState[psg_NEW][a] = v;
|
||||
// mark channel as used in the psgMask if volume is set > 0.
|
||||
if ((a % 4 == 2) && (v & 0x3f)) psgMask |= (1 << (a>>2));
|
||||
psgState[psg_NEW][a]=v;
|
||||
// mark channel as used in the psgMask if volume is set>0.
|
||||
if ((a%4==2) && (v&0x3f)) psgMask|=(1<<(a>>2));
|
||||
}
|
||||
|
||||
void DivZSM::writePCM(unsigned char a, unsigned char v) {
|
||||
|
|
@ -135,7 +136,7 @@ void DivZSM::writePCM(unsigned char a, unsigned char v) {
|
|||
|
||||
void DivZSM::tick(int numticks) {
|
||||
flushWrites();
|
||||
ticks += numticks;
|
||||
ticks+=numticks;
|
||||
}
|
||||
|
||||
void DivZSM::setLoopPoint() {
|
||||
|
|
@ -154,12 +155,14 @@ void DivZSM::setLoopPoint() {
|
|||
memset(&ymState[ym_PREV],-1,sizeof(ymState[ym_PREV]));
|
||||
// ... and cache (except for unused channels)
|
||||
memset(&ymState[ym_NEW],-1,0x20);
|
||||
for (int chan=0; chan<8 ; chan++) {
|
||||
for (int chan=0; chan<8; chan++) {
|
||||
// do not clear state for as-yet-unused channels
|
||||
if (!(ymMask & (1<<chan))) continue;
|
||||
if (!(ymMask&(1<<chan))) continue;
|
||||
// clear the state for channels in use so they match the unknown state
|
||||
// of the YM shadow.
|
||||
for (int i=0x20+chan; i<=0xff; i+= 8) ymState[ym_NEW][i] = -1;
|
||||
for (int i=0x20+chan; i<=0xff; i+=8) {
|
||||
ymState[ym_NEW][i]=-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,8 +172,8 @@ SafeWriter* DivZSM::finish() {
|
|||
w->writeC(ZSM_EOF);
|
||||
// update channel use masks.
|
||||
w->seek(0x09,SEEK_SET);
|
||||
w->writeC((unsigned char)(ymMask & 0xff));
|
||||
w->writeS((short)(psgMask & 0xffff));
|
||||
w->writeC((unsigned char)(ymMask&0xff));
|
||||
w->writeS((short)(psgMask&0xffff));
|
||||
// todo: put PCM offset/data writes here once defined in ZSM standard.
|
||||
return w;
|
||||
}
|
||||
|
|
@ -179,16 +182,16 @@ void DivZSM::flushWrites() {
|
|||
logD("ZSM: flushWrites.... numwrites=%d ticks=%d ymwrites=%d",numWrites,ticks,ymwrites.size());
|
||||
if (numWrites==0) return;
|
||||
flushTicks(); // only flush ticks if there are writes pending.
|
||||
for (unsigned char i=0;i<64;i++) {
|
||||
if (psgState[psg_NEW][i] == psgState[psg_PREV][i]) continue;
|
||||
for (unsigned char i=0; i<64; i++) {
|
||||
if (psgState[psg_NEW][i]==psgState[psg_PREV][i]) continue;
|
||||
psgState[psg_PREV][i]=psgState[psg_NEW][i];
|
||||
w->writeC(i);
|
||||
w->writeC(psgState[psg_NEW][i]);
|
||||
}
|
||||
int n=0; // n = completed YM writes. used to determine when to write the CMD byte...
|
||||
int n=0; // n=completed YM writes. used to determine when to write the CMD byte...
|
||||
for (DivRegWrite& write: ymwrites) {
|
||||
if (n%ZSM_YM_MAX_WRITES == 0) {
|
||||
if(ymwrites.size()-n > ZSM_YM_MAX_WRITES) {
|
||||
if (n%ZSM_YM_MAX_WRITES==0) {
|
||||
if (ymwrites.size()-n>ZSM_YM_MAX_WRITES) {
|
||||
w->writeC((unsigned char)(ZSM_YM_CMD+ZSM_YM_MAX_WRITES));
|
||||
logD("ZSM: YM-write: %d (%02x) [max]",ZSM_YM_MAX_WRITES,ZSM_YM_MAX_WRITES+ZSM_YM_CMD);
|
||||
} else {
|
||||
|
|
@ -205,10 +208,10 @@ void DivZSM::flushWrites() {
|
|||
}
|
||||
|
||||
void DivZSM::flushTicks() {
|
||||
while (ticks > ZSM_DELAY_MAX) {
|
||||
while (ticks>ZSM_DELAY_MAX) {
|
||||
logD("ZSM: write delay %d (max)",ZSM_DELAY_MAX);
|
||||
w->writeC((unsigned char)(ZSM_DELAY_CMD+ZSM_DELAY_MAX));
|
||||
ticks -= ZSM_DELAY_MAX;
|
||||
ticks-=ZSM_DELAY_MAX;
|
||||
}
|
||||
if (ticks>0) {
|
||||
logD("ZSM: write delay %d",ticks);
|
||||
|
|
|
|||
|
|
@ -27,36 +27,42 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
|||
constexpr int MASTER_CLOCK_MASK=(sizeof(void*)==8)?0xff:0;
|
||||
|
||||
SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
||||
int VERA=-1;
|
||||
int YM=-1;
|
||||
int IGNORED=0;
|
||||
|
||||
int VERA = -1;
|
||||
int YM = -1;
|
||||
int IGNORED = 0;
|
||||
|
||||
//loop = false;
|
||||
// find indexes for YM and VERA. Ignore other systems.
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
switch (song.system[i]) {
|
||||
case DIV_SYSTEM_VERA:
|
||||
if (VERA >= 0) { IGNORED++;break; }
|
||||
VERA = i;
|
||||
if (VERA>=0) {
|
||||
IGNORED++;
|
||||
break;
|
||||
}
|
||||
VERA=i;
|
||||
logD("VERA detected as chip id %d",i);
|
||||
break;
|
||||
case DIV_SYSTEM_YM2151:
|
||||
if (YM >= 0) { IGNORED++;break; }
|
||||
YM = i;
|
||||
if (YM>=0) {
|
||||
IGNORED++;
|
||||
break;
|
||||
}
|
||||
YM=i;
|
||||
logD("YM detected as chip id %d",i);
|
||||
break;
|
||||
default:
|
||||
IGNORED++;
|
||||
logD("Ignoring chip %d systemID %d",i,song.system[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (VERA < 0 && YM < 0) {
|
||||
logE("No supported systems for ZSM");
|
||||
return NULL;
|
||||
if (VERA<0 && YM<0) {
|
||||
logE("No supported systems for ZSM");
|
||||
return NULL;
|
||||
}
|
||||
if (IGNORED > 0)
|
||||
if (IGNORED>0) {
|
||||
logW("ZSM export ignoring %d unsupported system%c",IGNORED,IGNORED>1?'s':' ');
|
||||
}
|
||||
|
||||
stop();
|
||||
repeatPattern=false;
|
||||
|
|
@ -64,7 +70,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
BUSY_BEGIN_SOFT;
|
||||
|
||||
double origRate=got.rate;
|
||||
got.rate=zsmrate & 0xffff;
|
||||
got.rate=zsmrate&0xffff;
|
||||
|
||||
// determine loop point
|
||||
int loopOrder=0;
|
||||
|
|
@ -89,15 +95,14 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
//size_t tickCount=0;
|
||||
bool done=false;
|
||||
int loopPos=-1;
|
||||
int writeCount=0;
|
||||
int fracWait=0; // accumulates fractional ticks
|
||||
if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(true);
|
||||
if (YM >= 0) {
|
||||
if (VERA>=0) disCont[VERA].dispatch->toggleRegisterDump(true);
|
||||
if (YM>=0) {
|
||||
disCont[YM].dispatch->toggleRegisterDump(true);
|
||||
// emit LFO initialization commands
|
||||
zsm.writeYM(0x18,0); // freq = 0
|
||||
zsm.writeYM(0x19,0x7F); // AMD = 7F
|
||||
zsm.writeYM(0x19,0xFF); // PMD = 7F
|
||||
zsm.writeYM(0x18,0); // freq=0
|
||||
zsm.writeYM(0x19,0x7F); // AMD =7F
|
||||
zsm.writeYM(0x19,0xFF); // PMD =7F
|
||||
// TODO: incorporate the Furnace meta-command for init data and filter
|
||||
// out writes to otherwise-unused channels.
|
||||
}
|
||||
|
|
@ -126,35 +131,35 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
int i=0;
|
||||
// dump YM writes first
|
||||
if (j==0) {
|
||||
if (YM < 0)
|
||||
if (YM<0) {
|
||||
continue;
|
||||
else
|
||||
} else {
|
||||
i=YM;
|
||||
}
|
||||
}
|
||||
// dump VERA writes second
|
||||
if (j==1) {
|
||||
if (VERA < 0)
|
||||
if (VERA<0) {
|
||||
continue;
|
||||
else {
|
||||
} else {
|
||||
i=VERA;
|
||||
}
|
||||
}
|
||||
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
|
||||
if (writes.size() > 0)
|
||||
logD("zsmOps: Writing %d messages to chip %d",writes.size(), i);
|
||||
if (writes.size()>0)
|
||||
logD("zsmOps: Writing %d messages to chip %d",writes.size(),i);
|
||||
for (DivRegWrite& write: writes) {
|
||||
if (i==YM) zsm.writeYM(write.addr&0xff, write.val);
|
||||
if (i==VERA) zsm.writePSG(write.addr&0xff, write.val);
|
||||
writeCount++;
|
||||
if (i==YM) zsm.writeYM(write.addr&0xff,write.val);
|
||||
if (i==VERA) zsm.writePSG(write.addr&0xff,write.val);
|
||||
}
|
||||
writes.clear();
|
||||
}
|
||||
|
||||
// write wait
|
||||
int totalWait=cycles>>MASTER_CLOCK_PREC;
|
||||
fracWait += cycles & MASTER_CLOCK_MASK;
|
||||
totalWait += fracWait>>MASTER_CLOCK_PREC;
|
||||
fracWait &= MASTER_CLOCK_MASK;
|
||||
fracWait+=cycles&MASTER_CLOCK_MASK;
|
||||
totalWait+=fracWait>>MASTER_CLOCK_PREC;
|
||||
fracWait&=MASTER_CLOCK_MASK;
|
||||
if (totalWait>0) {
|
||||
zsm.tick(totalWait);
|
||||
//tickCount+=totalWait;
|
||||
|
|
@ -163,9 +168,9 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
// end of song
|
||||
|
||||
// done - close out.
|
||||
got.rate = origRate;
|
||||
if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(false);
|
||||
if (YM >= 0) disCont[YM].dispatch->toggleRegisterDump(false);
|
||||
got.rate=origRate;
|
||||
if (VERA>=0) disCont[VERA].dispatch->toggleRegisterDump(false);
|
||||
if (YM>=0) disCont[YM].dispatch->toggleRegisterDump(false);
|
||||
|
||||
remainingLoops=-1;
|
||||
playing=false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue