diff options
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | fm_banks/adldata-cache.dat | bin | 534937 -> 535056 bytes | |||
-rw-r--r-- | fm_banks/doom2/DMXOPL-by-sneakernets.op2 | bin | 11908 -> 11908 bytes | |||
-rw-r--r-- | fm_banks/wopl_files/DMXOPL3-by-sneakernets.wopl | bin | 15959 -> 15959 bytes | |||
-rw-r--r-- | include/adlmidi.h | 46 | ||||
-rw-r--r-- | libADLMIDI-test.pro | 9 | ||||
-rw-r--r-- | src/adldata.cpp | 470 | ||||
-rw-r--r-- | src/adlmidi.cpp | 245 | ||||
-rw-r--r-- | src/adlmidi_load.cpp | 127 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 1116 | ||||
-rw-r--r-- | src/adlmidi_opl3.cpp | 6 | ||||
-rw-r--r-- | src/adlmidi_private.cpp | 14 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 287 | ||||
-rw-r--r-- | src/dbopl.cpp | 9 | ||||
-rw-r--r-- | src/dbopl.h | 8 | ||||
-rw-r--r-- | utils/midiplay/adlmidiplay.cpp | 75 |
16 files changed, 1364 insertions, 1060 deletions
@@ -31,6 +31,7 @@ Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 e * Sustain enable/disable * MIDI and RMI file support * loopStart / loopEnd tag support (Final Fantasy VII) +* 111-th controller based loop start (RPG-Maker) * Use automatic arpeggio with chords to relieve channel pressure * Support for multiple concurrent MIDI synthesizers (per-track device/port select FF 09 message), can be used to overcome 16 channel limit * Support for playing Id-software Music File format (IMF) @@ -93,7 +94,7 @@ You need to make in the any IDE a library project and put into it next files **Tip 2**: To compile libADLMIDI without embedded banks, define the `DISABLE_EMBEDDED_BANKS` macro and remove building of the `adldata.cpp` file in your project. # Example -In the src/midiplay you will found alone CPP file which an example of library usage. +In the utils/midiplay you will found an example project which uses this library. That example is a simple audio player based on SDL Audio usage. To build that example you will need to have installed SDL2 library. @@ -105,12 +106,9 @@ To build that example you will need to have installed SDL2 library. # Todo * Add hooks to places where are originally was old UI calls to be able to reimplement original ADLMIDI tool which will use libADLMIDI as backend and also to be able to implement various other things based on MIDI event hooking. -* Move most of variables in the `ADL_MIDIPlayer` structure away into `MIDIplay` class. * Implement WOPL Version 3 which will contain pre-calculated `ms_sound_kon` and `ms_sound_koff` values per every instrument. * Implement multi-bank to support GS or XG standards. -* Add tempo multiplier to have tempo changing ability * Add support of MIDI Format 2 files (FL Studio made MIDI-files are wired and opening of those files making lossy of tempo and some meta-information events) -* Time based Seek/Tell support * Support of title and other meta-tags retrieving * Support of real-time listening of incoming MIDI-commands. That will allow to use library as software MIDI Output device @@ -126,6 +124,12 @@ To build that example you will need to have installed SDL2 library. * Demo tool now can correctly record WAVs and now can correctly deal with CTRL+C termination * When loop is disabled, loop points will be ignored * Loop now is disabled by default + * Reworked internal storage of MIDI events to easier pre-process them and retrieve any useful information before play them + * Added ability to get seconds time position and song length + * Added seekability support + * Fixed IMF playing when passing file as path nor as memory block + * Added ability to get time position of every loop point if presented + * Added ability to change playing tempo by giving multiplier (how faster or slower than original) * ... ## 1.2.1 2017-07-30 diff --git a/fm_banks/adldata-cache.dat b/fm_banks/adldata-cache.dat Binary files differindex 383c6f4..e7c74fa 100644 --- a/fm_banks/adldata-cache.dat +++ b/fm_banks/adldata-cache.dat diff --git a/fm_banks/doom2/DMXOPL-by-sneakernets.op2 b/fm_banks/doom2/DMXOPL-by-sneakernets.op2 Binary files differindex 853ac42..31bb5e4 100644 --- a/fm_banks/doom2/DMXOPL-by-sneakernets.op2 +++ b/fm_banks/doom2/DMXOPL-by-sneakernets.op2 diff --git a/fm_banks/wopl_files/DMXOPL3-by-sneakernets.wopl b/fm_banks/wopl_files/DMXOPL3-by-sneakernets.wopl Binary files differindex dbd72c2..0205bfa 100644 --- a/fm_banks/wopl_files/DMXOPL3-by-sneakernets.wopl +++ b/fm_banks/wopl_files/DMXOPL3-by-sneakernets.wopl diff --git a/include/adlmidi.h b/include/adlmidi.h index 145b5dd..1279e9f 100644 --- a/include/adlmidi.h +++ b/include/adlmidi.h @@ -40,33 +40,7 @@ enum ADLMIDI_VolumeModels struct ADL_MIDIPlayer { - unsigned int AdlBank; - unsigned int NumFourOps; - unsigned int NumCards; - unsigned int HighTremoloMode; - unsigned int HighVibratoMode; - unsigned int AdlPercussionMode; - unsigned int LogarithmicVolumes; - int VolumeModel; - unsigned int SkipForward; - unsigned int loopingIsEnabled; - unsigned int ScaleModulators; - double delay; - double carry; - - /* The lag between visual content and audio content equals */ - /* the sum of these two buffers. */ - double mindelay; - double maxdelay; - - /* For internal usage */ - ssize_t stored_samples; /* num of collected samples */ - short backup_samples[1024]; /* Backup sample storage. */ - ssize_t backup_samples_size; /* Backup sample storage. */ - /* For internal usage */ - void *adl_midiPlayer; - unsigned long PCM_RATE; }; /* Sets number of emulated sound cards (from 1 to 100). Emulation of multiple sound cards exchanges polyphony limits*/ @@ -127,14 +101,32 @@ extern int adl_openData(struct ADL_MIDIPlayer *device, void *mem, long size); /*Resets MIDI player*/ extern void adl_reset(struct ADL_MIDIPlayer *device); +/*Get total time length of current song*/ +extern double adl_totalTimeLength(struct ADL_MIDIPlayer *device); + +/*Get loop start time if presented. -1 means MIDI file has no loop points */ +extern double adl_loopStartTime(struct ADL_MIDIPlayer *device); + +/*Get loop end time if presented. -1 means MIDI file has no loop points */ +extern double adl_loopEndTime(struct ADL_MIDIPlayer *device); + +/*Get current time position in seconds*/ +extern double adl_positionTell(struct ADL_MIDIPlayer *device); + +/*Jump to absolute time position in seconds*/ +extern void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds); + /*Reset MIDI track position to begin */ extern void adl_positionRewind(struct ADL_MIDIPlayer *device); +/*Set tempo multiplier: 1.0 - original tempo, >1 - play faster, <1 - play slower */ +extern void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo); + /*Close and delete ADLMIDI device*/ extern void adl_close(struct ADL_MIDIPlayer *device); /*Take a sample buffer*/ -extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]); +extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]); #ifdef __cplusplus } diff --git a/libADLMIDI-test.pro b/libADLMIDI-test.pro index 30ba818..45313b4 100644 --- a/libADLMIDI-test.pro +++ b/libADLMIDI-test.pro @@ -2,6 +2,8 @@ TEMPLATE=app CONFIG-=qt CONFIG+=console +CONFIG -= c++11 + TARGET=adlmidiplay DESTDIR=$$PWD/bin/ @@ -11,7 +13,14 @@ INCLUDEPATH += $$PWD/src $$PWD/include #LIBS += -Wl,-Bstatic -lSDL2 -Wl,-Bdynamic -lpthread -ldl LIBS += -lSDL2 -lpthread -ldl +#DEFINES += DEBUG_TIME_CALCULATION +#DEFINES += DEBUG_SEEKING_TEST #DEFINES += DISABLE_EMBEDDED_BANKS +#DEFINES += ADLMIDI_USE_DOSBOX_OPL +#DEFINES += ENABLE_BEGIN_SILENCE_SKIPPING + +QMAKE_CFLAGS += -std=c90 -pedantic +QMAKE_CXXFLAGS += -std=c++98 -pedantic HEADERS += \ include/adlmidi.h \ diff --git a/src/adldata.cpp b/src/adldata.cpp index 48adaaa..10b452a 100644 --- a/src/adldata.cpp +++ b/src/adldata.cpp @@ -4,7 +4,7 @@ * FROM A NUMBER OF SOURCES, MOSTLY PC GAMES. * PREPROCESSED, CONVERTED, AND POSTPROCESSED OFF-SCREEN. */ -const adldata adl[4351] = +const adldata adl[4355] = { // ,---------+-------- Wave select settings // | ,-------ч-+------ Sustain/release rates // | | ,-----ч-ч-+---- Attack/decay rates @@ -1231,8 +1231,8 @@ const adldata adl[4351] = { 0x30457E0,0x04D67E0, 0x23,0x00, 0xE, +12 }, // 1212: dMM83; hxMM83; musM83; raptM83; Lead 4 (chiffer) { 0x304F7E0,0x04D87E0, 0x23,0x00, 0xE, +12 }, // 1213: dMM83; hxMM83; musM83; raptM83; Lead 4 (chiffer) { 0x10B78A1,0x12BF130, 0x42,0x00, 0x8, +12 }, // 1214: dMM84; hxMM84; musM84; Lead 5 (charang) - { 0x0558060,0x014F2E0, 0x21,0x00, 0x8, +12 }, // 1215: dMM85; hxMM85; musM85; raptM85; skeakernetsM85; Lead 6 (voice); Lead 6 (voice) - { 0x0559020,0x014A2A0, 0x21,0x00, 0x8, +12 }, // 1216: dMM85; hxMM85; musM85; raptM85; skeakernetsM85; Lead 6 (voice); Lead 6 (voice) + { 0x0558060,0x014F2E0, 0x21,0x00, 0x8, +12 }, // 1215: dMM85; hxMM85; musM85; raptM85; Lead 6 (voice) + { 0x0559020,0x014A2A0, 0x21,0x00, 0x8, +12 }, // 1216: dMM85; hxMM85; musM85; raptM85; Lead 6 (voice) { 0x195C120,0x16370B0, 0x43,0x80, 0xA, +12 }, // 1217: dMM86; hxMM86; musM86; raptM86; Lead 7 (5th sawtooth) { 0x19591A0,0x1636131, 0x49,0x00, 0xA, +7 }, // 1218: dMM86; hxMM86; musM86; raptM86; Lead 7 (5th sawtooth) { 0x1075124,0x229FDA0, 0x40,0x00, 0x9, +0 }, // 1219: dMM87; dMM88; hxMM87; hxMM88; musM87; musM88; raptM87; raptM88; * Lead 8 (bass & lead) @@ -1263,7 +1263,7 @@ const adldata adl[4351] = { 0x2ACF907,0x229F90F, 0x1A,0x00, 0x0, +12 }, // 1244: dMM106; hxMM106; musM106; raptM106; skeakernetsM106; Shamisen; Shamisen { 0x153F220,0x0E49122, 0x21,0x00, 0x8, +12 }, // 1245: dMM107; hxMM107; musM107; raptM107; Koto { 0x339F103,0x074D615, 0x4F,0x00, 0x6, +0 }, // 1246: dMM108; hxMM108; musM108; raptM108; Kalimba - { 0x1158930,0x2076B21, 0x42,0x00, 0xA, +12 }, // 1247: dMM109; hxMM109; musM109; raptM109; skeakernetsM109; Bag Pipe; Bag Pipe + { 0x1158930,0x2076B21, 0x42,0x00, 0xA, +12 }, // 1247: dMM109; hxMM109; musM109; raptM109; Bag Pipe { 0x003A130,0x0265221, 0x1F,0x00, 0xE, +12 }, // 1248: dMM110; hxMM110; musM110; raptM110; Fiddle { 0x0134030,0x1166130, 0x13,0x80, 0x8, +12 }, // 1249: dMM111; hxMM111; musM111; raptM111; Shanai { 0x032A113,0x172B212, 0x00,0x80, 0x1, +5 }, // 1250: dMM112; hxMM112; musM112; raptM112; Tinkle Bell @@ -4114,7 +4114,7 @@ const adldata adl[4351] = { 0x0E5B111,0x0B8F211, 0x9C,0x80, 0x0, +0 }, // 4095: skeakernetsM4; Electric Piano (Rhodes) { 0x0C7F437,0x0D7F230, 0x5D,0x0A, 0x8, +12 }, // 4096: skeakernetsM5; Electric Piano (DX-7) { 0x0C7F020,0x0F8F110, 0x1A,0x0F, 0x0, +12 }, // 4097: skeakernetsM5; Electric Piano (DX-7) - { 0x30AF231,0x1F5D130, 0x87,0x00, 0xA, +12 }, // 4098: skeakernetsM6; Harpsichord + { 0x303F232,0x1F6D131, 0x44,0x00, 0x8, +0 }, // 4098: skeakernetsM6; Harpsichord { 0x559F101,0x0F7F111, 0x44,0x08, 0x6, +0 }, // 4099: skeakernetsM7; Clavichord { 0x0F00000,0x4F7F111, 0x3F,0x0D, 0x9, +0 }, // 4100: skeakernetsM7; Clavichord { 0x087F607,0x0E4F231, 0x54,0x08, 0x9, +0 }, // 4101: skeakernetsM8; Celesta @@ -4127,7 +4127,7 @@ const adldata adl[4351] = { 0x0F6F2A4,0x007F08F, 0x40,0x00, 0x1, +0 }, // 4108: skeakernetsM11; Vibraphone { 0x0F6F618,0x0F7E500, 0x63,0x80, 0x6, +12 }, // 4109: skeakernetsM12; Marimba { 0x5A6F407,0x007D802, 0x5B,0x80, 0x0, +12 }, // 4110: skeakernetsM12; Marimba - { 0x696F614,0x055F610, 0x1F,0x00, 0x4, +12 }, // 4111: skeakernetsM13; Xylophone + { 0x096F616,0x0F5F111, 0x1F,0x03, 0x4, +0 }, // 4111: skeakernetsM13; Xylophone { 0x082F307,0x0E3F302, 0x97,0x8A, 0x6, -12 }, // 4112: skeakernetsM14; Tubular Bells { 0x082D307,0x0E3F302, 0x97,0x8A, 0x6, -12 }, // 4113: skeakernetsM14; Tubular Bells { 0x4109130,0x3B5F321, 0x52,0x88, 0x8, +12 }, // 4114: skeakernetsM15; Dulcimer @@ -4187,8 +4187,8 @@ const adldata adl[4351] = { 0x074A201,0x0356411, 0x29,0x07, 0xA, +0 }, // 4168: skeakernetsM45; Pizzicato Strings { 0x022E133,0x0F2F131, 0xA2,0x09, 0xE, +0 }, // 4169: skeakernetsM46; Orchestral Harp { 0x022F132,0x0F2F131, 0x24,0x0A, 0xE, +0 }, // 4170: skeakernetsM46; Orchestral Harp - { 0x4C3A413,0x0B4D215, 0x9B,0x09, 0x8, -12 }, // 4171: skeakernetsM47; Timpani - { 0x0B6F401,0x0B4F211, 0x05,0x09, 0x9, +0 }, // 4172: skeakernetsM47; Timpani + { 0x4C3C413,0x0B4D215, 0x9B,0x09, 0xA, -12 }, // 4171: skeakernetsM47; Timpani + { 0x6BAF900,0x0B4F211, 0x0A,0x09, 0x7, -12 }, // 4172: skeakernetsM47; Timpani { 0x223F832,0x4055421, 0x99,0x8A, 0xC, +0 }, // 4173: skeakernetsM48; String Ensemble1 { 0x433CB32,0x5057521, 0x9B,0x8A, 0xA, +0 }, // 4174: skeakernetsM48; String Ensemble1 { 0x5059022,0x1055521, 0x5B,0x85, 0x0, +0 }, // 4175: skeakernetsM49; String Ensemble 2 @@ -4219,7 +4219,7 @@ const adldata adl[4351] = { 0x0A66120,0x0976120, 0x9B,0x08, 0xE, +12 }, // 4200: skeakernetsM63; Synth Brass 2 { 0x0A66120,0x0976120, 0x9B,0x08, 0xE, +12 }, // 4201: skeakernetsM63; Synth Brass 2 { 0x0F37010,0x1F65051, 0x51,0x04, 0xA, +12 }, // 4202: skeakernetsM64; Soprano Sax - { 0x1067020,0x1165230, 0x88,0x00, 0x6, +12 }, // 4203: skeakernetsM65; Alto Sax + { 0x1067021,0x1165231, 0x8A,0x00, 0x6, +0 }, // 4203: skeakernetsM65; Alto Sax { 0x00B9820,0x10B5330, 0x8E,0x00, 0xA, +12 }, // 4204: skeakernetsM66; Tenor Sax { 0x10B8020,0x11B6330, 0x87,0x00, 0x8, +12 }, // 4205: skeakernetsM67; Baritone Sax { 0x0235030,0x0076C62, 0x58,0x08, 0xA, +12 }, // 4206: skeakernetsM68; Oboe @@ -4246,129 +4246,133 @@ const adldata adl[4351] = { 0x40457E1,0x03D67E0, 0x23,0x00, 0xE, +12 }, // 4227: skeakernetsM83; Lead 4 (chiffer) { 0x504F7E1,0x03D87E0, 0x23,0x00, 0xE, +12 }, // 4228: skeakernetsM83; Lead 4 (chiffer) { 0x32B7320,0x12BF131, 0x40,0x00, 0x8, +0 }, // 4229: skeakernetsM84; Lead 5 (charang) - { 0x195C120,0x1637030, 0x43,0x80, 0xA, +12 }, // 4230: skeakernetsM86; Lead 7 (5th sawtooth) - { 0x1959120,0x1636131, 0x49,0x00, 0xA, +7 }, // 4231: skeakernetsM86; Lead 7 (5th sawtooth) - { 0x132ED10,0x3E7D210, 0x87,0x08, 0x6, +12 }, // 4232: skeakernetsM87; Lead 8 brass - { 0x132ED10,0x3E7D210, 0x87,0x0D, 0x6, +12 }, // 4233: skeakernetsM87; Lead 8 brass - { 0x2946374,0x005A0A1, 0xA5,0x05, 0x2, +0 }, // 4234: skeakernetsM88; Pad 1 new age - { 0x2055F02,0x004FFE1, 0xA8,0x05, 0x0, +0 }, // 4235: skeakernetsM88; Pad 1 new age - { 0x00521A1,0x0053360, 0xC0,0x00, 0x9, +12 }, // 4236: skeakernetsM89; Pad 2 (warm) - { 0x0052161,0x00533E0, 0xC0,0x00, 0x7, +12 }, // 4237: skeakernetsM89; Pad 2 (warm) - { 0x2A5A120,0x196A120, 0x95,0x05, 0xC, +12 }, // 4238: skeakernetsM90; Pad 3 (polysynth) - { 0x2A5A120,0x196A120, 0x95,0x05, 0xC, +12 }, // 4239: skeakernetsM90; Pad 3 (polysynth) - { 0x005F0E0,0x0548160, 0x44,0x00, 0xB, +12 }, // 4240: skeakernetsM91; Pad 4 (choir) - { 0x105F0E0,0x0547160, 0x44,0x80, 0xB, +12 }, // 4241: skeakernetsM91; Pad 4 (choir) - { 0x0336183,0x05452E0, 0xA7,0x00, 0x6, +12 }, // 4242: skeakernetsM92; Pad 5 (bowed glass) - { 0x13351A3,0x05452E0, 0xA7,0x00, 0x0, +12 }, // 4243: skeakernetsM92; Pad 5 (bowed glass) - { 0x2529082,0x1534340, 0x9D,0x80, 0xC, +12 }, // 4244: skeakernetsM93; Pad 6 (metal) - { 0x2529081,0x0534340, 0x9D,0x80, 0xC, +12 }, // 4245: skeakernetsM93; Pad 6 (metal) - { 0x2345231,0x2135120, 0x98,0x00, 0x6, +0 }, // 4246: skeakernetsM94; Pad 7 halo - { 0x410F422,0x1233231, 0x20,0x00, 0xA, +0 }, // 4247: skeakernetsM94; Pad 7 halo - { 0x1521161,0x1632060, 0x90,0x80, 0x8, +12 }, // 4248: skeakernetsM95; Pad 8 (sweep) - { 0x1521160,0x1632060, 0x90,0x80, 0x8, +12 }, // 4249: skeakernetsM95; Pad 8 (sweep) - { 0x157B260,0x019F803, 0x04,0x40, 0x7, +12 }, // 4250: skeakernetsM96; FX 1 (rain) - { 0x157B260,0x0145112, 0x04,0x40, 0x7, +12 }, // 4251: skeakernetsM96; FX 1 (rain) - { 0x2322122,0x0133221, 0x8C,0x92, 0x6, +0 }, // 4252: skeakernetsM97; FX 2 soundtrack - { 0x0032121,0x0133122, 0x93,0x48, 0x2, +7 }, // 4253: skeakernetsM97; FX 2 soundtrack - { 0x074F624,0x0249303, 0xC0,0x0D, 0x0, +0 }, // 4254: skeakernetsM98; FX 3 (crystal) - { 0x074F624,0x0249303, 0xC0,0x0D, 0x0, +0 }, // 4255: skeakernetsM98; FX 3 (crystal) - { 0x3D2C091,0x1D2D130, 0x8E,0x03, 0x0, +12 }, // 4256: skeakernetsM99; FX 4 (atmosphere) - { 0x0D2D090,0x1D23131, 0x8E,0x03, 0x0, +12 }, // 4257: skeakernetsM99; FX 4 (atmosphere) - { 0x5F29052,0x0F2C240, 0x96,0x06, 0x8, +12 }, // 4258: skeakernetsM100; FX 5 (brightness) - { 0x1F19010,0x0F2C240, 0x1A,0x06, 0x6, +12 }, // 4259: skeakernetsM100; FX 5 (brightness) - { 0x05213E1,0x2131371, 0x1A,0x88, 0x7, +0 }, // 4260: skeakernetsM101; FX 6 (goblin) - { 0x0521363,0x2131331, 0x1A,0x8D, 0x7, +0 }, // 4261: skeakernetsM101; FX 6 (goblin) - { 0x0B67060,0x0928031, 0x9C,0x11, 0xA, +12 }, // 4262: skeakernetsM102; FX 7 (echo drops) - { 0x0057F20,0x0038F61, 0x9C,0x11, 0xA, +12 }, // 4263: skeakernetsM102; FX 7 (echo drops) - { 0x0025511,0x1748201, 0x94,0x06, 0xE, +0 }, // 4264: skeakernetsM103; * FX 8 (star-theme) - { 0x2045501,0x2445501, 0x15,0x0D, 0xA, +0 }, // 4265: skeakernetsM103; * FX 8 (star-theme) - { 0x0B37120,0x5F48220, 0x1B,0x08, 0x2, +12 }, // 4266: skeakernetsM104; Sitar - { 0x2B37101,0x5F48220, 0x90,0x08, 0x6, +12 }, // 4267: skeakernetsM104; Sitar - { 0x0127530,0x6F4F310, 0x0D,0x0A, 0x6, +12 }, // 4268: skeakernetsM105; Banjo - { 0x332F320,0x6E49423, 0x0E,0x08, 0x8, +0 }, // 4269: skeakernetsM107; Koto - { 0x0328413,0x073B410, 0xA1,0x00, 0xF, +12 }, // 4270: skeakernetsM108; Kalimba - { 0x302A130,0x0266221, 0x1E,0x00, 0xE, +0 }, // 4271: skeakernetsM110; Fiddle - { 0x0136030,0x1169130, 0x12,0x80, 0x8, +12 }, // 4272: skeakernetsM111; Shanai - { 0x032A115,0x172B212, 0x00,0x80, 0x1, +5 }, // 4273: skeakernetsM112; Tinkle Bell - { 0x4046303,0x005A901, 0xCA,0x08, 0x6, +12 }, // 4274: skeakernetsM114; Steel Drums - { 0x0045413,0x005A601, 0x51,0x08, 0xA, +0 }, // 4275: skeakernetsM114; Steel Drums - { 0x6D1F817,0x098F611, 0xA7,0x00, 0x6, +12 }, // 4276: skeakernetsM115; Woodblock - { 0x008F312,0x004F600, 0x08,0xC8, 0x4, -12 }, // 4277: skeakernetsM116; Taiko Drum (new) - { 0x27CFA01,0x004F200, 0x08,0x08, 0x0, +0 }, // 4278: skeakernetsM116; Taiko Drum (new) - { 0x0C8A820,0x0B7E601, 0x0B,0x00, 0x0, +0 }, // 4279: skeakernetsM117; Melodic Tom - { 0x518F890,0x0E5D310, 0x00,0x00, 0x8, -12 }, // 4280: skeakernetsM118; Synth Drum - { 0x050F011,0x0E5F510, 0x00,0xC8, 0xA, +0 }, // 4281: skeakernetsM118; Synth Drum - { 0x2114109,0x51D2101, 0x05,0x80, 0xA, +0 }, // 4282: skeakernetsM119; Reverse Cymbal - { 0x2114108,0x31D2101, 0x05,0x80, 0xA, +12 }, // 4283: skeakernetsM119; Reverse Cymbal - { 0x4543310,0x3574515, 0x19,0x03, 0xE, +12 }, // 4284: skeakernetsM120; Guitar Fret Noise - { 0x00437D2,0x0343471, 0xA1,0x07, 0xC, +0 }, // 4285: skeakernetsM121; Breath Noise - { 0x0F0F00C,0x0F66700, 0x00,0xCD, 0xE, +0 }, // 4286: skeakernetsM121; Breath Noise - { 0x200C327,0x6021300, 0x80,0x08, 0xE, -23 }, // 4287: skeakernetsM122; Seashore - { 0x200C32B,0x6021300, 0x80,0x08, 0xE, -24 }, // 4288: skeakernetsM122; Seashore - { 0x003EBD7,0x06845D8, 0xD4,0x00, 0x7, +12 }, // 4289: skeakernetsM123; Bird Tweet - { 0x62FDA20,0x614B009, 0x42,0x48, 0x4, -24 }, // 4290: skeakernetsM124; Telephone Ring - { 0x62FDA20,0x614B009, 0x82,0x48, 0x4, -20 }, // 4291: skeakernetsM124; Telephone Ring - { 0x101FE30,0x6142120, 0x00,0x00, 0xC, -36 }, // 4292: skeakernetsM125; Helicopter - { 0x6019460,0x1142120, 0x26,0x00, 0xC, -14 }, // 4293: skeakernetsM125; Helicopter - { 0x200832F,0x6044020, 0x80,0x00, 0xE, -36 }, // 4294: skeakernetsM126; Applause - { 0x200832F,0x6044020, 0x80,0x00, 0xE, -35 }, // 4295: skeakernetsM126; Applause - { 0x230732F,0x6E6F400, 0x00,0x00, 0xE, +0 }, // 4296: skeakernetsM127; Gun Shot - { 0x057FB00,0x046F800, 0x00,0x00, 0x0, +12 }, // 4297: skeakernetsP35; Acoustic Bass Drum - { 0x287F702,0x678F802, 0x80,0x88, 0xE, +12 }, // 4298: skeakernetsP37; Slide Stick - { 0x2F7F602,0x0F8F802, 0x00,0x88, 0xE, +12 }, // 4299: skeakernetsP37; Slide Stick - { 0x05476C1,0x30892C5, 0x80,0x08, 0x0, +0 }, // 4300: skeakernetsP39; ÿHand Clap - { 0x05477C1,0x30892C5, 0x00,0x08, 0xA, -2 }, // 4301: skeakernetsP39; ÿHand Clap - { 0x508F601,0x104F600, 0x08,0x00, 0x6, +0 }, // 4302: skeakernetsP41; skeakernetsP43; ÿHigh Floor Tom; ÿLow Floor Tom - { 0x254F307,0x307F905, 0x04,0x0B, 0x6, -5 }, // 4303: skeakernetsP42; Closed High-Hat - { 0x254F307,0x207F905, 0x04,0x0B, 0x8, +0 }, // 4304: skeakernetsP42; Closed High-Hat - { 0x254D307,0x3288905, 0x04,0x08, 0xA, -5 }, // 4305: skeakernetsP44; Pedal High Hat - { 0x508F601,0x104F600, 0x0C,0x00, 0x8, +0 }, // 4306: skeakernetsP45; skeakernetsP47; skeakernetsP48; skeakernetsP50; ÿHigh Tom; ÿHigh-Mid Tom; ÿLow Tom; ÿLow-Mid Tom - { 0x292F108,0x354F201, 0x00,0x08, 0x8, +12 }, // 4307: skeakernetsP49; Crash Cymbal 1 - { 0x292F108,0x354F201, 0x00,0x08, 0x8, +12 }, // 4308: skeakernetsP49; Crash Cymbal 1 - { 0x210F509,0x305FE03, 0x8A,0x88, 0xE, +12 }, // 4309: skeakernetsP51; Ride Cymbal 1 - { 0x200F508,0x305FE03, 0xC7,0x88, 0xC, +12 }, // 4310: skeakernetsP51; Ride Cymbal 1 - { 0x283E108,0x334D700, 0x00,0x08, 0x8, +12 }, // 4311: skeakernetsP52; Chinses Cymbal - { 0x283E109,0x334D500, 0x00,0x08, 0x8, +11 }, // 4312: skeakernetsP52; Chinses Cymbal - { 0x2E1F119,0x3F3F11B, 0x04,0x08, 0x8, +0 }, // 4313: skeakernetsP53; Ride Bell - { 0x2777603,0x3679601, 0x87,0x08, 0x6, +12 }, // 4314: skeakernetsP54; Tambourine - { 0x277C643,0x3679601, 0x87,0x08, 0xE, +12 }, // 4315: skeakernetsP54; Tambourine - { 0x251F206,0x263C504, 0x04,0x09, 0xA, +0 }, // 4316: skeakernetsP55; Splash Cymbal - { 0x241F287,0x353B502, 0x05,0x09, 0xA, +1 }, // 4317: skeakernetsP55; Splash Cymbal - { 0x366F905,0x099F701, 0x00,0x00, 0xC, +12 }, // 4318: skeakernetsP56; Cowbell - { 0x292F108,0x354F201, 0x00,0x03, 0x8, +12 }, // 4319: skeakernetsP57; Crash Cymbal 2 - { 0x292F108,0x354F201, 0x00,0x03, 0x8, +12 }, // 4320: skeakernetsP57; Crash Cymbal 2 - { 0x422F120,0x056B40E, 0x81,0x00, 0xA, +12 }, // 4321: skeakernetsP58; Vibraslap - { 0x212FD04,0x305FD03, 0x01,0x00, 0x8, +12 }, // 4322: skeakernetsP59; Ride Cymbal 2 - { 0x2A8F9E3,0x0779643, 0x1E,0x08, 0x2, +6 }, // 4323: skeakernetsP60; High Bongo (New) - { 0x0A5F7E8,0x0D89949, 0xDE,0x00, 0x0, +0 }, // 4324: skeakernetsP60; High Bongo (New) - { 0x2A8F9E3,0x0779643, 0x1E,0x00, 0xE, +12 }, // 4325: skeakernetsP61; Low Bongo (New) - { 0x0A5F7E9,0x0D8994A, 0xDE,0x08, 0xC, +0 }, // 4326: skeakernetsP61; Low Bongo (New) - { 0x0A8F7E9,0x5D8990A, 0x08,0x00, 0xC, +0 }, // 4327: skeakernetsP62; Mute high conga (New) - { 0x0A5F7E9,0x0D8994A, 0x29,0x08, 0xC, +10 }, // 4328: skeakernetsP62; Mute high conga (New) - { 0x2A8F9E2,0x0779642, 0x1E,0x00, 0xE, +8 }, // 4329: skeakernetsP63; skeakernetsP64; Low Conga (New); Open High Conga (New) - { 0x0A5F7E9,0x5D8994A, 0x08,0x00, 0xC, +0 }, // 4330: skeakernetsP63; skeakernetsP64; Low Conga (New); Open High Conga (New) - { 0x456FB02,0x017F700, 0x81,0x00, 0x0, +12 }, // 4331: skeakernetsP65; skeakernetsP66; High Timbale; Low Timbale - { 0x367FD01,0x098F601, 0x00,0x08, 0x8, +12 }, // 4332: skeakernetsP67; skeakernetsP68; High Agogo; Low Agogo - { 0x367FD10,0x098F901, 0x00,0x0D, 0x8, +6 }, // 4333: skeakernetsP67; High Agogo - { 0x367FD10,0x098F901, 0x00,0x0D, 0x8, +11 }, // 4334: skeakernetsP68; Low Agogo - { 0x098600F,0x3FC8590, 0x08,0xC0, 0xE, +12 }, // 4335: skeakernetsP70; Maracas - { 0x009F020,0x37DA588, 0x07,0x00, 0xA, +12 }, // 4336: skeakernetsP71; Short Whistle - { 0x00FC020,0x32DA5A8, 0x07,0x00, 0xA, +12 }, // 4337: skeakernetsP72; Long Whistle - { 0x106F680,0x016F610, 0x00,0x00, 0xC, +0 }, // 4338: skeakernetsP73; ÿShort Guiro - { 0x20F6F00,0x20F6F00, 0x00,0x00, 0x0, +0 }, // 4339: skeakernetsP73; ÿShort Guiro - { 0x106F680,0x016F610, 0x00,0x00, 0x6, +0 }, // 4340: skeakernetsP74; ÿLong Guiro - { 0x20F4F00,0x20F4F00, 0x00,0x00, 0x6, +0 }, // 4341: skeakernetsP74; ÿLong Guiro - { 0x0D1F815,0x078F512, 0x44,0x00, 0x8, +12 }, // 4342: skeakernetsP75; Claves - { 0x1DC5D01,0x06FF79F, 0x0B,0x00, 0xA, +12 }, // 4343: skeakernetsP78; Mute Cuica - { 0x1C7C900,0x05FF49F, 0x07,0x00, 0xA, +12 }, // 4344: skeakernetsP79; Open Cuica - { 0x160F2C6,0x07AF4D4, 0x4F,0x80, 0x8, +12 }, // 4345: skeakernetsP80; Mute Triangle - { 0x160F286,0x0B7F294, 0x4F,0x80, 0x8, +12 }, // 4346: skeakernetsP81; Open Triangle - { 0x227A305,0x36A560A, 0x87,0x08, 0xE, +12 }, // 4347: skeakernetsP82; (GS & XG) Shaker - { 0x247C345,0x3697809, 0x87,0x08, 0xE, +12 }, // 4348: skeakernetsP82; (GS & XG) Shaker - { 0x4755406,0x3667601, 0x87,0x08, 0x6, +12 }, // 4349: skeakernetsP83; (GS & XG) Jingle Bells - { 0x275A346,0x3667601, 0x87,0x08, 0x6, +12 }, // 4350: skeakernetsP83; (GS & XG) Jingle Bells + { 0x5029071,0x0069060, 0x96,0x09, 0x8, +12 }, // 4230: skeakernetsM85; Lead 6 (Voice) + { 0x1019030,0x0069060, 0x1A,0x09, 0x6, +12 }, // 4231: skeakernetsM85; Lead 6 (Voice) + { 0x195C120,0x1637030, 0x43,0x80, 0xA, +12 }, // 4232: skeakernetsM86; Lead 7 (5th sawtooth) + { 0x1959120,0x1636131, 0x49,0x00, 0xA, +7 }, // 4233: skeakernetsM86; Lead 7 (5th sawtooth) + { 0x132ED10,0x3E7D210, 0x87,0x08, 0x6, +12 }, // 4234: skeakernetsM87; Lead 8 brass + { 0x132ED10,0x3E7D210, 0x87,0x0D, 0x6, +12 }, // 4235: skeakernetsM87; Lead 8 brass + { 0x2946374,0x005A0A1, 0xA5,0x05, 0x2, +0 }, // 4236: skeakernetsM88; Pad 1 new age + { 0x2055F02,0x004FFE1, 0xA8,0x05, 0x0, +0 }, // 4237: skeakernetsM88; Pad 1 new age + { 0x00521A1,0x0053360, 0xC0,0x00, 0x9, +12 }, // 4238: skeakernetsM89; Pad 2 (warm) + { 0x0052161,0x00533E0, 0xC0,0x00, 0x7, +12 }, // 4239: skeakernetsM89; Pad 2 (warm) + { 0x2A5A120,0x196A120, 0x95,0x05, 0xC, +12 }, // 4240: skeakernetsM90; Pad 3 (polysynth) + { 0x2A5A120,0x196A120, 0x95,0x05, 0xC, +12 }, // 4241: skeakernetsM90; Pad 3 (polysynth) + { 0x005F0E0,0x0548160, 0x44,0x00, 0xB, +12 }, // 4242: skeakernetsM91; Pad 4 (choir) + { 0x105F0E0,0x0547160, 0x44,0x80, 0xB, +12 }, // 4243: skeakernetsM91; Pad 4 (choir) + { 0x0336183,0x05452E0, 0xA7,0x00, 0x6, +12 }, // 4244: skeakernetsM92; Pad 5 (bowed glass) + { 0x13351A3,0x05452E0, 0xA7,0x00, 0x0, +12 }, // 4245: skeakernetsM92; Pad 5 (bowed glass) + { 0x2529082,0x1534340, 0x9D,0x80, 0xC, +12 }, // 4246: skeakernetsM93; Pad 6 (metal) + { 0x2529081,0x0534340, 0x9D,0x80, 0xC, +12 }, // 4247: skeakernetsM93; Pad 6 (metal) + { 0x2345231,0x2135120, 0x98,0x00, 0x6, +0 }, // 4248: skeakernetsM94; Pad 7 halo + { 0x410F422,0x1233231, 0x20,0x00, 0xA, +0 }, // 4249: skeakernetsM94; Pad 7 halo + { 0x1521161,0x1632060, 0x90,0x80, 0x8, +12 }, // 4250: skeakernetsM95; Pad 8 (sweep) + { 0x1521160,0x1632060, 0x90,0x80, 0x8, +12 }, // 4251: skeakernetsM95; Pad 8 (sweep) + { 0x157B260,0x019F803, 0x04,0x40, 0x7, +12 }, // 4252: skeakernetsM96; FX 1 (rain) + { 0x157B260,0x0145112, 0x04,0x40, 0x7, +12 }, // 4253: skeakernetsM96; FX 1 (rain) + { 0x2322122,0x0133221, 0x8C,0x92, 0x6, +0 }, // 4254: skeakernetsM97; FX 2 soundtrack + { 0x0032121,0x0133122, 0x93,0x48, 0x2, +7 }, // 4255: skeakernetsM97; FX 2 soundtrack + { 0x074F624,0x0249303, 0xC0,0x0D, 0x0, +0 }, // 4256: skeakernetsM98; FX 3 (crystal) + { 0x074F624,0x0249303, 0xC0,0x0D, 0x0, +0 }, // 4257: skeakernetsM98; FX 3 (crystal) + { 0x3D2C091,0x1D2D130, 0x8E,0x03, 0x0, +12 }, // 4258: skeakernetsM99; FX 4 (atmosphere) + { 0x0D2D090,0x1D23131, 0x8E,0x03, 0x0, +12 }, // 4259: skeakernetsM99; FX 4 (atmosphere) + { 0x5F29052,0x0F2C240, 0x96,0x06, 0x8, +12 }, // 4260: skeakernetsM100; FX 5 (brightness) + { 0x1F19010,0x0F2C240, 0x1A,0x06, 0x6, +12 }, // 4261: skeakernetsM100; FX 5 (brightness) + { 0x05213E1,0x2131371, 0x1A,0x88, 0x7, +0 }, // 4262: skeakernetsM101; FX 6 (goblin) + { 0x0521363,0x2131331, 0x1A,0x8D, 0x7, +0 }, // 4263: skeakernetsM101; FX 6 (goblin) + { 0x0B67060,0x0928031, 0x9C,0x11, 0xA, +12 }, // 4264: skeakernetsM102; FX 7 (echo drops) + { 0x0057F20,0x0038F61, 0x9C,0x11, 0xA, +12 }, // 4265: skeakernetsM102; FX 7 (echo drops) + { 0x0025511,0x1748201, 0x94,0x06, 0xE, +0 }, // 4266: skeakernetsM103; * FX 8 (star-theme) + { 0x2045501,0x2445501, 0x15,0x0D, 0xA, +0 }, // 4267: skeakernetsM103; * FX 8 (star-theme) + { 0x0B37120,0x5F48220, 0x1B,0x08, 0x2, +12 }, // 4268: skeakernetsM104; Sitar + { 0x2B37101,0x5F48220, 0x90,0x08, 0x6, +12 }, // 4269: skeakernetsM104; Sitar + { 0x0127530,0x6F4F310, 0x0D,0x0A, 0x6, +12 }, // 4270: skeakernetsM105; Banjo + { 0x332F320,0x6E49423, 0x0E,0x08, 0x8, +0 }, // 4271: skeakernetsM107; Koto + { 0x0328413,0x073B410, 0xA1,0x00, 0xF, +12 }, // 4272: skeakernetsM108; Kalimba + { 0x1397931,0x2099B22, 0x80,0x00, 0x6, +0 }, // 4273: skeakernetsM109; Bag Pipe + { 0x2137931,0x1079B22, 0x42,0xC2, 0xA, +0 }, // 4274: skeakernetsM109; Bag Pipe + { 0x302A130,0x0266221, 0x1E,0x00, 0xE, +0 }, // 4275: skeakernetsM110; Fiddle + { 0x0136030,0x1169130, 0x12,0x80, 0x8, +12 }, // 4276: skeakernetsM111; Shanai + { 0x032A115,0x172B212, 0x00,0x80, 0x1, +5 }, // 4277: skeakernetsM112; Tinkle Bell + { 0x4046303,0x005A901, 0xCA,0x08, 0x6, +12 }, // 4278: skeakernetsM114; Steel Drums + { 0x0045413,0x005A601, 0x51,0x08, 0xA, +0 }, // 4279: skeakernetsM114; Steel Drums + { 0x6D1F817,0x098F611, 0xA7,0x00, 0x6, +12 }, // 4280: skeakernetsM115; Woodblock + { 0x008F312,0x004F600, 0x08,0xC8, 0x4, -12 }, // 4281: skeakernetsM116; Taiko Drum (new) + { 0x27CFA01,0x004F200, 0x08,0x08, 0x0, +0 }, // 4282: skeakernetsM116; Taiko Drum (new) + { 0x0C8A820,0x0B7E601, 0x0B,0x00, 0x0, +0 }, // 4283: skeakernetsM117; Melodic Tom + { 0x518F890,0x0E7F310, 0x00,0x00, 0x8, -12 }, // 4284: skeakernetsM118; Synth Drum + { 0x250F610,0x0E7F510, 0x00,0xC8, 0x6, +0 }, // 4285: skeakernetsM118; Synth Drum + { 0x2114109,0x51D2101, 0x05,0x80, 0xA, +0 }, // 4286: skeakernetsM119; Reverse Cymbal + { 0x2114108,0x31D2101, 0x05,0x80, 0xA, +12 }, // 4287: skeakernetsM119; Reverse Cymbal + { 0x4543310,0x3574515, 0x19,0x03, 0xE, +12 }, // 4288: skeakernetsM120; Guitar Fret Noise + { 0x00437D2,0x0343471, 0xA1,0x07, 0xC, +0 }, // 4289: skeakernetsM121; Breath Noise + { 0x0F0F00C,0x0F66700, 0x00,0xCD, 0xE, +0 }, // 4290: skeakernetsM121; Breath Noise + { 0x200C327,0x6021300, 0x80,0x08, 0xE, -23 }, // 4291: skeakernetsM122; Seashore + { 0x200C32B,0x6021300, 0x80,0x08, 0xE, -24 }, // 4292: skeakernetsM122; Seashore + { 0x003EBD7,0x06845D8, 0xD4,0x00, 0x7, +12 }, // 4293: skeakernetsM123; Bird Tweet + { 0x62FDA20,0x614B009, 0x42,0x48, 0x4, -24 }, // 4294: skeakernetsM124; Telephone Ring + { 0x62FDA20,0x614B009, 0x82,0x48, 0x4, -20 }, // 4295: skeakernetsM124; Telephone Ring + { 0x101FE30,0x6142120, 0x00,0x00, 0xC, -36 }, // 4296: skeakernetsM125; Helicopter + { 0x6019460,0x1142120, 0x26,0x00, 0xC, -14 }, // 4297: skeakernetsM125; Helicopter + { 0x200832F,0x6044020, 0x80,0x00, 0xE, -36 }, // 4298: skeakernetsM126; Applause + { 0x200832F,0x6044020, 0x80,0x00, 0xE, -35 }, // 4299: skeakernetsM126; Applause + { 0x230732F,0x6E6F400, 0x00,0x00, 0xE, +0 }, // 4300: skeakernetsM127; Gun Shot + { 0x057FB00,0x046F800, 0x00,0x00, 0x0, +12 }, // 4301: skeakernetsP35; Acoustic Bass Drum + { 0x287F702,0x678F802, 0x80,0x88, 0xE, +12 }, // 4302: skeakernetsP37; Slide Stick + { 0x2F7F602,0x0F8F802, 0x00,0x88, 0xE, +12 }, // 4303: skeakernetsP37; Slide Stick + { 0x05476C1,0x30892C5, 0x80,0x08, 0x0, +0 }, // 4304: skeakernetsP39; ÿHand Clap + { 0x05477C1,0x30892C5, 0x00,0x08, 0xA, -2 }, // 4305: skeakernetsP39; ÿHand Clap + { 0x508F601,0x104F600, 0x08,0x00, 0x6, +0 }, // 4306: skeakernetsP41; skeakernetsP43; ÿHigh Floor Tom; ÿLow Floor Tom + { 0x254F307,0x307F905, 0x04,0x0B, 0x6, -5 }, // 4307: skeakernetsP42; Closed High-Hat + { 0x254F307,0x207F905, 0x04,0x0B, 0x8, +0 }, // 4308: skeakernetsP42; Closed High-Hat + { 0x254D307,0x3288905, 0x04,0x08, 0xA, -5 }, // 4309: skeakernetsP44; Pedal High Hat + { 0x508F601,0x104F600, 0x0C,0x00, 0x8, +0 }, // 4310: skeakernetsP45; skeakernetsP47; skeakernetsP48; skeakernetsP50; ÿHigh Tom; ÿHigh-Mid Tom; ÿLow Tom; ÿLow-Mid Tom + { 0x292F108,0x354F201, 0x00,0x08, 0x8, +12 }, // 4311: skeakernetsP49; Crash Cymbal 1 + { 0x292F108,0x354F201, 0x00,0x08, 0x8, +12 }, // 4312: skeakernetsP49; Crash Cymbal 1 + { 0x210F509,0x305FE03, 0x8A,0x88, 0xE, +12 }, // 4313: skeakernetsP51; Ride Cymbal 1 + { 0x200F508,0x305FE03, 0xC7,0x88, 0xC, +12 }, // 4314: skeakernetsP51; Ride Cymbal 1 + { 0x283E108,0x334D700, 0x00,0x08, 0x8, +12 }, // 4315: skeakernetsP52; Chinses Cymbal + { 0x283E109,0x334D500, 0x00,0x08, 0x8, +11 }, // 4316: skeakernetsP52; Chinses Cymbal + { 0x2E1F119,0x3F3F11B, 0x04,0x08, 0x8, +0 }, // 4317: skeakernetsP53; Ride Bell + { 0x2777603,0x3679601, 0x87,0x08, 0x6, +12 }, // 4318: skeakernetsP54; Tambourine + { 0x277C643,0x3679601, 0x87,0x08, 0xE, +12 }, // 4319: skeakernetsP54; Tambourine + { 0x251F206,0x263C504, 0x04,0x09, 0xA, +0 }, // 4320: skeakernetsP55; Splash Cymbal + { 0x241F287,0x353B502, 0x05,0x09, 0xA, +1 }, // 4321: skeakernetsP55; Splash Cymbal + { 0x366F905,0x099F701, 0x00,0x00, 0xC, +12 }, // 4322: skeakernetsP56; Cowbell + { 0x292F108,0x354F201, 0x00,0x03, 0x8, +12 }, // 4323: skeakernetsP57; Crash Cymbal 2 + { 0x292F108,0x354F201, 0x00,0x03, 0x8, +12 }, // 4324: skeakernetsP57; Crash Cymbal 2 + { 0x422F120,0x056B40E, 0x81,0x00, 0xA, +12 }, // 4325: skeakernetsP58; Vibraslap + { 0x212FD04,0x305FD03, 0x01,0x00, 0x8, +12 }, // 4326: skeakernetsP59; Ride Cymbal 2 + { 0x2A8F9E3,0x0779643, 0x1E,0x08, 0x2, +6 }, // 4327: skeakernetsP60; High Bongo (New) + { 0x0A5F7E8,0x0D89949, 0xDE,0x00, 0x0, +0 }, // 4328: skeakernetsP60; High Bongo (New) + { 0x2A8F9E3,0x0779643, 0x1E,0x00, 0xE, +12 }, // 4329: skeakernetsP61; Low Bongo (New) + { 0x0A5F7E9,0x0D8994A, 0xDE,0x08, 0xC, +0 }, // 4330: skeakernetsP61; Low Bongo (New) + { 0x0A8F7E9,0x5D8990A, 0x08,0x00, 0xC, +0 }, // 4331: skeakernetsP62; Mute high conga (New) + { 0x0A5F7E9,0x0D8994A, 0x29,0x08, 0xC, +10 }, // 4332: skeakernetsP62; Mute high conga (New) + { 0x2A8F9E2,0x0779642, 0x1E,0x00, 0xE, +8 }, // 4333: skeakernetsP63; skeakernetsP64; Low Conga (New); Open High Conga (New) + { 0x0A5F7E9,0x5D8994A, 0x08,0x00, 0xC, +0 }, // 4334: skeakernetsP63; skeakernetsP64; Low Conga (New); Open High Conga (New) + { 0x456FB02,0x017F700, 0x81,0x00, 0x0, +12 }, // 4335: skeakernetsP65; skeakernetsP66; High Timbale; Low Timbale + { 0x367FD01,0x098F601, 0x00,0x08, 0x8, +12 }, // 4336: skeakernetsP67; skeakernetsP68; High Agogo; Low Agogo + { 0x367FD10,0x098F901, 0x00,0x0D, 0x8, +6 }, // 4337: skeakernetsP67; High Agogo + { 0x367FD10,0x098F901, 0x00,0x0D, 0x8, +11 }, // 4338: skeakernetsP68; Low Agogo + { 0x098600F,0x3FC8590, 0x08,0xC0, 0xE, +12 }, // 4339: skeakernetsP70; Maracas + { 0x009F020,0x37DA588, 0x07,0x00, 0xA, +12 }, // 4340: skeakernetsP71; Short Whistle + { 0x00FC020,0x32DA5A8, 0x07,0x00, 0xA, +12 }, // 4341: skeakernetsP72; Long Whistle + { 0x106F680,0x016F610, 0x00,0x00, 0xC, +0 }, // 4342: skeakernetsP73; ÿShort Guiro + { 0x20F6F00,0x20F6F00, 0x00,0x00, 0x0, +0 }, // 4343: skeakernetsP73; ÿShort Guiro + { 0x106F680,0x016F610, 0x00,0x00, 0x6, +0 }, // 4344: skeakernetsP74; ÿLong Guiro + { 0x20F4F00,0x20F4F00, 0x00,0x00, 0x6, +0 }, // 4345: skeakernetsP74; ÿLong Guiro + { 0x0D1F815,0x078F512, 0x44,0x00, 0x8, +12 }, // 4346: skeakernetsP75; Claves + { 0x1DC5D01,0x06FF79F, 0x0B,0x00, 0xA, +12 }, // 4347: skeakernetsP78; Mute Cuica + { 0x1C7C900,0x05FF49F, 0x07,0x00, 0xA, +12 }, // 4348: skeakernetsP79; Open Cuica + { 0x160F2C6,0x07AF4D4, 0x4F,0x80, 0x8, +12 }, // 4349: skeakernetsP80; Mute Triangle + { 0x160F286,0x0B7F294, 0x4F,0x80, 0x8, +12 }, // 4350: skeakernetsP81; Open Triangle + { 0x227A305,0x36A560A, 0x87,0x08, 0xE, +12 }, // 4351: skeakernetsP82; (GS & XG) Shaker + { 0x247C345,0x3697809, 0x87,0x08, 0xE, +12 }, // 4352: skeakernetsP82; (GS & XG) Shaker + { 0x4755406,0x3667601, 0x87,0x08, 0x6, +12 }, // 4353: skeakernetsP83; (GS & XG) Jingle Bells + { 0x275A346,0x3667601, 0x87,0x08, 0x6, +12 }, // 4354: skeakernetsP83; (GS & XG) Jingle Bells }; -const struct adlinsdata adlins[4495] = +const struct adlinsdata adlins[4496] = { // Amplitude begins at 1546.1, peaks 1623.1 at 0.0s, // fades to 20% at 0.0s, keyoff fades to 20% in 0.0s. @@ -9692,7 +9696,7 @@ const struct adlinsdata adlins[4495] = // Amplitude begins at 0.9, peaks 1281.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {1247,1247, 0, 0, 40000, 80,0.000000 }, // 1330: dMM109; hxMM109; musM109; raptM109; skeakernetsM109; Bag Pipe; Bag Pipe + {1247,1247, 0, 0, 40000, 80,0.000000 }, // 1330: dMM109; hxMM109; musM109; raptM109; Bag Pipe // Amplitude begins at 0.5, peaks 2984.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. @@ -21710,9 +21714,9 @@ const struct adlinsdata adlins[4495] = // fades to 20% at infs, keyoff fades to 20% in infs. {4096,4097, 0, 1, 380, 380,0.062500 }, // 4334: skeakernetsM5; Electric Piano (DX-7) - // Amplitude begins at 1431.0, peaks 1558.6 at infs, + // Amplitude begins at 1559.8, peaks 1689.4 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4098,4098, 0, 0, 1420, 1420,0.000000 }, // 4335: skeakernetsM6; Harpsichord + {4098,4098, 0, 0, 1633, 1633,0.000000 }, // 4335: skeakernetsM6; Harpsichord // Amplitude begins at 1860.8, peaks 2186.0 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. @@ -21738,9 +21742,9 @@ const struct adlinsdata adlins[4495] = // fades to 20% at infs, keyoff fades to 20% in infs. {4109,4110, 0, 1, 193, 193,0.000000 }, // 4341: skeakernetsM12; Marimba - // Amplitude begins at 2767.5, + // Amplitude begins at 2146.7, peaks 2349.0 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4111,4111, 0, 0, 46, 46,0.000000 }, // 4342: skeakernetsM13; Xylophone + {4111,4111, 0, 0, 1846, 1846,0.000000 }, // 4342: skeakernetsM13; Xylophone // Amplitude begins at 2338.5, peaks 2446.0 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. @@ -21874,9 +21878,9 @@ const struct adlinsdata adlins[4495] = // fades to 20% at infs, keyoff fades to 20% in infs. {4169,4170, 0, 1, 1813, 1813,0.031250 }, // 4375: skeakernetsM46; Orchestral Harp - // Amplitude begins at 2079.8, + // Amplitude begins at 2241.1, // fades to 20% at infs, keyoff fades to 20% in infs. - {4171,4172, 0, 1, 780, 780,0.031250 }, // 4376: skeakernetsM47; Timpani + {4171,4172, 0, 1, 1173, 1173,0.031250 }, // 4376: skeakernetsM47; Timpani // Amplitude begins at 0.6, peaks 1724.9 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. @@ -21942,9 +21946,9 @@ const struct adlinsdata adlins[4495] = // fades to 20% at infs, keyoff fades to 20% in infs. {4202,4202, 0, 0, 40000, 46,0.000000 }, // 4392: skeakernetsM64; Soprano Sax - // Amplitude begins at 0.2, peaks 1359.4 at infs, + // Amplitude begins at 0.0, peaks 1372.4 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4203,4203, 0, 0, 40000, 20,0.000000 }, // 4393: skeakernetsM65; Alto Sax + {4203,4203, 0, 0, 40000, 40,0.000000 }, // 4393: skeakernetsM65; Alto Sax // Amplitude begins at 2.5, peaks 1584.4 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. @@ -22018,89 +22022,89 @@ const struct adlinsdata adlins[4495] = // fades to 20% at infs, keyoff fades to 20% in infs. {4229,4229, 0, 0, 933, 933,0.000000 }, // 4411: skeakernetsM84; Lead 5 (charang) - // Amplitude begins at 3109.4, peaks 5378.8 at infs, - // fades to 20% at infs, keyoff fades to 20% in infs. - {1215,1216, 0, 1, 40000, 560,0.093750 }, // 4412: skeakernetsM85; Lead 6 (voice) + // Amplitude begins at 406.5, peaks 1972.6 at infs, + // fades to 20% at infs, keyoff fades to 20% in -nans. + {4230,4231, 0, 1, 40000, 0,0.156250 }, // 4412: skeakernetsM85; Lead 6 (Voice) // Amplitude begins at 12.3, peaks 2231.4 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4230,4231, 0, 1, 1660, 80,-0.046875 }, // 4413: skeakernetsM86; Lead 7 (5th sawtooth) + {4232,4233, 0, 1, 1660, 80,-0.046875 }, // 4413: skeakernetsM86; Lead 7 (5th sawtooth) // Amplitude begins at 1233.1, peaks 1298.7 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4232,4233, 0, 1, 793, 793,0.031250 }, // 4414: skeakernetsM87; Lead 8 brass + {4234,4235, 0, 1, 793, 793,0.031250 }, // 4414: skeakernetsM87; Lead 8 brass // Amplitude begins at 2625.0, peaks 4127.5 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4234,4235, 0, 1, 40000, 0,0.078125 }, // 4415: skeakernetsM88; Pad 1 new age + {4236,4237, 0, 1, 40000, 0,0.078125 }, // 4415: skeakernetsM88; Pad 1 new age // Amplitude begins at 0.8, peaks 5867.8 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4236,4237, 0, 1, 40000, 0,0.156250 }, // 4416: skeakernetsM89; Pad 2 (warm) + {4238,4239, 0, 1, 40000, 0,0.156250 }, // 4416: skeakernetsM89; Pad 2 (warm) // Amplitude begins at 699.2, peaks 1840.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4238,4239, 0, 1, 1446, 1446,0.078125 }, // 4417: skeakernetsM90; Pad 3 (polysynth) + {4240,4241, 0, 1, 1446, 1446,0.078125 }, // 4417: skeakernetsM90; Pad 3 (polysynth) // Amplitude begins at 787.1, peaks 5553.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4240,4241, 0, 1, 40000, 53,0.171875 }, // 4418: skeakernetsM91; Pad 4 (choir) + {4242,4243, 0, 1, 40000, 53,0.171875 }, // 4418: skeakernetsM91; Pad 4 (choir) // Amplitude begins at 0.9, peaks 5687.3 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4242,4243, 0, 1, 2006, 2006,0.109375 }, // 4419: skeakernetsM92; Pad 5 (bowed glass) + {4244,4245, 0, 1, 2006, 2006,0.109375 }, // 4419: skeakernetsM92; Pad 5 (bowed glass) // Amplitude begins at 0.5, peaks 3124.7 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4244,4245, 0, 1, 1166, 1166,0.062500 }, // 4420: skeakernetsM93; Pad 6 (metal) + {4246,4247, 0, 1, 1166, 1166,0.062500 }, // 4420: skeakernetsM93; Pad 6 (metal) // Amplitude begins at 0.0, peaks 2261.6 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4246,4247, 0, 1, 40000, 0,0.093750 }, // 4421: skeakernetsM94; Pad 7 halo + {4248,4249, 0, 1, 40000, 0,0.093750 }, // 4421: skeakernetsM94; Pad 7 halo // Amplitude begins at 0.0, peaks 2463.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4248,4249, 0, 1, 40000, 853,0.093750 }, // 4422: skeakernetsM95; Pad 8 (sweep) + {4250,4251, 0, 1, 40000, 853,0.093750 }, // 4422: skeakernetsM95; Pad 8 (sweep) // Amplitude begins at 1593.4, peaks 1865.4 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4250,4251, 0, 1, 653, 653,0.093750 }, // 4423: skeakernetsM96; FX 1 (rain) + {4252,4253, 0, 1, 653, 653,0.093750 }, // 4423: skeakernetsM96; FX 1 (rain) // Amplitude begins at 0.8, peaks 1037.1 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4252,4253, 0, 1, 40000, 0,0.062500 }, // 4424: skeakernetsM97; FX 2 soundtrack + {4254,4255, 0, 1, 40000, 0,0.062500 }, // 4424: skeakernetsM97; FX 2 soundtrack // Amplitude begins at 626.9, peaks 1813.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4254,4255, 0, 1, 886, 886,0.109375 }, // 4425: skeakernetsM98; FX 3 (crystal) + {4256,4257, 0, 1, 886, 886,0.109375 }, // 4425: skeakernetsM98; FX 3 (crystal) // Amplitude begins at 1227.0, peaks 1908.3 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4256,4257, 0, 1, 1653, 1653,-0.046875 }, // 4426: skeakernetsM99; FX 4 (atmosphere) + {4258,4259, 0, 1, 1653, 1653,-0.046875 }, // 4426: skeakernetsM99; FX 4 (atmosphere) // Amplitude begins at 2698.2, peaks 2784.1 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4258,4259, 0, 1, 1593, 1593,0.125000 }, // 4427: skeakernetsM100; FX 5 (brightness) + {4260,4261, 0, 1, 1593, 1593,0.125000 }, // 4427: skeakernetsM100; FX 5 (brightness) // Amplitude begins at 0.4, peaks 807.0 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4260,4261, 0, 1, 40000, 0,-0.078125 }, // 4428: skeakernetsM101; FX 6 (goblin) + {4262,4263, 0, 1, 40000, 0,-0.078125 }, // 4428: skeakernetsM101; FX 6 (goblin) // Amplitude begins at 433.0, peaks 1428.1 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4262,4263, 0, 1, 40000, 0,0.140625 }, // 4429: skeakernetsM102; FX 7 (echo drops) + {4264,4265, 0, 1, 40000, 0,0.140625 }, // 4429: skeakernetsM102; FX 7 (echo drops) // Amplitude begins at 8.7, peaks 939.3 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4264,4265, 0, 1, 360, 360,0.078125 }, // 4430: skeakernetsM103; * FX 8 (star-theme) + {4266,4267, 0, 1, 360, 360,0.078125 }, // 4430: skeakernetsM103; * FX 8 (star-theme) // Amplitude begins at 14.6, peaks 1251.6 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4266,4267, 0, 1, 1766, 1766,0.031250 }, // 4431: skeakernetsM104; Sitar + {4268,4269, 0, 1, 1766, 1766,0.031250 }, // 4431: skeakernetsM104; Sitar // Amplitude begins at 601.8, // fades to 20% at infs, keyoff fades to 20% in infs. - {4268,4268, 0, 0, 380, 380,0.000000 }, // 4432: skeakernetsM105; Banjo + {4270,4270, 0, 0, 380, 380,0.000000 }, // 4432: skeakernetsM105; Banjo // Amplitude begins at 1395.7, // fades to 20% at infs, keyoff fades to 20% in infs. @@ -22108,247 +22112,251 @@ const struct adlinsdata adlins[4495] = // Amplitude begins at 358.1, peaks 604.7 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4269,4269, 0, 0, 633, 633,0.000000 }, // 4434: skeakernetsM107; Koto + {4271,4271, 0, 0, 633, 633,0.000000 }, // 4434: skeakernetsM107; Koto // Amplitude begins at 2381.7, peaks 2936.3 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4270,4270, 0, 0, 173, 173,0.000000 }, // 4435: skeakernetsM108; Kalimba + {4272,4272, 0, 0, 173, 173,0.000000 }, // 4435: skeakernetsM108; Kalimba + + // Amplitude begins at 1110.8, peaks 1559.2 at infs, + // fades to 20% at infs, keyoff fades to 20% in -nans. + {4273,4274, 0, 1, 40000, 0,0.062500 }, // 4436: skeakernetsM109; Bag Pipe // Amplitude begins at 1.1, peaks 2642.3 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4271,4271, 0, 0, 40000, 66,0.000000 }, // 4436: skeakernetsM110; Fiddle + {4275,4275, 0, 0, 40000, 66,0.000000 }, // 4437: skeakernetsM110; Fiddle // Amplitude begins at 957.5, peaks 1239.4 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4272,4272, 0, 0, 40000, 33,0.000000 }, // 4437: skeakernetsM111; Shanai + {4276,4276, 0, 0, 40000, 33,0.000000 }, // 4438: skeakernetsM111; Shanai // Amplitude begins at 2706.1, peaks 3190.6 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4273,4273, 0, 0, 1940, 1940,0.000000 }, // 4438: skeakernetsM112; Tinkle Bell + {4277,4277, 0, 0, 1940, 1940,0.000000 }, // 4439: skeakernetsM112; Tinkle Bell // Amplitude begins at 464.7, peaks 2409.0 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4274,4275, 0, 1, 313, 313,0.031250 }, // 4439: skeakernetsM114; Steel Drums + {4278,4279, 0, 1, 313, 313,0.031250 }, // 4440: skeakernetsM114; Steel Drums // Amplitude begins at 2643.9, // fades to 20% at infs, keyoff fades to 20% in infs. - {4276,4276, 0, 0, 60, 60,0.000000 }, // 4440: skeakernetsM115; Woodblock + {4280,4280, 0, 0, 60, 60,0.000000 }, // 4441: skeakernetsM115; Woodblock // Amplitude begins at 2052.6, // fades to 20% at infs, keyoff fades to 20% in infs. - {4277,4278, 0, 1, 53, 53,0.000000 }, // 4441: skeakernetsM116; Taiko Drum (new) + {4281,4282, 0, 1, 53, 53,0.000000 }, // 4442: skeakernetsM116; Taiko Drum (new) // Amplitude begins at 2309.8, peaks 2481.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4279,4279, 0, 0, 140, 140,0.000000 }, // 4442: skeakernetsM117; Melodic Tom + {4283,4283, 0, 0, 140, 140,0.000000 }, // 4443: skeakernetsM117; Melodic Tom - // Amplitude begins at 2855.0, + // Amplitude begins at 2927.7, // fades to 20% at infs, keyoff fades to 20% in infs. - {4280,4281, 0, 1, 26, 26,0.000000 }, // 4443: skeakernetsM118; Synth Drum + {4284,4285, 0, 1, 26, 26,0.000000 }, // 4444: skeakernetsM118; Synth Drum // Amplitude begins at 0.0, peaks 1577.5 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4282,4283, 0, 1, 2153, 2153,0.109375 }, // 4444: skeakernetsM119; Reverse Cymbal + {4286,4287, 0, 1, 2153, 2153,0.109375 }, // 4445: skeakernetsM119; Reverse Cymbal // Amplitude begins at 0.0, peaks 1150.6 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4284,4284, 0, 0, 213, 213,0.000000 }, // 4445: skeakernetsM120; Guitar Fret Noise + {4288,4288, 0, 0, 213, 213,0.000000 }, // 4446: skeakernetsM120; Guitar Fret Noise // Amplitude begins at 0.5, peaks 1999.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4285,4286, 0, 1, 993, 26,0.000000 }, // 4446: skeakernetsM121; Breath Noise + {4289,4290, 0, 1, 993, 26,0.000000 }, // 4447: skeakernetsM121; Breath Noise // Amplitude begins at 0.0, peaks 814.7 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4287,4288, 0, 1, 5613, 5613,0.000000 }, // 4447: skeakernetsM122; Seashore + {4291,4292, 0, 1, 5613, 5613,0.000000 }, // 4448: skeakernetsM122; Seashore // Amplitude begins at 125.4, peaks 2558.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4289,4289, 0, 0, 220, 220,0.000000 }, // 4448: skeakernetsM123; Bird Tweet + {4293,4293, 0, 0, 220, 220,0.000000 }, // 4449: skeakernetsM123; Bird Tweet // Amplitude begins at 1177.5, peaks 1318.0 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4290,4291, 0, 1, 10306, 10306,0.000000 }, // 4449: skeakernetsM124; Telephone Ring + {4294,4295, 0, 1, 10306, 10306,0.000000 }, // 4450: skeakernetsM124; Telephone Ring // Amplitude begins at 0.0, peaks 1710.6 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4292,4293, 0, 1, 1486, 13,0.000000 }, // 4450: skeakernetsM125; Helicopter + {4296,4297, 0, 1, 1486, 13,0.000000 }, // 4451: skeakernetsM125; Helicopter // Amplitude begins at 0.0, peaks 1877.4 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4294,4295, 0, 1, 40000, 0,0.000000 }, // 4451: skeakernetsM126; Applause + {4298,4299, 0, 1, 40000, 0,0.000000 }, // 4452: skeakernetsM126; Applause // Amplitude begins at 1211.7, // fades to 20% at infs, keyoff fades to 20% in infs. - {4296,4296, 0, 0, 566, 566,0.000000 }, // 4452: skeakernetsM127; Gun Shot + {4300,4300, 0, 0, 566, 566,0.000000 }, // 4453: skeakernetsM127; Gun Shot // Amplitude begins at 2239.5, // fades to 20% at infs, keyoff fades to 20% in infs. - {4297,4297, 25, 0, 33, 33,0.000000 }, // 4453: skeakernetsP35; Acoustic Bass Drum + {4301,4301, 25, 0, 33, 33,0.000000 }, // 4454: skeakernetsP35; Acoustic Bass Drum // Amplitude begins at 612.8, // fades to 20% at infs, keyoff fades to 20% in infs. - {4298,4299, 61, 1, 40, 40,0.000000 }, // 4454: skeakernetsP37; Slide Stick + {4302,4303, 61, 1, 40, 40,0.000000 }, // 4455: skeakernetsP37; Slide Stick // Amplitude begins at 263.0, peaks 989.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4300,4301, 37, 1, 53, 53,0.000000 }, // 4455: skeakernetsP39; ÿHand Clap + {4304,4305, 37, 1, 53, 53,0.000000 }, // 4456: skeakernetsP39; ÿHand Clap // Amplitude begins at 1309.5, peaks 1611.0 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4302,4302, 32, 0, 60, 60,0.000000 }, // 4456: skeakernetsP41; ÿLow Floor Tom + {4306,4306, 32, 0, 60, 60,0.000000 }, // 4457: skeakernetsP41; ÿLow Floor Tom // Amplitude begins at 592.6, // fades to 20% at infs, keyoff fades to 20% in infs. - {4303,4304, 48, 1, 73, 73,-1.906250 }, // 4457: skeakernetsP42; Closed High-Hat + {4307,4308, 48, 1, 73, 73,-1.906250 }, // 4458: skeakernetsP42; Closed High-Hat // Amplitude begins at 1451.5, peaks 1533.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4302,4302, 34, 0, 73, 73,0.000000 }, // 4458: skeakernetsP43; ÿHigh Floor Tom + {4306,4306, 34, 0, 73, 73,0.000000 }, // 4459: skeakernetsP43; ÿHigh Floor Tom // Amplitude begins at 33.4, peaks 538.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4305,4305, 48, 0, 53, 53,0.000000 }, // 4459: skeakernetsP44; Pedal High Hat + {4309,4309, 48, 0, 53, 53,0.000000 }, // 4460: skeakernetsP44; Pedal High Hat // Amplitude begins at 1643.8, // fades to 20% at infs, keyoff fades to 20% in infs. - {4306,4306, 37, 0, 46, 46,0.000000 }, // 4460: skeakernetsP45; ÿLow Tom + {4310,4310, 37, 0, 46, 46,0.000000 }, // 4461: skeakernetsP45; ÿLow Tom // Amplitude begins at 1609.9, peaks 1679.4 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4306,4306, 40, 0, 40, 40,0.000000 }, // 4461: skeakernetsP47; ÿLow-Mid Tom + {4310,4310, 40, 0, 40, 40,0.000000 }, // 4462: skeakernetsP47; ÿLow-Mid Tom // Amplitude begins at 1612.5, // fades to 20% at infs, keyoff fades to 20% in infs. - {4306,4306, 43, 0, 33, 33,0.000000 }, // 4462: skeakernetsP48; ÿHigh-Mid Tom + {4310,4310, 43, 0, 33, 33,0.000000 }, // 4463: skeakernetsP48; ÿHigh-Mid Tom // Amplitude begins at 942.8, peaks 966.9 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4307,4308, 61, 1, 1713, 1713,0.093750 }, // 4463: skeakernetsP49; Crash Cymbal 1 + {4311,4312, 61, 1, 1713, 1713,0.093750 }, // 4464: skeakernetsP49; Crash Cymbal 1 // Amplitude begins at 1497.3, // fades to 20% at infs, keyoff fades to 20% in infs. - {4306,4306, 46, 0, 60, 60,0.000000 }, // 4464: skeakernetsP50; ÿHigh Tom + {4310,4310, 46, 0, 60, 60,0.000000 }, // 4465: skeakernetsP50; ÿHigh Tom // Amplitude begins at 527.6, // fades to 20% at infs, keyoff fades to 20% in infs. - {4309,4310, 60, 1, 286, 286,0.062500 }, // 4465: skeakernetsP51; Ride Cymbal 1 + {4313,4314, 60, 1, 286, 286,0.062500 }, // 4466: skeakernetsP51; Ride Cymbal 1 // Amplitude begins at 869.9, // fades to 20% at infs, keyoff fades to 20% in infs. - {4311,4312, 79, 1, 293, 293,0.078125 }, // 4466: skeakernetsP52; Chinses Cymbal + {4315,4316, 79, 1, 293, 293,0.078125 }, // 4467: skeakernetsP52; Chinses Cymbal // Amplitude begins at 645.3, peaks 666.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4313,4313, 62, 0, 1726, 1726,0.000000 }, // 4467: skeakernetsP53; Ride Bell + {4317,4317, 62, 0, 1726, 1726,0.000000 }, // 4468: skeakernetsP53; Ride Bell // Amplitude begins at 844.2, peaks 1100.9 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4314,4315, 80, 1, 106, 106,0.125000 }, // 4468: skeakernetsP54; Tambourine + {4318,4319, 80, 1, 106, 106,0.125000 }, // 4469: skeakernetsP54; Tambourine // Amplitude begins at 795.8, // fades to 20% at infs, keyoff fades to 20% in infs. - {4316,4317, 67, 1, 240, 240,0.078125 }, // 4469: skeakernetsP55; Splash Cymbal + {4320,4321, 67, 1, 240, 240,0.078125 }, // 4470: skeakernetsP55; Splash Cymbal // Amplitude begins at 2425.9, // fades to 20% at infs, keyoff fades to 20% in infs. - {4318,4318, 58, 0, 73, 73,0.000000 }, // 4470: skeakernetsP56; Cowbell + {4322,4322, 58, 0, 73, 73,0.000000 }, // 4471: skeakernetsP56; Cowbell // Amplitude begins at 1409.0, peaks 1532.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4319,4320, 62, 1, 1946, 1946,0.093750 }, // 4471: skeakernetsP57; Crash Cymbal 2 + {4323,4324, 62, 1, 1946, 1946,0.093750 }, // 4472: skeakernetsP57; Crash Cymbal 2 // Amplitude begins at 2144.7, peaks 2521.0 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4321,4321, 24, 0, 780, 780,0.000000 }, // 4472: skeakernetsP58; Vibraslap + {4325,4325, 24, 0, 780, 780,0.000000 }, // 4473: skeakernetsP58; Vibraslap // Amplitude begins at 1280.9, peaks 1290.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4322,4322, 61, 0, 206, 206,0.000000 }, // 4473: skeakernetsP59; Ride Cymbal 2 + {4326,4326, 61, 0, 206, 206,0.000000 }, // 4474: skeakernetsP59; Ride Cymbal 2 // Amplitude begins at 579.3, peaks 2735.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4323,4324, 41, 1, 100, 100,0.000000 }, // 4474: skeakernetsP60; High Bongo (New) + {4327,4328, 41, 1, 100, 100,0.000000 }, // 4475: skeakernetsP60; High Bongo (New) // Amplitude begins at 712.1, peaks 2862.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4325,4326, 35, 1, 160, 160,0.000000 }, // 4475: skeakernetsP61; Low Bongo (New) + {4329,4330, 35, 1, 160, 160,0.000000 }, // 4476: skeakernetsP61; Low Bongo (New) // Amplitude begins at 281.8, peaks 1597.9 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4327,4328, 29, 1, 40, 40,0.000000 }, // 4476: skeakernetsP62; Mute high conga (New) + {4331,4332, 29, 1, 40, 40,0.000000 }, // 4477: skeakernetsP62; Mute high conga (New) // Amplitude begins at 846.9, peaks 2855.8 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4329,4330, 41, 1, 166, 166,0.000000 }, // 4477: skeakernetsP63; Open High Conga (New) + {4333,4334, 41, 1, 166, 166,0.000000 }, // 4478: skeakernetsP63; Open High Conga (New) // Amplitude begins at 915.5, peaks 2837.2 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4329,4330, 37, 1, 160, 160,0.000000 }, // 4478: skeakernetsP64; Low Conga (New) + {4333,4334, 37, 1, 160, 160,0.000000 }, // 4479: skeakernetsP64; Low Conga (New) // Amplitude begins at 2674.8, // fades to 20% at infs, keyoff fades to 20% in infs. - {4331,4331, 55, 0, 66, 66,0.000000 }, // 4479: skeakernetsP65; High Timbale + {4335,4335, 55, 0, 66, 66,0.000000 }, // 4480: skeakernetsP65; High Timbale // Amplitude begins at 2756.4, // fades to 20% at infs, keyoff fades to 20% in infs. - {4331,4331, 48, 0, 80, 80,0.000000 }, // 4480: skeakernetsP66; Low Timbale + {4335,4335, 48, 0, 80, 80,0.000000 }, // 4481: skeakernetsP66; Low Timbale // Amplitude begins at 1471.3, // fades to 20% at infs, keyoff fades to 20% in infs. - {4332,4333, 78, 1, 126, 126,0.000000 }, // 4481: skeakernetsP67; High Agogo + {4336,4337, 78, 1, 126, 126,0.000000 }, // 4482: skeakernetsP67; High Agogo // Amplitude begins at 1721.1, // fades to 20% at infs, keyoff fades to 20% in infs. - {4332,4334, 73, 1, 113, 113,0.000000 }, // 4482: skeakernetsP68; Low Agogo + {4336,4338, 73, 1, 113, 113,0.000000 }, // 4483: skeakernetsP68; Low Agogo // Amplitude begins at 42.0, peaks 443.4 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4335,4335, 40, 0, 140, 140,0.000000 }, // 4483: skeakernetsP70; Maracas + {4339,4339, 40, 0, 140, 140,0.000000 }, // 4484: skeakernetsP70; Maracas // Amplitude begins at 1203.0, peaks 1485.9 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4336,4336, 45, 0, 313, 313,0.000000 }, // 4484: skeakernetsP71; Short Whistle + {4340,4340, 45, 0, 313, 313,0.000000 }, // 4485: skeakernetsP71; Short Whistle // Amplitude begins at 1200.3, peaks 1494.7 at infs, // fades to 20% at infs, keyoff fades to 20% in -nans. - {4337,4337, 42, 0, 40000, 0,0.000000 }, // 4485: skeakernetsP72; Long Whistle + {4341,4341, 42, 0, 40000, 0,0.000000 }, // 4486: skeakernetsP72; Long Whistle // Amplitude begins at 1813.9, // fades to 20% at infs, keyoff fades to 20% in infs. - {4338,4339, 48, 1, 80, 80,0.000000 }, // 4486: skeakernetsP73; ÿShort Guiro + {4342,4343, 48, 1, 80, 80,0.000000 }, // 4487: skeakernetsP73; ÿShort Guiro // Amplitude begins at 2421.1, // fades to 20% at infs, keyoff fades to 20% in infs. - {4340,4341, 48, 1, 53, 53,0.000000 }, // 4487: skeakernetsP74; ÿLong Guiro + {4344,4345, 48, 1, 53, 53,0.000000 }, // 4488: skeakernetsP74; ÿLong Guiro // Amplitude begins at 2419.2, // fades to 20% at infs, keyoff fades to 20% in infs. - {4342,4342, 73, 0, 60, 60,0.000000 }, // 4488: skeakernetsP75; Claves + {4346,4346, 73, 0, 60, 60,0.000000 }, // 4489: skeakernetsP75; Claves // Amplitude begins at 2067.1, // fades to 20% at infs, keyoff fades to 20% in infs. - {4343,4343, 16, 0, 20, 20,0.000000 }, // 4489: skeakernetsP78; Mute Cuica + {4347,4347, 16, 0, 20, 20,0.000000 }, // 4490: skeakernetsP78; Mute Cuica // Amplitude begins at 2547.6, // fades to 20% at infs, keyoff fades to 20% in infs. - {4344,4344, 16, 0, 146, 146,0.000000 }, // 4490: skeakernetsP79; Open Cuica + {4348,4348, 16, 0, 146, 146,0.000000 }, // 4491: skeakernetsP79; Open Cuica // Amplitude begins at 780.7, // fades to 20% at infs, keyoff fades to 20% in infs. - {4345,4345, 90, 0, 80, 80,0.000000 }, // 4491: skeakernetsP80; Mute Triangle + {4349,4349, 90, 0, 80, 80,0.000000 }, // 4492: skeakernetsP80; Mute Triangle // Amplitude begins at 823.4, // fades to 20% at infs, keyoff fades to 20% in infs. - {4346,4346, 90, 0, 306, 306,0.000000 }, // 4492: skeakernetsP81; Open Triangle + {4350,4350, 90, 0, 306, 306,0.000000 }, // 4493: skeakernetsP81; Open Triangle // Amplitude begins at 5.1, peaks 649.7 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4347,4348, 64, 1, 233, 233,0.031250 }, // 4493: skeakernetsP82; (GS & XG) Shaker + {4351,4352, 64, 1, 233, 233,0.031250 }, // 4494: skeakernetsP82; (GS & XG) Shaker // Amplitude begins at 15.2, peaks 820.6 at infs, // fades to 20% at infs, keyoff fades to 20% in infs. - {4349,4350, 80, 1, 140, 140,0.031250 }, // 4494: skeakernetsP83; (GS & XG) Jingle Bells + {4353,4354, 80, 1, 140, 140,0.031250 }, // 4495: skeakernetsP83; (GS & XG) Jingle Bells }; @@ -23744,14 +23752,14 @@ const unsigned short banks[73][256] = 4377,4378,4379,4380,4381,4382,4383,4384,4385,4386, 57,4387,4388,4389,4390,4391, 4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403,4404,4405,1300,4406, 4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,4417,4418,4419,4420,4421,4422, -4423,4424,4425,4426,4427,4428,4429,4430,4431,4432,4433,4434,4435,1330,4436,4437, -4438,1334,4439,4440,4441,4442,4443,4444,4445,4446,4447,4448,4449,4450,4451,4452, +4423,4424,4425,4426,4427,4428,4429,4430,4431,4432,4433,4434,4435,4436,4437,4438, +4439,1334,4440,4441,4442,4443,4444,4445,4446,4447,4448,4449,4450,4451,4452,4453, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, - 806, 806, 806,4453,1349,4454,4218,4455,4219,4456,4457,4458,4459,4460,4222,4461, -4462,4463,4464,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477, -4478,4479,4480,4481,4482, 320,4483,4484,4485,4486,4487,4488,1373,1374,4489,4490, -4491,4492,4493,4494, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, + 806, 806, 806,4454,1349,4455,4218,4456,4219,4457,4458,4459,4460,4461,4222,4462, +4463,4464,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478, +4479,4480,4481,4482,4483, 320,4484,4485,4486,4487,4488,4489,1373,1374,4490,4491, +4492,4493,4494,4495, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, 806, }, diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index 4710dfe..7aa9e8b 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -31,36 +31,11 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) { ADL_MIDIPlayer *midi_device; midi_device = (ADL_MIDIPlayer *)malloc(sizeof(ADL_MIDIPlayer)); - midi_device->PCM_RATE = static_cast<unsigned long>(sample_rate); - midi_device->AdlBank = 0; - midi_device->NumFourOps = 7; - midi_device->NumCards = 2; - midi_device->HighTremoloMode = 0; - midi_device->HighVibratoMode = 0; - midi_device->AdlPercussionMode = 0; - midi_device->LogarithmicVolumes = 0; - midi_device->SkipForward = 0; - midi_device->loopingIsEnabled = 0; - midi_device->ScaleModulators = 0; - midi_device->delay = 0.0; - midi_device->carry = 0.0; - midi_device->mindelay = 1.0 / (double)midi_device->PCM_RATE; - midi_device->maxdelay = 512.0 / (double)midi_device->PCM_RATE; - midi_device->stored_samples = 0; - midi_device->backup_samples_size = 0; - MIDIplay *player = new MIDIplay; midi_device->adl_midiPlayer = player; - player->config = midi_device; - player->opl._parent = midi_device; - player->opl.NumCards = midi_device->NumCards; - player->opl.AdlBank = midi_device->AdlBank; - player->opl.NumFourOps = midi_device->NumFourOps; - player->opl.LogarithmicVolumes = (midi_device->LogarithmicVolumes != 0); - player->opl.HighTremoloMode = (midi_device->HighTremoloMode != 0); - player->opl.HighVibratoMode = (midi_device->HighVibratoMode != 0); - player->opl.AdlPercussionMode = (midi_device->AdlPercussionMode != 0); - player->opl.ScaleModulators = (midi_device->ScaleModulators != 0); + player->m_setup.PCM_RATE = static_cast<unsigned long>(sample_rate); + player->m_setup.mindelay = 1.0 / (double)player->m_setup.PCM_RATE; + player->m_setup.maxdelay = 512.0 / (double)player->m_setup.PCM_RATE; player->ChooseDevice("none"); adlRefreshNumCards(midi_device); return midi_device; @@ -71,10 +46,9 @@ ADLMIDI_EXPORT int adl_setNumCards(ADL_MIDIPlayer *device, int numCards) if(device == NULL) return -2; - device->NumCards = static_cast<unsigned int>(numCards); - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.NumCards = device->NumCards; - - if(device->NumCards < 1 || device->NumCards > MaxCards) + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.NumCards = static_cast<unsigned int>(numCards); + if(play->m_setup.NumCards < 1 || play->m_setup.NumCards > MaxCards) { std::stringstream s; s << "number of cards may only be 1.." << MaxCards << ".\n"; @@ -82,6 +56,8 @@ ADLMIDI_EXPORT int adl_setNumCards(ADL_MIDIPlayer *device, int numCards) return -1; } + play->opl.NumCards = play->m_setup.NumCards; + return adlRefreshNumCards(device); } @@ -99,11 +75,11 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) if(bankno < 0) bankno = 0; - device->AdlBank = static_cast<uint32_t>(bankno); MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); - play->opl.AdlBank = device->AdlBank; + play->m_setup.AdlBank = static_cast<uint32_t>(bankno); + play->opl.AdlBank = play->m_setup.AdlBank; - if(device->AdlBank >= NumBanks) + if(play->m_setup.AdlBank >= NumBanks) { std::stringstream s; s << "bank number may only be 0.." << (NumBanks - 1) << ".\n"; @@ -127,13 +103,16 @@ ADLMIDI_EXPORT const char *const *adl_getBankNames() ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) { - device->NumFourOps = static_cast<unsigned int>(ops4); - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.NumFourOps = device->NumFourOps; + if(!device) + return -1; + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.NumFourOps = static_cast<unsigned int>(ops4); + play->opl.NumFourOps = play->m_setup.NumFourOps; - if(device->NumFourOps > 6 * device->NumCards) + if(play->m_setup.NumFourOps > 6 * play->m_setup.NumCards) { std::stringstream s; - s << "number of four-op channels may only be 0.." << (6 * (device->NumCards)) << " when " << device->NumCards << " OPL3 cards are used.\n"; + s << "number of four-op channels may only be 0.." << (6 * (play->m_setup.NumCards)) << " when " << play->m_setup.NumCards << " OPL3 cards are used.\n"; ADLMIDI_ErrorString = s.str(); return -1; } @@ -145,55 +124,56 @@ ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) { if(!device) return; - - device->AdlPercussionMode = static_cast<unsigned int>(percmod); - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.AdlPercussionMode = (device->AdlPercussionMode != 0); + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.AdlPercussionMode = (percmod != 0); + play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode; } ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) { if(!device) return; - - device->HighVibratoMode = static_cast<unsigned int>(hvibro); - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.HighVibratoMode = (device->HighVibratoMode != 0); + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.HighVibratoMode = (hvibro != 0); + play->opl.HighVibratoMode = play->m_setup.HighVibratoMode; } ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) { if(!device) return; - - device->HighTremoloMode = static_cast<unsigned int>(htremo); - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.HighTremoloMode = (device->HighTremoloMode != 0); + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.HighTremoloMode = (htremo != 0); + play->opl.HighTremoloMode = play->m_setup.HighTremoloMode; } ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) { if(!device) return; - - device->ScaleModulators = static_cast<unsigned int>(smod); - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.ScaleModulators = (device->ScaleModulators != 0); + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.ScaleModulators = (smod != 0); + play->opl.ScaleModulators = play->m_setup.ScaleModulators; } ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) { if(!device) return; - device->loopingIsEnabled = (loopEn != 0); + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.loopingIsEnabled = (loopEn != 0); } ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol) { if(!device) return; - - device->LogarithmicVolumes = static_cast<unsigned int>(logvol); - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.LogarithmicVolumes = (device->LogarithmicVolumes != 0); + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.LogarithmicVolumes = (logvol != 0); + play->opl.LogarithmicVolumes = play->m_setup.LogarithmicVolumes; } ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel) { if(!device) return; - - device->VolumeModel = volumeModel; - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(volumeModel)); + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.VolumeModel = volumeModel; + play->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(volumeModel)); } ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, char *filePath) @@ -202,10 +182,10 @@ ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, char *filePat if(device && device->adl_midiPlayer) { - device->stored_samples = 0; - device->backup_samples_size = 0; - - if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadBank(filePath)) + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.stored_samples = 0; + play->m_setup.backup_samples_size = 0; + if(!play->LoadBank(filePath)) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load file"; @@ -224,10 +204,10 @@ ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, void *mem, lo if(device && device->adl_midiPlayer) { - device->stored_samples = 0; - device->backup_samples_size = 0; - - if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadBank(mem, static_cast<size_t>(size))) + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.stored_samples = 0; + play->m_setup.backup_samples_size = 0; + if(!play->LoadBank(mem, static_cast<size_t>(size))) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory"; @@ -246,14 +226,13 @@ ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath) if(device && device->adl_midiPlayer) { - device->stored_samples = 0; - device->backup_samples_size = 0; - - if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadMIDI(filePath)) + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.stored_samples = 0; + play->m_setup.backup_samples_size = 0; + if(!play->LoadMIDI(filePath)) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load file"; - return -1; } else return 0; @@ -269,14 +248,13 @@ ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, void *mem, long size) if(device && device->adl_midiPlayer) { - device->stored_samples = 0; - device->backup_samples_size = 0; - - if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadMIDI(mem, static_cast<size_t>(size))) + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.stored_samples = 0; + play->m_setup.backup_samples_size = 0; + if(!play->LoadMIDI(mem, static_cast<size_t>(size))) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory"; - return -1; } else return 0; @@ -294,8 +272,8 @@ ADLMIDI_EXPORT const char *adl_errorString() ADLMIDI_EXPORT const char *adl_getMusicTitle(ADL_MIDIPlayer *device) { - if(!device) return ""; - + if(!device) + return ""; return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->musTitle.c_str(); } @@ -303,7 +281,6 @@ ADLMIDI_EXPORT void adl_close(ADL_MIDIPlayer *device) { if(device->adl_midiPlayer) delete reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); - device->adl_midiPlayer = NULL; free(device); device = NULL; @@ -313,10 +290,45 @@ ADLMIDI_EXPORT void adl_reset(ADL_MIDIPlayer *device) { if(!device) return; + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + play->m_setup.stored_samples = 0; + play->m_setup.backup_samples_size = 0; + play->opl.Reset(play->m_setup.PCM_RATE); +} + +ADLMIDI_EXPORT double adl_totalTimeLength(ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->timeLength(); +} + +ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->getLoopStart(); +} + +ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->getLoopEnd(); +} - device->stored_samples = 0; - device->backup_samples_size = 0; - reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.Reset(); +ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->tell(); +} + +ADLMIDI_EXPORT void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds) +{ + if(!device) + return; + reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->seek(seconds); } ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) @@ -326,8 +338,15 @@ ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->rewind(); } +ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo) +{ + if(!device || (tempo <= 0.0)) + return; + reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->setTempo(tempo); +} + -inline static void SendStereoAudio(ADL_MIDIPlayer *device, +inline static void SendStereoAudio(MIDIplay::Setup &device, int &samples_requested, ssize_t &in_size, short *_in, @@ -337,7 +356,7 @@ inline static void SendStereoAudio(ADL_MIDIPlayer *device, if(!in_size) return; - device->stored_samples = 0; + device.stored_samples = 0; size_t offset = static_cast<size_t>(out_pos); size_t inSamples = static_cast<size_t>(in_size * 2); size_t maxSamples = static_cast<size_t>(samples_requested) - offset; @@ -347,10 +366,10 @@ inline static void SendStereoAudio(ADL_MIDIPlayer *device, if(maxSamples < inSamples) { size_t appendSize = inSamples - maxSamples; - std::memcpy(device->backup_samples + device->backup_samples_size, + std::memcpy(device.backup_samples + device.backup_samples_size, maxSamples + _in, appendSize * sizeof(short)); - device->backup_samples_size += (ssize_t)appendSize; - device->stored_samples += (ssize_t)appendSize; + device.backup_samples_size += (ssize_t)appendSize; + device.stored_samples += (ssize_t)appendSize; } } @@ -360,6 +379,9 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) if(!device) return 0; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + MIDIplay::Setup &setup = player->m_setup; + sampleCount -= sampleCount % 2; //Avoid even sample requests if(sampleCount < 0) @@ -372,44 +394,42 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) while(left > 0) { - if(device->backup_samples_size > 0) + if(setup.backup_samples_size > 0) { //Send reserved samples if exist ssize_t ate = 0; - while((ate < device->backup_samples_size) && (ate < left)) + while((ate < setup.backup_samples_size) && (ate < left)) { - out[ate] = device->backup_samples[ate]; + out[ate] = setup.backup_samples[ate]; ate++; } left -= (int)ate; gotten_len += ate; - if(ate < device->backup_samples_size) + if(ate < setup.backup_samples_size) { for(ssize_t j = 0; j < ate; j++) - device->backup_samples[(ate - 1) - j] = device->backup_samples[(device->backup_samples_size - 1) - j]; + setup.backup_samples[(ate - 1) - j] = setup.backup_samples[(setup.backup_samples_size - 1) - j]; } - device->backup_samples_size -= ate; + setup.backup_samples_size -= ate; } else { - const double eat_delay = device->delay < device->maxdelay ? device->delay : device->maxdelay; - device->delay -= eat_delay; - device->carry += device->PCM_RATE * eat_delay; - n_periodCountStereo = static_cast<ssize_t>(device->carry); - device->carry -= n_periodCountStereo; - - if(device->SkipForward > 0) - device->SkipForward -= 1; + const double eat_delay = setup.delay < setup.maxdelay ? setup.delay : setup.maxdelay; + setup.delay -= eat_delay; + setup.carry += setup.PCM_RATE * eat_delay; + n_periodCountStereo = static_cast<ssize_t>(setup.carry); + setup.carry -= n_periodCountStereo; + + if(setup.SkipForward > 0) + setup.SkipForward -= 1; else { - MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); - - if((player->atEnd) && (device->delay <= 0.0)) - break;//Stop to fetch samples at reaching of song end with disabled loop + if((player->atEnd) && (setup.delay <= 0.0)) + break;//Stop to fetch samples at reaching the song end with disabled loop //! Count of stereo samples ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; @@ -420,7 +440,7 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) int16_t *out_buf = player->outBuf; std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t)); - if(device->NumCards == 1) + if(player->m_setup.NumCards == 1) { #ifdef ADLMIDI_USE_DOSBOX_OPL player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo); @@ -428,13 +448,11 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) #else OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast<Bit32u>(in_generatedStereo)); #endif - /* Process it */ - SendStereoAudio(device, sampleCount, in_generatedStereo, out_buf, gotten_len, out); } else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ - for(unsigned card = 0; card < device->NumCards; ++card) + for(unsigned card = 0; card < player->m_setup.NumCards; ++card) { #ifdef ADLMIDI_USE_DOSBOX_OPL player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo); @@ -443,16 +461,15 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast<Bit32u>(in_generatedStereo)); #endif } - - /* Process it */ - SendStereoAudio(device, sampleCount, in_generatedStereo, out_buf, gotten_len, out); } + /* Process it */ + SendStereoAudio(setup, sampleCount, in_generatedStereo, out_buf, gotten_len, out); left -= (int)in_generatedPhys; - gotten_len += (in_generatedPhys) - device->stored_samples; + gotten_len += (in_generatedPhys) - setup.stored_samples; } - device->delay = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->Tick(eat_delay, device->mindelay); + setup.delay = player->Tick(eat_delay, setup.mindelay); } } diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp index 9ec14e7..08892a5 100644 --- a/src/adlmidi_load.cpp +++ b/src/adlmidi_load.cpp @@ -48,34 +48,6 @@ uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes) return result; } -uint64_t MIDIplay::ReadVarLenEx(size_t tk, bool &ok) -{ - uint64_t result = 0; - ok = false; - - for(;;) - { - if(tk >= TrackData.size()) - return 1; - - if(tk >= CurrentPosition.track.size()) - return 2; - - size_t ptr = CurrentPosition.track[tk].ptr; - - if(ptr >= TrackData[tk].size()) - return 3; - - unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++]; - result = (result << 7) + (byte & 0x7F); - - if(!(byte & 0x80)) break; - } - - ok = true; - return result; -} - bool MIDIplay::LoadBank(const std::string &filename) { fileReader file; @@ -122,7 +94,7 @@ enum WOPL_InstrumentFlags { WOPL_Flags_NONE = 0, WOPL_Flag_Enable4OP = 0x01, - WOPL_Flag_Pseudo4OP = 0x02, + WOPL_Flag_Pseudo4OP = 0x02 }; struct WOPL_Inst @@ -295,7 +267,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) } } - opl.AdlBank = opl._parent->AdlBank; + opl.AdlBank = m_setup.AdlBank; opl.dynamic_metainstruments.clear(); opl.dynamic_instruments.clear(); @@ -369,6 +341,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) ADL_UNUSED(fsize); //! Temp buffer for conversion AdlMIDI_CPtr<uint8_t> cvt_buf; + errorString.clear(); #ifdef DISABLE_EMBEDDED_BANKS if((opl.AdlBank != ~0u) || (opl.dynamic_metainstruments.size() < 256)) @@ -389,18 +362,18 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) /**** Set all properties BEFORE starting of actial file reading! ****/ - config->stored_samples = 0; - config->backup_samples_size = 0; - opl.AdlPercussionMode = (config->AdlPercussionMode != 0); - opl.HighTremoloMode = (config->HighTremoloMode != 0); - opl.HighVibratoMode = (config->HighVibratoMode != 0); - opl.ScaleModulators = (config->ScaleModulators != 0); - opl.LogarithmicVolumes = (config->LogarithmicVolumes != 0); - opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(config->VolumeModel)); + m_setup.stored_samples = 0; + m_setup.backup_samples_size = 0; + opl.AdlPercussionMode = m_setup.AdlPercussionMode; + opl.HighTremoloMode = m_setup.HighTremoloMode; + opl.HighVibratoMode = m_setup.HighVibratoMode; + opl.ScaleModulators = m_setup.ScaleModulators; + opl.LogarithmicVolumes = m_setup.LogarithmicVolumes; + opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(m_setup.VolumeModel)); - if(config->VolumeModel == ADLMIDI_VolumeModel_AUTO) + if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO) { - switch(config->AdlBank) + switch(m_setup.AdlBank) { default: opl.m_volumeScale = OPL3::VOLUME_Generic; @@ -429,17 +402,15 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) } } - opl.NumCards = config->NumCards; - opl.NumFourOps = config->NumFourOps; + opl.NumCards = m_setup.NumCards; + opl.NumFourOps = m_setup.NumFourOps; cmf_percussion_mode = false; - opl.Reset(); + opl.Reset(m_setup.PCM_RATE); trackStart = true; atEnd = false; loopStart = true; - loopStart_passed = false; invalidLoop = false; - loopStart_hit = false; bool is_GMF = false; // GMD/MUS files (ScummVM) //bool is_MUS = false; // MUS/DMX files (Doom) @@ -655,8 +626,8 @@ InvFmt: TrackData.clear(); TrackData.resize(TrackCount, std::vector<uint8_t>()); - CurrentPosition.track.clear(); - CurrentPosition.track.resize(TrackCount); + //CurrentPosition.track.clear(); + //CurrentPosition.track.resize(TrackCount); InvDeltaTicks = fraction<uint64_t>(1, 1000000l * static_cast<uint64_t>(DeltaTicks)); //Tempo = 1000000l * InvDeltaTicks; Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks)); @@ -673,7 +644,8 @@ InvFmt: //std::fprintf(stderr, "Reading IMF file...\n"); size_t end = static_cast<uint8_t>(HeaderBuf[0]) + 256 * static_cast<uint8_t>(HeaderBuf[1]); unsigned IMF_tempo = 1428; - static const unsigned char imf_tempo[] = {0xFF, 0x51, 0x4, + static const unsigned char imf_tempo[] = {0x0,//Zero delay! + MidiEvent::T_SPECIAL, MidiEvent::ST_TEMPOCHANGE, 0x4, static_cast<uint8_t>(IMF_tempo >> 24), static_cast<uint8_t>(IMF_tempo >> 16), static_cast<uint8_t>(IMF_tempo >> 8), @@ -686,31 +658,31 @@ InvFmt: while(fr.tell() < end && !fr.eof()) { uint8_t special_event_buf[5]; - special_event_buf[0] = 0xFF; - special_event_buf[1] = 0xE3; + uint8_t raw[4]; + special_event_buf[0] = MidiEvent::T_SPECIAL; + special_event_buf[1] = MidiEvent::ST_RAWOPL; special_event_buf[2] = 0x02; - special_event_buf[3] = static_cast<uint8_t>(fr.getc()); // port index - special_event_buf[4] = static_cast<uint8_t>(fr.getc()); // port value - uint32_t delay = static_cast<uint16_t>(fr.getc()); - delay += 256 * static_cast<uint16_t>(fr.getc()); + if(fr.read(raw, 1, 4) != 4) + break; + special_event_buf[3] = raw[0]; // port index + special_event_buf[4] = raw[1]; // port value + uint32_t delay = static_cast<uint32_t>(raw[2]); + delay += 256 * static_cast<uint32_t>(raw[3]); totalGotten += 4; //if(special_event_buf[3] <= 8) continue; //fprintf(stderr, "Put %02X <- %02X, plus %04X delay\n", special_event_buf[3],special_event_buf[4], delay); TrackData[tk].insert(TrackData[tk].end(), special_event_buf, special_event_buf + 5); - //if(delay>>21) TrackData[tk].push_back( 0x80 | ((delay>>21) & 0x7F ) ); if(delay >> 14) TrackData[tk].push_back(0x80 | ((delay >> 14) & 0x7F)); - if(delay >> 7) TrackData[tk].push_back(0x80 | ((delay >> 7) & 0x7F)); - TrackData[tk].push_back(((delay >> 0) & 0x7F)); } TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4); - CurrentPosition.track[tk].delay = 0; - CurrentPosition.began = true; + //CurrentPosition.track[tk].delay = 0; + //CurrentPosition.began = true; //std::fprintf(stderr, "Done reading IMF file\n"); opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing! } @@ -734,10 +706,8 @@ InvFmt: else { fsize = fr.read(HeaderBuf, 1, 8); - if(std::memcmp(HeaderBuf, "MTrk", 4) != 0) goto InvFmt; - TrackLength = (size_t)ReadBEint(HeaderBuf + 4, 4); } @@ -749,19 +719,18 @@ InvFmt: if(is_GMF /*|| is_MUS*/) // Note: CMF does include the track end tag. TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4); - bool ok = false; - // Read next event time - uint64_t tkDelay = ReadVarLenEx(tk, ok); - - if(ok) - CurrentPosition.track[tk].delay = tkDelay; - else - { - std::stringstream msg; - msg << fr._fileName << ": invalid variable length in the track " << tk << "! (error code " << tkDelay << ")"; - ADLMIDI_ErrorString = msg.str(); - return false; - } + //bool ok = false; + //// Read next event time + //uint64_t tkDelay = ReadVarLenEx(tk, ok); + //if(ok) + // CurrentPosition.track[tk].delay = tkDelay; + //else + //{ + // std::stringstream msg; + // msg << fr._fileName << ": invalid variable length in the track " << tk << "! (error code " << tkDelay << ")"; + // ADLMIDI_ErrorString = msg.str(); + // return false; + //} } } @@ -774,10 +743,14 @@ InvFmt: return false; } - //Build new MIDI events table (WIP!!!) - buildTrackData(); + //Build new MIDI events table (ALPHA!!!) + if(!buildTrackData()) + { + ADLMIDI_ErrorString = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString; + return false; + } - opl.Reset(); // Reset AdLib + opl.Reset(m_setup.PCM_RATE); // Reset AdLib //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously) ch.clear(); ch.resize(opl.NumChannels); diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index f536f4e..577e8b8 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -23,7 +23,6 @@ #include "adlmidi_private.hpp" - // Mapping from MIDI volume level to OPL level value. static const uint32_t DMX_volume_mapping_table[] = @@ -132,27 +131,29 @@ void MIDIplay::AdlChannel::AddAge(int64_t ms) MIDIplay::MidiEvent::MidiEvent() : type(T_UNKNOWN), subtype(T_UNKNOWN), - channel(0) + channel(0), + isValid(1), + absPosition(0) {} -MIDIplay::MidiTrackPos::MidiTrackPos() : +MIDIplay::MidiTrackRow::MidiTrackRow() : time(0.0), delay(0), - timeDelay(0.0), - next(NULL) + absPos(0), + timeDelay(0.0) {} -void MIDIplay::MidiTrackPos::reset() +void MIDIplay::MidiTrackRow::reset() { time = 0.0; delay = 0; + absPos = 0; timeDelay = 0.0; events.clear(); - next = NULL; } -void MIDIplay::MidiTrackPos::sortEvents() +void MIDIplay::MidiTrackRow::sortEvents() { std::vector<MidiEvent> metas; std::vector<MidiEvent> noteOffs; @@ -172,7 +173,7 @@ void MIDIplay::MidiTrackPos::sortEvents() controllers.push_back(events[i]); } else - if((events[i].type == MidiEvent::T_SPECIAL) && (events[i].subtype == MidiEvent::ST_META)) + if((events[i].type == MidiEvent::T_SPECIAL) && (events[i].subtype == MidiEvent::ST_MARKER)) metas.push_back(events[i]); else anyOther.push_back(events[i]); @@ -185,83 +186,316 @@ void MIDIplay::MidiTrackPos::sortEvents() events.insert(events.end(), anyOther.begin(), anyOther.end()); } -void MIDIplay::buildTrackData() +bool MIDIplay::buildTrackData() { + fullSongTimeLength = 0.0; + loopStartTime = -1.0; + loopEndTime = -1.0; + musTitle.clear(); trackDataNew.clear(); - trackDataNewStatus.clear(); const size_t trackCount = TrackData.size(); - trackDataNew.resize(trackCount, std::vector<MidiTrackPos>()); - trackDataNewStatus.resize(trackCount, 0); + trackDataNew.resize(trackCount, MidiTrackQueue()); + + invalidLoop = false; + bool gotLoopStart = false, gotLoopEnd = false, gotLoopEventInThisRow = false; + //! Tick position of loop start tag + uint64_t loopStartTicks = 0; + //! Tick position of loop end tag + uint64_t loopEndTicks = 0; + //! Full length of song in ticks + uint64_t ticksSongLength = 0; + //! Cache for error message strign + char error[150]; CurrentPositionNew.track.clear(); CurrentPositionNew.track.resize(trackCount); - /* TODO: Based on tempo changes, make accurate seconds time marking. - * Current way is inaccurate, because of tempo change at different track - * will cause time desynchronization between tracks. - * Also, seconds calculation is incorrect */ + //Tempo change events + std::vector<MidiEvent> tempos; + + /* + * TODO: Make this be safer for memory in case of broken input data + * which may cause going away of available track data (and then give a crash!) + */ for(size_t tk = 0; tk < trackCount; ++tk) { - fraction<uint64_t> currentTempo = Tempo; - double time = 0.0; + uint64_t abs_position = 0; int status = 0; - std::vector<MidiTrackPos> posEvents; MidiEvent event; + bool ok = false; + uint8_t *end = TrackData[tk].data() + TrackData[tk].size(); uint8_t *trackPtr = TrackData[tk].data(); //Time delay that follows the first event in the track { - MidiTrackPos evtPos; - evtPos.delay = ReadVarLen(&trackPtr); - fraction<uint64_t> t = evtPos.delay * currentTempo; + MidiTrackRow evtPos; + evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); + if(!ok) + { + int len = std::snprintf(error, 150, "buildTrackData: Can't read variable-length value at begin of track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + errorString += std::string(error, (size_t)len); + return false; + } CurrentPositionNew.wait = evtPos.delay; - evtPos.timeDelay = t.value(); - time += evtPos.timeDelay; + evtPos.absPos = abs_position; + abs_position += evtPos.delay; trackDataNew[tk].push_back(evtPos); } - MidiTrackPos evtPos; + MidiTrackRow evtPos; do { - event = parseEvent(&trackPtr, status); + event = parseEvent(&trackPtr, end, status); + if(!event.isValid) + { + int len = std::snprintf(error, 150, "buildTrackData: Fail to parse event in the track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + errorString += std::string(error, (size_t)len); + return false; + } evtPos.events.push_back(event); - if(event.type == MidiEvent::T_SPECIAL && event.subtype == MidiEvent::ST_TEMPOCHANGE) - currentTempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(event.data.data(), event.data.size())); - evtPos.delay = ReadVarLen(&trackPtr); + if(event.type == MidiEvent::T_SPECIAL) + { + if(event.subtype == MidiEvent::ST_TEMPOCHANGE) + { + event.absPosition = abs_position; + tempos.push_back(event); + } + else + if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTART)) + { + /* + * loopStart is invalid when: + * - starts together with loopEnd + * - appears more than one time in same MIDI file + */ + if(gotLoopStart || gotLoopEventInThisRow) + invalidLoop = true; + else + { + gotLoopStart = true; + loopStartTicks = abs_position; + } + //In this row we got loop event, register this! + gotLoopEventInThisRow = true; + } + else + if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND)) + { + /* + * loopEnd is invalid when: + * - starts before loopStart + * - starts together with loopStart + * - appars more than one time in same MIDI file + */ + if(gotLoopEnd || gotLoopEventInThisRow) + invalidLoop = true; + else + { + gotLoopEnd = true; + loopEndTicks = abs_position; + } + //In this row we got loop event, register this! + gotLoopEventInThisRow = true; + } + } + + if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event! + { + evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); + if(!ok) + { + int len = std::snprintf(error, 150, "buildTrackData: Can't read variable-length value in the track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + errorString += std::string(error, (size_t)len); + return false; + } + } + if((evtPos.delay > 0) || (event.subtype == MidiEvent::ST_ENDTRACK)) { - fraction<uint64_t> t = evtPos.delay * currentTempo; - evtPos.timeDelay = t.value() ; - evtPos.time = time; - time += evtPos.timeDelay; + evtPos.absPos = abs_position; + abs_position += evtPos.delay; evtPos.sortEvents(); trackDataNew[tk].push_back(evtPos); evtPos.reset(); + gotLoopEventInThisRow = false; } - } while(event.subtype != MidiEvent::ST_ENDTRACK); - - // Build the chain of events - for(size_t i = 0, j = 1; i < trackDataNew[tk].size() && j < trackDataNew[tk].size(); i++, j++) - trackDataNew[tk][i].next = &(trackDataNew[tk][j]); + } while((trackPtr <= end) && (event.subtype != MidiEvent::ST_ENDTRACK)); + if(ticksSongLength < abs_position) + ticksSongLength = abs_position; + //Set the chain of events begin if(trackDataNew[tk].size() > 0) - CurrentPositionNew.track[tk].pos = &trackDataNew[tk][0]; + CurrentPositionNew.track[tk].pos = trackDataNew[tk].begin(); + } + + if(gotLoopStart && !gotLoopEnd) + { + gotLoopEnd = true; + loopEndTicks = ticksSongLength; } + //loopStart must be located before loopEnd! + if(loopStartTicks >= loopEndTicks) + invalidLoop = true; + + //! Calculate time basing on collected tempo events + for(size_t tk = 0; tk < trackCount; ++tk) + { + fraction<uint64_t> currentTempo = Tempo; + double time = 0.0; + uint8_t abs_position = 0; + size_t tempo_change_index = 0; + MidiTrackQueue &track = trackDataNew[tk]; + if(track.empty()) + continue;//Empty track is useless! + + #ifdef DEBUG_TIME_CALCULATION + std::fprintf(stdout, "\n============Track %" PRIuPTR "=============\n", tk); + std::fflush(stdout); + #endif + + MidiTrackRow *posPrev = &(*(track.begin()));//First element + for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) + { + #ifdef DEBUG_TIME_CALCULATION + bool tempoChanged = false; + #endif + MidiTrackRow &pos = *it; + if( (posPrev != &pos) && //Skip first event + (!tempos.empty()) && //Only when in-track tempo events are available + (tempo_change_index < tempos.size()) + ) + { + // If tempo event is going between of current and previous event + if(tempos[tempo_change_index].absPosition <= pos.absPos) + { + //Stop points: begin point and tempo change points are before end point + std::vector<TempoChangePoint> points; + fraction<uint64_t> t; + TempoChangePoint firstPoint = {posPrev->absPos, currentTempo}; + points.push_back(firstPoint); + + //Collect tempo change points between previous and current events + do + { + TempoChangePoint tempoMarker; + MidiEvent &tempoPoint = tempos[tempo_change_index]; + tempoMarker.absPos = tempoPoint.absPosition; + tempoMarker.tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(tempoPoint.data.data(), tempoPoint.data.size())); + points.push_back(tempoMarker); + tempo_change_index++; + } + while((tempo_change_index < tempos.size()) && + (tempos[tempo_change_index].absPosition <= pos.absPos)); + + // Re-calculate time delay of previous event + time -= posPrev->timeDelay; + posPrev->timeDelay = 0.0; + + for(size_t i = 0, j = 1; j < points.size(); i++, j++) + { + /* If one or more tempo events are appears between of two events, + * calculate delays between each tempo point, begin and end */ + uint64_t midDelay = 0; + //Delay between points + midDelay = points[j].absPos - points[i].absPos; + //Time delay between points + t = midDelay * currentTempo; + posPrev->timeDelay += t.value(); + + //Apply next tempo + currentTempo = points[j].tempo; + #ifdef DEBUG_TIME_CALCULATION + tempoChanged = true; + #endif + } + //Then calculate time between last tempo change point and end point + TempoChangePoint tailTempo = points.back(); + uint64_t postDelay = pos.absPos - tailTempo.absPos; + t = postDelay * currentTempo; + posPrev->timeDelay += t.value(); + + //Store Common time delay + posPrev->time = time; + time += posPrev->timeDelay; + } + } + + fraction<uint64_t> t = pos.delay * currentTempo; + pos.timeDelay = t.value(); + pos.time = time; + time += pos.timeDelay; + + if(!invalidLoop) + { + // Set loop points times + if(loopStartTicks == pos.absPos) + loopStartTime = pos.time; + else + if(loopEndTicks == pos.absPos) + loopEndTime = pos.time; + } + + #ifdef DEBUG_TIME_CALCULATION + std::fprintf(stdout, "= %10" PRId64 " = %10f%s\n", pos.absPos, pos.time, tempoChanged ? " <----TEMPO CHANGED" : ""); + std::fflush(stdout); + #endif + + abs_position += pos.delay; + posPrev = &pos; + } + + if(time > fullSongTimeLength) + fullSongTimeLength = time; + } + + fullSongTimeLength += postSongWaitDelay; + trackBeginPositionNew = CurrentPositionNew; + + return true; } MIDIplay::MIDIplay(): cmf_percussion_mode(false), - config(NULL), + fullSongTimeLength(0.0), + postSongWaitDelay(1.0), + loopStartTime(-1.0), + loopEndTime(-1.0), + tempoMultiplier(1.0), trackStart(false), atEnd(false), loopStart(false), loopEnd(false), - loopStart_passed(false), - invalidLoop(false), - loopStart_hit(false) + invalidLoop(false) { devices.clear(); + + m_setup.AdlBank = 0; + m_setup.NumFourOps = 7; + m_setup.NumCards = 2; + m_setup.HighTremoloMode = false; + m_setup.HighVibratoMode = false; + m_setup.AdlPercussionMode = false; + m_setup.LogarithmicVolumes = false; + m_setup.SkipForward = 0; + m_setup.loopingIsEnabled = false; + m_setup.ScaleModulators = false; + m_setup.delay = 0.0; + m_setup.carry = 0.0; + m_setup.stored_samples = 0; + m_setup.backup_samples_size = 0; + + opl.NumCards = m_setup.NumCards; + opl.AdlBank = m_setup.AdlBank; + opl.NumFourOps = m_setup.NumFourOps; + opl.LogarithmicVolumes = m_setup.LogarithmicVolumes; + opl.HighTremoloMode = m_setup.HighTremoloMode; + opl.HighVibratoMode = m_setup.HighVibratoMode; + opl.AdlPercussionMode = m_setup.AdlPercussionMode; + opl.ScaleModulators = m_setup.ScaleModulators; } uint64_t MIDIplay::ReadVarLen(uint8_t **ptr) @@ -277,24 +511,33 @@ uint64_t MIDIplay::ReadVarLen(uint8_t **ptr) return result; } -uint64_t MIDIplay::ReadVarLen(size_t tk) +uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) { uint64_t result = 0; + ok = false; + for(;;) { - uint8_t byte = TrackData[tk][CurrentPosition.track[tk].ptr++]; + if(*ptr >= end) + return 2; + unsigned char byte = *((*ptr)++); result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) - break; + if(!(byte & 0x80)) break; } + + ok = true; return result; } double MIDIplay::Tick(double s, double granularity) { - //if(CurrentPositionNew.began) + s *= tempoMultiplier; + #ifdef ENABLE_BEGIN_SILENCE_SKIPPING + if(CurrentPositionNew.began) + #endif CurrentPositionNew.wait -= s; + CurrentPositionNew.absTimePosition += s; int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing while((CurrentPositionNew.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) @@ -316,42 +559,100 @@ double MIDIplay::Tick(double s, double granularity) UpdateVibrato(s); UpdateArpeggio(s); + if(CurrentPositionNew.wait < 0.0)//Avoid negative delay value! + return 0.0; + return CurrentPositionNew.wait; -// if(CurrentPosition.began) -// CurrentPosition.wait -= s; +} -// int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing -// while((CurrentPosition.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) -// { -// //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); -// if(!ProcessEvents()) -// break; -// if(CurrentPosition.wait <= 0.0) -// antiFreezeCounter--; -// } +void MIDIplay::seek(double seconds) +{ + if(seconds < 0.0) + return;//Seeking negative position is forbidden! :-P + const double granularity = m_setup.mindelay, + granualityHalf = granularity * 0.5, + s = seconds;//m_setup.delay < m_setup.maxdelay ? m_setup.delay : m_setup.maxdelay; + + bool loopFlagState = m_setup.loopingIsEnabled; + m_setup.loopingIsEnabled = false; + + /* + * Seeking search is similar to regular ticking, except of next things: + * - We don't processsing arpeggio and vibrato + * - To keep correctness of the state after seek, begin every search from begin + * - All sustaining notes must be killed + * - Ignore Note-On events + */ + + rewind(); + while((CurrentPositionNew.absTimePosition < seconds) && + (CurrentPositionNew.absTimePosition < fullSongTimeLength)) + { + CurrentPositionNew.wait -= s; + CurrentPositionNew.absTimePosition += s; + int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing + double dstWait = CurrentPositionNew.wait + granualityHalf; + while((CurrentPositionNew.wait <= granualityHalf)/*&& (antiFreezeCounter > 0)*/) + { + //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); + if(!ProcessEventsNew(true)) + break; + //Avoid freeze because of no waiting increasing in more than 10000 cycles + if(CurrentPositionNew.wait <= dstWait) + antiFreezeCounter--; + else + { + dstWait = CurrentPositionNew.wait + granualityHalf; + antiFreezeCounter = 10000; + } + } + if(antiFreezeCounter <= 0) + CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events + with zero delay are been detected */ + } + + if(CurrentPositionNew.wait < 0.0) + CurrentPositionNew.wait = 0.0; -// if(antiFreezeCounter <= 0) -// CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events -// with zero delay are been detected */ + m_setup.loopingIsEnabled = loopFlagState; + m_setup.delay = CurrentPositionNew.wait; + m_setup.carry = 0.0; +} + +double MIDIplay::tell() +{ + return CurrentPositionNew.absTimePosition; +} -// for(uint16_t c = 0; c < opl.NumChannels; ++c) -// ch[c].AddAge(static_cast<int64_t>(s * 1000.0)); +double MIDIplay::timeLength() +{ + return fullSongTimeLength; +} -// UpdateVibrato(s); -// UpdateArpeggio(s); +double MIDIplay::getLoopStart() +{ + return loopStartTime; +} -// return CurrentPosition.wait; +double MIDIplay::getLoopEnd() +{ + return loopEndTime; } void MIDIplay::rewind() { - CurrentPosition = trackBeginPosition; + Panic(); + KillSustainingNotes(-1, -1); + CurrentPositionNew = trackBeginPositionNew; trackStart = true; atEnd = false; loopStart = true; - loopStart_passed = false; invalidLoop = false; - loopStart_hit = false; +} + +void MIDIplay::setTempo(double tempo) +{ + tempoMultiplier = tempo; } void MIDIplay::realTime_ResetState() @@ -562,10 +863,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) for(unsigned ccount = 0; ccount < 2; ++ccount) { int32_t c = adlchannel[ccount]; - if(c < 0) continue; - ir.first->second.phys[ static_cast<uint16_t>(adlchannel[ccount]) ] = i[ccount]; } NoteUpdate(channel, ir.first, Upd_All | Upd_Patch); @@ -976,153 +1275,77 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, } -bool MIDIplay::ProcessEvents() +bool MIDIplay::ProcessEventsNew(bool isSeek) { - if(TrackData.size() == 0) + if(CurrentPositionNew.track.size() == 0) atEnd = true;//No MIDI track data to play if(atEnd) return false;//No more events in the queue loopEnd = false; - const size_t TrackCount = TrackData.size(); - const Position RowBeginPosition(CurrentPosition); - - for(size_t tk = 0; tk < TrackCount; ++tk) - { - if(CurrentPosition.track[tk].status >= 0 - && CurrentPosition.track[tk].delay <= 0) - { - // Handle event - HandleEvent(tk); - - // Read next event time (unless the track just ended) - if(CurrentPosition.track[tk].ptr >= TrackData[tk].size()) - CurrentPosition.track[tk].status = -1; - - if(CurrentPosition.track[tk].status >= 0) - CurrentPosition.track[tk].delay += ReadVarLen(tk); - } - } - - // Find shortest delay from all track - uint64_t shortest = 0; - bool shortest_no = true; - - for(size_t tk = 0; tk < TrackCount; ++tk) - if((CurrentPosition.track[tk].status >= 0) && (shortest_no || CurrentPosition.track[tk].delay < shortest)) - { - shortest = CurrentPosition.track[tk].delay; - shortest_no = false; - } - - //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest); - - // Schedule the next playevent to be processed after that delay - for(size_t tk = 0; tk < TrackCount; ++tk) - CurrentPosition.track[tk].delay -= shortest; - - fraction<uint64_t> t = shortest * Tempo; - - if(CurrentPosition.began) - CurrentPosition.wait += t.valuel(); - - //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); - /* - if(CurrentPosition.track[0].ptr > 8119) - loopEnd = true; - // ^HACK: CHRONO TRIGGER LOOP - */ - - if(loopStart_hit && (loopStart || loopEnd)) //Avoid invalid loops - { - invalidLoop = true; - loopStart = false; - loopEnd = false; - LoopBeginPosition = trackBeginPosition; - } - else - loopStart_hit = false; - - if(loopStart) - { - if(trackStart) - { - trackBeginPosition = RowBeginPosition; - trackStart = false; - atEnd = false; - } - LoopBeginPosition = RowBeginPosition; - loopStart = false; - loopStart_hit = true; - } - - if(shortest_no || loopEnd) - { - //Loop if song end or loop end point has reached - loopEnd = false; - shortest = 0; - if(opl._parent->loopingIsEnabled == 0) - { - atEnd = true; //Don't handle events anymore - CurrentPosition.wait += 1.0;//One second delay until stop playing - return true;//We have caugh end here! - } - CurrentPosition = LoopBeginPosition; - } - - return true;//Has events in queue -} - -bool MIDIplay::ProcessEventsNew() -{ - if(TrackData.size() == 0) - atEnd = true;//No MIDI track data to play - if(atEnd) - return false;//No more events in the queue - - loopEnd = false; - const size_t TrackCount = TrackData.size(); + const size_t TrackCount = CurrentPositionNew.track.size(); const PositionNew RowBeginPosition(CurrentPositionNew); + #ifdef DEBUG_TIME_CALCULATION + double maxTime = 0.0; + #endif + for(size_t tk = 0; tk < TrackCount; ++tk) { - if(CurrentPositionNew.track[tk].status >= 0 - && CurrentPositionNew.track[tk].delay <= 0) + PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; + if((track.status >= 0) && (track.delay <= 0)) { // Handle event - for(size_t i = 0; i < CurrentPositionNew.track[tk].pos->events.size(); i++) + for(size_t i = 0; i < track.pos->events.size(); i++) { - MidiEvent &evt = CurrentPositionNew.track[tk].pos->events[i]; - HandleEvent(tk, evt, CurrentPositionNew.track[tk].status); + MidiEvent &evt = track.pos->events[i]; + #ifdef ENABLE_BEGIN_SILENCE_SKIPPING + if(!CurrentPositionNew.began && (evt.type == MidiEvent::T_NOTEON)) + CurrentPositionNew.began = true; + #endif + if(isSeek && (evt.type == MidiEvent::T_NOTEON)) + continue; + HandleEvent(tk, evt, track.status); if(loopEnd) break;//Stop event handling on catching loopEnd event! } - std::fprintf(stdout, "Time: %f\r", CurrentPositionNew.track[tk].pos->time); - std::fflush(stdout); + #ifdef DEBUG_TIME_CALCULATION + if(maxTime < track.pos->time) + maxTime = track.pos->time; + #endif // Read next event time (unless the track just ended) - if(CurrentPositionNew.track[tk].pos->next == NULL/* >= TrackData[tk].size()*/) - CurrentPositionNew.track[tk].status = -1; + if(track.pos == trackDataNew[tk].end()) + track.status = -1; - if(CurrentPositionNew.track[tk].status >= 0) - CurrentPositionNew.track[tk].delay += CurrentPositionNew.track[tk].pos->delay; - - if(CurrentPositionNew.track[tk].status >= 0) - CurrentPositionNew.track[tk].pos = CurrentPositionNew.track[tk].pos->next; + if(track.status >= 0) + { + track.delay += track.pos->delay; + track.pos++; + } } } + #ifdef DEBUG_TIME_CALCULATION + std::fprintf(stdout, " \r"); + std::fprintf(stdout, "Time: %10f; Audio: %10f\r", maxTime, CurrentPositionNew.absTimePosition); + std::fflush(stdout); + #endif + // Find shortest delay from all track uint64_t shortest = 0; bool shortest_no = true; for(size_t tk = 0; tk < TrackCount; ++tk) - if((CurrentPositionNew.track[tk].status >= 0) && (shortest_no || CurrentPositionNew.track[tk].delay < shortest)) + { + PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; + if((track.status >= 0) && (shortest_no || track.delay < shortest)) { - shortest = CurrentPositionNew.track[tk].delay; + shortest = track.delay; shortest_no = false; } + } //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest); @@ -1132,37 +1355,17 @@ bool MIDIplay::ProcessEventsNew() fraction<uint64_t> t = shortest * Tempo; - //if(CurrentPositionNew.began) - CurrentPositionNew.wait += t.valuel(); - - //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); - /* - if(CurrentPosition.track[0].ptr > 8119) - loopEnd = true; - // ^HACK: CHRONO TRIGGER LOOP - */ + #ifdef ENABLE_BEGIN_SILENCE_SKIPPING + if(CurrentPositionNew.began) + #endif + CurrentPositionNew.wait += t.value(); - if(loopStart_hit && (loopStart || loopEnd)) //Avoid invalid loops - { - invalidLoop = true; - loopStart = false; - loopEnd = false; - LoopBeginPositionNew = trackBeginPositionNew; - } - else - loopStart_hit = false; + //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); if(loopStart) { - if(trackStart) - { - trackBeginPositionNew = RowBeginPosition; - trackStart = false; - atEnd = false; - } LoopBeginPositionNew = RowBeginPosition; loopStart = false; - loopStart_hit = true; } if(shortest_no || loopEnd) @@ -1170,10 +1373,10 @@ bool MIDIplay::ProcessEventsNew() //Loop if song end or loop end point has reached loopEnd = false; shortest = 0; - if(opl._parent->loopingIsEnabled == 0) + if(!m_setup.loopingIsEnabled) { atEnd = true; //Don't handle events anymore - CurrentPositionNew.wait += 1.0;//One second delay until stop playing + CurrentPositionNew.wait += postSongWaitDelay;//One second delay until stop playing return true;//We have caugh end here! } CurrentPositionNew = LoopBeginPositionNew; @@ -1182,24 +1385,45 @@ bool MIDIplay::ProcessEventsNew() return true;//Has events in queue } -MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, int &status) +MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &status) { uint8_t *&ptr = *pptr; MIDIplay::MidiEvent evt; + + if(ptr + 1 > end) + { + errorString += "parseEvent: Can't read event type byte - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + unsigned char byte = *(ptr++); + bool ok = false; - if(byte == 0xF7 || byte == 0xF0) // Ignore SysEx + if(byte == MidiEvent::T_SYSEX || byte == MidiEvent::T_SYSEX2)// Ignore SysEx { - uint64_t length = ReadVarLen(pptr); + uint64_t length = ReadVarLenEx(pptr, end, ok); + if(!ok || (ptr + length > end)) + { + errorString += "parseEvent: Can't read SysEx event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } ptr += (size_t)length; return evt; } - if(byte == 0xFF) + if(byte == MidiEvent::T_SPECIAL) { // Special event FF uint8_t evtype = *(ptr++); - uint64_t length = ReadVarLen(pptr); + uint64_t length = ReadVarLenEx(pptr, end, ok); + if(!ok || (ptr + length > end)) + { + errorString += "parseEvent: Can't read Special event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } std::string data(length ? (const char *)ptr : 0, (size_t)length); ptr += (size_t)length; @@ -1207,8 +1431,62 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, int &status) evt.subtype = evtype; evt.data.insert(evt.data.begin(), data.begin(), data.end()); + /* TODO: Store those meta-strings separately and give ability to read them + * by external functions (to display song title and copyright in the player) */ + if(evt.subtype == MidiEvent::ST_COPYRIGHT) + { + //TODO: Implement own field for this + //TODO: Implement API call to retreive this + //TODO: Implement a hook to catch this + std::string str((const char*)evt.data.data(), evt.data.size()); + std::fprintf(stdout, "Copyright: %s\n", str.c_str()); + std::fflush(stdout); + } + else + if(evt.subtype == MidiEvent::ST_SQTRKTITLE) + { + //TODO: Implement API call to retreive this + //TODO: Implement a hook to catch this + if(musTitle.empty()) + musTitle = std::string((const char*)evt.data.data(), evt.data.size()); + } + else + if(evt.subtype == MidiEvent::ST_INSTRTITLE) + { + //TODO: Implement a hook to catch this + std::string str((const char*)evt.data.data(), evt.data.size()); + std::fprintf(stdout, "Instrument: %s\n", str.c_str()); + std::fflush(stdout); + } + else + if(evt.subtype == MidiEvent::ST_MARKER) + { + //To lower + for(size_t i = 0; i < data.size(); i++) + { + if(data[i] <= 'Z' && data[i] >= 'A') + data[i] = data[i] - ('Z' - 'z'); + } + + if(data == "loopstart") + { + //Return a custom Loop Start event instead of Marker + evt.subtype = MidiEvent::ST_LOOPSTART; + evt.data.clear();//Data is not needed + return evt; + } + + if(data == "loopend") + { + //Return a custom Loop End event instead of Marker + evt.subtype = MidiEvent::ST_LOOPEND; + evt.data.clear();//Data is not needed + return evt; + } + } + if(evtype == MidiEvent::ST_ENDTRACK) - status = -1; + status = -1;//Finalize track return evt; } @@ -1220,313 +1498,151 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, int &status) ptr--; } - if(byte == 0xF3) + //Sys Com Song Select(Song #) [0-127] + if(byte == MidiEvent::T_SYSCOMSNGSEL) { - ptr += 1; - return evt; - } - - if(byte == 0xF2) - { - ptr += 2; + if(ptr + 1 > end) + { + errorString += "parseEvent: Can't read System Command Song Select event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + evt.type = byte; + evt.data.push_back(*(ptr++)); return evt; } - uint8_t MidCh = byte & 0x0F, EvType = byte >> 4; - status = byte; - evt.channel = MidCh; - evt.type = EvType; - - switch(EvType) - { - case 0x8: // Note off - case 0x9: // Note on - case 0xA: // Note touch - case 0xB: // Controller change - case 0xE: // Wheel/pitch bend + //Sys Com Song Position Pntr [LSB, MSB] + if(byte == MidiEvent::T_SYSCOMSPOSPTR) { + if(ptr + 2 > end) + { + errorString += "parseEvent: Can't read System Command Position Pointer event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; + } + evt.type = byte; evt.data.push_back(*(ptr++)); evt.data.push_back(*(ptr++)); return evt; } - case 0xC: // Patch change - case 0xD: // Channel after-touch - { - evt.data.push_back(*(ptr++)); - return evt; - } - } - - return evt; -} - -void MIDIplay::HandleEvent(size_t tk) -{ - unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++]; - - if(byte == 0xF7 || byte == 0xF0) // Ignore SysEx - { - uint64_t length = ReadVarLen(tk); - //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); - CurrentPosition.track[tk].ptr += (size_t)length; - //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); - return; - } + uint8_t midCh = byte & 0x0F, evType = (byte >> 4) & 0x0F; + status = byte; + evt.channel = midCh; + evt.type = evType; - if(byte == 0xFF) + switch(evType) { - // Special event FF - uint8_t evtype = TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint64_t length = ReadVarLen(tk); - std::string data(length ? (const char *) &TrackData[tk][CurrentPosition.track[tk].ptr] : 0, (size_t)length); - CurrentPosition.track[tk].ptr += (size_t)length; - - if(evtype == 0x2F)//End Of Track - { - CurrentPosition.track[tk].status = -1; - return; - } - - if(evtype == 0x51)//Tempo change + case MidiEvent::T_NOTEOFF://2 byte length + case MidiEvent::T_NOTEON: + case MidiEvent::T_NOTETOUCH: + case MidiEvent::T_CTRLCHANGE: + case MidiEvent::T_WHEEL: + if(ptr + 2 > end) { - Tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(data.data(), data.size())); - return; + errorString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; } - if(evtype == 6)//Meta event - { - //Turn on/off Loop handling when loop is disabled - if(opl._parent->loopingIsEnabled != 0) - { - /* Move this away from events handler */ - for(size_t i = 0; i < data.size(); i++) - { - if(data[i] <= 'Z' && data[i] >= 'A') - data[i] = data[i] - ('Z' - 'z'); - } - - if((data == "loopstart") && (!invalidLoop)) - { - loopStart = true; - loopStart_passed = true; - } - - if((data == "loopend") && (!invalidLoop)) - { - if((loopStart_passed) && (!loopStart)) - loopEnd = true; - else - invalidLoop = true; - } - } - } - - if(evtype == 9) - current_device[tk] = ChooseDevice(data); - - //if(evtype >= 1 && evtype <= 6) - // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); + evt.data.push_back(*(ptr++)); + evt.data.push_back(*(ptr++)); - if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib + //111'th loopStart controller (RPG Maker and others) + if((evType == MidiEvent::T_CTRLCHANGE) && (evt.data[0] == 111)) { - uint8_t i = static_cast<uint8_t>(data[0]), v = static_cast<uint8_t>(data[1]); - - if((i & 0xF0) == 0xC0) - v |= 0x30; - - //std::printf("OPL poke %02X, %02X\n", i, v); - //std::fflush(stdout); - opl.PokeN(0, i, v); + //Change event type to custom Loop Start event and clear data + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTART; + evt.data.clear(); } - return; - } - - // Any normal event (80..EF) - if(byte < 0x80) - { - byte = static_cast<uint8_t>(CurrentPosition.track[tk].status | 0x80); - CurrentPosition.track[tk].ptr--; - } - - if(byte == 0xF3) - { - CurrentPosition.track[tk].ptr += 1; - return; - } - - if(byte == 0xF2) - { - CurrentPosition.track[tk].ptr += 2; - return; - } - - /*UI.PrintLn("@%X Track %u: %02X %02X", - CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte, - TrackData[tk][CurrentPosition.track[tk].ptr]);*/ - uint8_t MidCh = byte & 0x0F, EvType = byte >> 4; - MidCh += (uint8_t)current_device[tk]; - CurrentPosition.track[tk].status = byte; - - switch(EvType) - { - case 0x8: // Note off - { - uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++]; - /*uint8_t vol=*/TrackData[tk][CurrentPosition.track[tk].ptr++]; - //if(MidCh != 9) note -= 12; // HACK - realTime_NoteOff(MidCh, note); - break; - } - case 0x9: // Note on - { - uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++]; - //if(MidCh != 9) note -= 12; // HACK - if(realTime_NoteOn(MidCh, note, vol)) - CurrentPosition.began = true; - break; - } - - case 0xA: // Note touch - { - uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++]; - realTime_NoteAfterTouch(MidCh, note, vol); - break; - } - - case 0xB: // Controller change - { - uint8_t ctrlno = TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t value = TrackData[tk][CurrentPosition.track[tk].ptr++]; - - if((opl._parent->loopingIsEnabled != 0) && (ctrlno == 111) && !invalidLoop) + return evt; + case MidiEvent::T_PATCHCHANGE://1 byte length + case MidiEvent::T_CHANAFTTOUCH: + if(ptr + 1 > end) { - loopStart = true; - loopStart_passed = true; - break; + errorString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n"; + evt.isValid = 0; + return evt; } - - realTime_Controller(MidCh, ctrlno, value); - break; - } - - case 0xC: // Patch change - realTime_PatchChange(MidCh, TrackData[tk][CurrentPosition.track[tk].ptr++]); - break; - - case 0xD: // Channel after-touch - { - // TODO: Verify, is this correct action? - uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++]; - realTime_ChannelAfterTouch(MidCh, vol); - break; + evt.data.push_back(*(ptr++)); + return evt; } - case 0xE: // Wheel/pitch bend - { - uint8_t a = TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t b = TrackData[tk][CurrentPosition.track[tk].ptr++]; - realTime_PitchBend(MidCh, b, a); - break; - } - } + return evt; } + void MIDIplay::HandleEvent(size_t tk, MIDIplay::MidiEvent &evt, int &status) { - if(evt.type == 0xF7 || evt.type == 0xF0) // Ignore SysEx + if(evt.type == MidiEvent::T_SYSEX || evt.type == MidiEvent::T_SYSEX2) // Ignore SysEx { //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); return; } - if(evt.type == 0xFF) + if(evt.type == MidiEvent::T_SPECIAL) { // Special event FF uint8_t evtype = evt.subtype; uint64_t length = (uint64_t)evt.data.size(); std::string data(length ? (const char *)evt.data.data() : 0, (size_t)length); - if(evtype == 0x2F)//End Of Track + if(evtype == MidiEvent::ST_ENDTRACK)//End Of Track { status = -1; return; } - if(evtype == 0x51)//Tempo change + if(evtype == MidiEvent::ST_TEMPOCHANGE)//Tempo change { Tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(evt.data.data(), evt.data.size())); return; } - if(evtype == 6)//Meta event + if(evtype == MidiEvent::ST_MARKER)//Meta event { - //Turn on/off Loop handling when loop is disabled - if(opl._parent->loopingIsEnabled != 0) - { - /* Move this away from events handler */ - for(size_t i = 0; i < data.size(); i++) - { - if(data[i] <= 'Z' && data[i] >= 'A') - data[i] = data[i] - ('Z' - 'z'); - } - - if((data == "loopstart") && (!invalidLoop)) - { - loopStart = true; - loopStart_passed = true; - } - - if((data == "loopend") && (!invalidLoop)) - { - if((loopStart_passed) && (!loopStart)) - loopEnd = true; - else - invalidLoop = true; - } - } + //Do nothing! :-P + return; } - if(evtype == 9) + if(evtype == MidiEvent::ST_DEVICESWITCH) + { current_device[tk] = ChooseDevice(data); + return; + } //if(evtype >= 1 && evtype <= 6) // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); - if(evtype == 0xE1) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib + //Turn on Loop handling when loop is enabled + if(m_setup.loopingIsEnabled && !invalidLoop) { - if(!invalidLoop) + if(evtype == MidiEvent::ST_LOOPSTART) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib { loopStart = true; - loopStart_passed = true; + return; } - } - if(evtype == 0xE2) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - if(!invalidLoop) + if(evtype == MidiEvent::ST_LOOPEND) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib { - if((loopStart_passed) && (!loopStart)) - loopEnd = true; - else - invalidLoop = true; + loopEnd = true; + return; } } - if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib + if(evtype == MidiEvent::ST_RAWOPL) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib { uint8_t i = static_cast<uint8_t>(data[0]), v = static_cast<uint8_t>(data[1]); - if((i & 0xF0) == 0xC0) v |= 0x30; - //std::printf("OPL poke %02X, %02X\n", i, v); //std::fflush(stdout); opl.PokeN(0, i, v); + return; } return; @@ -1539,86 +1655,68 @@ void MIDIplay::HandleEvent(size_t tk, MIDIplay::MidiEvent &evt, int &status) // CurrentPosition.track[tk].ptr--; // } - if(evt.type == 0xF3) - { - //CurrentPosition.track[tk].ptr += 1; - return; - } - - if(evt.type == 0xF2) - { - //CurrentPosition.track[tk].ptr += 2; + if(evt.type == MidiEvent::T_SYSCOMSNGSEL || + evt.type == MidiEvent::T_SYSCOMSPOSPTR) return; - } /*UI.PrintLn("@%X Track %u: %02X %02X", CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte, TrackData[tk][CurrentPosition.track[tk].ptr]);*/ - uint8_t MidCh = evt.channel;//byte & 0x0F, EvType = byte >> 4; - MidCh += (uint8_t)current_device[tk]; + uint8_t midCh = evt.channel;//byte & 0x0F, EvType = byte >> 4; + midCh += (uint8_t)current_device[tk]; status = evt.type; switch(evt.type) { - case 0x8: // Note off + case MidiEvent::T_NOTEOFF: // Note off { uint8_t note = evt.data[0]; - /*uint8_t vol=*/ //TrackData[tk][CurrentPosition.track[tk].ptr++]; - //if(MidCh != 9) note -= 12; // HACK - realTime_NoteOff(MidCh, note); + realTime_NoteOff(midCh, note); break; } - case 0x9: // Note on + + case MidiEvent::T_NOTEON: // Note on { - uint8_t note = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t vol = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - //if(MidCh != 9) note -= 12; // HACK - if(realTime_NoteOn(MidCh, note, vol)) - CurrentPosition.began = true; + uint8_t note = evt.data[0]; + uint8_t vol = evt.data[1]; + /*if(*/ realTime_NoteOn(midCh, note, vol); /*)*/ + //CurrentPosition.began = true; break; } - case 0xA: // Note touch + case MidiEvent::T_NOTETOUCH: // Note touch { - uint8_t note = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t vol = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - realTime_NoteAfterTouch(MidCh, note, vol); + uint8_t note = evt.data[0]; + uint8_t vol = evt.data[1]; + realTime_NoteAfterTouch(midCh, note, vol); break; } - case 0xB: // Controller change + case MidiEvent::T_CTRLCHANGE: // Controller change { - uint8_t ctrlno = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t value = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - - if((opl._parent->loopingIsEnabled != 0) && (ctrlno == 111) && !invalidLoop) - { - loopStart = true; - loopStart_passed = true; - break; - } - - realTime_Controller(MidCh, ctrlno, value); + uint8_t ctrlno = evt.data[0]; + uint8_t value = evt.data[1]; + realTime_Controller(midCh, ctrlno, value); break; } - case 0xC: // Patch change - realTime_PatchChange(MidCh, evt.data[0] /*TrackData[tk][CurrentPosition.track[tk].ptr++]*/); + case MidiEvent::T_PATCHCHANGE: // Patch change + realTime_PatchChange(midCh, evt.data[0]); break; - case 0xD: // Channel after-touch + case MidiEvent::T_CHANAFTTOUCH: // Channel after-touch { // TODO: Verify, is this correct action? - uint8_t vol = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - realTime_ChannelAfterTouch(MidCh, vol); + uint8_t vol = evt.data[0]; + realTime_ChannelAfterTouch(midCh, vol); break; } - case 0xE: // Wheel/pitch bend + case MidiEvent::T_WHEEL: // Wheel/pitch bend { - uint8_t a = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - uint8_t b = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++]; - realTime_PitchBend(MidCh, b, a); + uint8_t a = evt.data[0]; + uint8_t b = evt.data[1]; + realTime_PitchBend(midCh, b, a); break; } } @@ -1688,11 +1786,8 @@ long MIDIplay::CalculateAdlChannelGoodness(unsigned c, uint16_t ins, uint16_t) c ++m) { if(m->second.sustained) continue; - if(m->second.vibdelay >= 200) continue; - if(m->second.ins != j->second.ins) continue; - n_evacuation_stations += 1; } } @@ -1813,6 +1908,15 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, AdlChannel::users_t::iterator static_cast<int32_t>(from_channel)); } +void MIDIplay::Panic() +{ + for(uint8_t chan = 0; chan < Ch.size(); chan++) + { + for(uint8_t note = 0; note < 128; note++) + realTime_NoteOff(chan, note); + } +} + void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) { uint32_t first = 0, last = opl.NumChannels; diff --git a/src/adlmidi_opl3.cpp b/src/adlmidi_opl3.cpp index 316c8e1..4878233 100644 --- a/src/adlmidi_opl3.cpp +++ b/src/adlmidi_opl3.cpp @@ -406,7 +406,7 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) } } -void OPL3::Reset() +void OPL3::Reset(unsigned long PCM_RATE) { #ifdef ADLMIDI_USE_DOSBOX_OPL DBOPL::Handler emptyChip; //Constructors inside are will initialize necessary fields @@ -445,9 +445,9 @@ void OPL3::Reset() for(unsigned card = 0; card < NumCards; ++card) { #ifdef ADLMIDI_USE_DOSBOX_OPL - cards[card].Init(_parent->PCM_RATE); + cards[card].Init(PCM_RATE); #else - OPL3_Reset(&cards[card], static_cast<Bit32u>(_parent->PCM_RATE)); + OPL3_Reset(&cards[card], static_cast<Bit32u>(PCM_RATE)); #endif for(unsigned a = 0; a < 18; ++a) Poke(card, 0xB0 + Channels[a], 0x00); diff --git a/src/adlmidi_private.cpp b/src/adlmidi_private.cpp index c888b18..04bb481 100644 --- a/src/adlmidi_private.cpp +++ b/src/adlmidi_private.cpp @@ -47,7 +47,7 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) //For embedded bank for(unsigned a = 0; a < 256; ++a) { - unsigned insno = banks[device->AdlBank][a]; + unsigned insno = banks[play->m_setup.AdlBank][a]; if(insno == 198) continue; ++n_total[a / 128]; @@ -57,13 +57,13 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) } } - device->NumFourOps = - (n_fourop[0] >= n_total[0] * 7 / 8) ? device->NumCards * 6 - : (n_fourop[0] < n_total[0] * 1 / 8) ? 0 - : (device->NumCards == 1 ? 1 : device->NumCards * 4); - play->opl.NumFourOps = device->NumFourOps; + play->m_setup.NumFourOps = + (n_fourop[0] >= n_total[0] * 7 / 8) ? play->m_setup.NumCards * 6 + : (n_fourop[0] < n_total[0] * 1 / 8) ? 0 + : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); + play->opl.NumFourOps = play->m_setup.NumFourOps; - if(n_fourop[0] >= n_total[0] * 15 / 16 && device->NumFourOps == 0) + if(n_fourop[0] >= n_total[0] * 15 / 16 && play->m_setup.NumFourOps == 0) { ADLMIDI_ErrorString = "ERROR: You have selected a bank that consists almost exclusively of four-op patches.\n" " The results (silence + much cpu load) would be probably\n" diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index d3709a2..622e9f2 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -50,6 +50,7 @@ #endif #include <vector> +#include <list> #include <string> #include <sstream> #include <map> @@ -155,7 +156,7 @@ public: VOLUME_CMF, VOLUME_DMX, VOLUME_APOGEE, - VOLUME_9X, + VOLUME_9X } m_volumeScale; OPL3(); @@ -180,31 +181,37 @@ public: void Silence(); void updateFlags(); void ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel); - void Reset(); + void Reset(unsigned long PCM_RATE); }; +/* + * TODO: Put usage of those hooks to the places where originally was UI usage. + * Also, provide external API to set those hooks + */ -class MIDIplay +/** + * @brief Hooks of the internal events + */ +struct MIDIEventHooks { - // Information about each track - struct Position - { - bool began; - char padding[7]; - double wait; - struct TrackInfo - { - size_t ptr; - uint64_t delay; - int status; - char padding2[4]; - TrackInfo(): ptr(0), delay(0), status(0) {} - }; - std::vector<TrackInfo> track; - - Position(): began(false), wait(0.0l), track() { } - } CurrentPosition, LoopBeginPosition, trackBeginPosition; + //! Raw MIDI event hook + typedef void (*RawEventHook)(void *userdata, uint8_t type, uint8_t subtype, uint8_t channel, uint8_t *data, size_t len); + RawEventHook onEvent; + void *onEvent_userData; + + //! Note on/off hooks + typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + NoteHook onNote; + void *onNote_userData; + + //! Library internal debug messages + typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...); + DebugMessageHook onDebugMessage; + void *onDebugMessage_userData; +}; +class MIDIplay +{ std::map<std::string, uint64_t> devices; std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> current_device; @@ -293,9 +300,10 @@ class MIDIplay AdlChannel(): users(), koff_time_until_neglible(0) { } void AddAge(int64_t ms); }; -public: + + //Padding to fix CLanc code model's warning char ____padding[7]; -private: + std::vector<AdlChannel> ch; std::vector<std::vector<uint8_t> > TrackData; @@ -310,29 +318,44 @@ private: enum Types { T_UNKNOWN = 0x00, - T_NOTEOFF = 0x08, - T_NOTEON = 0x09, - T_NOTETOUCH = 0x0A, - T_CTRLCHANGE = 0x0B, - T_PATCHCHANGE = 0x0C, - T_CHANAFTTOUCH = 0x0D, - T_WHEEL = 0x0E, - - T_SYSEX = 0xF7, - T_SYSEX2 = 0xF0, - T_SPECIAL = 0xFF, + T_NOTEOFF = 0x08,//size == 2 + T_NOTEON = 0x09,//size == 2 + T_NOTETOUCH = 0x0A,//size == 2 + T_CTRLCHANGE = 0x0B,//size == 2 + T_PATCHCHANGE = 0x0C,//size == 1 + T_CHANAFTTOUCH = 0x0D,//size == 1 + T_WHEEL = 0x0E,//size == 2 + + T_SYSEX = 0xF0,//size == len + T_SYSCOMSPOSPTR = 0xF2,//size == 2 + T_SYSCOMSNGSEL = 0xF3,//size == 1 + T_SYSEX2 = 0xF7,//size == len + T_SPECIAL = 0xFF }; enum SubTypes { - ST_UNKNOWN = 0x00, - ST_ENDTRACK = 0x2F, - ST_TEMPOCHANGE = 0x51, - ST_META = 0x06, + ST_SEQNUMBER = 0x00,//size == 2 + ST_TEXT = 0x01,//size == len + ST_COPYRIGHT = 0x02,//size == len + ST_SQTRKTITLE = 0x03,//size == len + ST_INSTRTITLE = 0x04,//size == len + ST_LYRICS = 0x05,//size == len + ST_MARKER = 0x06,//size == len + ST_CUEPOINT = 0x07,//size == len + ST_DEVICESWITCH = 0x09,//size == len <CUSTOM> + ST_MIDICHPREFIX = 0x20,//size == 1 + + ST_ENDTRACK = 0x2F,//size == 0 + ST_TEMPOCHANGE = 0x51,//size == 3 + ST_SMPTEOFFSET = 0x54,//size == 5 + ST_TIMESIGNATURE= 0x55,//size == 4 + ST_KEYSIGNATURE = 0x59,//size == 2 + ST_SEQUENCERSPEC= 0x7F,//size == len /* Non-standard, internal ADLMIDI usage only */ - ST_LOOPSTART = 0xE1, - ST_LOOPEND = 0xE2, - ST_RAWOPL = 0xE3, + ST_LOOPSTART = 0xE1,//size == 0 <CUSTOM> + ST_LOOPEND = 0xE2,//size == 0 <CUSTOM> + ST_RAWOPL = 0xE3//size == 0 <CUSTOM> }; //! Main type of event uint8_t type; @@ -340,9 +363,12 @@ private: uint8_t subtype; //! Targeted MIDI channel uint8_t channel; - + //! Is valid event + uint8_t isValid; //! Reserved 5 bytes padding - uint8_t __padding[5]; + uint8_t __padding[4]; + //! Absolute tick position (Used for the tempo calculation only) + uint64_t absPosition; //! Raw data of this event std::vector<uint8_t> data; }; @@ -353,15 +379,17 @@ private: * Created with purpose to sort events by type in the same position * (for example, to keep controllers always first than note on events or lower than note-off events) */ - class MidiTrackPos + class MidiTrackRow { public: - MidiTrackPos(); + MidiTrackRow(); void reset(); //! Absolute time position in seconds double time; //! Delay to next event in ticks uint64_t delay; + //! Absolute position in ticks + uint64_t absPos; //! Delay to next event in seconds double timeDelay; std::vector<MidiEvent> events; @@ -369,65 +397,142 @@ private: * @brief Sort events in this position */ void sortEvents(); - //! Next event that follows current - MidiTrackPos *next; }; + /** + * @brief Tempo change point entry. Used in the MIDI data building function only. + */ + struct TempoChangePoint + { + uint64_t absPos; + fraction<uint64_t> tempo; + }; + //P.S. I declared it here instead of local in-function because C++99 can't process templates with locally-declared structures + + typedef std::list<MidiTrackRow> MidiTrackQueue; + // Information about each track struct PositionNew { bool began; char padding[7]; double wait; + double absTimePosition; struct TrackInfo { size_t ptr; uint64_t delay; int status; char padding2[4]; - MidiTrackPos *pos; - TrackInfo(): ptr(0), delay(0), status(0), pos(NULL) {} + MidiTrackQueue::iterator pos; + TrackInfo(): ptr(0), delay(0), status(0) {} }; std::vector<TrackInfo> track; - PositionNew(): began(false), wait(0.0), track() + PositionNew(): began(false), wait(0.0), absTimePosition(0.0), track() {} } CurrentPositionNew, LoopBeginPositionNew, trackBeginPositionNew; - std::vector<std::vector<MidiTrackPos> > trackDataNew; - std::vector<int> trackDataNewStatus; - void buildTrackData(); - MidiEvent parseEvent(uint8_t **ptr, int &status); + //! Full song length in seconds + double fullSongTimeLength; + //! Delay after song playd before rejecting the output stream requests + double postSongWaitDelay; + + //! Loop start time + double loopStartTime; + //! Loop end time + double loopEndTime; + //! Local error string + std::string errorString; + + //! Pre-processed track data storage + std::vector<MidiTrackQueue > trackDataNew; + + /** + * @brief Build MIDI track data from the raw track data storage + * @return true if everything successfully processed, or false on any error + */ + bool buildTrackData(); + + /** + * @brief Parse one event from raw MIDI track stream + * @param [_inout] ptr pointer to pointer to current position on the raw data track + * @param [_in] end address to end of raw track data, needed to validate position and size + * @param [_inout] status status of the track processing + * @return Parsed MIDI event entry + */ + MidiEvent parseEvent(uint8_t **ptr, uint8_t *end, int &status); public: MIDIplay(); ~MIDIplay() {} - ADL_MIDIPlayer *config; std::string musTitle; fraction<uint64_t> InvDeltaTicks, Tempo; + //! Tempo multiplier + double tempoMultiplier; bool trackStart, atEnd, loopStart, loopEnd, - loopStart_passed /*Tells that "loopStart" already passed*/, - invalidLoop /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/, - loopStart_hit /*loopStart entry was hited in previous tick*/; + invalidLoop; /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/ char ____padding2[2]; OPL3 opl; - //! Generated per-tick audio output buffer + int16_t outBuf[1024]; -public: + + struct Setup + { + unsigned int AdlBank; + unsigned int NumFourOps; + unsigned int NumCards; + bool HighTremoloMode; + bool HighVibratoMode; + bool AdlPercussionMode; + bool LogarithmicVolumes; + int VolumeModel; + unsigned int SkipForward; + bool loopingIsEnabled; + bool ScaleModulators; + + double delay; + double carry; + + /* The lag between visual content and audio content equals */ + /* the sum of these two buffers. */ + double mindelay; + double maxdelay; + + /* For internal usage */ + ssize_t stored_samples; /* num of collected samples */ + short backup_samples[1024]; /* Backup sample storage. */ + ssize_t backup_samples_size; /* Backup sample storage. */ + /* For internal usage */ + + unsigned long PCM_RATE; + } m_setup; + static uint64_t ReadBEint(const void *buffer, size_t nbytes); static uint64_t ReadLEint(const void *buffer, size_t nbytes); + /** + * @brief Standard MIDI Variable-Length numeric value parser without of validation + * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value + * @return Unsigned integer that conains parsed variable-length value + */ uint64_t ReadVarLen(uint8_t **ptr); - uint64_t ReadVarLen(size_t tk); - uint64_t ReadVarLenEx(size_t tk, bool &ok); + /** + * @brief Secure Standard MIDI Variable-Length numeric value parser with anti-out-of-range protection + * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value, will be iterated forward + * @param [_in end Pointer to end of memory block where variable-length value is stored (after end of track) + * @param [_out] ok Reference to boolean which takes result of variable-length value parsing + * @return Unsigned integer that conains parsed variable-length value + */ + uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok); /* * A little class gives able to read filedata from disk and also from a memory segment - */ + */ class fileReader { public: @@ -565,7 +670,10 @@ public: bool eof() { - return mp_tell >= mp_size; + if(fp) + return std::feof(fp); + else + return mp_tell >= mp_size; } std::string _fileName; std::FILE *fp; @@ -582,15 +690,55 @@ public: bool LoadMIDI(void *data, unsigned long size); bool LoadMIDI(fileReader &fr); - /* Periodic tick handler. - * Input: s = seconds since last call - * Input: granularity = don't expect intervals smaller than this, in seconds - * Output: desired number of seconds until next call + /** + * @brief Periodic tick handler. + * @param s seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call */ double Tick(double s, double granularity); + /** + * @brief Change current position to specified time position in seconds + * @param seconds Absolute time position in seconds + */ + void seek(double seconds); + + /** + * @brief Gives current time position in seconds + * @return Current time position in seconds + */ + double tell(); + + /** + * @brief Gives time length of current song in seconds + * @return Time length of current song in seconds + */ + double timeLength(); + + /** + * @brief Gives loop start time position in seconds + * @return Loop start time position in seconds or -1 if song has no loop points + */ + double getLoopStart(); + + /** + * @brief Gives loop end time position in seconds + * @return Loop end time position in seconds or -1 if song has no loop points + */ + double getLoopEnd(); + + /** + * @brief Return to begin of current song + */ void rewind(); + /** + * @brief Set tempo multiplier + * @param tempo Tempo multiplier: 1.0 - original tempo. >1 - faster, <1 - slower + */ + void setTempo(double tempo); + /* RealTime event triggers */ void realTime_ResetState(); @@ -627,7 +775,7 @@ private: unsigned props_mask, int32_t select_adlchn = -1); bool ProcessEvents(); - bool ProcessEventsNew(); + bool ProcessEventsNew(bool isSeek = false); void HandleEvent(size_t tk); void HandleEvent(size_t tk, MidiEvent &evt, int &status); @@ -643,6 +791,7 @@ private: size_t from_channel, AdlChannel::users_t::iterator j, MIDIchannel::activenoteiterator i); + void Panic(); void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); void SetRPN(unsigned MidCh, unsigned value, bool MSB); //void UpdatePortamento(unsigned MidCh) diff --git a/src/dbopl.cpp b/src/dbopl.cpp index cd857f4..fb28e20 100644 --- a/src/dbopl.cpp +++ b/src/dbopl.cpp @@ -1997,7 +1997,7 @@ namespace DBOPL Bit32s out32[1024]; if(GCC_UNLIKELY(*samples > 512)) *samples = 512; - memset(out32, 0, sizeof(Bit32s) * size_t(*samples) * 2); + memset(out32, 0, sizeof(Bit32s) * 1024); if(!chip.opl3Active) chip.GenerateBlock2(static_cast<Bitu>(*samples), out32); else @@ -2011,7 +2011,6 @@ namespace DBOPL { if(GCC_UNLIKELY(*samples > 512)) *samples = 512; - if(!chip.opl3Active) chip.GenerateBlock2_Mix(static_cast<Bitu>(*samples), out); else @@ -2023,11 +2022,11 @@ namespace DBOPL Bit32s out32[1024]; if(GCC_UNLIKELY(*samples > 512)) *samples = 512; - memset(out32, 0, sizeof(Bit32s) * size_t(*samples) * 2); + memset(out32, 0, sizeof(Bit32s) * 1024); if(!chip.opl3Active) - chip.GenerateBlock2_Mix(static_cast<Bitu>(*samples), out32); + chip.GenerateBlock2(static_cast<Bitu>(*samples), out32); else - chip.GenerateBlock3_Mix(static_cast<Bitu>(*samples), out32); + chip.GenerateBlock3(static_cast<Bitu>(*samples), out32); ssize_t sz = *samples * 2; for(ssize_t i = 0; i < sz; i++) out[i] += static_cast<Bit16s>(DBOPL_CLAMP(out32[i], static_cast<ssize_t>(INT16_MIN), static_cast<ssize_t>(INT16_MAX))); diff --git a/src/dbopl.h b/src/dbopl.h index d6091a2..826cb44 100644 --- a/src/dbopl.h +++ b/src/dbopl.h @@ -84,14 +84,14 @@ namespace DBOPL sm3AMAM, sm6Start, sm2Percussion, - sm3Percussion, + sm3Percussion } SynthMode; //Shifts for the values contained in chandata variable enum { SHIFT_KSLBASE = 16, - SHIFT_KEYCODE = 24, + SHIFT_KEYCODE = 24 }; struct Operator @@ -103,7 +103,7 @@ namespace DBOPL MASK_KSR = 0x10, MASK_SUSTAIN = 0x20, MASK_VIBRATO = 0x40, - MASK_TREMOLO = 0x80, + MASK_TREMOLO = 0x80 }; typedef enum @@ -112,7 +112,7 @@ namespace DBOPL RELEASE, SUSTAIN, DECAY, - ATTACK, + ATTACK } State; VolumeHandler volHandler; diff --git a/utils/midiplay/adlmidiplay.cpp b/utils/midiplay/adlmidiplay.cpp index 9fb9033..a095390 100644 --- a/utils/midiplay/adlmidiplay.cpp +++ b/utils/midiplay/adlmidiplay.cpp @@ -3,6 +3,7 @@ #include <string> #include <cstdio> #include <cctype> +#include <cmath> #include <cstdlib> #include <cstring> #include <deque> @@ -84,6 +85,11 @@ static void sighandler(int dum) int main(int argc, char **argv) { + std::fprintf(stdout, "==========================================\n" + " libADLMIDI demo utility\n" + "==========================================\n\n"); + std::fflush(stdout); + if(argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h") { std::printf( @@ -130,6 +136,8 @@ int main(int argc, char **argv) std::printf(" This build of libADLMIDI has no embedded banks!\n\n"); } + std::fflush(stdout); + return 0; } @@ -161,8 +169,7 @@ int main(int argc, char **argv) } bool recordWave = false; - - adl_setLoopEnabled(myDevice, 1); + int loopEnabled = 1; while(argc > 2) { @@ -173,14 +180,11 @@ int main(int argc, char **argv) else if(!std::strcmp("-v", argv[2])) adl_setHVibrato(myDevice, 1); else if(!std::strcmp("-w", argv[2])) - { recordWave = true; - adl_setLoopEnabled(myDevice, 0);//Disable loop while record WAV - } else if(!std::strcmp("-t", argv[2])) adl_setHTremolo(myDevice, 1); else if(!std::strcmp("-nl", argv[2])) - adl_setLoopEnabled(myDevice, 0); + loopEnabled = 0; else if(!std::strcmp("-s", argv[2])) adl_setScaleModulators(myDevice, 1); else break; @@ -190,6 +194,9 @@ int main(int argc, char **argv) argc -= (had_option ? 2 : 1); } + //Turn loop on/off (for WAV recording loop must be disabled!) + adl_setLoopEnabled(myDevice, recordWave ? 0 : loopEnabled); + if(!recordWave) { // Set up SDL @@ -201,7 +208,8 @@ int main(int argc, char **argv) } if(spec.samples != obtained.samples) { - std::fprintf(stderr, "Wanted (samples=%u,rate=%u,channels=%u); obtained (samples=%u,rate=%u,channels=%u)\n", + std::fprintf(stderr, " - Audio wanted (samples=%u,rate=%u,channels=%u);\n" + " - Audio obtained (samples=%u,rate=%u,channels=%u)\n", spec.samples, spec.freq, spec.channels, obtained.samples, obtained.freq, obtained.channels); std::fflush(stderr); @@ -218,10 +226,12 @@ int main(int argc, char **argv) printError(adl_errorString()); return 1; } + std::fprintf(stdout, " - Use embedded bank #%d [%s]\n", bankno, adl_getBankNames()[bankno]); + std::fflush(stdout); } else { - std::fprintf(stdout, "Loading custom bank file %s...", argv[2]); + std::fprintf(stdout, " - Use custom bank [%s]...", argv[2]); std::fflush(stdout); if(adl_openBankFile(myDevice, argv[2]) != 0) { @@ -242,7 +252,7 @@ int main(int argc, char **argv) printError(adl_errorString()); return 1; } - std::fprintf(stdout, "Number of cards %s\n", argv[3]); + std::fprintf(stdout, " - Number of cards %s\n", argv[3]); } else { @@ -261,7 +271,7 @@ int main(int argc, char **argv) printError(adl_errorString()); return 1; } - std::fprintf(stdout, "Number of four-ops %s\n", argv[4]); + std::fprintf(stdout, " - Number of four-ops %s\n", argv[4]); } if(adl_openFile(myDevice, argv[1]) != 0) @@ -276,10 +286,26 @@ int main(int argc, char **argv) signal(SIGHUP, sighandler); #endif + double total = adl_totalTimeLength(myDevice); + double loopStart = adl_loopStartTime(myDevice); + double loopEnd = adl_loopEndTime(myDevice); + if(!recordWave) { + std::fprintf(stdout, " - Loop is turned %s\n", loopEnabled ? "ON" : "OFF"); + if(loopStart >= 0.0 && loopEnd >= 0.0) + std::fprintf(stdout, " - Has loop points: %10f ... %10f\n", loopStart, loopEnd); + std::fprintf(stdout, "\n==========================================\n"); + std::fflush(stdout); + SDL_PauseAudio(0); + #ifdef DEBUG_SEEKING_TEST + int delayBeforeSeek = 50; + std::fprintf(stdout, "DEBUG: === Random position set test is active! ===\n"); + std::fflush(stdout); + #endif + while(!stop) { short buff[4096]; @@ -287,6 +313,10 @@ int main(int argc, char **argv) if(got <= 0) break; + std::fprintf(stdout, " \r"); + std::fprintf(stdout, "Time position: %10f / %10f\r", adl_positionTell(myDevice), total); + std::fflush(stdout); + AudioBuffer_lock.Lock(); size_t pos = AudioBuffer.size(); AudioBuffer.resize(pos + got); @@ -299,14 +329,24 @@ int main(int argc, char **argv) { SDL_Delay(1); } - } + #ifdef DEBUG_SEEKING_TEST + if(delayBeforeSeek-- <= 0) + { + delayBeforeSeek = rand() % 50; + double seekTo = double((rand() % int(adl_totalTimeLength(myDevice)) - delayBeforeSeek - 1 )); + adl_positionSeek(myDevice, seekTo); + } + #endif + } + std::fprintf(stdout, " \n\n"); SDL_CloseAudio(); } else { std::string wave_out = std::string(argv[1]) + ".wav"; - std::fprintf(stdout, "Recording WAV file %s...\n", wave_out.c_str()); + std::fprintf(stdout, " - Recording WAV file %s...\n", wave_out.c_str()); + std::fprintf(stdout, "\n==========================================\n"); std::fflush(stdout); if(wave_open(spec.freq, wave_out.c_str()) == 0) @@ -319,10 +359,19 @@ int main(int argc, char **argv) if(got <= 0) break; wave_write(buff, (long)got); + + double complete = std::floor(100.0 * adl_positionTell(myDevice) / total); + std::fprintf(stdout, " \r"); + std::fprintf(stdout, "Recording WAV... [%d%% completed]\r", (int)complete); + std::fflush(stdout); } wave_close(); + std::fprintf(stdout, " \n\n"); - std::fprintf(stdout, "Completed!\n"); + if(stop) + std::fprintf(stdout, "Interrupted! Recorded WAV is incomplete, but playable!\n"); + else + std::fprintf(stdout, "Completed!\n"); std::fflush(stdout); } else |