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

View File

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

View File

@ -33,6 +33,7 @@ the coding style is described here:
- indent switch cases - indent switch cases
- preprocessor directives not intended - preprocessor directives not intended
- if macro comprises more than one line, indent - if macro comprises more than one line, indent
- no new line after `template<>`
- prefer built-in types: - prefer built-in types:
- `bool` - `bool`
- `signed char` or `unsigned char` are 8-bit - `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 Nintendo song covers.
- avoid big label song covers. - avoid big label song covers.
- avoid poor quality songs.
# Finishing # Finishing

View File

@ -1,5 +1,5 @@
# to-do for 0.6pre1.5-0.6pre2 # to-do for 0.6pre1.5-0.6pre2
- volume commands should work on Game Boy - volume commands should work on Game Boy
- ability to customize `OFF`, `===` and `REL`
- stereo separation control for AY - 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. 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] - Determines the initial waveform for playing.
- [Initial Waveform position in RAM] - Determines the initial waveform position will be load to RAM. - [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. - [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. 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: 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.
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 # 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: 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) - 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) - what frequencies to filter, along with filter level/sweep and resonance options (much like the C64)
- and many more. - 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. 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 # 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? 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 # effects
- `12xx`: set pulse width - `12xx`: set pulse width.
- `17xx`: trigger overlay drums. - `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: 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 - 106: Furnace dev106
- 105: Furnace dev105 - 105: Furnace dev105
- 104: Furnace dev104 - 104: Furnace dev104
@ -330,7 +336,9 @@ size | description
1 | broken outVol (>=99) or reserved 1 | broken outVol (>=99) or reserved
1 | E1xy and E2xy stop on same note (>=100) or reserved 1 | E1xy and E2xy stop on same note (>=100) or reserved
1 | broken initial position of porta after arp (>=101) 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** --- | **virtual tempo data**
2 | virtual tempo numerator of first song (>=96) or reserved 2 | virtual tempo numerator of first song (>=96) or reserved
2 | virtual tempo denominator of first song (>=96) or reserved 2 | virtual tempo denominator of first song (>=96) or reserved
@ -447,6 +455,7 @@ size | description
| - 29: SNES | - 29: SNES
| - 30: Sound Unit | - 30: Sound Unit
| - 31: Namco WSG | - 31: Namco WSG
| - 32: OPL (drums)
1 | reserved 1 | reserved
STR | instrument name STR | instrument name
--- | **FM instrument data** --- | **FM instrument data**
@ -538,7 +547,18 @@ size | description
4 | extra 1 macro loop (>=17) 4 | extra 1 macro loop (>=17)
4 | extra 2 macro loop (>=17) 4 | extra 2 macro loop (>=17)
4 | extra 3 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 volume macro height (>=15) or reserved
1 | reserved (>=17) or duty 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 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. | - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18.
4?? | arp macro 4?? | arp macro
| - before version 31, this macro's values were stored offset by 12. | - 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 4?? | duty macro
| - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12. | - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12.
4?? | wave macro 4?? | wave macro
@ -850,6 +871,111 @@ size | description
--- | **Game Boy extra flags** (>=106) --- | **Game Boy extra flags** (>=106)
1 | use software envelope 1 | use software envelope
1 | always init hard env on new note 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 # wavetable
@ -860,9 +986,9 @@ size | description
4 | "WAVE" block ID 4 | "WAVE" block ID
4 | size of this block 4 | size of this block
STR | wavetable name STR | wavetable name
4 | wavetable size 4 | wavetable width
4 | wavetable min 4 | reserved
4 | wavetable max 4 | wavetable height
4?? | wavetable data 4?? | wavetable data
``` ```

View File

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

View File

@ -420,13 +420,6 @@ class DivDispatch {
*/ */
virtual bool getWantPreNote(); 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. * set the chip flags.
* @param flags the flags. see song.h for possible values. * @param flags the flags. see song.h for possible values.

View File

@ -18,6 +18,7 @@
*/ */
#include "dispatch.h" #include "dispatch.h"
#include "song.h"
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES
#include "engine.h" #include "engine.h"
#include "instrument.h" #include "instrument.h"
@ -124,8 +125,15 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
if ((effect&0xf0)==0x90) { if ((effect&0xf0)==0x90) {
return "9xxx: Set sample offset*256"; return "9xxx: Set sample offset*256";
} else if (chan>=0 && chan<chans) { } else if (chan>=0 && chan<chans) {
const char* ret=disCont[dispatchOfChan[chan]].dispatch->getEffectName(effect); DivSysDef* sysDef=sysDefs[sysOfChan[chan]];
if (ret!=NULL) return ret; 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; break;
} }
@ -1380,6 +1388,115 @@ bool DivEngine::removeSystem(int index, bool preserveOrder) {
return true; 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) { void DivEngine::poke(int sys, unsigned int addr, unsigned short val) {
if (sys<0 || sys>=song.systemLen) return; if (sys<0 || sys>=song.systemLen) return;
BUSY_BEGIN; BUSY_BEGIN;
@ -1655,6 +1772,15 @@ int DivEngine::calcFreq(int base, int pitch, bool period, int octave, int pitch2
base+((pitch*octave)>>1)+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 DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int range) {
int panL=val>>bits; int panL=val>>bits;
int panR=val&((1<<bits)-1); int panR=val&((1<<bits)-1);
@ -2192,11 +2318,13 @@ void DivEngine::delInstrument(int index) {
song.ins.erase(song.ins.begin()+index); song.ins.erase(song.ins.begin()+index);
song.insLen=song.ins.size(); song.insLen=song.ins.size();
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (int j=0; j<256; j++) { for (size_t j=0; j<song.subsong.size(); j++) {
if (curPat[i].data[j]==NULL) continue; for (int k=0; k<256; k++) {
for (int k=0; k<curSubSong->patLen; k++) { if (song.subsong[j]->pat[i].data[k]==NULL) continue;
if (curPat[i].data[j]->data[k][2]>index) { for (int l=0; l<song.subsong[j]->patLen; l++) {
curPat[i].data[j]->data[k][2]--; 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() { int DivEngine::addWave() {
if (song.wave.size()>=256) return -1; if (song.wave.size()>=256) {
lastError="too many wavetables!";
return -1;
}
BUSY_BEGIN; BUSY_BEGIN;
saveLock.lock(); saveLock.lock();
DivWavetable* wave=new DivWavetable; DivWavetable* wave=new DivWavetable;
@ -2219,50 +2350,62 @@ int DivEngine::addWave() {
return waveCount; return waveCount;
} }
bool DivEngine::addWaveFromFile(const char* path, bool addRaw) { int DivEngine::addWavePtr(DivWavetable* which) {
if (song.wave.size()>=256) { if (song.wave.size()>=256) {
lastError="too many wavetables!"; 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"); FILE* f=ps_fopen(path,"rb");
if (f==NULL) { if (f==NULL) {
lastError=fmt::sprintf("%s",strerror(errno)); lastError=fmt::sprintf("%s",strerror(errno));
return false; return NULL;
} }
unsigned char* buf; unsigned char* buf;
ssize_t len; ssize_t len;
if (fseek(f,0,SEEK_END)!=0) { if (fseek(f,0,SEEK_END)!=0) {
fclose(f); fclose(f);
lastError=fmt::sprintf("could not seek to end: %s",strerror(errno)); lastError=fmt::sprintf("could not seek to end: %s",strerror(errno));
return false; return NULL;
} }
len=ftell(f); len=ftell(f);
if (len<0) { if (len<0) {
fclose(f); fclose(f);
lastError=fmt::sprintf("could not determine file size: %s",strerror(errno)); lastError=fmt::sprintf("could not determine file size: %s",strerror(errno));
return false; return NULL;
} }
if (len==(SIZE_MAX>>1)) { if (len==(SIZE_MAX>>1)) {
fclose(f); fclose(f);
lastError="file size is invalid!"; lastError="file size is invalid!";
return false; return NULL;
} }
if (len==0) { if (len==0) {
fclose(f); fclose(f);
lastError="file is empty"; lastError="file is empty";
return false; return NULL;
} }
if (fseek(f,0,SEEK_SET)!=0) { if (fseek(f,0,SEEK_SET)!=0) {
fclose(f); fclose(f);
lastError=fmt::sprintf("could not seek to beginning: %s",strerror(errno)); lastError=fmt::sprintf("could not seek to beginning: %s",strerror(errno));
return false; return NULL;
} }
buf=new unsigned char[len]; buf=new unsigned char[len];
if (fread(buf,1,len,f)!=(size_t)len) { if (fread(buf,1,len,f)!=(size_t)len) {
logW("did not read entire wavetable file buffer!"); logW("did not read entire wavetable file buffer!");
delete[] buf; delete[] buf;
lastError=fmt::sprintf("could not read entire file: %s",strerror(errno)); lastError=fmt::sprintf("could not read entire file: %s",strerror(errno));
return false; return NULL;
} }
fclose(f); fclose(f);
@ -2290,7 +2433,7 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
lastError="invalid wavetable header/data!"; lastError="invalid wavetable header/data!";
delete wave; delete wave;
delete[] buf; delete[] buf;
return false; return NULL;
} }
} else { } else {
try { try {
@ -2331,7 +2474,7 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
} else { } else {
delete wave; delete wave;
delete[] buf; delete[] buf;
return false; return NULL;
} }
} }
} catch (EndOfFileException& e) { } catch (EndOfFileException& e) {
@ -2349,7 +2492,7 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
} else { } else {
delete wave; delete wave;
delete[] buf; delete[] buf;
return false; return NULL;
} }
} }
} }
@ -2357,17 +2500,10 @@ bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
delete wave; delete wave;
delete[] buf; delete[] buf;
lastError="premature end of file"; lastError="premature end of file";
return false; return NULL;
} }
BUSY_BEGIN; return wave;
saveLock.lock();
int waveCount=(int)song.wave.size();
song.wave.push_back(wave);
song.waveLen=waveCount+1;
saveLock.unlock();
BUSY_END;
return true;
} }
void DivEngine::delWave(int index) { void DivEngine::delWave(int index) {
@ -2383,7 +2519,10 @@ void DivEngine::delWave(int index) {
} }
int DivEngine::addSample() { int DivEngine::addSample() {
if (song.sample.size()>=256) return -1; if (song.sample.size()>=256) {
lastError="too many samples!";
return -1;
}
BUSY_BEGIN; BUSY_BEGIN;
saveLock.lock(); saveLock.lock();
DivSample* sample=new DivSample; DivSample* sample=new DivSample;
@ -2400,11 +2539,28 @@ int DivEngine::addSample() {
return sampleCount; return sampleCount;
} }
int DivEngine::addSampleFromFile(const char* path) { int DivEngine::addSamplePtr(DivSample* which) {
if (song.sample.size()>=256) { if (song.sample.size()>=256) {
lastError="too many samples!"; lastError="too many samples!";
delete which;
return -1; 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; BUSY_BEGIN;
warnings=""; warnings="";
@ -2437,7 +2593,6 @@ int DivEngine::addSampleFromFile(const char* path) {
if (extS==".dmc") { // read as .dmc if (extS==".dmc") { // read as .dmc
size_t len=0; size_t len=0;
DivSample* sample=new DivSample; DivSample* sample=new DivSample;
int sampleCount=(int)song.sample.size();
sample->name=stripPath; sample->name=stripPath;
FILE* f=ps_fopen(path,"rb"); FILE* f=ps_fopen(path,"rb");
@ -2445,7 +2600,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END; BUSY_END;
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno)); lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
delete sample; delete sample;
return -1; return NULL;
} }
if (fseek(f,0,SEEK_END)<0) { if (fseek(f,0,SEEK_END)<0) {
@ -2453,7 +2608,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END; BUSY_END;
lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno)); lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno));
delete sample; delete sample;
return -1; return NULL;
} }
len=ftell(f); len=ftell(f);
@ -2463,7 +2618,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END; BUSY_END;
lastError="file is empty!"; lastError="file is empty!";
delete sample; delete sample;
return -1; return NULL;
} }
if (len==(SIZE_MAX>>1)) { if (len==(SIZE_MAX>>1)) {
@ -2471,7 +2626,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END; BUSY_END;
lastError="file is invalid!"; lastError="file is invalid!";
delete sample; delete sample;
return -1; return NULL;
} }
if (fseek(f,0,SEEK_SET)<0) { if (fseek(f,0,SEEK_SET)<0) {
@ -2479,7 +2634,7 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END; BUSY_END;
lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno)); lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno));
delete sample; delete sample;
return -1; return NULL;
} }
sample->rate=33144; sample->rate=33144;
@ -2492,22 +2647,16 @@ int DivEngine::addSampleFromFile(const char* path) {
BUSY_END; BUSY_END;
lastError=fmt::sprintf("could not read file! (%s)",strerror(errno)); lastError=fmt::sprintf("could not read file! (%s)",strerror(errno));
delete sample; delete sample;
return -1; return NULL;
} }
saveLock.lock();
song.sample.push_back(sample);
song.sampleLen=sampleCount+1;
saveLock.unlock();
renderSamples();
BUSY_END; BUSY_END;
return sampleCount; return sample;
} }
} }
#ifndef HAVE_SNDFILE #ifndef HAVE_SNDFILE
lastError="Furnace was not compiled with libsndfile!"; lastError="Furnace was not compiled with libsndfile!";
return -1; return NULL;
#else #else
SF_INFO si; SF_INFO si;
SFWrapper sfWrap; SFWrapper sfWrap;
@ -2519,15 +2668,15 @@ int DivEngine::addSampleFromFile(const char* path) {
if (err==SF_ERR_SYSTEM) { if (err==SF_ERR_SYSTEM) {
lastError=fmt::sprintf("could not open file! (%s %s)",sf_error_number(err),strerror(errno)); lastError=fmt::sprintf("could not open file! (%s %s)",sf_error_number(err),strerror(errno));
} else { } 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) { if (si.frames>16777215) {
lastError="this sample is too big! max sample size is 16777215."; lastError="this sample is too big! max sample size is 16777215.";
sfWrap.doClose(); sfWrap.doClose();
BUSY_END; BUSY_END;
return -1; return NULL;
} }
void* buf=NULL; void* buf=NULL;
sf_count_t sampleLen=sizeof(short); 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<4000) sample->centerRate=4000;
if (sample->centerRate>64000) sample->centerRate=64000; if (sample->centerRate>64000) sample->centerRate=64000;
sfWrap.doClose(); sfWrap.doClose();
saveLock.lock();
song.sample.push_back(sample);
song.sampleLen=sampleCount+1;
saveLock.unlock();
renderSamples();
BUSY_END; BUSY_END;
return sampleCount; return sample;
#endif #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) { void DivEngine::delSample(int index) {
BUSY_BEGIN; BUSY_BEGIN;
sPreview.sample=-1; sPreview.sample=-1;
@ -2816,13 +3130,15 @@ void DivEngine::moveOrderDown() {
void DivEngine::exchangeIns(int one, int two) { void DivEngine::exchangeIns(int one, int two) {
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (int j=0; j<256; j++) { for (size_t j=0; j<song.subsong.size(); j++) {
if (curPat[i].data[j]==NULL) continue; for (int k=0; k<256; k++) {
for (int k=0; k<curSubSong->patLen; k++) { if (song.subsong[j]->pat[i].data[k]==NULL) continue;
if (curPat[i].data[j]->data[k][2]==one) { for (int l=0; l<curSubSong->patLen; l++) {
curPat[i].data[j]->data[k][2]=two; if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) {
} else if (curPat[i].data[j]->data[k][2]==two) { song.subsong[j]->pat[i].data[k]->data[l][2]=two;
curPat[i].data[j]->data[k][2]=one; } 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) { void DivEngine::noteOn(int chan, int ins, int note, int vol) {
if (chan<0 || chan>=chans) return; if (chan<0 || chan>=chans) return;
BUSY_BEGIN; BUSY_BEGIN;
pendingNotes.push(DivNoteEvent(chan,ins,note,vol,true)); pendingNotes.push_back(DivNoteEvent(chan,ins,note,vol,true));
if (!playing) { if (!playing) {
reset(); reset();
freelance=true; freelance=true;
@ -2924,7 +3240,7 @@ void DivEngine::noteOn(int chan, int ins, int note, int vol) {
void DivEngine::noteOff(int chan) { void DivEngine::noteOff(int chan) {
if (chan<0 || chan>=chans) return; if (chan<0 || chan>=chans) return;
BUSY_BEGIN; BUSY_BEGIN;
pendingNotes.push(DivNoteEvent(chan,-1,-1,-1,false)); pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false));
if (!playing) { if (!playing) {
reset(); reset();
freelance=true; 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))) { if ((!midiPoly) || (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel))) {
chan[finalChan].midiNote=note; chan[finalChan].midiNote=note;
chan[finalChan].midiAge=midiAgeCounter++; chan[finalChan].midiAge=midiAgeCounter++;
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); pendingNotes.push_back(DivNoteEvent(finalChan,ins,note,vol,true));
return; return;
} }
if (++finalChan>=chans) { if (++finalChan>=chans) {
@ -2997,7 +3313,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
chan[candidate].midiNote=note; chan[candidate].midiNote=note;
chan[candidate].midiAge=midiAgeCounter++; 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) { 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; //if (ch<0 || ch>=chans) return;
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
if (chan[i].midiNote==note) { 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; chan[i].midiNote=-1;
} }
} }
@ -3023,7 +3339,7 @@ void DivEngine::autoNoteOffAll() {
} }
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
if (chan[i].midiNote!=-1) { 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; chan[i].midiNote=-1;
} }
} }

View File

@ -32,7 +32,8 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <map> #include <map>
#include <queue> #include <unordered_map>
#include <deque>
#define addWarning(x) \ #define addWarning(x) \
if (warnings.empty()) { \ if (warnings.empty()) { \
@ -45,8 +46,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false; #define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev106" #define DIV_VERSION "dev112"
#define DIV_ENGINE_VERSION 106 #define DIV_ENGINE_VERSION 112
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
@ -194,7 +195,29 @@ struct DivDispatchContainer {
dcOffCompensation(false) {} 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 { struct DivSysDef {
const char* name; const char* name;
@ -211,8 +234,8 @@ struct DivSysDef {
// 0: primary // 0: primary
// 1: alternate (usually PCM) // 1: alternate (usually PCM)
DivInstrumentType chanInsType[DIV_MAX_CHANS][2]; DivInstrumentType chanInsType[DIV_MAX_CHANS][2];
EffectProcess effectFunc; const EffectHandlerMap effectHandlers;
EffectProcess postEffectFunc; const EffectHandlerMap postEffectHandlers;
DivSysDef( DivSysDef(
const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans, 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, 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<int> chTypes,
std::initializer_list<DivInstrumentType> chInsType1, std::initializer_list<DivInstrumentType> chInsType1,
std::initializer_list<DivInstrumentType> chInsType2={}, std::initializer_list<DivInstrumentType> chInsType2={},
EffectProcess fxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}, const EffectHandlerMap fxHandlers_={},
EffectProcess postFxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}): const EffectHandlerMap postFxHandlers_={}):
name(sysName), name(sysName),
nameJ(sysNameJ), nameJ(sysNameJ),
description(desc), description(desc),
@ -233,8 +256,8 @@ struct DivSysDef {
isSTD(isSTDChip), isSTD(isSTDChip),
isCompound(compound), isCompound(compound),
vgmVersion(vgmVer), vgmVersion(vgmVer),
effectFunc(fxHandler), effectHandlers(fxHandlers_),
postEffectFunc(postFxHandler) { postEffectHandlers(postFxHandlers_) {
memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*)); memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*));
memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*)); memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*));
memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int)); memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int));
@ -334,7 +357,7 @@ class DivEngine {
DivAudioExportModes exportMode; DivAudioExportModes exportMode;
double exportFadeOut; double exportFadeOut;
std::map<String,String> conf; std::map<String,String> conf;
std::queue<DivNoteEvent> pendingNotes; std::deque<DivNoteEvent> pendingNotes;
bool isMuted[DIV_MAX_CHANS]; bool isMuted[DIV_MAX_CHANS];
std::mutex isBusy, saveLock; std::mutex isBusy, saveLock;
String configPath; String configPath;
@ -533,6 +556,9 @@ class DivEngine {
// calculate frequency/period // 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); 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 // convert panning formats
int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range); int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range);
int convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range); int convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range);
@ -603,6 +629,9 @@ class DivEngine {
// get japanese system name // get japanese system name
const char* getSystemNameJ(DivSystem sys); const char* getSystemNameJ(DivSystem sys);
// get sys definition
const DivSysDef* getSystemDef(DivSystem sys);
// convert sample rate format // convert sample rate format
int fileToDivRate(int frate); int fileToDivRate(int frate);
int divToFileRate(int drate); int divToFileRate(int drate);
@ -708,8 +737,11 @@ class DivEngine {
// add wavetable // add wavetable
int addWave(); int addWave();
// add wavetable from file // add wavetable from pointer
bool addWaveFromFile(const char* path, bool loadRaw=true); int addWavePtr(DivWavetable* which);
// get wavetable from file
DivWavetable* waveFromFile(const char* path, bool loadRaw=true);
// delete wavetable // delete wavetable
void delWave(int index); void delWave(int index);
@ -717,8 +749,14 @@ class DivEngine {
// add sample // add sample
int addSample(); int addSample();
// add sample from file // add sample from pointer
int addSampleFromFile(const char* path); 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 // delete sample
void delSample(int index); void delSample(int index);
@ -875,6 +913,9 @@ class DivEngine {
// remove system // remove system
bool removeSystem(int index, bool preserveOrder=true); bool removeSystem(int index, bool preserveOrder=true);
// move system
bool swapSystem(int src, int dest, bool preserveOrder=true);
// write to register on system // write to register on system
void poke(int sys, unsigned int addr, unsigned short val); 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.loopModality=0;
ds.properNoiseLayout=false; ds.properNoiseLayout=false;
ds.waveDutyIsVol=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.legacyVolumeSlides=true;
ds.compatibleArpeggio=true; ds.compatibleArpeggio=true;
ds.noteOffResetsSlides=true; ds.noteOffResetsSlides=true;
@ -175,6 +177,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.brokenOutVol=true; // ??? ds.brokenOutVol=true; // ???
ds.e1e2StopOnSameNote=true; ds.e1e2StopOnSameNote=true;
ds.brokenPortaArp=false; ds.brokenPortaArp=false;
ds.snNoLowPeriods=true;
ds.delayBehavior=0;
// 1.1 compat flags // 1.1 compat flags
if (ds.version>24) { 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++) { for (int j=0; j<ins->std.arpMacro.len; j++) {
ins->std.arpMacro.val[j]-=12; 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(); ins->std.dutyMacro.len=reader.readC();
@ -825,6 +837,13 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
data=new short[length]; data=new short[length];
reader.read(data,length*2); 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) { if (pitch!=5) {
logD("%d: scaling from %d...",i,pitch); logD("%d: scaling from %d...",i,pitch);
@ -1056,6 +1075,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<101) { if (ds.version<101) {
ds.brokenPortaArp=true; ds.brokenPortaArp=true;
} }
if (ds.version<108) {
ds.snNoLowPeriods=true;
}
if (ds.version<110) {
ds.delayBehavior=1;
}
ds.isDMF=false; ds.isDMF=false;
reader.readS(); // reserved reader.readS(); // reserved
@ -1323,9 +1348,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} }
// pointers // pointers
reader.read(insPtr,ds.insLen*4); for (int i=0; i<ds.insLen; i++) {
reader.read(wavePtr,ds.waveLen*4); insPtr[i]=reader.readI();
reader.read(samplePtr,ds.sampleLen*4); }
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()); for (int i=0; i<numberOfPats; i++) patPtr.push_back(reader.readI());
logD("reading orders (%d)...",subSong->ordersLen); logD("reading orders (%d)...",subSong->ordersLen);
@ -1462,7 +1493,17 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else { } else {
reader.readC(); 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(); reader.readC();
} }
} }
@ -1705,11 +1746,36 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version>=58) { // modern sample if (ds.version>=58) { // modern sample
sample->init(sample->samples); sample->init(sample->samples);
reader.read(sample->getCurBuf(),sample->getCurBufLen()); 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 } else { // legacy sample
int length=sample->samples; int length=sample->samples;
short* data=new short[length]; short* data=new short[length];
reader.read(data,2*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) { if (pitch!=5) {
logD("%d: scaling from %d...",i,pitch); logD("%d: scaling from %d...",i,pitch);
} }
@ -1872,6 +1938,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
ds.noSlidesOnFirstTick=true; ds.noSlidesOnFirstTick=true;
ds.rowResetsArpPos=true; ds.rowResetsArpPos=true;
ds.ignoreJumpAtEnd=false; ds.ignoreJumpAtEnd=false;
ds.delayBehavior=0;
int insCount=31; int insCount=31;
bool bypassLimits=false; bool bypassLimits=false;
@ -2265,6 +2332,95 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
return success; 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) { bool DivEngine::loadFC(unsigned char* file, size_t len) {
struct InvalidHeaderException {}; struct InvalidHeaderException {};
bool success=false; 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 patPtr, freqMacroPtr, volMacroPtr, samplePtr, wavePtr;
unsigned int seqLen, patLen, freqMacroLen, volMacroLen, sampleLen; unsigned int seqLen, patLen, freqMacroLen, volMacroLen, sampleLen;
unsigned char waveLen[40]; unsigned char waveLen[80];
unsigned char waveLoopLen[40]; //unsigned char waveLoopLen[40];
struct FCSequence { struct FCSequence {
unsigned char pat[4]; unsigned char pat[4];
@ -2305,9 +2461,11 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.tuning=436.0; ds.tuning=436.0;
ds.version=DIV_VERSION_FC; ds.version=DIV_VERSION_FC;
//ds.linearPitch=0; //ds.linearPitch=0;
//ds.pitchMacroIsLinear=false;
//ds.noSlidesOnFirstTick=true; //ds.noSlidesOnFirstTick=true;
//ds.rowResetsArpPos=true; //ds.rowResetsArpPos=true;
//ds.ignoreJumpAtEnd=false; ds.pitchSlideSpeed=8;
ds.ignoreJumpAtEnd=false;
// load here // load here
if (!reader.seek(0,SEEK_SET)) { if (!reader.seek(0,SEEK_SET)) {
@ -2366,25 +2524,25 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
wavePtr=0; wavePtr=0;
} }
logD("patPtr: %d",patPtr); logD("patPtr: %x",patPtr);
logD("patLen: %d",patLen); logD("patLen: %d",patLen);
logD("freqMacroPtr: %d",freqMacroPtr); logD("freqMacroPtr: %x",freqMacroPtr);
logD("freqMacroLen: %d",freqMacroLen); logD("freqMacroLen: %d",freqMacroLen);
logD("volMacroPtr: %d",volMacroPtr); logD("volMacroPtr: %x",volMacroPtr);
logD("volMacroLen: %d",volMacroLen); logD("volMacroLen: %d",volMacroLen);
logD("samplePtr: %d",samplePtr); logD("samplePtr: %x",samplePtr);
if (isFC14) { if (isFC14) {
logD("wavePtr: %d",wavePtr); logD("wavePtr: %x",wavePtr);
} else { } else {
logD("sampleLen: %d",sampleLen); logD("sampleLen: %d",sampleLen);
} }
// sample info // sample info
logD("samples:"); logD("samples: (%x)",reader.tell());
for (int i=0; i<10; i++) { for (int i=0; i<10; i++) {
sample[i].loopLen=reader.readS_BE();
sample[i].len=reader.readS_BE(); sample[i].len=reader.readS_BE();
sample[i].loopStart=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); 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 // wavetable lengths
if (isFC14) { if (isFC14) {
logD("wavetables:"); logD("wavetables:");
for (int i=0; i<40; i++) { for (int i=0; i<80; i++) {
waveLen[i]=reader.readC(); waveLen[i]=(unsigned char)reader.readC();
waveLoopLen[i]=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; DivSample* s=new DivSample;
s->depth=DIV_SAMPLE_DEPTH_8BIT; s->depth=DIV_SAMPLE_DEPTH_8BIT;
if (sample[i].len>0) { if (sample[i].len>0) {
s->init(sample[i].len); s->init(sample[i].len*2);
} }
s->loopStart=sample[i].loopStart*2; s->loopStart=sample[i].loopStart*2;
s->loopEnd=(sample[i].loopStart+sample[i].loopLen)*2; s->loopEnd=(sample[i].loopStart+sample[i].loopLen)*2;
s->loop=(s->loopStart>=0)&&(s->loopEnd>=0); s->loop=(s->loopStart>=0)&&(s->loopEnd>=0);
reader.read(s->data8,sample[i].len); 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.sample.push_back(s);
} }
ds.sampleLen=(int)ds.sample.size(); ds.sampleLen=(int)ds.sample.size();
@ -2504,11 +2668,11 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
return false; return false;
} }
logD("reading wavetables..."); logD("reading wavetables...");
for (int i=0; i<40; i++) { for (int i=0; i<80; i++) {
DivWavetable* w=new DivWavetable; DivWavetable* w=new DivWavetable;
w->min=0; w->min=0;
w->max=255; w->max=255;
w->len=MIN(256,waveLoopLen[i]*2); w->len=MIN(256,waveLen[i]*2);
for (int i=0; i<256; i++) { for (int i=0; i<256; i++) {
w->data[i]=128; w->data[i]=128;
@ -2517,19 +2681,24 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
if (waveLen[i]>0) { if (waveLen[i]>0) {
signed char* waveArray=new signed char[waveLen[i]*2]; signed char* waveArray=new signed char[waveLen[i]*2];
reader.read(waveArray,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; if (howMany>256) howMany=256;
for (int i=0; i<howMany; i++) { for (int i=0; i<howMany; i++) {
w->data[i]=waveArray[i]+128; w->data[i]=waveArray[i]+128;
} }
delete[] waveArray; delete[] waveArray;
} else { } else {
w->len=32; logV("empty wave %d",i);
for (int i=0; i<32; i++) { generateFCPresetWave(i,w);
w->data[i]=(i*255)/31;
}
} }
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); 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]->speed1=3;
ds.subsong[0]->speed2=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 (unsigned int i=0; i<seqLen; i++) {
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
ds.subsong[0]->orders.ord[j][i]=i; 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 ignoreNext=false;
bool isSliding=false;
for (int k=0; k<32; k++) { for (int k=0; k<32; k++) {
FCPattern& fp=pat[seq[i].pat[j]]; FCPattern& fp=pat[seq[i].pat[j]];
if (fp.note[k]>0 && fp.note[k]<0x49) { 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 note=(fp.note[k]+seq[i].transpose[j])%12;
short octave=2+((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; if (fp.note[k]>=0x3d) octave-=6;
@ -2572,23 +2751,48 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
octave&=0xff; octave&=0xff;
p->data[k][0]=note; p->data[k][0]=note;
p->data[k][1]=octave; p->data[k][1]=octave;
if (isSliding) { if (isSliding[j]) {
isSliding=false; isSliding[j]=false;
p->data[k][4]=2; p->data[k][4]=2;
p->data[k][5]=0; 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 (fp.val[k]) {
if (ignoreNext) { if (ignoreNext) {
ignoreNext=false; ignoreNext=false;
} else { } 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) { if (fp.val[k]&0x40) {
p->data[k][4]=2; p->data[k][4]=2;
p->data[k][5]=0; p->data[k][5]=0;
isSliding=false; isSliding[j]=false;
} else if (fp.val[k]&0x80) { } else if (fp.val[k]&0x80) {
isSliding=true; isSliding[j]=true;
if (k<31) { if (k<31) {
if (fp.val[k+1]&0x20) { if (fp.val[k+1]&0x20) {
p->data[k][4]=2; p->data[k][4]=2;
@ -2604,9 +2808,13 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
} }
} }
} else { } 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->type=DIV_INS_AMIGA;
ins->name=fmt::sprintf("Instrument %d",i); ins->name=fmt::sprintf("Instrument %d",i);
ins->amiga.useWave=true; ins->amiga.useWave=true;
//unsigned char seqSpeed=m.val[0]; unsigned char seqSpeed=m.val[0];
unsigned char freqMacro=m.val[1]; unsigned char freqMacro=m.val[1];
//unsigned char vibSpeed=m.val[2]; unsigned char vibSpeed=m.val[2];
//unsigned char vibDepth=m.val[3]; unsigned char vibDepth=m.val[3];
//unsigned char vibDelay=m.val[4]; unsigned char vibDelay=m.val[4];
unsigned char lastVal=m.val[5]; unsigned char lastVal=m.val[5];
signed char loopMap[64]; signed char loopMap[64];
memset(loopMap,-1,64); memset(loopMap,-1,64);
signed char loopMapFreq[64];
memset(loopMapFreq,-1,64);
signed char loopMapWave[64];
memset(loopMapWave,-1,64);
// volume sequence // volume sequence
ins->std.volMacro.len=0; ins->std.volMacro.len=0;
for (int j=5; j<64; j++) { for (int j=5; j<64; j++) {
@ -2647,8 +2861,9 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
// TODO: <= or <? // TODO: <= or <?
for (int k=0; k<=susTime; k++) { for (int k=0; k<=susTime; k++) {
ins->std.volMacro.val[ins->std.volMacro.len]=lastVal; 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 } else if (m.val[j]==0xe9 || m.val[j]==0xea) { // volume slide
if (++j>=64) break; if (++j>=64) break;
signed char slideStep=m.val[j]; 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; ins->std.volMacro.val[ins->std.volMacro.len]=lastVal;
if (++ins->std.volMacro.len>=128) break; if (++ins->std.volMacro.len>=255) break;
} }
} else { } else {
ins->std.volMacro.val[ins->std.volMacro.len]=m.val[j]; // TODO: replace with upcoming macro speed
lastVal=m.val[j]; for (int k=0; k<MAX(1,seqSpeed); k++) {
if (++ins->std.volMacro.len>=128) break; 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()) { if (freqMacro<freqMacros.size()) {
FCMacro& fm=freqMacros[freqMacro]; FCMacro& fm=freqMacros[freqMacro];
for (int j=0; j<64; j++) { for (int j=0; j<64; j++) {
loopMapFreq[j]=ins->std.arpMacro.len;
loopMapWave[j]=ins->std.waveMacro.len;
if (fm.val[j]==0xe1) { if (fm.val[j]==0xe1) {
break; break;
} else if (fm.val[j]==0xe2 || fm.val[j]==0xe4) { } 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.val[ins->std.waveMacro.len]=wave-10;
ins->std.waveMacro.open=true; ins->std.waveMacro.open=true;
lastVal=wave; lastVal=wave;
if (++ins->std.waveMacro.len>=128) break; //if (++ins->std.arpMacro.len>=255) break;
if (++ins->std.arpMacro.len>=128) break;
} }
} else if (fm.val[j]==0xe0) { } 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) { } else if (fm.val[j]==0xe3) {
logV("unhandled vibrato!"); logV("unhandled vibrato!");
} else if (fm.val[j]==0xe8) { } else if (fm.val[j]==0xe8) {
logV("unhandled sustain!"); logV("unhandled sustain!");
} else if (fm.val[j]==0xe7) { } 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) { } else if (fm.val[j]==0xe9) {
logV("unhandled pack!"); logV("unhandled pack!");
} else if (fm.val[j]==0xea) { } else if (fm.val[j]==0xea) {
logV("unhandled pitch!"); logV("unhandled pitch!");
} else { } 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; 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.ins.push_back(ins);
} }
ds.insLen=(int)ds.ins.size(); ds.insLen=(int)ds.ins.size();
// optimize
ds.subsong[0]->optimizePatterns();
ds.subsong[0]->rearrangePatterns();
if (active) quitDispatch(); if (active) quitDispatch();
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
saveLock.lock(); saveLock.lock();
@ -3039,6 +3307,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
ins->std.arpMacro.len=reader.readC(); ins->std.arpMacro.len=reader.readC();
ins->std.arpMacro.loop=reader.readI(); ins->std.arpMacro.loop=reader.readI();
ins->std.arpMacro.rel=reader.readI(); ins->std.arpMacro.rel=reader.readI();
// TODO: get rid
ins->std.arpMacro.mode=reader.readI(); ins->std.arpMacro.mode=reader.readI();
for (int j=0; j<ins->std.arpMacro.len; j++) { for (int j=0; j<ins->std.arpMacro.len; j++) {
ins->std.arpMacro.val[j]=reader.readC(); ins->std.arpMacro.val[j]=reader.readC();
@ -3318,15 +3587,27 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
// high short is channel // high short is channel
// low short is pattern number // low short is pattern number
std::vector<PatToWrite> patsToWrite; std::vector<PatToWrite> patsToWrite;
bool alreadyAdded[256]; if (getConfInt("saveUnusedPatterns",0)==1) {
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (size_t j=0; j<song.subsong.size(); j++) { for (size_t j=0; j<song.subsong.size(); j++) {
DivSubSong* subs=song.subsong[j]; DivSubSong* subs=song.subsong[j];
memset(alreadyAdded,0,256*sizeof(bool)); for (int k=0; k<256; k++) {
for (int k=0; k<subs->ordersLen; k++) { if (subs->pat[i].data[k]==NULL) continue;
if (alreadyAdded[subs->orders.ord[i][k]]) continue; patsToWrite.push_back(PatToWrite(j,i,k));
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k])); }
alreadyAdded[subs->orders.ord[i][k]]=true; }
}
} 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.brokenOutVol);
w->writeC(song.e1e2StopOnSameNote); w->writeC(song.e1e2StopOnSameNote);
w->writeC(song.brokenPortaArp); 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); w->writeC(0);
} }
@ -3598,7 +3881,21 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(0xffffffff); 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()); w->write(sample->getCurBuf(),sample->getCurBufLen());
#endif
blockEndSeek=w->tell(); blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET); 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][1]); // octave
w->writeS(pat->data[j][2]); // instrument w->writeS(pat->data[j][2]); // instrument
w->writeS(pat->data[j][3]); // volume 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 w->write(&pat->data[j][4],2*song.subsong[i.subsong]->pat[i.chan].effectCols*2); // effects
#endif
} }
w->writeString(pat->name,false); w->writeString(pat->name,false);
@ -3907,16 +4210,21 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeI(i->std.volMacro.val[j]+18); w->writeI(i->std.volMacro.val[j]+18);
} }
} else { } 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) { if (i->std.volMacro.len>0) {
w->writeC(i->std.volMacro.loop); w->writeC(i->std.volMacro.loop);
} }
} }
// TODO: take care of new arp macro format
w->writeC(i->std.arpMacro.len); w->writeC(i->std.arpMacro.len);
if (i->std.arpMacro.mode) { 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 { } else {
for (int j=0; j<i->std.arpMacro.len; j++) { for (int j=0; j<i->std.arpMacro.len; j++) {
w->writeI(i->std.arpMacro.val[j]+12); 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); w->writeI(i->std.dutyMacro.val[j]+12);
} }
} else { } 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) { if (i->std.dutyMacro.len>0) {
w->writeC(i->std.dutyMacro.loop); w->writeC(i->std.dutyMacro.loop);
} }
w->writeC(i->std.waveMacro.len); 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) { if (i->std.waveMacro.len>0) {
w->writeC(i->std.waveMacro.loop); w->writeC(i->std.waveMacro.loop);
} }
@ -3991,7 +4303,9 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeI(i->data[j]>>2); w->writeI(i->data[j]>>2);
} }
} else { } 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][0]); // note
w->writeS(pat->data[k][1]); // octave w->writeS(pat->data[k][1]); // octave
w->writeS(pat->data[k][3]); // volume 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 w->write(&pat->data[k][4],2*curPat[i].effectCols*2); // effects
#endif
w->writeS(pat->data[k][2]); // instrument w->writeS(pat->data[k][2]); // instrument
} }
} }
@ -4023,7 +4343,15 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(50); w->writeC(50);
// i'm too lazy to deal with .dmf's weird way of storing 8-bit samples // i'm too lazy to deal with .dmf's weird way of storing 8-bit samples
w->writeC(16); 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); w->write(i->data16,i->length16);
#endif
} }
saveLock.unlock(); saveLock.unlock();

View File

@ -733,6 +733,8 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector<DivInstrument*>& ret, S
ins = new DivInstrument; ins = new DivInstrument;
ins->type = DIV_INS_OPL; ins->type = DIV_INS_OPL;
ins->name = fmt::sprintf("%s (2)", insName); ins->name = fmt::sprintf("%s (2)", insName);
ins->fm.alg = (feedConnect2nd & 0x1);
ins->fm.fb = ((feedConnect2nd >> 1) & 0xF);
for (int i : {1,0}) { for (int i : {1,0}) {
readOpliOp(reader, ins->fm.op[i]); readOpliOp(reader, ins->fm.op[i]);
} }
@ -1498,6 +1500,8 @@ void DivEngine::loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, S
ins = new DivInstrument; ins = new DivInstrument;
ins->type = DIV_INS_OPL; ins->type = DIV_INS_OPL;
ins->name = fmt::sprintf("%s (2)", insName); ins->name = fmt::sprintf("%s (2)", insName);
ins->fm.alg = (feedConnect2nd & 0x1);
ins->fm.fb = ((feedConnect2nd >> 1) & 0xF);
for (int i : {1,0}) { for (int i : {1,0}) {
patchSum += readWoplOp(reader, ins->fm.op[i]); 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.ex1Macro.loop);
w->writeI(std.ex2Macro.loop); w->writeI(std.ex2Macro.loop);
w->writeI(std.ex3Macro.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); // reserved
w->writeC(0); w->writeC(0);
w->writeC(0); w->writeC(0);
@ -543,12 +543,126 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeC(gb.softEnv); w->writeC(gb.softEnv);
w->writeC(gb.alwaysInit); 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(); blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET); w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4); w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END); 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) { DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
char magic[4]; char magic[4];
reader.read(magic,4); reader.read(magic,4);
@ -674,10 +788,10 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
int oldVolHeight=reader.readC(); int oldVolHeight=reader.readC();
int oldDutyHeight=reader.readC(); int oldDutyHeight=reader.readC();
reader.readC(); // oldWaveHeight reader.readC(); // oldWaveHeight
reader.read(std.volMacro.val,4*std.volMacro.len); READ_MACRO_VALS(std.volMacro.val,std.volMacro.len);
reader.read(std.arpMacro.val,4*std.arpMacro.len); READ_MACRO_VALS(std.arpMacro.val,std.arpMacro.len);
reader.read(std.dutyMacro.val,4*std.dutyMacro.len); READ_MACRO_VALS(std.dutyMacro.val,std.dutyMacro.len);
reader.read(std.waveMacro.val,4*std.waveMacro.len); READ_MACRO_VALS(std.waveMacro.val,std.waveMacro.len);
if (version<31) { if (version<31) {
if (!std.arpMacro.mode) for (int j=0; j<std.arpMacro.len; j++) { if (!std.arpMacro.mode) for (int j=0; j<std.arpMacro.len; j++) {
std.arpMacro.val[j]-=12; std.arpMacro.val[j]-=12;
@ -692,10 +806,10 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
} }
} }
if (version>=17) { if (version>=17) {
reader.read(std.pitchMacro.val,4*std.pitchMacro.len); READ_MACRO_VALS(std.pitchMacro.val,std.pitchMacro.len);
reader.read(std.ex1Macro.val,4*std.ex1Macro.len); READ_MACRO_VALS(std.ex1Macro.val,std.ex1Macro.len);
reader.read(std.ex2Macro.val,4*std.ex2Macro.len); READ_MACRO_VALS(std.ex2Macro.val,std.ex2Macro.len);
reader.read(std.ex3Macro.val,4*std.ex3Macro.len); READ_MACRO_VALS(std.ex3Macro.val,std.ex3Macro.len);
} else { } else {
if (type==DIV_INS_STD) { if (type==DIV_INS_STD) {
if (oldVolHeight==31) { if (oldVolHeight==31) {
@ -730,10 +844,10 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
std.fmsMacro.open=reader.readC(); std.fmsMacro.open=reader.readC();
std.amsMacro.open=reader.readC(); std.amsMacro.open=reader.readC();
reader.read(std.algMacro.val,4*std.algMacro.len); READ_MACRO_VALS(std.algMacro.val,std.algMacro.len);
reader.read(std.fbMacro.val,4*std.fbMacro.len); READ_MACRO_VALS(std.fbMacro.val,std.fbMacro.len);
reader.read(std.fmsMacro.val,4*std.fmsMacro.len); READ_MACRO_VALS(std.fmsMacro.val,std.fmsMacro.len);
reader.read(std.amsMacro.val,4*std.amsMacro.len); READ_MACRO_VALS(std.amsMacro.val,std.amsMacro.len);
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
DivInstrumentSTD::OpMacro& op=std.opMacros[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 // clear noise macro if PCE instrument and version<63
if (version<63 && type==DIV_INS_PCE) { if (version<63 && type==DIV_INS_PCE) {
std.dutyMacro.len=0; std.dutyMacro.len=0;
std.dutyMacro.loop=-1; std.dutyMacro.loop=255;
std.dutyMacro.rel=-1; std.dutyMacro.rel=255;
} }
// clear wave macro if OPLL instrument and version<70 // clear wave macro if OPLL instrument and version<70
if (version<70 && type==DIV_INS_OPLL) { if (version<70 && type==DIV_INS_OPLL) {
std.waveMacro.len=0; std.waveMacro.len=0;
std.waveMacro.loop=-1; std.waveMacro.loop=255;
std.waveMacro.rel=-1; std.waveMacro.rel=255;
} }
// sample map // sample map
@ -1007,14 +1121,14 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
std.ex7Macro.open=reader.readC(); std.ex7Macro.open=reader.readC();
std.ex8Macro.open=reader.readC(); std.ex8Macro.open=reader.readC();
reader.read(std.panLMacro.val,4*std.panLMacro.len); READ_MACRO_VALS(std.panLMacro.val,std.panLMacro.len);
reader.read(std.panRMacro.val,4*std.panRMacro.len); READ_MACRO_VALS(std.panRMacro.val,std.panRMacro.len);
reader.read(std.phaseResetMacro.val,4*std.phaseResetMacro.len); READ_MACRO_VALS(std.phaseResetMacro.val,std.phaseResetMacro.len);
reader.read(std.ex4Macro.val,4*std.ex4Macro.len); READ_MACRO_VALS(std.ex4Macro.val,std.ex4Macro.len);
reader.read(std.ex5Macro.val,4*std.ex5Macro.len); READ_MACRO_VALS(std.ex5Macro.val,std.ex5Macro.len);
reader.read(std.ex6Macro.val,4*std.ex6Macro.len); READ_MACRO_VALS(std.ex6Macro.val,std.ex6Macro.len);
reader.read(std.ex7Macro.val,4*std.ex7Macro.len); READ_MACRO_VALS(std.ex7Macro.val,std.ex7Macro.len);
reader.read(std.ex8Macro.val,4*std.ex8Macro.len); READ_MACRO_VALS(std.ex8Macro.val,std.ex8Macro.len);
// FDS // FDS
fds.modSpeed=reader.readI(); fds.modSpeed=reader.readI();
@ -1111,6 +1225,136 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
gb.alwaysInit=reader.readC(); 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; return DIV_DATA_SUCCESS;
} }
@ -1150,3 +1394,148 @@ bool DivInstrument::save(const char* path) {
w->finish(); w->finish();
return true; 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]; int val[256];
unsigned int mode; unsigned int mode;
bool open; bool open;
unsigned char len; unsigned char len, delay, speed, loop, rel;
signed char loop;
signed char rel;
// the following variables are used by the GUI and not saved in the file // the following variables are used by the GUI and not saved in the file
int vScroll, vZoom; int vScroll, vZoom;
explicit DivInstrumentMacro(const String& n, bool initOpen=false): explicit DivInstrumentMacro(const String& n, bool initOpen=false):
name(n), name(n),
mode(0), mode(0),
open(initOpen), open(initOpen),
len(0), len(0),
loop(-1), delay(0),
rel(-1), speed(1),
loop(255),
rel(255),
vScroll(0), vScroll(0),
vZoom(-1) { vZoom(-1) {
memset(val,0,256*sizeof(int)); memset(val,0,256*sizeof(int));
@ -576,6 +575,13 @@ struct DivInstrument {
* @return whether it was successful. * @return whether it was successful.
*/ */
bool save(const char* path); 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(): DivInstrument():
name(""), name(""),
type(DIV_INS_FM) { type(DIV_INS_FM) {

View File

@ -32,6 +32,18 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
had=false; had=false;
return; 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) { if (finished) {
finished=false; finished=false;
} }
@ -41,16 +53,17 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
actualHad=has; actualHad=has;
had=actualHad; had=actualHad;
if (has) { if (has) {
lastPos=pos;
val=source.val[pos++]; val=source.val[pos++];
if (source.rel>=0 && pos>source.rel && !released) { if (pos>source.rel && !released) {
if (source.loop<source.len && source.loop>=0 && source.loop<source.rel) { if (source.loop<source.len && source.loop<source.rel) {
pos=source.loop; pos=source.loop;
} else { } else {
pos--; pos--;
} }
} }
if (pos>=source.len) { 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; pos=source.loop;
} else if (linger) { } else if (linger) {
pos--; pos--;
@ -239,7 +252,7 @@ void DivMacroInt::init(DivInstrument* which) {
for (size_t i=0; i<macroListLen; i++) { for (size_t i=0; i<macroListLen; i++) {
if (macroSource[i]!=NULL) { if (macroSource[i]!=NULL) {
macroList[i]->prepare(*macroSource[i],e); 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 { } else {
hasRelease=false; hasRelease=false;
} }
@ -251,3 +264,110 @@ void DivMacroInt::notifyInsDeletion(DivInstrument* which) {
init(NULL); 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; class DivEngine;
struct DivMacroStruct { struct DivMacroStruct {
int pos; int pos, lastPos, delay;
int val; int val;
bool has, had, actualHad, finished, will, linger; bool has, had, actualHad, finished, will, linger, began;
unsigned int mode; unsigned int mode;
void doMacro(DivInstrumentMacro& source, bool released, bool tick); void doMacro(DivInstrumentMacro& source, bool released, bool tick);
void init() { void init() {
pos=mode=0; pos=lastPos=mode=delay=0;
has=had=actualHad=will=false; has=had=actualHad=will=false;
linger=false; linger=false;
began=true;
// TODO: test whether this breaks anything? // TODO: test whether this breaks anything?
val=0; val=0;
} }
void prepare(DivInstrumentMacro& source, DivEngine* e); void prepare(DivInstrumentMacro& source, DivEngine* e);
DivMacroStruct(): DivMacroStruct():
pos(0), pos(0),
lastPos(0),
delay(0),
val(0), val(0),
has(false), has(false),
had(false), had(false),
@ -47,6 +50,7 @@ struct DivMacroStruct {
finished(false), finished(false),
will(false), will(false),
linger(false), linger(false),
began(false),
mode(0) {} mode(0) {}
}; };
@ -127,6 +131,13 @@ class DivMacroInt {
*/ */
void notifyInsDeletion(DivInstrument* which); 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(): DivMacroInt():
e(NULL), e(NULL),
ins(NULL), ins(NULL),

View File

@ -18,6 +18,7 @@
*/ */
#include "engine.h" #include "engine.h"
#include "../ta-log.h"
static DivPattern emptyPat; static DivPattern emptyPat;
@ -40,6 +41,44 @@ DivPattern* DivChannelData::getPattern(int index, bool create) {
return data[index]; 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() { void DivChannelData::wipePatterns() {
for (int i=0; i<256; i++) { for (int i=0; i<256; i++) {
if (data[i]!=NULL) { if (data[i]!=NULL) {
@ -54,81 +93,6 @@ void DivPattern::copyOn(DivPattern* dest) {
memcpy(dest->data,data,sizeof(data)); 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(): DivChannelData::DivChannelData():
effectCols(1) { effectCols(1) {
memset(data,0,256*sizeof(void*)); memset(data,0,256*sizeof(void*));

View File

@ -18,6 +18,7 @@
*/ */
#include "safeReader.h" #include "safeReader.h"
#include <vector>
struct DivPattern { struct DivPattern {
String name; String name;
@ -28,14 +29,6 @@ struct DivPattern {
* @param dest the destination pattern. * @param dest the destination pattern.
*/ */
void copyOn(DivPattern* dest); 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(); DivPattern();
}; };
@ -59,6 +52,20 @@ struct DivChannelData {
*/ */
DivPattern* getPattern(int index, bool create); 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. * destroy all patterns on this DivChannelData.
*/ */

View File

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

View File

@ -64,21 +64,6 @@ const char** DivPlatformAmiga::getRegisterSheet() {
return regCheatSheetAmiga; 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) \ #define writeAudDat(x) \
chan[i].audDat=x; \ chan[i].audDat=x; \
if (i<3 && chan[i].useV) { \ if (i<3 && chan[i].useV) { \
@ -178,19 +163,9 @@ void DivPlatformAmiga::tick(bool sysTick) {
} }
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { // TODO: why the off mult? this may be a bug!
if (chan[i].std.arp.mode) { chan[i].baseFreq=round(off*NOTE_PERIODIC_NOROUND(parent->calcArp(chan[i].note,chan[i].std.arp.val)));
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));
}
}
chan[i].freqChanged=true; 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].useWave && chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { 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 notifyWaveChange(int wave);
void notifyInsDeletion(void* ins); void notifyInsDeletion(void* ins);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
}; };

View File

@ -50,111 +50,6 @@ const char** DivPlatformArcade::getRegisterSheet() {
return regCheatSheetOPM; 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) { void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static int o[2]; static int o[2];
@ -266,18 +161,9 @@ void DivPlatformArcade::tick(bool sysTick) {
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_LINEAR(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformArcade(); ~DivPlatformArcade();

View File

@ -73,42 +73,6 @@ const char** DivPlatformAY8910::getRegisterSheet() {
return intellivision?regCheatSheetAY8914:regCheatSheetAY; 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) { void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (ayBufLen<len) { if (ayBufLen<len) {
ayBufLen=len; ayBufLen=len;
@ -132,6 +96,7 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
end=true; end=true;
break; break;
} }
// Partially
unsigned char dacData=(((unsigned char)s->data8[chan[i].dac.pos]^0x80)>>4); 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)); chan[i].dac.out=MAX(0,MIN(15,(dacData*chan[i].outVol)/15));
if (prev_out!=chan[i].dac.out) { 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
rWrite(0x06,31-chan[i].std.duty.val); 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8): DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8):

View File

@ -77,54 +77,6 @@ const char** DivPlatformAY8930::getRegisterSheet() {
return regCheatSheetAY8930; 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) { void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (ayBufLen<len) { if (ayBufLen<len) {
ayBufLen=len; ayBufLen=len;
@ -148,6 +100,7 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l
end=true; end=true;
break; break;
} }
// Partially
unsigned char dacData=(((unsigned char)s->data8[chan[i].dac.pos]^0x80)>>3); 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)); chan[i].dac.out=MAX(0,MIN(31,(dacData*chan[i].outVol)/31));
if (prev_out!=chan[i].dac.out) { 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
rWrite(0x06,chan[i].std.duty.val); 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
}; };

View File

@ -39,15 +39,6 @@ const char** DivPlatformBubSysWSG::getRegisterSheet() {
return regCheatSheetBubSysWSG; 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) { void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_t len) {
int chanOut=0; int chanOut=0;
for (size_t h=start; h<start+len; h++) { 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformBubSysWSG(); ~DivPlatformBubSysWSG();

View File

@ -62,52 +62,6 @@ const char** DivPlatformC64::getRegisterSheet() {
return regCheatSheetSID; 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) { void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) {
int dcOff=sid.get_dc(0); int dcOff=sid.get_dc(0);
for (size_t i=start; i<start+len; i++) { 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.duty.had) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64); 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void setChipModel(bool is6581); void setChipModel(bool is6581);
void quit(); void quit();

View File

@ -55,30 +55,6 @@ const char** DivPlatformFDS::getRegisterSheet() {
return regCheatSheetFDS; 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) { void DivPlatformFDS::acquire_puNES(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) { for (size_t i=start; i<start+len; i++) {
extcl_apu_tick_FDS(fds); extcl_apu_tick_FDS(fds);
@ -145,18 +121,9 @@ void DivPlatformFDS::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.duty.had) {

View File

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

View File

@ -61,27 +61,6 @@ const char** DivPlatformGB::getRegisterSheet() {
return regCheatSheetGB; 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) { void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) { for (size_t i=start; i<start+len; i++) {
if (!writes.empty()) { if (!writes.empty()) {
@ -181,34 +160,18 @@ void DivPlatformGB::tick(bool sysTick) {
chan[i].soundLen=64; chan[i].soundLen=64;
if (!chan[i].keyOn) chan[i].killIt=true; if (!chan[i].keyOn) chan[i].killIt=true;
chan[i].freqChanged=true;
} }
} }
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (i==3) { // noise if (i==3) { // noise
if (chan[i].std.arp.mode) { chan[i].baseFreq=parent->calcArp(chan[i].note,chan[i].std.arp.val,24);
chan[i].baseFreq=chan[i].std.arp.val+24;
} else {
chan[i].baseFreq=chan[i].note+chan[i].std.arp.val;
}
if (chan[i].baseFreq>255) chan[i].baseFreq=255; if (chan[i].baseFreq>255) chan[i].baseFreq=255;
if (chan[i].baseFreq<0) chan[i].baseFreq=0; if (chan[i].baseFreq<0) chan[i].baseFreq=0;
} else { } else {
if (!chan[i].inPorta) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val,24));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val; 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].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=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 (killDelta!=1) {
if (i!=2) { rWrite(16+i*5+2,((chan[i].envVol<<4))|8);
//rWrite(16+i*5+2,8); for (int j=0; j<killDelta; j++) {
int killDelta=chan[i].lastKill-chan[i].outVol+1; rWrite(16+i*5+2,0x09);
if (killDelta<0) killDelta+=16; rWrite(16+i*5+2,0x11);
chan[i].lastKill=chan[i].outVol; 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); ws.init(ins,32,15,chan[c.chan].insChanged);
} }
if ((chan[c.chan].insChanged || ins->gb.alwaysInit) && !chan[c.chan].softEnv) { 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].envLen=ins->gb.envLen;
chan[c.chan].envDir=ins->gb.envDir; chan[c.chan].envDir=ins->gb.envDir;
chan[c.chan].soundLen=ins->gb.soundLen; 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) { if (c.chan==2 && chan[c.chan].softEnv) {
chan[c.chan].soundLen=64; chan[c.chan].soundLen=64;
@ -460,6 +430,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
} }
if (!chan[c.chan].softEnv) { if (!chan[c.chan].softEnv) {
chan[c.chan].envVol=chan[c.chan].vol; chan[c.chan].envVol=chan[c.chan].vol;
chan[c.chan].soManyHacksToMakeItDefleCompatible=true;
} else if (c.chan!=2) { } else if (c.chan!=2) {
chan[c.chan].envVol=chan[c.chan].vol; chan[c.chan].envVol=chan[c.chan].vol;
if (!chan[c.chan].keyOn) chan[c.chan].killIt=true; if (!chan[c.chan].keyOn) chan[c.chan].killIt=true;
@ -636,7 +607,7 @@ void DivPlatformGB::notifyWaveChange(int wave) {
if (chan[2].wave==wave) { if (chan[2].wave==wave) {
ws.changeWave1(wave); ws.changeWave1(wave);
updateWave(); 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; int freq, baseFreq, pitch, pitch2, note, ins;
unsigned char duty, sweep; unsigned char duty, sweep;
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, released, softEnv, killIt; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, released, softEnv, killIt;
bool soManyHacksToMakeItDefleCompatible;
signed char vol, outVol, wave, lastKill; signed char vol, outVol, wave, lastKill;
unsigned char envVol, envDir, envLen, soundLen; unsigned char envVol, envDir, envLen, soundLen;
unsigned short hwSeqPos; unsigned short hwSeqPos;
@ -59,6 +60,7 @@ class DivPlatformGB: public DivDispatch {
released(false), released(false),
softEnv(false), softEnv(false),
killIt(false), killIt(false),
soManyHacksToMakeItDefleCompatible(false),
vol(15), vol(15),
outVol(15), outVol(15),
wave(-1), wave(-1),
@ -113,7 +115,6 @@ class DivPlatformGB: public DivDispatch {
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
void setFlags(unsigned int flags); void setFlags(unsigned int flags);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();

View File

@ -27,108 +27,6 @@
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[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() { void DivPlatformGenesis::processDAC() {
if (softPCM) { if (softPCM) {
softPCMTimer+=chipClock/576; softPCMTimer+=chipClock/576;
@ -365,26 +263,40 @@ void DivPlatformGenesis::tick(bool sysTick) {
} }
} }
if (chan[i].std.arp.had) { if (i>=5 && chan[i].furnaceDac) {
if (!chan[i].inPorta) { if (chan[i].std.arp.had) {
if (chan[i].std.arp.mode) { if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11); chan[i].baseFreq=parent->calcBaseFreq(1,1,parent->calcArp(chan[i].note,chan[i].std.arp.val),false);
} else {
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11);
} }
chan[i].freqChanged=true;
} }
chan[i].freqChanged=true;
} else { } else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) { if (chan[i].std.arp.had) {
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11); 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; chan[i].freqChanged=true;
} }
} }
if (chan[i].std.panL.had) { if (i>=5 && chan[i].furnaceDac) {
chan[i].pan=chan[i].std.panL.val&3; if (chan[i].std.panL.had) {
if (i<6) { chan[5].pan&=1;
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)); 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); int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
DivPlatformGenesis(): DivPlatformGenesis():

View File

@ -561,6 +561,11 @@ void DivPlatformGenesisExt::forceIns() {
opChan[i].freqChanged=true; 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) { void* DivPlatformGenesisExt::getChanState(int ch) {

View File

@ -130,19 +130,6 @@ const char** DivPlatformLynx::getRegisterSheet() {
return regCheatSheetLynx; 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) { void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
for (int i=0; i<4; i++) { 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].actualNote=parent->calcArp(chan[i].note,chan[i].std.arp.val);
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val); chan[i].baseFreq=NOTE_PERIODIC(chan[i].actualNote);
if (chan[i].pcm) chan[i].sampleBaseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); if (chan[i].pcm) chan[i].sampleBaseFreq=NOTE_FREQUENCY(chan[i].actualNote);
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].freqChanged=true; chan[i].freqChanged=true;
} }
} }

View File

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

View File

@ -43,15 +43,6 @@ const char** DivPlatformMMC5::getRegisterSheet() {
return regCheatSheetMMC5; 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) { void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) { for (size_t i=start; i<start+len; i++) {
if (dacSample!=-1) { if (dacSample!=-1) {
@ -110,20 +101,12 @@ void DivPlatformMMC5::tick(bool sysTick) {
if (chan[i].outVol<0) chan[i].outVol=0; if (chan[i].outVol<0) chan[i].outVol=0;
rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6)); 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val; 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformMMC5(); ~DivPlatformMMC5();

View File

@ -30,18 +30,6 @@ const char** DivPlatformMSM6258::getRegisterSheet() {
return NULL; 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) { void DivPlatformMSM6258::acquire(short* bufL, short* bufR, size_t start, size_t len) {
short* outs[2]={ short* outs[2]={
&msmOut, &msmOut,

View File

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

View File

@ -30,15 +30,6 @@ const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL; 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) { u8 DivPlatformMSM6295::read_byte(u32 address) {
if (adpcmMem==NULL || address>=getSampleMemCapacity(0)) { if (adpcmMem==NULL || address>=getSampleMemCapacity(0)) {
return 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 poke(std::vector<DivRegWrite>& wlist) override;
virtual void setFlags(unsigned int flags) override; virtual void setFlags(unsigned int flags) override;
virtual const char** getRegisterSheet() override; virtual const char** getRegisterSheet() override;
virtual const char* getEffectName(unsigned char effect) override;
virtual const void* getSampleMem(int index) override; virtual const void* getSampleMem(int index) override;
virtual size_t getSampleMemCapacity(int index) override; virtual size_t getSampleMemCapacity(int index) override;
virtual size_t getSampleMemUsage(int index) override; virtual size_t getSampleMemUsage(int index) override;

View File

@ -108,51 +108,6 @@ const char** DivPlatformN163::getRegisterSheet() {
return regCheatSheetN163; 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) { void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) { for (size_t i=start; i<start+len; i++) {
n163.tick(); n163.tick();
@ -234,18 +189,9 @@ void DivPlatformN163::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.duty.had) {
if (chan[i].wavePos!=chan[i].std.duty.val) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformN163(); ~DivPlatformN163();

View File

@ -151,18 +151,6 @@ const char** DivPlatformNamcoWSG::getRegisterSheet() {
return regCheatSheetNamcoWSG; 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) { void DivPlatformNamcoWSG::acquire(short* bufL, short* bufR, size_t start, size_t len) {
while (!writes.empty()) { while (!writes.empty()) {
QueuedWrite w=writes.front(); QueuedWrite w=writes.front();
@ -218,18 +206,9 @@ void DivPlatformNamcoWSG::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformNamcoWSG(); ~DivPlatformNamcoWSG();

View File

@ -62,27 +62,6 @@ const char** DivPlatformNES::getRegisterSheet() {
return regCheatSheetNES; 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) { void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
if (useNP) { if (useNP) {
nes1_NP->Write(addr,data); nes1_NP->Write(addr,data);
@ -240,28 +219,15 @@ void DivPlatformNES::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (i==3) { // noise if (i==3) { // noise
if (chan[i].std.arp.mode) { chan[i].baseFreq=parent->calcArp(chan[i].note,chan[i].std.arp.val);
chan[i].baseFreq=chan[i].std.arp.val;
} else {
chan[i].baseFreq=chan[i].note+chan[i].std.arp.val;
}
if (chan[i].baseFreq>255) chan[i].baseFreq=255; if (chan[i].baseFreq>255) chan[i].baseFreq=255;
if (chan[i].baseFreq<0) chan[i].baseFreq=0; if (chan[i].baseFreq<0) chan[i].baseFreq=0;
} else { } else {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val; 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index); const void* getSampleMem(int index);
size_t getSampleMemCapacity(int index); size_t getSampleMemCapacity(int index);
size_t getSampleMemUsage(int index); size_t getSampleMemUsage(int index);

View File

@ -152,98 +152,6 @@ const int orderedOpsL[4]={
#define ADDR_FREQH 0xb0 #define ADDR_FREQH 0xb0
#define ADDR_LR_FB_ALG 0xc0 #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) { void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2]; static short o[2];
static int os[2]; static int os[2];
@ -389,18 +297,9 @@ void DivPlatformOPL::tick(bool sysTick) {
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { 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].std.arp.had) {
if (!chan[adpcmChan].inPorta) { if (!chan[adpcmChan].inPorta) {
if (chan[adpcmChan].std.arp.mode) { chan[adpcmChan].baseFreq=NOTE_ADPCMB(parent->calcArp(chan[adpcmChan].note,chan[adpcmChan].std.arp.val));
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].freqChanged=true; 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.had) {
if ((chan[adpcmChan].std.phaseReset.val==1) && chan[adpcmChan].active) { 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; int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
chan[c.chan].fourOp=(ops==4); chan[c.chan].fourOp=(ops==4);
if (chan[c.chan].fourOp) { 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); chan[c.chan+1].macroInit(NULL);
} }
update4OpMask=true; update4OpMask=true;
@ -1691,7 +1588,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
adpcmChan=drums?11:9; adpcmChan=drums?11:9;
} }
break; break;
case 3: case 759: case 3: case 4: case 759:
slotsNonDrums=slotsOPL3; slotsNonDrums=slotsOPL3;
slotsDrums=slotsOPL3Drums; slotsDrums=slotsOPL3Drums;
slots=drums?slotsDrums:slotsNonDrums; slots=drums?slotsDrums:slotsNonDrums;
@ -1705,6 +1602,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
pretendYMU=true; pretendYMU=true;
adpcmChan=16; adpcmChan=16;
} else if (type==4) { } else if (type==4) {
chipFreqBase=32768*684;
downsample=true; downsample=true;
} }
break; break;

View File

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

View File

@ -27,68 +27,6 @@
#define CHIP_FREQBASE 1180068 #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]={ const unsigned char cycleMapOPLL[18]={
8, 7, 6, 7, 8, 7, 8, 6, 0, 1, 2, 7, 8, 9, 3, 4, 5, 9 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { 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); int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformOPLL(); ~DivPlatformOPLL();

View File

@ -53,27 +53,6 @@ const char** DivPlatformPCE::getRegisterSheet() {
return regCheatSheetPCE; 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) { void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
// PCM part // PCM part
@ -190,28 +169,12 @@ void DivPlatformPCE::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val);
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val); chan[i].baseFreq=NOTE_PERIODIC(noiseSeek);
// 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;
if (noiseSeek<0) noiseSeek=0; if (noiseSeek<0) noiseSeek=0;
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):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].std.wave.had && !chan[i].pcm) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformPCE(); ~DivPlatformPCE();

View File

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

View File

@ -190,10 +190,6 @@ const char** DivPlatformPCSpeaker::getRegisterSheet() {
return regCheatSheetPCSpeaker; return regCheatSheetPCSpeaker;
} }
const char* DivPlatformPCSpeaker::getEffectName(unsigned char effect) {
return NULL;
}
const float cut=0.05; const float cut=0.05;
const float reso=0.06; const float reso=0.06;
@ -351,18 +347,9 @@ void DivPlatformPCSpeaker::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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.had) {
if (chan[i].std.pitch.mode) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformPCSpeaker(); ~DivPlatformPCSpeaker();

View File

@ -37,15 +37,6 @@ const char** DivPlatformPET::getRegisterSheet() {
return regCheatSheet6522; 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 // high-level emulation of 6522 shift register and driver software for now
void DivPlatformPET::rWrite(unsigned int addr, unsigned char val) { void DivPlatformPET::rWrite(unsigned int addr, unsigned char val) {
bool hwSROutput=((regPool[11]>>2)&7)==4; bool hwSROutput=((regPool[11]>>2)&7)==4;
@ -113,18 +104,9 @@ void DivPlatformPET::tick(bool sysTick) {
} }
if (chan.std.arp.had) { if (chan.std.arp.had) {
if (!chan.inPorta) { if (!chan.inPorta) {
if (chan.std.arp.mode) { chan.baseFreq=NOTE_PERIODIC(parent->calcArp(chan.note,chan.std.arp.val));
chan.baseFreq=NOTE_PERIODIC(chan.std.arp.val);
} else {
chan.baseFreq=NOTE_PERIODIC(chan.note+chan.std.arp.val);
}
} }
chan.freqChanged=true; 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.std.wave.had) {
if (chan.wave!=chan.std.wave.val) { if (chan.wave!=chan.std.wave.val) {
@ -133,8 +115,14 @@ void DivPlatformPET::tick(bool sysTick) {
} }
} }
if (chan.std.pitch.had) { 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) { if (chan.freqChanged || chan.keyOn || chan.keyOff) {
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER)-2; chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER)-2;
if (chan.freq>65535) chan.freq=65535; 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformPET(); ~DivPlatformPET();

View File

@ -249,24 +249,6 @@ const char** DivPlatformQSound::getRegisterSheet() {
return regCheatSheetQSound; 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) { void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
qsound_update(&chip); qsound_update(&chip);
@ -322,18 +304,9 @@ void DivPlatformQSound::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=QS_NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
chan[i].echo=CLAMP(chan[i].std.duty.val,0,32767); 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index = 0); const void* getSampleMem(int index = 0);
size_t getSampleMemCapacity(int index = 0); size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0); size_t getSampleMemUsage(int index = 0);

View File

@ -43,10 +43,6 @@ const char** DivPlatformRF5C68::getRegisterSheet() {
return regCheatSheetRF5C68; return regCheatSheetRF5C68;
} }
const char* DivPlatformRF5C68::getEffectName(unsigned char effect) {
return NULL;
}
void DivPlatformRF5C68::chWrite(unsigned char ch, unsigned int addr, unsigned char val) { void DivPlatformRF5C68::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
if (!skipRegisterWrites) { if (!skipRegisterWrites) {
if (curChan!=ch) { if (curChan!=ch) {
@ -88,18 +84,9 @@ void DivPlatformRF5C68::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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.had) {
if (chan[i].std.pitch.mode) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
const void* getSampleMem(int index = 0); const void* getSampleMem(int index = 0);
size_t getSampleMemCapacity(int index = 0); size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0); size_t getSampleMemUsage(int index = 0);

View File

@ -56,21 +56,6 @@ const char** DivPlatformSAA1099::getRegisterSheet() {
return regCheatSheetSAA; 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) { void DivPlatformSAA1099::acquire_saaSound(short* bufL, short* bufR, size_t start, size_t len) {
if (saaBufLen<len*2) { if (saaBufLen<len*2) {
saaBufLen=len*2; saaBufLen=len*2;
@ -114,18 +99,9 @@ void DivPlatformSAA1099::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
saaNoise[i/3]=chan[i].std.duty.val&3; 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
}; };

View File

@ -80,15 +80,6 @@ const char** DivPlatformSCC::getRegisterSheet() {
return isPlus ? regCheatSheetSCCPlus : regCheatSheetSCC; 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) { void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
for (int i=0; i<16; i++) { 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
void setFlags(unsigned int flags); void setFlags(unsigned int flags);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void setChipModel(bool isPlus); void setChipModel(bool isPlus);

View File

@ -26,15 +26,6 @@
//#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} //#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
//#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } //#define immWrite(a,v) if (!skipRegisterWrites) {writes.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) { void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int os[2]; static int os[2];
@ -97,18 +88,9 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=(parent->calcArp(chan[i].note,chan[i].std.arp.val)<<6);
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].freqChanged=true; 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) { if (chan[i].std.panL.had) {

View File

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

View File

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

View File

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

View File

@ -300,12 +300,46 @@ void SoundUnit::NextSample(short* l, short* r) {
} }
} }
*l=minval(32767,maxval(-32767,tnsL)); if (dsOut) {
*r=minval(32767,maxval(-32767,tnsR)); 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; pcmSize=sampleMemSize;
dsOut=dsOutMode;
Reset(); Reset();
memset(pcm,0,pcmSize); memset(pcm,0,pcmSize);
for (int i=0; i<256; i++) { for (int i=0; i<256; i++) {
@ -346,6 +380,8 @@ void SoundUnit::Reset() {
oldflags[i]=0; oldflags[i]=0;
pcmdec[i]=0; pcmdec[i]=0;
} }
dsCounterL=0;
dsCounterR=0;
tnsL=0; tnsL=0;
tnsR=0; tnsR=0;
ilBufPos=0; ilBufPos=0;

View File

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

View File

@ -26,76 +26,18 @@
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } #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 chWrite(c,a,v) rWrite(((c)<<5)|(a),v);
#define CHIP_DIVIDER 2
#define CHIP_FREQBASE 524288 #define CHIP_FREQBASE 524288
const char** DivPlatformSoundUnit::getRegisterSheet() { const char** DivPlatformSoundUnit::getRegisterSheet() {
return NULL; return NULL;
} }
const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) { double DivPlatformSoundUnit::NOTE_SU(int ch, int note) {
switch (effect) { if (chan[ch].switchRoles) {
case 0x10: return NOTE_PERIODIC(note);
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;
} }
return NULL; return NOTE_FREQUENCY(note);
} }
void DivPlatformSoundUnit::acquire(short* bufL, short* bufR, size_t start, size_t len) { 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_SU(i,parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.duty.had) {
chan[i].duty=chan[i].std.duty.val; chan[i].duty=chan[i].std.duty.val;
@ -190,13 +123,18 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
if (chan[i].std.ex4.had) { if (chan[i].std.ex4.had) {
chan[i].syncTimer=chan[i].std.ex4.val&65535; chan[i].syncTimer=chan[i].std.ex4.val&65535;
chan[i].timerSync=(chan[i].syncTimer>0); chan[i].timerSync=(chan[i].syncTimer>0);
chWrite(i,0x1e,chan[i].syncTimer&0xff); if (chan[i].switchRoles) {
chWrite(i,0x1f,chan[i].syncTimer>>8); 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); writeControlUpper(i);
} }
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); //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) { if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
// TODO: sample map? // 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<0) chan[i].freq=0;
if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].freq>65535) chan[i].freq=65535;
chWrite(i,0x00,chan[i].freq&0xff); if (chan[i].switchRoles) {
chWrite(i,0x01,chan[i].freq>>8); 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].keyOn) {
if (chan[i].pcm) { if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
@ -256,6 +199,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SU); 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)) { 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); chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
writeControl(c.chan); writeControl(c.chan);
@ -263,7 +207,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
} }
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample); chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
if (c.value!=DIV_NOTE_NULL) { 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].freqChanged=true;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
} }
@ -413,7 +357,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
writeControlUpper(c.chan); writeControlUpper(c.chan);
break; break;
case DIV_CMD_C64_FINE_CUTOFF: 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) { if (!chan[c.chan].std.ex1.has) {
chan[c.chan].cutoff=chan[c.chan].baseCutoff; chan[c.chan].cutoff=chan[c.chan].baseCutoff;
chWrite(c.chan,0x06,chan[c.chan].cutoff&0xff); chWrite(c.chan,0x06,chan[c.chan].cutoff&0xff);
@ -421,7 +365,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
} }
break; break;
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_FREQUENCY(c.value2); int destFreq=NOTE_SU(c.chan,c.value2);
bool return2=false; bool return2=false;
if (destFreq>chan[c.chan].baseFreq) { if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:(1+(chan[c.chan].baseFreq>>9))); 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; chan[c.chan].keyOn=true;
break; break;
case DIV_CMD_LEGATO: 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].freqChanged=true;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
break; break;
@ -461,7 +405,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
if (chan[c.chan].active && c.value2) { 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 (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; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:
@ -485,6 +429,11 @@ void DivPlatformSoundUnit::forceIns() {
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
chan[i].freqChanged=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; sampleMemSize=flags&16;
su->Init(sampleMemSize?65536:8192); su->Init(sampleMemSize?65536:8192,flags&32);
renderSamples(); renderSamples();
} }

View File

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

View File

@ -50,27 +50,6 @@ const char** DivPlatformSwan::getRegisterSheet() {
return regCheatSheetWS; 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) { void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) { for (size_t h=start; h<start+len; h++) {
// PCM part // PCM part
@ -155,18 +134,9 @@ void DivPlatformSwan::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.wave.had && !(i==1 && pcm)) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformSwan(); ~DivPlatformSwan();

View File

@ -34,15 +34,6 @@ const char* regCheatSheetTIA[]={
NULL 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() { const char** DivPlatformTIA::getRegisterSheet() {
return regCheatSheetTIA; return regCheatSheetTIA;
} }
@ -96,20 +87,18 @@ void DivPlatformTIA::tick(bool sysTick) {
rWrite(0x19+i,chan[i].outVol&15); 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { if (chan[i].std.arp.val<0 && (!(chan[i].std.arp.val&0x40000000))) {
chan[i].baseFreq=0x80000000|chan[i].std.arp.val; 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 { } else {
chan[i].baseFreq=(chan[i].note+chan[i].std.arp.val)<<8; chan[i].baseFreq=(chan[i].note+chan[i].std.arp.val)<<8;
} }
} }
chan[i].freqChanged=true; 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) { if (chan[i].std.wave.had) {
chan[i].shape=chan[i].std.wave.val&15; chan[i].shape=chan[i].std.wave.val&15;
@ -126,13 +115,6 @@ void DivPlatformTIA::tick(bool sysTick) {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { 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; 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].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) {
if (chan[i].baseFreq<39*256) { 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) { if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol; 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]) { if (isMuted[c.chan]) {
rWrite(0x19+c.chan,0); rWrite(0x19+c.chan,0);
} else { } else {

View File

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

View File

@ -55,139 +55,6 @@ const char** DivPlatformTX81Z::getRegisterSheet() {
return regCheatSheetOPZ; 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) { void DivPlatformTX81Z::acquire(short* bufL, short* bufR, size_t start, size_t len) {
static int os[2]; static int os[2];
@ -256,18 +123,9 @@ void DivPlatformTX81Z::tick(bool sysTick) {
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_LINEAR(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformTX81Z(); ~DivPlatformTX81Z();

View File

@ -51,18 +51,6 @@ const char** DivPlatformVERA::getRegisterSheet() {
return regCheatSheetVERA; 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) { 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 // both PSG part and PCM part output a full 16-bit range, putting bufL/R
// argument right into both could cause an overflow // 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].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=calcNoteFreq(0,parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
rWriteLo(i,3,chan[i].std.duty.val); 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].std.arp.had) {
if (!chan[16].inPorta) { if (!chan[16].inPorta) {
if (chan[16].std.arp.mode) { chan[16].baseFreq=calcNoteFreq(16,parent->calcArp(chan[16].note,chan[16].std.arp.val));
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].freqChanged=true; 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) { if (chan[16].freqChanged) {
double off=65536.0; double off=65536.0;

View File

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

View File

@ -39,15 +39,6 @@ const char** DivPlatformVIC20::getRegisterSheet() {
return regCheatSheetVIC; 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) { void DivPlatformVIC20::acquire(short* bufL, short* bufR, size_t start, size_t len) {
const unsigned char loadFreq[3] = {0x7e, 0x7d, 0x7b}; const unsigned char loadFreq[3] = {0x7e, 0x7d, 0x7b};
const unsigned char wavePatterns[16] = { const unsigned char wavePatterns[16] = {
@ -103,18 +94,9 @@ void DivPlatformVIC20::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val) { 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
~DivPlatformVIC20(); ~DivPlatformVIC20();

View File

@ -46,18 +46,6 @@ const char** DivPlatformVRC6::getRegisterSheet() {
return regCheatSheetVRC6; 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) { void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t i=start; i<start+len; i++) { for (size_t i=start; i<start+len; i++) {
// PCM part // PCM part
@ -165,18 +153,9 @@ void DivPlatformVRC6::tick(bool sysTick) {
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
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].freqChanged=true; 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) { if (chan[i].std.duty.had) {
chan[i].duty=chan[i].std.duty.val; 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(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet(); const char** getRegisterSheet();
const char* getEffectName(unsigned char effect);
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
void quit(); void quit();
DivPlatformVRC6() : vrc6(intf) {}; DivPlatformVRC6() : vrc6(intf) {};

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