Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
This commit is contained in:
commit
7867b59580
|
@ -25,7 +25,7 @@ jobs:
|
|||
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
|
||||
- { name: 'macOS ARM', os: macos-latest, arch: arm64 }
|
||||
- { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 }
|
||||
- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf }
|
||||
#- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf }
|
||||
fail-fast: false
|
||||
|
||||
name: ${{ matrix.config.name }}
|
||||
|
|
|
@ -700,11 +700,11 @@ if (WIN32)
|
|||
if (NOT MSVC)
|
||||
list(APPEND DEPENDENCIES_LIBRARIES -static)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
elseif (APPLE)
|
||||
find_library(COCOA Cocoa REQUIRED)
|
||||
list(APPEND DEPENDENCIES_LIBRARIES ${COCOA})
|
||||
else()
|
||||
list(APPEND DEPENDENCIES_LIBRARIES dl)
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
|
|
|
@ -76,6 +76,7 @@ additional guidelines:
|
|||
- I will run a test suite to make sure this is the case.
|
||||
- if something breaks, you might want to add a compatibility flag (this requires changing the format though).
|
||||
- do not use `#pragma once`.
|
||||
- do not memcmp() structs.
|
||||
- on a switch block, **always** put `default` last and not in any other position.
|
||||
- I have fear of some C/C++ compilers ignoring the rest of cases upon hitting default.
|
||||
|
||||
|
@ -85,7 +86,7 @@ just put your demo song in `demos/`! be noted there are some guidelines:
|
|||
|
||||
- avoid Nintendo song covers.
|
||||
- avoid big label song covers.
|
||||
- avoid poor quality songs.
|
||||
- low effort compositions/covers may not be accepted at all.
|
||||
|
||||
# Finishing
|
||||
|
||||
|
|
1
TODO.md
1
TODO.md
|
@ -5,5 +5,4 @@
|
|||
- (maybe) YM2612 CSM (no DualPCM)
|
||||
- port presets to new format
|
||||
- bug fixes
|
||||
- (maybe) ExtCh FM macros?
|
||||
- (maybe) advanced linear arpeggio? (run arp+slide simultaneously)
|
||||
|
|
Binary file not shown.
|
@ -6,10 +6,11 @@ these demo songs are not under the GPL. all rights are reserved to the original
|
|||
|
||||
# submit demo songs!
|
||||
|
||||
contact me or send a pull request if you want your song to be added to this collection. be noted we have two rules:
|
||||
contact me or send a pull request if you want your song to be added to this collection. be noted we have three rules:
|
||||
|
||||
- Nintendo covers are frowned upon
|
||||
- big label music covers also are discouraged
|
||||
- low effort compositions/covers may not be accepted at all.
|
||||
|
||||
tildearrow also accepts demo songs in the .dmf format as well as the .fur format.
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -12,18 +12,6 @@
|
|||
#include "SAAFreq.h"
|
||||
#include "defns.h"
|
||||
|
||||
#ifdef SAAFREQ_FIXED_CLOCKRATE
|
||||
// 'load in' the data for the static frequency lookup table
|
||||
// precomputed for a fixed clockrate
|
||||
// See: tools/freqdat.py
|
||||
const unsigned long CSAAFreq::m_FreqTable[2048] = {
|
||||
#include "SAAFreq.dat"
|
||||
};
|
||||
#else
|
||||
unsigned long CSAAFreq::m_FreqTable[2048];
|
||||
unsigned long CSAAFreq::m_nClockRate = 0;
|
||||
#endif // SAAFREQ_FIXED_CLOCKRATE
|
||||
|
||||
const int INITIAL_LEVEL = 1;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -20,8 +20,8 @@ private:
|
|||
const static unsigned long m_FreqTable[2048];
|
||||
#else
|
||||
// we'll calculate the frequency lookup table at runtime.
|
||||
static unsigned long m_FreqTable[2048];
|
||||
static unsigned long m_nClockRate;
|
||||
unsigned long m_FreqTable[2048];
|
||||
unsigned long m_nClockRate;
|
||||
#endif
|
||||
|
||||
unsigned long m_nCounter;
|
||||
|
|
|
@ -26,7 +26,9 @@ m_uParamRate(0),
|
|||
m_nClockRate(EXTERNAL_CLK_HZ),
|
||||
m_nSampleRate(SAMPLE_RATE_HZ),
|
||||
m_nOversample(DEFAULT_OVERSAMPLE),
|
||||
m_bHighpass(false)
|
||||
m_bHighpass(false),
|
||||
filterout_z1_left_mixed(0),
|
||||
filterout_z1_right_mixed(0)
|
||||
{
|
||||
#ifdef USE_CONFIG_FILE
|
||||
m_Config.ReadConfig();
|
||||
|
@ -301,7 +303,6 @@ void scale_for_output(unsigned int left_input, unsigned int right_input,
|
|||
void CSAASoundInternal::GenerateMany(BYTE* pBuffer, unsigned long nSamples, DivDispatchOscBuffer** oscBuf)
|
||||
{
|
||||
unsigned int left_mixed, right_mixed;
|
||||
static double filterout_z1_left_mixed = 0, filterout_z1_right_mixed = 0;
|
||||
|
||||
#if defined(DEBUGSAA) || defined(USE_CONFIG_FILE)
|
||||
BYTE* pBufferStart = pBuffer;
|
||||
|
|
|
@ -36,6 +36,7 @@ private:
|
|||
unsigned int m_nSampleRate;
|
||||
unsigned int m_nOversample;
|
||||
bool m_bHighpass;
|
||||
double filterout_z1_left_mixed, filterout_z1_right_mixed;
|
||||
#ifdef USE_CONFIG_FILE
|
||||
SAAConfig m_Config;
|
||||
#endif
|
||||
|
|
|
@ -32,6 +32,10 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
|||
|
||||
the format versions are:
|
||||
|
||||
- 127: Furnace dev127
|
||||
- 126: Furnace dev126
|
||||
- 125: Furnace dev125
|
||||
- 124: Furnace dev124
|
||||
- 123: Furnace dev123
|
||||
- 122: Furnace dev122
|
||||
- 121: Furnace dev121
|
||||
|
@ -437,7 +441,23 @@ clock=4000000
|
|||
stereo=true
|
||||
```
|
||||
|
||||
# instrument
|
||||
# instrument (>=127)
|
||||
|
||||
Furnace dev127 and higher use the new instrument format.
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | "INS2" block ID
|
||||
4 | size of this block
|
||||
2 | format version
|
||||
2 | instrument type
|
||||
??? | features...
|
||||
```
|
||||
|
||||
see [newIns.md](newIns.md) for more information.
|
||||
|
||||
# old instrument (<127)
|
||||
|
||||
notes:
|
||||
|
||||
|
|
539
papers/newIns.md
539
papers/newIns.md
|
@ -1,49 +1,516 @@
|
|||
# possible new Furnace instrument format
|
||||
# new Furnace instrument format
|
||||
|
||||
the main issue with Furnace instrument files is that they are too big, even if the instrument is nothing more than the FM setup...
|
||||
|
||||
the aim of this new format is to greatly reduce the size of a resulting instrument.
|
||||
|
||||
# information
|
||||
|
||||
this format is "featural", meaning that only used parameters are stored (depending on instrument types).
|
||||
this is the biggest improvement over the previous format, which stored everything including unused parameters.
|
||||
|
||||
features which are not recognized by Furnace will be ignored.
|
||||
|
||||
instruments are not compressed using zlib, unlike Furnace songs.
|
||||
|
||||
all numbers are little-endian.
|
||||
|
||||
the following fields may be found in "size":
|
||||
- `f` indicates a floating point number.
|
||||
- `STR` is a UTF-8 zero-terminated string.
|
||||
- `???` is an array of variable size.
|
||||
- `S??` is an array of `STR`s.
|
||||
- `1??` is an array of bytes.
|
||||
- `2??` is an array of shorts.
|
||||
- `4??` is an array of ints.
|
||||
|
||||
the format may change across versions. a `(>=VER)` indicates this field is only present starting from format version `VER`, and `(<VER)` indicates this field is present only before version `VER`.
|
||||
|
||||
furthermore, an `or reserved` indicates this field is always present, but is reserved when the version condition is not met.
|
||||
|
||||
the `size of this block` fields represent the size of a block excluding the ID and the aforementioned field.
|
||||
|
||||
# header
|
||||
|
||||
.fui files use the following header:
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
6 | "FURINS" format magic
|
||||
4 | "FINS" format magic
|
||||
2 | format version
|
||||
1 | instrument type
|
||||
??? | feature bits
|
||||
4 | instrument length (if wave/sample bits are on)
|
||||
2 | instrument type
|
||||
??? | features...
|
||||
```
|
||||
|
||||
the "feature bits" field is a variable length bitfield. bit 7 in a byte indicates "read one more byte".
|
||||
instruments in a .fur file use the following header instead:
|
||||
|
||||
the feature bits are:
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | "INS2" block ID
|
||||
4 | size of this block
|
||||
2 | format version
|
||||
2 | instrument type
|
||||
??? | features...
|
||||
```
|
||||
|
||||
- 0: has wavetables
|
||||
- 1: has samples
|
||||
- 2: has name
|
||||
- 3: FM data
|
||||
- 4: FM data size (1: 2-op, 0: 4-op)
|
||||
- 5: FM data includes OPL/OPZ data
|
||||
- if off, only read an op until ssgEnv.
|
||||
- if on, read everything else.
|
||||
- 6: Game Boy data
|
||||
- 7: (continue in next byte)
|
||||
- 8: C64 data
|
||||
- 9: Amiga data
|
||||
- 10: standard data (macros)
|
||||
- 11: operator macros
|
||||
- 12: release points
|
||||
- 13: op release points
|
||||
- 14: extended op macros
|
||||
- 15: (continue in next byte)
|
||||
- 16: OPL drums mode data
|
||||
- 17: Amiga sample map data
|
||||
- 18: Namco 163 data
|
||||
- 19: extra macros
|
||||
- 20: FDS data
|
||||
- 21: OPZ data
|
||||
- 22: wavetable synth data
|
||||
- 23: (continue in next byte)
|
||||
- 24: additional macro modes
|
||||
- 25: extra C64 data
|
||||
- 26: MultiPCM data
|
||||
a feature uses the following format:
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
2 | feature code
|
||||
2 | length of block
|
||||
??? | data...
|
||||
```
|
||||
|
||||
the following feature codes are recognized:
|
||||
|
||||
- `NA`: instrument name
|
||||
- `FM`: FM ins data
|
||||
- `MA`: macro data
|
||||
- `64`: C64 ins data
|
||||
- `GB`: Game Boy ins data
|
||||
- `SM`: sample ins data
|
||||
- `O1`: operator 1 macros
|
||||
- `O2`: operator 2 macros
|
||||
- `O3`: operator 3 macros
|
||||
- `O4`: operator 4 macros
|
||||
- `LD`: OPL drums mode data
|
||||
- `SN`: SNES ins data
|
||||
- `N1`: Namco 163 ins data
|
||||
- `FD`: FDS/Virtual Boy ins data
|
||||
- `WS`: wavetable synth data
|
||||
- `SL`: list of samples
|
||||
- `WL`: list of wavetables
|
||||
- `MP`: MultiPCM ins data
|
||||
- `SU`: Sound Unit ins data
|
||||
- `ES`: ES5506 ins data
|
||||
- `X1`: X1-010 ins data
|
||||
- `EN`: end of features
|
||||
- if you find this feature code, stop reading the instrument.
|
||||
- it will usually appear only when there sample/wave lists.
|
||||
- instruments in a .fur shall end with this feature code.
|
||||
|
||||
# instrument name (NA)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
STR | instrument name
|
||||
```
|
||||
|
||||
# FM data (FM)
|
||||
|
||||
- FM operator order is:
|
||||
- 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op
|
||||
- 1/2/?/? (? = unused) for OPL 2-op and OPLL
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | flags
|
||||
| - bit 4-7: op enabled
|
||||
| - op order from 4 to 7: 0, 2, 1, 3
|
||||
| - 2-op instruments: 0, 1, x, x
|
||||
| - bit 0-3: op count
|
||||
-----|------------------------------------
|
||||
| **base data**
|
||||
| /7 6 5 4 3 2 1 0|
|
||||
1 | |x| ALG |x| FB |
|
||||
1 | |FMS2 |AMS| FMS |
|
||||
1 | |AM2|4| LLPatch |
|
||||
-----|------------------------------------
|
||||
| **operator data × opCount**
|
||||
| /7 6 5 4 3 2 1 0|
|
||||
1 | |r| D T | MULT |
|
||||
| \- KSR
|
||||
1 | |s| T L |
|
||||
| \- SUS
|
||||
1 | |R S|v| A R |
|
||||
| \- VIB
|
||||
1 | |A|KSL| D R |
|
||||
| \- AM
|
||||
1 | |e|KVS| D2R |
|
||||
| \- EGT
|
||||
1 | | S L | R R |
|
||||
1 | | DVB | SSG |
|
||||
1 | | DAM |DT2| W S |
|
||||
```
|
||||
|
||||
# macro data (MA)
|
||||
|
||||
notes:
|
||||
|
||||
- the macro range varies depending on the instrument type.
|
||||
- "macro open" indicates whether the macro is collapsed or not in the instrument editor.
|
||||
- meaning of extended macros varies depending on instrument type.
|
||||
- meaning of panning macros varies depending on instrument type:
|
||||
- for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right)
|
||||
- otherwise both left and right panning macros are used
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
2 | length of macro header
|
||||
??? | data...
|
||||
```
|
||||
|
||||
each macro is represented like this:
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | macro code
|
||||
| - 0: vol
|
||||
| - 1: arp
|
||||
| - 2: duty
|
||||
| - 3: wave
|
||||
| - 4: pitch
|
||||
| - 5: ex1
|
||||
| - 6: ex2
|
||||
| - 7: ex3
|
||||
| - 8: alg
|
||||
| - 9: fb
|
||||
| - 10: fms
|
||||
| - 11: ams
|
||||
| - 12: panL
|
||||
| - 13: panR
|
||||
| - 14: phaseReset
|
||||
| - 15: ex4
|
||||
| - 16: ex5
|
||||
| - 17: ex6
|
||||
| - 18: ex7
|
||||
| - 19: ex8
|
||||
| - 255: stop reading and move on
|
||||
1 | macro length
|
||||
1 | macro loop
|
||||
1 | macro release
|
||||
1 | macro mode
|
||||
1 | macro open/type/word size
|
||||
| - bit 6-7: word size
|
||||
| - 0: 8-bit unsigned
|
||||
| - 1: 8-bit signed
|
||||
| - 2: 16-bit signed
|
||||
| - 3: 32-bit signed
|
||||
| - bit 1-2: type
|
||||
| - 0: normal
|
||||
| - 1: ADSR
|
||||
| - 2: LFO
|
||||
| - bit 0: open
|
||||
1 | macro delay
|
||||
1 | macro speed
|
||||
??? | macro data
|
||||
| - length: macro length × word sizs
|
||||
```
|
||||
|
||||
## interpreting macro mode values
|
||||
|
||||
- sequence (normal): I think this is obvious...
|
||||
- ADSR:
|
||||
- `val[0]`: bottom
|
||||
- `val[1]`: top
|
||||
- `val[2]`: attack
|
||||
- `val[3]`: hold time
|
||||
- `val[4]`: decay
|
||||
- `val[5]`: sustain level
|
||||
- `val[6]`: sustain hold time
|
||||
- `val[7]`: decay 2
|
||||
- `val[8]`: release
|
||||
- LFO:
|
||||
- `val[11]`: speed
|
||||
- `val[12]`: waveform
|
||||
- 0: triangle
|
||||
- 1: saw
|
||||
- 2: pulse
|
||||
- `val[13]`: phase
|
||||
- `val[14]`: loop
|
||||
- `val[15]`: global (not sure how will I implement this)
|
||||
|
||||
# C64 data (64)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | flags 1
|
||||
| - bit 7: dutyIsAbs
|
||||
| - bit 6: initFilter
|
||||
| - bit 5: volIsCutoff
|
||||
| - bit 4: toFilter
|
||||
| - bit 3: noise on
|
||||
| - bit 2: pulse on
|
||||
| - bit 1: saw on
|
||||
| - bit 0: triangle on
|
||||
1 | flags 2
|
||||
| - bit 7: oscSync
|
||||
| - bit 6: ringMod
|
||||
| - bit 5: noTest
|
||||
| - bit 4: filterIsAbs
|
||||
| - bit 3: ch3off
|
||||
| - bit 2: band pass
|
||||
| - bit 1: high pass
|
||||
| - bit 0: low pass
|
||||
1 | attack/decay
|
||||
| - bit 4-7: attack
|
||||
| - bit 0-3: decay
|
||||
1 | sustain release
|
||||
| - bit 4-7: sustain
|
||||
| - bit 0-3: release
|
||||
2 | duty
|
||||
2 | cutoff/resonance
|
||||
| - bit 12-15: resonance
|
||||
| - bit 0-10: cutoff
|
||||
```
|
||||
|
||||
# Game Boy data (GB)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | envelope params
|
||||
| - bit 5-7: length
|
||||
| - bit 4: direction
|
||||
| - bit 0-3: volume
|
||||
1 | sound length
|
||||
| - 64 is infinity
|
||||
1 | flags
|
||||
| - bit 1: always init envelope
|
||||
| - bit 0: software envelope (zombie mode)
|
||||
1 | hardware sequence length
|
||||
??? | hardware sequence...
|
||||
| - length: 3*hwSeqLen
|
||||
```
|
||||
|
||||
a value in the hardware sequence has the following format:
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | command
|
||||
| - 0: set envelope
|
||||
| - 1: set sweep
|
||||
| - 2: wait
|
||||
| - 3: wait for release
|
||||
| - 4: loop
|
||||
| - 5: loop until release
|
||||
2 | data
|
||||
| - for set envelope:
|
||||
| - 1 byte: parameter
|
||||
| - bit 4-7: volume
|
||||
| - bit 3: direction
|
||||
| - bit 0-2: length
|
||||
| - 1 byte: sound length
|
||||
| - for set sweep:
|
||||
| - 1 byte: parameter
|
||||
| - bit 4-6: length
|
||||
| - bit 3: direction
|
||||
| - bit 0-2: shift
|
||||
| - 1 byte: nothing
|
||||
| - for wait:
|
||||
| - 1 byte: length (in ticks)
|
||||
| - 1 byte: nothing
|
||||
| - for wait for release:
|
||||
| - 2 bytes: nothing
|
||||
| - for loop/loop until release:
|
||||
| - 2 bytes: position
|
||||
```
|
||||
|
||||
# sample ins data (SM)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
2 | initial sample
|
||||
1 | flags
|
||||
| - bit 2: use wave
|
||||
| - bit 1: use sample
|
||||
| - bit 0: use sample map
|
||||
1 | waveform length
|
||||
4?? | sample map... (120 entries)
|
||||
| - only read if sample map is enabled
|
||||
```
|
||||
|
||||
the sample map format:
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
2 | note to play
|
||||
2 | sample to play
|
||||
```
|
||||
|
||||
# operator macro data (O1, O2, O3 and O4)
|
||||
|
||||
similar to macro data, but using these macro codes:
|
||||
|
||||
- 0: AM
|
||||
- 1: AR
|
||||
- 2: DR
|
||||
- 3: MULT
|
||||
- 4: RR
|
||||
- 5: SL
|
||||
- 6: TL
|
||||
- 7: DT2
|
||||
- 8: RS
|
||||
- 9: DT
|
||||
- 10: D2R
|
||||
- 11: SSG-EG
|
||||
- 12: DAM
|
||||
- 13: DVB
|
||||
- 14: EGT
|
||||
- 15: KSL
|
||||
- 16: SUS
|
||||
- 17: VIB
|
||||
- 18: WS
|
||||
- 19: KSR
|
||||
|
||||
# OPL drums mode data (LD)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | fixed frequency mode
|
||||
2 | kick freq
|
||||
2 | snare/hat freq
|
||||
2 | tom/top freq
|
||||
```
|
||||
|
||||
# SNES data (SN)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | attack/decay
|
||||
| - bit 4-6: decay
|
||||
| - bit 0-3: attack
|
||||
1 | sustain/release
|
||||
| - bit 5-7: sustain
|
||||
| - bit 0-4: release
|
||||
1 | flags
|
||||
| - bit 4: envelope on
|
||||
| - bit 3: make sustain effective
|
||||
| - bit 0-2: gain mode
|
||||
| - 0: direct
|
||||
| - 4: dec
|
||||
| - 5: exp
|
||||
| - 6: inc
|
||||
| - 7: bent
|
||||
1 | gain
|
||||
```
|
||||
|
||||
# Namco 163 data (N1)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | waveform
|
||||
1 | wave pos
|
||||
1 | wave len
|
||||
1 | wave mode
|
||||
```
|
||||
|
||||
# FDS/Virtual Boy data (FD)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | mod speed
|
||||
4 | mod depth
|
||||
1 | init mod table with first wave
|
||||
1?? | modulation table (32 entries)
|
||||
```
|
||||
|
||||
# wavetable synth data (WS)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | first wave
|
||||
4 | second wave
|
||||
1 | rate divider
|
||||
1 | effect
|
||||
| - bit 7: single or dual effect
|
||||
1 | enabled
|
||||
1 | global
|
||||
1 | speed (+1)
|
||||
1 | parameter 1
|
||||
1 | parameter 2
|
||||
1 | parameter 3
|
||||
1 | parameter 4
|
||||
```
|
||||
|
||||
# list of samples (SL)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | number of samples
|
||||
1?? | sample indexes...
|
||||
4?? | pointers to samples...
|
||||
| - these use the Furnace sample format.
|
||||
```
|
||||
|
||||
# list of wavetables (WL)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | number of wavetables
|
||||
1?? | wavetable indexes...
|
||||
4?? | pointers to wavetables...
|
||||
| - these use the Furnace wavetable format.
|
||||
```
|
||||
|
||||
# MultiPCM data (MP)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | attack rate
|
||||
1 | decay 1 rate
|
||||
1 | decay level
|
||||
1 | decay 2 rate
|
||||
1 | release rate
|
||||
1 | rate correction
|
||||
1 | LFO rate
|
||||
1 | vibrato depth
|
||||
1 | AM depth
|
||||
```
|
||||
|
||||
# Sound Unit data (SU)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
1 | switch roles of phase reset timer and frequency
|
||||
```
|
||||
|
||||
# ES5506 data (ES)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
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
|
||||
```
|
||||
|
||||
# X1-010 data (X1)
|
||||
|
||||
```
|
||||
size | description
|
||||
-----|------------------------------------
|
||||
4 | bank slot
|
||||
```
|
||||
|
|
|
@ -532,23 +532,46 @@ class DivDispatch {
|
|||
|
||||
/**
|
||||
* Get sample memory buffer.
|
||||
* @param index the memory index.
|
||||
* @return a pointer to sample memory, or NULL.
|
||||
*/
|
||||
virtual const void* getSampleMem(int index = 0);
|
||||
|
||||
/**
|
||||
* Get sample memory capacity.
|
||||
* @param index the memory index.
|
||||
* @return memory capacity in bytes, or 0 if memory doesn't exist.
|
||||
*/
|
||||
virtual size_t getSampleMemCapacity(int index = 0);
|
||||
|
||||
/**
|
||||
* get sample memory name.
|
||||
* @param index the memory index.
|
||||
* @return a name, or NULL if it doesn't have any name in particular.
|
||||
*/
|
||||
virtual const char* getSampleMemName(int index=0);
|
||||
|
||||
/**
|
||||
* Get sample memory usage.
|
||||
* @param index the memory index.
|
||||
* @return memory usage in bytes.
|
||||
*/
|
||||
virtual size_t getSampleMemUsage(int index = 0);
|
||||
|
||||
/**
|
||||
* Render samples into sample memory.
|
||||
* check whether sample has been loaded in memory.
|
||||
* @param memory index.
|
||||
* @param sample the sample in question.
|
||||
* @return whether it did.
|
||||
*/
|
||||
virtual void renderSamples();
|
||||
virtual bool isSampleLoaded(int index, int sample);
|
||||
|
||||
|
||||
/**
|
||||
* Render samples into sample memory.
|
||||
* @param sysID the chip's index in the chip list.
|
||||
*/
|
||||
virtual void renderSamples(int sysID);
|
||||
|
||||
/**
|
||||
* initialize this DivDispatch.
|
||||
|
|
|
@ -1315,7 +1315,7 @@ void DivEngine::renderSamples() {
|
|||
// step 2: render samples to dispatch
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
if (disCont[i].dispatch!=NULL) {
|
||||
disCont[i].dispatch->renderSamples();
|
||||
disCont[i].dispatch->renderSamples(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2478,10 +2478,10 @@ void DivEngine::previewSampleNoLock(int sample, int note, int pStart, int pEnd)
|
|||
return;
|
||||
}
|
||||
blip_clear(samp_bb);
|
||||
double rate=song.sample[sample]->rate;
|
||||
double rate=song.sample[sample]->centerRate;
|
||||
if (note>=0) {
|
||||
rate=(pow(2.0,(double)(note)/12.0)*((double)song.sample[sample]->centerRate)*0.0625);
|
||||
if (rate<=0) rate=song.sample[sample]->rate;
|
||||
if (rate<=0) rate=song.sample[sample]->centerRate;
|
||||
}
|
||||
if (rate<100) rate=100;
|
||||
blip_set_rates(samp_bb,rate,got.rate);
|
||||
|
|
|
@ -47,8 +47,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev125"
|
||||
#define DIV_ENGINE_VERSION 125
|
||||
#define DIV_VERSION "dev128"
|
||||
#define DIV_ENGINE_VERSION 128
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
@ -766,7 +766,7 @@ class DivEngine {
|
|||
|
||||
// get instrument from file
|
||||
// if the returned vector is empty then there was an error.
|
||||
std::vector<DivInstrument*> instrumentFromFile(const char* path);
|
||||
std::vector<DivInstrument*> instrumentFromFile(const char* path, bool loadAssets=true);
|
||||
|
||||
// load temporary instrument
|
||||
void loadTempIns(DivInstrument* which);
|
||||
|
|
|
@ -208,7 +208,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}*/
|
||||
|
||||
// Game Boy arp+soundLen screwery
|
||||
if (ds.system[0]==DIV_SYSTEM_GB) {
|
||||
ds.systemFlags[0].set("enoughAlready",true);
|
||||
}
|
||||
|
||||
logI("reading module data...");
|
||||
if (ds.version>0x0c) {
|
||||
|
@ -966,6 +968,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.system[1]=DIV_SYSTEM_FDS;
|
||||
}
|
||||
|
||||
// SMS noise freq
|
||||
if (ds.system[0]==DIV_SYSTEM_SMS) {
|
||||
ds.systemFlags[0].set("noEasyNoise",true);
|
||||
}
|
||||
|
||||
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
|
||||
|
||||
if (active) quitDispatch();
|
||||
|
@ -2348,142 +2355,24 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
// read samples
|
||||
for (int i=0; i<ds.sampleLen; i++) {
|
||||
int vol=0;
|
||||
int pitch=0;
|
||||
DivSample* sample=new DivSample;
|
||||
|
||||
if (!reader.seek(samplePtr[i],SEEK_SET)) {
|
||||
logE("couldn't seek to sample %d!",i);
|
||||
lastError=fmt::sprintf("couldn't seek to sample %d!",i);
|
||||
ds.unload();
|
||||
delete sample;
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
reader.read(magic,4);
|
||||
if (strcmp(magic,"SMPL")!=0 && strcmp(magic,"SMP2")!=0) {
|
||||
logE("%d: invalid sample header!",i);
|
||||
lastError="invalid sample header!";
|
||||
if (sample->readSampleData(reader,ds.version)!=DIV_DATA_SUCCESS) {
|
||||
lastError="invalid sample header/data!";
|
||||
ds.unload();
|
||||
delete sample;
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
bool isNewSample=(strcmp(magic,"SMP2")==0);
|
||||
reader.readI();
|
||||
DivSample* sample=new DivSample;
|
||||
logD("reading sample %d at %x...",i,samplePtr[i]);
|
||||
if (!isNewSample) logV("(old sample)");
|
||||
|
||||
sample->name=reader.readString();
|
||||
sample->samples=reader.readI();
|
||||
if (!isNewSample) {
|
||||
sample->loopEnd=sample->samples;
|
||||
}
|
||||
sample->rate=reader.readI();
|
||||
|
||||
if (isNewSample) {
|
||||
sample->centerRate=reader.readI();
|
||||
sample->depth=(DivSampleDepth)reader.readC();
|
||||
if (ds.version>=123) {
|
||||
sample->loopMode=(DivSampleLoopMode)reader.readC();
|
||||
} else {
|
||||
sample->loopMode=DIV_SAMPLE_LOOP_FORWARD;
|
||||
reader.readC();
|
||||
}
|
||||
|
||||
// reserved
|
||||
reader.readC();
|
||||
reader.readC();
|
||||
|
||||
sample->loopStart=reader.readI();
|
||||
sample->loopEnd=reader.readI();
|
||||
sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
reader.readI();
|
||||
}
|
||||
} else {
|
||||
if (ds.version<58) {
|
||||
vol=reader.readS();
|
||||
pitch=reader.readS();
|
||||
} else {
|
||||
reader.readI();
|
||||
}
|
||||
sample->depth=(DivSampleDepth)reader.readC();
|
||||
|
||||
// reserved
|
||||
reader.readC();
|
||||
|
||||
// while version 32 stored this value, it was unused.
|
||||
if (ds.version>=38) {
|
||||
sample->centerRate=(unsigned short)reader.readS();
|
||||
} else {
|
||||
reader.readS();
|
||||
}
|
||||
|
||||
if (ds.version>=19) {
|
||||
sample->loopStart=reader.readI();
|
||||
sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0);
|
||||
} else {
|
||||
reader.readI();
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.version>=58) { // modern sample
|
||||
sample->init(sample->samples);
|
||||
reader.read(sample->getCurBuf(),sample->getCurBufLen());
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// convert 16-bit samples to big-endian
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
|
||||
size_t sampleBufLen=sample->getCurBufLen();
|
||||
for (size_t pos=0; pos<sampleBufLen; pos+=2) {
|
||||
sampleBuf[pos]^=sampleBuf[pos+1];
|
||||
sampleBuf[pos+1]^=sampleBuf[pos];
|
||||
sampleBuf[pos]^=sampleBuf[pos+1];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else { // legacy sample
|
||||
int length=sample->samples;
|
||||
short* data=new short[length];
|
||||
reader.read(data,2*length);
|
||||
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// convert 16-bit samples to big-endian
|
||||
for (int pos=0; pos<length; pos++) {
|
||||
data[pos]=((unsigned short)data[pos]>>8)|((unsigned short)data[pos]<<8);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pitch!=5) {
|
||||
logD("%d: scaling from %d...",i,pitch);
|
||||
}
|
||||
|
||||
// render data
|
||||
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) {
|
||||
logW("%d: sample depth is wrong! (%d)",i,sample->depth);
|
||||
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
|
||||
}
|
||||
sample->samples=(double)sample->samples/samplePitches[pitch];
|
||||
sample->init(sample->samples);
|
||||
|
||||
unsigned int k=0;
|
||||
float mult=(float)(vol)/50.0f;
|
||||
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
||||
if (k>=sample->samples) {
|
||||
break;
|
||||
}
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
||||
sample->data8[k++]=fmin(fmax(next,-128),127);
|
||||
} else {
|
||||
float next=(float)data[(unsigned int)j]*mult;
|
||||
sample->data16[k++]=fmin(fmax(next,-32768),32767);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
ds.sample.push_back(sample);
|
||||
}
|
||||
|
@ -2631,6 +2520,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// SN noise compat
|
||||
if (ds.version<128) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_SMS ||
|
||||
ds.system[i]==DIV_SYSTEM_T6W28) {
|
||||
ds.systemFlags[i].set("noEasyNoise",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
@ -4621,7 +4520,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
for (int i=0; i<song.insLen; i++) {
|
||||
DivInstrument* ins=song.ins[i];
|
||||
insPtr.push_back(w->tell());
|
||||
ins->putInsData(w);
|
||||
logV("writing instrument %d...",i);
|
||||
ins->putInsData2(w,false);
|
||||
}
|
||||
|
||||
/// WAVETABLE
|
||||
|
@ -4635,45 +4535,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* sample=song.sample[i];
|
||||
samplePtr.push_back(w->tell());
|
||||
w->write("SMP2",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeString(sample->name,false);
|
||||
w->writeI(sample->samples);
|
||||
w->writeI(sample->rate);
|
||||
w->writeI(sample->centerRate);
|
||||
w->writeC(sample->depth);
|
||||
w->writeC(sample->loopMode);
|
||||
w->writeC(0); // reserved
|
||||
w->writeC(0);
|
||||
w->writeI(sample->loop?sample->loopStart:-1);
|
||||
w->writeI(sample->loop?sample->loopEnd:-1);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
w->writeI(0xffffffff);
|
||||
}
|
||||
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// store 16-bit samples as little-endian
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
|
||||
size_t bufLen=sample->getCurBufLen();
|
||||
for (size_t i=0; i<bufLen; i+=2) {
|
||||
w->writeC(sampleBuf[i+1]);
|
||||
w->writeC(sampleBuf[i]);
|
||||
}
|
||||
} else {
|
||||
w->write(sample->getCurBuf(),sample->getCurBufLen());
|
||||
}
|
||||
#else
|
||||
w->write(sample->getCurBuf(),sample->getCurBufLen());
|
||||
#endif
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
sample->putSampleData(w);
|
||||
}
|
||||
|
||||
/// PATTERN
|
||||
|
|
|
@ -147,7 +147,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
logD("instrument type is C64");
|
||||
break;
|
||||
case 8: // Arcade
|
||||
ins->type=DIV_INS_FM;
|
||||
ins->type=DIV_INS_OPM;
|
||||
logD("instrument type is Arcade");
|
||||
break;
|
||||
case 9: // Neo Geo
|
||||
|
@ -187,6 +187,8 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
ins->type=DIV_INS_OPLL;
|
||||
} else if (sys==1) {
|
||||
ins->type=DIV_INS_OPL;
|
||||
} else if (sys==8) {
|
||||
ins->type=DIV_INS_OPM;
|
||||
} else {
|
||||
ins->type=DIV_INS_FM;
|
||||
}
|
||||
|
@ -1341,7 +1343,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
// At this point we know any other line would be associated with patch params
|
||||
if (newPatch == NULL) {
|
||||
newPatch = new DivInstrument;
|
||||
newPatch->type = DIV_INS_FM;
|
||||
newPatch->type = DIV_INS_OPM;
|
||||
newPatch->fm.ops = 4;
|
||||
}
|
||||
|
||||
|
@ -1814,7 +1816,7 @@ void DivEngine::loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, S
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
|
||||
std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path, bool loadAssets) {
|
||||
std::vector<DivInstrument*> ret;
|
||||
warnings="";
|
||||
|
||||
|
@ -1880,10 +1882,19 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
|
|||
|
||||
unsigned char magic[16];
|
||||
bool isFurnaceInstr=false;
|
||||
bool isOldFurnaceIns=false;
|
||||
try {
|
||||
reader.read(magic,16);
|
||||
if (memcmp("-Furnace instr.-",magic,16)==0) {
|
||||
reader.read(magic,4);
|
||||
if (memcmp("FINS",magic,4)==0) {
|
||||
isFurnaceInstr=true;
|
||||
logV("found a new Furnace ins");
|
||||
} else {
|
||||
reader.read(&magic[4],12);
|
||||
if (memcmp("-Furnace instr.-",magic,16)==0) {
|
||||
logV("found an old Furnace ins");
|
||||
isFurnaceInstr=true;
|
||||
isOldFurnaceIns=true;
|
||||
}
|
||||
}
|
||||
} catch (EndOfFileException& e) {
|
||||
reader.seek(0,SEEK_SET);
|
||||
|
@ -1892,17 +1903,25 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
|
|||
if (isFurnaceInstr) {
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
try {
|
||||
short version=reader.readS();
|
||||
short version=0;
|
||||
if (isOldFurnaceIns) {
|
||||
version=reader.readS();
|
||||
reader.readS(); // reserved
|
||||
} else {
|
||||
version=reader.readS();
|
||||
reader.seek(0,SEEK_SET);
|
||||
}
|
||||
|
||||
if (version>DIV_ENGINE_VERSION) {
|
||||
warnings="this instrument is made with a more recent version of Furnace!";
|
||||
}
|
||||
|
||||
if (isOldFurnaceIns) {
|
||||
unsigned int dataPtr=reader.readI();
|
||||
reader.seek(dataPtr,SEEK_SET);
|
||||
}
|
||||
|
||||
if (ins->readInsData(reader,version)!=DIV_DATA_SUCCESS) {
|
||||
if (ins->readInsData(reader,version,loadAssets?(&song):NULL)!=DIV_DATA_SUCCESS) {
|
||||
lastError="invalid instrument header/data!";
|
||||
delete ins;
|
||||
delete[] buf;
|
||||
|
@ -1961,6 +1980,11 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
|
|||
format=DIV_INSFORMAT_WOPL;
|
||||
} else if (extS==".wopn") {
|
||||
format=DIV_INSFORMAT_WOPN;
|
||||
} else {
|
||||
// unknown format
|
||||
lastError="unknown instrument format";
|
||||
delete[] buf;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,6 +24,9 @@
|
|||
#include "safeWriter.h"
|
||||
#include "dataErrors.h"
|
||||
#include "../ta-utils.h"
|
||||
#include <vector>
|
||||
|
||||
struct DivSong;
|
||||
|
||||
// NOTICE!
|
||||
// before adding new instrument types to this struct, please ask me first.
|
||||
|
@ -97,11 +100,21 @@ struct DivInstrumentFM {
|
|||
unsigned char alg, fb, fms, ams, fms2, ams2, ops, opllPreset;
|
||||
bool fixedDrums;
|
||||
unsigned short kickFreq, snareHatFreq, tomTopFreq;
|
||||
|
||||
bool operator==(const DivInstrumentFM& other);
|
||||
bool operator!=(const DivInstrumentFM& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
struct Operator {
|
||||
bool enable;
|
||||
unsigned char am, ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv;
|
||||
unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759/OPL/OPZ
|
||||
unsigned char kvs;
|
||||
|
||||
bool operator==(const Operator& other);
|
||||
bool operator!=(const Operator& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
Operator():
|
||||
enable(true),
|
||||
am(0),
|
||||
|
@ -296,6 +309,12 @@ struct DivInstrumentGB {
|
|||
unsigned char cmd;
|
||||
unsigned short data;
|
||||
} hwSeq[256];
|
||||
|
||||
bool operator==(const DivInstrumentGB& other);
|
||||
bool operator!=(const DivInstrumentGB& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentGB():
|
||||
envVol(15),
|
||||
envDir(0),
|
||||
|
@ -318,6 +337,11 @@ struct DivInstrumentC64 {
|
|||
unsigned short cut;
|
||||
bool hp, lp, bp, ch3off;
|
||||
|
||||
bool operator==(const DivInstrumentC64& other);
|
||||
bool operator!=(const DivInstrumentC64& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentC64():
|
||||
triOn(false),
|
||||
sawOn(true),
|
||||
|
@ -369,6 +393,11 @@ struct DivInstrumentAmiga {
|
|||
unsigned char waveLen;
|
||||
SampleMap noteMap[120];
|
||||
|
||||
bool operator==(const DivInstrumentAmiga& other);
|
||||
bool operator!=(const DivInstrumentAmiga& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the sample at specified note.
|
||||
* @return the sample.
|
||||
|
@ -424,6 +453,11 @@ struct DivInstrumentAmiga {
|
|||
struct DivInstrumentX1_010 {
|
||||
int bankSlot;
|
||||
|
||||
bool operator==(const DivInstrumentX1_010& other);
|
||||
bool operator!=(const DivInstrumentX1_010& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentX1_010():
|
||||
bankSlot(0) {}
|
||||
};
|
||||
|
@ -432,6 +466,11 @@ struct DivInstrumentN163 {
|
|||
int wave, wavePos, waveLen;
|
||||
unsigned char waveMode;
|
||||
|
||||
bool operator==(const DivInstrumentN163& other);
|
||||
bool operator!=(const DivInstrumentN163& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentN163():
|
||||
wave(-1),
|
||||
wavePos(0),
|
||||
|
@ -444,6 +483,12 @@ struct DivInstrumentFDS {
|
|||
int modSpeed, modDepth;
|
||||
// this is here for compatibility.
|
||||
bool initModTableWithFirstWave;
|
||||
|
||||
bool operator==(const DivInstrumentFDS& other);
|
||||
bool operator!=(const DivInstrumentFDS& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentFDS():
|
||||
modSpeed(0),
|
||||
modDepth(0),
|
||||
|
@ -456,6 +501,11 @@ struct DivInstrumentMultiPCM {
|
|||
unsigned char ar, d1r, dl, d2r, rr, rc;
|
||||
unsigned char lfo, vib, am;
|
||||
|
||||
bool operator==(const DivInstrumentMultiPCM& other);
|
||||
bool operator!=(const DivInstrumentMultiPCM& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentMultiPCM():
|
||||
ar(15), d1r(15), dl(0), d2r(0), rr(15), rc(15),
|
||||
lfo(0), vib(0), am(0) {
|
||||
|
@ -494,6 +544,12 @@ struct DivInstrumentWaveSynth {
|
|||
unsigned char effect;
|
||||
bool oneShot, enabled, global;
|
||||
unsigned char speed, param1, param2, param3, param4;
|
||||
|
||||
bool operator==(const DivInstrumentWaveSynth& other);
|
||||
bool operator!=(const DivInstrumentWaveSynth& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentWaveSynth():
|
||||
wave1(0),
|
||||
wave2(0),
|
||||
|
@ -511,6 +567,12 @@ struct DivInstrumentWaveSynth {
|
|||
|
||||
struct DivInstrumentSoundUnit {
|
||||
bool switchRoles;
|
||||
|
||||
bool operator==(const DivInstrumentSoundUnit& other);
|
||||
bool operator!=(const DivInstrumentSoundUnit& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentSoundUnit():
|
||||
switchRoles(false) {}
|
||||
};
|
||||
|
@ -546,6 +608,12 @@ struct DivInstrumentES5506 {
|
|||
};
|
||||
Filter filter;
|
||||
Envelope envelope;
|
||||
|
||||
bool operator==(const DivInstrumentES5506& other);
|
||||
bool operator!=(const DivInstrumentES5506& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentES5506():
|
||||
filter(Filter()),
|
||||
envelope(Envelope()) {}
|
||||
|
@ -563,6 +631,12 @@ struct DivInstrumentSNES {
|
|||
GainMode gainMode;
|
||||
unsigned char gain;
|
||||
unsigned char a, d, s, r;
|
||||
|
||||
bool operator==(const DivInstrumentSNES& other);
|
||||
bool operator!=(const DivInstrumentSNES& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
|
||||
DivInstrumentSNES():
|
||||
useEnv(true),
|
||||
sus(false),
|
||||
|
@ -576,7 +650,6 @@ struct DivInstrumentSNES {
|
|||
|
||||
struct DivInstrument {
|
||||
String name;
|
||||
bool mode;
|
||||
DivInstrumentType type;
|
||||
DivInstrumentFM fm;
|
||||
DivInstrumentSTD std;
|
||||
|
@ -592,26 +665,79 @@ struct DivInstrument {
|
|||
DivInstrumentES5506 es5506;
|
||||
DivInstrumentSNES snes;
|
||||
|
||||
/**
|
||||
* these are internal functions.
|
||||
*/
|
||||
void writeMacro(SafeWriter* w, const DivInstrumentMacro& m, unsigned char macroCode);
|
||||
void writeFeatureNA(SafeWriter* w);
|
||||
void writeFeatureFM(SafeWriter* w, bool fui);
|
||||
void writeFeatureMA(SafeWriter* w);
|
||||
void writeFeature64(SafeWriter* w);
|
||||
void writeFeatureGB(SafeWriter* w);
|
||||
void writeFeatureSM(SafeWriter* w);
|
||||
void writeFeatureOx(SafeWriter* w, int op);
|
||||
void writeFeatureLD(SafeWriter* w);
|
||||
void writeFeatureSN(SafeWriter* w);
|
||||
void writeFeatureN1(SafeWriter* w);
|
||||
void writeFeatureFD(SafeWriter* w);
|
||||
void writeFeatureWS(SafeWriter* w);
|
||||
size_t writeFeatureSL(SafeWriter* w, std::vector<int>& list, const DivSong* song);
|
||||
size_t writeFeatureWL(SafeWriter* w, std::vector<int>& list, const DivSong* song);
|
||||
void writeFeatureMP(SafeWriter* w);
|
||||
void writeFeatureSU(SafeWriter* w);
|
||||
void writeFeatureES(SafeWriter* w);
|
||||
void writeFeatureX1(SafeWriter* w);
|
||||
|
||||
void readFeatureNA(SafeReader& reader);
|
||||
void readFeatureFM(SafeReader& reader);
|
||||
void readFeatureMA(SafeReader& reader);
|
||||
void readFeature64(SafeReader& reader);
|
||||
void readFeatureGB(SafeReader& reader);
|
||||
void readFeatureSM(SafeReader& reader);
|
||||
void readFeatureOx(SafeReader& reader, int op);
|
||||
void readFeatureLD(SafeReader& reader);
|
||||
void readFeatureSN(SafeReader& reader);
|
||||
void readFeatureN1(SafeReader& reader);
|
||||
void readFeatureFD(SafeReader& reader);
|
||||
void readFeatureWS(SafeReader& reader);
|
||||
void readFeatureSL(SafeReader& reader, DivSong* song, short version);
|
||||
void readFeatureWL(SafeReader& reader, DivSong* song, short version);
|
||||
void readFeatureMP(SafeReader& reader);
|
||||
void readFeatureSU(SafeReader& reader);
|
||||
void readFeatureES(SafeReader& reader);
|
||||
void readFeatureX1(SafeReader& reader);
|
||||
|
||||
DivDataErrors readInsDataOld(SafeReader& reader, short version);
|
||||
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);
|
||||
|
||||
/**
|
||||
* save the instrument to a SafeWriter.
|
||||
* @param w the SafeWriter in question.
|
||||
*/
|
||||
void putInsData(SafeWriter* w);
|
||||
|
||||
/**
|
||||
* save the instrument to a SafeWriter using new format.
|
||||
* @param w the SafeWriter in question.
|
||||
*/
|
||||
void putInsData2(SafeWriter* w, bool fui=false, const DivSong* song=NULL);
|
||||
|
||||
/**
|
||||
* read instrument data in .fui format.
|
||||
* @param reader the reader.
|
||||
* @param version the format version.
|
||||
* @return a DivDataErrors.
|
||||
*/
|
||||
DivDataErrors readInsData(SafeReader& reader, short version);
|
||||
DivDataErrors readInsData(SafeReader& reader, short version, DivSong* song=NULL);
|
||||
|
||||
/**
|
||||
* save this instrument to a file.
|
||||
* @param path file path.
|
||||
* @param oldFormat whether to save in legacy Furnace ins format.
|
||||
* @param song if new format, a DivSong to read wavetables and samples.
|
||||
* @return whether it was successful.
|
||||
*/
|
||||
bool save(const char* path);
|
||||
bool save(const char* path, bool oldFormat=false, DivSong* song=NULL);
|
||||
|
||||
/**
|
||||
* save this instrument to a file in .dmp format.
|
||||
|
|
|
@ -152,11 +152,20 @@ size_t DivDispatch::getSampleMemCapacity(int index) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
const char* DivDispatch::getSampleMemName(int index) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t DivDispatch::getSampleMemUsage(int index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivDispatch::renderSamples() {
|
||||
bool DivDispatch::isSampleLoaded(int index, int sample) {
|
||||
printf("you are calling.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivDispatch::renderSamples(int sysID) {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1191,13 +1191,25 @@ size_t DivPlatformES5506::getSampleMemUsage(int index) {
|
|||
return index == 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformES5506::renderSamples() {
|
||||
bool DivPlatformES5506::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformES5506::renderSamples(int sysID) {
|
||||
memset(sampleMem,0,getSampleMemCapacity());
|
||||
memset(sampleOffES5506,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=128; // add silent at begin and end of each bank for reverse playback
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffES5506[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int length=s->length16;
|
||||
// fit sample size to single bank size
|
||||
if (length>(4194304-128)) {
|
||||
|
@ -1217,6 +1229,7 @@ void DivPlatformES5506::renderSamples() {
|
|||
memcpy(sampleMem+(memPos/sizeof(short)),s->data16,length);
|
||||
}
|
||||
sampleOffES5506[i]=memPos;
|
||||
sampleLoaded[i]=true;
|
||||
memPos+=length;
|
||||
}
|
||||
sampleMemLen=memPos+256;
|
||||
|
|
|
@ -249,6 +249,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
signed short* sampleMem; // ES5506 uses 16 bit data bus for samples
|
||||
size_t sampleMemLen;
|
||||
unsigned int sampleOffES5506[256];
|
||||
bool sampleLoaded[256];
|
||||
struct QueuedHostIntf {
|
||||
unsigned char state;
|
||||
unsigned char step;
|
||||
|
@ -335,7 +336,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
virtual const void* getSampleMem(int index = 0) override;
|
||||
virtual size_t getSampleMemCapacity(int index = 0) override;
|
||||
virtual size_t getSampleMemUsage(int index = 0) override;
|
||||
virtual void renderSamples() override;
|
||||
virtual bool isSampleLoaded(int index, int sample) override;
|
||||
virtual void renderSamples(int sysID) override;
|
||||
virtual const char** getRegisterSheet() override;
|
||||
virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override;
|
||||
virtual void quit() override;
|
||||
|
|
|
@ -362,8 +362,15 @@ size_t DivPlatformMSM6258::getSampleMemUsage(int index) {
|
|||
return index == 0 ? adpcmMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::renderSamples() {
|
||||
bool DivPlatformMSM6258::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformMSM6258::renderSamples(int sysID) {
|
||||
memset(adpcmMem,0,getSampleMemCapacity(0));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
// sample data
|
||||
size_t memPos=0;
|
||||
|
@ -371,6 +378,8 @@ void DivPlatformMSM6258::renderSamples() {
|
|||
if (sampleCount>128) sampleCount=128;
|
||||
for (int i=0; i<sampleCount; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) continue;
|
||||
|
||||
int paddedLen=s->lengthVOX;
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
|
@ -381,6 +390,7 @@ void DivPlatformMSM6258::renderSamples() {
|
|||
logW("out of ADPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmMem+memPos,s->dataVOX,paddedLen);
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
memPos+=paddedLen;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ class DivPlatformMSM6258: public DivDispatch {
|
|||
|
||||
unsigned char* adpcmMem;
|
||||
size_t adpcmMemLen;
|
||||
bool sampleLoaded[256];
|
||||
unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel;
|
||||
signed char msmDividerCount, msmClockCount;
|
||||
short msmOut;
|
||||
|
@ -113,7 +114,8 @@ class DivPlatformMSM6258: public DivDispatch {
|
|||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
|
|
|
@ -335,11 +335,18 @@ size_t DivPlatformMSM6295::getSampleMemUsage(int index) {
|
|||
return index == 0 ? adpcmMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::renderSamples() {
|
||||
bool DivPlatformMSM6295::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::renderSamples(int sysID) {
|
||||
unsigned int sampleOffVOX[256];
|
||||
|
||||
memset(adpcmMem,0,getSampleMemCapacity(0));
|
||||
memset(sampleOffVOX,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
// sample data
|
||||
size_t memPos=128*8;
|
||||
|
@ -347,6 +354,11 @@ void DivPlatformMSM6295::renderSamples() {
|
|||
if (sampleCount>128) sampleCount=128;
|
||||
for (int i=0; i<sampleCount; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffVOX[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int paddedLen=s->lengthVOX;
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
|
@ -357,6 +369,7 @@ void DivPlatformMSM6295::renderSamples() {
|
|||
logW("out of ADPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmMem+memPos,s->dataVOX,paddedLen);
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOffVOX[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
|
|
@ -68,6 +68,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
|
|||
|
||||
unsigned char* adpcmMem;
|
||||
size_t adpcmMemLen;
|
||||
bool sampleLoaded[256];
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay, updateOsc;
|
||||
|
@ -101,7 +102,8 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
|
|||
virtual const void* getSampleMem(int index) override;
|
||||
virtual size_t getSampleMemCapacity(int index) override;
|
||||
virtual size_t getSampleMemUsage(int index) override;
|
||||
virtual void renderSamples() override;
|
||||
virtual bool isSampleLoaded(int index, int sample) override;
|
||||
virtual void renderSamples(int chipID) override;
|
||||
|
||||
virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override;
|
||||
virtual void quit() override;
|
||||
|
|
|
@ -721,12 +721,24 @@ size_t DivPlatformNES::getSampleMemUsage(int index) {
|
|||
return index==0?dpcmMemLen:0;
|
||||
}
|
||||
|
||||
void DivPlatformNES::renderSamples() {
|
||||
memset(dpcmMem,0,getSampleMemCapacity(0));
|
||||
bool DivPlatformNES::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformNES::renderSamples(int sysID) {
|
||||
memset(dpcmMem,0,getSampleMemCapacity(0));\
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffDPCM[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int paddedLen=(s->lengthDPCM+63)&(~0x3f);
|
||||
logV("%d padded length: %d",i,paddedLen);
|
||||
if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) {
|
||||
|
@ -744,6 +756,7 @@ void DivPlatformNES::renderSamples() {
|
|||
logW("out of DPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(dpcmMem+memPos,s->dataDPCM,MIN(s->lengthDPCM,paddedLen));
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOffDPCM[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
|
|
@ -68,6 +68,7 @@ class DivPlatformNES: public DivDispatch {
|
|||
int dacSample;
|
||||
unsigned char* dpcmMem;
|
||||
size_t dpcmMemLen;
|
||||
bool sampleLoaded[256];
|
||||
unsigned char dpcmBank;
|
||||
unsigned char sampleBank;
|
||||
unsigned char writeOscBuf;
|
||||
|
@ -115,7 +116,8 @@ class DivPlatformNES: public DivDispatch {
|
|||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformNES();
|
||||
|
|
|
@ -1757,14 +1757,26 @@ size_t DivPlatformOPL::getSampleMemUsage(int index) {
|
|||
return (index==0 && adpcmChan>=0) ? adpcmBMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformOPL::renderSamples() {
|
||||
bool DivPlatformOPL::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformOPL::renderSamples(int sysID) {
|
||||
if (adpcmChan<0) return;
|
||||
memset(adpcmBMem,0,getSampleMemCapacity(0));
|
||||
memset(sampleOffB,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffB[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int paddedLen=(s->lengthB+255)&(~0xff);
|
||||
if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) {
|
||||
memPos=(memPos+0xfffff)&0xf00000;
|
||||
|
@ -1778,6 +1790,7 @@ void DivPlatformOPL::renderSamples() {
|
|||
logW("out of ADPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmBMem+memPos,s->dataB,paddedLen);
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOffB[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
|
|
@ -91,6 +91,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
size_t adpcmBMemLen;
|
||||
DivOPLAInterface iface;
|
||||
unsigned int sampleOffB[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
ymfm::adpcm_b_engine* adpcmB;
|
||||
const unsigned char** slotsNonDrums;
|
||||
|
@ -152,7 +153,8 @@ class DivPlatformOPL: public DivDispatch {
|
|||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformOPL();
|
||||
|
|
|
@ -247,7 +247,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
int freqt=toFreq(chan[i].freq);
|
||||
chan[i].freqL=freqt&0xff;
|
||||
if (i>=6 && properDrums) {
|
||||
if (i>=6 && properDrums && (i<9 || !noTopHatFreq)) {
|
||||
immWrite(0x10+drumSlot[i],freqt&0xff);
|
||||
immWrite(0x20+drumSlot[i],freqt>>8);
|
||||
} else if (i<6 || !drums) {
|
||||
|
@ -963,6 +963,7 @@ void DivPlatformOPLL::setFlags(const DivConfig& flags) {
|
|||
for (int i=0; i<11; i++) {
|
||||
oscBuf[i]->rate=rate/2;
|
||||
}
|
||||
noTopHatFreq=flags.getBool("noTopHatFreq",false);
|
||||
}
|
||||
|
||||
int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
|
|
@ -82,7 +82,7 @@ class DivPlatformOPLL: public DivDispatch {
|
|||
|
||||
bool useYMFM;
|
||||
bool drums;
|
||||
bool properDrums, properDrumsSys;
|
||||
bool properDrums, properDrumsSys, noTopHatFreq;
|
||||
bool vrc7;
|
||||
|
||||
unsigned char patchSet;
|
||||
|
|
|
@ -252,6 +252,11 @@ void DivPlatformPCE::tick(bool sysTick) {
|
|||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
if (updateLFO) {
|
||||
rWrite(0x08,lfoSpeed);
|
||||
rWrite(0x09,lfoMode);
|
||||
updateLFO=false;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformPCE::dispatch(DivCommand c) {
|
||||
|
@ -389,13 +394,11 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
} else {
|
||||
lfoMode=c.value;
|
||||
}
|
||||
rWrite(0x08,lfoSpeed);
|
||||
rWrite(0x09,lfoMode);
|
||||
updateLFO=true;
|
||||
break;
|
||||
case DIV_CMD_PCE_LFO_SPEED:
|
||||
lfoSpeed=255-c.value;
|
||||
rWrite(0x08,lfoSpeed);
|
||||
rWrite(0x09,lfoMode);
|
||||
updateLFO=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
|
@ -525,8 +528,7 @@ void DivPlatformPCE::reset() {
|
|||
rWrite(0,0);
|
||||
rWrite(0x01,0xff);
|
||||
// set LFO
|
||||
rWrite(0x08,lfoSpeed);
|
||||
rWrite(0x09,lfoMode);
|
||||
updateLFO=true;
|
||||
// set per-channel initial panning
|
||||
for (int i=0; i<6; i++) {
|
||||
chWrite(i,0x05,isMuted[i]?0:chan[i].pan);
|
||||
|
@ -588,6 +590,7 @@ int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, const DivConfi
|
|||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
updateLFO=false;
|
||||
for (int i=0; i<6; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
|
|
|
@ -76,6 +76,7 @@ class DivPlatformPCE: public DivDispatch {
|
|||
DivDispatchOscBuffer* oscBuf[6];
|
||||
bool isMuted[6];
|
||||
bool antiClickEnabled;
|
||||
bool updateLFO;
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
|
|
|
@ -644,13 +644,25 @@ size_t DivPlatformQSound::getSampleMemUsage(int index) {
|
|||
return index == 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
bool DivPlatformQSound::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
// TODO: ADPCM... come on...
|
||||
void DivPlatformQSound::renderSamples() {
|
||||
void DivPlatformQSound::renderSamples(int sysID) {
|
||||
memset(sampleMem,0,getSampleMemCapacity());
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
offPCM[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int length=s->length8;
|
||||
if (length>65536-16) {
|
||||
length=65536-16;
|
||||
|
@ -671,6 +683,7 @@ void DivPlatformQSound::renderSamples() {
|
|||
for (int i=0; i<length; i++) {
|
||||
sampleMem[(memPos+i)^0x8000]=s->data8[i];
|
||||
}
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
offPCM[i]=memPos^0x8000;
|
||||
memPos+=length+16;
|
||||
|
|
|
@ -69,6 +69,7 @@ class DivPlatformQSound: public DivDispatch {
|
|||
|
||||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
bool sampleLoaded[256];
|
||||
struct qsound_chip chip;
|
||||
unsigned short regPool[512];
|
||||
|
||||
|
@ -103,7 +104,8 @@ class DivPlatformQSound: public DivDispatch {
|
|||
const void* getSampleMem(int index = 0);
|
||||
size_t getSampleMemCapacity(int index = 0);
|
||||
size_t getSampleMemUsage(int index = 0);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
};
|
||||
|
|
|
@ -385,13 +385,25 @@ size_t DivPlatformRF5C68::getSampleMemUsage(int index) {
|
|||
return index == 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::renderSamples() {
|
||||
bool DivPlatformRF5C68::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::renderSamples(int sysID) {
|
||||
memset(sampleMem,0,getSampleMemCapacity());
|
||||
memset(sampleOffRFC,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffRFC[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length);
|
||||
if (actualLength>0) {
|
||||
|
@ -412,6 +424,7 @@ void DivPlatformRF5C68::renderSamples() {
|
|||
}
|
||||
// align memPos to 256-byte boundary
|
||||
memPos=(memPos+0xff)&~0xff;
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ class DivPlatformRF5C68: public DivDispatch {
|
|||
int chipType;
|
||||
unsigned char curChan;
|
||||
unsigned int sampleOffRFC[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
|
@ -99,7 +100,8 @@ class DivPlatformRF5C68: public DivDispatch {
|
|||
const void* getSampleMem(int index = 0);
|
||||
size_t getSampleMemCapacity(int index = 0);
|
||||
size_t getSampleMemUsage(int index = 0);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
private:
|
||||
|
|
|
@ -71,10 +71,17 @@ void DivPlatformSAA1099::acquire_saaSound(short* bufL, short* bufR, size_t start
|
|||
writes.pop();
|
||||
}
|
||||
saa_saaSound->GenerateMany((unsigned char*)saaBuf[0],len,oscBuf);
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
for (size_t i=0; i<len; i++) {
|
||||
bufL[i+start]=(short)((((unsigned short)saaBuf[0][1+(i<<1)])<<8)|(((unsigned short)saaBuf[0][1+(i<<1)])>>8));
|
||||
bufR[i+start]=(short)((((unsigned short)saaBuf[0][i<<1])<<8)|(((unsigned short)saaBuf[0][i<<1])>>8));
|
||||
}
|
||||
#else
|
||||
for (size_t i=0; i<len; i++) {
|
||||
bufL[i+start]=saaBuf[0][i<<1];
|
||||
bufR[i+start]=saaBuf[0][1+(i<<1)];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DivPlatformSAA1099::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
|
|
|
@ -451,7 +451,7 @@ void DivPlatformSegaPCM::reset() {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformSegaPCM::renderSamples() {
|
||||
void DivPlatformSegaPCM::renderSamples(int sysID) {
|
||||
size_t memPos=0;
|
||||
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
|
|
|
@ -110,7 +110,7 @@ class DivPlatformSegaPCM: public DivDispatch {
|
|||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
void notifyInsChange(int ins);
|
||||
void renderSamples();
|
||||
void renderSamples(int chipID);
|
||||
void setFlags(const DivConfig& flags);
|
||||
bool isStereo();
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
|
|
|
@ -119,10 +119,29 @@ void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
}
|
||||
}
|
||||
|
||||
double DivPlatformSMS::NOTE_SN(int ch, int note) {
|
||||
double CHIP_DIVIDER=toneDivider;
|
||||
if (ch==3) CHIP_DIVIDER=noiseDivider;
|
||||
if (parent->song.linearPitch==2 || !easyNoise) {
|
||||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
if (note>easyThreshold) {
|
||||
return MAX(0,easyStartingPeriod-(note-easyThreshold));
|
||||
}
|
||||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
|
||||
int DivPlatformSMS::snCalcFreq(int ch) {
|
||||
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold<<7)) {
|
||||
int ret=(((easyStartingPeriod<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold<<7)))>>7;
|
||||
if (ret<0) ret=0;
|
||||
return ret;
|
||||
}
|
||||
return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,ch==3?noiseDivider:toneDivider);
|
||||
}
|
||||
|
||||
void DivPlatformSMS::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
double CHIP_DIVIDER=toneDivider;
|
||||
if (i==3) CHIP_DIVIDER=noiseDivider;
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].std.vol.val,chan[i].vol,15);
|
||||
|
@ -137,7 +156,7 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
// TODO: add compatibility flag. this is horrible.
|
||||
int areYouSerious=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
while (areYouSerious>0x60) areYouSerious-=12;
|
||||
chan[i].baseFreq=NOTE_PERIODIC(areYouSerious);
|
||||
chan[i].baseFreq=NOTE_SN(i,areYouSerious);
|
||||
chan[i].actualNote=areYouSerious;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
|
@ -177,7 +196,7 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
for (int i=0; i<3; i++) {
|
||||
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=snCalcFreq(i);
|
||||
if (chan[i].freq>1023) chan[i].freq=1023;
|
||||
if (parent->song.snNoLowPeriods) {
|
||||
if (chan[i].freq<8) chan[i].freq=1;
|
||||
|
@ -196,7 +215,8 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[3].freqChanged || updateSNMode) {
|
||||
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,noiseDivider);
|
||||
chan[3].freq=snCalcFreq(3);
|
||||
//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 (parent->song.snNoLowPeriods) {
|
||||
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
|
||||
|
@ -244,12 +264,10 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
int DivPlatformSMS::dispatch(DivCommand c) {
|
||||
double CHIP_DIVIDER=toneDivider;
|
||||
if (c.chan==3) CHIP_DIVIDER=noiseDivider;
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].baseFreq=NOTE_SN(c.chan,c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].actualNote=c.value;
|
||||
|
@ -300,7 +318,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_SN(c.chan,c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
@ -340,7 +358,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(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_SN(c.chan,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].actualNote=c.value;
|
||||
|
@ -349,7 +367,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(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_SN(c.chan,chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
|
@ -446,27 +464,42 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) {
|
|||
switch (flags.getInt("clockSel",0)) {
|
||||
case 1:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
easyThreshold=84;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 2:
|
||||
chipClock=4000000;
|
||||
easyThreshold=86;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 3:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
easyThreshold=72;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 4:
|
||||
chipClock=3000000;
|
||||
easyThreshold=81;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 5:
|
||||
chipClock=2000000;
|
||||
easyThreshold=74;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 6:
|
||||
chipClock=COLOR_NTSC/8.0;
|
||||
easyThreshold=48;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC;
|
||||
easyThreshold=84;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
}
|
||||
resetPhase=!flags.getBool("noPhaseReset",false);
|
||||
easyNoise=!flags.getBool("noEasyNoise",false);
|
||||
divider=16;
|
||||
toneDivider=64.0;
|
||||
noiseDivider=64.0;
|
||||
|
|
|
@ -65,11 +65,14 @@ class DivPlatformSMS: public DivDispatch {
|
|||
int divider=16;
|
||||
double toneDivider=64.0;
|
||||
double noiseDivider=64.0;
|
||||
int easyThreshold;
|
||||
int easyStartingPeriod;
|
||||
bool updateSNMode;
|
||||
bool resetPhase;
|
||||
bool isRealSN;
|
||||
bool stereo;
|
||||
bool nuked;
|
||||
bool easyNoise;
|
||||
sn76496_base_device* sn;
|
||||
ympsg_t sn_nuked;
|
||||
struct QueuedWrite {
|
||||
|
@ -82,6 +85,9 @@ class DivPlatformSMS: public DivDispatch {
|
|||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
double NOTE_SN(int ch, int note);
|
||||
int snCalcFreq(int ch);
|
||||
|
||||
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
||||
void acquire_mame(short* bufL, short* bufR, size_t start, size_t len);
|
||||
public:
|
||||
|
|
|
@ -797,14 +797,26 @@ size_t DivPlatformSNES::getSampleMemUsage(int index) {
|
|||
return index == 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformSNES::renderSamples() {
|
||||
bool DivPlatformSNES::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformSNES::renderSamples(int sysID) {
|
||||
memset(copyOfSampleMem,0,getSampleMemCapacity());
|
||||
memset(sampleOff,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
// skip past sample table and wavetable buffer
|
||||
size_t memPos=sampleTableBase+8*4+8*9*16;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOff[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int length=s->lengthBRR;
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length);
|
||||
if (actualLength>0) {
|
||||
|
@ -822,6 +834,7 @@ void DivPlatformSNES::renderSamples() {
|
|||
logW("out of BRR memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
memcpy(sampleMem,copyOfSampleMem,65536);
|
||||
|
|
|
@ -109,6 +109,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
signed char copyOfSampleMem[65536];
|
||||
size_t sampleMemLen;
|
||||
unsigned int sampleOff[256];
|
||||
bool sampleLoaded[256];
|
||||
unsigned char regPool[0x80];
|
||||
SPC_DSP dsp;
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
@ -136,7 +137,8 @@ class DivPlatformSNES: public DivDispatch {
|
|||
const void* getSampleMem(int index = 0);
|
||||
size_t getSampleMemCapacity(int index = 0);
|
||||
size_t getSampleMemUsage(int index = 0);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
private:
|
||||
|
|
|
@ -391,7 +391,18 @@ void SoundUnit::Reset() {
|
|||
memset(chan,0,sizeof(SUChannel)*8);
|
||||
}
|
||||
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
const unsigned char suBERemap[32]={
|
||||
0x01, 0x00, 0x02, 0x03, 0x05, 0x04, 0x07, 0x06, 0x08, 0x09, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e,
|
||||
0x11, 0x10, 0x12, 0x13, 0x15, 0x14, 0x16, 0x17, 0x19, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x1e
|
||||
};
|
||||
#endif
|
||||
|
||||
void SoundUnit::Write(unsigned char addr, unsigned char data) {
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// remap
|
||||
addr=(addr&0xe0)|(suBERemap[addr&0x1f]);
|
||||
#endif
|
||||
((unsigned char*)chan)[addr]=data;
|
||||
}
|
||||
|
||||
|
|
|
@ -286,6 +286,8 @@ int ymz280b_device::generate_pcm8(struct YMZ280BVoice *voice, s16 *buffer, int s
|
|||
|
||||
***********************************************************************************************/
|
||||
|
||||
// according to this core, it should be little-endian.
|
||||
// but it's big-endian in VGMPlay...
|
||||
int ymz280b_device::generate_pcm16(struct YMZ280BVoice *voice, s16 *buffer, int samples)
|
||||
{
|
||||
u32 position = voice->position;
|
||||
|
@ -298,7 +300,7 @@ int ymz280b_device::generate_pcm16(struct YMZ280BVoice *voice, s16 *buffer, int
|
|||
while (samples)
|
||||
{
|
||||
/* fetch the current value */
|
||||
val = (s16)((m_ext_mem[position / 2 + 1] << 8) + m_ext_mem[position / 2 + 0]);
|
||||
val = (s16)((m_ext_mem[position / 2 + 0] << 8) + m_ext_mem[position / 2 + 1]);
|
||||
|
||||
/* output to the buffer, scaling by the volume */
|
||||
*buffer++ = val;
|
||||
|
@ -321,7 +323,7 @@ int ymz280b_device::generate_pcm16(struct YMZ280BVoice *voice, s16 *buffer, int
|
|||
while (samples)
|
||||
{
|
||||
/* fetch the current value */
|
||||
val = (s16)((m_ext_mem[position / 2 + 1] << 8) + m_ext_mem[position / 2 + 0]);
|
||||
val = (s16)((m_ext_mem[position / 2 + 0] << 8) + m_ext_mem[position / 2 + 1]);
|
||||
|
||||
/* output to the buffer, scaling by the volume */
|
||||
*buffer++ = val;
|
||||
|
|
|
@ -524,7 +524,7 @@ void DivPlatformSoundUnit::setFlags(const DivConfig& flags) {
|
|||
sampleMemSize=flags.getInt("sampleMemSize",0);
|
||||
|
||||
su->Init(sampleMemSize?65536:8192,flags.getBool("pdm",false));
|
||||
renderSamples();
|
||||
renderSamples(sysIDCache);
|
||||
}
|
||||
|
||||
void DivPlatformSoundUnit::poke(unsigned int addr, unsigned short val) {
|
||||
|
@ -547,14 +547,26 @@ size_t DivPlatformSoundUnit::getSampleMemUsage(int index) {
|
|||
return (index==0)?sampleMemLen:0;
|
||||
}
|
||||
|
||||
void DivPlatformSoundUnit::renderSamples() {
|
||||
bool DivPlatformSoundUnit::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformSoundUnit::renderSamples(int sysID) {
|
||||
memset(su->pcm,0,getSampleMemCapacity(0));
|
||||
memset(sampleOffSU,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (s->data8==NULL) continue;
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffSU[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int paddedLen=s->length8;
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of PCM memory for sample %d!",i);
|
||||
|
@ -565,12 +577,13 @@ void DivPlatformSoundUnit::renderSamples() {
|
|||
logW("out of PCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(su->pcm+memPos,s->data8,paddedLen);
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOffSU[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
|
||||
sysIDCache=sysID;
|
||||
}
|
||||
|
||||
int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
@ -582,6 +595,7 @@ int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const Di
|
|||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
su=new SoundUnit();
|
||||
sysIDCache=0;
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 8;
|
||||
|
|
|
@ -102,8 +102,9 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
unsigned char initIlCtrl, initIlSize, initFil1;
|
||||
signed char echoVol, initEchoVol;
|
||||
unsigned int sampleOffSU[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
int cycles, curChan, delay;
|
||||
int cycles, curChan, delay, sysIDCache;
|
||||
short tempL;
|
||||
short tempR;
|
||||
unsigned char sampleBank, lfoMode, lfoSpeed;
|
||||
|
@ -138,7 +139,8 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformSoundUnit();
|
||||
|
|
|
@ -81,10 +81,29 @@ void DivPlatformT6W28::writeOutVol(int ch) {
|
|||
}
|
||||
}
|
||||
|
||||
double DivPlatformT6W28::NOTE_SN(int ch, int note) {
|
||||
double CHIP_DIVIDER=16;
|
||||
if (ch==3) CHIP_DIVIDER=15;
|
||||
if (parent->song.linearPitch==2 || !easyNoise) {
|
||||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
if (note>107) {
|
||||
return MAX(0,13-(note-107));
|
||||
}
|
||||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
|
||||
int DivPlatformT6W28::snCalcFreq(int ch) {
|
||||
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(107<<7)) {
|
||||
int ret=(((13<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(107<<7)))>>7;
|
||||
if (ret<0) ret=0;
|
||||
return ret;
|
||||
}
|
||||
return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,ch==3?15:16);
|
||||
}
|
||||
|
||||
void DivPlatformT6W28::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
double CHIP_DIVIDER=16;
|
||||
if (i==3) CHIP_DIVIDER=15;
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
|
@ -92,7 +111,7 @@ void DivPlatformT6W28::tick(bool sysTick) {
|
|||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
chan[i].baseFreq=NOTE_PERIODIC(noiseSeek);
|
||||
chan[i].baseFreq=NOTE_SN(i,noiseSeek);
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
|
@ -124,7 +143,7 @@ void DivPlatformT6W28::tick(bool sysTick) {
|
|||
rWrite(1,0xe0+chan[i].duty);
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
chan[i].freq=snCalcFreq(i);
|
||||
if (chan[i].freq>1023) chan[i].freq=1023;
|
||||
if (i==3) {
|
||||
rWrite(1,0x80|(2<<5)|(chan[3].freq&15));
|
||||
|
@ -141,13 +160,11 @@ void DivPlatformT6W28::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
int DivPlatformT6W28::dispatch(DivCommand c) {
|
||||
double CHIP_DIVIDER=16;
|
||||
if (c.chan==3) CHIP_DIVIDER=15;
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].baseFreq=NOTE_SN(c.chan,c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
|
@ -197,7 +214,7 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
int destFreq=NOTE_SN(c.chan,c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
@ -231,15 +248,15 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(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_SN(c.chan,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE));
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_T6W28));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(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_SN(c.chan,chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
|
@ -330,6 +347,7 @@ void DivPlatformT6W28::setFlags(const DivConfig& flags) {
|
|||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
easyNoise=!flags.getBool("noEasyNoise",false);
|
||||
|
||||
if (t6w!=NULL) {
|
||||
delete t6w;
|
||||
|
|
|
@ -60,6 +60,7 @@ class DivPlatformT6W28: public DivDispatch {
|
|||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
bool antiClickEnabled;
|
||||
bool easyNoise;
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
|
@ -75,6 +76,10 @@ class DivPlatformT6W28: public DivDispatch {
|
|||
unsigned char regPool[128];
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
double NOTE_SN(int ch, int note);
|
||||
int snCalcFreq(int ch);
|
||||
|
||||
void writeOutVol(int ch);
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
|
|
|
@ -949,13 +949,25 @@ size_t DivPlatformX1_010::getSampleMemUsage(int index) {
|
|||
return index >= 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformX1_010::renderSamples() {
|
||||
bool DivPlatformX1_010::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformX1_010::renderSamples(int sysID) {
|
||||
memset(sampleMem,0,getSampleMemCapacity());
|
||||
memset(sampleOffX1,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffX1[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int paddedLen=(s->length8+4095)&(~0xfff);
|
||||
if (isBanked) {
|
||||
// fit sample bank size to 128KB for Seta 2 external bankswitching logic (not emulated yet!)
|
||||
|
@ -975,6 +987,7 @@ void DivPlatformX1_010::renderSamples() {
|
|||
logW("out of X1-010 memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(sampleMem+memPos,s->data8,paddedLen);
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOffX1[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
|
|
@ -116,6 +116,7 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf {
|
|||
bool isBanked=false;
|
||||
unsigned int bankSlot[8];
|
||||
unsigned int sampleOffX1[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
unsigned char regPool[0x2000];
|
||||
double NoteX1_010(int ch, int note);
|
||||
|
@ -146,7 +147,8 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf {
|
|||
const void* getSampleMem(int index = 0);
|
||||
size_t getSampleMemCapacity(int index = 0);
|
||||
size_t getSampleMemUsage(int index = 0);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
const char** getRegisterSheet();
|
||||
void setBanked(bool banked);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
|
|
|
@ -1336,13 +1336,25 @@ size_t DivPlatformYM2608::getSampleMemUsage(int index) {
|
|||
return index == 0 ? adpcmBMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::renderSamples() {
|
||||
bool DivPlatformYM2608::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::renderSamples(int sysID) {
|
||||
memset(adpcmBMem,0,getSampleMemCapacity(0));
|
||||
memset(sampleOffB,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffB[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int paddedLen=(s->lengthB+255)&(~0xff);
|
||||
if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) {
|
||||
memPos=(memPos+0xfffff)&0xf00000;
|
||||
|
@ -1356,6 +1368,7 @@ void DivPlatformYM2608::renderSamples() {
|
|||
logW("out of ADPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmBMem+memPos,s->dataB,paddedLen);
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOffB[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
|
|
@ -100,6 +100,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
|
|||
size_t adpcmBMemLen;
|
||||
DivYM2608Interface iface;
|
||||
unsigned int sampleOffB[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
DivPlatformAY8910* ay;
|
||||
unsigned char sampleBank;
|
||||
|
@ -137,7 +138,8 @@ class DivPlatformYM2608: public DivPlatformOPN {
|
|||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
void setFlags(const DivConfig& flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
|
|
|
@ -143,6 +143,8 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
|
||||
bool extMode, noExtMacros;
|
||||
|
||||
bool sampleLoaded[2][256];
|
||||
|
||||
unsigned char writeADPCMAOff, writeADPCMAOn;
|
||||
int globalADPCMAVolume;
|
||||
|
||||
|
@ -204,18 +206,34 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
return index == 0 ? 16777216 : index == 1 ? 16777216 : 0;
|
||||
}
|
||||
|
||||
const char* getSampleMemName(int index=0) {
|
||||
return index == 0 ? "ADPCM-A" : index == 1 ? "ADPCM-B" : NULL;
|
||||
}
|
||||
|
||||
size_t getSampleMemUsage(int index) {
|
||||
return index == 0 ? adpcmAMemLen : index == 1 ? adpcmBMemLen : 0;
|
||||
}
|
||||
|
||||
void renderSamples() {
|
||||
bool isSampleLoaded(int index, int sample) {
|
||||
if (index<0 || index>1) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[index][sample];
|
||||
}
|
||||
|
||||
void renderSamples(int sysID) {
|
||||
memset(adpcmAMem,0,getSampleMemCapacity(0));
|
||||
memset(sampleOffA,0,256*sizeof(unsigned int));
|
||||
memset(sampleOffB,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*2*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOffA[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int paddedLen=(s->lengthA+255)&(~0xff);
|
||||
if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) {
|
||||
memPos=(memPos+0xfffff)&0xf00000;
|
||||
|
@ -229,6 +247,7 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
logW("out of ADPCM-A memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmAMem+memPos,s->dataA,paddedLen);
|
||||
sampleLoaded[0][i]=true;
|
||||
}
|
||||
sampleOffA[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
@ -240,6 +259,11 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[1][sysID]) {
|
||||
sampleOffB[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int paddedLen=(s->lengthB+255)&(~0xff);
|
||||
if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) {
|
||||
memPos=(memPos+0xfffff)&0xf00000;
|
||||
|
@ -253,6 +277,7 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
logW("out of ADPCM-B memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmBMem+memPos,s->dataB,paddedLen);
|
||||
sampleLoaded[1][i]=true;
|
||||
}
|
||||
sampleOffB[i]=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
|
|
@ -420,18 +420,40 @@ size_t DivPlatformYMZ280B::getSampleMemUsage(int index) {
|
|||
return index == 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformYMZ280B::renderSamples() {
|
||||
bool DivPlatformYMZ280B::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
void DivPlatformYMZ280B::renderSamples(int sysID) {
|
||||
memset(sampleMem,0,getSampleMemCapacity());
|
||||
memset(sampleOff,0,256*sizeof(unsigned int));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOff[i]=0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int length=s->getCurBufLen();
|
||||
unsigned char* src=(unsigned char*)s->getCurBuf();
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos),length);
|
||||
if (actualLength>0) {
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
memcpy(&sampleMem[memPos],src,actualLength);
|
||||
#else
|
||||
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
for (int i=0; i<actualLength; i++) {
|
||||
sampleMem[memPos+i]=src[i^1];
|
||||
}
|
||||
} else {
|
||||
memcpy(&sampleMem[memPos],src,actualLength);
|
||||
}
|
||||
#endif
|
||||
sampleOff[i]=memPos;
|
||||
memPos+=length;
|
||||
}
|
||||
|
@ -439,6 +461,7 @@ void DivPlatformYMZ280B::renderSamples() {
|
|||
logW("out of YMZ280B PCM memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ class DivPlatformYMZ280B: public DivDispatch {
|
|||
bool isMuted[8];
|
||||
int chipType;
|
||||
unsigned int sampleOff[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
|
@ -99,7 +100,8 @@ class DivPlatformYMZ280B: public DivDispatch {
|
|||
const void* getSampleMem(int index = 0);
|
||||
size_t getSampleMemCapacity(int index = 0);
|
||||
size_t getSampleMemUsage(int index = 0);
|
||||
void renderSamples();
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
void renderSamples(int chipID);
|
||||
void setFlags(const DivConfig& flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
|
|
|
@ -39,6 +39,186 @@ DivSampleHistory::~DivSampleHistory() {
|
|||
if (data!=NULL) delete[] data;
|
||||
}
|
||||
|
||||
void DivSample::putSampleData(SafeWriter* w) {
|
||||
size_t blockStartSeek, blockEndSeek;
|
||||
|
||||
w->write("SMP2",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeString(name,false);
|
||||
w->writeI(samples);
|
||||
w->writeI(rate);
|
||||
w->writeI(centerRate);
|
||||
w->writeC(depth);
|
||||
w->writeC(loopMode);
|
||||
w->writeC(0); // reserved
|
||||
w->writeC(0);
|
||||
w->writeI(loop?loopStart:-1);
|
||||
w->writeI(loop?loopEnd:-1);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
w->writeI(0xffffffff);
|
||||
}
|
||||
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// store 16-bit samples as little-endian
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
unsigned char* sampleBuf=(unsigned char*)getCurBuf();
|
||||
size_t bufLen=getCurBufLen();
|
||||
for (size_t i=0; i<bufLen; i+=2) {
|
||||
w->writeC(sampleBuf[i+1]);
|
||||
w->writeC(sampleBuf[i]);
|
||||
}
|
||||
} else {
|
||||
w->write(getCurBuf(),getCurBufLen());
|
||||
}
|
||||
#else
|
||||
w->write(getCurBuf(),getCurBufLen());
|
||||
#endif
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
// Delek why
|
||||
static double samplePitchesSD[11]={
|
||||
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
|
||||
1,
|
||||
2, 3, 4, 5, 6
|
||||
};
|
||||
|
||||
DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
|
||||
int vol=0;
|
||||
int pitch=0;
|
||||
char magic[4];
|
||||
|
||||
reader.read(magic,4);
|
||||
if (memcmp(magic,"SMPL",4)!=0 && memcmp(magic,"SMP2",4)!=0) {
|
||||
logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]);
|
||||
return DIV_DATA_INVALID_HEADER;
|
||||
}
|
||||
bool isNewSample=(memcmp(magic,"SMP2",4)==0);
|
||||
reader.readI();
|
||||
if (!isNewSample) logV("(old sample)");
|
||||
|
||||
name=reader.readString();
|
||||
samples=reader.readI();
|
||||
if (!isNewSample) {
|
||||
loopEnd=samples;
|
||||
}
|
||||
rate=reader.readI();
|
||||
|
||||
if (isNewSample) {
|
||||
centerRate=reader.readI();
|
||||
depth=(DivSampleDepth)reader.readC();
|
||||
if (version>=123) {
|
||||
loopMode=(DivSampleLoopMode)reader.readC();
|
||||
} else {
|
||||
loopMode=DIV_SAMPLE_LOOP_FORWARD;
|
||||
reader.readC();
|
||||
}
|
||||
|
||||
// reserved
|
||||
reader.readC();
|
||||
reader.readC();
|
||||
|
||||
loopStart=reader.readI();
|
||||
loopEnd=reader.readI();
|
||||
loop=(loopStart>=0)&&(loopEnd>=0);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
reader.readI();
|
||||
}
|
||||
} else {
|
||||
if (version<58) {
|
||||
vol=reader.readS();
|
||||
pitch=reader.readS();
|
||||
} else {
|
||||
reader.readI();
|
||||
}
|
||||
depth=(DivSampleDepth)reader.readC();
|
||||
|
||||
// reserved
|
||||
reader.readC();
|
||||
|
||||
// while version 32 stored this value, it was unused.
|
||||
if (version>=38) {
|
||||
centerRate=(unsigned short)reader.readS();
|
||||
} else {
|
||||
reader.readS();
|
||||
}
|
||||
|
||||
if (version>=19) {
|
||||
loopStart=reader.readI();
|
||||
loop=(loopStart>=0)&&(loopEnd>=0);
|
||||
} else {
|
||||
reader.readI();
|
||||
}
|
||||
}
|
||||
|
||||
if (version>=58) { // modern sample
|
||||
init(samples);
|
||||
reader.read(getCurBuf(),getCurBufLen());
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// convert 16-bit samples to big-endian
|
||||
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
unsigned char* sampleBuf=(unsigned char*)getCurBuf();
|
||||
size_t sampleBufLen=getCurBufLen();
|
||||
for (size_t pos=0; pos<sampleBufLen; pos+=2) {
|
||||
sampleBuf[pos]^=sampleBuf[pos+1];
|
||||
sampleBuf[pos+1]^=sampleBuf[pos];
|
||||
sampleBuf[pos]^=sampleBuf[pos+1];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else { // legacy sample
|
||||
int length=samples;
|
||||
short* data=new short[length];
|
||||
reader.read(data,2*length);
|
||||
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// convert 16-bit samples to big-endian
|
||||
for (int pos=0; pos<length; pos++) {
|
||||
data[pos]=((unsigned short)data[pos]>>8)|((unsigned short)data[pos]<<8);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pitch!=5) {
|
||||
logD("scaling from %d...",pitch);
|
||||
}
|
||||
|
||||
// render data
|
||||
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) {
|
||||
logW("sample depth is wrong! (%d)",depth);
|
||||
depth=DIV_SAMPLE_DEPTH_16BIT;
|
||||
}
|
||||
samples=(double)samples/samplePitchesSD[pitch];
|
||||
init(samples);
|
||||
|
||||
unsigned int k=0;
|
||||
float mult=(float)(vol)/50.0f;
|
||||
for (double j=0; j<length; j+=samplePitchesSD[pitch]) {
|
||||
if (k>=samples) {
|
||||
break;
|
||||
}
|
||||
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
|
||||
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
||||
data8[k++]=fmin(fmax(next,-128),127);
|
||||
} else {
|
||||
float next=(float)data[(unsigned int)j]*mult;
|
||||
data16[k++]=fmin(fmax(next,-32768),32767);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
bool DivSample::isLoopable() {
|
||||
return loop && ((loopStart>=0 && loopStart<loopEnd) && (loopEnd>loopStart && loopEnd<=(int)samples));
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#define _SAMPLE_H
|
||||
|
||||
#include "../ta-utils.h"
|
||||
#include "safeWriter.h"
|
||||
#include "dataErrors.h"
|
||||
#include <deque>
|
||||
|
||||
enum DivSampleLoopMode: unsigned char {
|
||||
|
@ -112,6 +114,8 @@ struct DivSample {
|
|||
// - 2: Pingpong loop
|
||||
DivSampleLoopMode loopMode;
|
||||
|
||||
bool renderOn[4][32];
|
||||
|
||||
// these are the new data structures.
|
||||
signed char* data8; // 8
|
||||
short* data16; // 16
|
||||
|
@ -131,6 +135,20 @@ struct DivSample {
|
|||
std::deque<DivSampleHistory*> undoHist;
|
||||
std::deque<DivSampleHistory*> redoHist;
|
||||
|
||||
/**
|
||||
* put sample data.
|
||||
* @param w a SafeWriter.
|
||||
*/
|
||||
void putSampleData(SafeWriter* w);
|
||||
|
||||
/**
|
||||
* read sample data.
|
||||
* @param reader the reader.
|
||||
* @param version the format version.
|
||||
* @return a DivDataErrors.
|
||||
*/
|
||||
DivDataErrors readSampleData(SafeReader& reader, short version);
|
||||
|
||||
/**
|
||||
* check if sample is loopable.
|
||||
* @return whether it is loopable.
|
||||
|
@ -310,7 +328,14 @@ struct DivSample {
|
|||
lengthB(0),
|
||||
lengthBRR(0),
|
||||
lengthVOX(0),
|
||||
samples(0) {}
|
||||
samples(0) {
|
||||
for (int i=0; i<32; i++) {
|
||||
renderOn[0][i]=true;
|
||||
renderOn[1][i]=true;
|
||||
renderOn[2][i]=true;
|
||||
renderOn[3][i]=true;
|
||||
}
|
||||
}
|
||||
~DivSample();
|
||||
};
|
||||
|
||||
|
|
|
@ -1711,8 +1711,7 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_T6W28]=new DivSysDef(
|
||||
// 0x0a = wild guess. it may as well be 0x83
|
||||
"T6W28", NULL, 0xbf, 0x0a, 4, false, true, 0x160, false, 0,
|
||||
"T6W28", NULL, 0xbf, 0, 4, false, true, 0x160, false, 0,
|
||||
"an SN76489 derivative used in Neo Geo Pocket, has independent stereo volume and noise channel frequency.",
|
||||
{"Square 1", "Square 2", "Square 3", "Noise"},
|
||||
{"S1", "S2", "S3", "NO"},
|
||||
|
|
|
@ -1800,21 +1800,6 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
size_t sampleMemLen=writeZ280[i]->getSampleMemUsage();
|
||||
unsigned char* sampleMem=new unsigned char[sampleMemLen];
|
||||
memcpy(sampleMem,writeZ280[i]->getSampleMem(),sampleMemLen);
|
||||
// TODO: please fix this later
|
||||
/*
|
||||
for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* s=song.sample[i];
|
||||
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
unsigned int pos=s->offYMZ280B;
|
||||
for (unsigned int j=0; j<s->samples; j++) {
|
||||
unsigned char lo=sampleMem[pos+j*2];
|
||||
unsigned char hi=sampleMem[pos+j*2+1];
|
||||
sampleMem[pos+j*2]=hi;
|
||||
sampleMem[pos+j*2+1]=lo;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
w->writeC(0x86);
|
||||
|
|
|
@ -48,6 +48,7 @@ DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) {
|
|||
char magic[4];
|
||||
reader.read(magic,4);
|
||||
if (memcmp(magic,"WAVE",4)!=0) {
|
||||
logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]);
|
||||
return DIV_DATA_INVALID_HEADER;
|
||||
}
|
||||
reader.readI(); // reserved
|
||||
|
@ -57,7 +58,10 @@ DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) {
|
|||
min=reader.readI();
|
||||
max=reader.readI();
|
||||
|
||||
if (len>256 || min!=0 || max>255) return DIV_DATA_INVALID_DATA;
|
||||
if (len>256 || min!=0 || max>255) {
|
||||
logV("invalid len/min/max: %d %d %d",len,min,max);
|
||||
return DIV_DATA_INVALID_DATA;
|
||||
}
|
||||
|
||||
for (int i=0; i<len; i++) {
|
||||
data[i]=reader.readI();
|
||||
|
|
|
@ -76,6 +76,7 @@ const char* aboutLine[]={
|
|||
"Fragmare",
|
||||
"freq-mod",
|
||||
"iyatemu",
|
||||
"JayBOB18",
|
||||
"kleeder",
|
||||
"jaezu",
|
||||
"Laggy",
|
||||
|
@ -93,6 +94,7 @@ const char* aboutLine[]={
|
|||
"SnugglyBun",
|
||||
"SuperJet Spade",
|
||||
"TakuikaNinja",
|
||||
"The Blender Fiddler",
|
||||
"TheDuccinator",
|
||||
"theloredev",
|
||||
"TheRealHedgehogSonic",
|
||||
|
|
|
@ -77,7 +77,12 @@ void FurnaceGUI::calcChanOsc() {
|
|||
int chans=e->getTotalChannelCount();
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
int tryAgain=i;
|
||||
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
|
||||
while (buf==NULL) {
|
||||
if (--tryAgain<0) break;
|
||||
buf=e->getOscBuffer(tryAgain);
|
||||
}
|
||||
if (buf!=NULL && e->curSubSong->chanShow[i]) {
|
||||
// 30ms should be enough
|
||||
int displaySize=(float)(buf->rate)*0.03f;
|
||||
|
|
|
@ -132,6 +132,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
if (ImGui::MenuItem("instrument")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::MenuItem("instrument (legacy .fui)")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
|
||||
}
|
||||
if (ImGui::MenuItem("instrument (.dmp)")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
|
||||
}
|
||||
|
@ -151,6 +154,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
}
|
||||
} else {
|
||||
if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
|
||||
if (ImGui::MenuItem("save in legacy format...")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
|
||||
}
|
||||
if (ImGui::MenuItem("save as .dmp...")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
|
||||
}
|
||||
|
@ -434,6 +440,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
if (ImGui::MenuItem("save")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::MenuItem("save (legacy .fui)")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
|
||||
}
|
||||
if (ImGui::MenuItem("save (.dmp)")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
|
||||
}
|
||||
|
|
|
@ -387,6 +387,33 @@ void FurnaceGUI::drawDebug() {
|
|||
ImGui::Text("System Managed Scale: %d",sysManagedScale);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Visualizer Debug")) {
|
||||
if (ImGui::BeginTable("visX",3,ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("channel");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("patChanX");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("patChanSlideY");
|
||||
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d",i);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%f",patChanX[i]);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%f",patChanSlideY[i]);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Text("particle count: %d",(int)particles.size());
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Playground")) {
|
||||
if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0;
|
||||
if (ImGui::BeginCombo("Chip",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) {
|
||||
|
|
|
@ -599,6 +599,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_INS_LIST_SAVE:
|
||||
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE);
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_SAVE_OLD:
|
||||
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_OLD);
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_SAVE_DMP:
|
||||
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_DMP);
|
||||
break;
|
||||
|
|
|
@ -204,13 +204,15 @@ void FurnaceGUI::drawMobileControls() {
|
|||
ImGui::SameLine();
|
||||
ImGui::Button("Export Audio");
|
||||
ImGui::SameLine();
|
||||
ImGui::Button("Export VGM");
|
||||
if (ImGui::Button("Export VGM")) {
|
||||
openFileDialog(GUI_FILE_EXPORT_VGM);
|
||||
}
|
||||
|
||||
ImGui::Button("CmdStream");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Text("Song info here...");
|
||||
drawSongInfo(true);
|
||||
break;
|
||||
}
|
||||
case GUI_SCENE_CHANNELS:
|
||||
|
|
|
@ -80,6 +80,14 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
|
|||
if (opened) return false;
|
||||
saving=false;
|
||||
curPath=path;
|
||||
|
||||
// strip excess directory separators
|
||||
while (!curPath.empty()) {
|
||||
if (curPath[curPath.size()-1]!=DIR_SEPARATOR) break;
|
||||
curPath.erase(curPath.size()-1);
|
||||
}
|
||||
curPath+=DIR_SEPARATOR;
|
||||
|
||||
logD("opening load file dialog with curPath %s",curPath.c_str());
|
||||
if (sysDialog) {
|
||||
#ifdef USE_NFD
|
||||
|
@ -145,6 +153,14 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, c
|
|||
if (opened) return false;
|
||||
saving=true;
|
||||
curPath=path;
|
||||
|
||||
// strip excess directory separators
|
||||
while (!curPath.empty()) {
|
||||
if (curPath[curPath.size()-1]!=DIR_SEPARATOR) break;
|
||||
curPath.erase(curPath.size()-1);
|
||||
}
|
||||
curPath+=DIR_SEPARATOR;
|
||||
|
||||
logD("opening save file dialog with curPath %s",curPath.c_str());
|
||||
if (sysDialog) {
|
||||
#ifdef USE_NFD
|
||||
|
|
|
@ -1474,8 +1474,12 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
workingDirIns,
|
||||
dpiScale,
|
||||
[this](const char* path) {
|
||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(path);
|
||||
int sampleCountBefore=e->song.sampleLen;
|
||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(path,false);
|
||||
if (!instruments.empty()) {
|
||||
if (e->song.sampleLen!=sampleCountBefore) {
|
||||
e->renderSamplesP();
|
||||
}
|
||||
if (curFileDialog==GUI_FILE_INS_OPEN_REPLACE) {
|
||||
if (prevIns==-3) {
|
||||
prevIns=curIns;
|
||||
|
@ -1506,6 +1510,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_INS_SAVE_OLD:
|
||||
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
"Save Instrument",
|
||||
{"Furnace instrument", "*.fui"},
|
||||
"Furnace instrument{.fui}",
|
||||
workingDirIns,
|
||||
dpiScale
|
||||
);
|
||||
break;
|
||||
case GUI_FILE_INS_SAVE_DMP:
|
||||
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
|
@ -3059,10 +3073,14 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
case SDL_DROPFILE:
|
||||
if (ev.drop.file!=NULL) {
|
||||
int sampleCountBefore=e->song.sampleLen;
|
||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file);
|
||||
DivWavetable* droppedWave=NULL;
|
||||
DivSample* droppedSample=NULL;;
|
||||
if (!instruments.empty()) {
|
||||
if (e->song.sampleLen!=sampleCountBefore) {
|
||||
e->renderSamplesP();
|
||||
}
|
||||
if (!e->getWarnings().empty()) {
|
||||
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
|
||||
}
|
||||
|
@ -3746,11 +3764,17 @@ bool FurnaceGUI::loop() {
|
|||
drawSampleEdit();
|
||||
drawPiano();
|
||||
break;
|
||||
case GUI_SCENE_CHIPS:
|
||||
sysManagerOpen=true;
|
||||
curWindow=GUI_WINDOW_SYS_MANAGER;
|
||||
drawSysManager();
|
||||
break;
|
||||
default:
|
||||
patternOpen=true;
|
||||
curWindow=GUI_WINDOW_PATTERN;
|
||||
drawPattern();
|
||||
drawPiano();
|
||||
drawMobileOrderSel();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3855,6 +3879,7 @@ bool FurnaceGUI::loop() {
|
|||
case GUI_FILE_INS_OPEN:
|
||||
case GUI_FILE_INS_OPEN_REPLACE:
|
||||
case GUI_FILE_INS_SAVE:
|
||||
case GUI_FILE_INS_SAVE_OLD:
|
||||
case GUI_FILE_INS_SAVE_DMP:
|
||||
workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||
break;
|
||||
|
@ -3950,6 +3975,9 @@ bool FurnaceGUI::loop() {
|
|||
if (curFileDialog==GUI_FILE_INS_SAVE) {
|
||||
checkExtension(".fui");
|
||||
}
|
||||
if (curFileDialog==GUI_FILE_INS_SAVE_OLD) {
|
||||
checkExtension(".fui");
|
||||
}
|
||||
if (curFileDialog==GUI_FILE_INS_SAVE_DMP) {
|
||||
checkExtension(".dmp");
|
||||
}
|
||||
|
@ -4041,7 +4069,12 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
case GUI_FILE_INS_SAVE:
|
||||
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
|
||||
e->song.ins[curIns]->save(copyOfName.c_str());
|
||||
e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song);
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_INS_SAVE_OLD:
|
||||
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
|
||||
e->song.ins[curIns]->save(copyOfName.c_str(),true);
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_INS_SAVE_DMP:
|
||||
|
@ -4141,6 +4174,7 @@ bool FurnaceGUI::loop() {
|
|||
bool ask=false;
|
||||
bool warn=false;
|
||||
String warns="there were some warnings/errors while loading instruments:\n";
|
||||
int sampleCountBefore=e->song.sampleLen;
|
||||
for (String i: fileDialog->getFileName()) {
|
||||
std::vector<DivInstrument*> insTemp=e->instrumentFromFile(i.c_str());
|
||||
if (insTemp.empty()) {
|
||||
|
@ -4155,6 +4189,9 @@ bool FurnaceGUI::loop() {
|
|||
instruments.push_back(j);
|
||||
}
|
||||
}
|
||||
if (e->song.sampleLen!=sampleCountBefore) {
|
||||
e->renderSamplesP();
|
||||
}
|
||||
if (warn) {
|
||||
if (instruments.empty()) {
|
||||
if (fileDialog->getFileName().size()>1) {
|
||||
|
@ -4184,8 +4221,12 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
}
|
||||
case GUI_FILE_INS_OPEN_REPLACE: {
|
||||
int sampleCountBefore=e->song.sampleLen;
|
||||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(copyOfName.c_str());
|
||||
if (!instruments.empty()) {
|
||||
if (e->song.sampleLen!=sampleCountBefore) {
|
||||
e->renderSamplesP();
|
||||
}
|
||||
if (!e->getWarnings().empty()) {
|
||||
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
|
||||
}
|
||||
|
@ -5801,6 +5842,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
oldOrdersLen(0),
|
||||
sampleZoom(1.0),
|
||||
prevSampleZoom(1.0),
|
||||
minSampleZoom(1.0),
|
||||
samplePos(0),
|
||||
resizeSize(1024),
|
||||
silenceSize(1024),
|
||||
|
@ -5809,6 +5851,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
amplifyVol(100.0),
|
||||
sampleSelStart(-1),
|
||||
sampleSelEnd(-1),
|
||||
sampleInfo(true),
|
||||
sampleDragActive(false),
|
||||
sampleDragMode(false),
|
||||
sampleDrag16(false),
|
||||
|
|
|
@ -227,6 +227,13 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_SAMPLE_SEL,
|
||||
GUI_COLOR_SAMPLE_SEL_POINT,
|
||||
GUI_COLOR_SAMPLE_NEEDLE,
|
||||
GUI_COLOR_SAMPLE_NEEDLE_PLAYING,
|
||||
GUI_COLOR_SAMPLE_LOOP_POINT,
|
||||
GUI_COLOR_SAMPLE_TIME_BG,
|
||||
GUI_COLOR_SAMPLE_TIME_FG,
|
||||
GUI_COLOR_SAMPLE_CHIP_DISABLED,
|
||||
GUI_COLOR_SAMPLE_CHIP_ENABLED,
|
||||
GUI_COLOR_SAMPLE_CHIP_WARNING,
|
||||
|
||||
GUI_COLOR_PAT_MANAGER_NULL,
|
||||
GUI_COLOR_PAT_MANAGER_USED,
|
||||
|
@ -313,6 +320,7 @@ enum FurnaceGUIFileDialogs {
|
|||
GUI_FILE_INS_OPEN,
|
||||
GUI_FILE_INS_OPEN_REPLACE,
|
||||
GUI_FILE_INS_SAVE,
|
||||
GUI_FILE_INS_SAVE_OLD,
|
||||
GUI_FILE_INS_SAVE_DMP,
|
||||
GUI_FILE_WAVE_OPEN,
|
||||
GUI_FILE_WAVE_OPEN_REPLACE,
|
||||
|
@ -507,6 +515,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_INS_LIST_OPEN,
|
||||
GUI_ACTION_INS_LIST_OPEN_REPLACE,
|
||||
GUI_ACTION_INS_LIST_SAVE,
|
||||
GUI_ACTION_INS_LIST_SAVE_OLD,
|
||||
GUI_ACTION_INS_LIST_SAVE_DMP,
|
||||
GUI_ACTION_INS_LIST_MOVE_UP,
|
||||
GUI_ACTION_INS_LIST_MOVE_DOWN,
|
||||
|
@ -913,7 +922,6 @@ struct FurnaceGUISysDefChip {
|
|||
struct FurnaceGUISysDef {
|
||||
const char* name;
|
||||
String definition;
|
||||
FurnaceGUISysDef(const char* n, std::initializer_list<int> def);
|
||||
FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def);
|
||||
};
|
||||
|
||||
|
@ -1073,6 +1081,7 @@ class FurnaceGUI {
|
|||
int displayInsTypeListMakeInsSample;
|
||||
float mobileMenuPos, autoButtonSize;
|
||||
const int* curSysSection;
|
||||
DivInstrumentFM opllPreview;
|
||||
|
||||
String pendingRawSample;
|
||||
int pendingRawSampleDepth, pendingRawSampleChannels;
|
||||
|
@ -1581,12 +1590,14 @@ class FurnaceGUI {
|
|||
// sample editor specific
|
||||
double sampleZoom;
|
||||
double prevSampleZoom;
|
||||
double minSampleZoom;
|
||||
int samplePos;
|
||||
int resizeSize, silenceSize;
|
||||
double resampleTarget;
|
||||
int resampleStrat;
|
||||
float amplifyVol;
|
||||
int sampleSelStart, sampleSelEnd;
|
||||
bool sampleInfo;
|
||||
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
|
||||
void* sampleDragTarget;
|
||||
ImVec2 sampleDragStart;
|
||||
|
@ -1719,8 +1730,9 @@ class FurnaceGUI {
|
|||
void popToggleColors();
|
||||
|
||||
void drawMobileControls();
|
||||
void drawMobileOrderSel();
|
||||
void drawEditControls();
|
||||
void drawSongInfo();
|
||||
void drawSongInfo(bool asChild=false);
|
||||
void drawOrders();
|
||||
void drawPattern();
|
||||
void drawInsList(bool asChild=false);
|
||||
|
|
|
@ -590,6 +590,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("INS_LIST_OPEN", "Open", 0),
|
||||
D("INS_LIST_OPEN_REPLACE", "Open (replace current)", 0),
|
||||
D("INS_LIST_SAVE", "Save", 0),
|
||||
D("INS_LIST_SAVE_OLD", "Save (legacy .fui)", 0),
|
||||
D("INS_LIST_SAVE_DMP", "Save (.dmp)", 0),
|
||||
D("INS_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
|
||||
D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
|
||||
|
@ -859,6 +860,13 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_SAMPLE_SEL,"",ImVec4(0.26f,0.59f,0.98f,0.25f)),
|
||||
D(GUI_COLOR_SAMPLE_SEL_POINT,"",ImVec4(0.06f,0.53f,0.98f,0.5f)),
|
||||
D(GUI_COLOR_SAMPLE_NEEDLE,"",ImVec4(1.0f,0.8f,0.0f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_NEEDLE_PLAYING,"",ImVec4(0.2f,1.0f,0.0f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_LOOP_POINT,"",ImVec4(1.0f,0.0f,0.0f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_TIME_BG,"",ImVec4(0.1f,0.11f,0.12f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_TIME_FG,"",ImVec4(0.4f,0.4f,0.4f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_CHIP_DISABLED,"",ImVec4(0.6f,0.6f,0.6f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_CHIP_ENABLED,"",ImVec4(0.3f,1.0f,0.3f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_CHIP_WARNING,"",ImVec4(1.0f,0.75f,0.3f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_PAT_MANAGER_NULL,"",ImVec4(0.15f,0.15f,0.15f,1.0f)),
|
||||
D(GUI_COLOR_PAT_MANAGER_USED,"",ImVec4(0.15f,1.0f,0.15f,1.0f)),
|
||||
|
|
|
@ -28,12 +28,16 @@
|
|||
#include <imgui.h>
|
||||
#include "plot_nolerp.h"
|
||||
|
||||
extern "C" {
|
||||
#include "../../extern/Nuked-OPLL/opll.h"
|
||||
}
|
||||
|
||||
const char* ssgEnvTypes[8]={
|
||||
"Down Down Down", "Down.", "Down Up Down Up", "Down UP", "Up Up Up", "Up.", "Up Down Up Down", "Up DOWN"
|
||||
};
|
||||
|
||||
const char* fmParamNames[3][32]={
|
||||
{"Algorithm", "Feedback", "LFO > Freq", "LFO > Amp", "Attack", "Decay", "Decay 2", "Release", "Sustain", "Level", "EnvScale", "Multiplier", "Detune", "Detune 2", "SSG-EG", "AM", "AM Depth", "Vibrato Depth", "Sustained", "Sustained", "Level Scaling", "Sustain", "Vibrato", "Waveform", "Key Scale Rate", "OP2 Half Sine", "OP1 Half Sine", "EnvShift", "Reverb", "Fine", "LFO2 > Freq", "LFO2 > Amp"},
|
||||
{"Algorithm", "Feedback", "LFO > Freq", "LFO > Amp", "Attack", "Decay", "Decay 2", "Release", "Sustain", "Level", "EnvScale", "Multiplier", "Detune", "Detune 2", "SSG-EG", "AM", "AM Depth", "Vibrato Depth", "Sustained", "Sustained", "Level Scaling", "Sustain", "Vibrato", "Waveform", "Scale Rate", "OP2 Half Sine", "OP1 Half Sine", "EnvShift", "Reverb", "Fine", "LFO2 > Freq", "LFO2 > Amp"},
|
||||
{"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "SR", "RR", "SL", "TL", "KS", "MULT", "DT", "DT2", "SSG-EG", "AM", "AMD", "FMD", "EGT", "EGT", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM", "EGS", "REV", "Fine", "FMS/PMS2", "AMS2"},
|
||||
{"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "D2R", "RR", "SL", "TL", "RS", "MULT", "DT", "DT2", "SSG-EG", "AM", "DAM", "DVB", "EGT", "EGS", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM", "EGS", "REV", "Fine", "FMS/PMS2", "AMS2"}
|
||||
};
|
||||
|
@ -333,6 +337,16 @@ const char* snesGainModes[5]={
|
|||
"Increase (bent line)"
|
||||
};
|
||||
|
||||
const int detuneMap[2][8]={
|
||||
{-3, -2, -1, 0, 1, 2, 3, 4},
|
||||
{ 7, 6, 5, 0, 1, 2, 3, 4}
|
||||
};
|
||||
|
||||
const int detuneUnmap[2][11]={
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0},
|
||||
{0, 0, 0, 3, 4, 5, 6, 7, 2, 1, 0}
|
||||
};
|
||||
|
||||
// do not change these!
|
||||
// anything other than a checkbox will look ugly!
|
||||
//
|
||||
|
@ -2153,6 +2167,9 @@ void FurnaceGUI::drawInsEdit() {
|
|||
doAction(GUI_ACTION_INS_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
|
||||
if (ImGui::MenuItem("save in legacy format...")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
|
||||
}
|
||||
if (ImGui::MenuItem("save as .dmp...")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
|
||||
}
|
||||
|
@ -2433,8 +2450,38 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->fm.opllPreset==16) {
|
||||
ImGui::Text("this volume slider only works in compatibility (non-drums) system.");
|
||||
}
|
||||
|
||||
// update OPLL preset preview
|
||||
if (ins->fm.opllPreset>0 && ins->fm.opllPreset<16) {
|
||||
const opll_patch_t* patchROM=OPLL_GetPatchROM(opll_type_ym2413);
|
||||
|
||||
const opll_patch_t* patch=&patchROM[ins->fm.opllPreset-1];
|
||||
|
||||
opllPreview.alg=0;
|
||||
opllPreview.fb=patch->fb;
|
||||
opllPreview.fms=patch->dm;
|
||||
opllPreview.ams=patch->dc;
|
||||
|
||||
opllPreview.op[0].tl=patch->tl;
|
||||
opllPreview.op[1].tl=ins->fm.op[1].tl;
|
||||
|
||||
for (int i=0; i<2; i++) {
|
||||
opllPreview.op[i].am=patch->am[i];
|
||||
opllPreview.op[i].vib=patch->vib[i];
|
||||
opllPreview.op[i].ssgEnv=patch->et[i]?8:0;
|
||||
opllPreview.op[i].ksr=patch->ksr[i];
|
||||
opllPreview.op[i].mult=patch->multi[i];
|
||||
opllPreview.op[i].ar=patch->ar[i];
|
||||
opllPreview.op[i].dr=patch->dr[i];
|
||||
opllPreview.op[i].sl=patch->sl[i];
|
||||
opllPreview.op[i].rr=patch->rr[i];
|
||||
}
|
||||
if (willDisplayOps) {
|
||||
}
|
||||
}
|
||||
|
||||
DivInstrumentFM& fmOrigin=(ins->type==DIV_INS_OPLL && ins->fm.opllPreset>0 && ins->fm.opllPreset<16)?opllPreview:ins->fm;
|
||||
|
||||
ImGui::BeginDisabled(!willDisplayOps);
|
||||
if (settings.fmLayout==0) {
|
||||
int numCols=15;
|
||||
if (ins->type==DIV_INS_OPL ||ins->type==DIV_INS_OPL_DRUMS) numCols=13;
|
||||
|
@ -2584,7 +2631,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
float sliderHeight=32.0f*dpiScale;
|
||||
|
||||
for (int i=0; i<opCount; i++) {
|
||||
DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
|
||||
DivInstrumentFM::Operator& op=fmOrigin.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
|
@ -2595,12 +2642,12 @@ void FurnaceGUI::drawInsEdit() {
|
|||
mod=false;
|
||||
} else if (opCount==4) {
|
||||
if (ins->type==DIV_INS_OPL) {
|
||||
if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false;
|
||||
if (opIsOutputOPL[fmOrigin.alg&3][i]) mod=false;
|
||||
} else {
|
||||
if (opIsOutput[ins->fm.alg&7][i]) mod=false;
|
||||
if (opIsOutput[fmOrigin.alg&7][i]) mod=false;
|
||||
}
|
||||
} else {
|
||||
if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false;
|
||||
if (i==1 || (ins->type==DIV_INS_OPL && (fmOrigin.alg&1))) mod=false;
|
||||
}
|
||||
if (mod) {
|
||||
pushAccentColors(
|
||||
|
@ -2625,7 +2672,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
String opNameLabel;
|
||||
if (ins->type==DIV_INS_OPL_DRUMS) {
|
||||
opNameLabel=fmt::sprintf("%s",oplDrumNames[i]);
|
||||
} else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) {
|
||||
} else if (ins->type==DIV_INS_OPL && fmOrigin.opllPreset==16) {
|
||||
if (i==1) {
|
||||
opNameLabel="Kick";
|
||||
} else {
|
||||
|
@ -2739,11 +2786,13 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
|
||||
int detune=(op.dt&7)-(settings.unsignedDetune?0:3);
|
||||
int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7];
|
||||
ImGui::TableNextColumn();
|
||||
CENTER_VSLIDER;
|
||||
if (CWVSliderInt("##DT",ImVec2(20.0f*dpiScale,sliderHeight),&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4)) { PARAMETER
|
||||
op.dt=detune+(settings.unsignedDetune?0:3);
|
||||
if (detune<-3) detune=-3;
|
||||
if (detune>7) detune=7;
|
||||
op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3];
|
||||
}
|
||||
|
||||
if (ins->type!=DIV_INS_FM) {
|
||||
|
@ -2847,7 +2896,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type);
|
||||
drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,fmOrigin.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type);
|
||||
|
||||
if (settings.separateFMColors) {
|
||||
popAccentColors();
|
||||
|
@ -2876,7 +2925,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(8.0f*dpiScale,4.0f*dpiScale));
|
||||
if (ImGui::BeginTable("AltFMOperators",columns,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersInner)) {
|
||||
for (int i=0; i<opCount; i++) {
|
||||
DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
|
||||
DivInstrumentFM::Operator& op=fmOrigin.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
|
||||
if ((settings.fmLayout!=6 && ((i+1)&1)) || i==0 || settings.fmLayout==5) ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(fmt::sprintf("op%d",i).c_str());
|
||||
|
@ -2888,12 +2937,12 @@ void FurnaceGUI::drawInsEdit() {
|
|||
mod=false;
|
||||
} else if (opCount==4) {
|
||||
if (ins->type==DIV_INS_OPL) {
|
||||
if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false;
|
||||
if (opIsOutputOPL[fmOrigin.alg&3][i]) mod=false;
|
||||
} else {
|
||||
if (opIsOutput[ins->fm.alg&7][i]) mod=false;
|
||||
if (opIsOutput[fmOrigin.alg&7][i]) mod=false;
|
||||
}
|
||||
} else {
|
||||
if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false;
|
||||
if (i==1 || (ins->type==DIV_INS_OPL && (fmOrigin.alg&1))) mod=false;
|
||||
}
|
||||
if (mod) {
|
||||
pushAccentColors(
|
||||
|
@ -2915,7 +2964,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::Dummy(ImVec2(dpiScale,dpiScale));
|
||||
if (ins->type==DIV_INS_OPL_DRUMS) {
|
||||
snprintf(tempID,1024,"%s",oplDrumNames[i]);
|
||||
} else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) {
|
||||
} else if (ins->type==DIV_INS_OPL && fmOrigin.opllPreset==16) {
|
||||
if (i==1) {
|
||||
snprintf(tempID,1024,"Envelope 2 (kick only)");
|
||||
} else {
|
||||
|
@ -3078,11 +3127,13 @@ void FurnaceGUI::drawInsEdit() {
|
|||
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
|
||||
P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
|
||||
|
||||
int detune=(op.dt&7)-(settings.unsignedDetune?0:3);
|
||||
int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7];
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT));
|
||||
if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER
|
||||
op.dt=detune+(settings.unsignedDetune?0:3);
|
||||
if (detune<-3) detune=-3;
|
||||
if (detune>7) detune=7;
|
||||
op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3];
|
||||
} rightClickable
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
|
@ -3100,11 +3151,13 @@ void FurnaceGUI::drawInsEdit() {
|
|||
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
|
||||
P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
|
||||
|
||||
int detune=(op.dt&7)-(settings.unsignedDetune?0:3);
|
||||
int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7];
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT));
|
||||
if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER
|
||||
op.dt=detune+(settings.unsignedDetune?0:3);
|
||||
if (detune<-3) detune=-3;
|
||||
if (detune>7) detune=7;
|
||||
op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3];
|
||||
} rightClickable
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
|
@ -3118,7 +3171,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
case DIV_INS_OPLL:
|
||||
// waveform
|
||||
drawWaveform(i==0?(ins->fm.ams&1):(ins->fm.fms&1),ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight));
|
||||
drawWaveform(i==0?(fmOrigin.ams&1):(fmOrigin.fms&1),ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight));
|
||||
|
||||
// params
|
||||
ImGui::Separator();
|
||||
|
@ -3241,11 +3294,13 @@ void FurnaceGUI::drawInsEdit() {
|
|||
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
|
||||
P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
|
||||
|
||||
int detune=(op.dt&7)-(settings.unsignedDetune?0:3);
|
||||
int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7];
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT));
|
||||
if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER
|
||||
op.dt=detune+(settings.unsignedDetune?0:3);
|
||||
if (detune<-3) detune=-3;
|
||||
if (detune>7) detune=7;
|
||||
op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3];
|
||||
} rightClickable
|
||||
}
|
||||
|
||||
|
@ -3270,7 +3325,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->type==DIV_INS_OPZ) {
|
||||
envHeight-=ImGui::GetFrameHeightWithSpacing()*2.0f;
|
||||
}
|
||||
drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,envHeight),ins->type);
|
||||
drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,fmOrigin.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,envHeight),ins->type);
|
||||
|
||||
if (ins->type==DIV_INS_OPZ) {
|
||||
ImGui::Separator();
|
||||
|
@ -3353,7 +3408,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
if (ImGui::BeginTable("FMOperators",columns,ImGuiTableFlags_SizingStretchSame)) {
|
||||
for (int i=0; i<opCount; i++) {
|
||||
DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
|
||||
DivInstrumentFM::Operator& op=fmOrigin.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
|
||||
if ((settings.fmLayout!=3 && ((i+1)&1)) || i==0 || settings.fmLayout==2) ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Separator();
|
||||
|
@ -3366,12 +3421,12 @@ void FurnaceGUI::drawInsEdit() {
|
|||
mod=false;
|
||||
} else if (opCount==4) {
|
||||
if (ins->type==DIV_INS_OPL) {
|
||||
if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false;
|
||||
if (opIsOutputOPL[fmOrigin.alg&3][i]) mod=false;
|
||||
} else {
|
||||
if (opIsOutput[ins->fm.alg&7][i]) mod=false;
|
||||
if (opIsOutput[fmOrigin.alg&7][i]) mod=false;
|
||||
}
|
||||
} else {
|
||||
if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false;
|
||||
if (i==1 || (ins->type==DIV_INS_OPL && (fmOrigin.alg&1))) mod=false;
|
||||
}
|
||||
if (mod) {
|
||||
pushAccentColors(
|
||||
|
@ -3396,7 +3451,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::SameLine();
|
||||
if (ins->type==DIV_INS_OPL_DRUMS) {
|
||||
opNameLabel=fmt::sprintf("%s",oplDrumNames[i]);
|
||||
} else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) {
|
||||
} else if (ins->type==DIV_INS_OPL && fmOrigin.opllPreset==16) {
|
||||
if (i==1) {
|
||||
opNameLabel="Envelope 2 (kick only)";
|
||||
} else {
|
||||
|
@ -3464,7 +3519,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
|
||||
//52.0 controls vert scaling; default 96
|
||||
drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type);
|
||||
drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,fmOrigin.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type);
|
||||
//P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable
|
||||
if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) {
|
||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \
|
||||
|
@ -3616,12 +3671,14 @@ void FurnaceGUI::drawInsEdit() {
|
|||
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) {
|
||||
if (!(ins->type==DIV_INS_OPZ && op.egt)) {
|
||||
int detune=(op.dt&7)-(settings.unsignedDetune?0:3);
|
||||
int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4)) { PARAMETER
|
||||
op.dt=detune+(settings.unsignedDetune?0:3);
|
||||
if (detune<-3) detune=-3;
|
||||
if (detune>7) detune=7;
|
||||
op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3];
|
||||
} rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_DT));
|
||||
|
@ -3682,7 +3739,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("FM Macros")) {
|
||||
|
|
|
@ -18,8 +18,45 @@
|
|||
*/
|
||||
|
||||
#include "gui.h"
|
||||
#include <fmt/printf.h>
|
||||
#include "IconsFontAwesome4.h"
|
||||
#include <imgui.h>
|
||||
#include "imgui_internal.h"
|
||||
|
||||
void FurnaceGUI::drawMobileOrderSel() {
|
||||
if (!portrait) return;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0.0f,mobileMenuPos*-0.65*canvasH));
|
||||
ImGui::SetNextWindowSize(ImVec2(canvasW,0.12*canvasW));
|
||||
if (ImGui::Begin("OrderSel",NULL,globalWinFlags)) {
|
||||
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||
ImGuiWindow* window=ImGui::GetCurrentWindow();
|
||||
ImGuiStyle& style=ImGui::GetStyle();
|
||||
|
||||
ImVec2 size=ImGui::GetContentRegionAvail();
|
||||
|
||||
ImVec2 minArea=window->DC.CursorPos;
|
||||
ImVec2 maxArea=ImVec2(
|
||||
minArea.x+size.x,
|
||||
minArea.y+size.y
|
||||
);
|
||||
ImRect rect=ImRect(minArea,maxArea);
|
||||
ImGui::ItemSize(size,style.FramePadding.y);
|
||||
ImU32 col=ImGui::GetColorU32(ImGuiCol_Text);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID("OrderSelW"))) {
|
||||
String text=fmt::sprintf("%.2X",curOrder);
|
||||
|
||||
ImVec2 pos=ImLerp(minArea,maxArea,ImVec2(0.5,0.0));
|
||||
ImGui::PushFont(bigFont);
|
||||
ImVec2 textSize=ImGui::CalcTextSize(text.c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
pos.x-=textSize.x*0.5*(size.y/textSize.y);
|
||||
|
||||
dl->AddText(bigFont,size.y,pos,col,text.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawOrders() {
|
||||
static char selID[4096];
|
||||
|
|
|
@ -133,14 +133,14 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
for (int j=0; j<chans; j++) {
|
||||
// check if channel is not hidden
|
||||
if (!e->curSubSong->chanShow[j]) {
|
||||
patChanX[j]=ImGui::GetCursorPosX();
|
||||
patChanX[j]=ImGui::GetCursorScreenPos().x;
|
||||
continue;
|
||||
}
|
||||
int chanVolMax=e->getMaxVolumeChan(j);
|
||||
if (chanVolMax<1) chanVolMax=1;
|
||||
const DivPattern* pat=patCache[j];
|
||||
ImGui::TableNextColumn();
|
||||
patChanX[j]=ImGui::GetCursorPosX();
|
||||
patChanX[j]=ImGui::GetCursorScreenPos().x;
|
||||
|
||||
// selection highlight flags
|
||||
int sel1XSum=sel1.xCoarse*32+sel1.xFine;
|
||||
|
@ -331,7 +331,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
|
|||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
patChanX[chans]=ImGui::GetCursorPosX();
|
||||
patChanX[chans]=ImGui::GetCursorScreenPos().x;
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawPattern() {
|
||||
|
@ -376,8 +376,8 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0.0f,0.0f));
|
||||
if (mobileUI) {
|
||||
patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f));
|
||||
patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)-(pianoOpen?(0.4*canvasW):0.0f)):ImVec2(canvasW-(0.16*canvasH),canvasH-(pianoOpen?(0.3*canvasH):0.0f)));
|
||||
patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)+(0.12*canvasW)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f));
|
||||
patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)-(0.12*canvasW)-(pianoOpen?(0.4*canvasW):0.0f)):ImVec2(canvasW-(0.16*canvasH),canvasH-(pianoOpen?(0.3*canvasH):0.0f)));
|
||||
ImGui::SetNextWindowPos(patWindowPos);
|
||||
ImGui::SetNextWindowSize(patWindowSize);
|
||||
}
|
||||
|
@ -652,7 +652,7 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
case 3: // split button
|
||||
ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale));
|
||||
ImGui::SetCursorPosX(minLabelArea.x);
|
||||
//ImGui::SetCursorPosX(minLabelArea.x);
|
||||
ImGui::TextUnformatted(chanID);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushFont(mainFont);
|
||||
|
@ -966,7 +966,7 @@ void FurnaceGUI::drawPattern() {
|
|||
if (fancyPattern) { // visualizer
|
||||
e->getCommandStream(cmdStream);
|
||||
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||
ImVec2 off=ImGui::GetWindowPos();
|
||||
ImVec2 off=ImVec2(0.0f,ImGui::GetWindowPos().y);
|
||||
|
||||
// commands
|
||||
for (DivCommand& i: cmdStream) {
|
||||
|
|
|
@ -158,7 +158,7 @@ void FurnaceGUI::drawPiano() {
|
|||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (pianoInputPadMode==1 && cursor.xFine>0) {
|
||||
if (pianoInputPadMode==1 && cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) {
|
||||
ImVec2 buttonSize=ImGui::GetContentRegionAvail();
|
||||
if (ImGui::BeginTable("InputPadP",8,ImGuiTableFlags_SizingFixedSame)) {
|
||||
ImGui::TableNextRow();
|
||||
|
@ -266,7 +266,7 @@ void FurnaceGUI::drawPiano() {
|
|||
} else {
|
||||
int bottomNotes=7*oct;
|
||||
// evaluate input
|
||||
for (TouchPoint& i: activePoints) {
|
||||
if (canInput) for (TouchPoint& i: activePoints) {
|
||||
if (rect.Contains(ImVec2(i.x,i.y))) {
|
||||
// top
|
||||
int o=((i.x-rect.Min.x)/(rect.Max.x-rect.Min.x))*oct;
|
||||
|
@ -430,7 +430,7 @@ void FurnaceGUI::drawPiano() {
|
|||
ImGui::End();
|
||||
|
||||
// draw input pad if necessary
|
||||
if ((pianoInputPadMode==2 && cursor.xFine>0) || pianoInputPadMode==3) {
|
||||
if (curWindow==GUI_WINDOW_PATTERN && ((pianoInputPadMode==2 && cursor.xFine>0) || pianoInputPadMode==3)) {
|
||||
if (ImGui::Begin("Input Pad",NULL,ImGuiWindowFlags_NoTitleBar)) {
|
||||
ImGui::BeginDisabled(cursor.xFine==0);
|
||||
if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) {
|
||||
|
|
1125
src/gui/presets.cpp
1125
src/gui/presets.cpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1540,11 +1540,6 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.waveLayout=waveLayoutB;
|
||||
}
|
||||
|
||||
bool sampleLayoutB=settings.sampleLayout;
|
||||
if (ImGui::Checkbox("Use compact sample editor",&sampleLayoutB)) {
|
||||
settings.sampleLayout=sampleLayoutB;
|
||||
}
|
||||
|
||||
bool oldMacroVSliderB=settings.oldMacroVSlider;
|
||||
if (ImGui::Checkbox("Use classic macro editor vertical slider",&oldMacroVSliderB)) {
|
||||
settings.oldMacroVSlider=oldMacroVSliderB;
|
||||
|
@ -1848,12 +1843,19 @@ void FurnaceGUI::drawSettings() {
|
|||
if (ImGui::TreeNode("Sample Editor")) {
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_BG,"Background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_FG,"Waveform");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_TIME_BG,"Time background");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_TIME_FG,"Time text");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_LOOP,"Loop region");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CENTER,"Center guide");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_GRID,"Grid");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL,"Selection");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_SEL_POINT,"Selection points");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_NEEDLE,"Preview needle");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_NEEDLE_PLAYING,"Playing needles");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_LOOP_POINT,"Loop markers");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_DISABLED,"Chip select: disabled");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_ENABLED,"Chip select: enabled");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_SAMPLE_CHIP_WARNING,"Chip select: enabled (failure)");
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Pattern Manager")) {
|
||||
|
|
|
@ -22,14 +22,15 @@
|
|||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include "intConst.h"
|
||||
|
||||
void FurnaceGUI::drawSongInfo() {
|
||||
void FurnaceGUI::drawSongInfo(bool asChild) {
|
||||
if (nextWindow==GUI_WINDOW_SONG_INFO) {
|
||||
songInfoOpen=true;
|
||||
ImGui::SetNextWindowFocus();
|
||||
nextWindow=GUI_WINDOW_NOTHING;
|
||||
}
|
||||
if (!songInfoOpen) return;
|
||||
if (ImGui::Begin("Song Information",&songInfoOpen,globalWinFlags)) {
|
||||
if (!songInfoOpen && !asChild) return;
|
||||
bool began=asChild?ImGui::BeginChild("Song Information"):ImGui::Begin("Song Information",&songInfoOpen,globalWinFlags);
|
||||
if (began) {
|
||||
if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) {
|
||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
|
||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
|
||||
|
@ -240,6 +241,10 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO;
|
||||
if (!asChild && ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO;
|
||||
if (asChild) {
|
||||
ImGui::EndChild();
|
||||
} else {
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
int clockSel=flags.getInt("clockSel",0);
|
||||
int chipType=flags.getInt("chipType",0);
|
||||
bool noPhaseReset=flags.getBool("noPhaseReset",false);
|
||||
bool noEasyNoise=flags.getBool("noEasyNoise",false);
|
||||
|
||||
ImGui::Text("Clock rate:");
|
||||
if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) {
|
||||
|
@ -152,11 +153,16 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
altered=true;
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("Disable easy period to note mapping on upper octaves",&noEasyNoise)) {
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("clockSel",clockSel);
|
||||
flags.set("chipType",chipType);
|
||||
flags.set("noPhaseReset",noPhaseReset);
|
||||
flags.set("noEasyNoise",noEasyNoise);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
@ -318,6 +324,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
case DIV_SYSTEM_VRC7: {
|
||||
int clockSel=flags.getInt("clockSel",0);
|
||||
int patchSet=flags.getInt("patchSet",0);
|
||||
bool noTopHatFreq=flags.getBool("noTopHatFreq",false);
|
||||
|
||||
ImGui::Text("Clock rate:");
|
||||
if (ImGui::RadioButton("NTSC (3.58MHz)",clockSel==0)) {
|
||||
|
@ -356,12 +363,19 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
}
|
||||
}
|
||||
|
||||
if (type==DIV_SYSTEM_OPLL_DRUMS) {
|
||||
if (ImGui::Checkbox("Ignore top/hi-hat frequency changes",&noTopHatFreq)) {
|
||||
altered=true;
|
||||
}
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("clockSel",clockSel);
|
||||
if (type!=DIV_SYSTEM_VRC7) {
|
||||
flags.set("patchSet",patchSet);
|
||||
}
|
||||
flags.set("noTopHatFreq",noTopHatFreq);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
@ -1529,12 +1543,25 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_T6W28: {
|
||||
bool noEasyNoise=flags.getBool("noEasyNoise",false);
|
||||
|
||||
if (ImGui::Checkbox("Disable easy period to note mapping on upper octaves",&noEasyNoise)) {
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("noEasyNoise",noEasyNoise);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_SWAN:
|
||||
case DIV_SYSTEM_VERA:
|
||||
case DIV_SYSTEM_BUBSYS_WSG:
|
||||
case DIV_SYSTEM_YMU759:
|
||||
case DIV_SYSTEM_PET:
|
||||
case DIV_SYSTEM_T6W28:
|
||||
case DIV_SYSTEM_VBOY:
|
||||
ImGui::Text("nothing to configure");
|
||||
break;
|
||||
|
|
|
@ -30,6 +30,14 @@ void FurnaceGUI::drawSysManager() {
|
|||
nextWindow=GUI_WINDOW_NOTHING;
|
||||
}
|
||||
if (!sysManagerOpen) return;
|
||||
if (mobileUI) {
|
||||
patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f));
|
||||
patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)):ImVec2(canvasW-(0.16*canvasH),canvasH));
|
||||
ImGui::SetNextWindowPos(patWindowPos);
|
||||
ImGui::SetNextWindowSize(patWindowSize);
|
||||
} else {
|
||||
//ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||
}
|
||||
if (ImGui::Begin("Chip Manager",&sysManagerOpen,globalWinFlags)) {
|
||||
ImGui::Checkbox("Preserve channel order",&preserveChanPos);
|
||||
if (ImGui::BeginTable("SystemList",3)) {
|
||||
|
|
Loading…
Reference in New Issue