/* * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation * * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma * ADLMIDI Library API: Copyright (c) 2017 Vitaly Novichkov * * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: * http://iki.fi/bisqwit/source/adlmidi.html * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "adlmidi_private.hpp" static const unsigned MaxCards = 100; /*---------------------------EXPORTS---------------------------*/ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) { ADL_MIDIPlayer *midi_device; midi_device = (ADL_MIDIPlayer *)malloc(sizeof(ADL_MIDIPlayer)); midi_device->PCM_RATE = static_cast(sample_rate); midi_device->AdlBank = 0; midi_device->NumFourOps = 7; midi_device->NumCards = 2; midi_device->HighTremoloMode = 0; midi_device->HighVibratoMode = 0; midi_device->AdlPercussionMode = 0; midi_device->LogarithmicVolumes = 0; midi_device->SkipForward = 0; midi_device->loopingIsEnabled = 0; midi_device->ScaleModulators = 0; midi_device->delay = 0.0; midi_device->carry = 0.0; midi_device->mindelay = 1.0 / (double)midi_device->PCM_RATE; midi_device->maxdelay = 512.0 / (double)midi_device->PCM_RATE; midi_device->stored_samples = 0; midi_device->backup_samples_size = 0; MIDIplay *player = new MIDIplay; midi_device->adl_midiPlayer = player; player->config = midi_device; player->opl._parent = midi_device; player->opl.NumCards = midi_device->NumCards; player->opl.AdlBank = midi_device->AdlBank; player->opl.NumFourOps = midi_device->NumFourOps; player->opl.LogarithmicVolumes = (midi_device->LogarithmicVolumes != 0); player->opl.HighTremoloMode = (midi_device->HighTremoloMode != 0); player->opl.HighVibratoMode = (midi_device->HighVibratoMode != 0); player->opl.AdlPercussionMode = (midi_device->AdlPercussionMode != 0); player->opl.ScaleModulators = (midi_device->ScaleModulators != 0); player->ChooseDevice("none"); adlRefreshNumCards(midi_device); return midi_device; } ADLMIDI_EXPORT int adl_setNumCards(ADL_MIDIPlayer *device, int numCards) { if(device == NULL) return -2; device->NumCards = static_cast(numCards); reinterpret_cast(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"; ADLMIDI_ErrorString = s.str(); return -1; } return adlRefreshNumCards(device); } ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) { #ifdef DISABLE_EMBEDDED_BANKS ADL_UNUSED(device); ADL_UNUSED(bank); ADLMIDI_ErrorString = "This build of libADLMIDI has no embedded banks. Please load bank by using of adl_openBankFile() or adl_openBankData() functions instead of adl_setBank()"; return -1; #else const uint32_t NumBanks = static_cast(maxAdlBanks()); int32_t bankno = bank; if(bankno < 0) bankno = 0; device->AdlBank = static_cast(bankno); MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); play->opl.AdlBank = device->AdlBank; if(device->AdlBank >= NumBanks) { std::stringstream s; s << "bank number may only be 0.." << (NumBanks - 1) << ".\n"; ADLMIDI_ErrorString = s.str(); return -1; } return adlRefreshNumCards(device); #endif } ADLMIDI_EXPORT int adl_getBanksCount() { return maxAdlBanks(); } ADLMIDI_EXPORT const char *const *adl_getBankNames() { return banknames; } ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) { device->NumFourOps = static_cast(ops4); reinterpret_cast(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"; ADLMIDI_ErrorString = s.str(); return -1; } return adlRefreshNumCards(device); } ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) { if(!device) return; device->AdlPercussionMode = static_cast(percmod); reinterpret_cast(device->adl_midiPlayer)->opl.AdlPercussionMode = (device->AdlPercussionMode != 0); } ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) { if(!device) return; device->HighVibratoMode = static_cast(hvibro); reinterpret_cast(device->adl_midiPlayer)->opl.HighVibratoMode = (device->HighVibratoMode != 0); } ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) { if(!device) return; device->HighTremoloMode = static_cast(htremo); reinterpret_cast(device->adl_midiPlayer)->opl.HighTremoloMode = (device->HighTremoloMode != 0); } ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) { if(!device) return; device->ScaleModulators = static_cast(smod); reinterpret_cast(device->adl_midiPlayer)->opl.ScaleModulators = (device->ScaleModulators != 0); } ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) { if(!device) return; device->loopingIsEnabled = (loopEn != 0); } ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol) { if(!device) return; device->LogarithmicVolumes = static_cast(logvol); reinterpret_cast(device->adl_midiPlayer)->opl.LogarithmicVolumes = (device->LogarithmicVolumes != 0); } ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel) { if(!device) return; device->VolumeModel = volumeModel; reinterpret_cast(device->adl_midiPlayer)->opl.ChangeVolumeRangesModel(static_cast(volumeModel)); } ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, char *filePath) { ADLMIDI_ErrorString.clear(); if(device && device->adl_midiPlayer) { device->stored_samples = 0; device->backup_samples_size = 0; if(!reinterpret_cast(device->adl_midiPlayer)->LoadBank(filePath)) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load file"; return -1; } else return 0; } ADLMIDI_ErrorString = "Can't load file: ADLMIDI is not initialized"; return -1; } ADLMIDI_EXPORT int adl_openBankData(struct 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(!reinterpret_cast(device->adl_midiPlayer)->LoadBank(mem, static_cast(size))) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory"; return -1; } else return 0; } ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; return -1; } 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(!reinterpret_cast(device->adl_midiPlayer)->LoadMIDI(filePath)) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load file"; return -1; } else return 0; } 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_ErrorString.clear(); if(device && device->adl_midiPlayer) { device->stored_samples = 0; device->backup_samples_size = 0; if(!reinterpret_cast(device->adl_midiPlayer)->LoadMIDI(mem, static_cast(size))) { if(ADLMIDI_ErrorString.empty()) ADLMIDI_ErrorString = "ADL MIDI: Can't load data from memory"; return -1; } else return 0; } ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; return -1; } ADLMIDI_EXPORT const char *adl_errorString() { return ADLMIDI_ErrorString.c_str(); } ADLMIDI_EXPORT const char *adl_getMusicTitle(ADL_MIDIPlayer *device) { if(!device) return ""; return reinterpret_cast(device->adl_midiPlayer)->musTitle.c_str(); } ADLMIDI_EXPORT void adl_close(ADL_MIDIPlayer *device) { if(device->adl_midiPlayer) delete reinterpret_cast(device->adl_midiPlayer); device->adl_midiPlayer = NULL; free(device); device = NULL; } ADLMIDI_EXPORT void adl_reset(ADL_MIDIPlayer *device) { if(!device) return; device->stored_samples = 0; device->backup_samples_size = 0; reinterpret_cast(device->adl_midiPlayer)->opl.Reset(); } ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) { if(!device) return; reinterpret_cast(device->adl_midiPlayer)->rewind(); } 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(out_pos); size_t inSamples = static_cast(in_size * 2); size_t maxSamples = static_cast(samples_requested) - offset; size_t toCopy = std::min(maxSamples, inSamples); std::memcpy(_out + out_pos, _in, toCopy * sizeof(short)); if(maxSamples < inSamples) { size_t appendSize = inSamples - maxSamples; std::memcpy(device->backup_samples + device->backup_samples_size, maxSamples + _in, appendSize * sizeof(short)); device->backup_samples_size += (ssize_t)appendSize; device->stored_samples += (ssize_t)appendSize; } } ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) { if(!device) return 0; sampleCount -= sampleCount % 2; //Avoid even sample requests if(sampleCount < 0) return 0; ssize_t gotten_len = 0; ssize_t n_periodCountStereo = 512; //ssize_t n_periodCountPhys = n_periodCountStereo * 2; int left = sampleCount; while(left > 0) { 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] = device->backup_samples[ate]; ate++; } left -= (int)ate; gotten_len += ate; if(ate < device->backup_samples_size) { for(ssize_t j = 0; j < ate; j++) device->backup_samples[(ate - 1) - j] = device->backup_samples[(device->backup_samples_size - 1) - j]; } 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_periodCountStereo = static_cast(device->carry); device->carry -= n_periodCountStereo; if(device->SkipForward > 0) device->SkipForward -= 1; else { MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); if((player->atEnd) && (device->delay <= 0.0)) break;//Stop to fetch samples at reaching of song end with disabled loop //! Count of stereo samples ssize_t in_generatedStereo = (n_periodCountStereo > 512) ? 512 : n_periodCountStereo; //! Total count of samples ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros int16_t *out_buf = player->outBuf; std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(int16_t)); if(device->NumCards == 1) { #ifdef ADLMIDI_USE_DOSBOX_OPL player->opl.cards[0].GenerateArr(out_buf, &in_generatedStereo); in_generatedPhys = in_generatedStereo * 2; #else OPL3_GenerateStream(&player->opl.cards[0], out_buf, static_cast(in_generatedStereo)); #endif /* Process it */ SendStereoAudio(device, sampleCount, in_generatedStereo, out_buf, gotten_len, out); } else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ for(unsigned card = 0; card < device->NumCards; ++card) { #ifdef ADLMIDI_USE_DOSBOX_OPL player->opl.cards[card].GenerateArrMix(out_buf, &in_generatedStereo); in_generatedPhys = in_generatedStereo * 2; #else OPL3_GenerateStreamMix(&player->opl.cards[card], out_buf, static_cast(in_generatedStereo)); #endif } /* Process it */ SendStereoAudio(device, sampleCount, in_generatedStereo, out_buf, gotten_len, out); } left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) - device->stored_samples; } device->delay = reinterpret_cast(device->adl_midiPlayer)->Tick(eat_delay, device->mindelay); } } return static_cast(gotten_len); }