diff options
author | Wohlstand <admin@wohlnet.ru> | 2017-10-24 23:01:01 +0300 |
---|---|---|
committer | Wohlstand <admin@wohlnet.ru> | 2017-10-24 23:01:01 +0300 |
commit | 6783770a8533dd099b39c51a5c3f7f5f4000b4bc (patch) | |
tree | 5532ae111fe681d4d410f4f3caa71500dea3b1b4 /src | |
parent | 306ecba785a64fa111c1198b9b986ac3e98db619 (diff) | |
download | libADLMIDI-6783770a8533dd099b39c51a5c3f7f5f4000b4bc.tar.gz libADLMIDI-6783770a8533dd099b39c51a5c3f7f5f4000b4bc.tar.bz2 libADLMIDI-6783770a8533dd099b39c51a5c3f7f5f4000b4bc.zip |
Added working seekability!
Testing and stabilizing is needed
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi.cpp | 31 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 124 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 23 |
3 files changed, 143 insertions, 35 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index 7f8564b..1bde95a 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -319,6 +319,27 @@ ADLMIDI_EXPORT void adl_reset(ADL_MIDIPlayer *device) reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->opl.Reset(); } +ADLMIDI_EXPORT double adl_totalTimeLength(ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->timeLength(); +} + +ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->tell(); +} + +ADLMIDI_EXPORT void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds) +{ + if(!device) + return; + reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->seek(seconds); +} + ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) { if(!device) @@ -327,6 +348,7 @@ ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) } + #ifdef ADLMIDI_USE_DOSBOX_OPL #define ADLMIDI_CLAMP(V, MIN, MAX) std::max(std::min(V, (MAX)), (MIN)) @@ -379,13 +401,13 @@ inline static void SendStereoAudio(ADL_MIDIPlayer *device, 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)); + std::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)); + 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; } @@ -480,7 +502,7 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) in_mixBuffer.resize(1024); //n_samples * 2 ssize_t in_generatedStereo = n_periodCountStereo; #endif - memset(out_buf.data(), 0, in_countStereoU * sizeof(short)); + std::memset(out_buf.data(), 0, in_countStereoU * sizeof(short)); /* Generate data from every chip and mix result */ for(unsigned card = 0; card < device->NumCards; ++card) @@ -502,6 +524,7 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) - device->stored_samples; + out_buf.clear(); } device->delay = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->Tick(eat_delay, device->mindelay); diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index d1ee447..5caa15e 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -140,8 +140,7 @@ MIDIplay::MidiTrackPos::MidiTrackPos() : time(0.0), delay(0), absPos(0), - timeDelay(0.0), - next(NULL) + timeDelay(0.0) {} void MIDIplay::MidiTrackPos::reset() @@ -151,7 +150,6 @@ void MIDIplay::MidiTrackPos::reset() absPos = 0; timeDelay = 0.0; events.clear(); - next = NULL; } void MIDIplay::MidiTrackPos::sortEvents() @@ -189,10 +187,11 @@ void MIDIplay::MidiTrackPos::sortEvents() void MIDIplay::buildTrackData() { + fullSongTimeLength = 0.0; trackDataNew.clear(); trackDataNewStatus.clear(); const size_t trackCount = TrackData.size(); - trackDataNew.resize(trackCount, std::vector<MidiTrackPos>()); + trackDataNew.resize(trackCount, MidiTrackQueue()); trackDataNewStatus.resize(trackCount, 0); CurrentPositionNew.track.clear(); @@ -233,7 +232,9 @@ void MIDIplay::buildTrackData() tempos.push_back(event); } - evtPos.delay = ReadVarLen(&trackPtr); + if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event! + evtPos.delay = ReadVarLen(&trackPtr); + if((evtPos.delay > 0) || (event.subtype == MidiEvent::ST_ENDTRACK)) { evtPos.absPos = abs_position; @@ -245,11 +246,10 @@ void MIDIplay::buildTrackData() } while(event.subtype != MidiEvent::ST_ENDTRACK); // Build the chain of events - for(size_t i = 0, j = 1; i < trackDataNew[tk].size() && j < trackDataNew[tk].size(); i++, j++) - trackDataNew[tk][i].next = &(trackDataNew[tk][j]); - + //for(size_t i = 0, j = 1; i < trackDataNew[tk].size() && j < trackDataNew[tk].size(); i++, j++) + // trackDataNew[tk][i].next = &(trackDataNew[tk][j]); if(trackDataNew[tk].size() > 0) - CurrentPositionNew.track[tk].pos = &trackDataNew[tk][0]; + CurrentPositionNew.track[tk].pos = trackDataNew[tk].begin(); } //! Calculate time basing on collected tempo events @@ -259,7 +259,7 @@ void MIDIplay::buildTrackData() double time = 0.0; uint8_t abs_position = 0; size_t tempo_change_index = 0; - std::vector<MidiTrackPos> &track = trackDataNew[tk]; + MidiTrackQueue &track = trackDataNew[tk]; if(track.empty()) continue;//Empty track is useless! @@ -268,8 +268,8 @@ void MIDIplay::buildTrackData() std::fflush(stdout); #endif - MidiTrackPos *posPrev = &track[0];//First element - for(std::vector<MidiTrackPos>::iterator it = track.begin(); it != track.end(); it++) + MidiTrackPos *posPrev = &(*(track.begin()));//First element + for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) { #ifdef DEBUG_TIME_CALCULATION bool tempoChanged = false; @@ -353,11 +353,18 @@ void MIDIplay::buildTrackData() abs_position += pos.delay; posPrev = &pos; } + + if(time > fullSongTimeLength) + fullSongTimeLength = time; } + + fullSongTimeLength += postSongWaitDelay; } MIDIplay::MIDIplay(): cmf_percussion_mode(false), + fullSongTimeLength(0.0), + postSongWaitDelay(1.0), config(NULL), trackStart(false), atEnd(false), @@ -403,12 +410,6 @@ double MIDIplay::Tick(double s, double granularity) CurrentPositionNew.wait -= s; CurrentPositionNew.absTimePosition += s; - #ifdef DEBUG_SHOW_AUDIO_TIMER - std::fprintf(stdout, " \r"); - std::fprintf(stdout, "Time position: %10f\r", CurrentPositionNew.absTimePosition); - std::fflush(stdout); - #endif - int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing while((CurrentPositionNew.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) { @@ -429,7 +430,11 @@ double MIDIplay::Tick(double s, double granularity) UpdateVibrato(s); UpdateArpeggio(s); + if(CurrentPositionNew.wait < 0.0)//Avoid negative delay value! + return 0.0; + return CurrentPositionNew.wait; + // if(CurrentPosition.began) // CurrentPosition.wait -= s; @@ -453,12 +458,70 @@ double MIDIplay::Tick(double s, double granularity) // UpdateVibrato(s); // UpdateArpeggio(s); -// return CurrentPosition.wait; + // return CurrentPosition.wait; +} + +void MIDIplay::seek(double seconds) +{ + if(seconds < 0.0) + return;//Seeking negative position is forbidden! :-P + ADL_MIDIPlayer *device = opl._parent; + const double granularity = device->mindelay, + s = device->delay < device->maxdelay ? device->delay : device->maxdelay; + + unsigned int loopFlagState = opl._parent->loopingIsEnabled; + opl._parent->loopingIsEnabled = 0; + + rewind(); + while((CurrentPositionNew.absTimePosition < seconds) && + (CurrentPositionNew.absTimePosition < fullSongTimeLength)) + { + CurrentPositionNew.wait -= s; + CurrentPositionNew.absTimePosition += s; + + int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing + while((CurrentPositionNew.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) + { + //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); + if(!ProcessEventsNew(true)) + break; + if(CurrentPositionNew.wait <= 0.0) + antiFreezeCounter--; + } + + if(antiFreezeCounter <= 0) + CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events + with zero delay are been detected */ + for(uint16_t c = 0; c < opl.NumChannels; ++c) + ch[c].AddAge(static_cast<int64_t>(s * 1000.0)); + } + + if(CurrentPositionNew.wait < 0.0) + CurrentPositionNew.wait = 0.0; + + device->loopingIsEnabled = loopFlagState; + device->delay = CurrentPositionNew.wait; + device->carry = 0.0; + //UpdateVibrato(s); + UpdateArpeggio(s); +} + +double MIDIplay::tell() +{ + return CurrentPositionNew.absTimePosition; +} + +double MIDIplay::timeLength() +{ + return fullSongTimeLength; } void MIDIplay::rewind() { + Panic(); + KillSustainingNotes(-1, -1); CurrentPosition = trackBeginPosition; + CurrentPositionNew = trackBeginPositionNew; trackStart = true; atEnd = false; loopStart = true; @@ -1186,7 +1249,7 @@ bool MIDIplay::ProcessEvents() return true;//Has events in queue } -bool MIDIplay::ProcessEventsNew() +bool MIDIplay::ProcessEventsNew(bool isSeek) { if(TrackData.size() == 0) atEnd = true;//No MIDI track data to play @@ -1210,6 +1273,8 @@ bool MIDIplay::ProcessEventsNew() for(size_t i = 0; i < track.pos->events.size(); i++) { MidiEvent &evt = track.pos->events[i]; + if(isSeek && (evt.type == MidiEvent::T_NOTEON)) + continue; HandleEvent(tk, evt, track.status); if(loopEnd) break;//Stop event handling on catching loopEnd event! @@ -1221,14 +1286,14 @@ bool MIDIplay::ProcessEventsNew() #endif // Read next event time (unless the track just ended) - if(track.pos->next == NULL/* >= TrackData[tk].size()*/) + if(track.pos == trackDataNew[tk].end()) track.status = -1; if(track.status >= 0) + { track.delay += track.pos->delay; - - if(track.status >= 0) - track.pos = track.pos->next; + track.pos++; + } } } @@ -1301,7 +1366,7 @@ bool MIDIplay::ProcessEventsNew() if(opl._parent->loopingIsEnabled == 0) { atEnd = true; //Don't handle events anymore - CurrentPositionNew.wait += 1.0;//One second delay until stop playing + CurrentPositionNew.wait += postSongWaitDelay;//One second delay until stop playing return true;//We have caugh end here! } CurrentPositionNew = LoopBeginPositionNew; @@ -1950,6 +2015,15 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, AdlChannel::users_t::iterator static_cast<int32_t>(from_channel)); } +void MIDIplay::Panic() +{ + for(size_t chan = 0; chan < Ch.size(); chan++) + { + for(uint8_t note = 0; note < 128; note++) + realTime_NoteOff(chan, note); + } +} + void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) { uint32_t first = 0, last = opl.NumChannels; diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 75094d2..451a8e6 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -50,6 +50,7 @@ #endif #include <vector> +#include <list> #include <string> #include <sstream> #include <map> @@ -387,10 +388,10 @@ private: * @brief Sort events in this position */ void sortEvents(); - //! Next event that follows current - MidiTrackPos *next; }; + typedef std::list<MidiTrackPos> MidiTrackQueue; + // Information about each track struct PositionNew { @@ -404,15 +405,20 @@ private: uint64_t delay; int status; char padding2[4]; - MidiTrackPos *pos; - TrackInfo(): ptr(0), delay(0), status(0), pos(NULL) {} + MidiTrackQueue::iterator pos; + TrackInfo(): ptr(0), delay(0), status(0) {} }; std::vector<TrackInfo> track; PositionNew(): began(false), wait(0.0), absTimePosition(0.0), track() {} } CurrentPositionNew, LoopBeginPositionNew, trackBeginPositionNew; - std::vector<std::vector<MidiTrackPos> > trackDataNew; + //! Full song length in seconds + double fullSongTimeLength; + //! Delay after song playd before rejecting the output stream requests + double postSongWaitDelay; + + std::vector<MidiTrackQueue > trackDataNew; std::vector<int> trackDataNewStatus; void buildTrackData(); MidiEvent parseEvent(uint8_t **ptr, int &status); @@ -609,6 +615,10 @@ public: */ double Tick(double s, double granularity); + void seek(double seconds); + double tell(); + double timeLength(); + void rewind(); /* RealTime event triggers */ @@ -647,7 +657,7 @@ private: unsigned props_mask, int32_t select_adlchn = -1); bool ProcessEvents(); - bool ProcessEventsNew(); + bool ProcessEventsNew(bool isSeek = false); void HandleEvent(size_t tk); void HandleEvent(size_t tk, MidiEvent &evt, int &status); @@ -663,6 +673,7 @@ private: size_t from_channel, AdlChannel::users_t::iterator j, MIDIchannel::activenoteiterator i); + void Panic(); void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); void SetRPN(unsigned MidCh, unsigned value, bool MSB); //void UpdatePortamento(unsigned MidCh) |