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;
|
||||
|
|
|
|||
|
|
@ -78,11 +78,14 @@ const char* aboutLine[]={
|
|||
"Dippy",
|
||||
"djtuBIG-MaliceX",
|
||||
"dumbut",
|
||||
"ElectricKeet",
|
||||
"EpicTyphlosion",
|
||||
"FΛDE",
|
||||
"Forte",
|
||||
"Fragmare",
|
||||
"freq-mod",
|
||||
"gtr3qq",
|
||||
"Hortus",
|
||||
"iyatemu",
|
||||
"JayBOB18",
|
||||
"Jimmy-DS",
|
||||
|
|
@ -100,6 +103,7 @@ const char* aboutLine[]={
|
|||
"MelonadeM",
|
||||
"Miker",
|
||||
"nicco1690",
|
||||
"niffuM",
|
||||
"<nk>",
|
||||
"NyaongI",
|
||||
"potatoTeto",
|
||||
|
|
@ -120,6 +124,7 @@ const char* aboutLine[]={
|
|||
"Ultraprogramer",
|
||||
"UserSniper",
|
||||
"Weeppiko",
|
||||
"Xan",
|
||||
"Yuzu4K",
|
||||
"Zaxolotl",
|
||||
"ZoomTen (Zumi)",
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ void FurnaceGUI::calcChanOsc() {
|
|||
unsigned short needlePos=buf->needle;
|
||||
needlePos-=displaySize;
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/32768.0f;
|
||||
if (minLevel>y) minLevel=y;
|
||||
if (maxLevel<y) maxLevel=y;
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ void FurnaceGUI::drawChanOsc() {
|
|||
}
|
||||
if (!chanOscOpen) return;
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||
if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen,globalWinFlags|((chanOscOptions)?0:ImGuiWindowFlags_NoTitleBar))) {
|
||||
if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen,globalWinFlags)) {
|
||||
bool centerSettingReset=false;
|
||||
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||
if (chanOscOptions) {
|
||||
|
|
@ -152,10 +152,10 @@ void FurnaceGUI::drawChanOsc() {
|
|||
|
||||
if (chanOscUseGrad) {
|
||||
if (chanOscGradTex==NULL) {
|
||||
chanOscGradTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,chanOscGrad.width,chanOscGrad.height);
|
||||
chanOscGradTex=rend->createTexture(true,chanOscGrad.width,chanOscGrad.height);
|
||||
|
||||
if (chanOscGradTex==NULL) {
|
||||
logE("error while creating gradient texture! %s",SDL_GetError());
|
||||
logE("error while creating gradient texture!");
|
||||
} else {
|
||||
updateChanOscGradTex=true;
|
||||
}
|
||||
|
|
@ -170,16 +170,16 @@ void FurnaceGUI::drawChanOsc() {
|
|||
if (chanOscGradTex!=NULL) {
|
||||
if (updateChanOscGradTex) {
|
||||
chanOscGrad.render();
|
||||
if (SDL_UpdateTexture(chanOscGradTex,NULL,chanOscGrad.grad.get(),chanOscGrad.width*4)==0) {
|
||||
if (rend->updateTexture(chanOscGradTex,chanOscGrad.grad.get(),chanOscGrad.width*4)) {
|
||||
updateChanOscGradTex=false;
|
||||
} else {
|
||||
logE("error while updating gradient texture! %s",SDL_GetError());
|
||||
logE("error while updating gradient texture!");
|
||||
}
|
||||
}
|
||||
|
||||
ImVec2 gradLeft=ImGui::GetCursorPos();
|
||||
ImVec2 gradSize=ImVec2(400.0f*dpiScale,400.0f*dpiScale);
|
||||
ImGui::Image(chanOscGradTex,gradSize);
|
||||
ImGui::Image(rend->getTextureID(chanOscGradTex),gradSize);
|
||||
ImVec2 gradLeftAbs=ImGui::GetItemRectMin();
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
if (chanOscGrad.points.size()<32) {
|
||||
|
|
@ -346,14 +346,19 @@ void FurnaceGUI::drawChanOsc() {
|
|||
ImRect rect=ImRect(minArea,maxArea);
|
||||
ImRect inRect=rect;
|
||||
inRect.Min.x+=dpiScale;
|
||||
inRect.Min.y+=dpiScale;
|
||||
inRect.Min.y+=2.0*dpiScale;
|
||||
inRect.Max.x-=dpiScale;
|
||||
inRect.Max.y-=dpiScale;
|
||||
inRect.Max.y-=2.0*dpiScale;
|
||||
|
||||
int precision=inRect.Max.x-inRect.Min.x;
|
||||
if (precision<1) precision=1;
|
||||
if (precision>512) precision=512;
|
||||
|
||||
ImGui::ItemSize(size,style.FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) {
|
||||
if (!e->isRunning()) {
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float x=(float)i/512.0f;
|
||||
for (unsigned short i=0; i<precision; i++) {
|
||||
float x=(float)i/(float)precision;
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
|
||||
}
|
||||
} else {
|
||||
|
|
@ -394,18 +399,19 @@ void FurnaceGUI::drawChanOsc() {
|
|||
*/
|
||||
|
||||
needlePos-=displaySize;
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
for (unsigned short i=0; i<precision; i++) {
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
|
||||
if (minLevel>y) minLevel=y;
|
||||
if (maxLevel<y) maxLevel=y;
|
||||
}
|
||||
dcOff=(minLevel+maxLevel)*0.5f;
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float x=(float)i/512.0f;
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
for (unsigned short i=0; i<precision; i++) {
|
||||
float x=(float)i/(float)precision;
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
|
||||
y-=dcOff;
|
||||
if (y<-0.5f) y=-0.5f;
|
||||
if (y>0.5f) y=0.5f;
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-(y-dcOff)));
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
|
||||
}
|
||||
}
|
||||
ImU32 color=ImGui::GetColorU32(chanOscColor);
|
||||
|
|
@ -418,7 +424,9 @@ void FurnaceGUI::drawChanOsc() {
|
|||
|
||||
color=chanOscGrad.get(xVal,1.0f-yVal);
|
||||
}
|
||||
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
|
||||
ImGui::PushClipRect(inRect.Min,inRect.Max,false);
|
||||
dl->AddPolyline(waveform,precision,color,ImDrawFlags_None,dpiScale);
|
||||
ImGui::PopClipRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,6 +180,10 @@ void FurnaceGUI::drawCompatFlags() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("behavior changed in 0.6pre4");
|
||||
}
|
||||
ImGui::Checkbox("Broken macros in some FM chips after note off",&e->song.brokenFMOff);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("behavior changed in 0.6pre5");
|
||||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem(".mod import")) {
|
||||
|
|
|
|||
1283
src/gui/dataList.cpp
1283
src/gui/dataList.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -380,7 +380,7 @@ void FurnaceGUI::drawDebug() {
|
|||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Window Debug")) {
|
||||
if (ImGui::TreeNodeEx("Window Debug",ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Text("Screen: %dx%d+%d+%d",scrW,scrH,scrX,scrY);
|
||||
ImGui::Text("Screen (Conf): %dx%d+%d+%d",scrConfW,scrConfH,scrConfX,scrConfY);
|
||||
ImGui::Text("Canvas: %dx%d",canvasW,canvasH);
|
||||
|
|
@ -549,6 +549,7 @@ void FurnaceGUI::drawDebug() {
|
|||
|
||||
ImGui::Text("audio: %dµs",lastProcTime);
|
||||
ImGui::Text("render: %.0fµs",(double)renderTimeDelta/perfFreq);
|
||||
ImGui::Text("draw: %.0fµs",(double)drawTimeDelta/perfFreq);
|
||||
ImGui::Text("layout: %.0fµs",(double)layoutTimeDelta/perfFreq);
|
||||
ImGui::Text("event: %.0fµs",(double)eventTimeDelta/perfFreq);
|
||||
ImGui::Separator();
|
||||
|
|
|
|||
|
|
@ -561,17 +561,17 @@ void FurnaceGUI::doAction(int what) {
|
|||
doFlip();
|
||||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE_ROWS:
|
||||
doCollapse(2,selStart,selEnd);
|
||||
doCollapse(collapseAmount,selStart,selEnd);
|
||||
break;
|
||||
case GUI_ACTION_PAT_EXPAND_ROWS:
|
||||
doExpand(2,selStart,selEnd);
|
||||
doExpand(collapseAmount,selStart,selEnd);
|
||||
break;
|
||||
case GUI_ACTION_PAT_COLLAPSE_PAT: {
|
||||
SelectionPoint selEndPat;
|
||||
selEndPat.xCoarse=e->getTotalChannelCount()-1;
|
||||
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
|
||||
selEndPat.y=e->curSubSong->patLen-1;
|
||||
doCollapse(2,SelectionPoint(0,0,0),selEndPat);
|
||||
doCollapse(collapseAmount,SelectionPoint(0,0,0),selEndPat);
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_PAT_EXPAND_PAT: {
|
||||
|
|
@ -579,14 +579,14 @@ void FurnaceGUI::doAction(int what) {
|
|||
selEndPat.xCoarse=e->getTotalChannelCount()-1;
|
||||
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
|
||||
selEndPat.y=e->curSubSong->patLen-1;
|
||||
doExpand(2,SelectionPoint(0,0,0),selEndPat);
|
||||
doExpand(collapseAmount,SelectionPoint(0,0,0),selEndPat);
|
||||
break;
|
||||
}
|
||||
case GUI_ACTION_PAT_COLLAPSE_SONG:
|
||||
doCollapseSong(2);
|
||||
doCollapseSong(collapseAmount);
|
||||
break;
|
||||
case GUI_ACTION_PAT_EXPAND_SONG:
|
||||
doExpandSong(2);
|
||||
doExpandSong(collapseAmount);
|
||||
break;
|
||||
case GUI_ACTION_PAT_LATCH: // TODO
|
||||
break;
|
||||
|
|
@ -684,6 +684,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_DIR_VIEW:
|
||||
insListDir=!insListDir;
|
||||
break;
|
||||
|
||||
case GUI_ACTION_WAVE_LIST_ADD:
|
||||
curWave=e->addWave();
|
||||
|
|
@ -757,6 +760,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
if (++curWave>=(int)e->song.wave.size()) curWave=((int)e->song.wave.size())-1;
|
||||
wantScrollList=true;
|
||||
break;
|
||||
case GUI_ACTION_WAVE_LIST_DIR_VIEW:
|
||||
waveListDir=!waveListDir;
|
||||
break;
|
||||
|
||||
case GUI_ACTION_SAMPLE_LIST_ADD:
|
||||
curSample=e->addSample();
|
||||
|
|
@ -785,6 +791,8 @@ void FurnaceGUI::doAction(int what) {
|
|||
sample->loopEnd=prevSample->loopEnd;
|
||||
sample->loop=prevSample->loop;
|
||||
sample->loopMode=prevSample->loopMode;
|
||||
sample->brrEmphasis=prevSample->brrEmphasis;
|
||||
sample->dither=prevSample->dither;
|
||||
sample->depth=prevSample->depth;
|
||||
if (sample->init(prevSample->samples)) {
|
||||
if (prevSample->getCurBuf()!=NULL) {
|
||||
|
|
@ -815,6 +823,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_SAMPLE_LIST_SAVE:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE);
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_SAVE_RAW:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE_RAW);
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_MOVE_UP:
|
||||
if (e->moveSampleUp(curSample)) {
|
||||
curSample--;
|
||||
|
|
@ -835,8 +846,8 @@ void FurnaceGUI::doAction(int what) {
|
|||
MARK_MODIFIED;
|
||||
if (curSample>=(int)e->song.sample.size()) {
|
||||
curSample--;
|
||||
updateSampleTex=true;
|
||||
}
|
||||
updateSampleTex=true;
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_EDIT:
|
||||
sampleEditOpen=true;
|
||||
|
|
@ -857,6 +868,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW:
|
||||
e->stopSamplePreview();
|
||||
break;
|
||||
case GUI_ACTION_SAMPLE_LIST_DIR_VIEW:
|
||||
sampleListDir=!sampleListDir;
|
||||
break;
|
||||
|
||||
case GUI_ACTION_SAMPLE_SELECT:
|
||||
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
|
||||
|
|
|
|||
|
|
@ -463,181 +463,185 @@ void FurnaceGUI::drawMobileControls() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
if (settings.unifiedDataView) {
|
||||
drawInsList(true);
|
||||
} else {
|
||||
switch (mobScene) {
|
||||
case GUI_SCENE_PATTERN:
|
||||
case GUI_SCENE_ORDERS:
|
||||
case GUI_SCENE_INSTRUMENT:
|
||||
switch (mobScene) {
|
||||
case GUI_SCENE_PATTERN:
|
||||
case GUI_SCENE_ORDERS:
|
||||
case GUI_SCENE_INSTRUMENT:
|
||||
drawInsList(true);
|
||||
break;
|
||||
case GUI_SCENE_WAVETABLE:
|
||||
if (settings.unifiedDataView) {
|
||||
drawInsList(true);
|
||||
break;
|
||||
case GUI_SCENE_WAVETABLE:
|
||||
} else {
|
||||
drawWaveList(true);
|
||||
break;
|
||||
case GUI_SCENE_SAMPLE:
|
||||
}
|
||||
break;
|
||||
case GUI_SCENE_SAMPLE:
|
||||
if (settings.unifiedDataView) {
|
||||
drawInsList(true);
|
||||
} else {
|
||||
drawSampleList(true);
|
||||
break;
|
||||
case GUI_SCENE_SONG: {
|
||||
if (ImGui::Button("New")) {
|
||||
mobileMenuOpen=false;
|
||||
//doAction(GUI_ACTION_NEW);
|
||||
if (modified) {
|
||||
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
|
||||
} else {
|
||||
displayNew=true;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Open")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save as...")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE_AS);
|
||||
}
|
||||
|
||||
ImGui::Button("1.1+ .dmf");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Legacy .dmf");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export Audio")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export VGM")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_VGM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("CmdStream")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("CmdStream Text")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Restore Backup")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTabBar("MobileSong")) {
|
||||
if (ImGui::BeginTabItem("Song Info")) {
|
||||
drawSongInfo(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Subsongs")) {
|
||||
drawSubSongs(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Speed")) {
|
||||
drawSpeed(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GUI_SCENE_CHANNELS:
|
||||
ImGui::Text("Channels here...");
|
||||
break;
|
||||
case GUI_SCENE_CHIPS:
|
||||
ImGui::Text("Chips here...");
|
||||
break;
|
||||
case GUI_SCENE_MIXER:
|
||||
ImGui::Text("What the hell...");
|
||||
break;
|
||||
case GUI_SCENE_OTHER: {
|
||||
if (ImGui::Button("Osc")) {
|
||||
oscOpen=!oscOpen;
|
||||
break;
|
||||
case GUI_SCENE_SONG: {
|
||||
if (ImGui::Button("New")) {
|
||||
mobileMenuOpen=false;
|
||||
//doAction(GUI_ACTION_NEW);
|
||||
if (modified) {
|
||||
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
|
||||
} else {
|
||||
displayNew=true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("ChanOsc")) {
|
||||
chanOscOpen=!chanOscOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("RegView")) {
|
||||
regViewOpen=!regViewOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Stats")) {
|
||||
statsOpen=!statsOpen;
|
||||
}
|
||||
if (ImGui::Button("Compat Flags")) {
|
||||
compatFlagsOpen=!compatFlagsOpen;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Button("Panic");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Settings")) {
|
||||
mobileMenuOpen=false;
|
||||
settingsOpen=true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Log")) {
|
||||
logOpen=!logOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Debug")) {
|
||||
debugOpen=!debugOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("About")) {
|
||||
mobileMenuOpen=false;
|
||||
mobileMenuPos=0.0f;
|
||||
aboutOpen=true;
|
||||
}
|
||||
if (ImGui::Button("Switch to Desktop Mode")) {
|
||||
toggleMobileUI(!mobileUI);
|
||||
}
|
||||
|
||||
int numAmiga=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
|
||||
}
|
||||
|
||||
if (numAmiga) {
|
||||
ImGui::Text(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
"comparing it with real Amiga output."
|
||||
);
|
||||
ImGui::Text("Directory");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##AVDPath",&workingDirROMExport);
|
||||
if (ImGui::Button("Bake Data")) {
|
||||
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
|
||||
if (workingDirROMExport.size()>0) {
|
||||
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
|
||||
}
|
||||
for (DivROMExportOutput& i: out) {
|
||||
String path=workingDirROMExport+i.name;
|
||||
FILE* outFile=ps_fopen(path.c_str(),"wb");
|
||||
if (outFile!=NULL) {
|
||||
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
|
||||
fclose(outFile);
|
||||
}
|
||||
i.data->finish();
|
||||
delete i.data;
|
||||
}
|
||||
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Open")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Save as...")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE_AS);
|
||||
}
|
||||
|
||||
ImGui::Button("1.1+ .dmf");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Legacy .dmf");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export Audio")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Export VGM")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_VGM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("CmdStream")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("CmdStream Text")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Restore Backup")) {
|
||||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTabBar("MobileSong")) {
|
||||
if (ImGui::BeginTabItem("Song Info")) {
|
||||
drawSongInfo(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Subsongs")) {
|
||||
drawSubSongs(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Speed")) {
|
||||
drawSpeed(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GUI_SCENE_CHANNELS:
|
||||
ImGui::Text("Channels here...");
|
||||
break;
|
||||
case GUI_SCENE_CHIPS:
|
||||
ImGui::Text("Chips here...");
|
||||
break;
|
||||
case GUI_SCENE_MIXER:
|
||||
ImGui::Text("What the hell...");
|
||||
break;
|
||||
case GUI_SCENE_OTHER: {
|
||||
if (ImGui::Button("Osc")) {
|
||||
oscOpen=!oscOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("ChanOsc")) {
|
||||
chanOscOpen=!chanOscOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("RegView")) {
|
||||
regViewOpen=!regViewOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Stats")) {
|
||||
statsOpen=!statsOpen;
|
||||
}
|
||||
if (ImGui::Button("Compat Flags")) {
|
||||
compatFlagsOpen=!compatFlagsOpen;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Button("Panic");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Settings")) {
|
||||
mobileMenuOpen=false;
|
||||
settingsOpen=true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Log")) {
|
||||
logOpen=!logOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Debug")) {
|
||||
debugOpen=!debugOpen;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("About")) {
|
||||
mobileMenuOpen=false;
|
||||
mobileMenuPos=0.0f;
|
||||
aboutOpen=true;
|
||||
}
|
||||
if (ImGui::Button("Switch to Desktop Mode")) {
|
||||
toggleMobileUI(!mobileUI);
|
||||
}
|
||||
|
||||
int numAmiga=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
|
||||
}
|
||||
|
||||
if (numAmiga) {
|
||||
ImGui::Text(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
"comparing it with real Amiga output."
|
||||
);
|
||||
ImGui::Text("Directory");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##AVDPath",&workingDirROMExport);
|
||||
if (ImGui::Button("Bake Data")) {
|
||||
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
|
||||
if (workingDirROMExport.size()>0) {
|
||||
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
|
||||
}
|
||||
for (DivROMExportOutput& i: out) {
|
||||
String path=workingDirROMExport+i.name;
|
||||
FILE* outFile=ps_fopen(path.c_str(),"wb");
|
||||
if (outFile!=NULL) {
|
||||
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
|
||||
fclose(outFile);
|
||||
}
|
||||
i.data->finish();
|
||||
delete i.data;
|
||||
}
|
||||
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,14 +245,22 @@ void FurnaceGUI::doPullDelete() {
|
|||
updateScroll(cursor.y);
|
||||
}
|
||||
|
||||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
SelectionPoint sStart=selStart;
|
||||
SelectionPoint sEnd=selEnd;
|
||||
|
||||
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && settings.pullDeleteRow) {
|
||||
sStart.xFine=0;
|
||||
sEnd.xFine=2+e->curPat[sEnd.xCoarse].effectCols*2;
|
||||
}
|
||||
|
||||
int iCoarse=sStart.xCoarse;
|
||||
int iFine=sStart.xFine;
|
||||
for (; iCoarse<=sEnd.xCoarse; iCoarse++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
|
||||
maskOut(opMaskPullDelete,iFine);
|
||||
for (int j=selStart.y; j<e->curSubSong->patLen; j++) {
|
||||
for (int j=sStart.y; j<e->curSubSong->patLen; j++) {
|
||||
if (j<e->curSubSong->patLen-1) {
|
||||
if (iFine==0) {
|
||||
pat->data[j][iFine]=pat->data[j+1][iFine];
|
||||
|
|
@ -277,15 +285,23 @@ void FurnaceGUI::doInsert() {
|
|||
prepareUndo(GUI_UNDO_PATTERN_PUSH);
|
||||
curNibble=false;
|
||||
|
||||
int iCoarse=selStart.xCoarse;
|
||||
int iFine=selStart.xFine;
|
||||
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
|
||||
SelectionPoint sStart=selStart;
|
||||
SelectionPoint sEnd=selEnd;
|
||||
|
||||
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && settings.insertBehavior) {
|
||||
sStart.xFine=0;
|
||||
sEnd.xFine=2+e->curPat[sEnd.xCoarse].effectCols*2;
|
||||
}
|
||||
|
||||
int iCoarse=sStart.xCoarse;
|
||||
int iFine=sStart.xFine;
|
||||
for (; iCoarse<=sEnd.xCoarse; iCoarse++) {
|
||||
if (!e->curSubSong->chanShow[iCoarse]) continue;
|
||||
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
|
||||
for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse<sEnd.xCoarse || iFine<=sEnd.xFine); iFine++) {
|
||||
maskOut(opMaskInsert,iFine);
|
||||
for (int j=e->curSubSong->patLen-1; j>=selStart.y; j--) {
|
||||
if (j==selStart.y) {
|
||||
for (int j=e->curSubSong->patLen-1; j>=sStart.y; j--) {
|
||||
if (j==sStart.y) {
|
||||
if (iFine==0) {
|
||||
pat->data[j][iFine]=0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "fileDialog.h"
|
||||
#include "ImGuiFileDialog.h"
|
||||
#include "util.h"
|
||||
#include "../ta-log.h"
|
||||
|
||||
#ifdef USE_NFD
|
||||
|
|
@ -152,6 +153,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
|
|||
ImGuiFileDialog::Instance()->singleClickSel=mobileUI;
|
||||
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
||||
ImGuiFileDialog::Instance()->mobileMode=mobileUI;
|
||||
ImGuiFileDialog::Instance()->homePath=getHomeDir();
|
||||
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,allowMultiple?999:1,nullptr,0,clickCallback);
|
||||
}
|
||||
opened=true;
|
||||
|
|
@ -235,6 +237,7 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, c
|
|||
ImGuiFileDialog::Instance()->singleClickSel=false;
|
||||
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
|
||||
ImGuiFileDialog::Instance()->mobileMode=mobileUI;
|
||||
ImGuiFileDialog::Instance()->homePath=getHomeDir();
|
||||
ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite);
|
||||
}
|
||||
opened=true;
|
||||
|
|
|
|||
384
src/gui/gui.cpp
384
src/gui/gui.cpp
|
|
@ -29,8 +29,6 @@
|
|||
#include "../fileutils.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "imgui_impl_sdlrenderer.h"
|
||||
#include "ImGuiFileDialog.h"
|
||||
#include "IconsFontAwesome4.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
|
@ -1726,6 +1724,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_SAMPLE_SAVE_RAW:
|
||||
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
"Load Raw Sample",
|
||||
{"all files", "*"},
|
||||
".*",
|
||||
workingDirSample,
|
||||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_EXPORT_AUDIO_ONE:
|
||||
if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
|
|
@ -1958,8 +1966,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
//ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard;
|
||||
}
|
||||
|
||||
#define FURNACE_ZLIB_COMPRESS
|
||||
|
||||
int FurnaceGUI::save(String path, int dmfVersion) {
|
||||
SafeWriter* w;
|
||||
logD("saving file...");
|
||||
|
|
@ -1967,7 +1973,7 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
if (dmfVersion<24) dmfVersion=24;
|
||||
w=e->saveDMF(dmfVersion);
|
||||
} else {
|
||||
w=e->saveFur();
|
||||
w=e->saveFur(false,settings.newPatternFormat);
|
||||
}
|
||||
if (w==NULL) {
|
||||
lastError=e->getLastError();
|
||||
|
|
@ -1982,35 +1988,56 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
#ifdef FURNACE_ZLIB_COMPRESS
|
||||
unsigned char zbuf[131072];
|
||||
int ret;
|
||||
z_stream zl;
|
||||
memset(&zl,0,sizeof(z_stream));
|
||||
ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
|
||||
if (ret!=Z_OK) {
|
||||
logE("zlib error!");
|
||||
lastError="compression error";
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
zl.avail_in=w->size();
|
||||
zl.next_in=w->getFinalBuf();
|
||||
while (zl.avail_in>0) {
|
||||
if (settings.compress) {
|
||||
unsigned char zbuf[131072];
|
||||
int ret;
|
||||
z_stream zl;
|
||||
memset(&zl,0,sizeof(z_stream));
|
||||
ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
|
||||
if (ret!=Z_OK) {
|
||||
logE("zlib error!");
|
||||
lastError="compression error";
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
zl.avail_in=w->size();
|
||||
zl.next_in=w->getFinalBuf();
|
||||
while (zl.avail_in>0) {
|
||||
zl.avail_out=131072;
|
||||
zl.next_out=zbuf;
|
||||
if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
|
||||
logE("zlib stream error!");
|
||||
lastError="zlib stream error";
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
size_t amount=131072-zl.avail_out;
|
||||
if (amount>0) {
|
||||
if (fwrite(zbuf,1,amount,outFile)!=amount) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
zl.avail_out=131072;
|
||||
zl.next_out=zbuf;
|
||||
if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
|
||||
logE("zlib stream error!");
|
||||
lastError="zlib stream error";
|
||||
if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
|
||||
logE("zlib finish stream error!");
|
||||
lastError="zlib finish stream error";
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
size_t amount=131072-zl.avail_out;
|
||||
if (amount>0) {
|
||||
if (fwrite(zbuf,1,amount,outFile)!=amount) {
|
||||
if (131072-zl.avail_out>0) {
|
||||
if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
deflateEnd(&zl);
|
||||
|
|
@ -2019,37 +2046,16 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
zl.avail_out=131072;
|
||||
zl.next_out=zbuf;
|
||||
if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
|
||||
logE("zlib finish stream error!");
|
||||
lastError="zlib finish stream error";
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 2;
|
||||
}
|
||||
if (131072-zl.avail_out>0) {
|
||||
if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
|
||||
} else {
|
||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
deflateEnd(&zl);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
deflateEnd(&zl);
|
||||
#else
|
||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||
logE("did not write entirely: %s!",strerror(errno));
|
||||
lastError=strerror(errno);
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
backupLock.lock();
|
||||
|
|
@ -2262,6 +2268,11 @@ void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) {
|
|||
displayExporting=true;
|
||||
}
|
||||
|
||||
void FurnaceGUI::editStr(String* which) {
|
||||
editString=which;
|
||||
displayEditString=true;
|
||||
}
|
||||
|
||||
void FurnaceGUI::showWarning(String what, FurnaceGUIWarnings type) {
|
||||
warnString=what;
|
||||
warnAction=type;
|
||||
|
|
@ -2842,8 +2853,14 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
|||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("flip selection",BIND_FOR(GUI_ACTION_PAT_FLIP_SELECTION))) doFlip();
|
||||
if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(2,selStart,selEnd);
|
||||
if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(2,selStart,selEnd);
|
||||
|
||||
ImGui::SetNextItemWidth(120.0f*dpiScale);
|
||||
if (ImGui::InputInt("collapse/expand amount##CollapseAmount",&collapseAmount,1,1)) {
|
||||
if (collapseAmount<2) collapseAmount=2;
|
||||
if (collapseAmount>256) collapseAmount=256;
|
||||
}
|
||||
if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(collapseAmount,selStart,selEnd);
|
||||
if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(collapseAmount,selStart,selEnd);
|
||||
|
||||
if (topMenu) {
|
||||
ImGui::Separator();
|
||||
|
|
@ -2933,6 +2950,12 @@ int _processEvent(void* instance, SDL_Event* event) {
|
|||
return ((FurnaceGUI*)instance)->processEvent(event);
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2,0,17)
|
||||
#define VALID_MODS KMOD_NUM|KMOD_CAPS|KMOD_SCROLL
|
||||
#else
|
||||
#define VALID_MODS KMOD_NUM|KMOD_CAPS
|
||||
#endif
|
||||
|
||||
int FurnaceGUI::processEvent(SDL_Event* ev) {
|
||||
if (introPos<11.0 && !shortIntro) return 1;
|
||||
#ifdef IS_MOBILE
|
||||
|
|
@ -2944,7 +2967,7 @@ int FurnaceGUI::processEvent(SDL_Event* ev) {
|
|||
}
|
||||
#endif
|
||||
if (ev->type==SDL_KEYDOWN) {
|
||||
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && !sampleMapWaitingInput && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) {
|
||||
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && !sampleMapWaitingInput && (ev->key.keysym.mod&(~(VALID_MODS)))==0) {
|
||||
if (settings.notePreviewBehavior==0) return 1;
|
||||
switch (curWindow) {
|
||||
case GUI_WINDOW_SAMPLE_EDIT:
|
||||
|
|
@ -3397,6 +3420,7 @@ bool FurnaceGUI::loop() {
|
|||
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
|
||||
logD("window resized to %dx%d",scrW,scrH);
|
||||
updateWindow=true;
|
||||
rend->resized(ev);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
scrX=ev.window.data1;
|
||||
|
|
@ -3431,6 +3455,7 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
}
|
||||
break;
|
||||
#if SDL_VERSION_ATLEAST(2,0,17)
|
||||
case SDL_DISPLAYEVENT: {
|
||||
switch (ev.display.event) {
|
||||
case SDL_DISPLAYEVENT_CONNECTED:
|
||||
|
|
@ -3448,6 +3473,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case SDL_KEYDOWN:
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard) {
|
||||
keyDown(ev);
|
||||
|
|
@ -3464,7 +3490,7 @@ bool FurnaceGUI::loop() {
|
|||
int sampleCountBefore=e->song.sampleLen;
|
||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file);
|
||||
DivWavetable* droppedWave=NULL;
|
||||
DivSample* droppedSample=NULL;;
|
||||
DivSample* droppedSample=NULL;
|
||||
if (!instruments.empty()) {
|
||||
if (e->song.sampleLen!=sampleCountBefore) {
|
||||
e->renderSamplesP();
|
||||
|
|
@ -3510,7 +3536,8 @@ bool FurnaceGUI::loop() {
|
|||
// update config x/y/w/h values based on scrMax state
|
||||
if (updateWindow) {
|
||||
logV("updateWindow is true");
|
||||
if (!scrMax) {
|
||||
if (!scrMax && !fullScreen) {
|
||||
logV("updating scrConf");
|
||||
scrConfX=scrX;
|
||||
scrConfY=scrY;
|
||||
scrConfW=scrW;
|
||||
|
|
@ -3518,8 +3545,8 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
// update canvas size as well
|
||||
if (SDL_GetRendererOutputSize(sdlRend,&canvasW,&canvasH)!=0) {
|
||||
logW("loop: error while getting output size! %s",SDL_GetError());
|
||||
if (!rend->getOutputSize(canvasW,canvasH)) {
|
||||
logW("loop: error while getting output size!");
|
||||
} else {
|
||||
//logV("updateWindow: canvas size %dx%d",canvasW,canvasH);
|
||||
// and therefore window size
|
||||
|
|
@ -3740,9 +3767,13 @@ bool FurnaceGUI::loop() {
|
|||
});
|
||||
}
|
||||
|
||||
bool fontsFailed=false;
|
||||
|
||||
layoutTimeBegin=SDL_GetPerformanceCounter();
|
||||
|
||||
ImGui_ImplSDLRenderer_NewFrame();
|
||||
if (!rend->newFrame()) {
|
||||
fontsFailed=true;
|
||||
}
|
||||
ImGui_ImplSDL2_NewFrame(sdlWin);
|
||||
ImGui::NewFrame();
|
||||
|
||||
|
|
@ -3768,6 +3799,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
if (ImGui::BeginMenu("open recent")) {
|
||||
exitDisabledTimer=1;
|
||||
for (int i=0; i<(int)recentFile.size(); i++) {
|
||||
String item=recentFile[i];
|
||||
if (ImGui::MenuItem(item.c_str())) {
|
||||
|
|
@ -3814,6 +3846,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("export audio...")) {
|
||||
exitDisabledTimer=1;
|
||||
if (ImGui::MenuItem("one file")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
|
||||
}
|
||||
|
|
@ -3832,6 +3865,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("export VGM...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text("settings:");
|
||||
if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) {
|
||||
for (int i=0; i<7; i++) {
|
||||
|
|
@ -3920,6 +3954,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
if (numZSMCompat > 0) {
|
||||
if (ImGui::BeginMenu("export ZSM...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text("Commander X16 Zsound Music File");
|
||||
if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) {
|
||||
if (zsmExportTickRate<1) zsmExportTickRate=1;
|
||||
|
|
@ -3940,6 +3975,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
if (numAmiga && settings.iCannotWait) {
|
||||
if (ImGui::BeginMenu("export Amiga validation data...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
|
|
@ -3970,6 +4006,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
if (ImGui::BeginMenu("export command stream...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Text(
|
||||
"this option exports a text or binary file which\n"
|
||||
"contains a dump of the internal command stream\n"
|
||||
|
|
@ -3987,6 +4024,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("add chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
DivSystem picked=systemPicker();
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
if (!e->addSystem(picked)) {
|
||||
|
|
@ -4003,6 +4041,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("configure chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
|
||||
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
|
||||
|
|
@ -4012,6 +4051,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("change chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
|
||||
|
|
@ -4031,6 +4071,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("remove chip...")) {
|
||||
exitDisabledTimer=1;
|
||||
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (ImGui::MenuItem(fmt::sprintf("%d. %s##_SYSR%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
|
||||
|
|
@ -4047,6 +4088,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::BeginDisabled(exitDisabledTimer);
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("restore backup",BIND_FOR(GUI_ACTION_OPEN_BACKUP))) {
|
||||
doAction(GUI_ACTION_OPEN_BACKUP);
|
||||
|
|
@ -4059,7 +4101,10 @@ bool FurnaceGUI::loop() {
|
|||
quit=true;
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndMenu();
|
||||
} else {
|
||||
exitDisabledTimer=0;
|
||||
}
|
||||
if (ImGui::BeginMenu("edit")) {
|
||||
ImGui::Text("...");
|
||||
|
|
@ -4310,6 +4355,7 @@ bool FurnaceGUI::loop() {
|
|||
MEASURE(log,drawLog());
|
||||
MEASURE(compatFlags,drawCompatFlags());
|
||||
MEASURE(stats,drawStats());
|
||||
MEASURE(chanOsc,drawChanOsc());
|
||||
} else {
|
||||
globalWinFlags=0;
|
||||
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
||||
|
|
@ -4381,7 +4427,7 @@ bool FurnaceGUI::loop() {
|
|||
portrait=(scrW<scrH);
|
||||
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
|
||||
|
||||
SDL_GetRendererOutputSize(sdlRend,&canvasW,&canvasH);
|
||||
rend->getOutputSize(canvasW,canvasH);
|
||||
#endif
|
||||
if (patternOpen) nextWindow=GUI_WINDOW_PATTERN;
|
||||
#ifdef __APPLE__
|
||||
|
|
@ -4447,6 +4493,7 @@ bool FurnaceGUI::loop() {
|
|||
case GUI_FILE_SAMPLE_OPEN_REPLACE:
|
||||
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
|
||||
case GUI_FILE_SAMPLE_SAVE:
|
||||
case GUI_FILE_SAMPLE_SAVE_RAW:
|
||||
workingDirSample=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||
break;
|
||||
case GUI_FILE_EXPORT_AUDIO_ONE:
|
||||
|
|
@ -4714,7 +4761,16 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
case GUI_FILE_SAMPLE_SAVE:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
||||
e->song.sample[curSample]->save(copyOfName.c_str());
|
||||
if (!e->song.sample[curSample]->save(copyOfName.c_str())) {
|
||||
showError("could not save sample! open Log Viewer for more information.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_SAMPLE_SAVE_RAW:
|
||||
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
|
||||
if (!e->song.sample[curSample]->saveRaw(copyOfName.c_str())) {
|
||||
showError("could not save sample! open Log Viewer for more information.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_EXPORT_AUDIO_ONE:
|
||||
|
|
@ -5023,6 +5079,10 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::OpenPopup("New Song");
|
||||
}
|
||||
|
||||
if (displayEditString) {
|
||||
ImGui::OpenPopup("EditString");
|
||||
}
|
||||
|
||||
if (nextWindow==GUI_WINDOW_ABOUT) {
|
||||
aboutOpen=true;
|
||||
nextWindow=GUI_WINDOW_NOTHING;
|
||||
|
|
@ -5044,7 +5104,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
//drawTutorial();
|
||||
drawTutorial();
|
||||
|
||||
ImVec2 newSongMinSize=mobileUI?ImVec2(canvasW-(portrait?0:(60.0*dpiScale)),canvasH-60.0*dpiScale):ImVec2(400.0f*dpiScale,200.0f*dpiScale);
|
||||
ImVec2 newSongMaxSize=ImVec2(canvasW-((mobileUI && !portrait)?(60.0*dpiScale):0),canvasH-(mobileUI?(60.0*dpiScale):0));
|
||||
|
|
@ -5562,6 +5622,27 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("EditString",ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
|
||||
if (editString==NULL) {
|
||||
ImGui::Text("Error! No string provided!");
|
||||
} else {
|
||||
if (displayEditString) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
ImGui::InputText("##StringVal",editString);
|
||||
}
|
||||
displayEditString=false;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("OK") || ImGui::IsKeyPressed(ImGuiKey_Enter,false)) {
|
||||
editString=NULL;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
editString=NULL;
|
||||
}
|
||||
|
||||
MEASURE_END(popup);
|
||||
|
||||
if (!tutorial.introPlayed || settings.alwaysPlayIntro!=0) {
|
||||
|
|
@ -5602,7 +5683,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
logD("saving backup...");
|
||||
SafeWriter* w=e->saveFur(true);
|
||||
SafeWriter* w=e->saveFur(true,true);
|
||||
logV("writing file...");
|
||||
|
||||
if (w!=NULL) {
|
||||
|
|
@ -5724,33 +5805,35 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].y*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].z*255,
|
||||
uiColors[GUI_COLOR_BACKGROUND].w*255);
|
||||
SDL_RenderClear(sdlRend);
|
||||
if (!settings.renderClearPos) {
|
||||
rend->clear(uiColors[GUI_COLOR_BACKGROUND]);
|
||||
}
|
||||
renderTimeBegin=SDL_GetPerformanceCounter();
|
||||
ImGui::Render();
|
||||
renderTimeEnd=SDL_GetPerformanceCounter();
|
||||
ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData());
|
||||
drawTimeBegin=SDL_GetPerformanceCounter();
|
||||
rend->renderGUI();
|
||||
if (mustClear) {
|
||||
SDL_RenderClear(sdlRend);
|
||||
rend->clear(ImVec4(0,0,0,0));
|
||||
mustClear--;
|
||||
} else {
|
||||
if (initialScreenWipe>0.0f && !settings.disableFadeIn) {
|
||||
WAKE_UP;
|
||||
initialScreenWipe-=ImGui::GetIO().DeltaTime*5.0f;
|
||||
if (initialScreenWipe>0.0f) {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(sdlRend,0,0,0,255*pow(initialScreenWipe,2.0f));
|
||||
SDL_RenderFillRect(sdlRend,NULL);
|
||||
rend->wipe(pow(initialScreenWipe,2.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_RenderPresent(sdlRend);
|
||||
drawTimeEnd=SDL_GetPerformanceCounter();
|
||||
rend->present();
|
||||
if (settings.renderClearPos) {
|
||||
rend->clear(uiColors[GUI_COLOR_BACKGROUND]);
|
||||
}
|
||||
|
||||
layoutTimeDelta=layoutTimeEnd-layoutTimeBegin;
|
||||
renderTimeDelta=renderTimeEnd-renderTimeBegin;
|
||||
drawTimeDelta=drawTimeEnd-drawTimeBegin;
|
||||
eventTimeDelta=eventTimeEnd-eventTimeBegin;
|
||||
|
||||
soloTimeout-=ImGui::GetIO().DeltaTime;
|
||||
|
|
@ -5760,6 +5843,10 @@ bool FurnaceGUI::loop() {
|
|||
WAKE_UP;
|
||||
}
|
||||
|
||||
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
|
||||
exitDisabledTimer=0;
|
||||
}
|
||||
|
||||
wheelX=0;
|
||||
wheelY=0;
|
||||
wantScrollList=false;
|
||||
|
|
@ -5772,6 +5859,20 @@ bool FurnaceGUI::loop() {
|
|||
willCommit=false;
|
||||
}
|
||||
|
||||
if (fontsFailed) {
|
||||
showError("it appears I couldn't load these fonts. any setting you can check?");
|
||||
logE("couldn't load fonts");
|
||||
ImGui::GetIO().Fonts->Clear();
|
||||
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
|
||||
patFont=mainFont;
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error again while building font atlas!");
|
||||
} else {
|
||||
rend->createFontsTexture();
|
||||
}
|
||||
}
|
||||
|
||||
if (!editOptsVisible) {
|
||||
latchTarget=0;
|
||||
latchNibble=false;
|
||||
|
|
@ -5846,6 +5947,10 @@ bool FurnaceGUI::init() {
|
|||
basicMode=true;
|
||||
}
|
||||
|
||||
insListDir=e->getConfBool("insListDir",false);
|
||||
waveListDir=e->getConfBool("waveListDir",false);
|
||||
sampleListDir=e->getConfBool("sampleListDir",false);
|
||||
|
||||
tempoView=e->getConfBool("tempoView",true);
|
||||
waveHex=e->getConfBool("waveHex",false);
|
||||
waveSigned=e->getConfBool("waveSigned",false);
|
||||
|
|
@ -5880,6 +5985,7 @@ bool FurnaceGUI::init() {
|
|||
pianoOptions=e->getConfBool("pianoOptions",pianoOptions);
|
||||
pianoSharePosition=e->getConfBool("pianoSharePosition",pianoSharePosition);
|
||||
pianoOptionsSet=e->getConfBool("pianoOptionsSet",pianoOptionsSet);
|
||||
pianoReadonly=e->getConfBool("pianoReadonly",false);
|
||||
pianoOffset=e->getConfInt("pianoOffset",pianoOffset);
|
||||
pianoOffsetEdit=e->getConfInt("pianoOffsetEdit",pianoOffsetEdit);
|
||||
pianoView=e->getConfInt("pianoView",pianoView);
|
||||
|
|
@ -5920,7 +6026,9 @@ bool FurnaceGUI::init() {
|
|||
e->setAutoNotePoly(noteInputPoly);
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER,"1");
|
||||
#if SDL_VERSION_ATLEAST(2,0,17)
|
||||
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS,"0");
|
||||
#endif
|
||||
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS,"0");
|
||||
// don't disable compositing on KWin
|
||||
#if SDL_VERSION_ATLEAST(2,0,22)
|
||||
|
|
@ -5929,7 +6037,18 @@ bool FurnaceGUI::init() {
|
|||
#endif
|
||||
|
||||
// initialize SDL
|
||||
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_HAPTIC);
|
||||
logD("initializing video...");
|
||||
if (SDL_Init(SDL_INIT_VIDEO)!=0) {
|
||||
logE("could not initialize video! %s",SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
logD("initializing haptic...");
|
||||
if (SDL_Init(SDL_INIT_HAPTIC)!=0) {
|
||||
logW("could not initialize haptic! %s",SDL_GetError());
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* videoBackend=SDL_GetCurrentVideoDriver();
|
||||
if (videoBackend!=NULL) {
|
||||
|
|
@ -6030,7 +6149,28 @@ bool FurnaceGUI::init() {
|
|||
|
||||
logV("window size: %dx%d",scrW,scrH);
|
||||
|
||||
sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW,scrH,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0));
|
||||
if (!initRender()) {
|
||||
if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) {
|
||||
settings.renderBackend="";
|
||||
e->setConf("renderBackend","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace.");
|
||||
} else {
|
||||
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
|
||||
if (!settings.renderDriver.empty()) {
|
||||
settings.renderDriver="";
|
||||
e->setConf("renderDriver","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
rend->preInit();
|
||||
|
||||
logD("creating window...");
|
||||
sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW,scrH,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)|rend->getWindowFlags());
|
||||
if (sdlWin==NULL) {
|
||||
lastError=fmt::sprintf("could not open window! %s",SDL_GetError());
|
||||
return false;
|
||||
|
|
@ -6094,22 +6234,28 @@ bool FurnaceGUI::init() {
|
|||
SDL_SetHint(SDL_HINT_RENDER_DRIVER,settings.renderDriver.c_str());
|
||||
}
|
||||
|
||||
sdlRend=SDL_CreateRenderer(sdlWin,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
|
||||
|
||||
if (sdlRend==NULL) {
|
||||
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
|
||||
if (!settings.renderDriver.empty()) {
|
||||
settings.renderDriver="";
|
||||
e->setConf("renderDriver","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
|
||||
logD("starting render backend...");
|
||||
if (!rend->init(sdlWin)) {
|
||||
if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) {
|
||||
settings.renderBackend="";
|
||||
//e->setConf("renderBackend","");
|
||||
//e->saveConf();
|
||||
//lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace.");
|
||||
} else {
|
||||
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
|
||||
if (!settings.renderDriver.empty()) {
|
||||
settings.renderDriver="";
|
||||
e->setConf("renderDriver","");
|
||||
e->saveConf();
|
||||
lastError=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// try acquiring the canvas size
|
||||
if (SDL_GetRendererOutputSize(sdlRend,&canvasW,&canvasH)!=0) {
|
||||
logW("could not get renderer output size! %s",SDL_GetError());
|
||||
if (!rend->getOutputSize(canvasW,canvasH)) {
|
||||
logW("could not get renderer output size!");
|
||||
} else {
|
||||
logV("canvas size: %dx%d",canvasW,canvasH);
|
||||
}
|
||||
|
|
@ -6117,30 +6263,45 @@ bool FurnaceGUI::init() {
|
|||
// special consideration for Wayland
|
||||
if (settings.dpiScale<0.5f) {
|
||||
if (strcmp(videoBackend,"wayland")==0) {
|
||||
dpiScale=(double)canvasW/(double)scrW;
|
||||
int realW=scrW;
|
||||
int realH=scrH;
|
||||
|
||||
SDL_GetWindowSize(sdlWin,&realW,&realH);
|
||||
|
||||
if (realW<1) {
|
||||
logW("screen width is zero!\n");
|
||||
dpiScale=1.0;
|
||||
} else {
|
||||
dpiScale=(double)canvasW/(double)realW;
|
||||
logV("we're on Wayland... scaling factor: %f",dpiScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
updateWindowTitle();
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(sdlWin,sdlRend);
|
||||
ImGui_ImplSDLRenderer_Init(sdlRend);
|
||||
rend->clear(ImVec4(0.0,0.0,0.0,1.0));
|
||||
rend->present();
|
||||
|
||||
logD("preparing user interface...");
|
||||
rend->initGUI(sdlWin);
|
||||
|
||||
applyUISettings();
|
||||
|
||||
logD("building font...");
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error while building font atlas!");
|
||||
showError("error while loading fonts! please check your settings.");
|
||||
ImGui::GetIO().Fonts->Clear();
|
||||
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
|
||||
patFont=mainFont;
|
||||
ImGui_ImplSDLRenderer_DestroyFontsTexture();
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error again while building font atlas!");
|
||||
}
|
||||
}
|
||||
|
||||
logD("preparing layout...");
|
||||
strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095);
|
||||
backupPath=e->getConfigPath();
|
||||
if (backupPath.size()>0) {
|
||||
|
|
@ -6152,8 +6313,6 @@ bool FurnaceGUI::init() {
|
|||
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
|
||||
toggleMobileUI(mobileUI,true);
|
||||
|
||||
updateWindowTitle();
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
oldPat[i]=new DivPattern;
|
||||
}
|
||||
|
|
@ -6200,6 +6359,7 @@ bool FurnaceGUI::init() {
|
|||
return curIns;
|
||||
});
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
vibrator=SDL_HapticOpen(0);
|
||||
if (vibrator==NULL) {
|
||||
logD("could not open vibration device: %s",SDL_GetError());
|
||||
|
|
@ -6210,7 +6370,9 @@ bool FurnaceGUI::init() {
|
|||
logD("vibration not available: %s",SDL_GetError());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
logI("done!");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -6273,6 +6435,11 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("spoilerOpen",spoilerOpen);
|
||||
e->setConf("basicMode",basicMode);
|
||||
|
||||
// commit dir state
|
||||
e->setConf("insListDir",insListDir);
|
||||
e->setConf("waveListDir",waveListDir);
|
||||
e->setConf("sampleListDir",sampleListDir);
|
||||
|
||||
// commit last window size
|
||||
e->setConf("lastWindowWidth",scrConfW);
|
||||
e->setConf("lastWindowHeight",scrConfH);
|
||||
|
|
@ -6310,6 +6477,7 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("pianoOptions",pianoOptions);
|
||||
e->setConf("pianoSharePosition",pianoSharePosition);
|
||||
e->setConf("pianoOptionsSet",pianoOptionsSet);
|
||||
e->setConf("pianoReadonly",pianoReadonly);
|
||||
e->setConf("pianoOffset",pianoOffset);
|
||||
e->setConf("pianoOffsetEdit",pianoOffsetEdit);
|
||||
e->setConf("pianoView",pianoView);
|
||||
|
|
@ -6342,10 +6510,10 @@ void FurnaceGUI::commitState() {
|
|||
|
||||
bool FurnaceGUI::finish() {
|
||||
commitState();
|
||||
ImGui_ImplSDLRenderer_Shutdown();
|
||||
rend->quitGUI();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
SDL_DestroyRenderer(sdlRend);
|
||||
quitRender();
|
||||
SDL_DestroyWindow(sdlWin);
|
||||
|
||||
if (vibrator) {
|
||||
|
|
@ -6365,8 +6533,9 @@ bool FurnaceGUI::finish() {
|
|||
|
||||
FurnaceGUI::FurnaceGUI():
|
||||
e(NULL),
|
||||
renderBackend(GUI_BACKEND_SDL),
|
||||
rend(NULL),
|
||||
sdlWin(NULL),
|
||||
sdlRend(NULL),
|
||||
vibrator(NULL),
|
||||
vibratorAvailable(false),
|
||||
sampleTex(NULL),
|
||||
|
|
@ -6401,6 +6570,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
displayPendingRawSample(false),
|
||||
snesFilterHex(false),
|
||||
modTableHex(false),
|
||||
displayEditString(false),
|
||||
mobileEdit(false),
|
||||
vgmExportVersion(0x171),
|
||||
vgmExportTrailingTicks(-1),
|
||||
|
|
@ -6420,6 +6590,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
fmPreviewOn(false),
|
||||
fmPreviewPaused(false),
|
||||
fmPreviewOPN(NULL),
|
||||
editString(NULL),
|
||||
pendingRawSampleDepth(8),
|
||||
pendingRawSampleChannels(1),
|
||||
pendingRawSampleUnsigned(false),
|
||||
|
|
@ -6474,7 +6645,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
loopEnd(-1),
|
||||
isClipping(0),
|
||||
extraChannelButtons(0),
|
||||
patNameTarget(-1),
|
||||
newSongCategory(0),
|
||||
latchTarget(0),
|
||||
wheelX(0),
|
||||
|
|
@ -6486,6 +6656,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
oldBeat(-1),
|
||||
oldBar(-1),
|
||||
curGroove(-1),
|
||||
exitDisabledTimer(0),
|
||||
soloTimeout(0.0f),
|
||||
exportFadeOut(5.0),
|
||||
editControlsOpen(true),
|
||||
|
|
@ -6524,6 +6695,10 @@ FurnaceGUI::FurnaceGUI():
|
|||
groovesOpen(false),
|
||||
introMonOpen(false),
|
||||
basicMode(true),
|
||||
shortIntro(false),
|
||||
insListDir(false),
|
||||
waveListDir(false),
|
||||
sampleListDir(false),
|
||||
clockShowReal(true),
|
||||
clockShowRow(true),
|
||||
clockShowBeat(true),
|
||||
|
|
@ -6541,7 +6716,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
collapseWindow(false),
|
||||
demandScrollX(false),
|
||||
fancyPattern(false),
|
||||
wantPatName(false),
|
||||
firstFrame(true),
|
||||
tempoView(true),
|
||||
waveHex(false),
|
||||
|
|
@ -6558,6 +6732,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
dragMobileMenu(false),
|
||||
dragMobileEditButton(false),
|
||||
wantGrooveListFocus(false),
|
||||
lastAssetType(0),
|
||||
curWindow(GUI_WINDOW_NOTHING),
|
||||
nextWindow(GUI_WINDOW_NOTHING),
|
||||
curWindowLast(GUI_WINDOW_NOTHING),
|
||||
|
|
@ -6573,6 +6748,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
wavePreviewLen(32),
|
||||
wavePreviewHeight(255),
|
||||
wavePreviewInit(true),
|
||||
wavePreviewPaused(false),
|
||||
pgSys(0),
|
||||
pgAddr(0),
|
||||
pgVal(0),
|
||||
|
|
@ -6662,6 +6838,9 @@ FurnaceGUI::FurnaceGUI():
|
|||
renderTimeBegin(0),
|
||||
renderTimeEnd(0),
|
||||
renderTimeDelta(0),
|
||||
drawTimeBegin(0),
|
||||
drawTimeEnd(0),
|
||||
drawTimeDelta(0),
|
||||
eventTimeBegin(0),
|
||||
eventTimeEnd(0),
|
||||
eventTimeDelta(0),
|
||||
|
|
@ -6670,11 +6849,14 @@ FurnaceGUI::FurnaceGUI():
|
|||
sysToMove(-1),
|
||||
sysToDelete(-1),
|
||||
opToMove(-1),
|
||||
assetToMove(-1),
|
||||
dirToMove(-1),
|
||||
transposeAmount(0),
|
||||
randomizeMin(0),
|
||||
randomizeMax(255),
|
||||
fadeMin(0),
|
||||
fadeMax(255),
|
||||
collapseAmount(2),
|
||||
scaleMax(100.0f),
|
||||
fadeMode(false),
|
||||
randomMode(false),
|
||||
|
|
@ -6698,6 +6880,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
sampleDragMode(false),
|
||||
sampleDrag16(false),
|
||||
sampleZoomAuto(true),
|
||||
sampleSelTarget(0),
|
||||
sampleDragTarget(NULL),
|
||||
sampleDragStart(0,0),
|
||||
sampleDragAreaSize(0,0),
|
||||
|
|
@ -6746,6 +6929,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
pianoOptions(true),
|
||||
pianoSharePosition(false),
|
||||
pianoOptionsSet(false),
|
||||
pianoReadonly(false),
|
||||
pianoOffset(6),
|
||||
pianoOffsetEdit(9),
|
||||
pianoView(PIANO_LAYOUT_AUTOMATIC),
|
||||
|
|
@ -6755,6 +6939,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
pianoOctavesEdit(4),
|
||||
pianoOptions(false),
|
||||
pianoSharePosition(true),
|
||||
pianoReadonly(false),
|
||||
pianoOffset(6),
|
||||
pianoOffsetEdit(6),
|
||||
pianoView(PIANO_LAYOUT_STANDARD),
|
||||
|
|
@ -6779,6 +6964,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
mustClear(2),
|
||||
initialScreenWipe(1.0f),
|
||||
introSkipDo(false),
|
||||
introStopped(false),
|
||||
curTutorial(-1),
|
||||
curTutorialStep(0) {
|
||||
// value keys
|
||||
|
|
|
|||
149
src/gui/gui.h
149
src/gui/gui.h
|
|
@ -23,8 +23,7 @@
|
|||
#include "../engine/engine.h"
|
||||
#include "../engine/waveSynth.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "imgui_impl_sdlrenderer.h"
|
||||
#include "imgui_impl_sdl2.h"
|
||||
#include <SDL.h>
|
||||
#include <fftw3.h>
|
||||
#include <deque>
|
||||
|
|
@ -70,19 +69,59 @@
|
|||
|
||||
#define FM_PREVIEW_SIZE 512
|
||||
|
||||
enum FurnaceGUIRenderBackend {
|
||||
GUI_BACKEND_SDL=0,
|
||||
GUI_BACKEND_GL,
|
||||
GUI_BACKEND_DX11
|
||||
};
|
||||
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL
|
||||
#define GUI_BACKEND_DEFAULT_NAME "SDL"
|
||||
#else
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_DX11
|
||||
#define GUI_BACKEND_DEFAULT_NAME "DirectX 11"
|
||||
#else
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_GL
|
||||
#define GUI_BACKEND_DEFAULT_NAME "OpenGL"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// TODO:
|
||||
// - add colors for FM envelope and waveform
|
||||
// - maybe add "alternate" color for FM modulators/carriers (a bit difficult)
|
||||
enum FurnaceGUIColors {
|
||||
GUI_COLOR_BACKGROUND=0,
|
||||
GUI_COLOR_FRAME_BACKGROUND,
|
||||
GUI_COLOR_FRAME_BACKGROUND_CHILD,
|
||||
GUI_COLOR_FRAME_BACKGROUND_POPUP,
|
||||
GUI_COLOR_MODAL_BACKDROP,
|
||||
GUI_COLOR_HEADER,
|
||||
GUI_COLOR_TEXT,
|
||||
GUI_COLOR_ACCENT_PRIMARY,
|
||||
GUI_COLOR_ACCENT_SECONDARY,
|
||||
GUI_COLOR_TITLE_INACTIVE,
|
||||
GUI_COLOR_TITLE_COLLAPSED,
|
||||
GUI_COLOR_MENU_BAR,
|
||||
GUI_COLOR_BORDER,
|
||||
GUI_COLOR_BORDER_SHADOW,
|
||||
GUI_COLOR_SCROLL_BACKGROUND,
|
||||
GUI_COLOR_SCROLL,
|
||||
GUI_COLOR_SCROLL_HOVER,
|
||||
GUI_COLOR_SCROLL_ACTIVE,
|
||||
GUI_COLOR_SEPARATOR,
|
||||
GUI_COLOR_SEPARATOR_HOVER,
|
||||
GUI_COLOR_SEPARATOR_ACTIVE,
|
||||
GUI_COLOR_DOCKING_PREVIEW,
|
||||
GUI_COLOR_DOCKING_EMPTY,
|
||||
GUI_COLOR_TABLE_HEADER,
|
||||
GUI_COLOR_TABLE_BORDER_HARD,
|
||||
GUI_COLOR_TABLE_BORDER_SOFT,
|
||||
GUI_COLOR_DRAG_DROP_TARGET,
|
||||
GUI_COLOR_NAV_HIGHLIGHT,
|
||||
GUI_COLOR_NAV_WIN_HIGHLIGHT,
|
||||
GUI_COLOR_NAV_WIN_BACKDROP,
|
||||
GUI_COLOR_TOGGLE_OFF,
|
||||
GUI_COLOR_TOGGLE_ON,
|
||||
GUI_COLOR_EDITING,
|
||||
|
|
@ -366,6 +405,7 @@ enum FurnaceGUIFileDialogs {
|
|||
GUI_FILE_SAMPLE_OPEN_REPLACE,
|
||||
GUI_FILE_SAMPLE_OPEN_REPLACE_RAW,
|
||||
GUI_FILE_SAMPLE_SAVE,
|
||||
GUI_FILE_SAMPLE_SAVE_RAW,
|
||||
GUI_FILE_EXPORT_AUDIO_ONE,
|
||||
GUI_FILE_EXPORT_AUDIO_PER_SYS,
|
||||
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
|
||||
|
|
@ -566,6 +606,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_INS_LIST_EDIT,
|
||||
GUI_ACTION_INS_LIST_UP,
|
||||
GUI_ACTION_INS_LIST_DOWN,
|
||||
GUI_ACTION_INS_LIST_DIR_VIEW,
|
||||
GUI_ACTION_INS_LIST_MAX,
|
||||
|
||||
GUI_ACTION_WAVE_LIST_MIN,
|
||||
|
|
@ -582,6 +623,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_WAVE_LIST_EDIT,
|
||||
GUI_ACTION_WAVE_LIST_UP,
|
||||
GUI_ACTION_WAVE_LIST_DOWN,
|
||||
GUI_ACTION_WAVE_LIST_DIR_VIEW,
|
||||
GUI_ACTION_WAVE_LIST_MAX,
|
||||
|
||||
GUI_ACTION_SAMPLE_LIST_MIN,
|
||||
|
|
@ -592,6 +634,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_SAMPLE_LIST_OPEN_RAW,
|
||||
GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW,
|
||||
GUI_ACTION_SAMPLE_LIST_SAVE,
|
||||
GUI_ACTION_SAMPLE_LIST_SAVE_RAW,
|
||||
GUI_ACTION_SAMPLE_LIST_MOVE_UP,
|
||||
GUI_ACTION_SAMPLE_LIST_MOVE_DOWN,
|
||||
GUI_ACTION_SAMPLE_LIST_DELETE,
|
||||
|
|
@ -600,6 +643,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_SAMPLE_LIST_DOWN,
|
||||
GUI_ACTION_SAMPLE_LIST_PREVIEW,
|
||||
GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW,
|
||||
GUI_ACTION_SAMPLE_LIST_DIR_VIEW,
|
||||
GUI_ACTION_SAMPLE_LIST_MAX,
|
||||
|
||||
GUI_ACTION_SAMPLE_MIN,
|
||||
|
|
@ -1166,7 +1210,7 @@ struct FurnaceGUIQueryResult {
|
|||
|
||||
struct FurnaceGUIImage {
|
||||
unsigned char* data;
|
||||
SDL_Texture* tex;
|
||||
void* tex;
|
||||
int width, height, ch;
|
||||
|
||||
FurnaceGUIImage():
|
||||
|
|
@ -1188,15 +1232,52 @@ struct FurnaceGUIPerfMetric {
|
|||
elapsed(0) {}
|
||||
};
|
||||
|
||||
enum FurnaceGUIBlendMode {
|
||||
GUI_BLEND_MODE_NONE=0,
|
||||
GUI_BLEND_MODE_BLEND,
|
||||
GUI_BLEND_MODE_ADD,
|
||||
GUI_BLEND_MODE_MULTIPLY
|
||||
};
|
||||
|
||||
class FurnaceGUIRender {
|
||||
public:
|
||||
virtual ImTextureID getTextureID(void* which);
|
||||
virtual bool lockTexture(void* which, void** data, int* pitch);
|
||||
virtual bool unlockTexture(void* which);
|
||||
virtual bool updateTexture(void* which, void* data, int pitch);
|
||||
virtual void* createTexture(bool dynamic, int width, int height);
|
||||
virtual bool destroyTexture(void* which);
|
||||
virtual void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
virtual void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
virtual void resized(const SDL_Event& ev);
|
||||
virtual void clear(ImVec4 color);
|
||||
virtual bool newFrame();
|
||||
virtual void createFontsTexture();
|
||||
virtual void destroyFontsTexture();
|
||||
virtual void renderGUI();
|
||||
virtual void wipe(float alpha);
|
||||
virtual void present();
|
||||
virtual bool getOutputSize(int& w, int& h);
|
||||
virtual int getWindowFlags();
|
||||
virtual void preInit();
|
||||
virtual bool init(SDL_Window* win);
|
||||
virtual void initGUI(SDL_Window* win);
|
||||
virtual void quitGUI();
|
||||
virtual bool quit();
|
||||
virtual ~FurnaceGUIRender();
|
||||
};
|
||||
|
||||
class FurnaceGUI {
|
||||
DivEngine* e;
|
||||
|
||||
FurnaceGUIRenderBackend renderBackend;
|
||||
FurnaceGUIRender* rend;
|
||||
|
||||
SDL_Window* sdlWin;
|
||||
SDL_Renderer* sdlRend;
|
||||
SDL_Haptic* vibrator;
|
||||
bool vibratorAvailable;
|
||||
|
||||
SDL_Texture* sampleTex;
|
||||
|
||||
void* sampleTex;
|
||||
int sampleTexW, sampleTexH;
|
||||
bool updateSampleTex;
|
||||
|
||||
|
|
@ -1206,6 +1287,7 @@ class FurnaceGUI {
|
|||
String workingDirLayout, workingDirROM, workingDirTest;
|
||||
String mmlString[32];
|
||||
String mmlStringW, mmlStringSNES, grooveString, grooveListString, mmlStringModTable;
|
||||
String folderString;
|
||||
|
||||
std::vector<DivSystem> sysSearchResults;
|
||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||
|
|
@ -1218,7 +1300,7 @@ class FurnaceGUI {
|
|||
bool portrait, injectBackUp, mobileMenuOpen;
|
||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||
bool mobileEdit;
|
||||
bool willExport[DIV_MAX_CHIPS];
|
||||
int vgmExportVersion;
|
||||
|
|
@ -1236,6 +1318,7 @@ class FurnaceGUI {
|
|||
short fmPreview[FM_PREVIEW_SIZE];
|
||||
bool updateFMPreview, fmPreviewOn, fmPreviewPaused;
|
||||
void* fmPreviewOPN;
|
||||
String* editString;
|
||||
|
||||
String pendingRawSample;
|
||||
int pendingRawSampleDepth, pendingRawSampleChannels;
|
||||
|
|
@ -1401,8 +1484,10 @@ class FurnaceGUI {
|
|||
int channelFont;
|
||||
int channelTextCenter;
|
||||
int midiOutClock;
|
||||
int midiOutTime;
|
||||
int midiOutProgramChange;
|
||||
int midiOutMode;
|
||||
int midiOutTimeRate;
|
||||
int maxRecentFile;
|
||||
int centerPattern;
|
||||
int ordersCursor;
|
||||
|
|
@ -1416,6 +1501,11 @@ class FurnaceGUI {
|
|||
int alwaysPlayIntro;
|
||||
int iCannotWait;
|
||||
int orderButtonPos;
|
||||
int compress;
|
||||
int newPatternFormat;
|
||||
int renderClearPos;
|
||||
int insertBehavior;
|
||||
int pullDeleteRow;
|
||||
unsigned int maxUndoSteps;
|
||||
String mainFontPath;
|
||||
String patFontPath;
|
||||
|
|
@ -1423,6 +1513,7 @@ class FurnaceGUI {
|
|||
String midiInDevice;
|
||||
String midiOutDevice;
|
||||
String c163Name;
|
||||
String renderBackend;
|
||||
String renderDriver;
|
||||
String initialSysName;
|
||||
String noteOffLabel;
|
||||
|
|
@ -1544,8 +1635,10 @@ class FurnaceGUI {
|
|||
channelFont(1),
|
||||
channelTextCenter(1),
|
||||
midiOutClock(0),
|
||||
midiOutTime(0),
|
||||
midiOutProgramChange(0),
|
||||
midiOutMode(1),
|
||||
midiOutTimeRate(0),
|
||||
maxRecentFile(10),
|
||||
centerPattern(0),
|
||||
ordersCursor(1),
|
||||
|
|
@ -1559,6 +1652,11 @@ class FurnaceGUI {
|
|||
alwaysPlayIntro(0),
|
||||
iCannotWait(0),
|
||||
orderButtonPos(2),
|
||||
compress(1),
|
||||
newPatternFormat(1),
|
||||
renderClearPos(0),
|
||||
insertBehavior(1),
|
||||
pullDeleteRow(1),
|
||||
maxUndoSteps(100),
|
||||
mainFontPath(""),
|
||||
patFontPath(""),
|
||||
|
|
@ -1566,6 +1664,7 @@ class FurnaceGUI {
|
|||
midiInDevice(""),
|
||||
midiOutDevice(""),
|
||||
c163Name(""),
|
||||
renderBackend(""),
|
||||
renderDriver(""),
|
||||
initialSysName("Sega Genesis/Mega Drive"),
|
||||
noteOffLabel("OFF"),
|
||||
|
|
@ -1578,12 +1677,12 @@ class FurnaceGUI {
|
|||
struct Tutorial {
|
||||
int userComesFrom;
|
||||
bool introPlayed;
|
||||
bool welcome;
|
||||
bool protoWelcome;
|
||||
bool taken[GUI_TUTORIAL_MAX];
|
||||
Tutorial():
|
||||
userComesFrom(0),
|
||||
introPlayed(false),
|
||||
welcome(false) {
|
||||
protoWelcome(false) {
|
||||
memset(taken,0,GUI_TUTORIAL_MAX*sizeof(bool));
|
||||
}
|
||||
} tutorial;
|
||||
|
|
@ -1593,9 +1692,9 @@ class FurnaceGUI {
|
|||
DivInstrument* prevInsData;
|
||||
|
||||
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
|
||||
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
|
||||
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, newSongCategory, latchTarget;
|
||||
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
|
||||
int curGroove;
|
||||
int curGroove, exitDisabledTimer;
|
||||
float soloTimeout;
|
||||
|
||||
double exportFadeOut;
|
||||
|
|
@ -1609,14 +1708,16 @@ class FurnaceGUI {
|
|||
bool groovesOpen, introMonOpen;
|
||||
|
||||
bool basicMode, shortIntro;
|
||||
bool insListDir, waveListDir, sampleListDir;
|
||||
|
||||
bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
|
||||
float clockMetroTick[16];
|
||||
|
||||
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
|
||||
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
|
||||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||
bool collapseWindow, demandScrollX, fancyPattern, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||
bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus;
|
||||
unsigned char lastAssetType;
|
||||
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
||||
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
||||
float peak[DIV_MAX_OUTPUTS];
|
||||
|
|
@ -1634,7 +1735,7 @@ class FurnaceGUI {
|
|||
|
||||
DivWaveSynth wavePreview;
|
||||
int wavePreviewLen, wavePreviewHeight;
|
||||
bool wavePreviewInit;
|
||||
bool wavePreviewInit, wavePreviewPaused;
|
||||
|
||||
// bit 31: ctrl
|
||||
// bit 30: reserved for SDL scancode mask
|
||||
|
|
@ -1771,6 +1872,7 @@ class FurnaceGUI {
|
|||
|
||||
int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta;
|
||||
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
|
||||
int drawTimeBegin, drawTimeEnd, drawTimeDelta;
|
||||
int eventTimeBegin, eventTimeEnd, eventTimeDelta;
|
||||
|
||||
FurnaceGUIPerfMetric perfMetrics[64];
|
||||
|
|
@ -1782,6 +1884,7 @@ class FurnaceGUI {
|
|||
std::map<FurnaceGUIImages,FurnaceGUIImage*> images;
|
||||
|
||||
int chanToMove, sysToMove, sysToDelete, opToMove;
|
||||
int assetToMove, dirToMove;
|
||||
|
||||
ImVec2 patWindowPos, patWindowSize;
|
||||
|
||||
|
|
@ -1790,7 +1893,7 @@ class FurnaceGUI {
|
|||
ImVec2 noteCellSize, insCellSize, volCellSize, effectCellSize, effectValCellSize;
|
||||
SelectionPoint sel1, sel2;
|
||||
int dummyRows, demandX;
|
||||
int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax;
|
||||
int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax, collapseAmount;
|
||||
float scaleMax;
|
||||
bool fadeMode, randomMode, haveHitBounds, pendingStepUpdate;
|
||||
|
||||
|
|
@ -1812,6 +1915,9 @@ class FurnaceGUI {
|
|||
int sampleSelStart, sampleSelEnd;
|
||||
bool sampleInfo, sampleCompatRate;
|
||||
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
|
||||
// 0: start
|
||||
// 1: end
|
||||
unsigned char sampleSelTarget;
|
||||
void* sampleDragTarget;
|
||||
ImVec2 sampleDragStart;
|
||||
ImVec2 sampleDragAreaSize;
|
||||
|
|
@ -1846,7 +1952,7 @@ class FurnaceGUI {
|
|||
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad;
|
||||
ImVec4 chanOscColor;
|
||||
Gradient2D chanOscGrad;
|
||||
SDL_Texture* chanOscGradTex;
|
||||
void* chanOscGradTex;
|
||||
float chanOscLP0[DIV_MAX_CHANS];
|
||||
float chanOscLP1[DIV_MAX_CHANS];
|
||||
float chanOscVol[DIV_MAX_CHANS];
|
||||
|
|
@ -1898,6 +2004,7 @@ class FurnaceGUI {
|
|||
bool pianoOptions, pianoSharePosition, pianoOptionsSet;
|
||||
float pianoKeyHit[180];
|
||||
bool pianoKeyPressed[180];
|
||||
bool pianoReadonly;
|
||||
int pianoOffset, pianoOffsetEdit;
|
||||
int pianoView, pianoInputPadMode;
|
||||
|
||||
|
|
@ -1932,7 +2039,7 @@ class FurnaceGUI {
|
|||
double monitorPos;
|
||||
int mustClear;
|
||||
float initialScreenWipe;
|
||||
bool introSkipDo;
|
||||
bool introSkipDo, introStopped;
|
||||
ImVec2 introMin, introMax;
|
||||
|
||||
// tutorial
|
||||
|
|
@ -1987,6 +2094,10 @@ class FurnaceGUI {
|
|||
void actualWaveList();
|
||||
void actualSampleList();
|
||||
|
||||
void insListItem(int index, int dir, int asset);
|
||||
void waveListItem(int index, float* wavePreview, int dir, int asset);
|
||||
void sampleListItem(int index, int dir, int asset);
|
||||
|
||||
void toggleMobileUI(bool enable, bool force=false);
|
||||
|
||||
void pushToggleColors(bool status);
|
||||
|
|
@ -1995,7 +2106,7 @@ class FurnaceGUI {
|
|||
void highlightWindow(const char* winName);
|
||||
|
||||
FurnaceGUIImage* getImage(FurnaceGUIImages image);
|
||||
SDL_Texture* getTexture(FurnaceGUIImages image, SDL_BlendMode blendMode=SDL_BLENDMODE_BLEND);
|
||||
void* getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode=GUI_BLEND_MODE_BLEND);
|
||||
void drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2& pos, const ImVec2& scale, double rotate, const ImVec2& uvMin, const ImVec2& uvMax, const ImVec4& imgColor);
|
||||
|
||||
void drawMobileControls();
|
||||
|
|
@ -2144,9 +2255,13 @@ class FurnaceGUI {
|
|||
String encodeKeyMap(std::map<int,int>& map);
|
||||
void decodeKeyMap(std::map<int,int>& map, String source);
|
||||
|
||||
bool initRender();
|
||||
bool quitRender();
|
||||
|
||||
const char* getSystemName(DivSystem which);
|
||||
|
||||
public:
|
||||
void editStr(String* which);
|
||||
void showWarning(String what, FurnaceGUIWarnings type);
|
||||
void showError(String what);
|
||||
String getLastError();
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("WINDOW_ABOUT", "About", 0),
|
||||
D("WINDOW_SETTINGS", "Settings", 0),
|
||||
D("WINDOW_MIXER", "Mixer", 0),
|
||||
D("WINDOW_DEBUG", "Debug Menu", 0),
|
||||
D("WINDOW_DEBUG", "Debug Menu", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_d),
|
||||
D("WINDOW_OSCILLOSCOPE", "Oscilloscope (master)", 0),
|
||||
D("WINDOW_VOL_METER", "Volume Meter", 0),
|
||||
D("WINDOW_STATS", "Statistics", 0),
|
||||
|
|
@ -611,6 +611,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("INS_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
|
||||
D("INS_LIST_UP", "Cursor up", SDLK_UP),
|
||||
D("INS_LIST_DOWN", "Cursor down", SDLK_DOWN),
|
||||
D("INS_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
|
||||
D("INS_LIST_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("WAVE_LIST_MIN", "---Wavetable list", NOT_AN_ACTION),
|
||||
|
|
@ -627,6 +628,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("WAVE_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
|
||||
D("WAVE_LIST_UP", "Cursor up", SDLK_UP),
|
||||
D("WAVE_LIST_DOWN", "Cursor down", SDLK_DOWN),
|
||||
D("WAVE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
|
||||
D("WAVE_LIST_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("SAMPLE_LIST_MIN", "---Sample list", NOT_AN_ACTION),
|
||||
|
|
@ -637,6 +639,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("SAMPLE_LIST_OPEN_RAW", "Import raw data", 0),
|
||||
D("SAMPLE_LIST_OPEN_REPLACE_RAW", "Import raw data (replace current)", 0),
|
||||
D("SAMPLE_LIST_SAVE", "Save", 0),
|
||||
D("SAMPLE_LIST_SAVE_RAW", "Save (raw)", 0),
|
||||
D("SAMPLE_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
|
||||
D("SAMPLE_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
|
||||
D("SAMPLE_LIST_DELETE", "Delete", 0),
|
||||
|
|
@ -645,6 +648,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("SAMPLE_LIST_DOWN", "Cursor down", SDLK_DOWN),
|
||||
D("SAMPLE_LIST_PREVIEW", "Preview", 0),
|
||||
D("SAMPLE_LIST_STOP_PREVIEW", "Stop preview", 0),
|
||||
D("SAMPLE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
|
||||
D("SAMPLE_LIST_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("SAMPLE_MIN", "---Sample editor", NOT_AN_ACTION),
|
||||
|
|
@ -707,13 +711,34 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
||||
D(GUI_COLOR_BACKGROUND,"Background",ImVec4(0.1f,0.1f,0.1f,1.0f)),
|
||||
D(GUI_COLOR_FRAME_BACKGROUND,"",ImVec4(0.0f,0.0f,0.0f,0.85f)),
|
||||
D(GUI_COLOR_FRAME_BACKGROUND_CHILD,"",ImVec4(0.0f,0.0f,0.0f,0.0f)),
|
||||
D(GUI_COLOR_FRAME_BACKGROUND_POPUP,"",ImVec4(0.08f,0.08f,0.08f,0.94f)),
|
||||
D(GUI_COLOR_MODAL_BACKDROP,"",ImVec4(0.0f,0.0f,0.0f,0.55f)),
|
||||
D(GUI_COLOR_HEADER,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_ACCENT_PRIMARY,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
|
||||
D(GUI_COLOR_ACCENT_SECONDARY,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
|
||||
D(GUI_COLOR_TITLE_INACTIVE,"",ImVec4(0.04f,0.04f,0.04f,1.0f)),
|
||||
D(GUI_COLOR_TITLE_COLLAPSED,"",ImVec4(0.0f,0.0f,0.0f,0.51f)),
|
||||
D(GUI_COLOR_MENU_BAR,"",ImVec4(0.14f,0.14f,0.14f,1.0f)),
|
||||
D(GUI_COLOR_BORDER,"",ImVec4(0.43f,0.43f,0.5f,0.5f)),
|
||||
D(GUI_COLOR_BORDER_SHADOW,"",ImVec4(0.0f,0.0f,0.0f,0.0f)),
|
||||
D(GUI_COLOR_SCROLL_BACKGROUND,"",ImVec4(0.02f,0.02f,0.02f,0.33f)),
|
||||
D(GUI_COLOR_SCROLL,"",ImVec4(0.31f,0.31f,0.31f,1.0f)),
|
||||
D(GUI_COLOR_SCROLL_HOVER,"",ImVec4(0.41f,0.41f,0.41f,1.0f)),
|
||||
D(GUI_COLOR_SCROLL_ACTIVE,"",ImVec4(0.51f,0.51f,0.51f,1.0f)),
|
||||
D(GUI_COLOR_SEPARATOR,"",ImVec4(0.43f,0.43f,0.5f,0.5f)),
|
||||
D(GUI_COLOR_SEPARATOR_HOVER,"",ImVec4(0.1f,0.4f,0.75f,0.78f)),
|
||||
D(GUI_COLOR_SEPARATOR_ACTIVE,"",ImVec4(0.1f,0.4f,0.75f,1.0f)),
|
||||
D(GUI_COLOR_DOCKING_PREVIEW,"",ImVec4(0.26f,0.59f,0.98f,0.7f)),
|
||||
D(GUI_COLOR_DOCKING_EMPTY,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TABLE_HEADER,"",ImVec4(0.19f,0.19f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TABLE_BORDER_HARD,"",ImVec4(0.31f,0.31f,0.35f,1.0f)),
|
||||
D(GUI_COLOR_TABLE_BORDER_SOFT,"",ImVec4(0.23f,0.23f,0.25f,1.0f)),
|
||||
D(GUI_COLOR_DRAG_DROP_TARGET,"",ImVec4(1.0f,1.0f,0.0f,0.9f)),
|
||||
D(GUI_COLOR_NAV_HIGHLIGHT,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
|
||||
D(GUI_COLOR_NAV_WIN_HIGHLIGHT,"",ImVec4(1.0f,1.0f,1.0f,0.7f)),
|
||||
D(GUI_COLOR_NAV_WIN_BACKDROP,"",ImVec4(0.8f,0.8f,0.8f,0.2f)),
|
||||
D(GUI_COLOR_TOGGLE_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_TOGGLE_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)),
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@
|
|||
* 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.
|
||||
*
|
||||
* this license only applies to the code. for the license of each font used,
|
||||
* see `papers/`.
|
||||
*/
|
||||
|
||||
#include "gui.h"
|
||||
|
|
@ -49,7 +46,7 @@ const unsigned int imageLen[GUI_IMAGE_MAX]={
|
|||
image_pat_size
|
||||
};
|
||||
|
||||
SDL_Texture* FurnaceGUI::getTexture(FurnaceGUIImages image, SDL_BlendMode blendMode) {
|
||||
void* FurnaceGUI::getTexture(FurnaceGUIImages image, FurnaceGUIBlendMode blendMode) {
|
||||
FurnaceGUIImage* img=getImage(image);
|
||||
|
||||
if (img==NULL) return NULL;
|
||||
|
|
@ -57,14 +54,14 @@ SDL_Texture* FurnaceGUI::getTexture(FurnaceGUIImages image, SDL_BlendMode blendM
|
|||
if (img->width<=0 || img->height<=0) return NULL;
|
||||
|
||||
if (img->tex==NULL) {
|
||||
img->tex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STATIC,img->width,img->height);
|
||||
img->tex=rend->createTexture(false,img->width,img->height);
|
||||
if (img->tex==NULL) {
|
||||
logE("error while creating image %d texture! %s",(int)image,SDL_GetError());
|
||||
return NULL;
|
||||
}
|
||||
SDL_SetTextureBlendMode(img->tex,blendMode);
|
||||
rend->setTextureBlendMode(img->tex,blendMode);
|
||||
|
||||
if (SDL_UpdateTexture(img->tex,NULL,img->data,img->width*4)!=0) {
|
||||
if (!rend->updateTexture(img->tex,img->data,img->width*4)) {
|
||||
logE("error while updating texture of image %d! %s",(int)image,SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2262,9 +2262,11 @@ void FurnaceGUI::drawInsEdit() {
|
|||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::PushID(2+curIns);
|
||||
if (ImGui::InputText("##Name",&ins->name)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
|
@ -5154,7 +5156,9 @@ void FurnaceGUI::drawInsEdit() {
|
|||
wavePreview2[i]=wave2->data[i];
|
||||
}
|
||||
}
|
||||
if (ins->ws.enabled) wavePreview.tick(true);
|
||||
if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) {
|
||||
wavePreview.tick(true);
|
||||
}
|
||||
for (int i=0; i<wavePreviewLen; i++) {
|
||||
if (wave2->data[i]>wavePreviewHeight) {
|
||||
wavePreview3[i]=wavePreviewHeight;
|
||||
|
|
@ -5200,9 +5204,43 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(wavePreviewPaused?(ICON_FA_PLAY "##WSPause"):(ICON_FA_PAUSE "##WSPause"))) {
|
||||
wavePreviewPaused=!wavePreviewPaused;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (wavePreviewPaused) {
|
||||
ImGui::SetTooltip("Resume preview");
|
||||
} else {
|
||||
ImGui::SetTooltip("Pause preview");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_REPEAT "##WSRestart")) {
|
||||
wavePreviewInit=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Restart preview");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_UPLOAD "##WSCopy")) {
|
||||
curWave=e->addWave();
|
||||
if (curWave==-1) {
|
||||
showError("too many wavetables!");
|
||||
} else {
|
||||
wantScrollList=true;
|
||||
MARK_MODIFIED;
|
||||
RESET_WAVE_MACRO_ZOOM;
|
||||
nextWindow=GUI_WINDOW_WAVE_EDIT;
|
||||
|
||||
DivWavetable* copyWave=e->song.wave[curWave];
|
||||
copyWave->len=wavePreviewLen;
|
||||
copyWave->max=wavePreviewHeight;
|
||||
memcpy(copyWave->data,wavePreview.output,256*sizeof(int));
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Copy to new wavetable");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("(%d×%d)",wavePreviewLen,wavePreviewHeight+1);
|
||||
ImGui::EndTable();
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@
|
|||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include "gui.h"
|
||||
#include "../ta-log.h"
|
||||
#include "imgui_internal.h"
|
||||
#include <fmt/printf.h>
|
||||
|
||||
void FurnaceGUI::drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2& pos, const ImVec2& scale, double rotate, const ImVec2& uvMin, const ImVec2& uvMax, const ImVec4& imgColor) {
|
||||
FurnaceGUIImage* imgI=getImage(image);
|
||||
SDL_Texture* img=getTexture(image);
|
||||
void* img=getTexture(image);
|
||||
|
||||
float squareSize=MAX(introMax.x-introMin.x,introMax.y-introMin.y);
|
||||
float uDiff=uvMax.x-uvMin.x;
|
||||
|
|
@ -69,10 +70,12 @@ void FurnaceGUI::drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2&
|
|||
|
||||
ImU32 colorConverted=ImGui::GetColorU32(imgColor);
|
||||
|
||||
dl->AddImageQuad(img,quad0,quad1,quad2,quad3,uv0,uv1,uv2,uv3,colorConverted);
|
||||
dl->AddImageQuad(rend->getTextureID(img),quad0,quad1,quad2,quad3,uv0,uv1,uv2,uv3,colorConverted);
|
||||
}
|
||||
|
||||
void FurnaceGUI::endIntroTune() {
|
||||
if (introStopped) return;
|
||||
logV("ending intro");
|
||||
stop();
|
||||
if (curFileName.empty()) {
|
||||
e->createNewFromDefaults();
|
||||
|
|
@ -95,6 +98,8 @@ void FurnaceGUI::endIntroTune() {
|
|||
selEnd=SelectionPoint();
|
||||
cursor=SelectionPoint();
|
||||
updateWindowTitle();
|
||||
updateScroll(0);
|
||||
introStopped=true;
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
||||
|
|
@ -162,7 +167,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
getTexture(GUI_IMAGE_TALOGO);
|
||||
getTexture(GUI_IMAGE_TACHIP);
|
||||
getTexture(GUI_IMAGE_LOGO);
|
||||
getTexture(GUI_IMAGE_INTROBG,SDL_BLENDMODE_ADD);
|
||||
getTexture(GUI_IMAGE_INTROBG,GUI_BLEND_MODE_ADD);
|
||||
|
||||
if (monitor) {
|
||||
ImVec2 textPos=ImLerp(top,bottom,ImVec2(0.5,0.5));
|
||||
|
|
@ -290,7 +295,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
if (introSkipDo) {
|
||||
introSkip+=ImGui::GetIO().DeltaTime;
|
||||
if (introSkip>=0.5) {
|
||||
if (e->isPlaying()) endIntroTune();
|
||||
if (!shortIntro) endIntroTune();
|
||||
introPos=0.1;
|
||||
if (introSkip>=0.75) introPos=12.0;
|
||||
}
|
||||
|
|
@ -317,7 +322,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
e->setRepeatPattern(false);
|
||||
play();
|
||||
}
|
||||
if (e->isPlaying() && introPos>=10.0 && !shortIntro) endIntroTune();
|
||||
if (introPos>=10.0 && !shortIntro) endIntroTune();
|
||||
introPos+=ImGui::GetIO().DeltaTime;
|
||||
if (introPos>=(shortIntro?1.0:11.0)) {
|
||||
introPos=12.0;
|
||||
|
|
@ -325,5 +330,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
|
|||
commitTutorial();
|
||||
}
|
||||
}
|
||||
} else if (!shortIntro) {
|
||||
endIntroTune();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,11 @@ void _popPartBlend(const ImDrawList* drawList, const ImDrawCmd* cmd) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::pushPartBlend() {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_ADD);
|
||||
rend->setBlendMode(GUI_BLEND_MODE_ADD);
|
||||
}
|
||||
|
||||
void FurnaceGUI::popPartBlend() {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
rend->setBlendMode(GUI_BLEND_MODE_BLEND);
|
||||
}
|
||||
|
||||
// draw a pattern row
|
||||
|
|
@ -287,7 +287,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
if (pat->data[i][index]>0xff) {
|
||||
snprintf(id,63,"??##PE%d_%d_%d",k,i,j);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
|
||||
} else if (pat->data[i][index]>0x10 || settings.oneDigitEffects==0) {
|
||||
} else if (pat->data[i][index]>=0x10 || settings.oneDigitEffects==0) {
|
||||
const unsigned char data=pat->data[i][index];
|
||||
snprintf(id,63,"%.2X##PE%d_%d_%d",data,k,i,j);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
|
||||
|
|
@ -834,28 +834,9 @@ void FurnaceGUI::drawPattern() {
|
|||
if (extraChannelButtons==2) {
|
||||
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
|
||||
ImGui::PushFont(mainFont);
|
||||
if (patNameTarget==i) {
|
||||
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(8.0f*dpiScale));
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
|
||||
ImGui::InputText(chanID,&pat->name);
|
||||
if (wantPatName) {
|
||||
wantPatName=false;
|
||||
ImGui::SetItemDefaultFocus();
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
} else {
|
||||
if (!ImGui::IsItemActive()) {
|
||||
patNameTarget=-1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
|
||||
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
patNameTarget=i;
|
||||
wantPatName=true;
|
||||
snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
|
||||
ImGui::SetActiveID(ImGui::GetID(chanID),ImGui::GetCurrentWindow());
|
||||
}
|
||||
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
|
||||
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
editStr(&pat->name);
|
||||
}
|
||||
ImGui::PopFont();
|
||||
} else if (extraChannelButtons==1) {
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ void FurnaceGUI::drawPiano() {
|
|||
pianoInputPadMode=PIANO_INPUT_PAD_SPLIT_VISIBLE;
|
||||
}
|
||||
ImGui::Checkbox("Share play/edit offset/range",&pianoSharePosition);
|
||||
ImGui::Checkbox("Read-only (can't input notes)",&pianoReadonly);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +224,7 @@ void FurnaceGUI::drawPiano() {
|
|||
//ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) {
|
||||
bool canInput=false;
|
||||
if (ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
|
||||
if (!pianoReadonly && ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
|
||||
canInput=true;
|
||||
ImGui::InhibitInertialScroll();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@
|
|||
|
||||
// portions based on imgui_widgets.cpp
|
||||
|
||||
#include "plot_nolerp.h"
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include "plot_nolerp.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
struct FurnacePlotArrayGetterData
|
||||
|
|
|
|||
79
src/gui/render.cpp
Normal file
79
src/gui/render.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* 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 "gui.h"
|
||||
#include "../ta-log.h"
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
#include "render/renderSDL.h"
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_GL
|
||||
#include "render/renderGL.h"
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
#include "render/renderDX11.h"
|
||||
#endif
|
||||
|
||||
bool FurnaceGUI::initRender() {
|
||||
if (rend!=NULL) return false;
|
||||
|
||||
if (settings.renderBackend=="OpenGL") {
|
||||
renderBackend=GUI_BACKEND_GL;
|
||||
} else if (settings.renderBackend=="DirectX 11") {
|
||||
renderBackend=GUI_BACKEND_DX11;
|
||||
} else if (settings.renderBackend=="SDL") {
|
||||
renderBackend=GUI_BACKEND_SDL;
|
||||
} else {
|
||||
renderBackend=GUI_BACKEND_DEFAULT;
|
||||
}
|
||||
|
||||
switch (renderBackend) {
|
||||
#ifdef HAVE_RENDER_GL
|
||||
case GUI_BACKEND_GL:
|
||||
logI("render backend: OpenGL");
|
||||
rend=new FurnaceGUIRenderGL;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
case GUI_BACKEND_DX11:
|
||||
logI("render backend: DirectX 11");
|
||||
rend=new FurnaceGUIRenderDX11;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
case GUI_BACKEND_SDL:
|
||||
logI("render backend: SDL_Renderer");
|
||||
rend=new FurnaceGUIRenderSDL;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
logE("invalid render backend!");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUI::quitRender() {
|
||||
if (rend==NULL) return false;
|
||||
bool ret=rend->quit();
|
||||
delete rend;
|
||||
rend=NULL;
|
||||
return ret;
|
||||
}
|
||||
103
src/gui/render/abstract.cpp
Normal file
103
src/gui/render/abstract.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* 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 "../gui.h"
|
||||
|
||||
ImTextureID FurnaceGUIRender::getTextureID(void* which) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::lockTexture(void* which, void** data, int* pitch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::unlockTexture(void* which) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::updateTexture(void* which, void* data, int pitch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRender::createTexture(bool dynamic, int width, int height) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::destroyTexture(void* which) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::resized(const SDL_Event& ev) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::clear(ImVec4 color) {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::newFrame() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::createFontsTexture() {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::destroyFontsTexture() {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::renderGUI() {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::wipe(float alpha) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::present() {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::getOutputSize(int& w, int& h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int FurnaceGUIRender::getWindowFlags() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::preInit() {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::init(SDL_Window* win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::initGUI(SDL_Window* win) {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRender::quit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FurnaceGUIRender::quitGUI() {
|
||||
}
|
||||
|
||||
FurnaceGUIRender::~FurnaceGUIRender() {
|
||||
}
|
||||
571
src/gui/render/renderDX11.cpp
Normal file
571
src/gui/render/renderDX11.cpp
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define INCLUDE_D3D11
|
||||
#include "renderDX11.h"
|
||||
#include <SDL_syswm.h>
|
||||
#include "backends/imgui_impl_dx11.h"
|
||||
#include "../../ta-log.h"
|
||||
|
||||
typedef HRESULT (__stdcall *D3DCompile_t)(LPCVOID,SIZE_T,LPCSTR,D3D_SHADER_MACRO*,ID3DInclude*,LPCSTR,LPCSTR,UINT,UINT,ID3DBlob**,ID3DBlob*);
|
||||
|
||||
const char* shD3D11_wipe_srcV=
|
||||
"cbuffer WipeUniform: register(b0) {\n"
|
||||
" float alpha;\n"
|
||||
" float padding1;\n"
|
||||
" float padding2;\n"
|
||||
" float padding3;\n"
|
||||
" float4 padding4;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"struct vsInput {\n"
|
||||
" float4 pos: POSITION;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"struct fsInput {\n"
|
||||
" float4 pos: SV_POSITION;\n"
|
||||
" float4 color: COLOR0;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"fsInput main(vsInput input) {\n"
|
||||
" fsInput output;\n"
|
||||
" output.pos=input.pos;\n"
|
||||
" output.color=float4(0.0f,0.0f,0.0f,alpha);\n"
|
||||
" return output;\n"
|
||||
"}";
|
||||
|
||||
const char* shD3D11_wipe_srcF=
|
||||
"struct fsInput {\n"
|
||||
" float4 pos: SV_POSITION;\n"
|
||||
" float4 color: COLOR0;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"float4 main(fsInput input): SV_Target {\n"
|
||||
" return input.color;\n"
|
||||
"}";
|
||||
|
||||
const D3D11_INPUT_ELEMENT_DESC shD3D11_wipe_inputLayout={
|
||||
"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0
|
||||
};
|
||||
|
||||
const D3D_FEATURE_LEVEL possibleFeatureLevels[2]={
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_0
|
||||
};
|
||||
|
||||
struct FurnaceDXTexture {
|
||||
ID3D11Texture2D* tex;
|
||||
ID3D11ShaderResourceView* view;
|
||||
int width, height;
|
||||
unsigned char* lockedData;
|
||||
bool dynamic;
|
||||
FurnaceDXTexture():
|
||||
tex(NULL),
|
||||
view(NULL),
|
||||
width(0),
|
||||
height(0),
|
||||
lockedData(NULL),
|
||||
dynamic(false) {}
|
||||
};
|
||||
|
||||
bool FurnaceGUIRenderDX11::destroyRenderTarget() {
|
||||
if (renderTarget!=NULL) {
|
||||
renderTarget->Release();
|
||||
renderTarget=NULL;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::createRenderTarget() {
|
||||
ID3D11Texture2D* screen=NULL;
|
||||
HRESULT result;
|
||||
|
||||
destroyRenderTarget();
|
||||
|
||||
if (swapchain==NULL || device==NULL) {
|
||||
logW("createRenderTarget: swapchain or device are NULL!");
|
||||
return false;
|
||||
}
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC chainDesc;
|
||||
memset(&chainDesc,0,sizeof(chainDesc));
|
||||
if (swapchain->GetDesc(&chainDesc)!=S_OK) {
|
||||
logW("createRenderTarget: could not get swapchain desc!");
|
||||
} else {
|
||||
outW=chainDesc.BufferDesc.Width;
|
||||
outH=chainDesc.BufferDesc.Height;
|
||||
logI("DX11: buffer desc sizes: %d, %d",chainDesc.BufferDesc.Width,chainDesc.BufferDesc.Height);
|
||||
}
|
||||
|
||||
result=swapchain->GetBuffer(0,IID_PPV_ARGS(&screen));
|
||||
if (result!=S_OK) {
|
||||
logW("createRenderTarget: could not get buffer! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
if (screen==NULL) {
|
||||
logW("createRenderTarget: screen is null!");
|
||||
return false;
|
||||
}
|
||||
|
||||
result=device->CreateRenderTargetView(screen,NULL,&renderTarget);
|
||||
if (result!=S_OK) {
|
||||
logW("createRenderTarget: could not create render target view! %.8x",result);
|
||||
screen->Release();
|
||||
return false;
|
||||
}
|
||||
if (renderTarget==NULL) {
|
||||
logW("createRenderTarget: what the hell the render target is null?");
|
||||
screen->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
ImTextureID FurnaceGUIRenderDX11::getTextureID(void* which) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
return (ImTextureID)t->view;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::lockTexture(void* which, void** data, int* pitch) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
if (t->lockedData!=NULL) return false;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mappedRes;
|
||||
memset(&mappedRes,0,sizeof(mappedRes));
|
||||
|
||||
HRESULT result=context->Map(t->tex,D3D11CalcSubresource(0,0,1),D3D11_MAP_WRITE_DISCARD,0,&mappedRes);
|
||||
if (result!=S_OK) {
|
||||
logW("could not map texture! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
t->lockedData=(unsigned char*)mappedRes.pData;
|
||||
*data=mappedRes.pData;
|
||||
*pitch=mappedRes.RowPitch;
|
||||
|
||||
logV("texture locked... pitch: %d",mappedRes.RowPitch);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::unlockTexture(void* which) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
if (t->lockedData==NULL) return false;
|
||||
context->Unmap(t->tex,D3D11CalcSubresource(0,0,1));
|
||||
t->lockedData=NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::updateTexture(void* which, void* data, int pitch) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
if (t->dynamic) {
|
||||
unsigned char* d=NULL;
|
||||
int p=0;
|
||||
if (!lockTexture(t,(void**)&d,&p)) return false;
|
||||
if (p==pitch) {
|
||||
memcpy(d,data,p*t->height);
|
||||
} else {
|
||||
unsigned char* ucData=(unsigned char*)data;
|
||||
int srcPos=0;
|
||||
int destPos=0;
|
||||
for (int i=0; i<t->height; i++) {
|
||||
memcpy(&d[destPos],&ucData[srcPos],pitch);
|
||||
srcPos+=pitch;
|
||||
destPos+=p;
|
||||
}
|
||||
}
|
||||
unlockTexture(t);
|
||||
} else {
|
||||
context->UpdateSubresource(t->tex,D3D11CalcSubresource(0,0,1),NULL,data,pitch,pitch*t->height);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRenderDX11::createTexture(bool dynamic, int width, int height) {
|
||||
D3D11_TEXTURE2D_DESC texDesc;
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
|
||||
ID3D11Texture2D* tex=NULL;
|
||||
ID3D11ShaderResourceView* view=NULL;
|
||||
HRESULT result;
|
||||
|
||||
memset(&texDesc,0,sizeof(texDesc));
|
||||
memset(&viewDesc,0,sizeof(viewDesc));
|
||||
|
||||
texDesc.Width=width;
|
||||
texDesc.Height=height;
|
||||
texDesc.MipLevels=1;
|
||||
texDesc.ArraySize=1;
|
||||
texDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; // ???
|
||||
texDesc.SampleDesc.Count=1;
|
||||
texDesc.SampleDesc.Quality=0;
|
||||
texDesc.Usage=dynamic?D3D11_USAGE_DYNAMIC:D3D11_USAGE_DEFAULT;
|
||||
texDesc.BindFlags=D3D11_BIND_SHADER_RESOURCE;
|
||||
texDesc.CPUAccessFlags=dynamic?D3D11_CPU_ACCESS_WRITE:0;
|
||||
texDesc.MiscFlags=0;
|
||||
|
||||
result=device->CreateTexture2D(&texDesc,NULL,&tex);
|
||||
if (result!=S_OK) {
|
||||
logW("could not create texture! %.8x",result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
viewDesc.Format=texDesc.Format=texDesc.Format;
|
||||
viewDesc.ViewDimension=D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
viewDesc.Texture2D.MostDetailedMip=0;
|
||||
viewDesc.Texture2D.MipLevels=texDesc.MipLevels;
|
||||
|
||||
result=device->CreateShaderResourceView(tex,&viewDesc,&view);
|
||||
if (result!=S_OK) {
|
||||
logW("could not create texture view! %.8x",result);
|
||||
tex->Release();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FurnaceDXTexture* ret=new FurnaceDXTexture;
|
||||
ret->width=width;
|
||||
ret->height=height;
|
||||
ret->tex=tex;
|
||||
ret->view=view;
|
||||
ret->dynamic=dynamic;
|
||||
textures.push_back(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::destroyTexture(void* which) {
|
||||
FurnaceDXTexture* t=(FurnaceDXTexture*)which;
|
||||
t->view->Release();
|
||||
t->tex->Release();
|
||||
delete t;
|
||||
|
||||
for (size_t i=0; i<textures.size(); i++) {
|
||||
if (textures[i]==t) {
|
||||
textures.erase(textures.begin()+i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::resized(const SDL_Event& ev) {
|
||||
destroyRenderTarget();
|
||||
logI("DX11: resizing buffers");
|
||||
HRESULT result=swapchain->ResizeBuffers(0,(unsigned int)ev.window.data1,(unsigned int)ev.window.data2,DXGI_FORMAT_UNKNOWN,DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
|
||||
if (result!=S_OK) {
|
||||
logW("error while resizing swapchain buffers! %.8x",result);
|
||||
}
|
||||
createRenderTarget();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::clear(ImVec4 color) {
|
||||
float floatColor[4]={
|
||||
color.x*color.w,
|
||||
color.y*color.w,
|
||||
color.z*color.w,
|
||||
color.w,
|
||||
};
|
||||
|
||||
context->OMSetRenderTargets(1,&renderTarget,NULL);
|
||||
context->ClearRenderTargetView(renderTarget,floatColor);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::newFrame() {
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::createFontsTexture() {
|
||||
ImGui_ImplDX11_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::destroyFontsTexture() {
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::renderGUI() {
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
const float blendFactor[4]={
|
||||
1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
|
||||
void FurnaceGUIRenderDX11::wipe(float alpha) {
|
||||
D3D11_VIEWPORT viewPort;
|
||||
unsigned int strides=4*sizeof(float);
|
||||
unsigned int offsets=0;
|
||||
|
||||
memset(&viewPort,0,sizeof(viewPort));
|
||||
viewPort.TopLeftX=0.0f;
|
||||
viewPort.TopLeftY=0.0f;
|
||||
viewPort.Width=outW;
|
||||
viewPort.Height=outH;
|
||||
viewPort.MinDepth=0.0f;
|
||||
viewPort.MaxDepth=1.0f;
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mappedUniform;
|
||||
if (context->Map(sh_wipe_uniform,0,D3D11_MAP_WRITE_DISCARD,0,&mappedUniform)!=S_OK) {
|
||||
logW("could not map constant");
|
||||
}
|
||||
WipeUniform* sh_wipe_uniformState=(WipeUniform*)mappedUniform.pData;
|
||||
sh_wipe_uniformState->alpha=alpha;
|
||||
context->Unmap(sh_wipe_uniform,0);
|
||||
|
||||
context->RSSetViewports(1,&viewPort);
|
||||
context->RSSetState(rsState);
|
||||
|
||||
context->OMSetBlendState(omBlendState,blendFactor,0xffffffff);
|
||||
context->IASetInputLayout(sh_wipe_inputLayout);
|
||||
context->IASetVertexBuffers(0,1,&quadVertex,&strides,&offsets);
|
||||
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||
context->VSSetShader(sh_wipe_vertex,NULL,0);
|
||||
context->VSSetConstantBuffers(0,1,&sh_wipe_uniform);
|
||||
context->PSSetShader(sh_wipe_fragment,NULL,0);
|
||||
|
||||
context->Draw(4,0);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::present() {
|
||||
swapchain->Present(1,0);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::getOutputSize(int& w, int& h) {
|
||||
w=outW;
|
||||
h=outH;
|
||||
return true;
|
||||
}
|
||||
|
||||
int FurnaceGUIRenderDX11::getWindowFlags() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::preInit() {
|
||||
}
|
||||
|
||||
const float wipeVertices[4][4]={
|
||||
-1.0, -1.0, 0.0, 1.0,
|
||||
1.0, -1.0, 0.0, 1.0,
|
||||
-1.0, 1.0, 0.0, 1.0,
|
||||
1.0, 1.0, 0.0, 1.0
|
||||
};
|
||||
|
||||
bool FurnaceGUIRenderDX11::init(SDL_Window* win) {
|
||||
SDL_SysWMinfo sysWindow;
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
|
||||
SDL_VERSION(&sysWindow.version);
|
||||
if (SDL_GetWindowWMInfo(win,&sysWindow)==SDL_FALSE) {
|
||||
logE("could not get window WM info! %s",SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
HWND window=(HWND)sysWindow.info.win.window;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC chainDesc;
|
||||
memset(&chainDesc,0,sizeof(chainDesc));
|
||||
chainDesc.BufferDesc.Width=0;
|
||||
chainDesc.BufferDesc.Height=0;
|
||||
chainDesc.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
chainDesc.BufferDesc.RefreshRate.Numerator=60;
|
||||
chainDesc.BufferDesc.RefreshRate.Denominator=1;
|
||||
chainDesc.SampleDesc.Count=1;
|
||||
chainDesc.SampleDesc.Quality=0;
|
||||
chainDesc.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
chainDesc.BufferCount=2;
|
||||
chainDesc.OutputWindow=window;
|
||||
chainDesc.Windowed=TRUE; // TODO: what if we're in full screen mode?
|
||||
chainDesc.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
|
||||
chainDesc.Flags=DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
|
||||
HRESULT result=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,possibleFeatureLevels,2,D3D11_SDK_VERSION,&chainDesc,&swapchain,&device,&featureLevel,&context);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create device and/or swap chain! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/ocornut/imgui/pull/638
|
||||
D3DCompile_t D3DCompile=NULL;
|
||||
char dllBuffer[20];
|
||||
for (int i=47; (i>30 && !D3DCompile); i--) {
|
||||
snprintf(dllBuffer,20,"d3dcompiler_%d.dll",i);
|
||||
HMODULE hDll=LoadLibraryA(dllBuffer);
|
||||
if (hDll) {
|
||||
D3DCompile=(D3DCompile_t)GetProcAddress(hDll,"D3DCompile");
|
||||
}
|
||||
}
|
||||
if (!D3DCompile) {
|
||||
logE("could not find D3DCompile!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// create wipe shader
|
||||
ID3DBlob* wipeBlobV=NULL;
|
||||
ID3DBlob* wipeBlobF=NULL;
|
||||
D3D11_BUFFER_DESC wipeConstantDesc;
|
||||
|
||||
result=D3DCompile(shD3D11_wipe_srcV,strlen(shD3D11_wipe_srcV),NULL,NULL,NULL,"main","vs_4_0",0,0,&wipeBlobV,NULL);
|
||||
if (result!=S_OK) {
|
||||
logE("could not compile vertex shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
result=D3DCompile(shD3D11_wipe_srcF,strlen(shD3D11_wipe_srcF),NULL,NULL,NULL,"main","ps_4_0",0,0,&wipeBlobF,NULL);
|
||||
if (result!=S_OK) {
|
||||
logE("could not compile pixel shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
result=device->CreateVertexShader(wipeBlobV->GetBufferPointer(),wipeBlobV->GetBufferSize(),NULL,&sh_wipe_vertex);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create vertex shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
result=device->CreatePixelShader(wipeBlobF->GetBufferPointer(),wipeBlobF->GetBufferSize(),NULL,&sh_wipe_fragment);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create pixel shader! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
result=device->CreateInputLayout(&shD3D11_wipe_inputLayout,1,wipeBlobV->GetBufferPointer(),wipeBlobV->GetBufferSize(),&sh_wipe_inputLayout);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create input layout! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&wipeConstantDesc,0,sizeof(wipeConstantDesc));
|
||||
wipeConstantDesc.ByteWidth=sizeof(WipeUniform);
|
||||
wipeConstantDesc.Usage=D3D11_USAGE_DYNAMIC;
|
||||
wipeConstantDesc.BindFlags=D3D11_BIND_CONSTANT_BUFFER;
|
||||
wipeConstantDesc.CPUAccessFlags=D3D11_CPU_ACCESS_WRITE;
|
||||
wipeConstantDesc.MiscFlags=0;
|
||||
wipeConstantDesc.StructureByteStride=0;
|
||||
|
||||
result=device->CreateBuffer(&wipeConstantDesc,NULL,&sh_wipe_uniform);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create constant buffer! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create wipe vertices
|
||||
D3D11_BUFFER_DESC vertexDesc;
|
||||
D3D11_SUBRESOURCE_DATA vertexRes;
|
||||
|
||||
memset(&vertexDesc,0,sizeof(vertexDesc));
|
||||
memset(&vertexRes,0,sizeof(vertexRes));
|
||||
|
||||
vertexDesc.ByteWidth=4*4*sizeof(float);
|
||||
vertexDesc.Usage=D3D11_USAGE_DEFAULT;
|
||||
vertexDesc.BindFlags=D3D11_BIND_VERTEX_BUFFER;
|
||||
vertexDesc.CPUAccessFlags=0;
|
||||
vertexDesc.MiscFlags=0;
|
||||
vertexDesc.StructureByteStride=0;
|
||||
|
||||
vertexRes.pSysMem=wipeVertices;
|
||||
vertexRes.SysMemPitch=0;
|
||||
vertexRes.SysMemSlicePitch=0;
|
||||
|
||||
result=device->CreateBuffer(&vertexDesc,&vertexRes,&quadVertex);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create vertex buffer! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize the rest
|
||||
D3D11_RASTERIZER_DESC rasterDesc;
|
||||
D3D11_BLEND_DESC blendDesc;
|
||||
|
||||
memset(&rasterDesc,0,sizeof(rasterDesc));
|
||||
memset(&blendDesc,0,sizeof(blendDesc));
|
||||
|
||||
rasterDesc.FillMode=D3D11_FILL_SOLID;
|
||||
rasterDesc.CullMode=D3D11_CULL_NONE;
|
||||
rasterDesc.FrontCounterClockwise=false;
|
||||
rasterDesc.DepthBias=0;
|
||||
rasterDesc.DepthBiasClamp=0.0f;
|
||||
rasterDesc.SlopeScaledDepthBias=0.0f;
|
||||
rasterDesc.DepthClipEnable=false;
|
||||
rasterDesc.ScissorEnable=false;
|
||||
rasterDesc.MultisampleEnable=false;
|
||||
rasterDesc.AntialiasedLineEnable=false;
|
||||
result=device->CreateRasterizerState(&rasterDesc,&rsState);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create rasterizer state! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
blendDesc.AlphaToCoverageEnable=false;
|
||||
blendDesc.IndependentBlendEnable=false;
|
||||
blendDesc.RenderTarget[0].BlendEnable=true;
|
||||
blendDesc.RenderTarget[0].SrcBlend=D3D11_BLEND_SRC_ALPHA;
|
||||
blendDesc.RenderTarget[0].DestBlend=D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blendDesc.RenderTarget[0].BlendOp=D3D11_BLEND_OP_ADD;
|
||||
blendDesc.RenderTarget[0].SrcBlendAlpha=D3D11_BLEND_ONE;
|
||||
blendDesc.RenderTarget[0].DestBlendAlpha=D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blendDesc.RenderTarget[0].BlendOpAlpha=D3D11_BLEND_OP_ADD;
|
||||
blendDesc.RenderTarget[0].RenderTargetWriteMask=D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
result=device->CreateBlendState(&blendDesc,&omBlendState);
|
||||
if (result!=S_OK) {
|
||||
logE("could not create blend state! %.8x",result);
|
||||
return false;
|
||||
}
|
||||
|
||||
createRenderTarget();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::initGUI(SDL_Window* win) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplSDL2_InitForD3D(win);
|
||||
ImGui_ImplDX11_Init(device,context);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderDX11::quit() {
|
||||
destroyRenderTarget();
|
||||
|
||||
for (FurnaceDXTexture* i: textures) {
|
||||
i->view->Release();
|
||||
i->tex->Release();
|
||||
delete i;
|
||||
}
|
||||
textures.clear();
|
||||
|
||||
if (swapchain!=NULL) {
|
||||
swapchain->Release();
|
||||
swapchain=NULL;
|
||||
}
|
||||
if (context!=NULL) {
|
||||
context->Release();
|
||||
context=NULL;
|
||||
}
|
||||
if (device!=NULL) {
|
||||
device->Release();
|
||||
device=NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderDX11::quitGUI() {
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
}
|
||||
103
src/gui/render/renderDX11.h
Normal file
103
src/gui/render/renderDX11.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* 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 "../gui.h"
|
||||
#ifdef INCLUDE_D3D11
|
||||
#include <d3d11.h>
|
||||
#else
|
||||
typedef void ID3D11DeviceContext;
|
||||
typedef void ID3D11RenderTargetView;
|
||||
typedef void ID3D11Buffer;
|
||||
typedef void ID3D11RasterizerState;
|
||||
typedef void ID3D11BlendState;
|
||||
typedef void ID3D11VertexShader;
|
||||
typedef void ID3D11PixelShader;
|
||||
typedef void ID3D11InputLayout;
|
||||
typedef void IDXGISwapChain;
|
||||
#endif
|
||||
|
||||
struct FurnaceDXTexture;
|
||||
|
||||
class FurnaceGUIRenderDX11: public FurnaceGUIRender {
|
||||
ID3D11Device* device;
|
||||
ID3D11DeviceContext* context;
|
||||
ID3D11RenderTargetView* renderTarget;
|
||||
IDXGISwapChain* swapchain;
|
||||
ID3D11RasterizerState* rsState;
|
||||
ID3D11BlendState* omBlendState;
|
||||
|
||||
ID3D11Buffer* quadVertex;
|
||||
int outW, outH;
|
||||
|
||||
// SHADERS //
|
||||
// -> wipe
|
||||
ID3D11VertexShader* sh_wipe_vertex;
|
||||
ID3D11PixelShader* sh_wipe_fragment;
|
||||
ID3D11InputLayout* sh_wipe_inputLayout;
|
||||
ID3D11Buffer* sh_wipe_uniform;
|
||||
struct WipeUniform {
|
||||
float alpha;
|
||||
float padding[7];
|
||||
};
|
||||
|
||||
bool destroyRenderTarget();
|
||||
bool createRenderTarget();
|
||||
|
||||
std::vector<FurnaceDXTexture*> textures;
|
||||
|
||||
public:
|
||||
ImTextureID getTextureID(void* which);
|
||||
bool lockTexture(void* which, void** data, int* pitch);
|
||||
bool unlockTexture(void* which);
|
||||
bool updateTexture(void* which, void* data, int pitch);
|
||||
void* createTexture(bool dynamic, int width, int height);
|
||||
bool destroyTexture(void* which);
|
||||
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
void resized(const SDL_Event& ev);
|
||||
void clear(ImVec4 color);
|
||||
bool newFrame();
|
||||
void createFontsTexture();
|
||||
void destroyFontsTexture();
|
||||
void renderGUI();
|
||||
void wipe(float alpha);
|
||||
void present();
|
||||
bool getOutputSize(int& w, int& h);
|
||||
int getWindowFlags();
|
||||
void preInit();
|
||||
bool init(SDL_Window* win);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
bool quit();
|
||||
FurnaceGUIRenderDX11():
|
||||
device(NULL),
|
||||
context(NULL),
|
||||
renderTarget(NULL),
|
||||
swapchain(NULL),
|
||||
rsState(NULL),
|
||||
omBlendState(NULL),
|
||||
quadVertex(NULL),
|
||||
outW(0),
|
||||
outH(0),
|
||||
sh_wipe_vertex(NULL),
|
||||
sh_wipe_fragment(NULL),
|
||||
sh_wipe_inputLayout(NULL),
|
||||
sh_wipe_uniform(NULL) {
|
||||
}
|
||||
};
|
||||
387
src/gui/render/renderGL.cpp
Normal file
387
src/gui/render/renderGL.cpp
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
/**
|
||||
* 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 "renderGL.h"
|
||||
#include "../../ta-log.h"
|
||||
#ifdef USE_GLES
|
||||
#include "SDL_opengles2.h"
|
||||
#define PIXEL_FORMAT GL_UNSIGNED_BYTE
|
||||
#else
|
||||
#include "SDL_opengl.h"
|
||||
#define PIXEL_FORMAT GL_UNSIGNED_INT_8_8_8_8_REV
|
||||
#endif
|
||||
#include "backends/imgui_impl_opengl3.h"
|
||||
|
||||
#define C(x) x; if (glGetError()!=GL_NO_ERROR) logW("OpenGL error in %s:%d: " #x,__FILE__,__LINE__);
|
||||
|
||||
PFNGLGENBUFFERSPROC furGenBuffers=NULL;
|
||||
PFNGLBINDBUFFERPROC furBindBuffer=NULL;
|
||||
PFNGLBUFFERDATAPROC furBufferData=NULL;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC furVertexAttribPointer=NULL;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC furEnableVertexAttribArray=NULL;
|
||||
PFNGLACTIVETEXTUREPROC furActiveTexture=NULL;
|
||||
|
||||
PFNGLCREATESHADERPROC furCreateShader=NULL;
|
||||
PFNGLSHADERSOURCEPROC furShaderSource=NULL;
|
||||
PFNGLCOMPILESHADERPROC furCompileShader=NULL;
|
||||
PFNGLGETSHADERIVPROC furGetShaderiv=NULL;
|
||||
PFNGLATTACHSHADERPROC furAttachShader=NULL;
|
||||
PFNGLBINDATTRIBLOCATIONPROC furBindAttribLocation=NULL;
|
||||
PFNGLCREATEPROGRAMPROC furCreateProgram=NULL;
|
||||
PFNGLLINKPROGRAMPROC furLinkProgram=NULL;
|
||||
PFNGLGETPROGRAMIVPROC furGetProgramiv=NULL;
|
||||
PFNGLUSEPROGRAMPROC furUseProgram=NULL;
|
||||
PFNGLGETUNIFORMLOCATIONPROC furGetUniformLocation=NULL;
|
||||
PFNGLUNIFORM1FPROC furUniform1f=NULL;
|
||||
PFNGLGETSHADERINFOLOGPROC furGetShaderInfoLog=NULL;
|
||||
|
||||
struct FurnaceGLTexture {
|
||||
GLuint id;
|
||||
int width, height;
|
||||
unsigned char* lockedData;
|
||||
FurnaceGLTexture():
|
||||
id(0),
|
||||
width(0),
|
||||
height(0),
|
||||
lockedData(NULL) {}
|
||||
};
|
||||
|
||||
#ifdef USE_GLES
|
||||
const char* sh_wipe_srcV=
|
||||
"attribute vec4 fur_position;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position=fur_position;\n"
|
||||
"}\n";
|
||||
|
||||
const char* sh_wipe_srcF=
|
||||
"uniform float uAlpha;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor=vec4(0.0,0.0,0.0,uAlpha);\n"
|
||||
"}\n";
|
||||
#else
|
||||
const char* sh_wipe_srcV=
|
||||
"#version 130\n"
|
||||
"in vec4 fur_position;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position=fur_position;\n"
|
||||
"}\n";
|
||||
|
||||
const char* sh_wipe_srcF=
|
||||
"#version 130\n"
|
||||
"uniform float uAlpha;\n"
|
||||
"out vec4 fur_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" fur_FragColor=vec4(0.0,0.0,0.0,uAlpha);\n"
|
||||
"}\n";
|
||||
#endif
|
||||
|
||||
bool FurnaceGUIRenderGL::createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program) {
|
||||
int status;
|
||||
char infoLog[4096];
|
||||
int infoLogLen;
|
||||
|
||||
if (!furCreateShader || !furShaderSource || !furCompileShader || !furGetShaderiv ||
|
||||
!furGetShaderInfoLog || !furCreateProgram || !furAttachShader || !furLinkProgram ||
|
||||
!furBindAttribLocation || !furGetProgramiv) {
|
||||
logW("I can't compile shaders");
|
||||
return false;
|
||||
}
|
||||
|
||||
vertex=furCreateShader(GL_VERTEX_SHADER);
|
||||
furShaderSource(vertex,1,&vertexS,NULL);
|
||||
furCompileShader(vertex);
|
||||
furGetShaderiv(vertex,GL_COMPILE_STATUS,&status);
|
||||
if (!status) {
|
||||
logW("failed to compile vertex shader");
|
||||
furGetShaderInfoLog(vertex,4095,&infoLogLen,infoLog);
|
||||
infoLog[infoLogLen]=0;
|
||||
logW("%s",infoLog);
|
||||
return false;
|
||||
}
|
||||
|
||||
fragment=furCreateShader(GL_FRAGMENT_SHADER);
|
||||
furShaderSource(fragment,1,&fragmentS,NULL);
|
||||
furCompileShader(fragment);
|
||||
furGetShaderiv(fragment,GL_COMPILE_STATUS,&status);
|
||||
if (!status) {
|
||||
logW("failed to compile fragment shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
program=furCreateProgram();
|
||||
furAttachShader(program,vertex);
|
||||
furAttachShader(program,fragment);
|
||||
furBindAttribLocation(program,0,"fur_position");
|
||||
furLinkProgram(program);
|
||||
furGetProgramiv(program,GL_LINK_STATUS,&status);
|
||||
if (!status) {
|
||||
logW("failed to link shader!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ImTextureID FurnaceGUIRenderGL::getTextureID(void* which) {
|
||||
intptr_t ret=((FurnaceGLTexture*)which)->id;
|
||||
return (ImTextureID)ret;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::lockTexture(void* which, void** data, int* pitch) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
if (t->lockedData!=NULL) return false;
|
||||
t->lockedData=new unsigned char[t->width*t->height*4];
|
||||
|
||||
*data=t->lockedData;
|
||||
*pitch=t->width*4;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::unlockTexture(void* which) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
if (t->lockedData==NULL) return false;
|
||||
|
||||
C(glBindTexture(GL_TEXTURE_2D,t->id));
|
||||
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,t->width,t->height,0,GL_RGBA,PIXEL_FORMAT,t->lockedData));
|
||||
|
||||
C(glFlush());
|
||||
delete[] t->lockedData;
|
||||
t->lockedData=NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::updateTexture(void* which, void* data, int pitch) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
|
||||
if (t->width*4!=pitch) return false;
|
||||
|
||||
C(glBindTexture(GL_TEXTURE_2D,t->id));
|
||||
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,t->width,t->height,0,GL_RGBA,PIXEL_FORMAT,data));
|
||||
return true;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRenderGL::createTexture(bool dynamic, int width, int height) {
|
||||
FurnaceGLTexture* t=new FurnaceGLTexture;
|
||||
C(glGenTextures(1,&t->id));
|
||||
C(glBindTexture(GL_TEXTURE_2D,t->id));
|
||||
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR));
|
||||
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR));
|
||||
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,width,height,0,GL_RGBA,PIXEL_FORMAT,NULL));
|
||||
C(furActiveTexture(GL_TEXTURE0));
|
||||
t->width=width;
|
||||
t->height=height;
|
||||
return t;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::destroyTexture(void* which) {
|
||||
FurnaceGLTexture* t=(FurnaceGLTexture*)which;
|
||||
C(glDeleteTextures(1,&t->id));
|
||||
delete t;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
switch (mode) {
|
||||
case GUI_BLEND_MODE_NONE:
|
||||
C(glBlendFunc(GL_ONE,GL_ZERO));
|
||||
C(glDisable(GL_BLEND));
|
||||
break;
|
||||
case GUI_BLEND_MODE_BLEND:
|
||||
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
|
||||
C(glEnable(GL_BLEND));
|
||||
break;
|
||||
case GUI_BLEND_MODE_ADD:
|
||||
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE));
|
||||
C(glEnable(GL_BLEND));
|
||||
break;
|
||||
case GUI_BLEND_MODE_MULTIPLY:
|
||||
C(glBlendFunc(GL_ZERO,GL_SRC_COLOR));
|
||||
C(glEnable(GL_BLEND));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::clear(ImVec4 color) {
|
||||
SDL_GL_MakeCurrent(sdlWin,context);
|
||||
C(glClearColor(color.x,color.y,color.z,color.w));
|
||||
C(glClear(GL_COLOR_BUFFER_BIT));
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::newFrame() {
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::createFontsTexture() {
|
||||
ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::destroyFontsTexture() {
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::renderGUI() {
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::wipe(float alpha) {
|
||||
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
|
||||
C(glEnable(GL_BLEND));
|
||||
|
||||
quadVertex[0][0]=-1.0f;
|
||||
quadVertex[0][1]=-1.0f;
|
||||
quadVertex[0][2]=0.0f;
|
||||
quadVertex[1][0]=1.0f;
|
||||
quadVertex[1][1]=-1.0f;
|
||||
quadVertex[1][2]=0.0f;
|
||||
quadVertex[2][0]=-1.0f;
|
||||
quadVertex[2][1]=1.0f;
|
||||
quadVertex[2][2]=0.0f;
|
||||
quadVertex[3][0]=1.0f;
|
||||
quadVertex[3][1]=1.0f;
|
||||
quadVertex[3][2]=0.0f;
|
||||
|
||||
C(furBindBuffer(GL_ARRAY_BUFFER,quadBuf));
|
||||
C(furBufferData(GL_ARRAY_BUFFER,sizeof(quadVertex),quadVertex,GL_STATIC_DRAW));
|
||||
C(furVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,NULL));
|
||||
C(furEnableVertexAttribArray(0));
|
||||
C(furActiveTexture(GL_TEXTURE0));
|
||||
C(glBindTexture(GL_TEXTURE_2D,0));
|
||||
if (furUseProgram && furUniform1f) {
|
||||
C(furUseProgram(sh_wipe_program));
|
||||
C(furUniform1f(sh_wipe_uAlpha,alpha));
|
||||
}
|
||||
C(glDrawArrays(GL_TRIANGLE_STRIP,0,4));
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::present() {
|
||||
SDL_GL_SwapWindow(sdlWin);
|
||||
C(glFlush());
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::getOutputSize(int& w, int& h) {
|
||||
SDL_GL_GetDrawableSize(sdlWin,&w,&h);
|
||||
return true;
|
||||
}
|
||||
|
||||
int FurnaceGUIRenderGL::getWindowFlags() {
|
||||
return SDL_WINDOW_OPENGL;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::preInit() {
|
||||
#if defined(USE_GLES)
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_ES);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
|
||||
#elif defined(__APPLE__)
|
||||
// not recommended...
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,2);
|
||||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
|
||||
#endif
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,0);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
|
||||
}
|
||||
|
||||
#define LOAD_PROC_MANDATORY(_v,_t,_s) \
|
||||
_v=(_t)SDL_GL_GetProcAddress(_s); \
|
||||
if (!_v) { \
|
||||
logE(_s " not found"); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define LOAD_PROC_OPTIONAL(_v,_t,_s) \
|
||||
_v=(_t)SDL_GL_GetProcAddress(_s); \
|
||||
if (!_v) { \
|
||||
logW(_s " not found"); \
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::init(SDL_Window* win) {
|
||||
sdlWin=win;
|
||||
context=SDL_GL_CreateContext(win);
|
||||
if (context==NULL) {
|
||||
return false;
|
||||
}
|
||||
SDL_GL_MakeCurrent(win,context);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
LOAD_PROC_MANDATORY(furGenBuffers,PFNGLGENBUFFERSPROC,"glGenBuffers");
|
||||
LOAD_PROC_MANDATORY(furBindBuffer,PFNGLBINDBUFFERPROC,"glBindBuffer");
|
||||
LOAD_PROC_MANDATORY(furBufferData,PFNGLBUFFERDATAPROC,"glBufferData");
|
||||
LOAD_PROC_MANDATORY(furVertexAttribPointer,PFNGLVERTEXATTRIBPOINTERPROC,"glVertexAttribPointer");
|
||||
LOAD_PROC_MANDATORY(furEnableVertexAttribArray,PFNGLENABLEVERTEXATTRIBARRAYPROC,"glEnableVertexAttribArray");
|
||||
LOAD_PROC_MANDATORY(furActiveTexture,PFNGLACTIVETEXTUREPROC,"glActiveTexture");
|
||||
|
||||
LOAD_PROC_OPTIONAL(furCreateShader,PFNGLCREATESHADERPROC,"glCreateShader");
|
||||
LOAD_PROC_OPTIONAL(furShaderSource,PFNGLSHADERSOURCEPROC,"glShaderSource");
|
||||
LOAD_PROC_OPTIONAL(furCompileShader,PFNGLCOMPILESHADERPROC,"glCompileShader");
|
||||
LOAD_PROC_OPTIONAL(furGetShaderiv,PFNGLGETSHADERIVPROC,"glGetShaderiv");
|
||||
LOAD_PROC_OPTIONAL(furAttachShader,PFNGLATTACHSHADERPROC,"glAttachShader");
|
||||
LOAD_PROC_OPTIONAL(furBindAttribLocation,PFNGLBINDATTRIBLOCATIONPROC,"glBindAttribLocation");
|
||||
LOAD_PROC_OPTIONAL(furCreateProgram,PFNGLCREATEPROGRAMPROC,"glCreateProgram");
|
||||
LOAD_PROC_OPTIONAL(furLinkProgram,PFNGLLINKPROGRAMPROC,"glLinkProgram");
|
||||
LOAD_PROC_OPTIONAL(furGetProgramiv,PFNGLGETPROGRAMIVPROC,"glGetProgramiv");
|
||||
LOAD_PROC_OPTIONAL(furUseProgram,PFNGLUSEPROGRAMPROC,"glUseProgram");
|
||||
LOAD_PROC_OPTIONAL(furGetUniformLocation,PFNGLGETUNIFORMLOCATIONPROC,"glGetUniformLocation");
|
||||
LOAD_PROC_OPTIONAL(furUniform1f,PFNGLUNIFORM1FPROC,"glUniform1f");
|
||||
LOAD_PROC_OPTIONAL(furGetShaderInfoLog,PFNGLGETSHADERINFOLOGPROC,"glGetShaderInfoLog");
|
||||
|
||||
|
||||
if (createShader(sh_wipe_srcV,sh_wipe_srcF,sh_wipe_vertex,sh_wipe_fragment,sh_wipe_program)) {
|
||||
sh_wipe_uAlpha=furGetUniformLocation(sh_wipe_program,"uAlpha");
|
||||
}
|
||||
|
||||
C(furGenBuffers(1,&quadBuf));
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::initGUI(SDL_Window* win) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplSDL2_InitForOpenGL(win,context);
|
||||
ImGui_ImplOpenGL3_Init();
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderGL::quit() {
|
||||
if (context==NULL) return false;
|
||||
SDL_GL_DeleteContext(context);
|
||||
context=NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderGL::quitGUI() {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
}
|
||||
65
src/gui/render/renderGL.h
Normal file
65
src/gui/render/renderGL.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* 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 "../gui.h"
|
||||
|
||||
class FurnaceGUIRenderGL: public FurnaceGUIRender {
|
||||
SDL_GLContext context;
|
||||
SDL_Window* sdlWin;
|
||||
float quadVertex[4][3];
|
||||
unsigned int quadBuf;
|
||||
|
||||
// SHADERS //
|
||||
// -> wipe
|
||||
int sh_wipe_vertex;
|
||||
int sh_wipe_fragment;
|
||||
int sh_wipe_program;
|
||||
int sh_wipe_uAlpha;
|
||||
|
||||
bool createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program);
|
||||
|
||||
public:
|
||||
ImTextureID getTextureID(void* which);
|
||||
bool lockTexture(void* which, void** data, int* pitch);
|
||||
bool unlockTexture(void* which);
|
||||
bool updateTexture(void* which, void* data, int pitch);
|
||||
void* createTexture(bool dynamic, int width, int height);
|
||||
bool destroyTexture(void* which);
|
||||
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
void clear(ImVec4 color);
|
||||
bool newFrame();
|
||||
void createFontsTexture();
|
||||
void destroyFontsTexture();
|
||||
void renderGUI();
|
||||
void wipe(float alpha);
|
||||
void present();
|
||||
bool getOutputSize(int& w, int& h);
|
||||
int getWindowFlags();
|
||||
void preInit();
|
||||
bool init(SDL_Window* win);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
bool quit();
|
||||
FurnaceGUIRenderGL():
|
||||
context(NULL),
|
||||
sdlWin(NULL) {
|
||||
memset(quadVertex,0,4*3*sizeof(float));
|
||||
}
|
||||
};
|
||||
147
src/gui/render/renderSDL.cpp
Normal file
147
src/gui/render/renderSDL.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* 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 "renderSDL.h"
|
||||
#include "backends/imgui_impl_sdlrenderer2.h"
|
||||
|
||||
ImTextureID FurnaceGUIRenderSDL::getTextureID(void* which) {
|
||||
return which;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::lockTexture(void* which, void** data, int* pitch) {
|
||||
return SDL_LockTexture((SDL_Texture*)which,NULL,data,pitch)==0;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::unlockTexture(void* which) {
|
||||
SDL_UnlockTexture((SDL_Texture*)which);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::updateTexture(void* which, void* data, int pitch) {
|
||||
return SDL_UpdateTexture((SDL_Texture*)which,NULL,data,pitch)==0;
|
||||
}
|
||||
|
||||
void* FurnaceGUIRenderSDL::createTexture(bool dynamic, int width, int height) {
|
||||
return SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,dynamic?SDL_TEXTUREACCESS_STREAMING:SDL_TEXTUREACCESS_STATIC,width,height);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::destroyTexture(void* which) {
|
||||
SDL_DestroyTexture((SDL_Texture*)which);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode) {
|
||||
switch (mode) {
|
||||
case GUI_BLEND_MODE_NONE:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_NONE);
|
||||
break;
|
||||
case GUI_BLEND_MODE_BLEND:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_BLEND);
|
||||
break;
|
||||
case GUI_BLEND_MODE_ADD:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_ADD);
|
||||
break;
|
||||
case GUI_BLEND_MODE_MULTIPLY:
|
||||
SDL_SetTextureBlendMode((SDL_Texture*)which,SDL_BLENDMODE_MOD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||
switch (mode) {
|
||||
case GUI_BLEND_MODE_NONE:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_NONE);
|
||||
break;
|
||||
case GUI_BLEND_MODE_BLEND:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
break;
|
||||
case GUI_BLEND_MODE_ADD:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_ADD);
|
||||
break;
|
||||
case GUI_BLEND_MODE_MULTIPLY:
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_MOD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::clear(ImVec4 color) {
|
||||
SDL_SetRenderDrawColor(sdlRend,color.x*255,color.y*255,color.z*255,color.w*255);
|
||||
SDL_RenderClear(sdlRend);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::newFrame() {
|
||||
return ImGui_ImplSDLRenderer2_NewFrame();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::createFontsTexture() {
|
||||
ImGui_ImplSDLRenderer2_CreateFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::destroyFontsTexture() {
|
||||
ImGui_ImplSDLRenderer2_DestroyFontsTexture();
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::renderGUI() {
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::wipe(float alpha) {
|
||||
SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(sdlRend,0,0,0,255*alpha);
|
||||
SDL_RenderFillRect(sdlRend,NULL);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::present() {
|
||||
SDL_RenderPresent(sdlRend);
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::getOutputSize(int& w, int& h) {
|
||||
return SDL_GetRendererOutputSize(sdlRend,&w,&h)==0;
|
||||
}
|
||||
|
||||
int FurnaceGUIRenderSDL::getWindowFlags() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::preInit() {
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::init(SDL_Window* win) {
|
||||
sdlRend=SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
|
||||
return (sdlRend!=NULL);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::initGUI(SDL_Window* win) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(win,sdlRend);
|
||||
ImGui_ImplSDLRenderer2_Init(sdlRend);
|
||||
}
|
||||
|
||||
void FurnaceGUIRenderSDL::quitGUI() {
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
}
|
||||
|
||||
bool FurnaceGUIRenderSDL::quit() {
|
||||
if (sdlRend==NULL) return false;
|
||||
SDL_DestroyRenderer(sdlRend);
|
||||
sdlRend=NULL;
|
||||
return true;
|
||||
}
|
||||
49
src/gui/render/renderSDL.h
Normal file
49
src/gui/render/renderSDL.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* 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 "../gui.h"
|
||||
|
||||
class FurnaceGUIRenderSDL: public FurnaceGUIRender {
|
||||
SDL_Renderer* sdlRend;
|
||||
public:
|
||||
ImTextureID getTextureID(void* which);
|
||||
bool lockTexture(void* which, void** data, int* pitch);
|
||||
bool unlockTexture(void* which);
|
||||
bool updateTexture(void* which, void* data, int pitch);
|
||||
void* createTexture(bool dynamic, int width, int height);
|
||||
bool destroyTexture(void* which);
|
||||
void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode);
|
||||
void setBlendMode(FurnaceGUIBlendMode mode);
|
||||
void clear(ImVec4 color);
|
||||
bool newFrame();
|
||||
void createFontsTexture();
|
||||
void destroyFontsTexture();
|
||||
void renderGUI();
|
||||
void wipe(float alpha);
|
||||
void present();
|
||||
bool getOutputSize(int& w, int& h);
|
||||
int getWindowFlags();
|
||||
void preInit();
|
||||
bool init(SDL_Window* win);
|
||||
void initGUI(SDL_Window* win);
|
||||
void quitGUI();
|
||||
bool quit();
|
||||
FurnaceGUIRenderSDL():
|
||||
sdlRend(NULL) {}
|
||||
};
|
||||
|
|
@ -133,6 +133,12 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("SampleEOpenOpt")) {
|
||||
if (ImGui::MenuItem("import raw...")) {
|
||||
doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW:GUI_ACTION_SAMPLE_LIST_OPEN_RAW);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FLOPPY_O "##SESave")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
|
||||
|
|
@ -140,15 +146,23 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("SampleESaveOpt")) {
|
||||
if (ImGui::MenuItem("save raw...")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_SAVE_RAW);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Text("Name");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::PushID(2+curSample);
|
||||
if (ImGui::InputText("##SampleName",&sample->name)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
|
@ -176,6 +190,8 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (isChipVisible[i]) selColumns++;
|
||||
}
|
||||
|
||||
int targetRate=sampleCompatRate?sample->rate:sample->centerRate;
|
||||
|
||||
if (ImGui::BeginTable("SampleProps",(selColumns>1)?4:3,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersV|ImGuiTableFlags_BordersOuterH)) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
|
|
@ -201,16 +217,21 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
popToggleColors();
|
||||
ImGui::TableNextColumn();
|
||||
bool doLoop=(sample->isLoopable());
|
||||
bool doLoop=(sample->loop);
|
||||
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
|
||||
if (doLoop) {
|
||||
sample->loop=true;
|
||||
sample->loopStart=0;
|
||||
sample->loopEnd=sample->samples;
|
||||
if (sample->loopStart<0) {
|
||||
sample->loopStart=0;
|
||||
}
|
||||
if (sample->loopEnd<0) {
|
||||
sample->loopEnd=sample->samples;
|
||||
}
|
||||
} else {
|
||||
sample->loop=false;
|
||||
/*
|
||||
sample->loopStart=-1;
|
||||
sample->loopEnd=sample->samples;
|
||||
sample->loopEnd=sample->samples;*/
|
||||
}
|
||||
updateSampleTex=true;
|
||||
if (e->getSampleFormatMask()&(1U<<DIV_SAMPLE_DEPTH_BRR)) {
|
||||
|
|
@ -238,8 +259,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::Selectable(sampleDepths[i])) {
|
||||
sample->prepareUndo(true);
|
||||
e->lockEngine([this,sample,i]() {
|
||||
sample->render();
|
||||
sample->depth=(DivSampleDepth)i;
|
||||
sample->convert((DivSampleDepth)i);
|
||||
e->renderSamples();
|
||||
});
|
||||
updateSampleTex=true;
|
||||
|
|
@ -273,8 +293,20 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && e->getSampleFormatMask()&(1L<<DIV_SAMPLE_DEPTH_8BIT)) {
|
||||
bool di=sample->dither;
|
||||
if (ImGui::Checkbox("8-bit dither",&di)) {
|
||||
sample->prepareUndo(true);
|
||||
sample->dither=di;
|
||||
e->renderSamplesP();
|
||||
updateSampleTex=true;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("dither the sample when used on a chip that only supports 8-bit samples.");
|
||||
}
|
||||
}
|
||||
|
||||
int targetRate=sampleCompatRate?sample->rate:sample->centerRate;
|
||||
int sampleNote=round(64.0+(128.0*12.0*log((double)targetRate/8363.0)/log(2.0)));
|
||||
int sampleNoteCoarse=60+(sampleNote>>7);
|
||||
int sampleNoteFine=(sampleNote&127)-64;
|
||||
|
|
@ -608,7 +640,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::SameLine();
|
||||
ImGui::Button(ICON_FA_EXPAND "##SResample");
|
||||
if (ImGui::IsItemClicked()) {
|
||||
resampleTarget=sample->rate;
|
||||
resampleTarget=targetRate;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Resample");
|
||||
|
|
@ -629,23 +661,23 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("==")) {
|
||||
resampleTarget=sample->rate;
|
||||
resampleTarget=targetRate;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("2.0x")) {
|
||||
resampleTarget*=2.0;
|
||||
}
|
||||
double factor=resampleTarget/(double)sample->rate;
|
||||
double factor=resampleTarget/(double)targetRate;
|
||||
if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) {
|
||||
resampleTarget=(double)sample->rate*factor;
|
||||
resampleTarget=(double)targetRate*factor;
|
||||
if (resampleTarget<0) resampleTarget=0;
|
||||
if (resampleTarget>96000) resampleTarget=96000;
|
||||
}
|
||||
ImGui::Combo("Filter",&resampleStrat,resampleStrats,6);
|
||||
if (ImGui::Button("Resample")) {
|
||||
sample->prepareUndo(true);
|
||||
e->lockEngine([this,sample]() {
|
||||
if (!sample->resample(resampleTarget,resampleStrat)) {
|
||||
e->lockEngine([this,sample,targetRate]() {
|
||||
if (!sample->resample(targetRate,resampleTarget,resampleStrat)) {
|
||||
showError("couldn't resample! make sure your sample is 8 or 16-bit.");
|
||||
}
|
||||
e->renderSamples();
|
||||
|
|
@ -658,7 +690,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
resampleTarget=sample->rate;
|
||||
resampleTarget=targetRate;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
|
||||
|
|
@ -1093,12 +1125,12 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
|
||||
if (sampleTex==NULL || sampleTexW!=avail.x || sampleTexH!=avail.y) {
|
||||
if (sampleTex!=NULL) {
|
||||
SDL_DestroyTexture(sampleTex);
|
||||
rend->destroyTexture(sampleTex);
|
||||
sampleTex=NULL;
|
||||
}
|
||||
if (avail.x>=1 && avail.y>=1) {
|
||||
logD("recreating sample texture.");
|
||||
sampleTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,avail.x,avail.y);
|
||||
sampleTex=rend->createTexture(true,avail.x,avail.y);
|
||||
sampleTexW=avail.x;
|
||||
sampleTexH=avail.y;
|
||||
if (sampleTex==NULL) {
|
||||
|
|
@ -1114,7 +1146,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
unsigned int* dataT=NULL;
|
||||
int pitch=0;
|
||||
logD("updating sample texture.");
|
||||
if (SDL_LockTexture(sampleTex,NULL,(void**)&dataT,&pitch)!=0) {
|
||||
if (!rend->lockTexture(sampleTex,(void**)&dataT,&pitch)) {
|
||||
logE("error while locking sample texture! %s",SDL_GetError());
|
||||
} else {
|
||||
unsigned int* data=new unsigned int[sampleTexW*sampleTexH];
|
||||
|
|
@ -1199,19 +1231,54 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
}
|
||||
|
||||
memcpy(dataT,data,sampleTexW*sampleTexH*sizeof(unsigned int));
|
||||
SDL_UnlockTexture(sampleTex);
|
||||
if ((pitch>>2)==sampleTexW) {
|
||||
memcpy(dataT,data,sampleTexW*sampleTexH*sizeof(unsigned int));
|
||||
} else {
|
||||
int srcY=0;
|
||||
int destY=0;
|
||||
for (int i=0; i<sampleTexH; i++) {
|
||||
memcpy(&dataT[destY],&data[srcY],sampleTexW*sizeof(unsigned int));
|
||||
srcY+=sampleTexW;
|
||||
destY+=pitch>>2;
|
||||
}
|
||||
}
|
||||
rend->unlockTexture(sampleTex);
|
||||
delete[] data;
|
||||
}
|
||||
updateSampleTex=false;
|
||||
}
|
||||
|
||||
ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0);
|
||||
ImGui::ImageButton(rend->getTextureID(sampleTex),avail,ImVec2(0,0),ImVec2(1,1),0);
|
||||
|
||||
ImVec2 rectMin=ImGui::GetItemRectMin();
|
||||
ImVec2 rectMax=ImGui::GetItemRectMax();
|
||||
ImVec2 rectSize=ImGui::GetItemRectSize();
|
||||
|
||||
unsigned char selectTarget=255;
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
int start=sampleSelStart;
|
||||
int end=sampleSelEnd;
|
||||
if (start>end) {
|
||||
start^=end;
|
||||
end^=start;
|
||||
start^=end;
|
||||
}
|
||||
ImVec2 p1=rectMin;
|
||||
p1.x+=(start-samplePos)/sampleZoom;
|
||||
|
||||
ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y);
|
||||
|
||||
ImVec2 mousePos=ImGui::GetMousePos();
|
||||
if (p1.x>=rectMin.x && p1.x<=rectMax.x && fabs(mousePos.x-p1.x)<2.0*dpiScale) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
selectTarget=0;
|
||||
} else if (p2.x>=rectMin.x && p2.x<=rectMax.x && fabs(mousePos.x-p2.x)<2.0*dpiScale) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
selectTarget=1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsItemClicked()) {
|
||||
nextWindow=GUI_WINDOW_SAMPLE_EDIT;
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
|
|
@ -1238,9 +1305,23 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
sampleDragLen=sample->samples;
|
||||
sampleDragActive=true;
|
||||
sampleSelStart=-1;
|
||||
sampleSelEnd=-1;
|
||||
if (sampleDragMode) sample->prepareUndo(true);
|
||||
if (!sampleDragMode) {
|
||||
switch (selectTarget) {
|
||||
case 0:
|
||||
sampleSelStart^=sampleSelEnd;
|
||||
sampleSelEnd^=sampleSelStart;
|
||||
sampleSelStart^=sampleSelEnd;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
sampleSelStart=-1;
|
||||
sampleSelEnd=-1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sample->prepareUndo(true);
|
||||
}
|
||||
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -543,6 +543,16 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.stepOnDelete=stepOnDeleteB;
|
||||
}
|
||||
|
||||
bool insertBehaviorB=settings.insertBehavior;
|
||||
if (ImGui::Checkbox("Insert pushes entire channel row",&insertBehaviorB)) {
|
||||
settings.insertBehavior=insertBehaviorB;
|
||||
}
|
||||
|
||||
bool pullDeleteRowB=settings.pullDeleteRow;
|
||||
if (ImGui::Checkbox("Pull delete affects entire channel row",&pullDeleteRowB)) {
|
||||
settings.pullDeleteRow=pullDeleteRowB;
|
||||
}
|
||||
|
||||
bool absorbInsInputB=settings.absorbInsInput;
|
||||
if (ImGui::Checkbox("Change current instrument when changing instrument column (absorb)",&absorbInsInputB)) {
|
||||
settings.absorbInsInput=absorbInsInputB;
|
||||
|
|
@ -626,6 +636,14 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!");
|
||||
}
|
||||
|
||||
bool renderClearPosB=settings.renderClearPos;
|
||||
if (ImGui::Checkbox("Late render clear",&renderClearPosB)) {
|
||||
settings.renderClearPos=renderClearPosB;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("calls rend->clear() after rend->present(). might reduce UI latency by one frame in some drivers.");
|
||||
}
|
||||
|
||||
#ifndef IS_MOBILE
|
||||
bool noThreadedInputB=settings.noThreadedInput;
|
||||
if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) {
|
||||
|
|
@ -654,6 +672,22 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.saveUnusedPatterns=saveUnusedPatternsB;
|
||||
}
|
||||
|
||||
bool compressB=settings.compress;
|
||||
if (ImGui::Checkbox("Compress when saving",&compressB)) {
|
||||
settings.compress=compressB;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("use zlib to compress saved songs.");
|
||||
}
|
||||
|
||||
bool newPatternFormatB=settings.newPatternFormat;
|
||||
if (ImGui::Checkbox("Use new pattern format when saving",&newPatternFormatB)) {
|
||||
settings.newPatternFormat=newPatternFormatB;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("use a packed format which saves space when saving songs.\ndisable if you need compatibility with older Furnace and/or tools\nwhich do not support this format.");
|
||||
}
|
||||
|
||||
bool cursorFollowsOrderB=settings.cursorFollowsOrder;
|
||||
if (ImGui::Checkbox("Cursor follows current order when moving it",&cursorFollowsOrderB)) {
|
||||
settings.cursorFollowsOrder=cursorFollowsOrderB;
|
||||
|
|
@ -1146,14 +1180,38 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.midiOutMode=2;
|
||||
}*/
|
||||
|
||||
bool midiOutProgramChangeB=settings.midiOutProgramChange;
|
||||
if (ImGui::Checkbox("Send Program Change",&midiOutProgramChangeB)) {
|
||||
settings.midiOutProgramChange=midiOutProgramChangeB;
|
||||
}
|
||||
|
||||
bool midiOutClockB=settings.midiOutClock;
|
||||
if (ImGui::Checkbox("Send MIDI clock",&midiOutClockB)) {
|
||||
settings.midiOutClock=midiOutClockB;
|
||||
}
|
||||
|
||||
bool midiOutProgramChangeB=settings.midiOutProgramChange;
|
||||
if (ImGui::Checkbox("Send Program Change",&midiOutProgramChangeB)) {
|
||||
settings.midiOutProgramChange=midiOutProgramChangeB;
|
||||
bool midiOutTimeB=settings.midiOutTime;
|
||||
if (ImGui::Checkbox("Send MIDI timecode",&midiOutTimeB)) {
|
||||
settings.midiOutTime=midiOutTimeB;
|
||||
}
|
||||
|
||||
if (settings.midiOutTime) {
|
||||
ImGui::Text("Timecode frame rate:");
|
||||
if (ImGui::RadioButton("Closest to Tick Rate",settings.midiOutTimeRate==0)) {
|
||||
settings.midiOutTimeRate=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Film (24fps)",settings.midiOutTimeRate==1)) {
|
||||
settings.midiOutTimeRate=1;
|
||||
}
|
||||
if (ImGui::RadioButton("PAL (25fps)",settings.midiOutTimeRate==2)) {
|
||||
settings.midiOutTimeRate=2;
|
||||
}
|
||||
if (ImGui::RadioButton("NTSC drop (29.97fps)",settings.midiOutTimeRate==3)) {
|
||||
settings.midiOutTimeRate=3;
|
||||
}
|
||||
if (ImGui::RadioButton("NTSC non-drop (30fps)",settings.midiOutTimeRate==4)) {
|
||||
settings.midiOutTimeRate=4;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
|
|
@ -1238,17 +1296,38 @@ void FurnaceGUI::drawSettings() {
|
|||
ImVec2 settingsViewSize=ImGui::GetContentRegionAvail();
|
||||
settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y;
|
||||
if (ImGui::BeginChild("SettingsView",settingsViewSize)) {
|
||||
if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) {
|
||||
if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) {
|
||||
settings.renderDriver="";
|
||||
String curRenderBackend=settings.renderBackend.empty()?GUI_BACKEND_DEFAULT_NAME:settings.renderBackend;
|
||||
if (ImGui::BeginCombo("Render backend",curRenderBackend.c_str())) {
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
if (ImGui::Selectable("SDL Renderer",curRenderBackend=="SDL")) {
|
||||
settings.renderBackend="SDL";
|
||||
}
|
||||
for (String& i: availRenderDrivers) {
|
||||
if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) {
|
||||
settings.renderDriver=i;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
if (ImGui::Selectable("DirectX 11",curRenderBackend=="DirectX 11")) {
|
||||
settings.renderBackend="DirectX 11";
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_RENDER_GL
|
||||
if (ImGui::Selectable("OpenGL",curRenderBackend=="OpenGL")) {
|
||||
settings.renderBackend="OpenGL";
|
||||
}
|
||||
#endif
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (curRenderBackend=="SDL") {
|
||||
if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) {
|
||||
if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) {
|
||||
settings.renderDriver="";
|
||||
}
|
||||
for (String& i: availRenderDrivers) {
|
||||
if (ImGui::Selectable(i.c_str(),i==settings.renderDriver)) {
|
||||
settings.renderDriver=i;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
bool dpiScaleAuto=(settings.dpiScale<0.5f);
|
||||
if (ImGui::Checkbox("Automatic UI scaling factor",&dpiScaleAuto)) {
|
||||
|
|
@ -1811,13 +1890,33 @@ void FurnaceGUI::drawSettings() {
|
|||
}
|
||||
UI_COLOR_CONFIG(GUI_COLOR_BACKGROUND,"Background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND,"Window background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND_CHILD,"Sub-window background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND_POPUP,"Pop-up background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MODAL_BACKDROP,"Modal backdrop");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_HEADER,"Header");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TEXT,"Text");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TITLE_INACTIVE,"Title bar (inactive)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TITLE_COLLAPSED,"Title bar (collapsed)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MENU_BAR,"Menu bar");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_BORDER,"Border");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_BORDER_SHADOW,"Border shadow");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL,"Scroll bar");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL_HOVER,"Scroll bar (hovered)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL_ACTIVE,"Scroll bar (clicked)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SCROLL_BACKGROUND,"Scroll bar background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR,"Separator");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_HOVER,"Separator (hover)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SEPARATOR_ACTIVE,"Separator (active)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DOCKING_PREVIEW,"Docking preview");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DOCKING_EMPTY,"Docking empty");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TABLE_HEADER,"Table header");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_HARD,"Table border (hard)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TABLE_BORDER_SOFT,"Table border (soft)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_DRAG_DROP_TARGET,"Drag and drop target");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_NAV_WIN_HIGHLIGHT,"Window switcher (highlight)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_NAV_WIN_BACKDROP,"Window switcher backdrop");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_ON,"Toggle on");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_OFF,"Toggle off");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing");
|
||||
|
|
@ -2328,6 +2427,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_EDIT);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_UP);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DOWN);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DIR_VIEW);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -2345,6 +2445,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_EDIT);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_UP);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DOWN);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DIR_VIEW);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -2364,6 +2465,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DOWN);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_PREVIEW);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DIR_VIEW);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -2630,8 +2732,10 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.channelTextCenter=e->getConfInt("channelTextCenter",1);
|
||||
settings.maxRecentFile=e->getConfInt("maxRecentFile",10);
|
||||
settings.midiOutClock=e->getConfInt("midiOutClock",0);
|
||||
settings.midiOutTime=e->getConfInt("midiOutTime",0);
|
||||
settings.midiOutProgramChange=e->getConfInt("midiOutProgramChange",0);
|
||||
settings.midiOutMode=e->getConfInt("midiOutMode",1);
|
||||
settings.midiOutTimeRate=e->getConfInt("midiOutTimeRate",0);
|
||||
settings.centerPattern=e->getConfInt("centerPattern",0);
|
||||
settings.ordersCursor=e->getConfInt("ordersCursor",1);
|
||||
settings.persistFadeOut=e->getConfInt("persistFadeOut",1);
|
||||
|
|
@ -2645,6 +2749,12 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.cursorFollowsOrder=e->getConfInt("cursorFollowsOrder",1);
|
||||
settings.iCannotWait=e->getConfInt("iCannotWait",0);
|
||||
settings.orderButtonPos=e->getConfInt("orderButtonPos",2);
|
||||
settings.compress=e->getConfInt("compress",1);
|
||||
settings.newPatternFormat=e->getConfInt("newPatternFormat",1);
|
||||
settings.renderBackend=e->getConfString("renderBackend","SDL");
|
||||
settings.renderClearPos=e->getConfInt("renderClearPos",0);
|
||||
settings.insertBehavior=e->getConfInt("insertBehavior",1);
|
||||
settings.pullDeleteRow=e->getConfInt("pullDeleteRow",1);
|
||||
|
||||
clampSetting(settings.mainFontSize,2,96);
|
||||
clampSetting(settings.patFontSize,2,96);
|
||||
|
|
@ -2750,8 +2860,10 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.channelTextCenter,0,1);
|
||||
clampSetting(settings.maxRecentFile,0,30);
|
||||
clampSetting(settings.midiOutClock,0,1);
|
||||
clampSetting(settings.midiOutTime,0,1);
|
||||
clampSetting(settings.midiOutProgramChange,0,1);
|
||||
clampSetting(settings.midiOutMode,0,2);
|
||||
clampSetting(settings.midiOutTimeRate,0,4);
|
||||
clampSetting(settings.centerPattern,0,1);
|
||||
clampSetting(settings.ordersCursor,0,1);
|
||||
clampSetting(settings.persistFadeOut,0,1);
|
||||
|
|
@ -2763,6 +2875,11 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.cursorFollowsOrder,0,1);
|
||||
clampSetting(settings.iCannotWait,0,1);
|
||||
clampSetting(settings.orderButtonPos,0,2);
|
||||
clampSetting(settings.compress,0,1);
|
||||
clampSetting(settings.newPatternFormat,0,1);
|
||||
clampSetting(settings.renderClearPos,0,1);
|
||||
clampSetting(settings.insertBehavior,0,1);
|
||||
clampSetting(settings.pullDeleteRow,0,1);
|
||||
|
||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||
|
|
@ -2961,8 +3078,10 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("channelTextCenter",settings.channelTextCenter);
|
||||
e->setConf("maxRecentFile",settings.maxRecentFile);
|
||||
e->setConf("midiOutClock",settings.midiOutClock);
|
||||
e->setConf("midiOutTime",settings.midiOutTime);
|
||||
e->setConf("midiOutProgramChange",settings.midiOutProgramChange);
|
||||
e->setConf("midiOutMode",settings.midiOutMode);
|
||||
e->setConf("midiOutTimeRate",settings.midiOutTimeRate);
|
||||
e->setConf("centerPattern",settings.centerPattern);
|
||||
e->setConf("ordersCursor",settings.ordersCursor);
|
||||
e->setConf("persistFadeOut",settings.persistFadeOut);
|
||||
|
|
@ -2976,6 +3095,12 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("cursorFollowsOrder",settings.cursorFollowsOrder);
|
||||
e->setConf("iCannotWait",settings.iCannotWait);
|
||||
e->setConf("orderButtonPos",settings.orderButtonPos);
|
||||
e->setConf("compress",settings.compress);
|
||||
e->setConf("newPatternFormat",settings.newPatternFormat);
|
||||
e->setConf("renderBackend",settings.renderBackend);
|
||||
e->setConf("renderClearPos",settings.renderClearPos);
|
||||
e->setConf("insertBehavior",settings.insertBehavior);
|
||||
e->setConf("pullDeleteRow",settings.pullDeleteRow);
|
||||
|
||||
// colors
|
||||
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
||||
|
|
@ -3015,17 +3140,21 @@ void FurnaceGUI::commitSettings() {
|
|||
|
||||
applyUISettings();
|
||||
|
||||
ImGui_ImplSDLRenderer_DestroyFontsTexture();
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error while building font atlas!");
|
||||
showError("error while loading fonts! please check your settings.");
|
||||
ImGui::GetIO().Fonts->Clear();
|
||||
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
|
||||
patFont=mainFont;
|
||||
ImGui_ImplSDLRenderer_DestroyFontsTexture();
|
||||
if (rend) rend->destroyFontsTexture();
|
||||
if (!ImGui::GetIO().Fonts->Build()) {
|
||||
logE("error again while building font atlas!");
|
||||
} else {
|
||||
rend->createFontsTexture();
|
||||
}
|
||||
} else {
|
||||
rend->createFontsTexture();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3494,7 +3623,28 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
|||
}
|
||||
|
||||
sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND];
|
||||
sty.Colors[ImGuiCol_ChildBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND_CHILD];
|
||||
sty.Colors[ImGuiCol_PopupBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND_POPUP];
|
||||
sty.Colors[ImGuiCol_TitleBg]=uiColors[GUI_COLOR_TITLE_INACTIVE];
|
||||
sty.Colors[ImGuiCol_TitleBgCollapsed]=uiColors[GUI_COLOR_TITLE_COLLAPSED];
|
||||
sty.Colors[ImGuiCol_MenuBarBg]=uiColors[GUI_COLOR_MENU_BAR];
|
||||
sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP];
|
||||
sty.Colors[ImGuiCol_ScrollbarBg]=uiColors[GUI_COLOR_SCROLL_BACKGROUND];
|
||||
sty.Colors[ImGuiCol_ScrollbarGrab]=uiColors[GUI_COLOR_SCROLL];
|
||||
sty.Colors[ImGuiCol_ScrollbarGrabHovered]=uiColors[GUI_COLOR_SCROLL_HOVER];
|
||||
sty.Colors[ImGuiCol_ScrollbarGrabActive]=uiColors[GUI_COLOR_SCROLL_ACTIVE];
|
||||
sty.Colors[ImGuiCol_Separator]=uiColors[GUI_COLOR_SEPARATOR];
|
||||
sty.Colors[ImGuiCol_SeparatorHovered]=uiColors[GUI_COLOR_SEPARATOR_HOVER];
|
||||
sty.Colors[ImGuiCol_SeparatorActive]=uiColors[GUI_COLOR_SEPARATOR_ACTIVE];
|
||||
sty.Colors[ImGuiCol_DockingPreview]=uiColors[GUI_COLOR_DOCKING_PREVIEW];
|
||||
sty.Colors[ImGuiCol_DockingEmptyBg]=uiColors[GUI_COLOR_DOCKING_EMPTY];
|
||||
sty.Colors[ImGuiCol_TableHeaderBg]=uiColors[GUI_COLOR_TABLE_HEADER];
|
||||
sty.Colors[ImGuiCol_TableBorderStrong]=uiColors[GUI_COLOR_TABLE_BORDER_HARD];
|
||||
sty.Colors[ImGuiCol_TableBorderLight]=uiColors[GUI_COLOR_TABLE_BORDER_SOFT];
|
||||
sty.Colors[ImGuiCol_DragDropTarget]=uiColors[GUI_COLOR_DRAG_DROP_TARGET];
|
||||
sty.Colors[ImGuiCol_NavHighlight]=uiColors[GUI_COLOR_NAV_HIGHLIGHT];
|
||||
sty.Colors[ImGuiCol_NavWindowingHighlight]=uiColors[GUI_COLOR_NAV_WIN_HIGHLIGHT];
|
||||
sty.Colors[ImGuiCol_NavWindowingDimBg]=uiColors[GUI_COLOR_NAV_WIN_BACKDROP];
|
||||
sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT];
|
||||
|
||||
sty.Colors[ImGuiCol_Button]=primary;
|
||||
|
|
@ -3710,7 +3860,8 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
|||
}
|
||||
|
||||
mainFont->FallbackChar='?';
|
||||
mainFont->DotChar='.';
|
||||
mainFont->EllipsisChar='.';
|
||||
mainFont->EllipsisCharCount=3;
|
||||
}
|
||||
|
||||
// TODO: allow changing these colors.
|
||||
|
|
@ -3732,6 +3883,10 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
|
|||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT);
|
||||
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".mod",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fc13",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fc14",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fc",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".smod",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ftm",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE);
|
||||
|
||||
ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ void FurnaceGUI::drawSpeed(bool asChild) {
|
|||
if (tempoView) setHz/=2.5;
|
||||
if (setHz<1) setHz=1;
|
||||
if (setHz>999) setHz=999;
|
||||
e->setSongRate(setHz,setHz<52);
|
||||
e->setSongRate(setHz);
|
||||
}
|
||||
if (tempoView) {
|
||||
ImGui::SameLine();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue