aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_load.cpp
diff options
context:
space:
mode:
authorVitaly Novichkov <admin@wohlnet.ru>2018-06-21 04:23:14 +0300
committerVitaly Novichkov <admin@wohlnet.ru>2018-06-21 04:23:14 +0300
commit9f0f9e4e374798851da39335406ac4bafad81297 (patch)
tree28b744d0e52afc29c1d27f06b227e3101a8844a8 /src/adlmidi_load.cpp
parenta0fa0cded46bdc94293f02d4070a69bcae5478bb (diff)
downloadlibADLMIDI-9f0f9e4e374798851da39335406ac4bafad81297.tar.gz
libADLMIDI-9f0f9e4e374798851da39335406ac4bafad81297.tar.bz2
libADLMIDI-9f0f9e4e374798851da39335406ac4bafad81297.zip
Move MIDI sequencer into completely separated class
TODO: - implement C bindings for most of class functions - test it in work on any different synthesizer
Diffstat (limited to 'src/adlmidi_load.cpp')
-rw-r--r--src/adlmidi_load.cpp450
1 files changed, 57 insertions, 393 deletions
diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp
index 10bea21..3e51b77 100644
--- a/src/adlmidi_load.cpp
+++ b/src/adlmidi_load.cpp
@@ -24,40 +24,6 @@
#include "adlmidi_private.hpp"
#include "wopl/wopl_file.h"
-#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
-# ifndef ADLMIDI_DISABLE_MUS_SUPPORT
-# include "adlmidi_mus2mid.h"
-# endif//MUS
-# ifndef ADLMIDI_DISABLE_XMI_SUPPORT
-# include "adlmidi_xmi2mid.h"
-# endif//XMI
-#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER
-
-#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
-uint64_t MIDIplay::ReadBEint(const void *buffer, size_t nbytes)
-{
- uint64_t result = 0;
- const unsigned char *data = reinterpret_cast<const unsigned char *>(buffer);
-
- for(unsigned n = 0; n < nbytes; ++n)
- result = (result << 8) + data[n];
-
- return result;
-}
-
-uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes)
-{
- uint64_t result = 0;
- const unsigned char *data = reinterpret_cast<const unsigned char *>(buffer);
-
- for(unsigned n = 0; n < nbytes; ++n)
- result = result + static_cast<uint64_t>(data[n] << (n * 8));
-
- return result;
-}
-
-#endif
-
bool MIDIplay::LoadBank(const std::string &filename)
{
FileAndMemReader file;
@@ -293,185 +259,44 @@ bool MIDIplay::LoadBank(FileAndMemReader &fr)
}
#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
-bool MIDIplay::LoadMIDI(const std::string &filename)
-{
- FileAndMemReader file;
- file.openFile(filename.c_str());
- if(!LoadMIDI(file))
- return false;
- return true;
-}
-
-bool MIDIplay::LoadMIDI(const void *data, size_t size)
-{
- FileAndMemReader file;
- file.openData(data, size);
- return LoadMIDI(file);
-}
-bool MIDIplay::LoadMIDI(FileAndMemReader &fr)
+bool MIDIplay::LoadMIDI_pre()
{
- size_t fsize;
- ADL_UNUSED(fsize);
- //! Temp buffer for conversion
- AdlMIDI_CPtr<uint8_t> cvt_buf;
- errorString.clear();
-
- #ifdef DISABLE_EMBEDDED_BANKS
+#ifdef DISABLE_EMBEDDED_BANKS
if((opl.AdlBank != ~0u) || opl.dynamic_banks.empty())
{
errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!";
return false;
}
- #endif
-
- if(!fr.isValid())
- {
- errorStringOut = "Invalid data stream!\n";
- #ifndef _WIN32
- errorStringOut += std::strerror(errno);
- #endif
- return false;
- }
-
+#endif
/**** Set all properties BEFORE starting of actial file reading! ****/
applySetup();
- atEnd = false;
- loopStart = true;
- invalidLoop = 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)
- bool is_RSXX = false; // RSXX, such as Cartooners
-
- const size_t HeaderSize = 4 + 4 + 2 + 2 + 2; // 14
- char HeaderBuf[HeaderSize] = "";
- size_t DeltaTicks = 192, TrackCount = 1;
-
-riffskip:
- fsize = fr.read(HeaderBuf, 1, HeaderSize);
+ caugh_missing_instruments.clear();
+ caugh_missing_banks_melodic.clear();
+ caugh_missing_banks_percussion.clear();
- if(std::memcmp(HeaderBuf, "RIFF", 4) == 0)
- {
- fr.seek(6l, SEEK_CUR);
- goto riffskip;
- }
+ return true;
+}
- if(std::memcmp(HeaderBuf, "GMF\x1", 4) == 0)
- {
- // GMD/MUS files (ScummVM)
- fr.seek(7 - static_cast<long>(HeaderSize), SEEK_CUR);
- is_GMF = true;
- }
- #ifndef ADLMIDI_DISABLE_MUS_SUPPORT
- else if(std::memcmp(HeaderBuf, "MUS\x1A", 4) == 0)
- {
- // MUS/DMX files (Doom)
- fr.seek(0, SEEK_END);
- size_t mus_len = fr.tell();
- fr.seek(0, SEEK_SET);
- uint8_t *mus = (uint8_t *)malloc(mus_len);
- if(!mus)
- {
- errorStringOut = "Out of memory!";
- return false;
- }
- fr.read(mus, 1, mus_len);
- //Close source stream
- fr.close();
-
- uint8_t *mid = NULL;
- uint32_t mid_len = 0;
- int m2mret = AdlMidi_mus2midi(mus, static_cast<uint32_t>(mus_len),
- &mid, &mid_len, 0);
- if(mus) free(mus);
- if(m2mret < 0)
- {
- errorStringOut = "Invalid MUS/DMX data format!";
- return false;
- }
- cvt_buf.reset(mid);
- //Open converted MIDI file
- fr.openData(mid, static_cast<size_t>(mid_len));
- //Re-Read header again!
- goto riffskip;
- }
- #endif //ADLMIDI_DISABLE_MUS_SUPPORT
- #ifndef ADLMIDI_DISABLE_XMI_SUPPORT
- else if(std::memcmp(HeaderBuf, "FORM", 4) == 0)
+bool MIDIplay::LoadMIDI_post()
+{
+ MidiSequencer::FileFormat format = m_sequencer.getFormat();
+ if(format == MidiSequencer::Format_CMF)
{
- if(std::memcmp(HeaderBuf + 8, "XDIR", 4) != 0)
- {
- fr.close();
- errorStringOut = fr.fileName() + ": Invalid format\n";
- return false;
- }
+ const std::vector<MidiSequencer::CmfInstrument> &instruments = m_sequencer.getRawCmfInstruments();
+ opl.dynamic_banks.clear();//Clean up old banks
- fr.seek(0, SEEK_END);
- size_t mus_len = fr.tell();
- fr.seek(0, SEEK_SET);
- uint8_t *mus = (uint8_t*)malloc(mus_len);
- if(!mus)
+ uint16_t ins_count = static_cast<uint16_t>(instruments.size());
+ for(uint16_t i = 0; i < ins_count; ++i)
{
- errorStringOut = "Out of memory!";
- return false;
- }
- fr.read(mus, 1, mus_len);
- //Close source stream
- fr.close();
-
- uint8_t *mid = NULL;
- uint32_t mid_len = 0;
- int m2mret = AdlMidi_xmi2midi(mus, static_cast<uint32_t>(mus_len),
- &mid, &mid_len, XMIDI_CONVERT_NOCONVERSION);
- if(mus) free(mus);
- if(m2mret < 0)
- {
- errorStringOut = "Invalid XMI data format!";
- return false;
- }
- cvt_buf.reset(mid);
- //Open converted MIDI file
- fr.openData(mid, static_cast<size_t>(mid_len));
- //Re-Read header again!
- goto riffskip;
- }
- #endif //ADLMIDI_DISABLE_XMI_SUPPORT
- else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0)
- {
- opl.dynamic_banks.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);
- uint64_t ins_start = ReadLEint(HeaderBuf + 6, 2);
- uint64_t mus_start = ReadLEint(HeaderBuf + 8, 2);
- //unsigned deltas = ReadLEint(HeaderBuf+10, 2);
- uint64_t 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);
- uint64_t ins_count = ReadLEint(HeaderBuf + 0, 2); //, basictempo = ReadLEint(HeaderBuf+2, 2);
- fr.seek(static_cast<long>(ins_start), SEEK_SET);
-
- //std::printf("%u instruments\n", ins_count);
- for(unsigned i = 0; i < ins_count; ++i)
- {
- unsigned bank = i / 256;
+ const uint8_t *InsData = instruments[i].data;
+ uint16_t bank = i / 256;
bank = (bank & 127) + ((bank >> 7) << 8);
if(bank > 127 + (127 << 8))
break;
bank += (i % 256 < 128) ? 0 : OPL3::PercussionTag;
- 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]);*/
@@ -500,224 +325,63 @@ riffskip:
adlins.voice2_fine_tune = 0.0;
}
- fr.seeku(mus_start, SEEK_SET);
- TrackCount = 1;
- DeltaTicks = (size_t)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.AdlPercussionMode = true;
opl.m_musicMode = OPL3::MODE_CMF;
opl.m_volumeScale = OPL3::VOLUME_NATIVE;
}
- else
+ else if(format == MidiSequencer::Format_RSXX)
{
- // Try to identify RSXX format
- if(HeaderBuf[0] == 0x7D)
- {
- fr.seek(0x6D, SEEK_SET);
- fr.read(HeaderBuf, 6, 1);
- if(std::memcmp(HeaderBuf, "rsxx}u", 6) == 0)
- {
- is_RSXX = true;
- fr.seek(0x7D, SEEK_SET);
- TrackCount = 1;
- DeltaTicks = 60;
- //opl.CartoonersVolumes = true;
- opl.m_musicMode = OPL3::MODE_RSXX;
- opl.m_volumeScale = OPL3::VOLUME_NATIVE;
- }
- }
-
- // Try parsing as an IMF file
- if(!is_RSXX)
- {
- do
- {
- uint8_t raw[4];
- size_t end = static_cast<size_t>(HeaderBuf[0]) + 256 * static_cast<size_t>(HeaderBuf[1]);
-
- if(!end || (end & 3))
- break;
-
- size_t backup_pos = fr.tell();
- int64_t sum1 = 0, sum2 = 0;
- fr.seek(2, SEEK_SET);
-
- for(unsigned n = 0; n < 42; ++n)
- {
- if(fr.read(raw, 1, 4) != 4)
- break;
- int64_t value1 = raw[0];
- value1 += raw[1] << 8;
- sum1 += value1;
- int64_t value2 = raw[2];
- value2 += raw[3] << 8;
- sum2 += value2;
- }
-
- fr.seek(static_cast<long>(backup_pos), SEEK_SET);
-
- if(sum1 > sum2)
- {
- is_IMF = true;
- DeltaTicks = 1;
- }
- } while(false);
- }
-
- if(!is_IMF && !is_RSXX)
- {
- if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0)
- {
- fr.close();
- errorStringOut = fr.fileName() + ": Invalid format, Header signature is unknown!\n";
- return false;
- }
-
- /*size_t Fmt = ReadBEint(HeaderBuf + 8, 2);*/
- TrackCount = (size_t)ReadBEint(HeaderBuf + 10, 2);
- DeltaTicks = (size_t)ReadBEint(HeaderBuf + 12, 2);
- }
+ //opl.CartoonersVolumes = true;
+ opl.m_musicMode = OPL3::MODE_RSXX;
+ opl.m_volumeScale = OPL3::VOLUME_NATIVE;
}
-
- TrackData.clear();
- TrackData.resize(TrackCount, std::vector<uint8_t>());
- InvDeltaTicks = fraction<uint64_t>(1, 1000000l * static_cast<uint64_t>(DeltaTicks));
- if(is_CMF || is_RSXX)
- Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks));
- else
- Tempo = fraction<uint64_t>(1, static_cast<uint64_t>(DeltaTicks) * 2);
- static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00};
- size_t totalGotten = 0;
-
- for(size_t tk = 0; tk < TrackCount; ++tk)
+ else if(format == MidiSequencer::Format_IMF)
{
- // Read track header
- size_t TrackLength;
-
- if(is_IMF)
- {
- //std::fprintf(stderr, "Reading IMF file...\n");
- size_t end = static_cast<size_t>(HeaderBuf[0]) + 256 * static_cast<size_t>(HeaderBuf[1]);
- unsigned IMF_tempo = 1428;
- 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),
- static_cast<uint8_t>(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() < end && !fr.eof())
- {
- uint8_t special_event_buf[5];
- uint8_t raw[4];
- special_event_buf[0] = MidiEvent::T_SPECIAL;
- special_event_buf[1] = MidiEvent::ST_RAWOPL;
- special_event_buf[2] = 0x02;
- 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;
- //std::fprintf(stderr, "Done reading IMF file\n");
- opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing!
- opl.m_musicMode = OPL3::MODE_IMF;
- }
- else
- {
- // Take the rest of the file
- if(is_GMF || is_CMF || is_RSXX)
- {
- size_t pos = fr.tell();
- fr.seek(0, SEEK_END);
- TrackLength = fr.tell() - pos;
- fr.seek(static_cast<long>(pos), SEEK_SET);
- }
- //else if(is_MUS) // Read TrackLength from file position 4
- //{
- // size_t pos = fr.tell();
- // fr.seek(4, SEEK_SET);
- // TrackLength = static_cast<size_t>(fr.getc());
- // TrackLength += static_cast<size_t>(fr.getc() << 8);
- // fr.seek(static_cast<long>(pos), SEEK_SET);
- //}
- else
- {
- fsize = fr.read(HeaderBuf, 1, 8);
- if(std::memcmp(HeaderBuf, "MTrk", 4) != 0)
- {
- fr.close();
- errorStringOut = fr.fileName() + ": Invalid format, MTrk signature is not found!\n";
- return false;
- }
- TrackLength = (size_t)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);
- if(is_RSXX)//Finalize raw track data with a zero
- TrackData[tk].push_back(0);
-
- //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;
- //}
- }
+ //std::fprintf(stderr, "Done reading IMF file\n");
+ opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing!
+ opl.m_musicMode = OPL3::MODE_IMF;
}
- for(size_t tk = 0; tk < TrackCount; ++tk)
- totalGotten += TrackData[tk].size();
+ opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
+ //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
+ ch.clear();
+ ch.resize(opl.NumChannels);
+
+ return true;
+}
- if(totalGotten == 0)
+bool MIDIplay::LoadMIDI(const std::string &filename)
+{
+ FileAndMemReader file;
+ file.openFile(filename.c_str());
+ if(!LoadMIDI_pre())
+ return false;
+ if(!m_sequencer.loadMIDI(file))
{
- errorStringOut = fr.fileName() + ": Empty track data";
+ errorStringOut = m_sequencer.getErrorString();
return false;
}
+ if(!LoadMIDI_post())
+ return false;
+ return true;
+}
- //Build new MIDI events table
- if(!buildTrackData())
+bool MIDIplay::LoadMIDI(const void *data, size_t size)
+{
+ FileAndMemReader file;
+ file.openData(data, size);
+ if(!LoadMIDI_pre())
+ return false;
+ if(!m_sequencer.loadMIDI(file))
{
- errorStringOut = fr.fileName() + ": MIDI data parsing error has occouped!\n" + errorString;
+ errorStringOut = m_sequencer.getErrorString();
return false;
}
-
- opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip
- //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously)
- ch.clear();
- ch.resize(opl.NumChannels);
+ if(!LoadMIDI_post())
+ return false;
return true;
}
-#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER
+
+#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */