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