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

This commit is contained in:
cam900 2022-11-30 17:39:43 +09:00
commit 7867b59580
85 changed files with 6024 additions and 3108 deletions

View File

@ -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 }}

View File

@ -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)

View File

@ -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

View File

@ -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)

BIN
demos/Mars_Bar_On_Mars.fur Normal file

Binary file not shown.

View File

@ -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.

BIN
demos/ice-cap-nc30.fur Normal file

Binary file not shown.

View File

@ -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;
//////////////////////////////////////////////////////////////////////

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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
```

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -208,7 +208,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}*/
// Game Boy arp+soundLen screwery
ds.systemFlags[0].set("enoughAlready",true);
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

View File

@ -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();
reader.readS(); // reserved
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!";
}
unsigned int dataPtr=reader.readI();
reader.seek(dataPtr,SEEK_SET);
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,7 +1980,12 @@ 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;
}
}
switch (format) {

File diff suppressed because it is too large Load Diff

View File

@ -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;
@ -591,6 +664,51 @@ struct DivInstrument {
DivInstrumentSoundUnit su;
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.
@ -598,20 +716,28 @@ struct DivInstrument {
*/
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.

View File

@ -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) {
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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) {

View File

@ -82,7 +82,7 @@ class DivPlatformOPLL: public DivDispatch {
bool useYMFM;
bool drums;
bool properDrums, properDrumsSys;
bool properDrums, properDrumsSys, noTopHatFreq;
bool vrc7;
unsigned char patchSet;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();
};

View File

@ -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;
}

View File

@ -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:

View File

@ -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) {

View File

@ -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++) {

View File

@ -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);

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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:

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -142,6 +142,8 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
unsigned char sampleBank;
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;

View File

@ -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;
}

View File

@ -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();

View File

@ -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));
}

View File

@ -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();
};

View File

@ -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"},

View File

@ -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);

View File

@ -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();

View File

@ -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",

View File

@ -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;

View File

@ -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);
}

View File

@ -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())) {

View File

@ -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;

View File

@ -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:

View File

@ -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

View File

@ -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),

View File

@ -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);

View File

@ -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)),

File diff suppressed because it is too large Load Diff

View File

@ -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];

View File

@ -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) {

View File

@ -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)) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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")) {

View File

@ -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;
ImGui::End();
if (!asChild && ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO;
if (asChild) {
ImGui::EndChild();
} else {
ImGui::End();
}
}

View File

@ -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;

View File

@ -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)) {