Merge branch 'master' into doc-general

This commit is contained in:
Electric Keet 2023-08-26 10:33:01 -07:00
commit 9a9b69c319
27 changed files with 267 additions and 156 deletions

View file

@ -284,12 +284,6 @@ if (USE_SDL2)
# This should probably go in a FAQ. # This should probably go in a FAQ.
set(SDL_LIBC ON CACHE BOOL "Tell SDL that we want it to use our C runtime (required for proper static linking)" FORCE) set(SDL_LIBC ON CACHE BOOL "Tell SDL that we want it to use our C runtime (required for proper static linking)" FORCE)
# https://github.com/tildearrow/furnace/issues/1237
# enabling this will result in SDL finding the Direct3D headers, forcing _WIN32_WINNT to an undesirable value (which makes the Wine headers define GetTickCount64)
if (SUPPORT_XP)
set(SDL_RENDER_D3D OFF CACHE BOOL "Enable the Direct3D render driver" FORCE)
endif()
add_subdirectory(extern/SDL EXCLUDE_FROM_ALL) add_subdirectory(extern/SDL EXCLUDE_FROM_ALL)
list(APPEND DEPENDENCIES_DEFINES HAVE_SDL2) list(APPEND DEPENDENCIES_DEFINES HAVE_SDL2)
list(APPEND DEPENDENCIES_INCLUDE_DIRS extern/SDL/include) list(APPEND DEPENDENCIES_INCLUDE_DIRS extern/SDL/include)

View file

@ -15,8 +15,8 @@ android {
} }
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 26 targetSdkVersion 26
versionCode 166 versionCode 169
versionName "0.6pre8" versionName "0.6pre9"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static", "-DWARNINGS_ARE_ERRORS=ON" arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static", "-DWARNINGS_ARE_ERRORS=ON"

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.tildearrow.furnace" package="org.tildearrow.furnace"
android:versionCode="166" android:versionCode="169"
android:versionName="0.6pre8" android:versionName="0.6pre9"
android:installLocation="auto"> android:installLocation="auto">
<!-- OpenGL ES 2.0 --> <!-- OpenGL ES 2.0 -->

View file

@ -1,32 +1,27 @@
# settings # settings
settings are saved when clicking the **OK** button at the bottom of the dialog. settings are saved when clicking the **OK** or **Apply** buttons at the bottom of the dialog.
## General ## General
### Program ### Program
- **Render backend** - **Render backend**: changing this may help with performace issues.
- changing this may help with performace issues. - **Late render clear**: this option is only useful when using old versions of Mesa drivers. it force-waits for VBlank by clearing after present, reducing latency.
- **Late render clear**
- **Power-saving mode**: saves power by lowering the frame rate to 2fps when idle. - **Power-saving mode**: saves power by lowering the frame rate to 2fps when idle.
- may cause issues under Mesa drivers! - may cause issues under Mesa drivers!
- **Disable threaded input (restart after changing!)**: processes key presses for note preview on a separate thread (on supported platforms), which reduces latency. - **Disable threaded input (restart after changing!)**: processes key presses for note preview on a separate thread (on supported platforms), which reduces latency.
- however, crashes have been reported when threaded input is on. enable this option if that is the case. - however, crashes have been reported when threaded input is on. enable this option if that is the case.
- **Enable event delay** - **Enable event delay**: may cause issues with high-polling-rate mice when previewing notes.
- may cause issues with high-polling-rate mice when previewing notes.
### File ### File
- **Use system file picker**: uses native OS file dialog instead of Furnace's. - **Use system file picker**: uses native OS file dialog instead of Furnace's.
- **Number of recent files**: number of files to show in the _open recent..._ menu. - **Number of recent files**: number of files that will be remembered in the _open recent..._ menu.
- **Compress when saving** - **Compress when saving**: uses zlib to compress saved songs.
- uses zlib to compress saved songs. - **Save unused patterns**: stores unused patterns in a saved song.
- **Save unused patterns** - **Use new pattern format when saving**: stores patterns in the new, optimized and smaller format. only disable if you need to work with older versions of Furnace.
- **Use new pattern format when saving** - **Don't apply compatibility flags when loading .dmf**: does exactly what the option says. your .dmf songs may not play correctly after enabled.
- **Don't apply compatibility flags when loading .dmf**
- **Play after opening song:** - **Play after opening song:**
- No - No
- Only if already playing - Only if already playing
@ -73,22 +68,24 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **Backend**: selects SDL or JACK for audio output. - **Backend**: selects SDL or JACK for audio output.
- only appears on Linux, or MacOS compiled with JACK support - only appears on Linux, or MacOS compiled with JACK support
- **Driver** - **Driver**: select a different SDL audio driver if you're having problems with the default one.
- **Device**: audio device for playback. - **Device**: audio device for playback.
- **Sample rate** - **Sample rate**
- **Outputs**: number of audio outputs created, up to 16. - **Outputs**: number of audio outputs created, up to 16.
- only appears when Backend is JACK. - only appears when Backend is JACK.
- **Channels**: number of output channels to use. - **Channels**: number of output channels to use.
- **Buffer size**: size of buffer in both samples and milliseconds. - **Buffer size**: size of buffer in both samples and milliseconds.
- setting this to a low value may cause stuttering/glitches in playback (known as "underruns" or "xruns").
- setting this to a high value increases latency.
- **Low-latency mode (experimental!)**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode. - **Low-latency mode (experimental!)**: reduces latency by running the engine faster than the tick rate. useful for live playback/jam mode.
- _warning:_ experimental! may produce glitches. only enable if your buffer size is small (10ms or less). - only enable if your buffer size is small (10ms or less).
- **Force mono audio** - **Force mono audio**: use if you're unable to hear stereo audio (e.g. single speaker or hearing loss in one ear).
- **want:** displays requested audio configuration. - **want:** displays requested audio configuration.
- **got:** displays actual audio configuration returned by audio backend. - **got:** displays actual audio configuration returned by audio backend.
### Mixing ### Mixing
- **Quality**: selects quality of resampling. low quality reduces CPU load. - **Quality**: selects quality of resampling. low quality reduces CPU load by a small amount.
- **Software clipping**: clips output to nominal range (-1.0 to 1.0) before passing it to the audio device. - **Software clipping**: clips output to nominal range (-1.0 to 1.0) before passing it to the audio device.
- this avoids activating Windows' built-in limiter. - this avoids activating Windows' built-in limiter.
@ -96,32 +93,39 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **Metronome volume** - **Metronome volume**
## MIDI ## MIDI
### MIDI input ### MIDI input
- **MIDI input**: input device. - **MIDI input**: input device.
- **Note input** - **Note input**: enables note input. disable if you intend to use this device only for binding actions.
- **Velocity input** - **Velocity input**: enables velocity input when entering notes in the pattern.
- **Map MIDI channels to direct channels** - **Map MIDI channels to direct channels**: when enabled, notes from MIDI channels will be mapped to channels rather than the cursor position.
- **Map Yamaha FM voice data to instruments** - **Map Yamaha FM voice data to instruments**: when enabled, Furnace will listen for any transmitted Yamaha SysEx patches.
- **Program change is instrument selection** - this option is only useful if you have a Yamaha FM synthesizer (e.g. TX81Z).
- **Value input style**: - selecting a voice or using the "Voice Transmit?" option will send a patch, and Furnace will create a new instrument with its data.
- **Disabled/custom** - this may also be triggered by clicking on "Receive from TX81Z" in the instrument editor (OPZ only).
- **Two octaves (0 is C-4, F is D#5)** - **Program change is instrument selection**: changes the current instrument when a program change event is received.
- **Raw (note number is value)** - **Value input style**: changes the way values are entered when the pattern cursor is not in the Note column. the following styles are available:
- **Two octaves alternate (lower keys are 0-9, upper keys are A-F)** - **Disabled/custom**: no value input through MIDI.
- **Use dual control change (one for each nibble)** - **Two octaves (0 is C-4, F is D#5)**: maps keys in two octaves to single nibble input. the layout is:
- **CC of upper nibble** - ` - octave n -- octave n+1 -`
- **CC of lower nibble** - ` 1 3 6 8 A D F # # # `
- **Use 14-bit control change** - `0 2 4 5 7 9 B C E # # # # #`
- **MSB CC** - **Raw (note number is value)**: the note number becomes the input value. not useful if you want to input anything above 7F.
- **LSB CC** - **Two octaves alternate (lower keys are 0-9, upper keys are A-F)**: maps keys in two octaves, but with a different layout:
- **Use single control change** - ` - octave n -- octave n+1 -`
- **Control** - ` A B C D E F # # # # `
- **Per-column control change** - `0 1 2 3 4 5 6 7 8 9 # # # #`
- **Use dual control change (one for each nibble)**: maps two control change events to the nibbles of a value.
- **CC of upper nibble**: select the CC number that will change the upper nibble.
- **CC of lower nibble**: select the CC number that will change the lower nibble.
- **Use 14-bit control change**: maps two control change events that together form a single 14-bit CC. some MIDI controllers do these.
- **MSB CC**: select the CC containing the upper portion of the control.
- **LSB CC**: select the CC containing the lower portion of the control.
- **Use single control change**: maps one control change event. not useful if you want to input odd numbers.
- **Control**: select the CC number that will change the value.
- **Per-column control change**: when enabled, you can map several control change events to a channel's columns.
- **Instrument**\ - **Instrument**\
**Volume**\ **Volume**\
**Effect `x` type**\ **Effect `x` type**\
@ -135,36 +139,34 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **LSB CC** - **LSB CC**
- **Use single control change (imprecise)** - **Use single control change (imprecise)**
- **Control** - **Control**
- **Volume curve** - **Volume curve**: adjust the velocity to volume curve.
- **Actions:** - **Actions**: this allows you to bind note input and control change events to actions.
- **`+`** button: adds a new action. - **`+`** button: adds a new action.
- window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device. - window-with-arrow button: new action with learning! press a button or move a slider/knob/something on your device.
- each action has the following: - each action has the following:
- **Type** - **Type**: type of event.
- **Channel** - **Channel**: channel of event.
- **Note/Control** - **Note/Control**: the note/control change number.
- **Velocity/Value** - **Velocity/Value**: the velocity or control value
- **Action** - **Action**: the GUI action to perform.
- **Learn** - **Learn**: after clicking on this button, do something in your MIDI device and Furnace will map that to this action.
- **Remove** - **Remove**: remove this action.
### MIDI output ### MIDI output
- **MIDI output**: output device. - **MIDI output**: output device.
- **Output mode:** - **Output mode:**
- **Off (use for TX81Z)** - **Off (use for TX81Z)**: don't output anything. use if you plan to use Furnace as sync master, or the "Receive from TX81Z" button in the OPZ instrument editor.
- **Melodic** - **Melodic**: output MIDI events.
- **Send Program Change** - **Send Program Change**: output program change events when instrument change commands occur.
- **Send MIDI clock** - **Send MIDI clock**: output MIDI beat clock.
- **Send MIDI timecode** - **Send MIDI timecode**: output MIDI timecode.
- **Timecode frame rate:** - **Timecode frame rate**: sets the timing standard used for MIDI timecode.
- **Closest to Tick Rate** - **Closest to Tick Rate**: automatically sets the rate based on the song's Tick Rate.
- **Film (24fps)** - **Film (24fps)**: output at 24 codes per second.
- **PAL (25fps)** - **PAL (25fps)**: output at 25 codes per second.
- **NTSC drop (29.97fps)** - **NTSC drop (29.97fps)**: output at ~29.97 codes per second, skipping frames 0 and 1 of each minute that doesn't divide by 10.
- **NTSC non-drop (30fps)** - **NTSC non-drop (30fps)**: output at 30 codes per second.
## Emulation ## Emulation
@ -184,13 +186,6 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **PC Speaker strategy**: this is covered in the [PC speaker system doc](../7-systems/pcspkr.md). - **PC Speaker strategy**: this is covered in the [PC speaker system doc](../7-systems/pcspkr.md).
- **Sample ROMs:**
- **OPL4 YRW801 path**
- **MultiPCM TG100 path**
- **MultiPCM MU5 path**
## Keyboard ## Keyboard
### Keyboard ### Keyboard
@ -417,10 +412,8 @@ settings are saved when clicking the **OK** button at the bottom of the dialog.
- **Macro editor layout:** - **Macro editor layout:**
- **Unified** - **Unified**
- **Mobile**
- **Grid** - **Grid**
- **Single (with list)** - **Single (with list)**
- **Single (combo box)**
- **Use classic macro editor vertical slider** - **Use classic macro editor vertical slider**
### Wave Editor ### Wave Editor

View file

@ -1,6 +1,8 @@
# TI SN76489 (e.g. Sega Master System) # TI SN76489 (e.g. Sega Master System)
a relatively simple sound chip made by Texas Instruments. a derivative of it is used in Sega's Master System, the predecessor to Genesis. a relatively simple sound chip made by Texas Instruments. a derivative of it is used in Sega's Master System, the predecessor to Genesis. It has three square wave channels and one noise channel... not really.
Nominal mode of SN76489 has 3 quare wave channels, with noise channel having only 3 preset frequencies to use (absurdly low, very low, low). To use more pitches, one can enable mode, which "steals" pitch data from square wave channel 3. By doing that, SN76489 becomes effectively a 3 channel sound chip. In addition, periodic noise mode can be enabled, with same caveats.
the original iteration of the SN76489 used in the TI-99/4A computer, the SN94624, could only produce tones as low as 100Hz, and was clocked at 447 KHz. all later versions (such as the one in the Master System and Genesis) had a clock divider but ran on a faster clock... except for the SN76494, which can play notes as low as 13.670 Hz (A -1). consequently, its pitch accuracy for higher notes is compromised. the original iteration of the SN76489 used in the TI-99/4A computer, the SN94624, could only produce tones as low as 100Hz, and was clocked at 447 KHz. all later versions (such as the one in the Master System and Genesis) had a clock divider but ran on a faster clock... except for the SN76494, which can play notes as low as 13.670 Hz (A -1). consequently, its pitch accuracy for higher notes is compromised.

View file

@ -61,7 +61,7 @@ class NFDWinEvents: public IFileDialogEvents {
return ret; return ret;
} }
IFACEMETHODIMP OnFileOk(IFileDialog*) { return E_NOTIMPL; } IFACEMETHODIMP OnFileOk(IFileDialog*) { return S_OK; }
IFACEMETHODIMP OnFolderChange(IFileDialog*) { return E_NOTIMPL; } IFACEMETHODIMP OnFolderChange(IFileDialog*) { return E_NOTIMPL; }
IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return E_NOTIMPL; } IFACEMETHODIMP OnFolderChanging(IFileDialog*, IShellItem*) { return E_NOTIMPL; }
IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return E_NOTIMPL; } IFACEMETHODIMP OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return E_NOTIMPL; }
@ -677,6 +677,21 @@ nfdresult_t NFD_SaveDialog( const std::vector<std::string>& filterList,
goto end; goto end;
} }
// Set a flag for no history
DWORD dwFlags;
result = fileSaveDialog->GetOptions(&dwFlags);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not get options.");
goto end;
}
result = fileSaveDialog->SetOptions(dwFlags | FOS_DONTADDTORECENT);
if ( !SUCCEEDED(result) )
{
NFDi_SetError("Could not set options.");
goto end;
}
// Show the dialog. // Show the dialog.
result = fileSaveDialog->Show(NULL); result = fileSaveDialog->Show(NULL);
if ( SUCCEEDED(result) ) if ( SUCCEEDED(result) )

View file

@ -67,7 +67,7 @@ void k007232_core::voice_t::tick(u8 ne)
} }
} }
m_out = s8(m_data) - 0x40; // send to output (ASD/BSD) pin m_out = s8(m_data&0x7f) - 0x40; // send to output (ASD/BSD) pin
} }
else else
{ {

View file

@ -6,7 +6,7 @@ when copying pattern data from Furnace, it's stored in the clipboard as plain te
org.tildearrow.furnace - Pattern Data (144) org.tildearrow.furnace - Pattern Data (144)
``` ```
this top line of text is always the same except for the number in parentheses, which is the internal build number. for example, 0.6pre8 is `166`. this top line of text is always the same except for the number in parentheses, which is the internal build number. for example, 0.6pre9 is `169`.
the second line is a number between 0 and 18 (decimal) which indicates which column the clip starts from. the second line is a number between 0 and 18 (decimal) which indicates which column the clip starts from.
- `0`: note. - `0`: note.

View file

@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are: the format versions are:
- 169: Furnace 0.6pre9
- 166: Furnace 0.6pre8 - 166: Furnace 0.6pre8
- 162: Furnace 0.6pre7 - 162: Furnace 0.6pre7
- 161: Furnace 0.6pre6 - 161: Furnace 0.6pre6

View file

@ -15,17 +15,17 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleLongVersionString</key> <key>CFBundleLongVersionString</key>
<string>0.6pre8</string> <string>0.6pre9</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Furnace</string> <string>Furnace</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.6pre8</string> <string>0.6pre9</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.6pre8</string> <string>0.6pre9</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string></string> <string></string>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>

View file

@ -303,7 +303,7 @@ void DivEngine::notifyWaveChange(int wave) {
} }
int DivEngine::loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret) { int DivEngine::loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret) {
ret = NULL; ret=NULL;
if (path.empty()) { if (path.empty()) {
return 0; return 0;
} }
@ -358,7 +358,7 @@ int DivEngine::loadSampleROM(String path, ssize_t expectedSize, unsigned char*&
return -1; return -1;
} }
fclose(f); fclose(f);
ret = file; ret=file;
return 0; return 0;
} }
@ -385,10 +385,10 @@ int DivEngine::loadSampleROMs() {
delete[] mu5ROM; delete[] mu5ROM;
mu5ROM=NULL; mu5ROM=NULL;
} }
int error = 0; int error=0;
error += loadSampleROM(getConfString("yrw801Path",""), 0x200000, yrw801ROM); error+=loadSampleROM(getConfString("yrw801Path",""), 0x200000, yrw801ROM);
error += loadSampleROM(getConfString("tg100Path",""), 0x200000, tg100ROM); error+=loadSampleROM(getConfString("tg100Path",""), 0x200000, tg100ROM);
error += loadSampleROM(getConfString("mu5Path",""), 0x200000, mu5ROM); error+=loadSampleROM(getConfString("mu5Path",""), 0x200000, mu5ROM);
return error; return error;
} }
@ -950,12 +950,13 @@ bool DivEngine::addSystem(DivSystem which) {
unsigned int outs=disCont[i].dispatch->getOutputCount(); unsigned int outs=disCont[i].dispatch->getOutputCount();
if (outs>16) outs=16; if (outs>16) outs=16;
if (outs<2) { if (outs<2) {
song.patchbay.reserve(DIV_MAX_OUTPUTS);
for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) { for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) {
song.patchbay.push_back((i<<20)|j); song.patchbay.push_back((i<<20)|j);
} }
} else { } else {
song.patchbay.reserve(outs);
for (unsigned int j=0; j<outs; j++) { for (unsigned int j=0; j<outs; j++) {
song.patchbay.push_back((i<<20)|(j<<16)|j); song.patchbay.push_back((i<<20)|(j<<16)|j);
} }
} }
@ -1062,9 +1063,12 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
// prepare swap list // prepare swap list
int index=0; int index=0;
swapList.reserve(song.systemLen);
for (int i=0; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
chanList.clear(); chanList.clear();
for (int j=0; j<getChannelCount(song.system[i]); j++) { const int channelCount=getChannelCount(song.system[i]);
chanList.reserve(channelCount);
for (int j=0; j<channelCount; j++) {
chanList.push_back(index); chanList.push_back(index);
index++; index++;
} }
@ -1319,6 +1323,7 @@ void DivEngine::enableCommandStream(bool enable) {
void DivEngine::getCommandStream(std::vector<DivCommand>& where) { void DivEngine::getCommandStream(std::vector<DivCommand>& where) {
BUSY_BEGIN; BUSY_BEGIN;
where.clear(); where.clear();
where.reserve(cmdStream.size());
for (DivCommand& i: cmdStream) { for (DivCommand& i: cmdStream) {
where.push_back(i); where.push_back(i);
} }
@ -2822,23 +2827,26 @@ void DivEngine::autoPatchbay() {
unsigned int outs=disCont[i].dispatch->getOutputCount(); unsigned int outs=disCont[i].dispatch->getOutputCount();
if (outs>16) outs=16; if (outs>16) outs=16;
if (outs<2) { if (outs<2) {
song.patchbay.reserve(DIV_MAX_OUTPUTS);
for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) { for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) {
song.patchbay.push_back((i<<20)|j); song.patchbay.push_back((i<<20)|j);
} }
} else { } else {
song.patchbay.reserve(outs);
for (unsigned int j=0; j<outs; j++) { for (unsigned int j=0; j<outs; j++) {
song.patchbay.push_back((i<<20)|(j<<16)|j); song.patchbay.push_back((i<<20)|(j<<16)|j);
} }
} }
} }
// wave/sample preview // wave/sample preview
song.patchbay.reserve(DIV_MAX_OUTPUTS);
for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) { for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) {
song.patchbay.push_back(0xffd00000|j); song.patchbay.push_back(0xffd00000|j);
} }
// metronome // metronome
song.patchbay.reserve(DIV_MAX_OUTPUTS);
for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) { for (unsigned int j=0; j<DIV_MAX_OUTPUTS; j++) {
song.patchbay.push_back(0xffe00000|j); song.patchbay.push_back(0xffe00000|j);
} }

View file

@ -54,10 +54,10 @@
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock(); #define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false; #define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
#define DIV_UNSTABLE //#define DIV_UNSTABLE
#define DIV_VERSION "dev168" #define DIV_VERSION "0.6pre9"
#define DIV_ENGINE_VERSION 168 #define DIV_ENGINE_VERSION 169
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02 #define DIV_VERSION_FC 0xff02

View file

@ -266,6 +266,7 @@ std::vector<DivROMExportOutput> DivExportAmigaValidation::go(DivEngine* e) {
} }
// finish // finish
ret.reserve(5);
ret.push_back(DivROMExportOutput("sbook.bin",sbook)); ret.push_back(DivROMExportOutput("sbook.bin",sbook));
ret.push_back(DivROMExportOutput("wbook.bin",wbook)); ret.push_back(DivROMExportOutput("wbook.bin",wbook));
ret.push_back(DivROMExportOutput("sample.bin",sample)); ret.push_back(DivROMExportOutput("sample.bin",sample));

View file

@ -342,6 +342,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.insLen=16; ds.insLen=16;
} }
logI("reading instruments (%d)...",ds.insLen); logI("reading instruments (%d)...",ds.insLen);
ds.ins.reserve(ds.insLen);
for (int i=0; i<ds.insLen; i++) { for (int i=0; i<ds.insLen; i++) {
DivInstrument* ins=new DivInstrument; DivInstrument* ins=new DivInstrument;
unsigned char mode=0; unsigned char mode=0;
@ -669,6 +670,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
if (ds.version>0x0b) { if (ds.version>0x0b) {
ds.waveLen=(unsigned char)reader.readC(); ds.waveLen=(unsigned char)reader.readC();
logI("reading wavetables (%d)...",ds.waveLen); logI("reading wavetables (%d)...",ds.waveLen);
ds.wave.reserve(ds.waveLen);
for (int i=0; i<ds.waveLen; i++) { for (int i=0; i<ds.waveLen; i++) {
DivWavetable* wave=new DivWavetable; DivWavetable* wave=new DivWavetable;
wave->len=(unsigned char)reader.readI(); wave->len=(unsigned char)reader.readI();
@ -838,6 +840,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
// it appears this byte stored the YMU759 sample rate // it appears this byte stored the YMU759 sample rate
ymuSampleRate=reader.readC(); ymuSampleRate=reader.readC();
} }
ds.sample.reserve(ds.sampleLen);
for (int i=0; i<ds.sampleLen; i++) { for (int i=0; i<ds.sampleLen; i++) {
DivSample* sample=new DivSample; DivSample* sample=new DivSample;
int length=reader.readI(); int length=reader.readI();
@ -2127,6 +2130,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
for (int i=0; i<ds.sampleLen; i++) { for (int i=0; i<ds.sampleLen; i++) {
samplePtr[i]=reader.readI(); samplePtr[i]=reader.readI();
} }
patPtr.reserve(numberOfPats);
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);
@ -2345,6 +2349,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
// patchbay // patchbay
unsigned int conns=reader.readI(); unsigned int conns=reader.readI();
ds.patchbay.reserve(conns);
for (unsigned int i=0; i<conns; i++) { for (unsigned int i=0; i<conns; i++) {
ds.patchbay.push_back((unsigned int)reader.readI()); ds.patchbay.push_back((unsigned int)reader.readI());
} }
@ -2377,6 +2382,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
// grooves // grooves
unsigned char grooveCount=reader.readC(); unsigned char grooveCount=reader.readC();
ds.grooves.reserve(grooveCount);
for (int i=0; i<grooveCount; i++) { for (int i=0; i<grooveCount; i++) {
DivGroovePattern gp; DivGroovePattern gp;
gp.len=reader.readC(); gp.len=reader.readC();
@ -2477,6 +2483,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
// read subsongs // read subsongs
if (ds.version>=95) { if (ds.version>=95) {
ds.subsong.reserve(numberOfSubSongs);
for (int i=0; i<numberOfSubSongs; i++) { for (int i=0; i<numberOfSubSongs; i++) {
ds.subsong.push_back(new DivSubSong); ds.subsong.push_back(new DivSubSong);
if (!reader.seek(subSongPtr[i],SEEK_SET)) { if (!reader.seek(subSongPtr[i],SEEK_SET)) {
@ -2558,6 +2565,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} }
// read instruments // read instruments
ds.ins.reserve(ds.insLen);
for (int i=0; i<ds.insLen; i++) { for (int i=0; i<ds.insLen; i++) {
DivInstrument* ins=new DivInstrument; DivInstrument* ins=new DivInstrument;
logD("reading instrument %d at %x...",i,insPtr[i]); logD("reading instrument %d at %x...",i,insPtr[i]);
@ -2582,6 +2590,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} }
// read wavetables // read wavetables
ds.wave.reserve(ds.waveLen);
for (int i=0; i<ds.waveLen; i++) { for (int i=0; i<ds.waveLen; i++) {
DivWavetable* wave=new DivWavetable; DivWavetable* wave=new DivWavetable;
logD("reading wavetable %d at %x...",i,wavePtr[i]); logD("reading wavetable %d at %x...",i,wavePtr[i]);
@ -2606,6 +2615,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} }
// read samples // read samples
ds.sample.reserve(ds.sampleLen);
for (int i=0; i<ds.sampleLen; i++) { for (int i=0; i<ds.sampleLen; i++) {
DivSample* sample=new DivSample; DivSample* sample=new DivSample;
@ -3069,6 +3079,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
// samples // samples
logD("reading samples... (%d)",insCount); logD("reading samples... (%d)",insCount);
ds.sample.reserve(insCount);
for (int i=0; i<insCount; i++) { for (int i=0; i<insCount; i++) {
DivSample* sample=new DivSample; DivSample* sample=new DivSample;
sample->depth=DIV_SAMPLE_DEPTH_8BIT; sample->depth=DIV_SAMPLE_DEPTH_8BIT;
@ -3373,6 +3384,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
} }
// instrument creation // instrument creation
ds.ins.reserve(insCount);
for(int i=0; i<insCount; i++) { for(int i=0; i<insCount; i++) {
DivInstrument* ins=new DivInstrument; DivInstrument* ins=new DivInstrument;
ins->type=DIV_INS_AMIGA; ins->type=DIV_INS_AMIGA;
@ -3651,6 +3663,7 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) {
} }
// load instruments/samples // load instruments/samples
ds.ins.reserve(ds.insLen);
for (int i=0; i<ds.insLen; i++) { for (int i=0; i<ds.insLen; i++) {
DivInstrument* ins=new DivInstrument; DivInstrument* ins=new DivInstrument;
if (!reader.seek(0x4c+insPtr[i]*16,SEEK_SET)) { if (!reader.seek(0x4c+insPtr[i]*16,SEEK_SET)) {
@ -3877,6 +3890,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
// sequences // sequences
seqLen/=13; seqLen/=13;
logD("reading sequences... (%d)",seqLen); logD("reading sequences... (%d)",seqLen);
seq.reserve(seqLen);
for (unsigned int i=0; i<seqLen; i++) { for (unsigned int i=0; i<seqLen; i++) {
FCSequence s; FCSequence s;
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
@ -3906,6 +3920,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
} }
patLen/=64; patLen/=64;
logD("reading patterns... (%d)",patLen); logD("reading patterns... (%d)",patLen);
pat.reserve(patLen);
for (unsigned int i=0; i<patLen; i++) { for (unsigned int i=0; i<patLen; i++) {
FCPattern p; FCPattern p;
logV("- pattern %d",i); logV("- pattern %d",i);
@ -3926,6 +3941,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
} }
freqMacroLen/=64; freqMacroLen/=64;
logD("reading freq sequences... (%d)",freqMacroLen); logD("reading freq sequences... (%d)",freqMacroLen);
freqMacros.reserve(freqMacroLen);
for (unsigned int i=0; i<freqMacroLen; i++) { for (unsigned int i=0; i<freqMacroLen; i++) {
FCMacro m; FCMacro m;
reader.read(m.val,64); reader.read(m.val,64);
@ -3941,6 +3957,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
} }
volMacroLen/=64; volMacroLen/=64;
logD("reading volume sequences... (%d)",volMacroLen); logD("reading volume sequences... (%d)",volMacroLen);
volMacros.reserve(volMacroLen);
for (unsigned int i=0; i<volMacroLen; i++) { for (unsigned int i=0; i<volMacroLen; i++) {
FCMacro m; FCMacro m;
reader.read(m.val,64); reader.read(m.val,64);
@ -3955,6 +3972,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
return false; return false;
} }
logD("reading samples..."); logD("reading samples...");
ds.sample.reserve(10);
for (int i=0; i<10; i++) { for (int i=0; i<10; i++) {
DivSample* s=new DivSample; DivSample* s=new DivSample;
s->depth=DIV_SAMPLE_DEPTH_8BIT; s->depth=DIV_SAMPLE_DEPTH_8BIT;
@ -3981,6 +3999,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
return false; return false;
} }
logD("reading wavetables..."); logD("reading wavetables...");
ds.wave.reserve(80);
for (int i=0; i<80; i++) { for (int i=0; i<80; i++) {
DivWavetable* w=new DivWavetable; DivWavetable* w=new DivWavetable;
w->min=0; w->min=0;
@ -4009,6 +4028,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
} }
} else { } else {
// generate preset waves // generate preset waves
ds.wave.reserve(48);
for (int i=0; i<48; i++) { for (int i=0; i<48; i++) {
DivWavetable* w=new DivWavetable; DivWavetable* w=new DivWavetable;
generateFCPresetWave(i,w); generateFCPresetWave(i,w);
@ -4156,6 +4176,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
// volume sequence // volume sequence
ins->std.volMacro.len=0; ins->std.volMacro.len=0;
ds.ins.reserve(64 - 5);
for (int j=5; j<64; j++) { for (int j=5; j<64; j++) {
loopMap[j]=ins->std.volMacro.len; loopMap[j]=ins->std.volMacro.len;
if (m.val[j]==0xe1) { // end if (m.val[j]==0xe1) { // end
@ -4553,6 +4574,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
CHECK_BLOCK_VERSION(4); CHECK_BLOCK_VERSION(4);
unsigned char totalSongs=reader.readC(); unsigned char totalSongs=reader.readC();
logV("%d songs:",totalSongs+1); logV("%d songs:",totalSongs+1);
ds.subsong.reserve(totalSongs);
for (int i=0; i<=totalSongs; i++) { for (int i=0; i<=totalSongs; i++) {
String subSongName=reader.readString(); String subSongName=reader.readString();
ds.subsong.push_back(new DivSubSong); ds.subsong.push_back(new DivSubSong);
@ -5087,12 +5109,14 @@ DivDataErrors DivEngine::readAssetDirData(SafeReader& reader, std::vector<DivAss
unsigned int numDirs=reader.readI(); unsigned int numDirs=reader.readI();
dir.reserve(numDirs);
for (unsigned int i=0; i<numDirs; i++) { for (unsigned int i=0; i<numDirs; i++) {
DivAssetDir d; DivAssetDir d;
d.name=reader.readString(); d.name=reader.readString();
unsigned short numEntries=reader.readS(); unsigned short numEntries=reader.readS();
d.entries.reserve(numEntries);
for (unsigned short j=0; j<numEntries; j++) { for (unsigned short j=0; j<numEntries; j++) {
d.entries.push_back(((unsigned char)reader.readC())); d.entries.push_back(((unsigned char)reader.readC()));
} }
@ -5425,6 +5449,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
w->seek(0,SEEK_END); w->seek(0,SEEK_END);
/// SUBSONGS /// SUBSONGS
subSongPtr.reserve(song.subsong.size() - 1);
for (subSongIndex=1; subSongIndex<song.subsong.size(); subSongIndex++) { for (subSongIndex=1; subSongIndex<song.subsong.size(); subSongIndex++) {
subSong=song.subsong[subSongIndex]; subSong=song.subsong[subSongIndex];
subSongPtr.push_back(w->tell()); subSongPtr.push_back(w->tell());
@ -5486,6 +5511,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
} }
/// CHIP FLAGS /// CHIP FLAGS
sysFlagsPtr.reserve(song.systemLen);
for (int i=0; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
String data=song.systemFlags[i].toString(); String data=song.systemFlags[i].toString();
if (data.empty()) { if (data.empty()) {
@ -5515,6 +5541,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
putAssetDirData(w,song.sampleDir); putAssetDirData(w,song.sampleDir);
/// INSTRUMENT /// INSTRUMENT
insPtr.reserve(song.insLen);
for (int i=0; i<song.insLen; i++) { for (int i=0; i<song.insLen; i++) {
DivInstrument* ins=song.ins[i]; DivInstrument* ins=song.ins[i];
insPtr.push_back(w->tell()); insPtr.push_back(w->tell());
@ -5522,6 +5549,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
} }
/// WAVETABLE /// WAVETABLE
wavePtr.reserve(song.waveLen);
for (int i=0; i<song.waveLen; i++) { for (int i=0; i<song.waveLen; i++) {
DivWavetable* wave=song.wave[i]; DivWavetable* wave=song.wave[i];
wavePtr.push_back(w->tell()); wavePtr.push_back(w->tell());
@ -5529,6 +5557,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
} }
/// SAMPLE /// SAMPLE
samplePtr.reserve(song.sampleLen);
for (int i=0; i<song.sampleLen; i++) { for (int i=0; i<song.sampleLen; i++) {
DivSample* sample=song.sample[i]; DivSample* sample=song.sample[i];
samplePtr.push_back(w->tell()); samplePtr.push_back(w->tell());
@ -5536,6 +5565,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
} }
/// PATTERN /// PATTERN
patPtr.reserve(patsToWrite.size());
for (PatToWrite& i: patsToWrite) { for (PatToWrite& i: patsToWrite) {
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false); DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
patPtr.push_back(w->tell()); patPtr.push_back(w->tell());

View file

@ -2264,8 +2264,14 @@ void DivInstrument::readFeatureOx(SafeReader& reader, int op, short version) {
// <167 TL macro compat // <167 TL macro compat
if (macroCode==6 && version<167) { if (macroCode==6 && version<167) {
for (int i=0; i<target->len; i++) { if (target->open&6) {
target->val[i]^=0x7f; for (int j=0; j<2; j++) {
target->val[j]^=0x7f;
}
} else {
for (int j=0; j<target->len; j++) {
target->val[j]^=0x7f;
}
} }
} }
} }
@ -3329,8 +3335,14 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
// <167 TL macro compat // <167 TL macro compat
if (version<167) { if (version<167) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
for (int j=0; j<std.opMacros[i].tlMacro.len; j++) { if (std.opMacros[i].tlMacro.open&6) {
std.opMacros[i].tlMacro.val[j]^=0x7f; for (int j=0; j<2; j++) {
std.opMacros[i].tlMacro.val[j]^=0x7f;
}
} else {
for (int j=0; j<std.opMacros[i].tlMacro.len; j++) {
std.opMacros[i].tlMacro.val[j]^=0x7f;
}
} }
} }
} }

View file

@ -56,6 +56,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
has=false; has=false;
return; return;
} }
if (released && type==1 && lastPos<3) delay=0;
if (delay>0) { if (delay>0) {
delay--; delay--;
if (!linger) had=false; if (!linger) had=false;
@ -401,15 +402,13 @@ void DivMacroInt::init(DivInstrument* which) {
if (macroSource[i]!=NULL) { if (macroSource[i]!=NULL) {
macroList[i]->prepare(*macroSource[i],e); macroList[i]->prepare(*macroSource[i],e);
// check ADSR mode // check ADSR mode
if ((macroSource[i]->open&6)==4) { if ((macroSource[i]->open&6)==2) {
hasRelease=false; if (macroSource[i]->val[8]>0) {
} else if ((macroSource[i]->open&6)==2) { hasRelease=true;
}
} else if (macroSource[i]->rel<macroSource[i]->len) {
hasRelease=true; hasRelease=true;
} else {
hasRelease=(macroSource[i]->rel<macroSource[i]->len);
} }
} else {
hasRelease=false;
} }
} }
} }

View file

@ -78,15 +78,21 @@ void DivPlatformK007232::acquire(short** buf, size_t len) {
const signed int rout[2]={(k007232.output(0)*((vol1>>4)&0xf)),(k007232.output(1)*((vol2>>4)&0xf))}; const signed int rout[2]={(k007232.output(0)*((vol1>>4)&0xf)),(k007232.output(1)*((vol2>>4)&0xf))};
buf[0][h]=(lout[0]+lout[1])<<4; buf[0][h]=(lout[0]+lout[1])<<4;
buf[1][h]=(rout[0]+rout[1])<<4; buf[1][h]=(rout[0]+rout[1])<<4;
for (int i=0; i<2; i++) { if (++oscDivider>=8) {
oscBuf[i]->data[oscBuf[i]->needle++]=(lout[i]+rout[i])<<3; oscDivider=0;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(lout[i]+rout[i])<<3;
}
} }
} else { } else {
const unsigned char vol=regPool[0xc]; const unsigned char vol=regPool[0xc];
const signed int out[2]={(k007232.output(0)*(vol&0xf)),(k007232.output(1)*((vol>>4)&0xf))}; const signed int out[2]={(k007232.output(0)*(vol&0xf)),(k007232.output(1)*((vol>>4)&0xf))};
buf[0][h]=(out[0]+out[1])<<4; buf[0][h]=(out[0]+out[1])<<4;
for (int i=0; i<2; i++) { if (++oscDivider>=8) {
oscBuf[i]->data[oscBuf[i]->needle++]=out[i]<<4; oscDivider=0;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=out[i]<<4;
}
} }
} }
} }
@ -484,7 +490,7 @@ void DivPlatformK007232::setFlags(const DivConfig& flags) {
stereo=flags.getBool("stereo",false); stereo=flags.getBool("stereo",false);
for (int i=0; i<2; i++) { for (int i=0; i<2; i++) {
chan[i].volumeChanged=true; chan[i].volumeChanged=true;
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate/8;
} }
} }
@ -575,6 +581,7 @@ int DivPlatformK007232::init(DivEngine* p, int channels, int sugRate, const DivC
} }
sampleMem=new unsigned char[getSampleMemCapacity()]; sampleMem=new unsigned char[getSampleMemCapacity()];
sampleMemLen=0; sampleMemLen=0;
oscDivider=0;
setFlags(flags); setFlags(flags);
reset(); reset();

View file

@ -68,7 +68,7 @@ class DivPlatformK007232: public DivDispatch, public k007232_intf {
bool sampleLoaded[256]; bool sampleLoaded[256];
int delay; int delay;
unsigned char lastLoop, lastVolume; unsigned char lastLoop, lastVolume, oscDivider;
bool stereo; bool stereo;
unsigned char* sampleMem; unsigned char* sampleMem;

View file

@ -1164,7 +1164,11 @@ void DivEngine::nextRow() {
} }
if (haltOn==DIV_HALT_PATTERN) halted=true; if (haltOn==DIV_HALT_PATTERN) halted=true;
} else if (playing) if (++curRow>=curSubSong->patLen) { } else if (playing) if (++curRow>=curSubSong->patLen) {
nextOrder(); if (shallStopSched) {
curRow=curSubSong->patLen-1;
} else {
nextOrder();
}
if (haltOn==DIV_HALT_PATTERN) halted=true; if (haltOn==DIV_HALT_PATTERN) halted=true;
} }

View file

@ -22,6 +22,22 @@
#include "instrument.h" #include "instrument.h"
#include "../ta-log.h" #include "../ta-log.h"
inline bool effectOnlyAltersOutput(unsigned char effect) {
switch (effect) {
case DIV_WS_NONE:
case DIV_WS_INVERT:
case DIV_WS_ADD:
case DIV_WS_SUBTRACT:
case DIV_WS_AVERAGE:
return true;
break;
default:
return false;
break;
}
return false;
}
bool DivWaveSynth::activeChanged() { bool DivWaveSynth::activeChanged() {
if (activeChangedB) { if (activeChangedB) {
activeChangedB=false; activeChangedB=false;
@ -211,19 +227,21 @@ void DivWaveSynth::setWidth(int val) {
if (width>256) width=256; if (width>256) width=256;
} }
void DivWaveSynth::changeWave1(int num) { #define SHALL_UPDATE_OUT (!state.enabled || force || (state.enabled && effectOnlyAltersOutput(state.effect)))
void DivWaveSynth::changeWave1(int num, bool force) {
DivWavetable* w1=e->getWave(num); DivWavetable* w1=e->getWave(num);
if (width<1) return; if (width<1) return;
for (int i=0; i<width; i++) { for (int i=0; i<width; i++) {
if (w1->max<1 || w1->len<1) { if (w1->max<1 || w1->len<1) {
wave1[i]=0; wave1[i]=0;
output[i]=0; if (SHALL_UPDATE_OUT) output[i]=0;
} else { } else {
int data=w1->data[i*w1->len/width]*height/w1->max; int data=w1->data[i*w1->len/width]*height/w1->max;
if (data<0) data=0; if (data<0) data=0;
if (data>height) data=height; if (data>height) data=height;
wave1[i]=data; wave1[i]=data;
output[i]=data; if (SHALL_UPDATE_OUT) output[i]=data;
} }
} }
first=true; first=true;
@ -280,9 +298,9 @@ void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) {
divCounter=0; divCounter=0;
subDivCounter=0; subDivCounter=0;
changeWave1(state.wave1); changeWave1(state.wave1,true);
changeWave2(state.wave2); changeWave2(state.wave2);
tick(true); //tick(true); // ???
first=true; first=true;
} }
} }

View file

@ -55,8 +55,9 @@ class DivWaveSynth {
/** /**
* change the first wave. * change the first wave.
* @param num wavetable number. * @param num wavetable number.
* @param force whether to force overwriting the current wave.
*/ */
void changeWave1(int num); void changeWave1(int num, bool force=false);
/** /**
* change the second wave. * change the second wave.
* @param num wavetable number. * @param num wavetable number.

View file

@ -448,6 +448,7 @@ void FurnaceGUI::drawChanOsc() {
float maxLevel=-1.0f; float maxLevel=-1.0f;
float dcOff=0.0f; float dcOff=0.0f;
unsigned short needlePos=buf->needle; unsigned short needlePos=buf->needle;
//unsigned short needlePosOrig=needlePos;
for (int i=0; i<FURNACE_FFT_SIZE; i++) { for (int i=0; i<FURNACE_FFT_SIZE; i++) {
fft->inBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0; fft->inBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0;
} }
@ -475,10 +476,7 @@ void FurnaceGUI::drawChanOsc() {
} }
chanOscPitch[ch]=(float)point/32.0f; chanOscPitch[ch]=(float)point/32.0f;
/*
String cPhase=fmt::sprintf("%d cphase: %f vol: %f",point,phase,chanOscVol[ch]);
dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
*/
needlePos-=displaySize; needlePos-=displaySize;
for (unsigned short i=0; i<precision; i++) { for (unsigned short i=0; i<precision; i++) {
@ -496,6 +494,9 @@ void FurnaceGUI::drawChanOsc() {
y*=chanOscAmplify; y*=chanOscAmplify;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
} }
//String cPhase=fmt::sprintf("%d cphase: %f\nvol: %f\nmin: %.2f\nmax: %.2f\ndcOff: %.2f\nneedles:\n- %d\n- %d\n- %d (%s)",point,phase,chanOscVol[ch],minLevel,maxLevel,dcOff,needlePosOrig,needlePos,(needlePos+displaySize),((needlePos+displaySize)>=needlePosOrig)?"WARN":"OK");
//dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
} }
ImU32 color=ImGui::GetColorU32(chanOscColor); ImU32 color=ImGui::GetColorU32(chanOscColor);
if (chanOscUseGrad) { if (chanOscUseGrad) {

View file

@ -2115,6 +2115,7 @@ int FurnaceGUI::save(String path, int dmfVersion) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC); showWarning(e->getWarnings(),GUI_WARN_GENERIC);
} }
pushRecentFile(path); pushRecentFile(path);
pushRecentSys(path.c_str());
logD("save complete."); logD("save complete.");
return 0; return 0;
} }
@ -2222,6 +2223,13 @@ void FurnaceGUI::pushRecentFile(String path) {
} }
} }
void FurnaceGUI::pushRecentSys(const char* path) {
#ifdef _WIN32
WString widePath=utf8To16(path);
SHAddToRecentDocs(SHARD_PATHW,widePath.c_str());
#endif
}
void FurnaceGUI::delFirstBackup(String name) { void FurnaceGUI::delFirstBackup(String name) {
std::vector<String> listOfFiles; std::vector<String> listOfFiles;
#ifdef _WIN32 #ifdef _WIN32
@ -4819,29 +4827,39 @@ bool FurnaceGUI::loop() {
break; break;
case GUI_FILE_INS_SAVE: case GUI_FILE_INS_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) { if (curIns>=0 && curIns<(int)e->song.ins.size()) {
e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song); if (e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song)) {
pushRecentSys(copyOfName.c_str());
}
} }
break; break;
case GUI_FILE_INS_SAVE_DMP: case GUI_FILE_INS_SAVE_DMP:
if (curIns>=0 && curIns<(int)e->song.ins.size()) { if (curIns>=0 && curIns<(int)e->song.ins.size()) {
if (!e->song.ins[curIns]->saveDMP(copyOfName.c_str())) { if (!e->song.ins[curIns]->saveDMP(copyOfName.c_str())) {
showError("error while saving instrument! make sure your instrument is compatible."); showError("error while saving instrument! make sure your instrument is compatible.");
} else {
pushRecentSys(copyOfName.c_str());
} }
} }
break; break;
case GUI_FILE_WAVE_SAVE: case GUI_FILE_WAVE_SAVE:
if (curWave>=0 && curWave<(int)e->song.wave.size()) { if (curWave>=0 && curWave<(int)e->song.wave.size()) {
e->song.wave[curWave]->save(copyOfName.c_str()); if (e->song.wave[curWave]->save(copyOfName.c_str())) {
pushRecentSys(copyOfName.c_str());
}
} }
break; break;
case GUI_FILE_WAVE_SAVE_DMW: case GUI_FILE_WAVE_SAVE_DMW:
if (curWave>=0 && curWave<(int)e->song.wave.size()) { if (curWave>=0 && curWave<(int)e->song.wave.size()) {
e->song.wave[curWave]->saveDMW(copyOfName.c_str()); if (e->song.wave[curWave]->saveDMW(copyOfName.c_str())) {
pushRecentSys(copyOfName.c_str());
}
} }
break; break;
case GUI_FILE_WAVE_SAVE_RAW: case GUI_FILE_WAVE_SAVE_RAW:
if (curWave>=0 && curWave<(int)e->song.wave.size()) { if (curWave>=0 && curWave<(int)e->song.wave.size()) {
e->song.wave[curWave]->saveRaw(copyOfName.c_str()); if (e->song.wave[curWave]->saveRaw(copyOfName.c_str())) {
pushRecentSys(copyOfName.c_str());
}
} }
break; break;
case GUI_FILE_SAMPLE_OPEN: { case GUI_FILE_SAMPLE_OPEN: {
@ -4905,6 +4923,8 @@ bool FurnaceGUI::loop() {
if (curSample>=0 && curSample<(int)e->song.sample.size()) { if (curSample>=0 && curSample<(int)e->song.sample.size()) {
if (!e->song.sample[curSample]->save(copyOfName.c_str())) { if (!e->song.sample[curSample]->save(copyOfName.c_str())) {
showError("could not save sample! open Log Viewer for more information."); showError("could not save sample! open Log Viewer for more information.");
} else {
pushRecentSys(copyOfName.c_str());
} }
} }
break; break;
@ -4912,6 +4932,8 @@ bool FurnaceGUI::loop() {
if (curSample>=0 && curSample<(int)e->song.sample.size()) { if (curSample>=0 && curSample<(int)e->song.sample.size()) {
if (!e->song.sample[curSample]->saveRaw(copyOfName.c_str())) { if (!e->song.sample[curSample]->saveRaw(copyOfName.c_str())) {
showError("could not save sample! open Log Viewer for more information."); showError("could not save sample! open Log Viewer for more information.");
} else {
pushRecentSys(copyOfName.c_str());
} }
} }
break; break;
@ -5061,6 +5083,7 @@ bool FurnaceGUI::loop() {
if (f!=NULL) { if (f!=NULL) {
fwrite(w->getFinalBuf(),1,w->size(),f); fwrite(w->getFinalBuf(),1,w->size(),f);
fclose(f); fclose(f);
pushRecentSys(copyOfName.c_str());
} else { } else {
showError("could not open file!"); showError("could not open file!");
} }
@ -5081,6 +5104,7 @@ bool FurnaceGUI::loop() {
if (f!=NULL) { if (f!=NULL) {
fwrite(w->getFinalBuf(),1,w->size(),f); fwrite(w->getFinalBuf(),1,w->size(),f);
fclose(f); fclose(f);
pushRecentSys(copyOfName.c_str());
} else { } else {
showError("could not open file!"); showError("could not open file!");
} }
@ -5107,6 +5131,7 @@ bool FurnaceGUI::loop() {
if (f!=NULL) { if (f!=NULL) {
fwrite(w->getFinalBuf(),1,w->size(),f); fwrite(w->getFinalBuf(),1,w->size(),f);
fclose(f); fclose(f);
pushRecentSys(copyOfName.c_str());
} else { } else {
showError("could not open file!"); showError("could not open file!");
} }

View file

@ -2342,6 +2342,7 @@ class FurnaceGUI {
int load(String path); int load(String path);
int loadStream(String path); int loadStream(String path);
void pushRecentFile(String path); void pushRecentFile(String path);
void pushRecentSys(const char* path);
void exportAudio(String path, DivAudioExportModes mode); void exportAudio(String path, DivAudioExportModes mode);
void delFirstBackup(String name); void delFirstBackup(String name);

View file

@ -5166,10 +5166,9 @@ void FurnaceGUI::drawInsEdit() {
ins->snes.sus=3; ins->snes.sus=3;
} }
} else { } else {
if (ImGui::BeginTable("SNESGainParams",3,ImGuiTableFlags_NoHostExtendX)) { if (ImGui::BeginTable("SNESGainParams",2,ImGuiTableFlags_NoHostExtendX)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -5178,9 +5177,6 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
CENTER_TEXT("Gain"); CENTER_TEXT("Gain");
ImGui::TextUnformatted("Gain"); ImGui::TextUnformatted("Gain");
ImGui::TableNextColumn();
CENTER_TEXT("Envelope");
ImGui::TextUnformatted("Envelope");
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -5210,11 +5206,11 @@ void FurnaceGUI::drawInsEdit() {
if (ins->snes.gain>gainMax) ins->snes.gain=gainMax; if (ins->snes.gain>gainMax) ins->snes.gain=gainMax;
P(CWVSliderScalar("##Gain",sliderSize,ImGuiDataType_U8,&ins->snes.gain,&_ZERO,&gainMax)); rightClickable P(CWVSliderScalar("##Gain",sliderSize,ImGuiDataType_U8,&ins->snes.gain,&_ZERO,&gainMax)); rightClickable
ImGui::TableNextColumn();
ImGui::Text("Envelope goes here...");
ImGui::EndTable(); ImGui::EndTable();
} }
if (ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_DEC_LINEAR || ins->snes.gainMode==DivInstrumentSNES::GAIN_MODE_DEC_LOG) {
ImGui::TextWrapped("using decrease modes will not produce any sound at all, unless you know what you are doing.\nit is recommended to use the Gain macro for decrease instead.");
}
} }
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
@ -5322,9 +5318,9 @@ void FurnaceGUI::drawInsEdit() {
wavePreview.init(ins,wavePreviewLen,wavePreviewHeight,true); wavePreview.init(ins,wavePreviewLen,wavePreviewHeight,true);
wavePreviewInit=false; wavePreviewInit=false;
} }
float wavePreview1[256]; float wavePreview1[257];
float wavePreview2[256]; float wavePreview2[257];
float wavePreview3[256]; float wavePreview3[257];
for (int i=0; i<wave1->len; i++) { for (int i=0; i<wave1->len; i++) {
if (wave1->data[i]>wave1->max) { if (wave1->data[i]>wave1->max) {
wavePreview1[i]=wave1->max; wavePreview1[i]=wave1->max;
@ -5332,6 +5328,9 @@ void FurnaceGUI::drawInsEdit() {
wavePreview1[i]=wave1->data[i]; wavePreview1[i]=wave1->data[i];
} }
} }
if (wave1->len>0) {
wavePreview1[wave1->len]=wave1->data[wave1->len-1];
}
for (int i=0; i<wave2->len; i++) { for (int i=0; i<wave2->len; i++) {
if (wave2->data[i]>wave2->max) { if (wave2->data[i]>wave2->max) {
wavePreview2[i]=wave2->max; wavePreview2[i]=wave2->max;
@ -5339,15 +5338,18 @@ void FurnaceGUI::drawInsEdit() {
wavePreview2[i]=wave2->data[i]; wavePreview2[i]=wave2->data[i];
} }
} }
if (wave2->len>0) {
wavePreview2[wave2->len]=wave2->data[wave2->len-1];
}
if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) { if (ins->ws.enabled && (!wavePreviewPaused || wavePreviewInit)) {
wavePreview.tick(true); wavePreview.tick(true);
WAKE_UP;
} }
for (int i=0; i<wavePreviewLen; i++) { for (int i=0; i<wavePreviewLen; i++) {
if (wave2->data[i]>wavePreviewHeight) { wavePreview3[i]=wavePreview.output[i];
wavePreview3[i]=wavePreviewHeight; }
} else { if (wavePreviewLen>0) {
wavePreview3[i]=wavePreview.output[i]; wavePreview3[wavePreviewLen]=wavePreview3[wavePreviewLen-1];
}
} }
float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale; float ySize=(isSingleWaveFX?96.0f:128.0f)*dpiScale;
@ -5363,7 +5365,7 @@ void FurnaceGUI::drawInsEdit() {
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize); ImVec2 size3=ImVec2(ImGui::GetContentRegionAvail().x,ySize);
PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen,0,"Result",0,wavePreviewHeight,size3); PlotNoLerp("##WaveformP3",wavePreview3,wavePreviewLen+1,0,"Result",0,wavePreviewHeight,size3);
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -5701,6 +5703,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_ES5506) waveMax=0; if (ins->type==DIV_INS_ES5506) waveMax=0;
if (ins->type==DIV_INS_GA20) waveMax=0; if (ins->type==DIV_INS_GA20) waveMax=0;
if (ins->type==DIV_INS_K053260) waveMax=0; if (ins->type==DIV_INS_K053260) waveMax=0;
if (ins->type==DIV_INS_BEEPER) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0; if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_TED) waveMax=0; if (ins->type==DIV_INS_TED) waveMax=0;
if (ins->type==DIV_INS_C140) waveMax=0; if (ins->type==DIV_INS_C140) waveMax=0;

View file

@ -1303,6 +1303,7 @@ void FurnaceGUI::drawSettings() {
ImGui::SameLine(); ImGui::SameLine();
ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,pcspkrOutMethods,5); ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,pcspkrOutMethods,5);
/*
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Sample ROMs:"); ImGui::Text("Sample ROMs:");
@ -1332,6 +1333,7 @@ void FurnaceGUI::drawSettings() {
if (ImGui::Button(ICON_FA_FOLDER "##MU5Load")) { if (ImGui::Button(ICON_FA_FOLDER "##MU5Load")) {
openFileDialog(GUI_FILE_MU5_ROM_OPEN); openFileDialog(GUI_FILE_MU5_ROM_OPEN);
} }
*/
END_SECTION; END_SECTION;
} }

View file

@ -255,12 +255,6 @@ void FurnaceGUI::drawTutorial() {
ImGui::Text("welcome to Furnace, the biggest open-source chiptune tracker!"); ImGui::Text("welcome to Furnace, the biggest open-source chiptune tracker!");
ImGui::TextWrapped(
"did I say that 0.6pre5 will have a tutorial? well, it doesn't...\n"
"the reason is because 0.6pre5 fixes a critical bug which may cause config loss in some machines.\n"
"furthermore, it dramatically improves the backup system. couldn't put this version on hold anymore."
);
ImGui::Separator(); ImGui::Separator();
ImGui::TextWrapped("here are some tips to get you started:"); ImGui::TextWrapped("here are some tips to get you started:");
@ -280,7 +274,7 @@ void FurnaceGUI::drawTutorial() {
ImGui::TextWrapped( ImGui::TextWrapped(
"if you need help, you may:\n" "if you need help, you may:\n"
"- read the (incomplete) manual: https://github.com/tildearrow/furnace/blob/master/doc/README.md\n" "- read the (incomplete) manual: https://github.com/tildearrow/furnace/blob/master/doc/README.md\n"
"- ask for help in Discussions (https://github.com/tildearrow/furnace/discussions) or the Furnace Discord (https://discord.gg/EfrwT2wq7z)" "- ask for help in Discussions (https://github.com/tildearrow/furnace/discussions), the Furnace Discord (https://discord.gg/EfrwT2wq7z) or Furnace in Revolt (https://rvlt.gg/GRPS6tmc)"
); );
ImGui::Separator(); ImGui::Separator();