aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adlmidi.cpp')
-rw-r--r--src/adlmidi.cpp2484
1 files changed, 280 insertions, 2204 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index 70532b8..d54ce9a 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -2,7 +2,7 @@
* libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation
*
* Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
- * ADLMIDI Library API: Copyright (c) 2016 Vitaly Novichkov <admin@wohlnet.ru>
+ * ADLMIDI Library API: Copyright (c) 2017 Vitaly Novichkov <admin@wohlnet.ru>
*
* Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
* http://iki.fi/bisqwit/source/adlmidi.html
@@ -21,2075 +21,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Setup compiler defines useful for exporting required public API symbols in gme.cpp
-#ifndef ADLMIDI_EXPORT
- #if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL)
- #define ADLMIDI_EXPORT __declspec(dllexport)
- #elif defined (LIBADLMIDI_VISIBILITY)
- #define ADLMIDI_EXPORT __attribute__((visibility ("default")))
- #else
- #define ADLMIDI_EXPORT
- #endif
-#endif
-
-#ifdef _WIN32
-#undef NO_OLDNAMES
-#endif
-#include <vector>
-#include <string>
-#include <sstream>
-#include <map>
-#include <set>
-#include <cstdlib>
-#include <cstring>
-#include <cmath>
-#include <cstdio>
-#include <vector> // vector
-#include <deque> // deque
-#include <cmath> // exp, log, ceil
-#include <stdio.h>
-
-#include <deque>
-#include <algorithm>
-
-#include "fraction.h"
-#include "dbopl.h"
-
-#include "adldata.hh"
-#include "adlmidi.h"
+#include "adlmidi_private.hpp"
#ifdef ADLMIDI_buildAsApp
#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
#endif
-class MIDIplay;
-
static const unsigned MaxCards = 100;
-static std::string ADLMIDI_ErrorString;
-
-static const unsigned short Operators[23*2] =
- {// Channels 0-2
- 0x000,0x003,0x001,0x004,0x002,0x005, // operators 0, 3, 1, 4, 2, 5
- // Channels 3-5
- 0x008,0x00B,0x009,0x00C,0x00A,0x00D, // operators 6, 9, 7,10, 8,11
- // Channels 6-8
- 0x010,0x013,0x011,0x014,0x012,0x015, // operators 12,15, 13,16, 14,17
- // Same for second card
- 0x100,0x103,0x101,0x104,0x102,0x105, // operators 18,21, 19,22, 20,23
- 0x108,0x10B,0x109,0x10C,0x10A,0x10D, // operators 24,27, 25,28, 26,29
- 0x110,0x113,0x111,0x114,0x112,0x115, // operators 30,33, 31,34, 32,35
- // Channel 18
- 0x010,0x013, // operators 12,15
- // Channel 19
- 0x014,0xFFF, // operator 16
- // Channel 19
- 0x012,0xFFF, // operator 14
- // Channel 19
- 0x015,0xFFF, // operator 17
- // Channel 19
- 0x011,0xFFF }; // operator 13
-
-static const unsigned short Channels[23] =
- {0x000,0x001,0x002, 0x003,0x004,0x005, 0x006,0x007,0x008, // 0..8
- 0x100,0x101,0x102, 0x103,0x104,0x105, 0x106,0x107,0x108, // 9..17 (secondary set)
- 0x006,0x007,0x008, 0xFFF,0xFFF }; // <- hw percussions, 0xFFF = no support for pitch/pan
-
-/*
- In OPL3 mode:
- 0 1 2 6 7 8 9 10 11 16 17 18
- op0 op1 op2 op12 op13 op14 op18 op19 op20 op30 op31 op32
- op3 op4 op5 op15 op16 op17 op21 op22 op23 op33 op34 op35
- 3 4 5 13 14 15
- op6 op7 op8 op24 op25 op26
- op9 op10 op11 op27 op28 op29
- Ports:
- +0 +1 +2 +10 +11 +12 +100 +101 +102 +110 +111 +112
- +3 +4 +5 +13 +14 +15 +103 +104 +105 +113 +114 +115
- +8 +9 +A +108 +109 +10A
- +B +C +D +10B +10C +10D
-
- Percussion:
- bassdrum = op(0): 0xBD bit 0x10, operators 12 (0x10) and 15 (0x13) / channels 6, 6b
- snare = op(3): 0xBD bit 0x08, operators 16 (0x14) / channels 7b
- tomtom = op(4): 0xBD bit 0x04, operators 14 (0x12) / channels 8
- cym = op(5): 0xBD bit 0x02, operators 17 (0x17) / channels 8b
- hihat = op(2): 0xBD bit 0x01, operators 13 (0x11) / channels 7
-
-
- In OPTi mode ("extended FM" in 82C924, 82C925, 82C931 chips):
- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
- op0 op4 op6 op10 op12 op16 op18 op22 op24 op28 op30 op34 op36 op38 op40 op42 op44 op46
- op1 op5 op7 op11 op13 op17 op19 op23 op25 op29 op31 op35 op37 op39 op41 op43 op45 op47
- op2 op8 op14 op20 op26 op32
- op3 op9 op15 op21 op27 op33 for a total of 6 quad + 12 dual
- Ports: ???
-*/
-
-struct OPL3
-{
- friend class MIDIplay;
- unsigned NumChannels;
- ADL_MIDIPlayer* _parent;
-
- std::vector<DBOPL::Handler> cards;
-private:
- std::vector<unsigned short> ins; // index to adl[], cached, needed by Touch()
- std::vector<unsigned char> pit; // value poked to B0, cached, needed by NoteOff)(
- std::vector<unsigned char> regBD;
-
- std::vector<adlinsdata> dynamic_metainstruments; // Replaces adlins[] when CMF file
- std::vector<adldata> dynamic_instruments; // Replaces adl[] when CMF file
- const unsigned DynamicInstrumentTag = 0x8000u, DynamicMetaInstrumentTag = 0x4000000u;
- const adlinsdata& GetAdlMetaIns(unsigned n)
- {
- return (n & DynamicMetaInstrumentTag) ?
- dynamic_metainstruments[n & ~DynamicMetaInstrumentTag]
- : adlins[n];
- }
- unsigned GetAdlMetaNumber(unsigned midiins)
- {
- return (AdlBank == ~0u) ?
- (midiins | DynamicMetaInstrumentTag)
- : banks[AdlBank][midiins];
- }
- const adldata& GetAdlIns(unsigned short insno)
- {
- return (insno & DynamicInstrumentTag)
- ? dynamic_instruments[insno & ~DynamicInstrumentTag]
- : adl[insno];
- }
-
-public:
- unsigned int NumCards;
- unsigned int AdlBank;
- unsigned int NumFourOps;
- bool HighTremoloMode;
- bool HighVibratoMode;
- bool AdlPercussionMode;
- bool ScaleModulators;
- bool LogarithmicVolumes;
- OPL3() :
- NumCards(1),
- AdlBank(0),
- NumFourOps(0),
- HighTremoloMode(false),
- HighVibratoMode(false),
- AdlPercussionMode(false),
- LogarithmicVolumes(false)
- {}
- std::vector<char> four_op_category; // 1 = quad-master, 2 = quad-slave, 0 = regular
- // 3 = percussion BassDrum
- // 4 = percussion Snare
- // 5 = percussion Tom
- // 6 = percussion Crash cymbal
- // 7 = percussion Hihat
- // 8 = percussion slave
-
- void Poke(unsigned card, unsigned index, unsigned value)
- {
- cards[card].WriteReg(index, value);
- }
- void NoteOff(unsigned c)
- {
- unsigned card = c/23, cc = c%23;
- if(cc >= 18)
- {
- regBD[card] &= ~(0x10 >> (cc-18));
- Poke(card, 0xBD, regBD[card]);
- return;
- }
- Poke(card, 0xB0 + Channels[cc], pit[c] & 0xDF);
- }
- void NoteOn(unsigned c, double hertz) // Hertz range: 0..131071
- {
- unsigned card = c/23, cc = c%23;
- unsigned x = 0x2000;
- if(hertz < 0 || hertz > 131071) // Avoid infinite loop
- return;
- while(hertz >= 1023.5) { hertz /= 2.0; x += 0x400; } // Calculate octave
- x += (int)(hertz + 0.5);
- unsigned chn = Channels[cc];
- if(cc >= 18)
- {
- regBD[card] |= (0x10 >> (cc-18));
- Poke(card, 0x0BD, regBD[card]);
- x &= ~0x2000;
- //x |= 0x800; // for test
- }
- if(chn != 0xFFF)
- {
- Poke(card, 0xA0 + chn, x & 0xFF);
- Poke(card, 0xB0 + chn, pit[c] = x >> 8);
- }
- }
- void Touch_Real(unsigned c, unsigned volume)
- {
- if(volume > 63) volume = 63;
- unsigned card = c/23, cc = c%23;
- unsigned i = ins[c];
- unsigned o1 = Operators[cc*2+0];
- unsigned o2 = Operators[cc*2+1];
-
- const adldata& adli = GetAdlIns(i);
- unsigned x = adli.modulator_40, y = adli.carrier_40;
-
- unsigned mode = 1; // 2-op AM
- if(four_op_category[c] == 0 || four_op_category[c] == 3)
- {
- mode = adli.feedconn & 1; // 2-op FM or 2-op AM
- }
- else if(four_op_category[c] == 1 || four_op_category[c] == 2)
- {
- unsigned i0, i1;
- if ( four_op_category[c] == 1 )
- {
- i0 = i;
- i1 = ins[c + 3];
- mode = 2; // 4-op xx-xx ops 1&2
- }
- else
- {
- i0 = ins[c - 3];
- i1 = i;
- mode = 6; // 4-op xx-xx ops 3&4
- }
- mode += (GetAdlIns(i0).feedconn & 1) + (GetAdlIns(i1).feedconn & 1) * 2;
- }
- static const bool do_ops[10][2] =
- { { false, true }, /* 2 op FM */
- { true, true }, /* 2 op AM */
- { false, false }, /* 4 op FM-FM ops 1&2 */
- { true, false }, /* 4 op AM-FM ops 1&2 */
- { false, true }, /* 4 op FM-AM ops 1&2 */
- { true, false }, /* 4 op AM-AM ops 1&2 */
- { false, true }, /* 4 op FM-FM ops 3&4 */
- { false, true }, /* 4 op AM-FM ops 3&4 */
- { false, true }, /* 4 op FM-AM ops 3&4 */
- { true, true } /* 4 op AM-AM ops 3&4 */
- };
-
- bool do_modulator = do_ops[ mode ][ 0 ] || ScaleModulators;
- bool do_carrier = do_ops[ mode ][ 1 ] || ScaleModulators;
-
- Poke(card, 0x40+o1, do_modulator ? (x|63) - volume + volume*(x&63)/63 : x);
- if(o2 != 0xFFF)
- Poke(card, 0x40+o2, do_carrier ? (y|63) - volume + volume*(y&63)/63 : y);
- // Correct formula (ST3, AdPlug):
- // 63-((63-(instrvol))/63)*chanvol
- // Reduces to (tested identical):
- // 63 - chanvol + chanvol*instrvol/63
- // Also (slower, floats):
- // 63 + chanvol * (instrvol / 63.0 - 1)
- }
- void Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127
- {
- if(LogarithmicVolumes)
- {
- Touch_Real(c, volume*127/(127*127*127));
- }
- else
- {
- // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A)
- Touch_Real(c, volume>8725 ? std::log(volume)*11.541561 + (0.5 - 104.22845) : 0);
-
- // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A)
- //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0);
- }
- }
- void Patch(unsigned c, unsigned i)
- {
- unsigned card = c/23, cc = c%23;
- static const unsigned char data[4] = {0x20,0x60,0x80,0xE0};
- ins[c] = i;
- unsigned o1 = Operators[cc*2+0];
- unsigned o2 = Operators[cc*2+1];
-
- const adldata& adli = GetAdlIns(i);
- unsigned x = adli.modulator_E862, y = adli.carrier_E862;
- for(unsigned a=0; a<4; ++a, x>>=8, y>>=8)
- {
- Poke(card, data[a]+o1, x&0xFF);
- if(o2 != 0xFFF)
- Poke(card, data[a]+o2, y&0xFF);
- }
- }
- void Pan(unsigned c, unsigned value)
- {
- unsigned card = c/23, cc = c%23;
- if(Channels[cc] != 0xFFF)
- Poke(card, 0xC0 + Channels[cc], GetAdlIns(ins[c]).feedconn | value);
- }
- void Silence() // Silence all OPL channels.
- {
- for(unsigned c=0; c<NumChannels; ++c) { NoteOff(c); Touch_Real(c,0); }
- }
-
- void updateFlags()
- {
- unsigned fours = NumFourOps;
- for(unsigned card=0; card<NumCards; ++card)
- {
- Poke(card, 0x0BD, regBD[card] = (HighTremoloMode*0x80
- + HighVibratoMode*0x40
- + AdlPercussionMode*0x20) );
- unsigned fours_this_card = std::min(fours, 6u);
- Poke(card, 0x104, (1 << fours_this_card) - 1);
- fours -= fours_this_card;
- }
-
- // Mark all channels that are reserved for four-operator function
- if(AdlPercussionMode == 1)
- for(unsigned a=0; a<NumCards; ++a)
- {
- for(unsigned b=0; b<5; ++b) four_op_category[a*23 + 18 + b] = b+3;
- for(unsigned b=0; b<3; ++b) four_op_category[a*23 + 6 + b] = 8;
- }
-
- unsigned nextfour = 0;
- for(unsigned a=0; a<NumFourOps; ++a)
- {
- four_op_category[nextfour ] = 1;
- four_op_category[nextfour+3] = 2;
- switch(a % 6)
- {
- case 0: case 1: nextfour += 1; break;
- case 2: nextfour += 9-2; break;
- case 3: case 4: nextfour += 1; break;
- case 5: nextfour += 23-9-2; break;
- }
- }
- }
- void Reset()
- {
- LogarithmicVolumes = false;
- cards.resize(NumCards);
- NumChannels = NumCards * 23;
- ins.resize(NumChannels, 189);
- pit.resize(NumChannels, 0);
- regBD.resize(NumCards);
- four_op_category.resize(NumChannels);
- for(unsigned p=0, a=0; a<NumCards; ++a)
- {
- for(unsigned b=0; b<18; ++b) four_op_category[p++] = 0;
- for(unsigned b=0; b< 5; ++b) four_op_category[p++] = 8;
- }
-
- static const short data[] =
- { 0x004,96, 0x004,128, // Pulse timer
- 0x105, 0, 0x105,1, 0x105,0, // Pulse OPL3 enable
- 0x001,32, 0x105,1 // Enable wave, OPL3 extensions
- };
- unsigned fours = NumFourOps;
- for(unsigned card=0; card<NumCards; ++card)
- {
- cards[card].Init(_parent->PCM_RATE);
- for(unsigned a=0; a< 18; ++a) Poke(card, 0xB0+Channels[a], 0x00);
- for(unsigned a=0; a< sizeof(data)/sizeof(*data); a+=2)
- Poke(card, data[a], data[a+1]);
- Poke(card, 0x0BD, regBD[card] = (HighTremoloMode*0x80
- + HighVibratoMode*0x40
- + AdlPercussionMode*0x20) );
- unsigned fours_this_card = std::min(fours, 6u);
- Poke(card, 0x104, (1 << fours_this_card) - 1);
- //fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card);
- fours -= fours_this_card;
- }
-
- // Mark all channels that are reserved for four-operator function
- if(AdlPercussionMode == 1)
- for(unsigned a=0; a<NumCards; ++a)
- {
- for(unsigned b=0; b<5; ++b) four_op_category[a*23 + 18 + b] = b+3;
- for(unsigned b=0; b<3; ++b) four_op_category[a*23 + 6 + b] = 8;
- }
-
- unsigned nextfour = 0;
- for(unsigned a=0; a<NumFourOps; ++a)
- {
- four_op_category[nextfour ] = 1;
- four_op_category[nextfour+3] = 2;
- switch(a % 6)
- {
- case 0: case 1: nextfour += 1; break;
- case 2: nextfour += 9-2; break;
- case 3: case 4: nextfour += 1; break;
- case 5: nextfour += 23-9-2; break;
- }
- }
-
- /**/
- /*
- In two-op mode, channels 0..8 go as follows:
- Op1[port] Op2[port]
- Channel 0: 00 00 03 03
- Channel 1: 01 01 04 04
- Channel 2: 02 02 05 05
- Channel 3: 06 08 09 0B
- Channel 4: 07 09 10 0C
- Channel 5: 08 0A 11 0D
- Channel 6: 12 10 15 13
- Channel 7: 13 11 16 14
- Channel 8: 14 12 17 15
- In four-op mode, channels 0..8 go as follows:
- Op1[port] Op2[port] Op3[port] Op4[port]
- Channel 0: 00 00 03 03 06 08 09 0B
- Channel 1: 01 01 04 04 07 09 10 0C
- Channel 2: 02 02 05 05 08 0A 11 0D
- Channel 3: CHANNEL 0 SLAVE
- Channel 4: CHANNEL 1 SLAVE
- Channel 5: CHANNEL 2 SLAVE
- Channel 6: 12 10 15 13
- Channel 7: 13 11 16 14
- Channel 8: 14 12 17 15
- Same goes principally for channels 9-17 respectively.
- */
-
- Silence();
- }
-};
-
-//static const char MIDIsymbols[256+1] =
-//"PPPPPPhcckmvmxbd" // Ins 0-15
-//"oooooahoGGGGGGGG" // Ins 16-31
-//"BBBBBBBBVVVVVHHM" // Ins 32-47
-//"SSSSOOOcTTTTTTTT" // Ins 48-63
-//"XXXXTTTFFFFFFFFF" // Ins 64-79
-//"LLLLLLLLpppppppp" // Ins 80-95
-//"XXXXXXXXGGGGGTSS" // Ins 96-111
-//"bbbbMMMcGXXXXXXX" // Ins 112-127
-//"????????????????" // Prc 0-15
-//"????????????????" // Prc 16-31
-//"???DDshMhhhCCCbM" // Prc 32-47
-//"CBDMMDDDMMDDDDDD" // Prc 48-63
-//"DDDDDDDDDDDDDDDD" // Prc 64-79
-//"DD??????????????" // Prc 80-95
-//"????????????????" // Prc 96-111
-//"????????????????"; // Prc 112-127
-
-static const char PercussionMap[256] =
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GM
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 3 = bass drum
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 4 = snare
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 5 = tom
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 6 = cymbal
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // 7 = hihat
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP0
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"//GP16
-//2 3 4 5 6 7 8 940 1 2 3 4 5 6 7
-"\0\0\0\3\3\7\4\7\4\5\7\5\7\5\7\5"//GP32
-//8 950 1 2 3 4 5 6 7 8 960 1 2 3
-"\5\6\5\6\6\0\5\6\0\6\0\6\5\5\5\5"//GP48
-//4 5 6 7 8 970 1 2 3 4 5 6 7 8 9
-"\5\0\0\0\0\0\7\0\0\0\0\0\0\0\0\0"//GP64
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
-
-class MIDIplay
-{
- // Information about each track
- struct Position
- {
- bool began;
- double wait;
- struct TrackInfo
- {
- size_t ptr;
- long delay;
- int status;
-
- TrackInfo(): ptr(0), delay(0), status(0) { }
- };
- std::vector<TrackInfo> track;
-
- Position(): began(false), wait(0.0), track() { }
- } CurrentPosition, LoopBeginPosition, trackBeginPosition;
-
- std::map<std::string, unsigned> devices;
- std::map<unsigned/*track*/, unsigned/*channel begin index*/> current_device;
-
- // Persistent settings for each MIDI channel
- struct MIDIchannel
- {
- unsigned short portamento;
- unsigned char bank_lsb, bank_msb;
- unsigned char patch;
- unsigned char volume, expression;
- unsigned char panning, vibrato, sustain;
- double bend, bendsense;
- double vibpos, vibspeed, vibdepth;
- long vibdelay;
- unsigned char lastlrpn,lastmrpn; bool nrpn;
- struct NoteInfo
- {
- // Current pressure
- unsigned char vol;
- // Tone selected on noteon:
- short tone;
- // Patch selected on noteon; index to banks[AdlBank][]
- unsigned char midiins;
- // Index to physical adlib data structure, adlins[]
- unsigned short insmeta;
- // List of adlib channels it is currently occupying.
- std::map<unsigned short/*adlchn*/,
- unsigned short/*ins, inde to adl[]*/
- > phys;
- };
- typedef std::map<unsigned char,NoteInfo> activenotemap_t;
- typedef activenotemap_t::iterator activenoteiterator;
- activenotemap_t activenotes;
-
- MIDIchannel()
- : portamento(0),
- bank_lsb(0), bank_msb(0), patch(0),
- volume(100),expression(100),
- panning(0x30), vibrato(0), sustain(0),
- bend(0.0), bendsense(2 / 8192.0),
- vibpos(0), vibspeed(2*3.141592653*5.0),
- vibdepth(0.5/127), vibdelay(0),
- lastlrpn(0),lastmrpn(0),nrpn(false),
- activenotes() { }
- };
- std::vector<MIDIchannel> Ch;
- bool cmf_percussion_mode = false;
-
- // Additional information about AdLib channels
- struct AdlChannel
- {
- // For collisions
- struct Location
- {
- unsigned short MidCh;
- unsigned char note;
- bool operator==(const Location&b) const
- { return MidCh==b.MidCh && note==b.note; }
- bool operator< (const Location&b) const
- { return MidCh<b.MidCh || (MidCh==b.MidCh&& note<b.note); }
- };
- struct LocationData
- {
- bool sustained;
- unsigned short ins; // a copy of that in phys[]
- long kon_time_until_neglible;
- long vibdelay;
- };
- typedef std::map<Location, LocationData> users_t;
- users_t users;
-
- // If the channel is keyoff'd
- long koff_time_until_neglible;
- // For channel allocation:
- AdlChannel(): users(), koff_time_until_neglible(0) { }
-
- void AddAge(long ms)
- {
- if(users.empty())
- koff_time_until_neglible =
- std::max(koff_time_until_neglible-ms, -0x1FFFFFFFl);
- else
- {
- koff_time_until_neglible = 0;
- for(users_t::iterator i = users.begin(); i != users.end(); ++i)
- {
- i->second.kon_time_until_neglible =
- std::max(i->second.kon_time_until_neglible-ms, -0x1FFFFFFFl);
- i->second.vibdelay += ms;
- }
- }
- }
- };
- std::vector<AdlChannel> ch;
-
- std::vector< std::vector<unsigned char> > TrackData;
-public:
- MIDIplay():
- cmf_percussion_mode(false),
- config(NULL),
- trackStart(false),
- loopStart(false),
- loopEnd(false),
- loopStart_passed(false),
- invalidLoop(false),
- loopStart_hit(false)
- {
- devices.clear();
- }
- ~MIDIplay()
- {}
-
- ADL_MIDIPlayer* config;
- std::string musTitle;
- fraction<long> InvDeltaTicks, Tempo;
- bool trackStart,
- 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*/;
- OPL3 opl;
-public:
- static unsigned long ReadBEint(const void* buffer, unsigned nbytes)
- {
- unsigned long result=0;
- const unsigned char* data = (const unsigned char*) buffer;
- for(unsigned n=0; n<nbytes; ++n)
- result = (result << 8) + data[n];
- return result;
- }
- static unsigned long ReadLEint(const void* buffer, unsigned nbytes)
- {
- unsigned long result=0;
- const unsigned char* data = (const unsigned char*) buffer;
- for(unsigned n=0; n<nbytes; ++n)
- result = result + (data[n] << (n*8));
- return result;
- }
- unsigned long ReadVarLen(unsigned tk)
- {
- unsigned long result = 0;
- for(;;)
- {
- unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
- result = (result << 7) + (byte & 0x7F);
- if(!(byte & 0x80)) break;
- }
- return result;
- }
-
- /*
- * A little class gives able to read filedata from disk and also from a memory segment
- */
- class fileReader
- {
- public:
- enum relTo
- {
- SET=0,
- CUR=1,
- END=2
- };
-
- fileReader()
- {
- fp=NULL;
- mp=NULL;
- mp_size=0;
- mp_tell=0;
- }
- ~fileReader()
- {
- close();
- }
-
- void openFile(const char *path)
- {
- fp = std::fopen(path, "rb");
- _fileName=path;
- mp=NULL;
- mp_size=0;
- mp_tell=0;
- }
-
- void openData(void* mem, unsigned long lenght)
- {
- fp=NULL;
- mp=mem;
- mp_size=lenght;
- mp_tell=0;
- }
-
- void seek(long pos, int rel_to)
- {
- if(fp)
- std::fseek(fp, pos, rel_to);
- else
- {
- switch(rel_to)
- {
- case SET: mp_tell = pos; break;
- case END: mp_tell = mp_size - pos; break;
- case CUR: mp_tell = mp_tell + pos; break;
- }
- if(mp_tell > mp_size)
- mp_tell = mp_size;
- }
- }
-
- long read(void *buf, long num, long size)
- {
- if(fp)
- return std::fread(buf, num, size, fp);
- else
- {
- int pos=0;
- int maxSize = (size*num);
- while( (pos < maxSize) && (mp_tell < mp_size) )
- {
- ((unsigned char*)buf)[pos]=((unsigned char*)mp)[mp_tell];
- mp_tell++;
- pos++;
- }
- return pos;
- }
- }
-
- int getc()
- {
- if(fp)
- return std::getc(fp);
- else
- {
- if(mp_tell>=mp_size)
- {
- return -1;
- }
- int x=((unsigned char*)mp)[mp_tell]; mp_tell++;
- return x;
- }
- }
-
- unsigned long tell()
- {
- if(fp)
- return std::ftell(fp);
- else
- return mp_tell;
- }
-
- void close()
- {
- if(fp) std::fclose(fp);
- fp=NULL;
- mp=NULL;
- mp_size=0;
- mp_tell=0;
- }
-
- bool isValid()
- {
- return (fp)||(mp);
- }
-
- bool eof()
- {
- return mp_tell>=mp_size;
- }
- std::string _fileName;
- std::FILE* fp;
- void* mp;
- unsigned long mp_size;
- unsigned long mp_tell;
- };
-
- bool LoadMIDI(const std::string& filename)
- {
- fileReader file;
- file.openFile(filename.c_str());
- if(!LoadMIDI(file))
- {
- std::perror(filename.c_str());
- return false;
- }
- return true;
- }
-
- bool LoadMIDI(void *data, unsigned long size)
- {
- fileReader file;
- file.openData(data, size);
- return LoadMIDI(file);
- }
-
- bool LoadMIDI(fileReader &fr)
- {
- #define qqq(x) (void)x
- int fsize;
- qqq(fsize);
- //std::FILE* fr = std::fopen(filename.c_str(), "rb");
- if(!fr.isValid()) { ADLMIDI_ErrorString = "Invalid data stream!"; return false; }
- const unsigned HeaderSize = 4+4+2+2+2; // 14
- char HeaderBuf[HeaderSize]="";
- riffskip:;
- fsize=fr.read(HeaderBuf, 1, HeaderSize);
- if(std::memcmp(HeaderBuf, "RIFF", 4) == 0)
- { fr.seek(6, SEEK_CUR); goto riffskip; }
- size_t DeltaTicks=192, TrackCount=1;
-
- config->stored_samples = 0;
- config->backup_samples_size = 0;
- opl.AdlPercussionMode = config->AdlPercussionMode;
- opl.HighTremoloMode = config->HighTremoloMode;
- opl.HighVibratoMode = config->HighVibratoMode;
- opl.ScaleModulators = config->ScaleModulators;
- opl.LogarithmicVolumes = config->LogarithmicVolumes;
- opl.NumCards = config->NumCards;
- opl.NumFourOps = config->NumFourOps;
- cmf_percussion_mode = false;
-
- opl.Reset();
-
- trackStart = true;
- 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)
- bool is_IMF = false; // IMF
- bool is_CMF = false; // Creative Music format (CMF/CTMF)
- //std::vector<unsigned char> MUS_instrumentList;
-
- if(std::memcmp(HeaderBuf, "GMF\x1", 4) == 0)
- {
- // GMD/MUS files (ScummVM)
- fr.seek(7-(HeaderSize), SEEK_CUR);
- is_GMF = true;
- }
- else if(std::memcmp(HeaderBuf, "MUS\x1A", 4) == 0)
- {
- // MUS/DMX files (Doom)
- unsigned start = ReadLEint(HeaderBuf+6, 2);
- is_MUS = true;
- fr.seek(start, SEEK_SET);
- }
- else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0)
- {
- opl.dynamic_instruments.clear();
- opl.dynamic_metainstruments.clear();
- // Creative Music Format (CMF).
- // When playing CTMF files, use the following commandline:
- // adlmidi song8.ctmf -p -v 1 1 0
- // i.e. enable percussion mode, deeper vibrato, and use only 1 card.
-
- is_CMF = true;
- //unsigned version = ReadLEint(HeaderBuf+4, 2);
- unsigned ins_start = ReadLEint(HeaderBuf+6, 2);
- unsigned mus_start = ReadLEint(HeaderBuf+8, 2);
- //unsigned deltas = ReadLEint(HeaderBuf+10, 2);
- unsigned ticks = ReadLEint(HeaderBuf+12, 2);
- // Read title, author, remarks start offsets in file
- fr.read(HeaderBuf, 1, 6);
- //unsigned long notes_starts[3] = {ReadLEint(HeaderBuf+0,2),ReadLEint(HeaderBuf+0,4),ReadLEint(HeaderBuf+0,6)};
- fr.seek(16, SEEK_CUR); // Skip the channels-in-use table
- fr.read(HeaderBuf, 1, 4);
- unsigned ins_count = ReadLEint(HeaderBuf+0, 2);//, basictempo = ReadLEint(HeaderBuf+2, 2);
- fr.seek(ins_start, SEEK_SET);
- //std::printf("%u instruments\n", ins_count);
- for(unsigned i=0; i<ins_count; ++i)
- {
- unsigned char InsData[16];
- fr.read(InsData, 1, 16);
- /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
- i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7],
- InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/
- struct adldata adl;
- struct adlinsdata adlins;
- adl.modulator_E862 =
- (((unsigned int)(InsData[8]&0x07) << 24)&0xFF000000)//WaveForm
- | (((unsigned int)(InsData[6]) << 16)&0x00FF0000)//Sustain/Release
- | (((unsigned int)(InsData[4]) << 8)&0x0000FF00)//Attack/Decay
- | (((unsigned int)(InsData[0]) << 0)&0x000000FF);//MultKEVA
- adl.carrier_E862 =
- (((unsigned int)(InsData[9]&0x07) << 24)&0xFF000000)//WaveForm
- | (((unsigned int)(InsData[7]) << 16)&0x00FF0000)//Sustain/Release
- | (((unsigned int)(InsData[5]) << 8)&0x0000FF00)//Attack/Decay
- | (((unsigned int)(InsData[1]) << 0)&0x000000FF);//MultKEVA
- adl.modulator_40 = InsData[2];
- adl.carrier_40 = InsData[3];
- adl.feedconn = InsData[10]&0x0F;
- adl.finetune = 0;
-
- adlins.adlno1 = opl.dynamic_instruments.size() | opl.DynamicInstrumentTag;
- adlins.adlno2 = adlins.adlno1;
- adlins.ms_sound_kon = 1000;
- adlins.ms_sound_koff = 500;
- adlins.tone = 0;
- adlins.flags = 0;
- adlins.fine_tune = 0.0;
- opl.dynamic_metainstruments.push_back(adlins);
- opl.dynamic_instruments.push_back(adl);
- }
- fr.seek(mus_start, SEEK_SET);
- TrackCount = 1;
- DeltaTicks = ticks;
- opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead
- //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo);
- opl.LogarithmicVolumes = true;
- opl.AdlPercussionMode = true;
- }
- else
- {
- // Try parsing as an IMF file
- {
- unsigned end = (unsigned char)HeaderBuf[0] + 256*(unsigned char)HeaderBuf[1];
- if(!end || (end & 3)) goto not_imf;
-
- long backup_pos = fr.tell();
- unsigned sum1 = 0, sum2 = 0;
- fr.seek(2, SEEK_SET);
- for(unsigned n=0; n<42; ++n)
- {
- unsigned value1 = fr.getc(); value1 += fr.getc() << 8; sum1 += value1;
- unsigned value2 = fr.getc(); value2 += fr.getc() << 8; sum2 += value2;
- }
- fr.seek(backup_pos, SEEK_SET);
- if(sum1 > sum2)
- {
- is_IMF = true;
- DeltaTicks = 1;
- }
- }
-
- if(!is_IMF)
- {
- not_imf:
- if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0)
- { InvFmt:
- fr.close();
- ADLMIDI_ErrorString=fr._fileName+": Invalid format\n";
- return false;
- }
- /*size_t Fmt =*/ ReadBEint(HeaderBuf+8, 2);
- TrackCount = ReadBEint(HeaderBuf+10, 2);
- DeltaTicks = ReadBEint(HeaderBuf+12, 2);
- }
- }
- TrackData.resize(TrackCount);
- CurrentPosition.track.resize(TrackCount);
- InvDeltaTicks = fraction<long>(1, 1000000l * DeltaTicks);
- //Tempo = 1000000l * InvDeltaTicks;
- Tempo = fraction<long>(1, DeltaTicks);
-
- static const unsigned char EndTag[4] = {0xFF,0x2F,0x00,0x00};
-
- int totalGotten=0;
- for(size_t tk = 0; tk < TrackCount; ++tk)
- {
- // Read track header
- size_t TrackLength;
- if(is_IMF)
- {
- //std::fprintf(stderr, "Reading IMF file...\n");
- long end = (unsigned char)HeaderBuf[0] + 256*(unsigned char)HeaderBuf[1];
-
- unsigned IMF_tempo = 1428;
- static const unsigned char imf_tempo[] = {0xFF,0x51,0x4,
- (unsigned char)(IMF_tempo>>24),
- (unsigned char)(IMF_tempo>>16),
- (unsigned char)(IMF_tempo>>8),
- (unsigned char)(IMF_tempo)};
- TrackData[tk].insert(TrackData[tk].end(), imf_tempo, imf_tempo + sizeof(imf_tempo));
- TrackData[tk].push_back(0x00);
-
- fr.seek(2, SEEK_SET);
- while(fr.tell() < (unsigned)end)
- {
- unsigned char special_event_buf[5];
- special_event_buf[0] = 0xFF;
- special_event_buf[1] = 0xE3;
- special_event_buf[2] = 0x02;
- special_event_buf[3] = fr.getc(); // port index
- special_event_buf[4] = fr.getc(); // port value
- unsigned delay = fr.getc(); delay += 256 * fr.getc();
- 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;
- //std::fprintf(stderr, "Done reading IMF file\n");
- opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing!
- }
- else
- {
- if(is_GMF || is_CMF) // Take the rest of the file
- {
- long pos = fr.tell();
- fr.seek(0, SEEK_END);
- TrackLength = fr.tell() - pos;
- fr.seek(pos, SEEK_SET);
- }
- else if(is_MUS) // Read TrackLength from file position 4
- {
- long pos = fr.tell();
- fr.seek(4, SEEK_SET);
- TrackLength = fr.getc(); TrackLength += (fr.getc() << 8);
- fr.seek(pos, SEEK_SET);
- }
- else
- {
- fsize=fr.read(HeaderBuf, 1, 8);
- if(std::memcmp(HeaderBuf, "MTrk", 4) != 0) goto InvFmt;
- TrackLength = ReadBEint(HeaderBuf+4, 4);
- }
- // Read track data
- TrackData[tk].resize(TrackLength);
- fsize=fr.read(&TrackData[tk][0], 1, TrackLength);
- totalGotten += fsize;
- if(is_GMF || is_MUS) // Note: CMF does include the track end tag.
- {
- TrackData[tk].insert(TrackData[tk].end(), EndTag+0, EndTag+4);
- }
- // Read next event time
- CurrentPosition.track[tk].delay = ReadVarLen(tk);
- }
- }
- for(size_t tk = 0; tk < TrackCount; ++tk)
- totalGotten+= TrackData[tk].size();
- if(totalGotten==0)
- {
- ADLMIDI_ErrorString=fr._fileName+": Empty track data";
- return false;
- }
-
- opl.Reset(); // Reset AdLib
- //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
- ch.clear();
- ch.resize(opl.NumChannels);
- return true;
- }
-
- /* 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
- */
- double Tick(double s, double granularity)
- {
- 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);
- ProcessEvents();
- if(CurrentPosition.wait <= 0.0)
- AntiFreezeCounter--;
- }
-
- if(AntiFreezeCounter <= 0)
- CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events
- with zero delay are been detected */
-
- for(unsigned c = 0; c < opl.NumChannels; ++c)
- ch[c].AddAge(s * 1000);
-
- UpdateVibrato(s);
- UpdateArpeggio(s);
- return CurrentPosition.wait;
- }
-
-private:
- enum { Upd_Patch = 0x1,
- Upd_Pan = 0x2,
- Upd_Volume = 0x4,
- Upd_Pitch = 0x8,
- Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch,
- Upd_Off = 0x20 };
-
- void NoteUpdate
- (unsigned MidCh,
- MIDIchannel::activenoteiterator i,
- unsigned props_mask,
- int select_adlchn = -1)
- {
- MIDIchannel::NoteInfo& info = i->second;
- const int tone = info.tone;
- const int vol = info.vol;
- //const int midiins = info.midiins;
- const int insmeta = info.insmeta;
- const adlinsdata& ains = opl.GetAdlMetaIns(insmeta);
-
- AdlChannel::Location my_loc;
- my_loc.MidCh = MidCh;
- my_loc.note = i->first;
-
- for(std::map<unsigned short,unsigned short>::iterator
- jnext = info.phys.begin();
- jnext != info.phys.end();
- )
- {
- std::map<unsigned short,unsigned short>::iterator j(jnext++);
- int c = j->first;
- int ins = j->second;
- if(select_adlchn >= 0 && c != select_adlchn) continue;
-
- if(props_mask & Upd_Patch)
- {
- opl.Patch(c, ins);
- AdlChannel::LocationData& d = ch[c].users[my_loc];
- d.sustained = false; // inserts if necessary
- d.vibdelay = 0;
- d.kon_time_until_neglible = ains.ms_sound_kon;
- d.ins = ins;
- }
- }
- for(std::map<unsigned short,unsigned short>::iterator
- jnext = info.phys.begin();
- jnext != info.phys.end();
- )
- {
- std::map<unsigned short,unsigned short>::iterator j(jnext++);
- int c = j->first;
- int ins = j->second;
- if(select_adlchn >= 0 && c != select_adlchn) continue;
-
- if(props_mask & Upd_Off) // note off
- {
- if(Ch[MidCh].sustain == 0)
- {
- AdlChannel::users_t::iterator k = ch[c].users.find(my_loc);
- if(k != ch[c].users.end())
- ch[c].users.erase(k);
- //UI.IllustrateNote(c, tone, midiins, 0, 0.0);
-
- if(ch[c].users.empty())
- {
- opl.NoteOff(c);
- ch[c].koff_time_until_neglible =
- ains.ms_sound_koff;
- }
- }
- else
- {
- // Sustain: Forget about the note, but don't key it off.
- // Also will avoid overwriting it very soon.
- AdlChannel::LocationData& d = ch[c].users[my_loc];
- d.sustained = true; // note: not erased!
- //UI.IllustrateNote(c, tone, midiins, -1, 0.0);
- }
- info.phys.erase(j);
- continue;
- }
- if(props_mask & Upd_Pan)
- {
- opl.Pan(c, Ch[MidCh].panning);
- }
- if(props_mask & Upd_Volume)
- {
- int volume = vol * Ch[MidCh].volume * Ch[MidCh].expression;
- /* If the channel has arpeggio, the effective volume of
- * *this* instrument is actually lower due to timesharing.
- * To compensate, add extra volume that corresponds to the
- * time this note is *not* heard.
- * Empirical tests however show that a full equal-proportion
- * increment sounds wrong. Therefore, using the square root.
- */
- //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() ));
- opl.Touch(c, volume);
- }
- if(props_mask & Upd_Pitch)
- {
- AdlChannel::LocationData& d = ch[c].users[my_loc];
- // Don't bend a sustained note
- if(!d.sustained)
- {
- double bend = Ch[MidCh].bend + opl.GetAdlIns(ins).finetune;
- double phase = 0.0;
-
- if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins == ains.adlno2)
- {
- phase = ains.fine_tune;//0.125; // Detune the note slightly (this is what Doom does)
- }
-
- if(Ch[MidCh].vibrato && d.vibdelay >= Ch[MidCh].vibdelay)
- bend += Ch[MidCh].vibrato * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos);
- opl.NoteOn(c, 172.00093 * std::exp(0.057762265 * (tone + bend + phase)));
- //UI.IllustrateNote(c, tone, midiins, vol, Ch[MidCh].bend);
- }
- }
- }
- if(info.phys.empty())
- Ch[MidCh].activenotes.erase(i);
- }
-
- void ProcessEvents()
- {
- 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
- long shortest = -1;
- for(size_t tk=0; tk<TrackCount; ++tk)
- if(CurrentPosition.track[tk].status >= 0
- && (shortest == -1
- || CurrentPosition.track[tk].delay < shortest))
- {
- shortest = CurrentPosition.track[tk].delay;
- }
- //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<long> 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;
- }
- LoopBeginPosition = RowBeginPosition;
- loopStart = false;
- loopStart_hit=true;
- }
- if(shortest < 0 || loopEnd)
- {
- // Loop if song end reached
- loopEnd = false;
- CurrentPosition = LoopBeginPosition;
- shortest = 0;
- if(opl._parent->QuitWithoutLooping==1)
- {
- opl._parent->QuitFlag = 1;
- //^ HACK: QUIT WITHOUT LOOPING
- }
- }
- }
-
- void HandleEvent(size_t tk)
- {
- unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
- if(byte == 0xF7 || byte == 0xF0) // Ignore SysEx
- {
- unsigned length = ReadVarLen(tk);
- //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length );
- CurrentPosition.track[tk].ptr += length;
- //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/);
- return;
- }
- if(byte == 0xFF)
- {
- // Special event FF
- unsigned char evtype = TrackData[tk][CurrentPosition.track[tk].ptr++];
- unsigned long length = ReadVarLen(tk);
- std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length );
- CurrentPosition.track[tk].ptr += length;
- if(evtype == 0x2F) { CurrentPosition.track[tk].status = -1; return; }
- if(evtype == 0x51) { Tempo = InvDeltaTicks * fraction<long>( (long) ReadBEint(data.data(), data.size())); return; }
- if(evtype == 6)
- {
- 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());
-
- if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
- {
- unsigned char i = data[0], v = data[1];
- if( (i&0xF0) == 0xC0 ) v |= 0x30;
- //std::printf("OPL poke %02X, %02X\n", i, v);
- //std::fflush(stdout);
- opl.Poke(0, i,v);
- }
- return;
- }
- // Any normal event (80..EF)
- if(byte < 0x80)
- { byte = 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]);*/
- unsigned MidCh = byte & 0x0F, EvType = byte >> 4;
- MidCh += current_device[tk];
-
- CurrentPosition.track[tk].status = byte;
- switch(EvType)
- {
- case 0x8: // Note off
- case 0x9: // Note on
- {
- int note = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
- //if(MidCh != 9) note -= 12; // HACK
- NoteOff(MidCh, note);
- // On Note on, Keyoff the note first, just in case keyoff
- // was omitted; this fixes Dance of sugar-plum fairy
- // by Microsoft. Now that we've done a Keyoff,
- // check if we still need to do a Keyon.
- // vol=0 and event 8x are both Keyoff-only.
- if(vol == 0 || EvType == 0x8) break;
-
- unsigned midiins = Ch[MidCh].patch;
- if(MidCh%16 == 9) midiins = 128 + note; // Percussion instrument
-
- /*
- if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50))
- break; // HACK
- if(midiins == 46) vol = (vol*7)/10; // HACK
- if(midiins == 48 || midiins == 50) vol /= 4; // HACK
- */
- //if(midiins == 56) vol = vol*6/10; // HACK
-
- static std::set<unsigned> bank_warnings;
- if(Ch[MidCh].bank_msb)
- {
- unsigned bankid = midiins + 256*Ch[MidCh].bank_msb;
- std::set<unsigned>::iterator
- i = bank_warnings.lower_bound(bankid);
- if(i == bank_warnings.end() || *i != bankid)
- {
- ADLMIDI_ErrorString.clear();
- std::stringstream s;
- s<<"[" << MidCh <<"]Bank " << Ch[MidCh].bank_msb <<
- " undefined, patch="<< ((midiins&128) ? 'P':'M') << (midiins&127);
- ADLMIDI_ErrorString=s.str();
- bank_warnings.insert(i, bankid);
- }
- }
- if(Ch[MidCh].bank_lsb)
- {
- unsigned bankid = Ch[MidCh].bank_lsb*65536;
- std::set<unsigned>::iterator
- i = bank_warnings.lower_bound(bankid);
- if(i == bank_warnings.end() || *i != bankid)
- {
- ADLMIDI_ErrorString.clear();
- std::stringstream s;
- s<<"["<<MidCh<< "]Bank lsb " << Ch[MidCh].bank_lsb <<" undefined";
- ADLMIDI_ErrorString=s.str();
- bank_warnings.insert(i, bankid);
- }
- }
-
- //int meta = banks[opl.AdlBank][midiins];
- const unsigned meta = opl.GetAdlMetaNumber(midiins);
- const adlinsdata& ains = opl.GetAdlMetaIns(meta);
-
- int tone = note;
- if(ains.tone)
- {
- if(ains.tone < 20)
- tone += ains.tone;
- else if(ains.tone < 128)
- tone = ains.tone;
- else
- tone -= ains.tone-128;
- }
- int i[2] = { ains.adlno1, ains.adlno2 };
- bool pseudo_4op = ains.flags & adlinsdata::Flag_Pseudo4op;
-
- if((opl.AdlPercussionMode==1) && PercussionMap[midiins & 0xFF]) i[1] = i[0];
-
- static std::set<unsigned char> missing_warnings;
- if(!missing_warnings.count(midiins) && (ains.flags & adlinsdata::Flag_NoSound))
- {
- //UI.PrintLn("[%i]Playing missing instrument %i", MidCh, midiins);
- missing_warnings.insert(midiins);
- }
-
- // Allocate AdLib channel (the physical sound channel for the note)
- int adlchannel[2] = { -1, -1 };
- for(unsigned ccount = 0; ccount < 2; ++ccount)
- {
- if(ccount == 1)
- {
- if(i[0] == i[1]) break; // No secondary channel
- if(adlchannel[0] == -1) break; // No secondary if primary failed
- }
-
- int c = -1;
- long bs = -0x7FFFFFFFl;
- for(int a = 0; a < (int)opl.NumChannels; ++a)
- {
- if(ccount == 1 && a == adlchannel[0]) continue;
- // ^ Don't use the same channel for primary&secondary
-
- if(i[0] == i[1] || pseudo_4op)
- {
- // Only use regular channels
- int expected_mode = 0;
- if(opl.AdlPercussionMode==1)
- {
- if(cmf_percussion_mode)
- expected_mode = MidCh < 11 ? 0 : (3+MidCh-11); // CMF
- else
- expected_mode = PercussionMap[midiins & 0xFF];
- }
- if(opl.four_op_category[a] != expected_mode)
- continue;
- }
- else
- {
- if(ccount == 0)
- {
- // Only use four-op master channels
- if(opl.four_op_category[a] != 1)
- continue;
- }
- else
- {
- // The secondary must be played on a specific channel.
- if(a != adlchannel[0] + 3)
- continue;
- }
- }
-
- long s = CalculateAdlChannelGoodness(a, i[ccount], MidCh);
- if(s > bs) { bs=s; c = a; } // Best candidate wins
- }
-
- if(c < 0)
- {
- //UI.PrintLn("ignored unplaceable note");
- continue; // Could not play this note. Ignore it.
- }
- PrepareAdlChannelForNewNote(c, i[ccount]);
- adlchannel[ccount] = c;
- }
- if(adlchannel[0] < 0 && adlchannel[1] < 0)
- {
- // The note could not be played, at all.
- break;
- }
- //UI.PrintLn("i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]);
-
- // Allocate active note for MIDI channel
- std::pair<MIDIchannel::activenoteiterator,bool>
- ir = Ch[MidCh].activenotes.insert(
- std::make_pair(note, MIDIchannel::NoteInfo()));
- ir.first->second.vol = vol;
- ir.first->second.tone = tone;
- ir.first->second.midiins = midiins;
- ir.first->second.insmeta = meta;
- for(unsigned ccount=0; ccount<2; ++ccount)
- {
- int c = adlchannel[ccount];
- if(c < 0) continue;
- ir.first->second.phys[ adlchannel[ccount] ] = i[ccount];
- }
- CurrentPosition.began = true;
- NoteUpdate(MidCh, ir.first, Upd_All | Upd_Patch);
- break;
- }
- case 0xA: // Note touch
- {
- int note = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
- MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.find(note);
- if(i == Ch[MidCh].activenotes.end())
- {
- // Ignore touch if note is not active
- break;
- }
- i->second.vol = vol;
- NoteUpdate(MidCh, i, Upd_Volume);
- break;
- }
- case 0xB: // Controller change
- {
- int ctrlno = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int value = TrackData[tk][CurrentPosition.track[tk].ptr++];
- switch(ctrlno)
- {
- case 1: // Adjust vibrato
- //UI.PrintLn("%u:vibrato %d", MidCh,value);
- Ch[MidCh].vibrato = value; break;
- case 0: // Set bank msb (GM bank)
- Ch[MidCh].bank_msb = value;
- break;
- case 32: // Set bank lsb (XG bank)
- Ch[MidCh].bank_lsb = value;
- break;
- case 5: // Set portamento msb
- Ch[MidCh].portamento = (Ch[MidCh].portamento & 0x7F) | (value<<7);
- //UpdatePortamento(MidCh);
- break;
- case 37: // Set portamento lsb
- Ch[MidCh].portamento = (Ch[MidCh].portamento & 0x3F80) | (value);
- //UpdatePortamento(MidCh);
- break;
- case 65: // Enable/disable portamento
- // value >= 64 ? enabled : disabled
- //UpdatePortamento(MidCh);
- break;
- case 7: // Change volume
- Ch[MidCh].volume = value;
- NoteUpdate_All(MidCh, Upd_Volume);
- break;
- case 64: // Enable/disable sustain
- Ch[MidCh].sustain = value;
- if(!value) KillSustainingNotes( MidCh );
- break;
- case 11: // Change expression (another volume factor)
- Ch[MidCh].expression = value;
- NoteUpdate_All(MidCh, Upd_Volume);
- break;
- case 10: // Change panning
- Ch[MidCh].panning = 0x00;
- if(value < 64+32) Ch[MidCh].panning |= 0x10;
- if(value >= 64-32) Ch[MidCh].panning |= 0x20;
- NoteUpdate_All(MidCh, Upd_Pan);
- break;
- case 121: // Reset all controllers
- Ch[MidCh].bend = 0;
- Ch[MidCh].volume = 100;
- Ch[MidCh].expression = 100;
- Ch[MidCh].sustain = 0;
- Ch[MidCh].vibrato = 0;
- Ch[MidCh].vibspeed = 2*3.141592653*5.0;
- Ch[MidCh].vibdepth = 0.5/127;
- Ch[MidCh].vibdelay = 0;
- Ch[MidCh].panning = 0x30;
- Ch[MidCh].portamento = 0;
- //UpdatePortamento(MidCh);
- NoteUpdate_All(MidCh, Upd_Pan+Upd_Volume+Upd_Pitch);
- // Kill all sustained notes
- KillSustainingNotes(MidCh);
- break;
- case 123: // All notes off
- NoteUpdate_All(MidCh, Upd_Off);
- break;
- case 91: break; // Reverb effect depth. We don't do per-channel reverb.
- case 92: break; // Tremolo effect depth. We don't do...
- case 93: break; // Chorus effect depth. We don't do.
- case 94: break; // Celeste effect depth. We don't do.
- case 95: break; // Phaser effect depth. We don't do.
- case 98: Ch[MidCh].lastlrpn=value; Ch[MidCh].nrpn=true; break;
- case 99: Ch[MidCh].lastmrpn=value; Ch[MidCh].nrpn=true; break;
- case 100:Ch[MidCh].lastlrpn=value; Ch[MidCh].nrpn=false; break;
- case 101:Ch[MidCh].lastmrpn=value; Ch[MidCh].nrpn=false; break;
- case 113: break; // Related to pitch-bender, used by missimp.mid in Duke3D
- case 6: SetRPN(MidCh, value, true); break;
- case 38: SetRPN(MidCh, value, false); break;
- case 103: cmf_percussion_mode = value; break; // CMF (ctrl 0x67) rhythm mode
- case 111://LoopStart unofficial controller
- if(!invalidLoop)
- {
- loopStart = true;
- loopStart_passed=true;
- } break;
- default: break;
- //UI.PrintLn("Ctrl %d <- %d (ch %u)", ctrlno, value, MidCh);
- }
- break;
- }
- case 0xC: // Patch change
- Ch[MidCh].patch = TrackData[tk][CurrentPosition.track[tk].ptr++];
- break;
- case 0xD: // Channel after-touch
- {
- // TODO: Verify, is this correct action?
- int vol = TrackData[tk][CurrentPosition.track[tk].ptr++];
- for(MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.begin();
- i != Ch[MidCh].activenotes.end();
- ++i)
- {
- // Set this pressure to all active notes on the channel
- i->second.vol = vol;
- }
- NoteUpdate_All(MidCh, Upd_Volume);
- break;
- }
- case 0xE: // Wheel/pitch bend
- {
- int a = TrackData[tk][CurrentPosition.track[tk].ptr++];
- int b = TrackData[tk][CurrentPosition.track[tk].ptr++];
- Ch[MidCh].bend = (a + b*128 - 8192) * Ch[MidCh].bendsense;
- NoteUpdate_All(MidCh, Upd_Pitch);
- break;
- }
- }
- }
-
- // Determine how good a candidate this adlchannel
- // would be for playing a note from this instrument.
- long CalculateAdlChannelGoodness
- (unsigned c, unsigned ins, unsigned /*MidCh*/) const
- {
- long s = -ch[c].koff_time_until_neglible;
-
- // Same midi-instrument = some stability
- //if(c == MidCh) s += 4;
- for(AdlChannel::users_t::const_iterator
- j = ch[c].users.begin();
- j != ch[c].users.end();
- ++j)
- {
- s -= 4000;
- if(!j->second.sustained)
- s -= j->second.kon_time_until_neglible;
- else
- s -= j->second.kon_time_until_neglible / 2;
-
- MIDIchannel::activenotemap_t::const_iterator
- k = Ch[j->first.MidCh].activenotes.find(j->first.note);
- if(k != Ch[j->first.MidCh].activenotes.end())
- {
- // Same instrument = good
- if(j->second.ins == ins)
- {
- s += 300;
- // Arpeggio candidate = even better
- if(j->second.vibdelay < 70
- || j->second.kon_time_until_neglible > 20000)
- s += 0;
- }
- // Percussion is inferior to melody
- s += 50 * (k->second.midiins / 128);
-
- /*
- if(k->second.midiins >= 25
- && k->second.midiins < 40
- && j->second.ins != ins)
- {
- s -= 14000; // HACK: Don't clobber the bass or the guitar
- }
- */
- }
-
- // If there is another channel to which this note
- // can be evacuated to in the case of congestion,
- // increase the score slightly.
- unsigned n_evacuation_stations = 0;
- for(unsigned c2 = 0; c2 < opl.NumChannels; ++c2)
- {
- if(c2 == c) continue;
- if(opl.four_op_category[c2]
- != opl.four_op_category[c]) continue;
- for(AdlChannel::users_t::const_iterator
- m = ch[c2].users.begin();
- m != ch[c2].users.end();
- ++m)
- {
- if(m->second.sustained) continue;
- if(m->second.vibdelay >= 200) continue;
- if(m->second.ins != j->second.ins) continue;
- n_evacuation_stations += 1;
- }
- }
- s += n_evacuation_stations * 4;
- }
- return s;
- }
-
- // A new note will be played on this channel using this instrument.
- // Kill existing notes on this channel (or don't, if we do arpeggio)
- void PrepareAdlChannelForNewNote(int c, int ins)
- {
- if(ch[c].users.empty()) return; // Nothing to do
- //bool doing_arpeggio = false;
- for(AdlChannel::users_t::iterator
- jnext = ch[c].users.begin();
- jnext != ch[c].users.end();
- )
- {
- AdlChannel::users_t::iterator j(jnext++);
- if(!j->second.sustained)
- {
- // Collision: Kill old note,
- // UNLESS we're going to do arpeggio
-
- MIDIchannel::activenoteiterator i
- ( Ch[j->first.MidCh].activenotes.find( j->first.note ) );
-
- // Check if we can do arpeggio.
- if((j->second.vibdelay < 70
- || j->second.kon_time_until_neglible > 20000)
- && j->second.ins == ins)
- {
- // Do arpeggio together with this note.
- //doing_arpeggio = true;
- continue;
- }
-
- KillOrEvacuate(c,j,i);
- // ^ will also erase j from ch[c].users.
- }
- }
-
- // Kill all sustained notes on this channel
- // Don't keep them for arpeggio, because arpeggio requires
- // an intact "activenotes" record. This is a design flaw.
- KillSustainingNotes(-1, c);
-
- // Keyoff the channel so that it can be retriggered,
- // unless the new note will be introduced as just an arpeggio.
- if(ch[c].users.empty())
- opl.NoteOff(c);
- }
-
- void KillOrEvacuate(
- unsigned from_channel,
- AdlChannel::users_t::iterator j,
- MIDIchannel::activenoteiterator i)
- {
- // Before killing the note, check if it can be
- // evacuated to another channel as an arpeggio
- // instrument. This helps if e.g. all channels
- // are full of strings and we want to do percussion.
- // FIXME: This does not care about four-op entanglements.
- for(unsigned c = 0; c < opl.NumChannels; ++c)
- {
- if(c == from_channel) continue;
- if(opl.four_op_category[c]
- != opl.four_op_category[from_channel]
- ) continue;
- for(AdlChannel::users_t::iterator
- m = ch[c].users.begin();
- m != ch[c].users.end();
- ++m)
- {
- if(m->second.vibdelay >= 200
- && m->second.kon_time_until_neglible < 10000) continue;
- if(m->second.ins != j->second.ins) continue;
-
- // the note can be moved here!
-// UI.IllustrateNote(
-// from_channel,
-// i->second.tone,
-// i->second.midiins, 0, 0.0);
-// UI.IllustrateNote(
-// c,
-// i->second.tone,
-// i->second.midiins,
-// i->second.vol,
-// 0.0);
-
- i->second.phys.erase(from_channel);
- i->second.phys[c] = j->second.ins;
- ch[c].users.insert( *j );
- ch[from_channel].users.erase( j );
- return;
- }
- }
-
- /*UI.PrintLn(
- "collision @%u: [%ld] <- ins[%3u]",
- c,
- //ch[c].midiins<128?'M':'P', ch[c].midiins&127,
- ch[c].age, //adlins[ch[c].insmeta].ms_sound_kon,
- ins
- );*/
-
- // Kill it
- NoteUpdate(j->first.MidCh,
- i,
- Upd_Off,
- from_channel);
- }
-
- void KillSustainingNotes(int MidCh = -1, int this_adlchn = -1)
- {
- unsigned first=0, last=opl.NumChannels;
- if(this_adlchn >= 0) { first=this_adlchn; last=first+1; }
- for(unsigned c = first; c < last; ++c)
- {
- if(ch[c].users.empty()) continue; // Nothing to do
- for(AdlChannel::users_t::iterator
- jnext = ch[c].users.begin();
- jnext != ch[c].users.end();
- )
- {
- AdlChannel::users_t::iterator j(jnext++);
- if((MidCh < 0 || j->first.MidCh == MidCh)
- && j->second.sustained)
- {
- //int midiins = '?';
- //UI.IllustrateNote(c, j->first.note, midiins, 0, 0.0);
- ch[c].users.erase(j);
- }
- }
- // Keyoff the channel, if there are no users left.
- if(ch[c].users.empty())
- opl.NoteOff(c);
- }
- }
-
- void SetRPN(unsigned MidCh, unsigned value, bool MSB)
- {
- bool nrpn = Ch[MidCh].nrpn;
- unsigned addr = Ch[MidCh].lastmrpn*0x100 + Ch[MidCh].lastlrpn;
- switch(addr + nrpn*0x10000 + MSB*0x20000)
- {
- case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity
- Ch[MidCh].bendsense = value/8192.0;
- break;
- case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed
- if(value == 64)
- Ch[MidCh].vibspeed = 1.0;
- else if(value < 100)
- Ch[MidCh].vibspeed = 1.0/(1.6e-2*(value?value:1));
- else
- Ch[MidCh].vibspeed = 1.0/(0.051153846*value-3.4965385);
- Ch[MidCh].vibspeed *= 2*3.141592653*5.0;
- break;
- case 0x0109 + 1*0x10000 + 1*0x20000: // Vibrato depth
- Ch[MidCh].vibdepth = ((value-64)*0.15)*0.01;
- break;
- case 0x010A + 1*0x10000 + 1*0x20000: // Vibrato delay in millisecons
- Ch[MidCh].vibdelay =
- value ? long(0.2092 * std::exp(0.0795 * value)) : 0.0;
- break;
-
- default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)",
- "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ break;
- }
- }
-
-// void UpdatePortamento(unsigned MidCh)
-// {
-// // mt = 2^(portamento/2048) * (1.0 / 5000.0)
-// /*
-// double mt = std::exp(0.00033845077 * Ch[MidCh].portamento);
-// NoteUpdate_All(MidCh, Upd_Pitch);
-// */
-// //UI.PrintLn("Portamento %u: %u (unimplemented)", MidCh, Ch[MidCh].portamento);
-// }
-
- void NoteUpdate_All(unsigned MidCh, unsigned props_mask)
- {
- for(MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.begin();
- i != Ch[MidCh].activenotes.end();
- )
- {
- MIDIchannel::activenoteiterator j(i++);
- NoteUpdate(MidCh, j, props_mask);
- }
- }
-
- void NoteOff(unsigned MidCh, int note)
- {
- MIDIchannel::activenoteiterator
- i = Ch[MidCh].activenotes.find(note);
- if(i != Ch[MidCh].activenotes.end())
- {
- NoteUpdate(MidCh, i, Upd_Off);
- }
- }
-
- void UpdateVibrato(double amount)
- {
- for(unsigned a=0, b=Ch.size(); a<b; ++a)
- if(Ch[a].vibrato && !Ch[a].activenotes.empty())
- {
- NoteUpdate_All(a, Upd_Pitch);
- Ch[a].vibpos += amount * Ch[a].vibspeed;
- }
- else
- Ch[a].vibpos = 0.0;
- }
-
- void UpdateArpeggio(double /*amount*/) // amount = amount of time passed
- {
- // If there is an adlib channel that has multiple notes
- // simulated on the same channel, arpeggio them.
- #if 0
- const unsigned desired_arpeggio_rate = 40; // Hz (upper limit)
- #if 1
- static unsigned cache=0;
- amount=amount; // Ignore amount. Assume we get a constant rate.
- cache += MaxSamplesAtTime * desired_arpeggio_rate;
- if(cache < PCM_RATE) return;
- cache %= PCM_RATE;
- #else
- static double arpeggio_cache = 0;
- arpeggio_cache += amount * desired_arpeggio_rate;
- if(arpeggio_cache < 1.0) return;
- arpeggio_cache = 0.0;
- #endif
- #endif
- static unsigned arpeggio_counter = 0;
- ++arpeggio_counter;
-
- for(unsigned c = 0; c < opl.NumChannels; ++c)
- {
- retry_arpeggio:;
- size_t n_users = ch[c].users.size();
- /*if(true)
- {
- UI.GotoXY(64,c+1); UI.Color(2);
- std::fprintf(stderr, "%7ld/%7ld,%3u\r",
- ch[c].keyoff,
- (unsigned) n_users);
- UI.x = 0;
- }*/
- if(n_users > 1)
- {
- AdlChannel::users_t::const_iterator i = ch[c].users.begin();
- size_t rate_reduction = 3;
- if(n_users >= 3) rate_reduction = 2;
- if(n_users >= 4) rate_reduction = 1;
- std::advance(i, (arpeggio_counter / rate_reduction) % n_users);
- if(i->second.sustained == false)
- {
- if(i->second.kon_time_until_neglible <= 0l)
- {
- NoteUpdate(
- i->first.MidCh,
- Ch[ i->first.MidCh ].activenotes.find( i->first.note ),
- Upd_Off,
- c);
- goto retry_arpeggio;
- }
- NoteUpdate(
- i->first.MidCh,
- Ch[ i->first.MidCh ].activenotes.find( i->first.note ),
- Upd_Pitch | Upd_Volume | Upd_Pan,
- c);
- }
- }
- }
- }
-
-public:
- unsigned ChooseDevice(const std::string& name)
- {
- std::map<std::string, unsigned>::iterator i = devices.find(name);
- if(i != devices.end()) return i->second;
- size_t n = devices.size() * 16;
- devices.insert( std::make_pair(name, n) );
- Ch.resize(n+16);
- return n;
- }
-};
-
-#ifdef ADLMIDI_buildAsApp
-static std::deque<short> AudioBuffer;
-static MutexType AudioBuffer_lock;
-
-static void SDL_AudioCallbackX(void*, Uint8* stream, int len)
-{
- SDL_LockAudio();
- short* target = (short*) stream;
- AudioBuffer_lock.Lock();
- /*if(len != AudioBuffer.size())
- fprintf(stderr, "len=%d stereo samples, AudioBuffer has %u stereo samples",
- len/4, (unsigned) AudioBuffer.size()/2);*/
- unsigned ate = len/2; // number of shorts
- if(ate > AudioBuffer.size()) ate = AudioBuffer.size();
- for(unsigned a=0; a<ate; ++a)
- {
- target[a] = AudioBuffer[a];
- }
- AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin() + ate);
- AudioBuffer_lock.Unlock();
- SDL_UnlockAudio();
-}
-#endif
-
-struct FourChars
-{
- char ret[4];
-
- FourChars(const char* s)
- {
- for(unsigned c=0; c<4; ++c) ret[c] = s[c];
- }
- FourChars(unsigned w) // Little-endian
- {
- for(unsigned c=0; c<4; ++c) ret[c] = (w >> (c*8)) & 0xFF;
- }
-};
-
-int adlRefreshNumCards(ADL_MIDIPlayer* device)
-{
- unsigned n_fourop[2] = {0,0}, n_total[2] = {0,0};
- for(unsigned a=0; a<256; ++a)
- {
- unsigned insno = banks[device->AdlBank][a];
- if(insno == 198) continue;
- ++n_total[a/128];
- if(adlins[insno].adlno1 != adlins[insno].adlno2)
- ++n_fourop[a/128];
- }
-
- 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);
-
- ((MIDIplay*)device->adl_midiPlayer)->opl.NumFourOps=device->NumFourOps;
-
- if(n_fourop[0] >= n_total[0]*15/16 && device->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"
- " not what you want, therefore ignoring the request.\n";
- return -1;
- }
- return 0;
-}
/*---------------------------EXPORTS---------------------------*/
-ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate)
+ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate)
{
- ADL_MIDIPlayer* _device;
- _device = (ADL_MIDIPlayer*)malloc(sizeof(ADL_MIDIPlayer));
- _device->PCM_RATE = sample_rate;
+ ADL_MIDIPlayer *_device;
+ _device = (ADL_MIDIPlayer *)malloc(sizeof(ADL_MIDIPlayer));
+ _device->PCM_RATE = static_cast<unsigned long>(sample_rate);
_device->AdlBank = 0;
_device->NumFourOps = 7;
_device->NumCards = 2;
@@ -2101,14 +48,13 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate)
_device->SkipForward = 0;
_device->QuitWithoutLooping = 0;
_device->ScaleModulators = 0;
- _device->delay=0.0;
- _device->carry=0.0;
+ _device->delay = 0.0;
+ _device->carry = 0.0;
_device->mindelay = 1.0 / (double)_device->PCM_RATE;
_device->maxdelay = 512.0 / (double)_device->PCM_RATE;
-
- _device->stored_samples=0;
- _device->backup_samples_size=0;
- MIDIplay* player = new MIDIplay;
+ _device->stored_samples = 0;
+ _device->backup_samples_size = 0;
+ MIDIplay *player = new MIDIplay;
_device->adl_midiPlayer = player;
player->config = _device;
player->opl._parent = _device;
@@ -2127,34 +73,42 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer* adl_init(long sample_rate)
ADLMIDI_EXPORT int adl_setNumCards(ADL_MIDIPlayer *device, int numCards)
{
- if(device==NULL) return -2;
- device->NumCards = numCards;
- ((MIDIplay*)device->adl_midiPlayer)->opl.NumCards=device->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)
{
std::stringstream s;
- s<<"number of cards may only be 1.."<< MaxCards<<".\n";
+ s << "number of cards may only be 1.." << MaxCards << ".\n";
ADLMIDI_ErrorString = s.str();
return -1;
}
+
return adlRefreshNumCards(device);
}
ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank)
{
- const unsigned NumBanks = maxAdlBanks();
- int bankno = bank;
+ const uint32_t NumBanks = static_cast<uint32_t>(maxAdlBanks());
+ int32_t bankno = bank;
+
if(bankno < 0)
bankno = 0;
- device->AdlBank = bankno;
- ((MIDIplay*)device->adl_midiPlayer)->opl.AdlBank=device->AdlBank;
+
+ device->AdlBank = static_cast<uint32_t>(bankno);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.AdlBank = device->AdlBank;
+
if(device->AdlBank >= NumBanks)
{
std::stringstream s;
- s<<"bank number may only be 0.."<< (NumBanks-1)<<".\n";
+ s << "bank number may only be 0.." << (NumBanks - 1) << ".\n";
ADLMIDI_ErrorString = s.str();
return -1;
}
+
return adlRefreshNumCards(device);
}
@@ -2163,22 +117,24 @@ ADLMIDI_EXPORT int adl_getBanksCount()
return maxAdlBanks();
}
-ADLMIDI_EXPORT const char * const* adl_getBankNames()
+ADLMIDI_EXPORT const char *const *adl_getBankNames()
{
return banknames;
}
ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4)
{
- device->NumFourOps = ops4;
- ((MIDIplay*)device->adl_midiPlayer)->opl.NumFourOps=device->NumFourOps;
+ device->NumFourOps = static_cast<unsigned int>(ops4);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.NumFourOps = device->NumFourOps;
+
if(device->NumFourOps > 6 * device->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 * (device->NumCards)) << " when " << device->NumCards << " OPL3 cards are used.\n";
ADLMIDI_ErrorString = s.str();
return -1;
}
+
return adlRefreshNumCards(device);
}
@@ -2186,77 +142,101 @@ 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=percmod;
- ((MIDIplay*)device->adl_midiPlayer)->opl.AdlPercussionMode=(bool)device->AdlPercussionMode;
+
+ device->AdlPercussionMode = static_cast<unsigned int>(percmod);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.AdlPercussionMode = (bool)device->AdlPercussionMode;
}
ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro)
{
if(!device) return;
- device->HighVibratoMode=hvibro;
- ((MIDIplay*)device->adl_midiPlayer)->opl.HighVibratoMode=(bool)device->HighVibratoMode;
+
+ device->HighVibratoMode = static_cast<unsigned int>(hvibro);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.HighVibratoMode = (bool)device->HighVibratoMode;
}
ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo)
{
if(!device) return;
- device->HighTremoloMode=htremo;
- ((MIDIplay*)device->adl_midiPlayer)->opl.HighTremoloMode=(bool)device->HighTremoloMode;
+
+ device->HighTremoloMode = static_cast<unsigned int>(htremo);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.HighTremoloMode = (bool)device->HighTremoloMode;
}
ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod)
{
if(!device) return;
- device->ScaleModulators=smod;
- ((MIDIplay*)device->adl_midiPlayer)->opl.ScaleModulators=(bool)device->ScaleModulators;
+
+ device->ScaleModulators = static_cast<unsigned int>(smod);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.ScaleModulators = (bool)device->ScaleModulators;
}
ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn)
{
if(!device) return;
- device->QuitWithoutLooping=(int)(!(bool)loopEn);
+
+ device->QuitWithoutLooping = (loopEn == 0);
}
-ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer* device, int logvol)
+ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol)
{
if(!device) return;
- device->LogarithmicVolumes = logvol;
- ((MIDIplay*)device->adl_midiPlayer)->opl.LogarithmicVolumes = (bool)device->LogarithmicVolumes;
+
+ device->LogarithmicVolumes = static_cast<unsigned int>(logvol);
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.LogarithmicVolumes = (bool)device->LogarithmicVolumes;
+}
+
+ADLMIDI_EXPORT void adl_setVolumeRangeModel(ADL_MIDIPlayer *device, int volumeModel)
+{
+ if(!device) return;
+
+ device->VolumeModel = volumeModel;
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.ChangeVolumeRangesModel(static_cast<ADLMIDI_VolumeModels>(volumeModel));
}
ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, char *filePath)
{
ADLMIDI_ErrorString.clear();
+
if(device && device->adl_midiPlayer)
{
- device->stored_samples=0;
- device->backup_samples_size=0;
- if(!((MIDIplay *)device->adl_midiPlayer)->LoadMIDI(filePath))
+ device->stored_samples = 0;
+ device->backup_samples_size = 0;
+
+ if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadMIDI(filePath))
{
if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString="ADL MIDI: Can't load file";
+ ADLMIDI_ErrorString = "ADL MIDI: Can't load file";
+
return -1;
- } else return 0;
+ }
+ else return 0;
}
- ADLMIDI_ErrorString="Can't load file: ADL MIDI is not initialized";
+
+ ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized";
return -1;
}
-ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer* device, void *mem, long size)
+ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, void *mem, long size)
{
ADLMIDI_ErrorString.clear();
+
if(device && device->adl_midiPlayer)
{
- device->stored_samples=0;
- device->backup_samples_size=0;
- if(!((MIDIplay *)device->adl_midiPlayer)->LoadMIDI(mem, size))
+ device->stored_samples = 0;
+ device->backup_samples_size = 0;
+
+ if(!reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->LoadMIDI(mem, static_cast<size_t>(size)))
{
if(ADLMIDI_ErrorString.empty())
- ADLMIDI_ErrorString="ADL MIDI: Can't load data from memory";
+ ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory";
+
return -1;
- } else return 0;
+ }
+ else return 0;
}
- ADLMIDI_ErrorString="Can't load file: ADL MIDI is not initialized";
+
+ ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized";
return -1;
}
@@ -2269,144 +249,236 @@ ADLMIDI_EXPORT const char *adl_errorString()
ADLMIDI_EXPORT const char *adl_getMusicTitle(ADL_MIDIPlayer *device)
{
if(!device) return "";
- return ((MIDIplay*)(device->adl_midiPlayer))->musTitle.c_str();
+
+ return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->musTitle.c_str();
}
ADLMIDI_EXPORT void adl_close(ADL_MIDIPlayer *device)
{
if(device->adl_midiPlayer)
- {
- delete ((MIDIplay*)(device->adl_midiPlayer));
- }
+ delete reinterpret_cast<MIDIplay *>(device->adl_midiPlayer);
+
device->adl_midiPlayer = NULL;
free(device);
- device=NULL;
+ device = NULL;
}
ADLMIDI_EXPORT void adl_reset(ADL_MIDIPlayer *device)
{
- if(!device) return;
- device->stored_samples=0;
- device->backup_samples_size=0;
- ((MIDIplay*)device->adl_midiPlayer)->opl.Reset();
+ if(!device)
+ return;
+
+ device->stored_samples = 0;
+ device->backup_samples_size = 0;
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.Reset();
}
-inline static void SendStereoAudio(ADL_MIDIPlayer*device,
- int& samples_requested,
- unsigned long& in_size,
- int* _in,
- int out_pos,
- short* _out)
+#ifdef ADLMIDI_USE_DOSBOX_OPL
+inline static void SendStereoAudio(ADL_MIDIPlayer *device,
+ int &samples_requested,
+ ssize_t &in_size,
+ int *_in,
+ ssize_t out_pos,
+ short *_out)
{
if(!in_size) return;
- device->stored_samples=0;
- int out;
- int offset;
- for(unsigned long p = 0; p < in_size; ++p)
+
+ device->stored_samples = 0;
+ ssize_t out;
+ ssize_t offset;
+ ssize_t pos = static_cast<ssize_t>(out_pos);
+
+ for(ssize_t p = 0; p < in_size; ++p)
{
- for(unsigned w=0; w<2; ++w)
+ for(ssize_t w = 0; w < 2; ++w)
{
- out = _in[p*2+w];
- offset = out_pos+p*2+w;
- if(offset<samples_requested)
- {
- _out[offset] = (short)out;
- }
+ out = _in[p * 2 + w];
+ offset = pos + p * 2 + w;
+
+ if(offset < samples_requested)
+ _out[offset] = static_cast<short>(out);
else
{
- device->backup_samples[device->backup_samples_size]=out;
- device->backup_samples_size++; device->stored_samples++;
+ device->backup_samples[device->backup_samples_size] = static_cast<short>(out);
+ device->backup_samples_size++;
+ device->stored_samples++;
}
}
}
}
+#else
+inline static void SendStereoAudio(ADL_MIDIPlayer *device,
+ int &samples_requested,
+ ssize_t &in_size,
+ short *_in,
+ ssize_t out_pos,
+ short *_out)
+{
+ if(!in_size)
+ return;
+
+ 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;
+ size_t toCopy = std::min(maxSamples, inSamples);
+ memcpy(_out + out_pos, _in, toCopy * sizeof(short));
+
+ if(maxSamples < inSamples)
+ {
+ size_t appendSize = inSamples - maxSamples;
+ memcpy(device->backup_samples + device->backup_samples_size,
+ maxSamples + _in, appendSize * sizeof(short));
+ device->backup_samples_size += appendSize;
+ device->stored_samples += appendSize;
+ }
+}
+#endif
-ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer*device, int sampleCount, short *out)
+ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out)
{
- if(!device) return 0;
- sampleCount -= sampleCount%2;//Avoid non-odd sample requests
- if(sampleCount<0) return 0;
- if(device->QuitFlag) return 0;
+ if(!device)
+ return 0;
+
+ sampleCount -= sampleCount % 2; //Avoid even sample requests
- int gotten_len=0;
+ if(sampleCount < 0)
+ return 0;
+
+ if(device->QuitFlag)
+ return 0;
- unsigned long n_samples=512;
- unsigned long n_samples_2=n_samples*2;
+ ssize_t gotten_len = 0;
+ ssize_t n_periodCountStereo = 512;
+ //ssize_t n_periodCountPhys = n_periodCountStereo * 2;
int left = sampleCount;
- while(left>0)
+
+ while(left > 0)
{
- if(device->backup_samples_size>0)
- { //Send reserved samples if exist
- int ate=0;
- while((ate<device->backup_samples_size) && (ate<left))
+ if(device->backup_samples_size > 0)
+ {
+ //Send reserved samples if exist
+ ssize_t ate = 0;
+
+ while((ate < device->backup_samples_size) && (ate < left))
{
- out[ate]=(short)device->backup_samples[ate]; ate++;
+ out[ate] = device->backup_samples[ate];
+ ate++;
}
- left-=ate;
- gotten_len+=ate;
- if(ate<device->backup_samples_size)
+
+ left -= ate;
+ gotten_len += ate;
+
+ if(ate < device->backup_samples_size)
{
- for(int j=0;
- j<ate;
- j++)
- device->backup_samples[(ate-1)-j]=device->backup_samples[(device->backup_samples_size-1)-j];
+ for(int j = 0; j < ate; j++)
+ device->backup_samples[(ate - 1) - j] = device->backup_samples[(device->backup_samples_size - 1) - j];
}
- device->backup_samples_size-=ate;
- } else {
+
+ device->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_samples = (unsigned) device->carry;
- device->carry -= n_samples;
+ n_periodCountStereo = static_cast<ssize_t>(device->carry);
+ //n_periodCountPhys = n_periodCountStereo * 2;
+ device->carry -= n_periodCountStereo;
if(device->SkipForward > 0)
device->SkipForward -= 1;
else
{
- left -= n_samples_2;
- int buf[n_samples*2];
- unsigned long in_count=n_samples;
+ #ifdef ADLMIDI_USE_DOSBOX_OPL
+ std::vector<int> out_buf;
+ #else
+ std::vector<int16_t> out_buf;
+ #endif
+ out_buf.resize(1024 /*n_samples * 2*/);
+
+ ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo;
+ ssize_t in_generatedPhys = in_generatedStereo * 2;
+
+ //fill buffer with zeros
+ size_t in_countStereoU = static_cast<size_t>(in_generatedStereo * 2);
if(device->NumCards == 1)
{
- ((MIDIplay*)(device->adl_midiPlayer))->opl.cards[0].GenerateArr(buf, &in_count);
+ #ifdef ADLMIDI_USE_DOSBOX_OPL
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.cards[0].GenerateArr(out_buf.data(), &in_generatedStereo);
+ in_generatedPhys = in_generatedStereo * 2;
+ #else
+ OPL3_GenerateStream(&(reinterpret_cast<MIDIplay *>(device->adl_midiPlayer))->opl.cards[0], out_buf.data(), static_cast<Bit32u>(in_generatedStereo));
+ #endif
/* Process it */
- SendStereoAudio(device, sampleCount, in_count, buf, gotten_len, out);
+ SendStereoAudio(device, sampleCount, in_generatedStereo, out_buf.data(), gotten_len, out);
}
- else if(n_samples > 0)
+ else if(n_periodCountStereo > 0)
{
- int in[n_samples*2];
-
- //fill buffer with zeros
- for(unsigned long a=0; a<(in_count*2); ++a) buf[a] = 0;
+ #ifdef ADLMIDI_USE_DOSBOX_OPL
+ std::vector<int32_t> in_mixBuffer;
+ in_mixBuffer.resize(1024); //n_samples * 2
+ ssize_t in_generatedStereo = n_periodCountStereo;
+ #endif
+ memset(out_buf.data(), 0, in_countStereoU * sizeof(short));
- unsigned long in_count = n_samples;
+ /* Generate data from every chip and mix result */
for(unsigned card = 0; card < device->NumCards; ++card)
{
- ((MIDIplay*)(device->adl_midiPlayer))->opl.cards[card].GenerateArr( in, &in_count );
- for(unsigned long a=0; a<(in_count*2); ++a)
- buf[a] += in[a];
+ #ifdef ADLMIDI_USE_DOSBOX_OPL
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.cards[card].GenerateArr(in_mixBuffer.data(), &in_generatedStereo);
+ in_generatedPhys = in_generatedStereo * 2;
+ size_t in_samplesCount = static_cast<size_t>(in_generatedPhys);
+ for(size_t a = 0; a < in_samplesCount; ++a)
+ out_buf[a] += in_mixBuffer[a];
+ #else
+ OPL3_GenerateStreamMix(&(reinterpret_cast<MIDIplay *>(device->adl_midiPlayer))->opl.cards[card], out_buf.data(), static_cast<Bit32u>(in_generatedStereo));
+ #endif
}
/* Process it */
- SendStereoAudio(device, sampleCount, in_count, buf, gotten_len, out);
+ SendStereoAudio(device, sampleCount, in_generatedStereo, out_buf.data(), gotten_len, out);
}
- gotten_len += (n_samples*2)-device->stored_samples;
+
+ left -= in_generatedPhys;
+ gotten_len += (in_generatedPhys) - device->stored_samples;
}
- device->delay = ((MIDIplay*)device->adl_midiPlayer)->Tick(eat_delay, device->mindelay);
+
+ device->delay = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->Tick(eat_delay, device->mindelay);
}
}
- return gotten_len;
-}
+ return static_cast<int>(gotten_len);
+}
#ifdef ADLMIDI_buildAsApp
+static std::deque<short> AudioBuffer;
+static MutexType AudioBuffer_lock;
-int main(int argc, char** argv)
+static void SDL_AudioCallbackX(void *, Uint8 *stream, int len)
{
+ SDL_LockAudio();
+ short *target = (short *) stream;
+ AudioBuffer_lock.Lock();
+ /*if(len != AudioBuffer.size())
+ fprintf(stderr, "len=%d stereo samples, AudioBuffer has %u stereo samples",
+ len/4, (unsigned) AudioBuffer.size()/2);*/
+ unsigned ate = len / 2; // number of shorts
+
+ if(ate > AudioBuffer.size()) ate = AudioBuffer.size();
+
+ for(unsigned a = 0; a < ate; ++a)
+ target[a] = AudioBuffer[a];
+
+ AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin() + ate);
+ AudioBuffer_lock.Unlock();
+ SDL_UnlockAudio();
+}
+int main(int argc, char **argv)
+{
if(argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h")
{
std::printf(
@@ -2418,11 +490,13 @@ int main(int argc, char** argv)
" -nl Quit without looping\n"
" -w Write WAV file rather than playing\n"
);
- for(unsigned a=0; a<sizeof(banknames)/sizeof(*banknames); ++a)
+
+ for(unsigned a = 0; a < sizeof(banknames) / sizeof(*banknames); ++a)
std::printf("%10s%2u = %s\n",
- a?"":"Banks:",
- a,
- banknames[a]);
+ a ? "" : "Banks:",
+ a,
+ banknames[a]);
+
std::printf(
" Use banks 2-5 to play Descent \"q\" soundtracks.\n"
" Look up the relevant bank number from descent.sng.\n"
@@ -2433,7 +507,7 @@ int main(int argc, char** argv)
" The Doom & Hexen sets require one or two, while\n"
" Miles four-op set requires the maximum of numcards*6.\n"
"\n"
- );
+ );
return 0;
}
@@ -2449,7 +523,6 @@ int main(int argc, char** argv)
// the sum of these two buffers.
SDL_AudioSpec spec;
SDL_AudioSpec obtained;
-
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
@@ -2462,20 +535,21 @@ int main(int argc, char** argv)
std::fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
//return 1;
}
+
if(spec.samples != obtained.samples)
std::fprintf(stderr, "Wanted (samples=%u,rate=%u,channels=%u); obtained (samples=%u,rate=%u,channels=%u)\n",
- spec.samples, spec.freq, spec.channels,
- obtained.samples,obtained.freq,obtained.channels);
+ spec.samples, spec.freq, spec.channels,
+ obtained.samples, obtained.freq, obtained.channels);
- ADL_MIDIPlayer* myDevice;
+ ADL_MIDIPlayer *myDevice;
myDevice = adl_init(44100);
- if(myDevice==NULL)
+
+ if(myDevice == NULL)
{
std::fprintf(stderr, "Failed to init MIDI device!\n");
return 1;
}
-
while(argc > 2)
{
bool had_option = false;
@@ -2493,16 +567,17 @@ int main(int argc, char** argv)
else break;
std::copy(argv + (had_option ? 4 : 3), argv + argc,
- argv+2);
+ argv + 2);
argc -= (had_option ? 2 : 1);
}
if(argc >= 3)
{
int bankno = std::atoi(argv[2]);
- if(adl_setBank(myDevice, bankno)!=0)
+
+ if(adl_setBank(myDevice, bankno) != 0)
{
- std::fprintf(stderr,"%s", adl_errorString());
+ std::fprintf(stderr, "%s", adl_errorString());
return 0;
}
}
@@ -2515,16 +590,17 @@ int main(int argc, char** argv)
return 0;
}
}
+
if(argc >= 5)
{
- if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4]))!=0)
+ if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4])) != 0)
{
std::fprintf(stderr, "%s\n", adl_errorString());
return 0;
}
}
- if(adl_openFile(myDevice, argv[1])!=0)
+ if(adl_openFile(myDevice, argv[1]) != 0)
{
std::fprintf(stderr, "%s\n", adl_errorString());
return 2;
@@ -2535,21 +611,22 @@ int main(int argc, char** argv)
while(1)
{
int buff[4096];
- unsigned long gotten=adl_play(myDevice, 4096, buff);
- if(gotten<=0) break;
+ unsigned long gotten = adl_play(myDevice, 4096, buff);
+
+ if(gotten <= 0) break;
AudioBuffer_lock.Lock();
- size_t pos = AudioBuffer.size();
- AudioBuffer.resize(pos + gotten);
- for(unsigned long p = 0; p < gotten; ++p)
- AudioBuffer[pos+p] = buff[p];
+ size_t pos = AudioBuffer.size();
+ AudioBuffer.resize(pos + gotten);
+
+ for(unsigned long p = 0; p < gotten; ++p)
+ AudioBuffer[pos + p] = buff[p];
+
AudioBuffer_lock.Unlock();
+ const SDL_AudioSpec &spec_ = obtained;
- const SDL_AudioSpec& spec_ = obtained;
- while(AudioBuffer.size() > spec_.samples + (spec_.freq*2) * OurHeadRoomLength)
- {
+ while(AudioBuffer.size() > spec_.samples + (spec_.freq * 2) * OurHeadRoomLength)
SDL_Delay(1);
- }
}
adl_close(myDevice);
@@ -2557,4 +634,3 @@ int main(int argc, char** argv)
return 0;
}
#endif
-