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

# Conflicts:
#	src/engine/fileOps.cpp
#	src/engine/platform/lynx.cpp
#	src/engine/platform/rf5c68.cpp
#	src/engine/platform/su.cpp
#	src/engine/platform/x1_010.h
#	src/engine/platform/ym2610.cpp
#	src/engine/platform/ym2610.h
#	src/engine/platform/ym2610b.cpp
#	src/engine/platform/ym2610b.h
#	src/engine/sysDef.cpp
#	src/gui/insEdit.cpp

Add effect command for ADPCM-A global volume, X1-010 Sample bank slot
This commit is contained in:
cam900 2022-08-27 17:25:48 +09:00
commit 54dbd0690c
148 changed files with 4114 additions and 3610 deletions

View File

@ -130,7 +130,7 @@ jobs:
zlib1g-dev \
libjack-jackd2-dev \
appstream
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" || wget "https://tildearrow.org/storage/furnace/ci/appimagetool-x86_64.AppImage"
chmod +x appimagetool-x86_64.AppImage
- name: Install Dependencies [Linux armhf]
@ -151,8 +151,8 @@ jobs:
libsndfile1-dev:armhf \
zlib1g-dev:armhf \
libjack-jackd2-dev:armhf
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-armhf"
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" || wget "https://tildearrow.org/storage/furnace/ci/appimagetool-x86_64.AppImage"
wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-armhf" || wget "https://tildearrow.org/storage/furnace/ci/runtime-armhf"
chmod +x appimagetool-x86_64.AppImage
ls /usr/arm-linux-gnueabihf/lib

View File

@ -23,6 +23,7 @@ set(USE_SNDFILE_DEFAULT ON)
set(SYSTEM_SDL2_DEFAULT OFF)
include(CheckIncludeFile)
include(TestBigEndian)
if (ANDROID)
set(USE_RTMIDI_DEFAULT OFF)
@ -72,9 +73,15 @@ option(WITH_INSTRUMENTS "Install instruments" ON)
set(DEPENDENCIES_INCLUDE_DIRS "")
if (ANDROID AND NOT TERMUX)
set(DEPENDENCIES_DEFINES "IS_MOBILE")
set(DEPENDENCIES_DEFINES "IS_MOBILE")
else()
set(DEPENDENCIES_DEFINES "")
set(DEPENDENCIES_DEFINES "")
endif()
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
if (IS_BIG_ENDIAN)
list(APPEND DEPENDENCIES_DEFINES "TA_BIG_ENDIAN")
endif()
set(DEPENDENCIES_COMPILE_OPTIONS "")
@ -531,6 +538,7 @@ src/gui/midiMap.cpp
src/gui/newSong.cpp
src/gui/orders.cpp
src/gui/osc.cpp
src/gui/patManager.cpp
src/gui/pattern.cpp
src/gui/piano.cpp
src/gui/presets.cpp
@ -544,6 +552,8 @@ src/gui/stats.cpp
src/gui/subSongs.cpp
src/gui/sysConf.cpp
src/gui/sysEx.cpp
src/gui/sysManager.cpp
src/gui/sysPicker.cpp
src/gui/util.cpp
src/gui/waveEdit.cpp
src/gui/volMeter.cpp

View File

@ -33,6 +33,7 @@ the coding style is described here:
- indent switch cases
- preprocessor directives not intended
- if macro comprises more than one line, indent
- no new line after `template<>`
- prefer built-in types:
- `bool`
- `signed char` or `unsigned char` are 8-bit
@ -81,6 +82,7 @@ just put your demo song in `demos/`! be noted there are some guidelines:
- avoid Nintendo song covers.
- avoid big label song covers.
- avoid poor quality songs.
# Finishing

View File

@ -1,5 +1,5 @@
# to-do for 0.6pre1.5-0.6pre2
- volume commands should work on Game Boy
- ability to customize `OFF`, `===` and `REL`
- stereo separation control for AY
- "paste with instrument"

Binary file not shown.

BIN
demos/Egyptian_Rule.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/Waterworld_-_Map.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/rule2.fur Normal file

Binary file not shown.

View File

@ -2,7 +2,7 @@
Namco 163 instrument editor consists of two tabs: one controlling various parameters for waveform initialize and macro tab containing 10 macros.
## N163
## Namco 163
- [Initial Waveform] - Determines the initial waveform for playing.
- [Initial Waveform position in RAM] - Determines the initial waveform position will be load to RAM.
- [Initial Waveform length in RAM] - Determines the initial waveform length will be load to RAM.

View File

@ -4,29 +4,54 @@ In the context of Furnace, a sound sample (usually just referred to as a sample)
In Furnace, these samples can be generated by importing a .wav (think of it as an higher quality MP3) file.
## supported systems
## supported chips
As of Furnace 0.6, the following sound chips have sample support:
- NES/Ricoh 2A03 (with DPCM support and only on channel 5)
- Sega Genesis/YM2612 (channel 6 only; but only if there exists a `1701` effect that gets played on or before a trigger for a sample, or if you are using an instrument with Sample type)
- PC Engine/TurboGrafx 16/Huc6280 (same conditions as above)
- Amiga/Paula (on all channels)
- Arcade/SEGA PCM (same as above)
- Neo Geo/Neo Geo CD (on the last 7 channels (6 if you are using Neo Geo CD) only and can be resampled the same way as above)
- Seta/Allumer X1-010 (same as YM2612)
- Atari Lynx
- MSM6258 and MSM6295
- YMU759/MA-2 (last channel only)
- QSound
- ZX Spectrum 48k
- RF5C68
- WonderSwan
- Tildearrow Sound Unit
- VERA (last channel only)
- Y8590 (last channel only)
- And a few more that I've forgotten to mention.
as of Furnace 0.6, the following sound chips have sample support:
Furnace also has a feature where you can make an Amiga formarted instrument on the YM2612 and Huc6280 to resample a sample you have in the module.
- NES/Ricoh 2A03 (with DPCM support and only on channel 5)
- Sega Genesis/YM2612 (channel 6 only)
- PC Engine/TurboGrafx-16/HuC6280
- Amiga/Paula
- SegaPCM
- Neo Geo/Neo Geo CD/YM2610 (ADPCM channels only)
- Seta/Allumer X1-010
- Atari Lynx
- MSM6258 and MSM6295
- YMU759/MA-2 (last channel only)
- QSound
- ZX Spectrum 48k (1-bit)
- RF5C68
- WonderSwan
- tildearrow Sound Unit
- VERA (last channel only)
- Y8950 (last channel only)
- a few more that I've forgotten to mention
## compatible sample mode
effect `17xx` enables/disables compatible sample mode whether supported (e.g. on Sega Genesis or PC Engine).
in this mode, samples are mapped to notes in an octave from C to B, allowing you to use up to 12 samples.
if you need to use more samples, you may change the sample bank using effect `EBxx`.
use of this mode is discouraged in favor of Sample type instruments.
## notes
due to limitations in some of those sound chips, some restrictions exist:
- Amiga: sample lengths and loop will be set to an even number, and your sample can't be longer than 131070.
- NES: if on DPCM mode, only a limited selection of frequencies is available, and loop position isn't supported (only entire sample).
- SegaPCM: your sample can't be longer than 65535, and the maximum frequency is 31.25KHz.
- QSound: your sample can't be longer than 65535, and the loop length shall not be greater than 32767.
- Neo Geo (ADPCM-A): no looping supported. your samples will play at ~18.5KHz.
- Neo Geo (ADPCM-B): no loop position supported (only entire sample), and the maximum frequency is ~55KHz.
- YM2608: the maximum frequency is ~55KHz.
- MSM6258/MSM6295: no arbitrary frequency.
- ZX Spectrum Beeper: your sample can't be longer than 2048, and it always plays at ~55KHz.
- Seta/Allumer X1-010: frequency resolution is terrible in the lower end. your sample can't be longer than 131072.
furthermore, many of these chips have a limited amount of sample memory. check memory usage in window > statistics.
# the sample editor
@ -34,11 +59,12 @@ You can actually tweak your samples in Furnace's sample editor, which can be acc
In there, you can modify certain data pertaining to your sample, such as the:
- volume of the sample in percentage, where 100% is the current level of the sample (note that you can distort it if you put it too high)
- the sample rate, from 0Hz (no sample movement) to 65535Hz (65.5kHz).
- the sample rate.
- what frequencies to filter, along with filter level/sweep and resonance options (much like the C64)
- and many more.
The changes you make will be applied as soon as you've committed them to your sample, but they can be undoed and redoed, just like text.
# tips
If you have a sample you wanna use that is about 44100 or anything over 32000Hz, downsample the sample to 32000Hz so that the pitch of the sample in Furnace stays like the original audio file. You can do this in Audacity by going to the bottom left of the screen (If you see "Project Rate (Hz)" you are there), change the project rate to 32000Hz and save the file to wav in Audacity using "File -> Export -> Export as WAV".
if you have a sample you wanna use that is about 44100 or anything over 32000Hz, downsample the sample to 32000Hz so that the pitch of the sample in Furnace stays like the original audio file,

View File

@ -2,9 +2,12 @@
Rather than having a dedicated sound synthesizer, early ZX Spectrum models had one piezo beeper, controlled by Z80 CPU and ULA chip. It's capabilities should be on par with an IBM PC speaker... right?
Not really - very soon talented programmers found out ways to output much more than one square wave channel. A lot of ZX beeper routines do exist, as of 0.6pre1 Furnace supports only one - Follin-like engine with 6 channels of narrow pulse wave and click drums.
Not really - very soon talented programmers found out ways to output much more than one square wave channel. A lot of ZX beeper routines do exist, but as of 0.6pre1 Furnace supports only one - Follin-like engine with 6 channels of narrow pulse wave and click drums.
# effects
- `12xx`: set pulse width
- `17xx`: trigger overlay drums.
- `12xx`: set pulse width.
- `17xx`: trigger overlay drum.
- `xx` is the sample number.
- overlay drums are 1-bit and always play at 55930Hz (NTSC) or 55420Hz (PAL).
- the maximum length is 2048!

View File

@ -32,6 +32,12 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
- 112: Furnace dev112
- 111: Furnace dev111
- 110: Furnace dev110
- 109: Furnace dev109
- 108: Furnace dev108
- 107: Furnace dev107
- 106: Furnace dev106
- 105: Furnace dev105
- 104: Furnace dev104
@ -330,7 +336,9 @@ size | description
1 | broken outVol (>=99) or reserved
1 | E1xy and E2xy stop on same note (>=100) or reserved
1 | broken initial position of porta after arp (>=101) or reserved
7 | reserved
1 | SN periods under 8 are treated as 1 (>=108) or reserved
1 | cut/delay effect policy (>=110) or reserved
5 | reserved
--- | **virtual tempo data**
2 | virtual tempo numerator of first song (>=96) or reserved
2 | virtual tempo denominator of first song (>=96) or reserved
@ -447,6 +455,7 @@ size | description
| - 29: SNES
| - 30: Sound Unit
| - 31: Namco WSG
| - 32: OPL (drums)
1 | reserved
STR | instrument name
--- | **FM instrument data**
@ -538,7 +547,18 @@ size | description
4 | extra 1 macro loop (>=17)
4 | extra 2 macro loop (>=17)
4 | extra 3 macro loop (>=17)
1 | arp macro mode
1 | arp macro mode (<112) or reserved
| - treat this value in a special way.
| - before version 112, this byte indicates whether the arp macro mode is fixed or not.
| - from that version onwards, the fixed mode is part of the macro values.
| - to convert a <112 macro mode to a modern one, do the following:
| - is the macro mode set to fixed?
| - if yes, then:
| - set bit 30 of all arp macro values (this is the fixed mode bit)
| - does the macro loop?
| - if yes, then do nothing else
| - if no, then add one to the macro length, and set the last macro value to 0
| - if no, then do nothing
1 | reserved (>=17) or volume macro height (>=15) or reserved
1 | reserved (>=17) or duty macro height (>=15) or reserved
1 | reserved (>=17) or wave macro height (>=15) or reserved
@ -546,6 +566,7 @@ size | description
| - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18.
4?? | arp macro
| - before version 31, this macro's values were stored offset by 12.
| - from version 112 onward, bit 30 of a value indicates fixed mode.
4?? | duty macro
| - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12.
4?? | wave macro
@ -850,6 +871,111 @@ size | description
--- | **Game Boy extra flags** (>=106)
1 | use software envelope
1 | always init hard env on new note
--- | **ES5506 data** (>=107)
1 | filter mode
| - 0: HPK2_HPK2
| - 1: HPK2_LPK1
| - 2: LPK2_LPK2
| - 3: LPK2_LPK1
2 | K1
2 | K2
2 | envelope count
1 | left volume ramp
1 | right volume ramp
1 | K1 ramp
1 | K2 ramp
1 | K1 slow
1 | K2 slow
--- | **SNES data** (>=109)
1 | use envelope
1 | gain mode
1 | gain
1 | attack
1 | decay
1 | sustain
1 | release
--- | **macro speeds/delays** (>=111)
1 | volume macro speed
1 | arp macro speed
1 | duty macro speed
1 | wave macro speed
1 | pitch macro speed
1 | extra 1 macro speed
1 | extra 2 macro speed
1 | extra 3 macro speed
1 | alg macro speed
1 | fb macro speed
1 | fms macro speed
1 | ams macro speed
1 | left panning macro speed
1 | right panning macro speed
1 | phase reset macro speed
1 | extra 4 macro speed
1 | extra 5 macro speed
1 | extra 6 macro speed
1 | extra 7 macro speed
1 | extra 8 macro speed
1 | volume macro delay
1 | arp macro delay
1 | duty macro delay
1 | wave macro delay
1 | pitch macro delay
1 | extra 1 macro delay
1 | extra 2 macro delay
1 | extra 3 macro delay
1 | alg macro delay
1 | fb macro delay
1 | fms macro delay
1 | ams macro delay
1 | left panning macro delay
1 | right panning macro delay
1 | phase reset macro delay
1 | extra 4 macro delay
1 | extra 5 macro delay
1 | extra 6 macro delay
1 | extra 7 macro delay
1 | extra 8 macro delay
--- | **operator macro speeds/delay** × 4 (>=111)
1 | AM macro speed
1 | AR macro speed
1 | DR macro speed
1 | MULT macro speed
1 | RR macro speed
1 | SL macro speed
1 | TL macro speed
1 | DT2 macro speed
1 | RS macro speed
1 | DT macro speed
1 | D2R macro speed
1 | SSG-EG macro speed
1 | DAM macro speed
1 | DVB macro speed
1 | EGT macro speed
1 | KSL macro speed
1 | SUS macro speed
1 | VIB macro speed
1 | WS macro speed
1 | KSR macro speed
1 | AM macro delay
1 | AR macro delay
1 | DR macro delay
1 | MULT macro delay
1 | RR macro delay
1 | SL macro delay
1 | TL macro delay
1 | DT2 macro delay
1 | RS macro delay
1 | DT macro delay
1 | D2R macro delay
1 | SSG-EG macro delay
1 | DAM macro delay
1 | DVB macro delay
1 | EGT macro delay
1 | KSL macro delay
1 | SUS macro delay
1 | VIB macro delay
1 | WS macro delay
1 | KSR macro delay
```
# wavetable
@ -860,9 +986,9 @@ size | description
4 | "WAVE" block ID
4 | size of this block
STR | wavetable name
4 | wavetable size
4 | wavetable min
4 | wavetable max
4 | wavetable width
4 | reserved
4 | wavetable height
4?? | wavetable data
```

View File

@ -110,7 +110,11 @@ bool TAAudioSDL::init(TAAudioDesc& request, TAAudioDesc& response) {
desc.outFormat=TA_AUDIO_FORMAT_F32;
ac.freq=desc.rate;
#ifdef TA_BIG_ENDIAN
ac.format=AUDIO_F32MSB;
#else
ac.format=AUDIO_F32;
#endif
ac.channels=desc.outChans;
ac.samples=desc.bufsize;
ac.callback=taSDLProcess;

View File

@ -420,13 +420,6 @@ class DivDispatch {
*/
virtual bool getWantPreNote();
/**
* get a description of a dispatch-specific effect.
* @param effect the effect.
* @return the description, or NULL if effect is invalid.
*/
virtual const char* getEffectName(unsigned char effect);
/**
* set the chip flags.
* @param flags the flags. see song.h for possible values.

View File

@ -18,6 +18,7 @@
*/
#include "dispatch.h"
#include "song.h"
#define _USE_MATH_DEFINES
#include "engine.h"
#include "instrument.h"
@ -124,8 +125,15 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
if ((effect&0xf0)==0x90) {
return "9xxx: Set sample offset*256";
} else if (chan>=0 && chan<chans) {
const char* ret=disCont[dispatchOfChan[chan]].dispatch->getEffectName(effect);
if (ret!=NULL) return ret;
DivSysDef* sysDef=sysDefs[sysOfChan[chan]];
auto iter=sysDef->effectHandlers.find(effect);
if (iter!=sysDef->effectHandlers.end()) {
return iter->second.description;
}
iter=sysDef->postEffectHandlers.find(effect);
if (iter!=sysDef->postEffectHandlers.end()) {
return iter->second.description;
}
}
break;
}
@ -1380,6 +1388,115 @@ bool DivEngine::removeSystem(int index, bool preserveOrder) {
return true;
}
bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
if (src==dest) {
lastError="source and destination are equal";
return false;
}
if (src<0 || src>=song.systemLen) {
lastError="invalid source index";
return false;
}
if (dest<0 || dest>=song.systemLen) {
lastError="invalid destination index";
return false;
}
//int chanCount=chans;
quitDispatch();
BUSY_BEGIN;
saveLock.lock();
if (!preserveOrder) {
// move channels
unsigned char unswappedChannels[DIV_MAX_CHANS];
unsigned char swappedChannels[DIV_MAX_CHANS];
std::vector<std::vector<int>> swapList;
std::vector<int> chanList;
int tchans=0;
for (int i=0; i<song.systemLen; i++) {
tchans+=getChannelCount(song.system[i]);
}
memset(unswappedChannels,0,DIV_MAX_CHANS);
memset(swappedChannels,0,DIV_MAX_CHANS);
for (int i=0; i<tchans; i++) {
unswappedChannels[i]=i;
}
// prepare swap list
int index=0;
for (int i=0; i<song.systemLen; i++) {
chanList.clear();
for (int j=0; j<getChannelCount(song.system[i]); j++) {
chanList.push_back(index);
index++;
}
swapList.push_back(chanList);
}
swapList[src].swap(swapList[dest]);
// unfold it
index=0;
for (std::vector<int>& i: swapList) {
for (int& j: i) {
swappedChannels[index++]=j;
}
}
logV("swap list:");
for (int i=0; i<tchans; i++) {
logV("- %d -> %d",unswappedChannels[i],swappedChannels[i]);
}
// swap channels
bool allComplete=false;
while (!allComplete) {
logD("doing swap...");
allComplete=true;
for (int i=0; i<tchans; i++) {
if (unswappedChannels[i]!=swappedChannels[i]) {
swapChannels(i,swappedChannels[i]);
allComplete=false;
logD("> %d -> %d",unswappedChannels[i],unswappedChannels[swappedChannels[i]]);
unswappedChannels[i]^=unswappedChannels[swappedChannels[i]];
unswappedChannels[swappedChannels[i]]^=unswappedChannels[i];
unswappedChannels[i]^=unswappedChannels[swappedChannels[i]];
}
}
}
}
DivSystem srcSystem=song.system[src];
song.system[src]=song.system[dest];
song.system[dest]=srcSystem;
song.systemVol[src]^=song.systemVol[dest];
song.systemVol[dest]^=song.systemVol[src];
song.systemVol[src]^=song.systemVol[dest];
song.systemPan[src]^=song.systemPan[dest];
song.systemPan[dest]^=song.systemPan[src];
song.systemPan[src]^=song.systemPan[dest];
song.systemFlags[src]^=song.systemFlags[dest];
song.systemFlags[dest]^=song.systemFlags[src];
song.systemFlags[src]^=song.systemFlags[dest];
recalcChans();
saveLock.unlock();
BUSY_END;
initDispatch();
BUSY_BEGIN;
renderSamples();
reset();
BUSY_END;
return true;
}
void DivEngine::poke(int sys, unsigned int addr, unsigned short val) {
if (sys<0 || sys>=song.systemLen) return;
BUSY_BEGIN;
@ -1655,6 +1772,15 @@ int DivEngine::calcFreq(int base, int pitch, bool period, int octave, int pitch2
base+((pitch*octave)>>1)+pitch2;
}
int DivEngine::calcArp(int note, int arp, int offset) {
if (arp<0) {
if (!(arp&0x40000000)) return (arp|0x40000000)+offset;
} else {
if (arp&0x40000000) return (arp&(~0x40000000))+offset;
}
return note+arp;
}
int DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int range) {
int panL=val>>bits;
int panR=val&((1<<bits)-1);
@ -2192,11 +2318,13 @@ void DivEngine::delInstrument(int index) {
song.ins.erase(song.ins.begin()+index);
song.insLen=song.ins.size();
for (int i=0; i<chans; i++) {
for (int j=0; j<256; j++) {
if (curPat[i].data[j]==NULL) continue;
for (int k=0; k<curSubSong->patLen; k++) {
if (curPat[i].data[j]->data[k][2]>index) {
curPat[i].data[j]->data[k][2]--;
for (size_t j=0; j<song.subsong.size(); j++) {
for (int k=0; k<256; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<song.subsong[j]->patLen; l++) {
if (song.subsong[j]->pat[i].data[k]->data[l][2]>index) {
song.subsong[j]->pat[i].data[k]->data[l][2]--;
}
}
}
}
@ -2207,7 +2335,10 @@ void DivEngine::delInstrument(int index) {
}
int DivEngine::addWave() {
if (song.wave.size()>=256) return -1;
if (song.wave.size()>=256) {
lastError="too many wavetables!";
return -1;
}
BUSY_BEGIN;
saveLock.lock();
DivWavetable* wave=new DivWavetable;
@ -2219,50 +2350,62 @@ int DivEngine::addWave() {
return waveCount;
}
bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
int DivEngine::addWavePtr(DivWavetable* which) {
if (song.wave.size()>=256) {
lastError="too many wavetables!";
return false;
delete which;
return -1;
}
BUSY_BEGIN;
saveLock.lock();
int waveCount=(int)song.wave.size();
song.wave.push_back(which);
song.waveLen=waveCount+1;
saveLock.unlock();
BUSY_END;
return song.waveLen;
}
DivWavetable* DivEngine::waveFromFile(const char* path, bool addRaw) {
FILE* f=ps_fopen(path,"rb");
if (f==NULL) {
lastError=fmt::sprintf("%s",strerror(errno));
return false;
return NULL;
}
unsigned char* buf;
ssize_t len;
if (fseek(f,0,SEEK_END)!=0) {
fclose(f);
lastError=fmt::sprintf("could not seek to end: %s",strerror(errno));
return false;
return NULL;
}
len=ftell(f);
if (len<0) {
fclose(f);
lastError=fmt::sprintf("could not determine file size: %s",strerror(errno));
return false;
return NULL;
}
if (len==(SIZE_MAX>>1)) {
fclose(f);
lastError="file size is invalid!";
return false;
return NULL;
}
if (len==0) {
fclose(f);
lastError="file is empty";
return false;
return NULL;
}
if (fseek(f,0,SEEK_SET)!=0) {
fclose(f);
lastError=fmt::sprintf("could not seek to beginning: %s",strerror(errno));
return false;
return NULL;
}
buf=new unsigned char[len];
if (fread(buf,1,len,f)!=(size_t)len) {
logW("did not read entire wavetable file buffer!");
delete[] buf;
lastError=fmt::sprintf("could not read entire file: %s",strerror(errno));
return false;
return NULL;
}
fclose(f);
@ -2290,7 +2433,7 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
lastError="invalid wavetable header/data!";
delete wave;
delete[] buf;
return false;
return NULL;
}
} else {
try {
@ -2331,7 +2474,7 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
} else {
delete wave;
delete[] buf;
return false;
return NULL;
}
}
} catch (EndOfFileException& e) {
@ -2349,7 +2492,7 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
} else {
delete wave;
delete[] buf;
return false;
return NULL;
}
}
}
@ -2357,17 +2500,10 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
delete wave;
delete[] buf;
lastError="premature end of file";
return false;
return NULL;
}
BUSY_BEGIN;
saveLock.lock();
int waveCount=(int)song.wave.size();
song.wave.push_back(wave);
song.waveLen=waveCount+1;
saveLock.unlock();
BUSY_END;
return true;
return wave;
}
void DivEngine::delWave(int index) {
@ -2383,7 +2519,10 @@ void DivEngine::delWave(int index) {
}
int DivEngine::addSample() {
if (song.sample.size()>=256) return -1;
if (song.sample.size()>=256) {
lastError="too many samples!";
return -1;
}
BUSY_BEGIN;
saveLock.lock();
DivSample* sample=new DivSample;
@ -2400,11 +2539,28 @@ int DivEngine::addSample() {
return sampleCount;
}
int DivEngine::addSampleFromFile(const char* path) {
int DivEngine::addSamplePtr(DivSample* which) {
if (song.sample.size()>=256) {
lastError="too many samples!";
delete which;
return -1;
}
int sampleCount=(int)song.sample.size();
BUSY_BEGIN;
saveLock.lock();
song.sample.push_back(which);
song.sampleLen=sampleCount+1;
saveLock.unlock();
renderSamples();
BUSY_END;
return sampleCount;
}
DivSample* DivEngine::sampleFromFile(const char* path) {
if (song.sample.size()>=256) {
lastError="too many samples!";
return NULL;
}
BUSY_BEGIN;
warnings="";
@ -2437,7 +2593,6 @@ int DivEngine::addSampleFromFile(const char* path) {
if (extS==".dmc") { // read as .dmc
size_t len=0;
DivSample* sample=new DivSample;
int sampleCount=(int)song.sample.size();
sample->name=stripPath;
FILE* f=ps_fopen(path,"rb");
@ -2445,7 +2600,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END;
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
delete sample;
return -1;
return NULL;
}
if (fseek(f,0,SEEK_END)<0) {
@ -2453,7 +2608,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END;
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
delete sample;
return -1;
return NULL;
}
len=ftell(f);
@ -2463,7 +2618,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END;
lastError="file is empty!";
delete sample;
return -1;
return NULL;
}
if (len==(SIZE_MAX>>1)) {
@ -2471,7 +2626,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END;
lastError="file is invalid!";
delete sample;
return -1;
return NULL;
}
if (fseek(f,0,SEEK_SET)<0) {
@ -2479,7 +2634,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END;
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
delete sample;
return -1;
return NULL;
}
sample->rate=33144;
@ -2492,22 +2647,16 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END;
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
delete sample;
return -1;
return NULL;
}
saveLock.lock();
song.sample.push_back(sample);
song.sampleLen=sampleCount+1;
saveLock.unlock();
renderSamples();
BUSY_END;
return sampleCount;
return sample;
}
}
#ifndef HAVE_SNDFILE
lastError="Furnace was not compiled with libsndfile!";
return -1;
return NULL;
#else
SF_INFO si;
SFWrapper sfWrap;
@ -2519,15 +2668,15 @@ int DivEngine::addSampleFromFile(const char* path) {
if (err==SF_ERR_SYSTEM) {
lastError=fmt::sprintf("could not open file! (%s %s)",sf_error_number(err),strerror(errno));
} else {
lastError=fmt::sprintf("could not open file! (%s)",sf_error_number(err));
lastError=fmt::sprintf("could not open file! (%s)\nif this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \"import raw\".",sf_error_number(err));
}
return -1;
return NULL;
}
if (si.frames>16777215) {
lastError="this sample is too big! max sample size is 16777215.";
sfWrap.doClose();
BUSY_END;
return -1;
return NULL;
}
void* buf=NULL;
sf_count_t sampleLen=sizeof(short);
@ -2628,16 +2777,181 @@ int DivEngine::addSampleFromFile(const char* path) {
if (sample->centerRate<4000) sample->centerRate=4000;
if (sample->centerRate>64000) sample->centerRate=64000;
sfWrap.doClose();
saveLock.lock();
song.sample.push_back(sample);
song.sampleLen=sampleCount+1;
saveLock.unlock();
renderSamples();
BUSY_END;
return sampleCount;
return sample;
#endif
}
DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign) {
if (song.sample.size()>=256) {
lastError="too many samples!";
return NULL;
}
if (channels<1) {
lastError="invalid channel count";
return NULL;
}
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) {
if (channels!=1) {
lastError="channel count has to be 1 for non-8/16-bit format";
return NULL;
}
}
BUSY_BEGIN;
warnings="";
const char* pathRedux=strrchr(path,DIR_SEPARATOR);
if (pathRedux==NULL) {
pathRedux=path;
} else {
pathRedux++;
}
String stripPath;
const char* pathReduxEnd=strrchr(pathRedux,'.');
if (pathReduxEnd==NULL) {
stripPath=pathRedux;
} else {
for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) {
stripPath+=*i;
}
}
size_t len=0;
size_t lenDivided=0;
DivSample* sample=new DivSample;
sample->name=stripPath;
FILE* f=ps_fopen(path,"rb");
if (f==NULL) {
BUSY_END;
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
delete sample;
return NULL;
}
if (fseek(f,0,SEEK_END)<0) {
fclose(f);
BUSY_END;
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
delete sample;
return NULL;
}
len=ftell(f);
if (len==0) {
fclose(f);
BUSY_END;
lastError="file is empty!";
delete sample;
return NULL;
}
if (len==(SIZE_MAX>>1)) {
fclose(f);
BUSY_END;
lastError="file is invalid!";
delete sample;
return NULL;
}
if (fseek(f,0,SEEK_SET)<0) {
fclose(f);
BUSY_END;
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
delete sample;
return NULL;
}
lenDivided=len/channels;
unsigned int samples=lenDivided;
switch (depth) {
case DIV_SAMPLE_DEPTH_1BIT:
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
samples=lenDivided*8;
break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
case DIV_SAMPLE_DEPTH_ADPCM_A:
case DIV_SAMPLE_DEPTH_ADPCM_B:
case DIV_SAMPLE_DEPTH_VOX:
samples=lenDivided*2;
break;
case DIV_SAMPLE_DEPTH_8BIT:
samples=lenDivided;
break;
case DIV_SAMPLE_DEPTH_BRR:
samples=16*((lenDivided+8)/9);
break;
case DIV_SAMPLE_DEPTH_16BIT:
samples=(lenDivided+1)/2;
break;
default:
break;
}
if (samples>16777215) {
fclose(f);
BUSY_END;
lastError="this sample is too big! max sample size is 16777215.";
delete sample;
return NULL;
}
sample->rate=32000;
sample->centerRate=32000;
sample->depth=depth;
sample->init(samples);
unsigned char* buf=new unsigned char[len];
if (fread(buf,1,len,f)==0) {
fclose(f);
BUSY_END;
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
delete[] buf;
delete sample;
return NULL;
}
fclose(f);
// import sample
size_t pos=0;
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=0; i<samples; i++) {
int accum=0;
for (int j=0; j<channels; j++) {
if (pos+1>=len) break;
if (bigEndian) {
accum+=(short)(((short)((buf[pos]<<8)|buf[pos+1]))^(unsign?0x8000:0));
} else {
accum+=(short)(((short)(buf[pos]|(buf[pos+1]<<8)))^(unsign?0x8000:0));
}
pos+=2;
}
accum/=channels;
sample->data16[i]=accum;
}
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=0; i<samples; i++) {
int accum=0;
for (int j=0; j<channels; j++) {
if (pos>=len) break;
accum+=(signed char)(buf[pos++]^(unsign?0x80:0));
}
accum/=channels;
sample->data8[i]=accum;
}
} else {
memcpy(sample->getCurBuf(),buf,len);
}
delete[] buf;
BUSY_END;
return sample;
}
void DivEngine::delSample(int index) {
BUSY_BEGIN;
sPreview.sample=-1;
@ -2816,13 +3130,15 @@ void DivEngine::moveOrderDown() {
void DivEngine::exchangeIns(int one, int two) {
for (int i=0; i<chans; i++) {
for (int j=0; j<256; j++) {
if (curPat[i].data[j]==NULL) continue;
for (int k=0; k<curSubSong->patLen; k++) {
if (curPat[i].data[j]->data[k][2]==one) {
curPat[i].data[j]->data[k][2]=two;
} else if (curPat[i].data[j]->data[k][2]==two) {
curPat[i].data[j]->data[k][2]=one;
for (size_t j=0; j<song.subsong.size(); j++) {
for (int k=0; k<256; k++) {
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
for (int l=0; l<curSubSong->patLen; l++) {
if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) {
song.subsong[j]->pat[i].data[k]->data[l][2]=two;
} else if (song.subsong[j]->pat[i].data[k]->data[l][2]==two) {
song.subsong[j]->pat[i].data[k]->data[l][2]=one;
}
}
}
}
@ -2912,7 +3228,7 @@ bool DivEngine::moveSampleDown(int which) {
void DivEngine::noteOn(int chan, int ins, int note, int vol) {
if (chan<0 || chan>=chans) return;
BUSY_BEGIN;
pendingNotes.push(DivNoteEvent(chan,ins,note,vol,true));
pendingNotes.push_back(DivNoteEvent(chan,ins,note,vol,true));
if (!playing) {
reset();
freelance=true;
@ -2924,7 +3240,7 @@ void DivEngine::noteOn(int chan, int ins, int note, int vol) {
void DivEngine::noteOff(int chan) {
if (chan<0 || chan>=chans) return;
BUSY_BEGIN;
pendingNotes.push(DivNoteEvent(chan,-1,-1,-1,false));
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false));
if (!playing) {
reset();
freelance=true;
@ -2976,7 +3292,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
if ((!midiPoly) || (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel))) {
chan[finalChan].midiNote=note;
chan[finalChan].midiAge=midiAgeCounter++;
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
pendingNotes.push_back(DivNoteEvent(finalChan,ins,note,vol,true));
return;
}
if (++finalChan>=chans) {
@ -2997,7 +3313,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
chan[candidate].midiNote=note;
chan[candidate].midiAge=midiAgeCounter++;
pendingNotes.push(DivNoteEvent(candidate,ins,note,vol,true));
pendingNotes.push_back(DivNoteEvent(candidate,ins,note,vol,true));
}
void DivEngine::autoNoteOff(int ch, int note, int vol) {
@ -3009,7 +3325,7 @@ void DivEngine::autoNoteOff(int ch, int note, int vol) {
//if (ch<0 || ch>=chans) return;
for (int i=0; i<chans; i++) {
if (chan[i].midiNote==note) {
pendingNotes.push(DivNoteEvent(i,-1,-1,-1,false));
pendingNotes.push_back(DivNoteEvent(i,-1,-1,-1,false));
chan[i].midiNote=-1;
}
}
@ -3023,7 +3339,7 @@ void DivEngine::autoNoteOffAll() {
}
for (int i=0; i<chans; i++) {
if (chan[i].midiNote!=-1) {
pendingNotes.push(DivNoteEvent(i,-1,-1,-1,false));
pendingNotes.push_back(DivNoteEvent(i,-1,-1,-1,false));
chan[i].midiNote=-1;
}
}

View File

@ -32,7 +32,8 @@
#include <thread>
#include <mutex>
#include <map>
#include <queue>
#include <unordered_map>
#include <deque>
#define addWarning(x) \
if (warnings.empty()) { \
@ -45,8 +46,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev106"
#define DIV_ENGINE_VERSION 106
#define DIV_VERSION "dev112"
#define DIV_ENGINE_VERSION 112
// for imports
#define DIV_VERSION_MOD 0xff01
@ -194,7 +195,29 @@ struct DivDispatchContainer {
dcOffCompensation(false) {}
};
typedef std::function<bool(int,unsigned char,unsigned char)> EffectProcess;
typedef int EffectValConversion(unsigned char,unsigned char);
struct EffectHandler {
DivDispatchCmds dispatchCmd;
const char* description;
EffectValConversion* val;
EffectValConversion* val2;
EffectHandler(
DivDispatchCmds dispatchCmd_,
const char* description_,
EffectValConversion val_=NULL,
EffectValConversion val2_=NULL
):
dispatchCmd(dispatchCmd_),
description(description_),
val(val_),
val2(val2_) {}
};
struct DivDoNotHandleEffect {
};
typedef std::unordered_map<unsigned char,const EffectHandler> EffectHandlerMap;
struct DivSysDef {
const char* name;
@ -211,8 +234,8 @@ struct DivSysDef {
// 0: primary
// 1: alternate (usually PCM)
DivInstrumentType chanInsType[DIV_MAX_CHANS][2];
EffectProcess effectFunc;
EffectProcess postEffectFunc;
const EffectHandlerMap effectHandlers;
const EffectHandlerMap postEffectHandlers;
DivSysDef(
const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans,
bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, const char* desc,
@ -221,8 +244,8 @@ struct DivSysDef {
std::initializer_list<int> chTypes,
std::initializer_list<DivInstrumentType> chInsType1,
std::initializer_list<DivInstrumentType> chInsType2={},
EffectProcess fxHandler=[](int,unsigned char,unsigned char) -> bool {return false;},
EffectProcess postFxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}):
const EffectHandlerMap fxHandlers_={},
const EffectHandlerMap postFxHandlers_={}):
name(sysName),
nameJ(sysNameJ),
description(desc),
@ -233,8 +256,8 @@ struct DivSysDef {
isSTD(isSTDChip),
isCompound(compound),
vgmVersion(vgmVer),
effectFunc(fxHandler),
postEffectFunc(postFxHandler) {
effectHandlers(fxHandlers_),
postEffectHandlers(postFxHandlers_) {
memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*));
memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*));
memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int));
@ -334,7 +357,7 @@ class DivEngine {
DivAudioExportModes exportMode;
double exportFadeOut;
std::map<String,String> conf;
std::queue<DivNoteEvent> pendingNotes;
std::deque<DivNoteEvent> pendingNotes;
bool isMuted[DIV_MAX_CHANS];
std::mutex isBusy, saveLock;
String configPath;
@ -533,6 +556,9 @@ class DivEngine {
// calculate frequency/period
int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0, double clock=1.0, double divider=1.0, int blockBits=0);
// calculate arpeggio
int calcArp(int note, int arp, int offset=0);
// convert panning formats
int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range);
int convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range);
@ -603,6 +629,9 @@ class DivEngine {
// get japanese system name
const char* getSystemNameJ(DivSystem sys);
// get sys definition
const DivSysDef* getSystemDef(DivSystem sys);
// convert sample rate format
int fileToDivRate(int frate);
int divToFileRate(int drate);
@ -708,8 +737,11 @@ class DivEngine {
// add wavetable
int addWave();
// add wavetable from file
bool addWaveFromFile(const char* path, bool loadRaw=true);
// add wavetable from pointer
int addWavePtr(DivWavetable* which);
// get wavetable from file
DivWavetable* waveFromFile(const char* path, bool loadRaw=true);
// delete wavetable
void delWave(int index);
@ -717,8 +749,14 @@ class DivEngine {
// add sample
int addSample();
// add sample from file
int addSampleFromFile(const char* path);
// add sample from pointer
int addSamplePtr(DivSample* which);
// get sample from file
DivSample* sampleFromFile(const char* path);
// get raw sample
DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign);
// delete sample
void delSample(int index);
@ -875,6 +913,9 @@ class DivEngine {
// remove system
bool removeSystem(int index, bool preserveOrder=true);
// move system
bool swapSystem(int src, int dest, bool preserveOrder=true);
// write to register on system
void poke(int sys, unsigned int addr, unsigned short val);

View File

@ -145,7 +145,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.loopModality=0;
ds.properNoiseLayout=false;
ds.waveDutyIsVol=false;
ds.resetMacroOnPorta=true;
// TODO: WHAT?! geodude.dmf fails when this is true
// but isn't that how Defle behaves???
ds.resetMacroOnPorta=false;
ds.legacyVolumeSlides=true;
ds.compatibleArpeggio=true;
ds.noteOffResetsSlides=true;
@ -175,6 +177,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.brokenOutVol=true; // ???
ds.e1e2StopOnSameNote=true;
ds.brokenPortaArp=false;
ds.snNoLowPeriods=true;
ds.delayBehavior=0;
// 1.1 compat flags
if (ds.version>24) {
@ -505,6 +509,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
for (int j=0; j<ins->std.arpMacro.len; j++) {
ins->std.arpMacro.val[j]-=12;
}
} else {
ins->std.arpMacro.mode=0;
for (int j=0; j<ins->std.arpMacro.len; j++) {
ins->std.arpMacro.val[j]^=0x40000000;
}
if (ins->std.arpMacro.loop==255 && ins->std.arpMacro.len<255) {
ins->std.arpMacro.val[ins->std.arpMacro.len++]=0;
}
}
ins->std.dutyMacro.len=reader.readC();
@ -825,6 +837,13 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
data=new short[length];
reader.read(data,length*2);
}
#ifdef TA_BIG_ENDIAN
// convert to big-endian
for (int pos=0; pos<length; pos++) {
data[pos]=(short)((((unsigned short)data[pos])<<8)|(((unsigned short)data[pos])>>8));
}
#endif
if (pitch!=5) {
logD("%d: scaling from %d...",i,pitch);
@ -1056,6 +1075,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<101) {
ds.brokenPortaArp=true;
}
if (ds.version<108) {
ds.snNoLowPeriods=true;
}
if (ds.version<110) {
ds.delayBehavior=1;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -1323,9 +1348,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
// pointers
reader.read(insPtr,ds.insLen*4);
reader.read(wavePtr,ds.waveLen*4);
reader.read(samplePtr,ds.sampleLen*4);
for (int i=0; i<ds.insLen; i++) {
insPtr[i]=reader.readI();
}
for (int i=0; i<ds.waveLen; i++) {
wavePtr[i]=reader.readI();
}
for (int i=0; i<ds.sampleLen; i++) {
samplePtr[i]=reader.readI();
}
for (int i=0; i<numberOfPats; i++) patPtr.push_back(reader.readI());
logD("reading orders (%d)...",subSong->ordersLen);
@ -1462,7 +1493,17 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readC();
}
for (int i=0; i<7; i++) {
if (ds.version>=108) {
ds.snNoLowPeriods=reader.readC();
} else {
reader.readC();
}
if (ds.version>=110) {
ds.delayBehavior=reader.readC();
} else {
reader.readC();
}
for (int i=0; i<5; i++) {
reader.readC();
}
}
@ -1705,11 +1746,36 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version>=58) { // modern sample
sample->init(sample->samples);
reader.read(sample->getCurBuf(),sample->getCurBufLen());
#ifdef TA_BIG_ENDIAN
// convert 16-bit samples to big-endian
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
size_t sampleBufLen=sample->getCurBufLen();
for (size_t pos=0; pos<sampleBufLen; pos+=2) {
sampleBuf[pos]^=sampleBuf[pos+1];
sampleBuf[pos+1]^=sampleBuf[pos];
sampleBuf[pos]^=sampleBuf[pos+1];
}
}
#endif
} else { // legacy sample
int length=sample->samples;
short* data=new short[length];
reader.read(data,2*length);
#ifdef TA_BIG_ENDIAN
// convert 16-bit samples to big-endian
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
size_t sampleBufLen=sample->getCurBufLen();
for (size_t pos=0; pos<sampleBufLen; pos+=2) {
sampleBuf[pos]^=sampleBuf[pos+1];
sampleBuf[pos+1]^=sampleBuf[pos];
sampleBuf[pos]^=sampleBuf[pos+1];
}
}
#endif
if (pitch!=5) {
logD("%d: scaling from %d...",i,pitch);
}
@ -1872,6 +1938,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
ds.noSlidesOnFirstTick=true;
ds.rowResetsArpPos=true;
ds.ignoreJumpAtEnd=false;
ds.delayBehavior=0;
int insCount=31;
bool bypassLimits=false;
@ -2265,6 +2332,95 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
return success;
}
unsigned char fcXORTriangle[32]={
0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8,
0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8
};
unsigned char fcCustom1[32]={
0x45, 0x45, 0x79, 0x7d, 0x7a, 0x77, 0x70, 0x66, 0x61, 0x58, 0x53, 0x4d, 0x2c, 0x20, 0x18, 0x12,
0x04, 0xdb, 0xd3, 0xcd, 0xc6, 0xbc, 0xb5, 0xae, 0xa8, 0xa3, 0x9d, 0x99, 0x93, 0x8e, 0x8b, 0x8a
};
unsigned char fcCustom2[32]={
0x45, 0x45, 0x79, 0x7d, 0x7a, 0x77, 0x70, 0x66, 0x5b, 0x4b, 0x43, 0x37, 0x2c, 0x20, 0x18, 0x12,
0x04, 0xf8, 0xe8, 0xdb, 0xcf, 0xc6, 0xbe, 0xb0, 0xa8, 0xa4, 0x9e, 0x9a, 0x95, 0x94, 0x8d, 0x83
};
unsigned char fcTinyTriangle[16]={
0x00, 0x00, 0x40, 0x60, 0x7f, 0x60, 0x40, 0x20, 0x00, 0xe0, 0xc0, 0xa0, 0x80, 0xa0, 0xc0, 0xe0
};
void generateFCPresetWave(int index, DivWavetable* wave) {
wave->max=255;
wave->len=32;
switch (index) {
case 0x00: case 0x01: case 0x02: case 0x03:
case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
// XOR triangle
for (int i=0; i<32; i++) {
wave->data[i]=(unsigned char)((fcXORTriangle[i]^0x80)^(((index+15)<i)?0x87:0x00));
}
break;
case 0x10: case 0x11: case 0x12: case 0x13:
case 0x14: case 0x15: case 0x16: case 0x17:
case 0x18: case 0x19: case 0x1a: case 0x1b:
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
// pulse
for (int i=0; i<32; i++) {
wave->data[i]=(index>i)?0x01:0xff;
}
break;
case 0x20: case 0x21: case 0x22: case 0x23:
case 0x24: case 0x25: case 0x26: case 0x27:
// tiny pulse
for (int i=0; i<32; i++) {
wave->data[i]=((index-0x18)>(i&15))?0x01:0xff;
}
break;
case 0x28:
case 0x2e:
// saw
for (int i=0; i<32; i++) {
wave->data[i]=i<<3;
}
break;
case 0x29:
case 0x2f:
// tiny saw
for (int i=0; i<32; i++) {
wave->data[i]=(i<<4)&0xff;
}
break;
case 0x2a:
// custom 1
for (int i=0; i<32; i++) {
wave->data[i]=fcCustom1[i]^0x80;
}
break;
case 0x2b:
// custom 2
for (int i=0; i<32; i++) {
wave->data[i]=fcCustom2[i]^0x80;
}
break;
case 0x2c: case 0x2d:
// tiny triangle
for (int i=0; i<32; i++) {
wave->data[i]=fcTinyTriangle[i&15]^0x80;
}
break;
default:
for (int i=0; i<32; i++) {
wave->data[i]=i;
}
break;
}
}
bool DivEngine::loadFC(unsigned char* file, size_t len) {
struct InvalidHeaderException {};
bool success=false;
@ -2275,8 +2431,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
unsigned int patPtr, freqMacroPtr, volMacroPtr, samplePtr, wavePtr;
unsigned int seqLen, patLen, freqMacroLen, volMacroLen, sampleLen;
unsigned char waveLen[40];
unsigned char waveLoopLen[40];
unsigned char waveLen[80];
//unsigned char waveLoopLen[40];
struct FCSequence {
unsigned char pat[4];
@ -2305,9 +2461,11 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.tuning=436.0;
ds.version=DIV_VERSION_FC;
//ds.linearPitch=0;
//ds.pitchMacroIsLinear=false;
//ds.noSlidesOnFirstTick=true;
//ds.rowResetsArpPos=true;
//ds.ignoreJumpAtEnd=false;
ds.pitchSlideSpeed=8;
ds.ignoreJumpAtEnd=false;
// load here
if (!reader.seek(0,SEEK_SET)) {
@ -2366,25 +2524,25 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
wavePtr=0;
}
logD("patPtr: %d",patPtr);
logD("patPtr: %x",patPtr);
logD("patLen: %d",patLen);
logD("freqMacroPtr: %d",freqMacroPtr);
logD("freqMacroPtr: %x",freqMacroPtr);
logD("freqMacroLen: %d",freqMacroLen);
logD("volMacroPtr: %d",volMacroPtr);
logD("volMacroPtr: %x",volMacroPtr);
logD("volMacroLen: %d",volMacroLen);
logD("samplePtr: %d",samplePtr);
logD("samplePtr: %x",samplePtr);
if (isFC14) {
logD("wavePtr: %d",wavePtr);
logD("wavePtr: %x",wavePtr);
} else {
logD("sampleLen: %d",sampleLen);
}
// sample info
logD("samples:");
logD("samples: (%x)",reader.tell());
for (int i=0; i<10; i++) {
sample[i].loopLen=reader.readS_BE();
sample[i].len=reader.readS_BE();
sample[i].loopStart=reader.readS_BE();
sample[i].loopLen=reader.readS_BE();
logD("- %d: %d (%d, %d)",i,sample[i].len,sample[i].loopStart,sample[i].loopLen);
}
@ -2392,11 +2550,10 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
// wavetable lengths
if (isFC14) {
logD("wavetables:");
for (int i=0; i<40; i++) {
waveLen[i]=reader.readC();
waveLoopLen[i]=reader.readC();
for (int i=0; i<80; i++) {
waveLen[i]=(unsigned char)reader.readC();
logD("- %d: %.4x (%.4x)",i,waveLen[i],waveLoopLen[i]);
logD("- %d: %.4x",i,waveLen[i]);
}
}
@ -2485,12 +2642,19 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
DivSample* s=new DivSample;
s->depth=DIV_SAMPLE_DEPTH_8BIT;
if (sample[i].len>0) {
s->init(sample[i].len);
s->init(sample[i].len*2);
}
s->loopStart=sample[i].loopStart*2;
s->loopEnd=(sample[i].loopStart+sample[i].loopLen)*2;
s->loop=(s->loopStart>=0)&&(s->loopEnd>=0);
reader.read(s->data8,sample[i].len);
s->name=fmt::sprintf("Sample %d",i+1);
if (sample[i].loopLen>1) {
s->loopStart=sample[i].loopStart;
s->loopEnd=sample[i].loopStart+(sample[i].loopLen*2);
s->loop=(s->loopStart>=0)&&(s->loopEnd>=0);
}
reader.read(s->data8,sample[i].len*2);
ds.sample.push_back(s);
}
ds.sampleLen=(int)ds.sample.size();
@ -2504,11 +2668,11 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
return false;
}
logD("reading wavetables...");
for (int i=0; i<40; i++) {
for (int i=0; i<80; i++) {
DivWavetable* w=new DivWavetable;
w->min=0;
w->max=255;
w->len=MIN(256,waveLoopLen[i]*2);
w->len=MIN(256,waveLen[i]*2);
for (int i=0; i<256; i++) {
w->data[i]=128;
@ -2517,19 +2681,24 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
if (waveLen[i]>0) {
signed char* waveArray=new signed char[waveLen[i]*2];
reader.read(waveArray,waveLen[i]*2);
int howMany=MIN(waveLen[i]*2,waveLoopLen[i]*2);
int howMany=waveLen[i]*2;
if (howMany>256) howMany=256;
for (int i=0; i<howMany; i++) {
w->data[i]=waveArray[i]+128;
}
delete[] waveArray;
} else {
w->len=32;
for (int i=0; i<32; i++) {
w->data[i]=(i*255)/31;
}
logV("empty wave %d",i);
generateFCPresetWave(i,w);
}
ds.wave.push_back(w);
}
} else {
// generate preset waves
for (int i=0; i<48; i++) {
DivWavetable* w=new DivWavetable;
generateFCPresetWave(i,w);
ds.wave.push_back(w);
}
}
@ -2545,6 +2714,16 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.subsong[0]->speed1=3;
ds.subsong[0]->speed2=3;
int lastIns[4];
int lastNote[4];
signed char lastTranspose[4];
bool isSliding[4];
memset(lastIns,-1,4*sizeof(int));
memset(lastNote,-1,4*sizeof(int));
memset(lastTranspose,0,4);
memset(isSliding,0,4*sizeof(bool));
for (unsigned int i=0; i<seqLen; i++) {
for (int j=0; j<4; j++) {
ds.subsong[0]->orders.ord[j][i]=i;
@ -2557,11 +2736,11 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
}
bool ignoreNext=false;
bool isSliding=false;
for (int k=0; k<32; k++) {
FCPattern& fp=pat[seq[i].pat[j]];
if (fp.note[k]>0 && fp.note[k]<0x49) {
lastNote[j]=fp.note[k];
short note=(fp.note[k]+seq[i].transpose[j])%12;
short octave=2+((fp.note[k]+seq[i].transpose[j])/12);
if (fp.note[k]>=0x3d) octave-=6;
@ -2572,23 +2751,48 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
octave&=0xff;
p->data[k][0]=note;
p->data[k][1]=octave;
if (isSliding) {
isSliding=false;
if (isSliding[j]) {
isSliding[j]=false;
p->data[k][4]=2;
p->data[k][5]=0;
}
} else if (fp.note[k]==0x49) {
if (k>0) {
p->data[k-1][4]=0x0d;
p->data[k-1][5]=0;
}
} else if (k==0 && lastTranspose[j]!=seq[i].transpose[j]) {
p->data[0][2]=lastIns[j];
p->data[0][4]=0x03;
p->data[0][5]=0xff;
lastTranspose[j]=seq[i].transpose[j];
short note=(lastNote[j]+seq[i].transpose[j])%12;
short octave=2+((lastNote[j]+seq[i].transpose[j])/12);
if (lastNote[j]>=0x3d) octave-=6;
if (note==0) {
note=12;
octave--;
}
octave&=0xff;
p->data[k][0]=note;
p->data[k][1]=octave;
}
if (fp.val[k]) {
if (ignoreNext) {
ignoreNext=false;
} else {
if (fp.val[k]&0xe0) {
if (fp.val[k]==0xf0) {
p->data[k][0]=100;
p->data[k][1]=0;
p->data[k][2]=-1;
} else if (fp.val[k]&0xe0) {
if (fp.val[k]&0x40) {
p->data[k][4]=2;
p->data[k][5]=0;
isSliding=false;
isSliding[j]=false;
} else if (fp.val[k]&0x80) {
isSliding=true;
isSliding[j]=true;
if (k<31) {
if (fp.val[k+1]&0x20) {
p->data[k][4]=2;
@ -2604,9 +2808,13 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
}
}
} else {
p->data[k][2]=fp.val[k]&0x3f;
p->data[k][2]=(fp.val[k]+seq[i].offsetIns[j])&0x3f;
lastIns[j]=p->data[k][2];
}
}
} else if (fp.note[k]>0 && fp.note[k]<0x49) {
p->data[k][2]=seq[i].offsetIns[j];
lastIns[j]=p->data[k][2];
}
}
}
@ -2620,17 +2828,23 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ins->type=DIV_INS_AMIGA;
ins->name=fmt::sprintf("Instrument %d",i);
ins->amiga.useWave=true;
//unsigned char seqSpeed=m.val[0];
unsigned char seqSpeed=m.val[0];
unsigned char freqMacro=m.val[1];
//unsigned char vibSpeed=m.val[2];
//unsigned char vibDepth=m.val[3];
//unsigned char vibDelay=m.val[4];
unsigned char vibSpeed=m.val[2];
unsigned char vibDepth=m.val[3];
unsigned char vibDelay=m.val[4];
unsigned char lastVal=m.val[5];
signed char loopMap[64];
memset(loopMap,-1,64);
signed char loopMapFreq[64];
memset(loopMapFreq,-1,64);
signed char loopMapWave[64];
memset(loopMapWave,-1,64);
// volume sequence
ins->std.volMacro.len=0;
for (int j=5; j<64; j++) {
@ -2647,8 +2861,9 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
// TODO: <= or <?
for (int k=0; k<=susTime; k++) {
ins->std.volMacro.val[ins->std.volMacro.len]=lastVal;
if (++ins->std.volMacro.len>=128) break;
if (++ins->std.volMacro.len>=255) break;
}
if (ins->std.volMacro.len>=255) break;
} else if (m.val[j]==0xe9 || m.val[j]==0xea) { // volume slide
if (++j>=64) break;
signed char slideStep=m.val[j];
@ -2667,12 +2882,16 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
}
}
ins->std.volMacro.val[ins->std.volMacro.len]=lastVal;
if (++ins->std.volMacro.len>=128) break;
if (++ins->std.volMacro.len>=255) break;
}
} else {
ins->std.volMacro.val[ins->std.volMacro.len]=m.val[j];
lastVal=m.val[j];
if (++ins->std.volMacro.len>=128) break;
// TODO: replace with upcoming macro speed
for (int k=0; k<MAX(1,seqSpeed); k++) {
ins->std.volMacro.val[ins->std.volMacro.len]=m.val[j];
lastVal=m.val[j];
if (++ins->std.volMacro.len>=255) break;
}
if (ins->std.volMacro.len>=255) break;
}
}
@ -2682,6 +2901,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
if (freqMacro<freqMacros.size()) {
FCMacro& fm=freqMacros[freqMacro];
for (int j=0; j<64; j++) {
loopMapFreq[j]=ins->std.arpMacro.len;
loopMapWave[j]=ins->std.waveMacro.len;
if (fm.val[j]==0xe1) {
break;
} else if (fm.val[j]==0xe2 || fm.val[j]==0xe4) {
@ -2696,33 +2917,80 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ins->std.waveMacro.val[ins->std.waveMacro.len]=wave-10;
ins->std.waveMacro.open=true;
lastVal=wave;
if (++ins->std.waveMacro.len>=128) break;
if (++ins->std.arpMacro.len>=128) break;
//if (++ins->std.arpMacro.len>=255) break;
}
} else if (fm.val[j]==0xe0) {
logV("unhandled loop!");
if (++j>=64) break;
ins->std.arpMacro.loop=loopMapFreq[fm.val[j]&63];
ins->std.waveMacro.loop=loopMapWave[fm.val[j]&63];
break;
} else if (fm.val[j]==0xe3) {
logV("unhandled vibrato!");
} else if (fm.val[j]==0xe8) {
logV("unhandled sustain!");
} else if (fm.val[j]==0xe7) {
logV("unhandled newseq!");
if (++j>=64) break;
fm=freqMacros[MIN(fm.val[j],freqMacros.size()-1)];
j=0;
} else if (fm.val[j]==0xe9) {
logV("unhandled pack!");
} else if (fm.val[j]==0xea) {
logV("unhandled pitch!");
} else {
ins->std.arpMacro.val[ins->std.arpMacro.len]=(signed char)fm.val[j];
if (fm.val[j]>0x80) {
ins->std.arpMacro.val[ins->std.arpMacro.len]=(fm.val[j]-0x80+24)^0x40000000;
} else {
ins->std.arpMacro.val[ins->std.arpMacro.len]=fm.val[j];
}
if (lastVal>=10) {
ins->std.waveMacro.val[ins->std.waveMacro.len]=lastVal-10;
}
ins->std.arpMacro.open=true;
if (++ins->std.arpMacro.len>=128) break;
if (++ins->std.arpMacro.len>=255) break;
if (++ins->std.waveMacro.len>=255) break;
}
}
}
// waveform width
if (lastVal>=10 && (unsigned int)(lastVal-10)<ds.wave.size()) {
ins->amiga.waveLen=ds.wave[lastVal-10]->len-1;
}
// vibrato
for (int j=0; j<=vibDelay; j++) {
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=0;
if (++ins->std.pitchMacro.len>=255) break;
}
int vibPos=0;
ins->std.pitchMacro.loop=ins->std.pitchMacro.len;
do {
vibPos+=vibSpeed;
if (vibPos>vibDepth) vibPos=vibDepth;
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*32;
if (++ins->std.pitchMacro.len>=255) break;
} while (vibPos<vibDepth);
do {
vibPos-=vibSpeed;
if (vibPos<-vibDepth) vibPos=-vibDepth;
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*32;
if (++ins->std.pitchMacro.len>=255) break;
} while (vibPos>-vibDepth);
do {
vibPos+=vibSpeed;
if (vibPos>0) vibPos=0;
ins->std.pitchMacro.val[ins->std.pitchMacro.len]=vibPos*32;
if (++ins->std.pitchMacro.len>=255) break;
} while (vibPos<0);
ds.ins.push_back(ins);
}
ds.insLen=(int)ds.ins.size();
// optimize
ds.subsong[0]->optimizePatterns();
ds.subsong[0]->rearrangePatterns();
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
@ -3039,6 +3307,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
ins->std.arpMacro.len=reader.readC();
ins->std.arpMacro.loop=reader.readI();
ins->std.arpMacro.rel=reader.readI();
// TODO: get rid
ins->std.arpMacro.mode=reader.readI();
for (int j=0; j<ins->std.arpMacro.len; j++) {
ins->std.arpMacro.val[j]=reader.readC();
@ -3318,15 +3587,27 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
// high short is channel
// low short is pattern number
std::vector<PatToWrite> patsToWrite;
bool alreadyAdded[256];
for (int i=0; i<chans; i++) {
for (size_t j=0; j<song.subsong.size(); j++) {
DivSubSong* subs=song.subsong[j];
memset(alreadyAdded,0,256*sizeof(bool));
for (int k=0; k<subs->ordersLen; k++) {
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k]));
alreadyAdded[subs->orders.ord[i][k]]=true;
if (getConfInt("saveUnusedPatterns",0)==1) {
for (int i=0; i<chans; i++) {
for (size_t j=0; j<song.subsong.size(); j++) {
DivSubSong* subs=song.subsong[j];
for (int k=0; k<256; k++) {
if (subs->pat[i].data[k]==NULL) continue;
patsToWrite.push_back(PatToWrite(j,i,k));
}
}
}
} else {
bool alreadyAdded[256];
for (int i=0; i<chans; i++) {
for (size_t j=0; j<song.subsong.size(); j++) {
DivSubSong* subs=song.subsong[j];
memset(alreadyAdded,0,256*sizeof(bool));
for (int k=0; k<subs->ordersLen; k++) {
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k]));
alreadyAdded[subs->orders.ord[i][k]]=true;
}
}
}
}
@ -3472,7 +3753,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(song.brokenOutVol);
w->writeC(song.e1e2StopOnSameNote);
w->writeC(song.brokenPortaArp);
for (int i=0; i<7; i++) {
w->writeC(song.snNoLowPeriods);
w->writeC(song.delayBehavior);
for (int i=0; i<5; i++) {
w->writeC(0);
}
@ -3598,7 +3881,21 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(0xffffffff);
}
#ifdef TA_BIG_ENDIAN
// store 16-bit samples as little-endian
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
size_t bufLen=sample->getCurBufLen();
for (size_t i=0; i<bufLen; i+=2) {
w->writeC(sampleBuf[i+1]);
w->writeC(sampleBuf[i]);
}
} else {
w->write(sample->getCurBuf(),sample->getCurBufLen());
}
#else
w->write(sample->getCurBuf(),sample->getCurBufLen());
#endif
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
@ -3625,7 +3922,13 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
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);
@ -3907,16 +4210,21 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeI(i->std.volMacro.val[j]+18);
}
} else {
w->write(i->std.volMacro.val,4*i->std.volMacro.len);
for (int j=0; j<i->std.volMacro.len; j++) {
w->writeI(i->std.volMacro.val[j]);
}
}
if (i->std.volMacro.len>0) {
w->writeC(i->std.volMacro.loop);
}
}
// TODO: take care of new arp macro format
w->writeC(i->std.arpMacro.len);
if (i->std.arpMacro.mode) {
w->write(i->std.arpMacro.val,4*i->std.arpMacro.len);
for (int j=0; j<i->std.arpMacro.len; j++) {
w->writeI(i->std.arpMacro.val[j]);
}
} else {
for (int j=0; j<i->std.arpMacro.len; j++) {
w->writeI(i->std.arpMacro.val[j]+12);
@ -3933,14 +4241,18 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeI(i->std.dutyMacro.val[j]+12);
}
} else {
w->write(i->std.dutyMacro.val,4*i->std.dutyMacro.len);
for (int j=0; j<i->std.dutyMacro.len; j++) {
w->writeI(i->std.dutyMacro.val[j]);
}
}
if (i->std.dutyMacro.len>0) {
w->writeC(i->std.dutyMacro.loop);
}
w->writeC(i->std.waveMacro.len);
w->write(i->std.waveMacro.val,4*i->std.waveMacro.len);
for (int j=0; j<i->std.waveMacro.len; j++) {
w->writeI(i->std.waveMacro.val[j]);
}
if (i->std.waveMacro.len>0) {
w->writeC(i->std.waveMacro.loop);
}
@ -3991,7 +4303,9 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeI(i->data[j]>>2);
}
} else {
w->write(i->data,4*i->len);
for (int j=0; j<i->len; j++) {
w->writeI(i->data[j]);
}
}
}
@ -4004,7 +4318,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeS(pat->data[k][0]); // note
w->writeS(pat->data[k][1]); // octave
w->writeS(pat->data[k][3]); // volume
#ifdef TA_BIG_ENDIAN
for (int l=0; l<curPat[i].effectCols*2; l++) {
w->writeS(pat->data[k][4+l]);
}
#else
w->write(&pat->data[k][4],2*curPat[i].effectCols*2); // effects
#endif
w->writeS(pat->data[k][2]); // instrument
}
}
@ -4023,7 +4343,15 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(50);
// i'm too lazy to deal with .dmf's weird way of storing 8-bit samples
w->writeC(16);
// well I can't be lazy if it's on a big-endian system
#ifdef TA_BIG_ENDIAN
for (unsigned int j=0; j<i->length16; j++) {
w->writeC(((unsigned short)i->data16[j])&0xff);
w->writeC(((unsigned short)i->data16[j])>>8);
}
#else
w->write(i->data16,i->length16);
#endif
}
saveLock.unlock();

View File

@ -733,6 +733,8 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector<DivInstrument*>& ret, S
ins = new DivInstrument;
ins->type = DIV_INS_OPL;
ins->name = fmt::sprintf("%s (2)", insName);
ins->fm.alg = (feedConnect2nd & 0x1);
ins->fm.fb = ((feedConnect2nd >> 1) & 0xF);
for (int i : {1,0}) {
readOpliOp(reader, ins->fm.op[i]);
}
@ -1498,6 +1500,8 @@ void DivEngine::loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, S
ins = new DivInstrument;
ins->type = DIV_INS_OPL;
ins->name = fmt::sprintf("%s (2)", insName);
ins->fm.alg = (feedConnect2nd & 0x1);
ins->fm.fb = ((feedConnect2nd >> 1) & 0xF);
for (int i : {1,0}) {
patchSum += readWoplOp(reader, ins->fm.op[i]);
}

View File

@ -132,7 +132,7 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeI(std.ex1Macro.loop);
w->writeI(std.ex2Macro.loop);
w->writeI(std.ex3Macro.loop);
w->writeC(std.arpMacro.mode);
w->writeC(0); // this was arp macro mode
w->writeC(0); // reserved
w->writeC(0);
w->writeC(0);
@ -543,12 +543,126 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeC(gb.softEnv);
w->writeC(gb.alwaysInit);
// ES5506
w->writeC(es5506.filter.mode);
w->writeS(es5506.filter.k1);
w->writeS(es5506.filter.k2);
w->writeS(es5506.envelope.ecount);
w->writeC(es5506.envelope.lVRamp);
w->writeC(es5506.envelope.rVRamp);
w->writeC(es5506.envelope.k1Ramp);
w->writeC(es5506.envelope.k2Ramp);
w->writeC(es5506.envelope.k1Slow);
w->writeC(es5506.envelope.k2Slow);
// SNES
w->writeC(snes.useEnv);
w->writeC(snes.gainMode);
w->writeC(snes.gain);
w->writeC(snes.a);
w->writeC(snes.d);
w->writeC(snes.s);
w->writeC(snes.r);
// macro speed/delay
w->writeC(std.volMacro.speed);
w->writeC(std.arpMacro.speed);
w->writeC(std.dutyMacro.speed);
w->writeC(std.waveMacro.speed);
w->writeC(std.pitchMacro.speed);
w->writeC(std.ex1Macro.speed);
w->writeC(std.ex2Macro.speed);
w->writeC(std.ex3Macro.speed);
w->writeC(std.algMacro.speed);
w->writeC(std.fbMacro.speed);
w->writeC(std.fmsMacro.speed);
w->writeC(std.amsMacro.speed);
w->writeC(std.panLMacro.speed);
w->writeC(std.panRMacro.speed);
w->writeC(std.phaseResetMacro.speed);
w->writeC(std.ex4Macro.speed);
w->writeC(std.ex5Macro.speed);
w->writeC(std.ex6Macro.speed);
w->writeC(std.ex7Macro.speed);
w->writeC(std.ex8Macro.speed);
w->writeC(std.volMacro.delay);
w->writeC(std.arpMacro.delay);
w->writeC(std.dutyMacro.delay);
w->writeC(std.waveMacro.delay);
w->writeC(std.pitchMacro.delay);
w->writeC(std.ex1Macro.delay);
w->writeC(std.ex2Macro.delay);
w->writeC(std.ex3Macro.delay);
w->writeC(std.algMacro.delay);
w->writeC(std.fbMacro.delay);
w->writeC(std.fmsMacro.delay);
w->writeC(std.amsMacro.delay);
w->writeC(std.panLMacro.delay);
w->writeC(std.panRMacro.delay);
w->writeC(std.phaseResetMacro.delay);
w->writeC(std.ex4Macro.delay);
w->writeC(std.ex5Macro.delay);
w->writeC(std.ex6Macro.delay);
w->writeC(std.ex7Macro.delay);
w->writeC(std.ex8Macro.delay);
// op macro speed/delay
for (int i=0; i<4; i++) {
DivInstrumentSTD::OpMacro& op=std.opMacros[i];
w->writeC(op.amMacro.speed);
w->writeC(op.arMacro.speed);
w->writeC(op.drMacro.speed);
w->writeC(op.multMacro.speed);
w->writeC(op.rrMacro.speed);
w->writeC(op.slMacro.speed);
w->writeC(op.tlMacro.speed);
w->writeC(op.dt2Macro.speed);
w->writeC(op.rsMacro.speed);
w->writeC(op.dtMacro.speed);
w->writeC(op.d2rMacro.speed);
w->writeC(op.ssgMacro.speed);
w->writeC(op.damMacro.speed);
w->writeC(op.dvbMacro.speed);
w->writeC(op.egtMacro.speed);
w->writeC(op.kslMacro.speed);
w->writeC(op.susMacro.speed);
w->writeC(op.vibMacro.speed);
w->writeC(op.wsMacro.speed);
w->writeC(op.ksrMacro.speed);
w->writeC(op.amMacro.delay);
w->writeC(op.arMacro.delay);
w->writeC(op.drMacro.delay);
w->writeC(op.multMacro.delay);
w->writeC(op.rrMacro.delay);
w->writeC(op.slMacro.delay);
w->writeC(op.tlMacro.delay);
w->writeC(op.dt2Macro.delay);
w->writeC(op.rsMacro.delay);
w->writeC(op.dtMacro.delay);
w->writeC(op.d2rMacro.delay);
w->writeC(op.ssgMacro.delay);
w->writeC(op.damMacro.delay);
w->writeC(op.dvbMacro.delay);
w->writeC(op.egtMacro.delay);
w->writeC(op.kslMacro.delay);
w->writeC(op.susMacro.delay);
w->writeC(op.vibMacro.delay);
w->writeC(op.wsMacro.delay);
w->writeC(op.ksrMacro.delay);
}
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END);
}
#define READ_MACRO_VALS(x,y) \
for (int macroValPos=0; macroValPos<y; macroValPos++) x[macroValPos]=reader.readI();
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
char magic[4];
reader.read(magic,4);
@ -674,10 +788,10 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
int oldVolHeight=reader.readC();
int oldDutyHeight=reader.readC();
reader.readC(); // oldWaveHeight
reader.read(std.volMacro.val,4*std.volMacro.len);
reader.read(std.arpMacro.val,4*std.arpMacro.len);
reader.read(std.dutyMacro.val,4*std.dutyMacro.len);
reader.read(std.waveMacro.val,4*std.waveMacro.len);
READ_MACRO_VALS(std.volMacro.val,std.volMacro.len);
READ_MACRO_VALS(std.arpMacro.val,std.arpMacro.len);
READ_MACRO_VALS(std.dutyMacro.val,std.dutyMacro.len);
READ_MACRO_VALS(std.waveMacro.val,std.waveMacro.len);
if (version<31) {
if (!std.arpMacro.mode) for (int j=0; j<std.arpMacro.len; j++) {
std.arpMacro.val[j]-=12;
@ -692,10 +806,10 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
}
}
if (version>=17) {
reader.read(std.pitchMacro.val,4*std.pitchMacro.len);
reader.read(std.ex1Macro.val,4*std.ex1Macro.len);
reader.read(std.ex2Macro.val,4*std.ex2Macro.len);
reader.read(std.ex3Macro.val,4*std.ex3Macro.len);
READ_MACRO_VALS(std.pitchMacro.val,std.pitchMacro.len);
READ_MACRO_VALS(std.ex1Macro.val,std.ex1Macro.len);
READ_MACRO_VALS(std.ex2Macro.val,std.ex2Macro.len);
READ_MACRO_VALS(std.ex3Macro.val,std.ex3Macro.len);
} else {
if (type==DIV_INS_STD) {
if (oldVolHeight==31) {
@ -730,10 +844,10 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
std.fmsMacro.open=reader.readC();
std.amsMacro.open=reader.readC();
reader.read(std.algMacro.val,4*std.algMacro.len);
reader.read(std.fbMacro.val,4*std.fbMacro.len);
reader.read(std.fmsMacro.val,4*std.fmsMacro.len);
reader.read(std.amsMacro.val,4*std.amsMacro.len);
READ_MACRO_VALS(std.algMacro.val,std.algMacro.len);
READ_MACRO_VALS(std.fbMacro.val,std.fbMacro.len);
READ_MACRO_VALS(std.fmsMacro.val,std.fmsMacro.len);
READ_MACRO_VALS(std.amsMacro.val,std.amsMacro.len);
for (int i=0; i<4; i++) {
DivInstrumentSTD::OpMacro& op=std.opMacros[i];
@ -936,15 +1050,15 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
// clear noise macro if PCE instrument and version<63
if (version<63 && type==DIV_INS_PCE) {
std.dutyMacro.len=0;
std.dutyMacro.loop=-1;
std.dutyMacro.rel=-1;
std.dutyMacro.loop=255;
std.dutyMacro.rel=255;
}
// clear wave macro if OPLL instrument and version<70
if (version<70 && type==DIV_INS_OPLL) {
std.waveMacro.len=0;
std.waveMacro.loop=-1;
std.waveMacro.rel=-1;
std.waveMacro.loop=255;
std.waveMacro.rel=255;
}
// sample map
@ -1007,14 +1121,14 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
std.ex7Macro.open=reader.readC();
std.ex8Macro.open=reader.readC();
reader.read(std.panLMacro.val,4*std.panLMacro.len);
reader.read(std.panRMacro.val,4*std.panRMacro.len);
reader.read(std.phaseResetMacro.val,4*std.phaseResetMacro.len);
reader.read(std.ex4Macro.val,4*std.ex4Macro.len);
reader.read(std.ex5Macro.val,4*std.ex5Macro.len);
reader.read(std.ex6Macro.val,4*std.ex6Macro.len);
reader.read(std.ex7Macro.val,4*std.ex7Macro.len);
reader.read(std.ex8Macro.val,4*std.ex8Macro.len);
READ_MACRO_VALS(std.panLMacro.val,std.panLMacro.len);
READ_MACRO_VALS(std.panRMacro.val,std.panRMacro.len);
READ_MACRO_VALS(std.phaseResetMacro.val,std.phaseResetMacro.len);
READ_MACRO_VALS(std.ex4Macro.val,std.ex4Macro.len);
READ_MACRO_VALS(std.ex5Macro.val,std.ex5Macro.len);
READ_MACRO_VALS(std.ex6Macro.val,std.ex6Macro.len);
READ_MACRO_VALS(std.ex7Macro.val,std.ex7Macro.len);
READ_MACRO_VALS(std.ex8Macro.val,std.ex8Macro.len);
// FDS
fds.modSpeed=reader.readI();
@ -1111,6 +1225,136 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
gb.alwaysInit=reader.readC();
}
// ES5506
if (version>=107) {
es5506.filter.mode=(DivInstrumentES5506::Filter::FilterMode)reader.readC();
es5506.filter.k1=reader.readS();
es5506.filter.k2=reader.readS();
es5506.envelope.ecount=reader.readS();
es5506.envelope.lVRamp=reader.readC();
es5506.envelope.rVRamp=reader.readC();
es5506.envelope.k1Ramp=reader.readC();
es5506.envelope.k2Ramp=reader.readC();
es5506.envelope.k1Slow=reader.readC();
es5506.envelope.k2Slow=reader.readC();
}
// SNES
if (version>=109) {
snes.useEnv=reader.readC();
snes.gainMode=(DivInstrumentSNES::GainMode)reader.readC();
snes.gain=reader.readC();
snes.a=reader.readC();
snes.d=reader.readC();
snes.s=reader.readC();
snes.r=reader.readC();
}
// macro speed/delay
if (version>=111) {
std.volMacro.speed=reader.readC();
std.arpMacro.speed=reader.readC();
std.dutyMacro.speed=reader.readC();
std.waveMacro.speed=reader.readC();
std.pitchMacro.speed=reader.readC();
std.ex1Macro.speed=reader.readC();
std.ex2Macro.speed=reader.readC();
std.ex3Macro.speed=reader.readC();
std.algMacro.speed=reader.readC();
std.fbMacro.speed=reader.readC();
std.fmsMacro.speed=reader.readC();
std.amsMacro.speed=reader.readC();
std.panLMacro.speed=reader.readC();
std.panRMacro.speed=reader.readC();
std.phaseResetMacro.speed=reader.readC();
std.ex4Macro.speed=reader.readC();
std.ex5Macro.speed=reader.readC();
std.ex6Macro.speed=reader.readC();
std.ex7Macro.speed=reader.readC();
std.ex8Macro.speed=reader.readC();
std.volMacro.delay=reader.readC();
std.arpMacro.delay=reader.readC();
std.dutyMacro.delay=reader.readC();
std.waveMacro.delay=reader.readC();
std.pitchMacro.delay=reader.readC();
std.ex1Macro.delay=reader.readC();
std.ex2Macro.delay=reader.readC();
std.ex3Macro.delay=reader.readC();
std.algMacro.delay=reader.readC();
std.fbMacro.delay=reader.readC();
std.fmsMacro.delay=reader.readC();
std.amsMacro.delay=reader.readC();
std.panLMacro.delay=reader.readC();
std.panRMacro.delay=reader.readC();
std.phaseResetMacro.delay=reader.readC();
std.ex4Macro.delay=reader.readC();
std.ex5Macro.delay=reader.readC();
std.ex6Macro.delay=reader.readC();
std.ex7Macro.delay=reader.readC();
std.ex8Macro.delay=reader.readC();
// op macro speed/delay
for (int i=0; i<4; i++) {
DivInstrumentSTD::OpMacro& op=std.opMacros[i];
op.amMacro.speed=reader.readC();
op.arMacro.speed=reader.readC();
op.drMacro.speed=reader.readC();
op.multMacro.speed=reader.readC();
op.rrMacro.speed=reader.readC();
op.slMacro.speed=reader.readC();
op.tlMacro.speed=reader.readC();
op.dt2Macro.speed=reader.readC();
op.rsMacro.speed=reader.readC();
op.dtMacro.speed=reader.readC();
op.d2rMacro.speed=reader.readC();
op.ssgMacro.speed=reader.readC();
op.damMacro.speed=reader.readC();
op.dvbMacro.speed=reader.readC();
op.egtMacro.speed=reader.readC();
op.kslMacro.speed=reader.readC();
op.susMacro.speed=reader.readC();
op.vibMacro.speed=reader.readC();
op.wsMacro.speed=reader.readC();
op.ksrMacro.speed=reader.readC();
op.amMacro.delay=reader.readC();
op.arMacro.delay=reader.readC();
op.drMacro.delay=reader.readC();
op.multMacro.delay=reader.readC();
op.rrMacro.delay=reader.readC();
op.slMacro.delay=reader.readC();
op.tlMacro.delay=reader.readC();
op.dt2Macro.delay=reader.readC();
op.rsMacro.delay=reader.readC();
op.dtMacro.delay=reader.readC();
op.d2rMacro.delay=reader.readC();
op.ssgMacro.delay=reader.readC();
op.damMacro.delay=reader.readC();
op.dvbMacro.delay=reader.readC();
op.egtMacro.delay=reader.readC();
op.kslMacro.delay=reader.readC();
op.susMacro.delay=reader.readC();
op.vibMacro.delay=reader.readC();
op.wsMacro.delay=reader.readC();
op.ksrMacro.delay=reader.readC();
}
}
// old arp macro format
if (version<112) {
if (std.arpMacro.mode) {
std.arpMacro.mode=0;
for (int i=0; i<std.arpMacro.len; i++) {
std.arpMacro.val[i]^=0x40000000;
}
if ((std.arpMacro.loop>=std.arpMacro.len || (std.arpMacro.rel>std.arpMacro.loop && std.arpMacro.rel<std.arpMacro.len)) && std.arpMacro.len<255) {
std.arpMacro.val[std.arpMacro.len++]=0;
}
}
}
return DIV_DATA_SUCCESS;
}
@ -1150,3 +1394,148 @@ bool DivInstrument::save(const char* path) {
w->finish();
return true;
}
bool DivInstrument::saveDMP(const char* path) {
SafeWriter* w=new SafeWriter();
w->init();
// write version
w->writeC(11);
// guess the system
switch (type) {
case DIV_INS_FM:
// we can't tell between Genesis, Neo Geo and Arcade ins type yet
w->writeC(0x02);
w->writeC(1);
break;
case DIV_INS_STD:
// we can't tell between SMS and NES ins type yet
w->writeC(0x03);
w->writeC(0);
break;
case DIV_INS_GB:
w->writeC(0x04);
w->writeC(0);
break;
case DIV_INS_C64:
w->writeC(0x07);
w->writeC(0);
break;
case DIV_INS_PCE:
w->writeC(0x06);
w->writeC(0);
break;
case DIV_INS_OPLL:
// ???
w->writeC(0x13);
w->writeC(1);
break;
case DIV_INS_OPZ:
// data will be lost
w->writeC(0x08);
w->writeC(1);
break;
case DIV_INS_FDS:
// ???
w->writeC(0x06);
w->writeC(0);
break;
default:
// not supported by .dmp
w->finish();
return false;
}
if (type==DIV_INS_FM || type==DIV_INS_OPLL || type==DIV_INS_OPZ) {
w->writeC(fm.fms);
w->writeC(fm.fb);
w->writeC(fm.alg);
w->writeC(fm.ams);
// TODO: OPLL params
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=fm.op[i];
w->writeC(op.mult);
w->writeC(op.tl);
w->writeC(op.ar);
w->writeC(op.dr);
w->writeC(op.sl);
w->writeC(op.rr);
w->writeC(op.am);
w->writeC(op.rs);
w->writeC(op.dt|(op.dt2<<4));
w->writeC(op.d2r);
w->writeC(op.ssgEnv);
}
} else {
if (type!=DIV_INS_GB) {
w->writeC(std.volMacro.len);
for (int i=0; i<std.volMacro.len; i++) {
w->writeI(std.volMacro.val[i]);
}
if (std.volMacro.len>0) w->writeC(std.volMacro.loop);
}
w->writeC(std.arpMacro.len);
for (int i=0; i<std.arpMacro.len; i++) {
w->writeI(std.arpMacro.val[i]+12);
}
if (std.arpMacro.len>0) w->writeC(std.arpMacro.loop);
w->writeC(std.arpMacro.mode);
w->writeC(std.dutyMacro.len);
for (int i=0; i<std.dutyMacro.len; i++) {
w->writeI(std.dutyMacro.val[i]+12);
}
if (std.dutyMacro.len>0) w->writeC(std.dutyMacro.loop);
w->writeC(std.waveMacro.len);
for (int i=0; i<std.waveMacro.len; i++) {
w->writeI(std.waveMacro.val[i]+12);
}
if (std.waveMacro.len>0) w->writeC(std.waveMacro.loop);
if (type==DIV_INS_C64) {
w->writeC(c64.triOn);
w->writeC(c64.sawOn);
w->writeC(c64.pulseOn);
w->writeC(c64.noiseOn);
w->writeC(c64.a);
w->writeC(c64.d);
w->writeC(c64.s);
w->writeC(c64.r);
w->writeC((c64.duty*100)/4095);
w->writeC(c64.ringMod);
w->writeC(c64.oscSync);
w->writeC(c64.toFilter);
w->writeC(c64.volIsCutoff);
w->writeC(c64.initFilter);
w->writeC(c64.res);
w->writeC((c64.cut*100)/2047);
w->writeC(c64.hp);
w->writeC(c64.lp);
w->writeC(c64.bp);
w->writeC(c64.ch3off);
}
if (type==DIV_INS_GB) {
w->writeC(gb.envVol);
w->writeC(gb.envDir);
w->writeC(gb.envLen);
w->writeC(gb.soundLen);
}
}
FILE* outFile=ps_fopen(path,"wb");
if (outFile==NULL) {
logE("could not save instrument: %s!",strerror(errno));
w->finish();
return false;
}
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
logW("did not write entire instrument!");
}
fclose(outFile);
w->finish();
return true;
}

View File

@ -173,21 +173,20 @@ struct DivInstrumentMacro {
int val[256];
unsigned int mode;
bool open;
unsigned char len;
signed char loop;
signed char rel;
unsigned char len, delay, speed, loop, rel;
// the following variables are used by the GUI and not saved in the file
int vScroll, vZoom;
explicit DivInstrumentMacro(const String& n, bool initOpen=false):
name(n),
mode(0),
open(initOpen),
len(0),
loop(-1),
rel(-1),
delay(0),
speed(1),
loop(255),
rel(255),
vScroll(0),
vZoom(-1) {
memset(val,0,256*sizeof(int));
@ -576,6 +575,13 @@ struct DivInstrument {
* @return whether it was successful.
*/
bool save(const char* path);
/**
* save this instrument to a file in .dmp format.
* @param path file path.
* @return whether it was successful.
*/
bool saveDMP(const char* path);
DivInstrument():
name(""),
type(DIV_INS_FM) {

View File

@ -32,6 +32,18 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
had=false;
return;
}
if (delay>0) {
delay--;
return;
}
if (began && source.delay>0) {
delay=source.delay;
} else {
delay=source.speed-1;
}
if (began) {
began=false;
}
if (finished) {
finished=false;
}
@ -41,16 +53,17 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
actualHad=has;
had=actualHad;
if (has) {
lastPos=pos;
val=source.val[pos++];
if (source.rel>=0 && pos>source.rel && !released) {
if (source.loop<source.len && source.loop>=0 && source.loop<source.rel) {
if (pos>source.rel && !released) {
if (source.loop<source.len && source.loop<source.rel) {
pos=source.loop;
} else {
pos--;
}
}
if (pos>=source.len) {
if (source.loop<source.len && source.loop>=0 && (source.loop>=source.rel || source.rel>=source.len)) {
if (source.loop<source.len && (source.loop>=source.rel || source.rel>=source.len)) {
pos=source.loop;
} else if (linger) {
pos--;
@ -239,7 +252,7 @@ void DivMacroInt::init(DivInstrument* which) {
for (size_t i=0; i<macroListLen; i++) {
if (macroSource[i]!=NULL) {
macroList[i]->prepare(*macroSource[i],e);
hasRelease=(macroSource[i]->rel>=0 && macroSource[i]->rel<macroSource[i]->len);
hasRelease=(macroSource[i]->rel<macroSource[i]->len);
} else {
hasRelease=false;
}
@ -251,3 +264,110 @@ void DivMacroInt::notifyInsDeletion(DivInstrument* which) {
init(NULL);
}
}
// randomly-generated
constexpr unsigned int hashTable[256]={
0x0718657, 0xe904eb33, 0x14b2da2b, 0x0ef67ca9,
0x0f0559a, 0x4142065a, 0x4d9ab4ba, 0x3cdd601a,
0x6635aca, 0x2c41ab72, 0xf98e8d31, 0x1003ee63,
0x3fd9fb5, 0x30734d16, 0xe8964431, 0x29bb9b79,
0x817f580, 0xfe083b9e, 0x974b5e85, 0x3b5729c2,
0x2afea96, 0xf1573b4b, 0x308a1024, 0xaa94b92d,
0x693fa93, 0x547ba3da, 0xac4f206c, 0x93f72ea9,
0xcc44001, 0x37e27670, 0xf35a63d0, 0xd1cdbb92,
0x7c7ee24, 0xfa267ee9, 0xf9cd9956, 0x6a6768d4,
0x9e6a108, 0xf6ca4bd0, 0xa53cba9f, 0x526a523a,
0xf46f0c8, 0xf131bd4c, 0x82800d48, 0xabff9214,
0x40eabd4, 0xea0ef8f7, 0xdc3968d6, 0x54c3cb63,
0x8855023, 0xaab73861, 0xff0bea2c, 0x139b9765,
0x4a21279, 0x6b2aa29a, 0xf147cc3f, 0xc42edc1a,
0xfe2f86f, 0x6d352047, 0xd3cac3e4, 0x35e5c389,
0xe923727, 0x12fe3b32, 0x204295c5, 0x254a8b7a,
0xc1d995d, 0x26a512d2, 0xa3e34033, 0x9a968df0,
0x53447ed, 0x36cf4077, 0x189b03a7, 0x558790e8,
0x01f921a, 0x840f260c, 0x93dd2b86, 0x12f69cb0,
0x117d93a, 0xcb2cbc2b, 0xd41e3aed, 0x5ff6ec75,
0x607290d, 0xd41adb92, 0x64f94ba7, 0xaff720f7,
0x6bf1d5d, 0xc8e36c6d, 0x7095bab5, 0xdfbf7b0d,
0x01ddeea, 0xe8f262da, 0xf589512f, 0xc2ecac5d,
0xbe29d98, 0xff8b5a2e, 0x18e7279e, 0x6ad24dcb,
0x2b3b9b1, 0x6f5227d8, 0x076d7553, 0x6c5856e2,
0x995f655, 0xe9fcf5a6, 0x83671b70, 0xaf3aed1e,
0xac340f0, 0x5c7008b4, 0x14651282, 0x8bf855b9,
0x4a933af, 0x829b87f1, 0x9a673070, 0xb19da64f,
0x77d8f36, 0x584c9fdc, 0xa9e52c0d, 0x6da5e13d,
0xae1051f, 0xe85e976f, 0xfeac2d9a, 0x19c46754,
0x1cba6f3, 0xaf21bc31, 0x16b6a8d4, 0xe08b0fdb,
0x97e6e54, 0x5da499ae, 0xab472e19, 0xc2491f2e,
0xc08c563, 0xe91b131b, 0xc8e22451, 0x6995c8fe,
0x7042718, 0x01043738, 0xc7d88b28, 0x2d9f330f,
0x4b3aae5, 0xf1e705ba, 0xc5b8ee59, 0xa8ba4e8f,
0x55f65a2, 0xa1899e41, 0x296243c8, 0x1e502bf2,
0x20080de, 0x841d2239, 0x37b082af, 0xbdd7f7da,
0x4075090, 0x1dc7dc49, 0x5cd3c69a, 0x7fb13b62,
0xb382bf1, 0xa0cfbc2f, 0x9eca4dc1, 0xb9355453,
0x5d0dd24, 0x834f4d8e, 0xe9b136b2, 0xe7b8738d,
0x1c91d41, 0x8cb3ddb5, 0xdc600590, 0x607cff55,
0x2ca7675, 0x4622a8e4, 0x9340e414, 0xcb44928a,
0xa9e791c, 0x68849920, 0xc5b5fcd8, 0xbc352269,
0x3ab13cf, 0xaa3cbbd0, 0x1abacc64, 0x623b5b49,
0xcc8c4c3, 0x3c8f2f70, 0x3e584a28, 0x9316d24d,
0xfe315a2, 0x10f0ba7a, 0xed15a523, 0x4f987369,
0x7aa4a4a, 0x90eaf98f, 0xcf0af610, 0x1b38f4e7,
0x19df72d, 0xd8306808, 0xd54e25ac, 0x76b79c6d,
0x58110cf, 0x06a3e5f2, 0x873a6039, 0xf52684e3,
0xecf39c3, 0x7cbb2759, 0xe280d361, 0x91e8471a,
0xa67cdd3, 0x17cac3be, 0xfc9eff1f, 0x71abdf49,
0x6168624, 0xb68f86f7, 0x67a8e72a, 0xe746911d,
0xca48fd7, 0x8f3cc436, 0x3a3851a8, 0x30a7e26e,
0xca49308, 0xb598ef74, 0x49ef167a, 0xa9e17632,
0x0f7308a, 0xf156efed, 0xcf799645, 0xbae4b85a,
0xecba3fe, 0xd97f861d, 0xc164af62, 0xb1aca42f,
0xf249576, 0x83d1bf4e, 0x2f486a9c, 0xd3b53cc2,
0x17d7c26, 0xd95ddae1, 0x76c1a2f5, 0xf8af6782,
0xdbaece4, 0x010b2b53, 0x049be200, 0xd9fd0d1a,
0x37d7e6c, 0x5b848651, 0x203c98c7, 0x669681b0,
0x683086f, 0xdd0ee8ab, 0x5dbe008b, 0xe5d0690d,
0x23dd758, 0x6b34acbc, 0x4b2b3e65, 0xcc7b56c1,
0x196b0a0, 0x7b065105, 0xb731b01a, 0xd37daa16,
0xf77816b, 0x3c9fa546, 0x81dfadb8, 0x39b1fb8b
};
constexpr unsigned int NAME_HASH(const char* name) {
unsigned int nameHash=0xffffffff;
for (const char* i=name; *i; i++) {
nameHash=(nameHash>>8)^hashTable[(unsigned char)*i];
}
return nameHash;
}
#define CONSIDER(x) case NAME_HASH(#x): return &x; break;
DivMacroStruct* DivMacroInt::structByName(const String& name) {
unsigned int hash=NAME_HASH(name.c_str());
switch (hash) {
CONSIDER(vol)
CONSIDER(arp)
CONSIDER(duty)
CONSIDER(wave)
CONSIDER(pitch)
CONSIDER(ex1)
CONSIDER(ex2)
CONSIDER(ex3)
CONSIDER(alg)
CONSIDER(fb)
CONSIDER(fms)
CONSIDER(ams)
CONSIDER(panL)
CONSIDER(panR)
CONSIDER(phaseReset)
CONSIDER(ex4)
CONSIDER(ex5)
CONSIDER(ex6)
CONSIDER(ex7)
CONSIDER(ex8)
}
return NULL;
}

View File

@ -25,21 +25,24 @@
class DivEngine;
struct DivMacroStruct {
int pos;
int pos, lastPos, delay;
int val;
bool has, had, actualHad, finished, will, linger;
bool has, had, actualHad, finished, will, linger, began;
unsigned int mode;
void doMacro(DivInstrumentMacro& source, bool released, bool tick);
void init() {
pos=mode=0;
pos=lastPos=mode=delay=0;
has=had=actualHad=will=false;
linger=false;
began=true;
// TODO: test whether this breaks anything?
val=0;
}
void prepare(DivInstrumentMacro& source, DivEngine* e);
DivMacroStruct():
pos(0),
lastPos(0),
delay(0),
val(0),
has(false),
had(false),
@ -47,6 +50,7 @@ struct DivMacroStruct {
finished(false),
will(false),
linger(false),
began(false),
mode(0) {}
};
@ -127,6 +131,13 @@ class DivMacroInt {
*/
void notifyInsDeletion(DivInstrument* which);
/**
* get DivMacroStruct by macro name.
* @param which the macro name.
* @return a DivMacroStruct, or NULL if none found.
*/
DivMacroStruct* structByName(const String& name);
DivMacroInt():
e(NULL),
ins(NULL),

View File

@ -18,6 +18,7 @@
*/
#include "engine.h"
#include "../ta-log.h"
static DivPattern emptyPat;
@ -40,6 +41,44 @@ DivPattern* DivChannelData::getPattern(int index, bool create) {
return data[index];
}
std::vector<std::pair<int,int>> DivChannelData::optimize() {
std::vector<std::pair<int,int>> ret;
for (int i=0; i<256; i++) {
if (data[i]!=NULL) {
// compare
for (int j=0; j<256; j++) {
if (j==i) continue;
if (data[j]==NULL) continue;
if (memcmp(data[i]->data,data[j]->data,256*32*sizeof(short))==0) {
delete data[j];
data[j]=NULL;
logV("%d == %d",i,j);
ret.push_back(std::pair<int,int>(j,i));
}
}
}
}
return ret;
}
std::vector<std::pair<int,int>> DivChannelData::rearrange() {
std::vector<std::pair<int,int>> ret;
for (int i=0; i<256; i++) {
if (data[i]==NULL) {
for (int j=i; j<256; j++) {
if (data[j]!=NULL) {
data[i]=data[j];
data[j]=NULL;
logV("%d -> %d",j,i);
ret.push_back(std::pair<int,int>(j,i));
if (++i>=256) break;
}
}
}
}
return ret;
}
void DivChannelData::wipePatterns() {
for (int i=0; i<256; i++) {
if (data[i]!=NULL) {
@ -54,81 +93,6 @@ void DivPattern::copyOn(DivPattern* dest) {
memcpy(dest->data,data,sizeof(data));
}
SafeReader* DivPattern::compile(int len, int fxRows) {
SafeWriter w;
w.init();
short lastNote, lastOctave, lastInstr, lastVolume, lastEffect[8], lastEffectVal[8];
unsigned char rows=0;
lastNote=0;
lastOctave=0;
lastInstr=-1;
lastVolume=-1;
memset(lastEffect,-1,8*sizeof(short));
memset(lastEffectVal,-1,8*sizeof(short));
for (int i=0; i<len; i++) {
unsigned char mask=0;
if (data[i][0]!=-1) {
lastNote=data[i][0];
lastOctave=data[i][1];
mask|=128;
}
if (data[i][2]!=-1 && data[i][2]!=lastInstr) {
lastInstr=data[i][2];
mask|=32;
}
if (data[i][3]!=-1 && data[i][3]!=lastVolume) {
lastVolume=data[i][3];
mask|=64;
}
for (int j=0; j<fxRows; j++) {
if (data[i][4+(j<<1)]!=-1) {
lastEffect[j]=data[i][4+(j<<1)];
lastEffectVal[j]=data[i][5+(j<<1)];
mask=(mask&0xf8)|j;
}
}
if (!mask) {
rows++;
continue;
}
if (rows!=0) {
w.writeC(rows);
}
rows=1;
w.writeC(mask);
if (mask&128) {
if (lastNote==100) {
w.writeC(-128);
} else {
w.writeC(lastNote+(lastOctave*12));
}
}
if (mask&64) {
w.writeC(lastVolume);
}
if (mask&32) {
w.writeC(lastInstr);
}
for (int j=0; j<(mask&7); j++) {
w.writeC(lastEffect[j]);
if (lastEffectVal[j]==-1) {
w.writeC(0);
} else {
w.writeC(lastEffectVal[j]);
}
}
}
w.writeC(rows);
w.writeC(0);
return w.toReader();
}
DivChannelData::DivChannelData():
effectCols(1) {
memset(data,0,256*sizeof(void*));

View File

@ -18,6 +18,7 @@
*/
#include "safeReader.h"
#include <vector>
struct DivPattern {
String name;
@ -28,14 +29,6 @@ struct DivPattern {
* @param dest the destination pattern.
*/
void copyOn(DivPattern* dest);
/**
* don't use yet!
* @param len the pattern length
* @param fxRows number of effect ...columns
* @return a SafeReader.
*/
SafeReader* compile(int len=256, int fxRows=1);
DivPattern();
};
@ -59,6 +52,20 @@ struct DivChannelData {
*/
DivPattern* getPattern(int index, bool create);
/**
* optimize pattern data.
* not thread-safe! use a mutex!
* @return a list of From -> To pairs
*/
std::vector<std::pair<int,int>> optimize();
/**
* re-arrange NULLs.
* not thread-safe! use a mutex!
* @return a list of From -> To pairs
*/
std::vector<std::pair<int,int>> rearrange();
/**
* destroy all patterns on this DivChannelData.
*/

View File

@ -94,10 +94,6 @@ bool DivDispatch::getWantPreNote() {
return false;
}
const char* DivDispatch::getEffectName(unsigned char effect) {
return NULL;
}
void DivDispatch::setFlags(unsigned int flags) {
}

View File

@ -64,21 +64,6 @@ const char** DivPlatformAmiga::getRegisterSheet() {
return regCheatSheetAmiga;
}
const char* DivPlatformAmiga::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Toggle filter (0 disables; 1 enables)";
break;
case 0x11:
return "11xx: Toggle AM with next channel";
break;
case 0x12:
return "12xx: Toggle period modulation with next channel";
break;
}
return NULL;
}
#define writeAudDat(x) \
chan[i].audDat=x; \
if (i<3 && chan[i].useV) { \
@ -178,19 +163,9 @@ void DivPlatformAmiga::tick(bool sysTick) {
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(chan[i].std.arp.val));
} else {
chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(chan[i].note+chan[i].std.arp.val));
}
}
// TODO: why the off mult? this may be a bug!
chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val)));
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(chan[i].note));
chan[i].freqChanged=true;
}
}
if (chan[i].useWave && chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {

View File

@ -105,7 +105,6 @@ class DivPlatformAmiga: public DivDispatch {
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
};

View File

@ -50,111 +50,6 @@ const char** DivPlatformArcade::getRegisterSheet() {
return regCheatSheetOPM;
}
const char* DivPlatformArcade::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set noise frequency (xx: value; 0 disables noise)";
break;
case 0x11:
return "11xx: Set feedback (0 to 7)";
break;
case 0x12:
return "12xx: Set level of operator 1 (0 highest, 7F lowest)";
break;
case 0x13:
return "13xx: Set level of operator 2 (0 highest, 7F lowest)";
break;
case 0x14:
return "14xx: Set level of operator 3 (0 highest, 7F lowest)";
break;
case 0x15:
return "15xx: Set level of operator 4 (0 highest, 7F lowest)";
break;
case 0x16:
return "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)";
break;
case 0x17:
return "17xx: Set LFO speed";
break;
case 0x18:
return "18xx: Set LFO waveform (0 saw, 1 square, 2 triangle, 3 noise)";
break;
case 0x19:
return "19xx: Set attack of all operators (0 to 1F)";
break;
case 0x1a:
return "1Axx: Set attack of operator 1 (0 to 1F)";
break;
case 0x1b:
return "1Bxx: Set attack of operator 2 (0 to 1F)";
break;
case 0x1c:
return "1Cxx: Set attack of operator 3 (0 to 1F)";
break;
case 0x1d:
return "1Dxx: Set attack of operator 4 (0 to 1F)";
break;
case 0x1e:
return "1Exx: Set AM depth (0 to 7F)";
break;
case 0x1f:
return "1Fxx: Set PM depth (0 to 7F)";
break;
case 0x30:
return "30xx: Toggle hard envelope reset on new notes";
break;
case 0x50:
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
break;
case 0x51:
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
break;
case 0x52:
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
break;
case 0x53:
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
break;
case 0x54:
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
break;
case 0x55:
return "55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)";
break;
case 0x56:
return "56xx: Set decay of all operators (0 to 1F)";
break;
case 0x57:
return "57xx: Set decay of operator 1 (0 to 1F)";
break;
case 0x58:
return "58xx: Set decay of operator 2 (0 to 1F)";
break;
case 0x59:
return "59xx: Set decay of operator 3 (0 to 1F)";
break;
case 0x5a:
return "5Axx: Set decay of operator 4 (0 to 1F)";
break;
case 0x5b:
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
break;
case 0x5c:
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
break;
case 0x5d:
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
break;
case 0x5e:
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
break;
case 0x5f:
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
break;
}
return NULL;
}
void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static int o[2];
@ -266,18 +161,9 @@ void DivPlatformArcade::tick(bool sysTick) {
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_LINEAR(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_LINEAR(chan[i].note+(signed char)chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_LINEAR(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_LINEAR(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {

View File

@ -115,7 +115,6 @@ class DivPlatformArcade: public DivPlatformOPM {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformArcade();

View File

@ -73,42 +73,6 @@ const char** DivPlatformAY8910::getRegisterSheet() {
return intellivision?regCheatSheetAY8914:regCheatSheetAY;
}
const char* DivPlatformAY8910::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set channel mode (bit 0: square; bit 1: noise; bit 2: envelope)";
break;
case 0x21:
return "21xx: Set noise frequency (0 to 1F)";
break;
case 0x22:
return "22xy: Set envelope mode (x: shape, y: enable for this channel)";
break;
case 0x23:
return "23xx: Set envelope period low byte";
break;
case 0x24:
return "24xx: Set envelope period high byte";
break;
case 0x25:
return "25xx: Envelope slide up";
break;
case 0x26:
return "26xx: Envelope slide down";
break;
case 0x29:
return "29xy: Set auto-envelope (x: numerator; y: denominator)";
break;
case 0x2e:
return "2Exx: Write to I/O port A";
break;
case 0x2f:
return "2Fxx: Write to I/O port B";
break;
}
return NULL;
}
void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (ayBufLen<len) {
ayBufLen=len;
@ -132,6 +96,7 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
end=true;
break;
}
// Partially
unsigned char dacData=(((unsigned char)s->data8[chan[i].dac.pos]^0x80)>>4);
chan[i].dac.out=MAX(0,MIN(15,(dacData*chan[i].outVol)/15));
if (prev_out!=chan[i].dac.out) {
@ -237,18 +202,9 @@ void DivPlatformAY8910::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
rWrite(0x06,31-chan[i].std.duty.val);

View File

@ -170,7 +170,6 @@ class DivPlatformAY8910: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8):

View File

@ -77,54 +77,6 @@ const char** DivPlatformAY8930::getRegisterSheet() {
return regCheatSheetAY8930;
}
const char* DivPlatformAY8930::getEffectName(unsigned char effect) {
switch (effect) {
case 0x12:
return "12xx: Set duty cycle (0 to 8)";
break;
case 0x20:
return "20xx: Set channel mode (bit 0: square; bit 1: noise; bit 2: envelope)";
break;
case 0x21:
return "21xx: Set noise frequency (0 to 1F)";
break;
case 0x22:
return "22xy: Set envelope mode (x: shape, y: enable for this channel)";
break;
case 0x23:
return "23xx: Set envelope period low byte";
break;
case 0x24:
return "24xx: Set envelope period high byte";
break;
case 0x25:
return "25xx: Envelope slide up";
break;
case 0x26:
return "26xx: Envelope slide down";
break;
case 0x27:
return "27xx: Set noise AND mask";
break;
case 0x28:
return "28xx: Set noise OR mask";
break;
case 0x29:
return "29xy: Set auto-envelope (x: numerator; y: denominator)";
break;
case 0x2d:
return "2Dxx: NOT TO BE EMPLOYED BY THE COMPOSER";
break;
case 0x2e:
return "2Exx: Write to I/O port A";
break;
case 0x2f:
return "2Fxx: Write to I/O port B";
break;
}
return NULL;
}
void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (ayBufLen<len) {
ayBufLen=len;
@ -148,6 +100,7 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l
end=true;
break;
}
// Partially
unsigned char dacData=(((unsigned char)s->data8[chan[i].dac.pos]^0x80)>>3);
chan[i].dac.out=MAX(0,MIN(31,(dacData*chan[i].outVol)/31));
if (prev_out!=chan[i].dac.out) {
@ -257,18 +210,9 @@ void DivPlatformAY8930::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
rWrite(0x06,chan[i].std.duty.val);

View File

@ -171,7 +171,6 @@ class DivPlatformAY8930: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
};

View File

@ -39,15 +39,6 @@ const char** DivPlatformBubSysWSG::getRegisterSheet() {
return regCheatSheetBubSysWSG;
}
const char* DivPlatformBubSysWSG::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
}
return NULL;
}
void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_t len) {
int chanOut=0;
for (size_t h=start; h<start+len; h++) {
@ -101,18 +92,9 @@ void DivPlatformBubSysWSG::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {

View File

@ -85,7 +85,6 @@ class DivPlatformBubSysWSG: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformBubSysWSG();

View File

@ -62,52 +62,6 @@ const char** DivPlatformC64::getRegisterSheet() {
return regCheatSheetSID;
}
const char* DivPlatformC64::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set waveform (bit 0: triangle; bit 1: saw; bit 2: pulse; bit 3: noise)";
break;
case 0x11:
return "11xx: Set coarse cutoff (not recommended; use 4xxx instead)";
break;
case 0x12:
return "12xx: Set coarse pulse width (not recommended; use 3xxx instead)";
break;
case 0x13:
return "13xx: Set resonance (0 to F)";
break;
case 0x14:
return "14xx: Set filter mode (bit 0: low pass; bit 1: band pass; bit 2: high pass)";
break;
case 0x15:
return "15xx: Set envelope reset time";
break;
case 0x1a:
return "1Axx: Disable envelope reset for this channel (1 disables; 0 enables)";
break;
case 0x1b:
return "1Bxy: Reset cutoff (x: on new note; y: now)";
break;
case 0x1c:
return "1Cxy: Reset pulse width (x: on new note; y: now)";
break;
case 0x1e:
return "1Exy: Change additional parameters";
break;
case 0x30: case 0x31: case 0x32: case 0x33:
case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b:
case 0x3c: case 0x3d: case 0x3e: case 0x3f:
return "3xxx: Set pulse width (0 to FFF)";
break;
case 0x40: case 0x41: case 0x42: case 0x43:
case 0x44: case 0x45: case 0x46: case 0x47:
return "4xxx: Set cutoff (0 to 7FF)";
break;
}
return NULL;
}
void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) {
int dcOff=sid.get_dc(0);
for (size_t i=start; i<start+len; i++) {
@ -150,18 +104,9 @@ void DivPlatformC64::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);

View File

@ -103,7 +103,6 @@ class DivPlatformC64: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void setChipModel(bool is6581);
void quit();

View File

@ -55,30 +55,6 @@ const char** DivPlatformFDS::getRegisterSheet() {
return regCheatSheetFDS;
}
const char* DivPlatformFDS::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
case 0x11:
return "11xx: Set modulation depth";
break;
case 0x12:
return "12xy: Set modulation speed high byte (x: enable; y: value)";
break;
case 0x13:
return "13xx: Set modulation speed low byte";
break;
case 0x14:
return "14xx: Set modulator position";
break;
case 0x15:
return "15xx: Set modulator table to waveform";
break;
}
return NULL;
}
void DivPlatformFDS::acquire_puNES(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
extcl_apu_tick_FDS(fds);
@ -145,18 +121,9 @@ void DivPlatformFDS::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
/*
if (chan[i].std.duty.had) {

View File

@ -104,7 +104,6 @@ class DivPlatformFDS: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformFDS();

View File

@ -61,27 +61,6 @@ const char** DivPlatformGB::getRegisterSheet() {
return regCheatSheetGB;
}
const char* DivPlatformGB::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
case 0x11:
return "11xx: Set noise length (0: long; 1: short)";
break;
case 0x12:
return "12xx: Set duty cycle (0 to 3)";
break;
case 0x13:
return "13xy: Setup sweep (x: time; y: shift)";
break;
case 0x14:
return "14xx: Set sweep direction (0: up; 1: down)";
break;
}
return NULL;
}
void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
if (!writes.empty()) {
@ -181,34 +160,18 @@ void DivPlatformGB::tick(bool sysTick) {
chan[i].soundLen=64;
if (!chan[i].keyOn) chan[i].killIt=true;
chan[i].freqChanged=true;
}
}
}
if (chan[i].std.arp.had) {
if (i==3) { // noise
if (chan[i].std.arp.mode) {
chan[i].baseFreq=chan[i].std.arp.val+24;
} else {
chan[i].baseFreq=chan[i].note+chan[i].std.arp.val;
}
chan[i].baseFreq=parent->calcArp(chan[i].note,chan[i].std.arp.val,24);
if (chan[i].baseFreq>255) chan[i].baseFreq=255;
if (chan[i].baseFreq<0) chan[i].baseFreq=0;
} else {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val+24);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val,24));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
@ -353,26 +316,27 @@ void DivPlatformGB::tick(bool sysTick) {
if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false;
}
if (chan[i].killIt) {
if (i!=2) {
//rWrite(16+i*5+2,8);
int killDelta=chan[i].lastKill-chan[i].outVol+1;
if (killDelta<0) killDelta+=16;
chan[i].lastKill=chan[i].outVol;
if (chan[i].killIt) {
if (i!=2) {
//rWrite(16+i*5+2,8);
int killDelta=chan[i].lastKill-chan[i].outVol+1;
if (killDelta<0) killDelta+=16;
chan[i].lastKill=chan[i].outVol;
if (killDelta!=1) {
rWrite(16+i*5+2,((chan[i].envVol<<4))|8);
for (int j=0; j<killDelta; j++) {
rWrite(16+i*5+2,0x09);
rWrite(16+i*5+2,0x11);
rWrite(16+i*5+2,0x08);
}
if (killDelta!=1) {
rWrite(16+i*5+2,((chan[i].envVol<<4))|8);
for (int j=0; j<killDelta; j++) {
rWrite(16+i*5+2,0x09);
rWrite(16+i*5+2,0x11);
rWrite(16+i*5+2,0x08);
}
}
chan[i].killIt=false;
}
chan[i].killIt=false;
}
chan[i].soManyHacksToMakeItDefleCompatible=false;
}
}
@ -409,10 +373,16 @@ int DivPlatformGB::dispatch(DivCommand c) {
ws.init(ins,32,15,chan[c.chan].insChanged);
}
if ((chan[c.chan].insChanged || ins->gb.alwaysInit) && !chan[c.chan].softEnv) {
chan[c.chan].envVol=ins->gb.envVol;
if (!chan[c.chan].soManyHacksToMakeItDefleCompatible && c.chan!=2) {
chan[c.chan].envVol=ins->gb.envVol;
}
chan[c.chan].envLen=ins->gb.envLen;
chan[c.chan].envDir=ins->gb.envDir;
chan[c.chan].soundLen=ins->gb.soundLen;
if (!chan[c.chan].soManyHacksToMakeItDefleCompatible && c.chan!=2) {
chan[c.chan].vol=chan[c.chan].envVol;
chan[c.chan].outVol=chan[c.chan].envVol;
}
}
if (c.chan==2 && chan[c.chan].softEnv) {
chan[c.chan].soundLen=64;
@ -460,6 +430,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
}
if (!chan[c.chan].softEnv) {
chan[c.chan].envVol=chan[c.chan].vol;
chan[c.chan].soManyHacksToMakeItDefleCompatible=true;
} else if (c.chan!=2) {
chan[c.chan].envVol=chan[c.chan].vol;
if (!chan[c.chan].keyOn) chan[c.chan].killIt=true;
@ -636,7 +607,7 @@ void DivPlatformGB::notifyWaveChange(int wave) {
if (chan[2].wave==wave) {
ws.changeWave1(wave);
updateWave();
if (!chan[2].keyOff) chan[2].keyOn=true;
if (!chan[2].keyOff && chan[2].active) chan[2].keyOn=true;
}
}

View File

@ -31,6 +31,7 @@ class DivPlatformGB: public DivDispatch {
int freq, baseFreq, pitch, pitch2, note, ins;
unsigned char duty, sweep;
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, released, softEnv, killIt;
bool soManyHacksToMakeItDefleCompatible;
signed char vol, outVol, wave, lastKill;
unsigned char envVol, envDir, envLen, soundLen;
unsigned short hwSeqPos;
@ -59,6 +60,7 @@ class DivPlatformGB: public DivDispatch {
released(false),
softEnv(false),
killIt(false),
soManyHacksToMakeItDefleCompatible(false),
vol(15),
outVol(15),
wave(-1),
@ -113,7 +115,6 @@ class DivPlatformGB: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
void setFlags(unsigned int flags);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();

View File

@ -27,108 +27,6 @@
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xy: Setup LFO (x: enable; y: speed)";
break;
case 0x11:
return "11xx: Set feedback (0 to 7)";
break;
case 0x12:
return "12xx: Set level of operator 1 (0 highest, 7F lowest)";
break;
case 0x13:
return "13xx: Set level of operator 2 (0 highest, 7F lowest)";
break;
case 0x14:
return "14xx: Set level of operator 3 (0 highest, 7F lowest)";
break;
case 0x15:
return "15xx: Set level of operator 4 (0 highest, 7F lowest)";
break;
case 0x16:
return "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)";
break;
case 0x17:
return "17xx: Enable channel 6 DAC";
break;
case 0x18:
return "18xx: Toggle extended channel 3 mode";
break;
case 0x19:
return "19xx: Set attack of all operators (0 to 1F)";
break;
case 0x1a:
return "1Axx: Set attack of operator 1 (0 to 1F)";
break;
case 0x1b:
return "1Bxx: Set attack of operator 2 (0 to 1F)";
break;
case 0x1c:
return "1Cxx: Set attack of operator 3 (0 to 1F)";
break;
case 0x1d:
return "1Dxx: Set attack of operator 4 (0 to 1F)";
break;
case 0x30:
return "30xx: Toggle hard envelope reset on new notes";
break;
case 0x50:
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
break;
case 0x51:
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
break;
case 0x52:
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
break;
case 0x53:
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
break;
case 0x54:
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
break;
case 0x55:
return "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)";
break;
case 0x56:
return "56xx: Set decay of all operators (0 to 1F)";
break;
case 0x57:
return "57xx: Set decay of operator 1 (0 to 1F)";
break;
case 0x58:
return "58xx: Set decay of operator 2 (0 to 1F)";
break;
case 0x59:
return "59xx: Set decay of operator 3 (0 to 1F)";
break;
case 0x5a:
return "5Axx: Set decay of operator 4 (0 to 1F)";
break;
case 0x5b:
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
break;
case 0x5c:
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
break;
case 0x5d:
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
break;
case 0x5e:
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
break;
case 0x5f:
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
break;
case 0xdf:
return "DFxx: Set sample playback direction (0: normal; 1: reverse)";
break;
}
return NULL;
}
void DivPlatformGenesis::processDAC() {
if (softPCM) {
softPCMTimer+=chipClock/576;
@ -365,26 +263,40 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11);
} else {
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11);
if (i>=5 && chan[i].furnaceDac) {
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=parent->calcBaseFreq(1,1,parent->calcArp(chan[i].note,chan[i].std.arp.val),false);
}
chan[i].freqChanged=true;
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11);
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(chan[i].note,chan[i].std.arp.val),11);
}
chan[i].freqChanged=true;
}
}
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
if (i<6) {
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
if (i>=5 && chan[i].furnaceDac) {
if (chan[i].std.panL.had) {
chan[5].pan&=1;
chan[5].pan|=chan[i].std.panL.val?2:0;
}
if (chan[i].std.panR.had) {
chan[5].pan&=2;
chan[5].pan|=chan[i].std.panR.val?1:0;
}
if (chan[i].std.panL.had || chan[i].std.panR.had) {
rWrite(chanOffs[5]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[5].pan<<6))|(chan[5].state.fms&7)|((chan[5].state.ams&3)<<4));
}
} else {
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
if (i<6) {
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
}
}

View File

@ -144,7 +144,6 @@ class DivPlatformGenesis: public DivPlatformOPN {
int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
DivPlatformGenesis():

View File

@ -561,6 +561,11 @@ void DivPlatformGenesisExt::forceIns() {
opChan[i].freqChanged=true;
}
}
if (extMode && softPCM && chan[7].active) { // CSM
chan[7].insChanged=true;
chan[7].freqChanged=true;
chan[7].keyOn=true;
}
}
void* DivPlatformGenesisExt::getChanState(int ch) {

View File

@ -130,19 +130,6 @@ const char** DivPlatformLynx::getRegisterSheet() {
return regCheatSheetLynx;
}
const char* DivPlatformLynx::getEffectName(unsigned char effect) {
switch (effect)
{
case 0x30: case 0x31: case 0x32: case 0x33:
case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b:
case 0x3c: case 0x3d: case 0x3e: case 0x3f:
return "3xxx: Load LFSR (0 to FFF)";
break;
}
return NULL;
}
void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
for (int i=0; i<4; i++) {
@ -186,22 +173,9 @@ void DivPlatformLynx::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
if (chan[i].pcm) chan[i].sampleBaseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
chan[i].actualNote=chan[i].std.arp.val;
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
if (chan[i].pcm) chan[i].sampleBaseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
chan[i].actualNote=chan[i].note+chan[i].std.arp.val;
}
chan[i].freqChanged=true;
}
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
if (chan[i].pcm) chan[i].sampleBaseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].actualNote=chan[i].note;
chan[i].actualNote=parent->calcArp(chan[i].note,chan[i].std.arp.val);
chan[i].baseFreq=NOTE_PERIODIC(chan[i].actualNote);
if (chan[i].pcm) chan[i].sampleBaseFreq=NOTE_FREQUENCY(chan[i].actualNote);
chan[i].freqChanged=true;
}
}

View File

@ -106,7 +106,6 @@ class DivPlatformLynx: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName( unsigned char effect );
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformLynx();

View File

@ -43,15 +43,6 @@ const char** DivPlatformMMC5::getRegisterSheet() {
return regCheatSheetMMC5;
}
const char* DivPlatformMMC5::getEffectName(unsigned char effect) {
switch (effect) {
case 0x12:
return "12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)";
break;
}
return NULL;
}
void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
if (dacSample!=-1) {
@ -110,20 +101,12 @@ void DivPlatformMMC5::tick(bool sysTick) {
if (chan[i].outVol<0) chan[i].outVol=0;
rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
}
// TODO: arp macros on NES PCM?
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;

View File

@ -89,7 +89,6 @@ class DivPlatformMMC5: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformMMC5();

View File

@ -30,18 +30,6 @@ const char** DivPlatformMSM6258::getRegisterSheet() {
return NULL;
}
const char* DivPlatformMSM6258::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set frequency divider (0-2)";
break;
case 0x21:
return "21xx: Select clock rate (0: full; 1: half)";
break;
}
return NULL;
}
void DivPlatformMSM6258::acquire(short* bufL, short* bufR, size_t start, size_t len) {
short* outs[2]={
&msmOut,

View File

@ -109,7 +109,6 @@ class DivPlatformMSM6258: public DivDispatch {
void poke(std::vector<DivRegWrite>& wlist);
void setFlags(unsigned int flags);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);

View File

@ -30,15 +30,6 @@ const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL;
}
const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set chip output rate (0: clock/132; 1: clock/165)";
break;
}
return NULL;
}
u8 DivPlatformMSM6295::read_byte(u32 address) {
if (adpcmMem==NULL || address>=getSampleMemCapacity(0)) {
return 0;

View File

@ -97,7 +97,6 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
virtual void poke(std::vector<DivRegWrite>& wlist) override;
virtual void setFlags(unsigned int flags) override;
virtual const char** getRegisterSheet() override;
virtual const char* getEffectName(unsigned char effect) override;
virtual const void* getSampleMem(int index) override;
virtual size_t getSampleMemCapacity(int index) override;
virtual size_t getSampleMemUsage(int index) override;

View File

@ -108,51 +108,6 @@ const char** DivPlatformN163::getRegisterSheet() {
return regCheatSheetN163;
}
const char* DivPlatformN163::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Select waveform";
break;
case 0x11:
return "11xx: Set waveform position in RAM (single nibble unit)";
break;
case 0x12:
return "12xx: Set waveform length in RAM (04 to FC, 4 nibble unit)";
break;
case 0x13:
return "130x: Change waveform update mode (0: off, bit 0: update now, bit 1: update when every waveform changes)";
break;
case 0x14:
return "14xx: Select waveform for load to RAM";
break;
case 0x15:
return "15xx: Set waveform position for load to RAM (single nibble unit)";
break;
case 0x16:
return "16xx: Set waveform length for load to RAM (04 to FC, 4 nibble unit)";
break;
case 0x17:
return "170x: Change waveform load mode (0: off, bit 0: load now, bit 1: load when every waveform changes)";
break;
case 0x18:
return "180x: Change channel limits (0 to 7, x + 1)";
break;
case 0x20:
return "20xx: (Global) Select waveform for load to RAM";
break;
case 0x21:
return "21xx: (Global) Set waveform position for load to RAM (single nibble unit)";
break;
case 0x22:
return "22xx: (Global) Set waveform length for load to RAM (04 to FC, 4 nibble unit)";
break;
case 0x23:
return "230x: (Global) Change waveform load mode (0: off, bit 0: load now, bit 1: load when every waveform changes)";
break;
}
return NULL;
}
void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
n163.tick();
@ -234,18 +189,9 @@ void DivPlatformN163::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
if (chan[i].wavePos!=chan[i].std.duty.val) {

View File

@ -110,7 +110,6 @@ class DivPlatformN163: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformN163();

View File

@ -151,18 +151,6 @@ const char** DivPlatformNamcoWSG::getRegisterSheet() {
return regCheatSheetNamcoWSG;
}
const char* DivPlatformNamcoWSG::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
case 0x11:
return "11xx: Toggle noise mode";
break;
}
return NULL;
}
void DivPlatformNamcoWSG::acquire(short* bufL, short* bufR, size_t start, size_t len) {
while (!writes.empty()) {
QueuedWrite w=writes.front();
@ -218,18 +206,9 @@ void DivPlatformNamcoWSG::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {

View File

@ -96,7 +96,6 @@ class DivPlatformNamcoWSG: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformNamcoWSG();

View File

@ -62,27 +62,6 @@ const char** DivPlatformNES::getRegisterSheet() {
return regCheatSheetNES;
}
const char* DivPlatformNES::getEffectName(unsigned char effect) {
switch (effect) {
case 0x11:
return "11xx: Write to delta modulation counter (0 to 7F)";
break;
case 0x12:
return "12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)";
break;
case 0x13:
return "13xy: Sweep up (x: time; y: shift)";
break;
case 0x14:
return "14xy: Sweep down (x: time; y: shift)";
break;
case 0x18:
return "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)";
break;
}
return NULL;
}
void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
if (useNP) {
nes1_NP->Write(addr,data);
@ -240,28 +219,15 @@ void DivPlatformNES::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (i==3) { // noise
if (chan[i].std.arp.mode) {
chan[i].baseFreq=chan[i].std.arp.val;
} else {
chan[i].baseFreq=chan[i].note+chan[i].std.arp.val;
}
chan[i].baseFreq=parent->calcArp(chan[i].note,chan[i].std.arp.val);
if (chan[i].baseFreq>255) chan[i].baseFreq=255;
if (chan[i].baseFreq<0) chan[i].baseFreq=0;
} else {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;

View File

@ -106,7 +106,6 @@ class DivPlatformNES: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);

View File

@ -152,98 +152,6 @@ const int orderedOpsL[4]={
#define ADDR_FREQH 0xb0
#define ADDR_LR_FB_ALG 0xc0
const char* DivPlatformOPL::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set global AM depth (0: 1dB, 1: 4.8dB)";
break;
case 0x11:
return "11xx: Set feedback (0 to 7)";
break;
case 0x12:
return "12xx: Set level of operator 1 (0 highest, 3F lowest)";
break;
case 0x13:
return "13xx: Set level of operator 2 (0 highest, 3F lowest)";
break;
case 0x14:
return "14xx: Set level of operator 3 (0 highest, 3F lowest; 4-op only)";
break;
case 0x15:
return "15xx: Set level of operator 4 (0 highest, 3F lowest; 4-op only)";
break;
case 0x16:
return "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)";
break;
case 0x17:
return "17xx: Set global vibrato depth (0: normal, 1: double)";
break;
case 0x18:
if (properDrumsSys) {
return "18xx: Toggle drums mode (1: enabled; 0: disabled)";
}
break;
case 0x19:
return "19xx: Set attack of all operators (0 to F)";
break;
case 0x1a:
return "1Axx: Set attack of operator 1 (0 to F)";
break;
case 0x1b:
return "1Bxx: Set attack of operator 2 (0 to F)";
break;
case 0x1c:
return "1Cxx: Set attack of operator 3 (0 to F; 4-op only)";
break;
case 0x1d:
return "1Dxx: Set attack of operator 4 (0 to F; 4-op only)";
break;
case 0x2a:
return "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 3 in OPL2 and 0 to 7 in OPL3)";
break;
case 0x30:
return "30xx: Toggle hard envelope reset on new notes";
break;
case 0x50:
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
break;
case 0x51:
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
break;
case 0x52:
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
break;
case 0x53:
return "53xy: Set vibrato (x: operator from 1 to 4 (0 for all ops); y: enabled)";
break;
case 0x54:
return "54xy: Set key scale level (x: operator from 1 to 4 (0 for all ops); y: level from 0 to 3)";
break;
case 0x55:
return "55xy: Set envelope sustain (x: operator from 1 to 4 (0 for all ops); y: enabled)";
break;
case 0x56:
return "56xx: Set decay of all operators (0 to F)";
break;
case 0x57:
return "57xx: Set decay of operator 1 (0 to F)";
break;
case 0x58:
return "58xx: Set decay of operator 2 (0 to F)";
break;
case 0x59:
return "59xx: Set decay of operator 3 (0 to F; 4-op only)";
break;
case 0x5a:
return "5Axx: Set decay of operator 4 (0 to F; 4-op only)";
break;
case 0x5b:
return "5Bxy: Set whether key will scale envelope (x: operator from 1 to 4 (0 for all ops); y: enabled)";
break;
}
return NULL;
}
void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2];
static int os[2];
@ -389,18 +297,9 @@ void DivPlatformOPL::tick(bool sysTick) {
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (oplType==3 && chan[i].std.panL.had) {
@ -583,18 +482,9 @@ void DivPlatformOPL::tick(bool sysTick) {
if (chan[adpcmChan].std.arp.had) {
if (!chan[adpcmChan].inPorta) {
if (chan[adpcmChan].std.arp.mode) {
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].std.arp.val);
} else {
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note+(signed char)chan[adpcmChan].std.arp.val);
}
chan[adpcmChan].baseFreq=NOTE_ADPCMB(parent->calcArp(chan[adpcmChan].note,chan[adpcmChan].std.arp.val));
}
chan[adpcmChan].freqChanged=true;
} else {
if (chan[adpcmChan].std.arp.mode && chan[adpcmChan].std.arp.finished) {
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note);
chan[adpcmChan].freqChanged=true;
}
}
if (chan[adpcmChan].std.phaseReset.had) {
if ((chan[adpcmChan].std.phaseReset.val==1) && chan[adpcmChan].active) {
@ -891,6 +781,13 @@ int DivPlatformOPL::dispatch(DivCommand c) {
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
chan[c.chan].fourOp=(ops==4);
if (chan[c.chan].fourOp) {
/*
if (chan[c.chan+1].active) {
chan[c.chan+1].keyOff=true;
chan[c.chan+1].keyOn=false;
chan[c.chan+1].active=false;
}*/
chan[c.chan+1].insChanged=true;
chan[c.chan+1].macroInit(NULL);
}
update4OpMask=true;
@ -1691,7 +1588,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
adpcmChan=drums?11:9;
}
break;
case 3: case 759:
case 3: case 4: case 759:
slotsNonDrums=slotsOPL3;
slotsDrums=slotsOPL3Drums;
slots=drums?slotsDrums:slotsNonDrums;
@ -1705,6 +1602,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
pretendYMU=true;
adpcmChan=16;
} else if (type==4) {
chipFreqBase=32768*684;
downsample=true;
}
break;

View File

@ -147,7 +147,6 @@ class DivPlatformOPL: public DivDispatch {
int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);

View File

@ -27,68 +27,6 @@
#define CHIP_FREQBASE 1180068
const char* DivPlatformOPLL::getEffectName(unsigned char effect) {
switch (effect) {
case 0x11:
return "11xx: Set feedback (0 to 7)";
break;
case 0x12:
return "12xx: Set level of operator 1 (0 highest, 3F lowest)";
break;
case 0x13:
return "13xx: Set level of operator 2 (0 highest, F lowest)";
break;
case 0x16:
return "16xy: Set operator multiplier (x: operator from 1 to 2; y: multiplier)";
break;
case 0x18:
if (properDrumsSys) {
return "18xx: Toggle drums mode (1: enabled; 0: disabled)";
}
break;
case 0x19:
return "19xx: Set attack of all operators (0 to F)";
break;
case 0x1a:
return "1Axx: Set attack of operator 1 (0 to F)";
break;
case 0x1b:
return "1Bxx: Set attack of operator 2 (0 to F)";
break;
case 0x50:
return "50xy: Set AM (x: operator from 1 to 2 (0 for all ops); y: AM)";
break;
case 0x51:
return "51xy: Set sustain level (x: operator from 1 to 2 (0 for all ops); y: sustain)";
break;
case 0x52:
return "52xy: Set release (x: operator from 1 to 2 (0 for all ops); y: release)";
break;
case 0x53:
return "53xy: Set vibrato (x: operator from 1 to 2 (0 for all ops); y: enabled)";
break;
case 0x54:
return "54xy: Set key scale level (x: operator from 1 to 2 (0 for all ops); y: level from 0 to 3)";
break;
case 0x55:
return "55xy: Set envelope sustain (x: operator from 1 to 2 (0 for all ops); y: enabled)";
break;
case 0x56:
return "56xx: Set decay of all operators (0 to F)";
break;
case 0x57:
return "57xx: Set decay of operator 1 (0 to F)";
break;
case 0x58:
return "58xx: Set decay of operator 2 (0 to F)";
break;
case 0x5b:
return "5Bxy: Set whether key will scale envelope (x: operator from 1 to 2 (0 for all ops); y: enabled)";
break;
}
return NULL;
}
const unsigned char cycleMapOPLL[18]={
8, 7, 6, 7, 8, 7, 8, 6, 0, 1, 2, 7, 8, 9, 3, 4, 5, 9
};
@ -169,18 +107,9 @@ void DivPlatformOPLL::tick(bool sysTick) {
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.wave.had && chan[i].state.opllPreset!=16) {

View File

@ -122,7 +122,6 @@ class DivPlatformOPLL: public DivDispatch {
int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformOPLL();

View File

@ -53,27 +53,6 @@ const char** DivPlatformPCE::getRegisterSheet() {
return regCheatSheetPCE;
}
const char* DivPlatformPCE::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
case 0x11:
return "11xx: Toggle noise mode";
break;
case 0x12:
return "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)";
break;
case 0x13:
return "13xx: Set LFO speed";
break;
case 0x17:
return "17xx: Toggle PCM mode";
break;
}
return NULL;
}
void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
// PCM part
@ -190,28 +169,12 @@ void DivPlatformPCE::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
// noise
int noiseSeek=chan[i].std.arp.val;
if (noiseSeek<0) noiseSeek=0;
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
int noiseSeek=chan[i].note+chan[i].std.arp.val;
if (noiseSeek<0) noiseSeek=0;
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
}
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
int noiseSeek=chan[i].note;
int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val);
chan[i].baseFreq=NOTE_PERIODIC(noiseSeek);
if (noiseSeek<0) noiseSeek=0;
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
chan[i].freqChanged=true;
}
chan[i].freqChanged=true;
}
if (chan[i].std.wave.had && !chan[i].pcm) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {

View File

@ -112,7 +112,6 @@ class DivPlatformPCE: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformPCE();

View File

@ -128,18 +128,9 @@ void DivPlatformPCMDAC::tick(bool sysTick) {
}
if (chan.std.arp.had) {
if (!chan.inPorta) {
if (chan.std.arp.mode) {
chan.baseFreq=NOTE_FREQUENCY(chan.std.arp.val);
} else {
chan.baseFreq=NOTE_FREQUENCY(chan.note+chan.std.arp.val);
}
chan.baseFreq=NOTE_FREQUENCY(parent->calcArp(chan.note,chan.std.arp.val));
}
chan.freqChanged=true;
} else {
if (chan.std.arp.mode && chan.std.arp.finished) {
chan.baseFreq=NOTE_FREQUENCY(chan.note);
chan.freqChanged=true;
}
}
if (chan.useWave && chan.std.wave.had) {
if (chan.wave!=chan.std.wave.val || chan.ws.activeChanged()) {

View File

@ -190,10 +190,6 @@ const char** DivPlatformPCSpeaker::getRegisterSheet() {
return regCheatSheetPCSpeaker;
}
const char* DivPlatformPCSpeaker::getEffectName(unsigned char effect) {
return NULL;
}
const float cut=0.05;
const float reso=0.06;
@ -351,18 +347,9 @@ void DivPlatformPCSpeaker::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {

View File

@ -113,7 +113,6 @@ class DivPlatformPCSpeaker: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformPCSpeaker();

View File

@ -37,15 +37,6 @@ const char** DivPlatformPET::getRegisterSheet() {
return regCheatSheet6522;
}
const char* DivPlatformPET::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
}
return NULL;
}
// high-level emulation of 6522 shift register and driver software for now
void DivPlatformPET::rWrite(unsigned int addr, unsigned char val) {
bool hwSROutput=((regPool[11]>>2)&7)==4;
@ -113,18 +104,9 @@ void DivPlatformPET::tick(bool sysTick) {
}
if (chan.std.arp.had) {
if (!chan.inPorta) {
if (chan.std.arp.mode) {
chan.baseFreq=NOTE_PERIODIC(chan.std.arp.val);
} else {
chan.baseFreq=NOTE_PERIODIC(chan.note+chan.std.arp.val);
}
chan.baseFreq=NOTE_PERIODIC(parent->calcArp(chan.note,chan.std.arp.val));
}
chan.freqChanged=true;
} else {
if (chan.std.arp.mode && chan.std.arp.finished) {
chan.baseFreq=NOTE_PERIODIC(chan.note);
chan.freqChanged=true;
}
}
if (chan.std.wave.had) {
if (chan.wave!=chan.std.wave.val) {
@ -133,8 +115,14 @@ void DivPlatformPET::tick(bool sysTick) {
}
}
if (chan.std.pitch.had) {
chan.freqChanged=true;
if (chan.std.pitch.mode) {
chan.pitch2+=chan.std.pitch.val;
CLAMP_VAR(chan.pitch2,-32768,32767);
} else {
chan.pitch2=chan.std.pitch.val;
}
chan.freqChanged=true;
}
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER)-2;
if (chan.freq>65535) chan.freq=65535;

View File

@ -80,7 +80,6 @@ class DivPlatformPET: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformPET();

View File

@ -249,24 +249,6 @@ const char** DivPlatformQSound::getRegisterSheet() {
return regCheatSheetQSound;
}
const char* DivPlatformQSound::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set echo feedback level (00 to FF)";
break;
case 0x11:
return "11xx: Set channel echo level (00 to FF)";
break;
case 0x12:
return "12xx: Toggle QSound algorithm (0: disabled; 1: enabled)";
break;
default:
if ((effect & 0xf0) == 0x30) {
return "3xxx: Set echo delay buffer length (000 to AA5)";
}
}
return NULL;
}
void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
qsound_update(&chip);
@ -322,18 +304,9 @@ void DivPlatformQSound::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=QS_NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
chan[i].echo=CLAMP(chan[i].std.duty.val,0,32767);

View File

@ -96,7 +96,6 @@ class DivPlatformQSound: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index = 0);
size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0);

View File

@ -43,10 +43,6 @@ const char** DivPlatformRF5C68::getRegisterSheet() {
return regCheatSheetRF5C68;
}
const char* DivPlatformRF5C68::getEffectName(unsigned char effect) {
return NULL;
}
void DivPlatformRF5C68::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
if (!skipRegisterWrites) {
if (curChan!=ch) {
@ -88,18 +84,9 @@ void DivPlatformRF5C68::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {

View File

@ -94,7 +94,6 @@ class DivPlatformRF5C68: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index = 0);
size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0);

View File

@ -56,21 +56,6 @@ const char** DivPlatformSAA1099::getRegisterSheet() {
return regCheatSheetSAA;
}
const char* DivPlatformSAA1099::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xy: Set channel mode (x: noise; y: tone)";
break;
case 0x11:
return "11xx: Set noise frequency";
break;
case 0x12:
return "12xx: Setup envelope (refer to docs for more information)";
break;
}
return NULL;
}
void DivPlatformSAA1099::acquire_saaSound(short* bufL, short* bufR, size_t start, size_t len) {
if (saaBufLen<len*2) {
saaBufLen=len*2;
@ -114,18 +99,9 @@ void DivPlatformSAA1099::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
saaNoise[i/3]=chan[i].std.duty.val&3;

View File

@ -96,7 +96,6 @@ class DivPlatformSAA1099: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
};

View File

@ -80,15 +80,6 @@ const char** DivPlatformSCC::getRegisterSheet() {
return isPlus ? regCheatSheetSCCPlus : regCheatSheetSCC;
}
const char* DivPlatformSCC::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
}
return NULL;
}
void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
for (int i=0; i<16; i++) {
@ -124,18 +115,9 @@ void DivPlatformSCC::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {

View File

@ -84,7 +84,6 @@ class DivPlatformSCC: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
void setFlags(unsigned int flags);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void setChipModel(bool isPlus);

View File

@ -26,15 +26,6 @@
//#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
//#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
const char* DivPlatformSegaPCM::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set PCM frequency";
break;
}
return NULL;
}
void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int os[2];
@ -97,18 +88,9 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=(chan[i].std.arp.val<<6);
} else {
chan[i].baseFreq=((chan[i].note+(signed char)chan[i].std.arp.val)<<6);
}
chan[i].baseFreq=(parent->calcArp(chan[i].note,chan[i].std.arp.val)<<6);
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=(chan[i].note<<6);
chan[i].freqChanged=true;
}
}
if (chan[i].std.panL.had) {

View File

@ -114,7 +114,6 @@ class DivPlatformSegaPCM: public DivDispatch {
bool isStereo();
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformSegaPCM();

View File

@ -38,15 +38,6 @@ const char** DivPlatformSMS::getRegisterSheet() {
return stereo?regCheatSheetGG:regCheatSheetSN;
}
const char* DivPlatformSMS::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xy: Set noise mode (x: preset freq/ch3 freq; y: thin pulse/noise)";
break;
}
return NULL;
}
void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
int oL=0;
int oR=0;
@ -141,22 +132,12 @@ void DivPlatformSMS::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
chan[i].actualNote=chan[i].std.arp.val;
} else {
// TODO: check whether this weird octave boundary thing applies to other systems as well
int areYouSerious=chan[i].note+chan[i].std.arp.val;
while (areYouSerious>0x60) areYouSerious-=12;
chan[i].baseFreq=NOTE_PERIODIC(areYouSerious);
chan[i].actualNote=areYouSerious;
}
chan[i].freqChanged=true;
}
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].actualNote=chan[i].note;
// TODO: check whether this weird octave boundary thing applies to other systems as well
// TODO: add compatibility flag. this is horrible.
int areYouSerious=parent->calcArp(chan[i].note,chan[i].std.arp.val);
while (areYouSerious>0x60) areYouSerious-=12;
chan[i].baseFreq=NOTE_PERIODIC(areYouSerious);
chan[i].actualNote=areYouSerious;
chan[i].freqChanged=true;
}
}
@ -197,7 +178,11 @@ void DivPlatformSMS::tick(bool sysTick) {
if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,toneDivider);
if (chan[i].freq>1023) chan[i].freq=1023;
if (chan[i].freq<8) chan[i].freq=1;
if (parent->song.snNoLowPeriods) {
if (chan[i].freq<8) chan[i].freq=1;
} else {
if (chan[i].freq<0) chan[i].freq=0;
}
//if (chan[i].actualNote>0x5d) chan[i].freq=0x01;
rWrite(0,0x80|i<<5|(chan[i].freq&15));
rWrite(0,chan[i].freq>>4);
@ -212,7 +197,9 @@ void DivPlatformSMS::tick(bool sysTick) {
if (chan[3].freqChanged || updateSNMode) {
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,noiseDivider);
if (chan[3].freq>1023) chan[3].freq=1023;
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
if (parent->song.snNoLowPeriods) {
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
}
if (snNoiseMode&2) { // take period from channel 3
if (updateSNMode || resetPhase) {
if (snNoiseMode&1) {
@ -232,12 +219,8 @@ void DivPlatformSMS::tick(bool sysTick) {
} else { // 3 fixed values
unsigned char value;
if (chan[3].std.arp.had) {
if (chan[3].std.arp.mode) {
value=chan[3].std.arp.val%12;
} else {
value=(chan[3].note+chan[3].std.arp.val)%12;
}
} else {
value=parent->calcArp(chan[3].note,chan[3].std.arp.val)%12;
} else { // pardon?
value=chan[3].note%12;
}
if (value<3) {

View File

@ -101,7 +101,6 @@ class DivPlatformSMS: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
void setNuked(bool value);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();

View File

@ -300,12 +300,46 @@ void SoundUnit::NextSample(short* l, short* r) {
}
}
*l=minval(32767,maxval(-32767,tnsL));
*r=minval(32767,maxval(-32767,tnsR));
if (dsOut) {
tnsL=minval(32767,maxval(-32767,tnsL<<1));
tnsR=minval(32767,maxval(-32767,tnsR<<1));
short accumL=0;
short accumR=0;
for (int i=0; i<4; i++) {
if ((tnsL>>8)==0 && dsCounterL>0) dsCounterL=0;
dsCounterL+=tnsL>>8;
if (dsCounterL>=0) {
accumL+=4095;
dsCounterL-=127;
} else {
accumL+=-4095;
dsCounterL+=127;
}
if ((tnsR>>8)==0 && dsCounterR>0) dsCounterR=0;
dsCounterR+=tnsR>>8;
if (dsCounterR>=0) {
accumR+=4095;
dsCounterR-=127;
} else {
accumR+=-4095;
dsCounterR+=127;
}
}
*l=accumL;
*r=accumR;
} else {
*l=minval(32767,maxval(-32767,tnsL));
*r=minval(32767,maxval(-32767,tnsR));
}
}
void SoundUnit::Init(int sampleMemSize) {
void SoundUnit::Init(int sampleMemSize, bool dsOutMode) {
pcmSize=sampleMemSize;
dsOut=dsOutMode;
Reset();
memset(pcm,0,pcmSize);
for (int i=0; i<256; i++) {
@ -346,6 +380,8 @@ void SoundUnit::Reset() {
oldflags[i]=0;
pcmdec[i]=0;
}
dsCounterL=0;
dsCounterR=0;
tnsL=0;
tnsR=0;
ilBufPos=0;

View File

@ -27,6 +27,8 @@ class SoundUnit {
unsigned short oldfreq[8];
unsigned short oldflags[8];
unsigned int pcmSize;
bool dsOut;
short dsCounterL, dsCounterR;
public:
unsigned short resetfreq[8];
unsigned short voldcycles[8];
@ -99,7 +101,7 @@ class SoundUnit {
if (ret>32767) ret=32767;
return ret;
}
void Init(int sampleMemSize=8192);
void Init(int sampleMemSize=8192, bool dsOutMode=false);
void Reset();
SoundUnit();
};

View File

@ -26,76 +26,18 @@
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) rWrite(((c)<<5)|(a),v);
#define CHIP_DIVIDER 2
#define CHIP_FREQBASE 524288
const char** DivPlatformSoundUnit::getRegisterSheet() {
return NULL;
}
const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set waveform (0 to 7)";
break;
case 0x12:
return "12xx: Set pulse width (0 to 7F)";
break;
case 0x13:
return "13xx: Set resonance (0 to F)";
break;
case 0x14:
return "14xx: Set filter mode (bit 0: ring mod; bit 1: low pass; bit 2: high pass; bit 3: band pass)";
break;
case 0x15:
return "15xx: Set frequency sweep period low byte";
break;
case 0x16:
return "16xx: Set frequency sweep period high byte";
break;
case 0x17:
return "17xx: Set volume sweep period low byte";
break;
case 0x18:
return "18xx: Set volume sweep period high byte";
break;
case 0x19:
return "19xx: Set cutoff sweep period low byte";
break;
case 0x1a:
return "1Axx: Set cutoff sweep period high byte";
break;
case 0x1b:
return "1Bxx: Set frequency sweep boundary";
break;
case 0x1c:
return "1Cxx: Set volume sweep boundary";
break;
case 0x1d:
return "1Dxx: Set cutoff sweep boundary";
break;
case 0x1e:
return "1Exx: Set phase reset period low byte";
break;
case 0x1f:
return "1Fxx: Set phase reset period high byte";
break;
case 0x20:
return "20xx: Toggle frequency sweep (bit 0-6: speed; bit 7: direction is up)";
break;
case 0x21:
return "21xx: Toggle volume sweep (bit 0-4: speed; bit 5: direciton is up; bit 6: loop; bit 7: alternate)";
break;
case 0x22:
return "22xx: Toggle cutoff sweep (bit 0-6: speed; bit 7: direction is up)";
break;
case 0x40: case 0x41: case 0x42: case 0x43:
case 0x44: case 0x45: case 0x46: case 0x47:
case 0x48: case 0x49: case 0x4a: case 0x4b:
case 0x4c: case 0x4d: case 0x4e: case 0x4f:
return "4xxx: Set cutoff (0 to FFF)";
break;
double DivPlatformSoundUnit::NOTE_SU(int ch, int note) {
if (chan[ch].switchRoles) {
return NOTE_PERIODIC(note);
}
return NULL;
return NOTE_FREQUENCY(note);
}
void DivPlatformSoundUnit::acquire(short* bufL, short* bufR, size_t start, size_t len) {
@ -136,18 +78,9 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_SU(i,parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;
@ -190,13 +123,18 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
if (chan[i].std.ex4.had) {
chan[i].syncTimer=chan[i].std.ex4.val&65535;
chan[i].timerSync=(chan[i].syncTimer>0);
chWrite(i,0x1e,chan[i].syncTimer&0xff);
chWrite(i,0x1f,chan[i].syncTimer>>8);
if (chan[i].switchRoles) {
chWrite(i,0x00,chan[i].syncTimer&0xff);
chWrite(i,0x01,chan[i].syncTimer>>8);
} else {
chWrite(i,0x1e,chan[i].syncTimer&0xff);
chWrite(i,0x1f,chan[i].syncTimer>>8);
}
writeControlUpper(i);
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE);
if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
// TODO: sample map?
@ -213,8 +151,13 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
}
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>65535) chan[i].freq=65535;
chWrite(i,0x00,chan[i].freq&0xff);
chWrite(i,0x01,chan[i].freq>>8);
if (chan[i].switchRoles) {
chWrite(i,0x1e,chan[i].freq&0xff);
chWrite(i,0x1f,chan[i].freq>>8);
} else {
chWrite(i,0x00,chan[i].freq&0xff);
chWrite(i,0x01,chan[i].freq>>8);
}
if (chan[i].keyOn) {
if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
@ -256,6 +199,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SU);
chan[c.chan].switchRoles=ins->su.switchRoles;
if (chan[c.chan].pcm && !(ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) {
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
writeControl(c.chan);
@ -263,7 +207,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
}
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value);
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
@ -413,7 +357,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
writeControlUpper(c.chan);
break;
case DIV_CMD_C64_FINE_CUTOFF:
chan[c.chan].baseCutoff=c.value;
chan[c.chan].baseCutoff=c.value*4;
if (!chan[c.chan].std.ex1.has) {
chan[c.chan].cutoff=chan[c.chan].baseCutoff;
chWrite(c.chan,0x06,chan[c.chan].cutoff&0xff);
@ -421,7 +365,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
}
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_FREQUENCY(c.value2);
int destFreq=NOTE_SU(c.chan,c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:(1+(chan[c.chan].baseFreq>>9)));
@ -453,7 +397,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
break;
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
@ -461,7 +405,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SU));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_SU(c.chan,chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
@ -485,6 +429,11 @@ void DivPlatformSoundUnit::forceIns() {
for (int i=0; i<8; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
// restore channel attributes
chWrite(i,0x03,chan[i].pan);
writeControl(i);
writeControlUpper(i);
}
}
@ -572,7 +521,7 @@ void DivPlatformSoundUnit::setFlags(unsigned int flags) {
sampleMemSize=flags&16;
su->Init(sampleMemSize?65536:8192);
su->Init(sampleMemSize?65536:8192,flags&32);
renderSamples();
}

View File

@ -31,7 +31,7 @@ class DivPlatformSoundUnit: public DivDispatch {
int ins, cutoff, baseCutoff, res, control, hasOffset;
signed char pan;
unsigned char duty;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, phaseReset, filterPhaseReset;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, phaseReset, filterPhaseReset, switchRoles;
bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep;
unsigned short freqSweepP, volSweepP, cutSweepP;
unsigned char freqSweepB, volSweepB, cutSweepB;
@ -67,6 +67,7 @@ class DivPlatformSoundUnit: public DivDispatch {
pcm(false),
phaseReset(false),
filterPhaseReset(false),
switchRoles(false),
pcmLoop(false),
timerSync(false),
freqSweep(false),
@ -108,6 +109,7 @@ class DivPlatformSoundUnit: public DivDispatch {
SoundUnit* su;
size_t sampleMemLen;
unsigned char regPool[128];
double NOTE_SU(int ch, int note);
void writeControl(int ch);
void writeControlUpper(int ch);
@ -131,7 +133,6 @@ class DivPlatformSoundUnit: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index);

View File

@ -50,27 +50,6 @@ const char** DivPlatformSwan::getRegisterSheet() {
return regCheatSheetWS;
}
const char* DivPlatformSwan::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
case 0x11:
return "11xx: Setup noise mode (0: disabled; 1-8: enabled/tap)";
break;
case 0x12:
return "12xx: Setup sweep period (0: disabled; 1-20: enabled/period)";
break;
case 0x13:
return "13xx: Set sweep amount";
break;
case 0x17:
return "17xx: Toggle PCM mode";
break;
}
return NULL;
}
void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
// PCM part
@ -155,18 +134,9 @@ void DivPlatformSwan::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.wave.had && !(i==1 && pcm)) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {

View File

@ -93,7 +93,6 @@ class DivPlatformSwan: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformSwan();

View File

@ -34,15 +34,6 @@ const char* regCheatSheetTIA[]={
NULL
};
const char* DivPlatformTIA::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Select shape (0 to F)";
break;
}
return NULL;
}
const char** DivPlatformTIA::getRegisterSheet() {
return regCheatSheetTIA;
}
@ -96,20 +87,18 @@ void DivPlatformTIA::tick(bool sysTick) {
rWrite(0x19+i,chan[i].outVol&15);
}
}
// TODO: the way arps work on TIA is really weird
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=0x80000000|chan[i].std.arp.val;
if (chan[i].std.arp.val<0 && (!(chan[i].std.arp.val&0x40000000))) {
chan[i].baseFreq=0x80000000|(chan[i].std.arp.val|0x40000000);
} else if (chan[i].std.arp.val>=0 && chan[i].std.arp.val&0x40000000) {
chan[i].baseFreq=0x80000000|(chan[i].std.arp.val&(~0x40000000));
} else {
chan[i].baseFreq=(chan[i].note+chan[i].std.arp.val)<<8;
}
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=chan[i].note<<8;
chan[i].freqChanged=true;
}
}
if (chan[i].std.wave.had) {
chan[i].shape=chan[i].std.wave.val&15;
@ -126,13 +115,6 @@ void DivPlatformTIA::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (chan[i].insChanged) {
if (!chan[i].std.wave.will) {
chan[i].shape=4;
rWrite(0x15+i,chan[i].shape);
}
chan[i].insChanged=false;
}
chan[i].freq=dealWithFreq(chan[i].shape,chan[i].baseFreq,chan[i].pitch)+chan[i].pitch2;
if ((chan[i].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) {
if (chan[i].baseFreq<39*256) {
@ -173,6 +155,13 @@ int DivPlatformTIA::dispatch(DivCommand c) {
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].insChanged) {
if (!chan[c.chan].std.wave.will) {
chan[c.chan].shape=4;
rWrite(0x15+c.chan,chan[c.chan].shape);
}
chan[c.chan].insChanged=false;
}
if (isMuted[c.chan]) {
rWrite(0x19+c.chan,0);
} else {

View File

@ -67,7 +67,6 @@ class DivPlatformTIA: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
};

View File

@ -55,139 +55,6 @@ const char** DivPlatformTX81Z::getRegisterSheet() {
return regCheatSheetOPZ;
}
const char* DivPlatformTX81Z::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Set noise frequency (xx: value; 0 disables noise)";
break;
case 0x11:
return "11xx: Set feedback (0 to 7)";
break;
case 0x12:
return "12xx: Set level of operator 1 (0 highest, 7F lowest)";
break;
case 0x13:
return "13xx: Set level of operator 2 (0 highest, 7F lowest)";
break;
case 0x14:
return "14xx: Set level of operator 3 (0 highest, 7F lowest)";
break;
case 0x15:
return "15xx: Set level of operator 4 (0 highest, 7F lowest)";
break;
case 0x16:
return "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)";
break;
case 0x17:
return "17xx: Set LFO speed";
break;
case 0x18:
return "18xx: Set LFO waveform (0 saw, 1 square, 2 triangle, 3 noise)";
break;
case 0x19:
return "19xx: Set attack of all operators (0 to 1F)";
break;
case 0x1a:
return "1Axx: Set attack of operator 1 (0 to 1F)";
break;
case 0x1b:
return "1Bxx: Set attack of operator 2 (0 to 1F)";
break;
case 0x1c:
return "1Cxx: Set attack of operator 3 (0 to 1F)";
break;
case 0x1d:
return "1Dxx: Set attack of operator 4 (0 to 1F)";
break;
case 0x1e:
return "1Exx: Set AM depth (0 to 7F)";
break;
case 0x1f:
return "1Fxx: Set PM depth (0 to 7F)";
break;
case 0x28:
return "28xy: Set reverb (x: operator from 1 to 4 (0 for all ops); y: reverb from 0 to 7)";
break;
case 0x2a:
return "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 7)";
break;
case 0x2b:
return "2Bxy: Set envelope generator shift (x: operator from 1 to 4 (0 for all ops); y: shift from 0 to 3)";
break;
case 0x2c:
return "2Cxy: Set fine multiplier (x: operator from 1 to 4 (0 for all ops); y: fine)";
break;
case 0x2f:
return "2Fxx: Toggle hard envelope reset on new notes";
break;
case 0x30: case 0x31: case 0x32: case 0x33:
case 0x34: case 0x35: case 0x36: case 0x37:
return "3xyy: Set fixed frequency of operator 1 (x: octave from 0 to 7; y: frequency)";
break;
case 0x38: case 0x39: case 0x3a: case 0x3b:
case 0x3c: case 0x3d: case 0x3e: case 0x3f:
return "3xyy: Set fixed frequency of operator 2 (x: octave from 8 to F; y: frequency)";
break;
case 0x40: case 0x41: case 0x42: case 0x43:
case 0x44: case 0x45: case 0x46: case 0x47:
return "4xyy: Set fixed frequency of operator 3 (x: octave from 0 to 7; y: frequency)";
break;
case 0x48: case 0x49: case 0x4a: case 0x4b:
case 0x4c: case 0x4d: case 0x4e: case 0x4f:
return "4xyy: Set fixed frequency of operator 4 (x: octave from 8 to F; y: frequency)";
break;
case 0x50:
return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)";
break;
case 0x51:
return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)";
break;
case 0x52:
return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)";
break;
case 0x53:
return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)";
break;
case 0x54:
return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)";
break;
case 0x55:
return "55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)";
break;
case 0x56:
return "56xx: Set decay of all operators (0 to 1F)";
break;
case 0x57:
return "57xx: Set decay of operator 1 (0 to 1F)";
break;
case 0x58:
return "58xx: Set decay of operator 2 (0 to 1F)";
break;
case 0x59:
return "59xx: Set decay of operator 3 (0 to 1F)";
break;
case 0x5a:
return "5Axx: Set decay of operator 4 (0 to 1F)";
break;
case 0x5b:
return "5Bxx: Set decay 2 of all operators (0 to 1F)";
break;
case 0x5c:
return "5Cxx: Set decay 2 of operator 1 (0 to 1F)";
break;
case 0x5d:
return "5Dxx: Set decay 2 of operator 2 (0 to 1F)";
break;
case 0x5e:
return "5Exx: Set decay 2 of operator 3 (0 to 1F)";
break;
case 0x5f:
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
break;
}
return NULL;
}
void DivPlatformTX81Z::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int os[2];
@ -256,18 +123,9 @@ void DivPlatformTX81Z::tick(bool sysTick) {
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_LINEAR(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_LINEAR(chan[i].note+(signed char)chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_LINEAR(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_LINEAR(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {

View File

@ -108,7 +108,6 @@ class DivPlatformTX81Z: public DivPlatformOPM {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformTX81Z();

View File

@ -51,18 +51,6 @@ const char** DivPlatformVERA::getRegisterSheet() {
return regCheatSheetVERA;
}
const char* DivPlatformVERA::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Change waveform";
break;
case 0x22:
return "22xx: Set duty cycle (0 to 3F)";
break;
}
return NULL;
}
void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len) {
// both PSG part and PCM part output a full 16-bit range, putting bufL/R
// argument right into both could cause an overflow
@ -171,18 +159,9 @@ void DivPlatformVERA::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=calcNoteFreq(0,chan[i].std.arp.val);
} else {
chan[i].baseFreq=calcNoteFreq(0,chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=calcNoteFreq(0,parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=calcNoteFreq(0,chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
rWriteLo(i,3,chan[i].std.duty.val);
@ -221,18 +200,9 @@ void DivPlatformVERA::tick(bool sysTick) {
}
if (chan[16].std.arp.had) {
if (!chan[16].inPorta) {
if (chan[16].std.arp.mode) {
chan[16].baseFreq=calcNoteFreq(16,chan[16].std.arp.val);
} else {
chan[16].baseFreq=calcNoteFreq(16,chan[16].note+chan[16].std.arp.val);
}
chan[16].baseFreq=calcNoteFreq(16,parent->calcArp(chan[16].note,chan[16].std.arp.val));
}
chan[16].freqChanged=true;
} else {
if (chan[16].std.arp.mode && chan[16].std.arp.finished) {
chan[16].baseFreq=calcNoteFreq(16,chan[16].note);
chan[16].freqChanged=true;
}
}
if (chan[16].freqChanged) {
double off=65536.0;

View File

@ -79,7 +79,6 @@ class DivPlatformVERA: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformVERA();

View File

@ -39,15 +39,6 @@ const char** DivPlatformVIC20::getRegisterSheet() {
return regCheatSheetVIC;
}
const char* DivPlatformVIC20::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform";
break;
}
return NULL;
}
void DivPlatformVIC20::acquire(short* bufL, short* bufR, size_t start, size_t len) {
const unsigned char loadFreq[3] = {0x7e, 0x7d, 0x7b};
const unsigned char wavePatterns[16] = {
@ -103,18 +94,9 @@ void DivPlatformVIC20::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val) {

View File

@ -82,7 +82,6 @@ class DivPlatformVIC20: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
~DivPlatformVIC20();

View File

@ -46,18 +46,6 @@ const char** DivPlatformVRC6::getRegisterSheet() {
return regCheatSheetVRC6;
}
const char* DivPlatformVRC6::getEffectName(unsigned char effect) {
switch (effect) {
case 0x12:
return "12xx: Set duty cycle (pulse: 0 to 7)";
break;
case 0x17:
return "17xx: Toggle PCM mode (pulse channel)";
break;
}
return NULL;
}
void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) {
// PCM part
@ -165,18 +153,9 @@ void DivPlatformVRC6::tick(bool sysTick) {
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
} else {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
}
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
} else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
chan[i].freqChanged=true;
}
}
if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val;

View File

@ -99,7 +99,6 @@ class DivPlatformVRC6: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit();
DivPlatformVRC6() : vrc6(intf) {};

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