void EcoreCallbackManager::RemoveAllCallbacksFromMainThread() { // always called from main thread // the mutex will already be locked at this point for( CallbackList::iterator iter = mCallbackContainer.begin(); iter != mCallbackContainer.end(); ++iter) { CallbackData* data = (*iter); if (data->mType == CallbackData::STANDARD_CALLBACK) { RemoveStandardCallback(data); } else if (data->mType == CallbackData::EVENT_HANDLER) { RemoveEventCallback(data); } } mCallbackContainer.clear(); }
void UpdateEditor() { static GHEvent *pHold[8] = { (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1 }; bool ctrlState = MFInput_Read(Key_LControl, IDD_Keyboard) || MFInput_Read(Key_RControl, IDD_Keyboard); bool shiftState = MFInput_Read(Key_LShift, IDD_Keyboard) || MFInput_Read(Key_LShift, IDD_Keyboard); bool altState = MFInput_Read(Key_LAlt, IDD_Keyboard) || MFInput_Read(Key_RAlt, IDD_Keyboard); int res = gEditor.pSong->GetRes(); if(TestControl(dBCtrl_Edit_Save, GHCT_Once)) { gEditor.pSong->SaveChart(); MFVoice *pVoice = MFSound_Play(gEditor.pSaveSound, MFPF_BeginPaused); MFSound_SetVolume(pVoice, gConfig.sound.fxLevel); MFSound_Pause(pVoice, false); if(gConfig.editor.saveAction[0]) system(gConfig.editor.saveAction); } if(TestControl(dBCtrl_Edit_Event, GHCT_Once)) PlaceEvent(); else if(TestControl(dBCtrl_Edit_TrackEvent, GHCT_Once)) PlaceTrackEvent(); else if(TestControl(dBCtrl_Edit_Section, GHCT_Once)) PlaceSection(); else if(TestControl(dBCtrl_Edit_Quantise, GHCT_Once)) gpStringBox->Show(MFTranslation_GetString(pStrings, MENU_SETQUANTISE), "", SetCustomQuantisation); else if(TestControl(dBCtrl_Edit_Mixer, GHCT_Once)) ((EditorScreen*)dBScreen::GetCurrent())->gMixer.Push(); // check selection if(bSelecting && !TestControl(dBCtrl_Edit_RangeSelect, GHCT_Hold)) { gEditor.selectEnd = gEditor.offset; bSelecting = false; } else if(!bSelecting && TestControl(dBCtrl_Edit_RangeSelect, GHCT_Hold)) { gEditor.selectStart = gEditor.selectEnd = gEditor.offset; bSelecting = true; } if(TestControl(dBCtrl_Edit_Cut, GHCT_Once) || TestControl(dBCtrl_Edit_Copy, GHCT_Once)) { if(gEditor.selectStart != gEditor.selectEnd) { gEditor.copyLen = gEditor.selectEnd - gEditor.selectStart; gEditor.selectEvents.Clear(); GHEventManager ¬eStream = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]]; GHEvent *pEv = noteStream.GetNextEvent(gEditor.selectStart); while(pEv && pEv->tick < gEditor.selectEnd) { // copy the next pointer incase we cut the note. GHEvent *pNext = pEv->Next(); // just cut/copy notes if(pEv->event == GHE_Note) { // copy to clipboard gEditor.selectEvents.AddEvent(pEv->event, pEv->tick - gEditor.selectStart, pEv->key, pEv->parameter); // if we cut if(TestControl(dBCtrl_Edit_Cut, GHCT_Once)) pNext = noteStream.RemoveEvent(pEv); } pEv = pNext; } } } else if(TestControl(dBCtrl_Edit_Paste, GHCT_Once)) { GHEvent *pEv = gEditor.selectEvents.First(); if(pEv) { int curStream = gEditor.currentStream[gEditor.selectedStream]; GHEventManager ¬eStream = gEditor.pSong->notes[curStream]; // delete notes in paste range GHEvent *pDel = noteStream.GetNextEvent(gEditor.offset); while(pDel && pDel->tick < gEditor.offset + gEditor.copyLen) { if(pDel->event == GHE_Note) pDel = noteStream.RemoveEvent(pDel); else pDel = pDel->Next(); } // paste notes while(pEv) { noteStream.AddEvent(pEv->event, pEv->tick + gEditor.offset, pEv->key, pEv->parameter); pEv = pEv->Next(); } gEditor.pSong->CalculateNoteTimes(curStream, gEditor.offset); } } if(TestControl(dBCtrl_Edit_Delete, GHCT_Once)) { GHEventManager ¬es = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]]; if(gEditor.selectStart != gEditor.selectEnd) { // delete notes in selected range GHEvent *pDel = notes.GetNextEvent(gEditor.selectStart); while(pDel && pDel->tick < gEditor.selectEnd) { if(pDel->event == GHE_Note) pDel = notes.RemoveEvent(pDel); else pDel = pDel->Next(); } } else { int numEvents = 0; uint32 eventTypes = 0; // find note events GHEvent *pEvent = notes.GetNextEvent(gEditor.offset); while(pEvent && pEvent->tick == gEditor.offset) { uint32 bit = 1 << pEvent->event; if(!(eventTypes & bit)) { ++numEvents; eventTypes |= bit; } pEvent = pEvent->Next(); } // find sync events if(gEditor.offset > 0) { pEvent = gEditor.pSong->sync.GetNextEvent(gEditor.offset); while(pEvent && pEvent->tick == gEditor.offset) { uint32 bit = 1 << pEvent->event; if(!(eventTypes & bit)) { ++numEvents; eventTypes |= bit; } pEvent = pEvent->Next(); } } // find global events pEvent = gEditor.pSong->events.GetNextEvent(gEditor.offset); while(pEvent && pEvent->tick == gEditor.offset) { uint32 bit = 1 << pEvent->event; if(!(eventTypes & bit)) { ++numEvents; eventTypes |= bit; } pEvent = pEvent->Next(); } // if there are multiple event types to remove, show a list box if(numEvents > 1) gpListBox->Show(MFTranslation_GetString(pStrings, SELECT_EVENT_REMOVE), RemoveEventCallback, 200.0f, 100.0f); for(int a=0; a<GHE_Max; ++a) { if(eventTypes & (1 << a)) { if(numEvents > 1) gpListBox->AddItem(MFTranslation_GetString(pStrings, EVENT_TYPE_UNKNOWN+a), (void*)(size_t)a); else RemoveEventCallback(false, 0, NULL, (void*)(size_t)a); } } } } // shift notes left or right int selStart, selEnd; if(gEditor.selectStart != gEditor.selectEnd) { selStart = gEditor.selectStart; selEnd = gEditor.selectEnd; } else { selStart = gEditor.offset; selEnd = gEditor.pSong->GetLastNoteTick(); } if(TestControl(dBCtrl_Edit_ShiftForwards, GHCT_Delay)) { int offset = 4*res / gEditor.quantisation; // TODO: gotta remove notes after the selection that will be overwritten as the selection shifts.. if(altState) { // shift events, sync and other tracks too GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { if(pEv->tick != 0) pEv->tick += offset; pEv = pEv->Next(); } pEv = gEditor.pSong->events.GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { pEv->tick += offset; pEv = pEv->Next(); } for(int i=0; i<GHS_Max; ++i) { pEv = gEditor.pSong->notes[i].GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { pEv->tick += offset; pEv = pEv->Next(); } } } else { GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { pEv->tick += offset; pEv = pEv->Next(); } } gEditor.selectStart += offset; gEditor.selectEnd += offset; gEditor.offset += offset; gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[gEditor.selectedStream], gEditor.offset); } if(TestControl(dBCtrl_Edit_ShiftBackwards, GHCT_Delay)) { int offset = MFMin(4*res / gEditor.quantisation, gEditor.selectStart); // TODO: gotta remove notes before the selection that will be overwritten as the selection shifts.. if(altState) { // shift events, sync and other tracks too GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { if(pEv->tick != 0) pEv->tick -= offset; pEv = pEv->Next(); } pEv = gEditor.pSong->events.GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { pEv->tick -= offset; pEv = pEv->Next(); } for(int i=0; i<GHS_Max; ++i) { pEv = gEditor.pSong->notes[i].GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { pEv->tick -= offset; pEv = pEv->Next(); } } } else { GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { pEv->tick -= offset; pEv = pEv->Next(); } } gEditor.selectStart -= offset; gEditor.selectEnd -= offset; gEditor.offset -= offset; gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[gEditor.selectedStream], gEditor.offset); } if(TestControl(dBCtrl_Edit_ShiftLeft, GHCT_Once)) { GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { if(pEv->event == GHE_Note) pEv->key = MFMax(0, pEv->key - 1); pEv = pEv->Next(); } } if(TestControl(dBCtrl_Edit_ShiftRight, GHCT_Once)) { GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart); while(pEv && pEv->tick < selEnd) { if(pEv->event == GHE_Note) pEv->key = MFMin(4, pEv->key + 1); pEv = pEv->Next(); } } // change quantisation if(TestControl(dBCtrl_Edit_QuantiseDown, GHCT_Delay)) { gEditor.quantiseStep = MFMax(0, gEditor.quantiseStep-1); gEditor.quantisation = gQuantiseSteps[gEditor.quantiseStep]; OffsetToMeasureAndBeat(gEditor.offset, &gEditor.measure, &gEditor.beat); MFVoice *pVoice = MFSound_Play(gEditor.pChangeSound, MFPF_BeginPaused); MFSound_SetVolume(pVoice, gConfig.sound.fxLevel); MFSound_Pause(pVoice, false); } if(TestControl(dBCtrl_Edit_QuantiseUp, GHCT_Delay)) { gEditor.quantiseStep = MFMin((int)(sizeof(gQuantiseSteps)/sizeof(gQuantiseSteps[0]))-1, gEditor.quantiseStep+1); gEditor.quantisation = gQuantiseSteps[gEditor.quantiseStep]; OffsetToMeasureAndBeat(gEditor.offset, &gEditor.measure, &gEditor.beat); MFVoice *pVoice = MFSound_Play(gEditor.pChangeSound, MFPF_BeginPaused); MFSound_SetVolume(pVoice, gConfig.sound.fxLevel); MFSound_Pause(pVoice, false); } // move the track if(TestControl(dBCtrl_Edit_Forward, GHCT_Delay)) { // forward one step ++gEditor.beat; if(gEditor.beat == gEditor.quantisation) { ++gEditor.measure; gEditor.beat = 0; } } if(TestControl(dBCtrl_Edit_Back, GHCT_Delay)) { // back one step if(gEditor.measure || gEditor.beat) { --gEditor.beat; if(gEditor.beat < 0) { --gEditor.measure; gEditor.beat += gEditor.quantisation; } } } if(TestControl(dBCtrl_Edit_Start, GHCT_Once)) { // go to start gEditor.measure = gEditor.beat = 0; } if(TestControl(dBCtrl_Edit_End, GHCT_Once)) { // go to the last note... OffsetToMeasureAndBeat(gEditor.pSong->GetLastNoteTick(), &gEditor.measure, &gEditor.beat); } if(TestControl(dBCtrl_Edit_UpMeasure, GHCT_Delay)) { // forward one measure // TODO: consider bar lengths while moving ++gEditor.measure; } if(TestControl(dBCtrl_Edit_DownMeasure, GHCT_Delay)) { // back one measure // TODO: consider bar lengths while moving if(gEditor.measure < 1) gEditor.measure = gEditor.beat = 0; else --gEditor.measure; } if(TestControl(dBCtrl_Edit_NextSection, GHCT_Delay)) { GHEvent *pEv = gEditor.pSong->events.GetNextEvent(gEditor.offset); while(pEv) { if(pEv->tick >= gEditor.offset + gEditor.pSong->resolution*4 / gEditor.quantisation && !MFString_CompareN(pEv->GetString(), "section ", 8)) { OffsetToMeasureAndBeat(pEv->tick, &gEditor.measure, &gEditor.beat); break; } pEv = pEv->Next(); } if(!pEv) OffsetToMeasureAndBeat(gEditor.pSong->GetLastNoteTick(), &gEditor.measure, &gEditor.beat); } if(TestControl(dBCtrl_Edit_PrevSection, GHCT_Delay)) { GHEvent *pMostRecent = NULL; GHEvent *pEv = gEditor.pSong->events.First(); while(pEv && pEv->tick < gEditor.offset) { if(!MFString_CompareN(pEv->GetString(), "section ", 8)) pMostRecent = pEv; pEv = pEv->Next(); } if(pMostRecent) OffsetToMeasureAndBeat(pMostRecent->tick, &gEditor.measure, &gEditor.beat); else gEditor.measure = gEditor.beat = 0; } int newOffset = MeasureAndBeatToOffset(gEditor.measure, gEditor.beat); int shift = newOffset - gEditor.offset; shift = MFMax(gEditor.offset + shift, 0) - gEditor.offset; if(shift) { // update BPM if applicable int shiftStart, shiftEnd; if(shift > 0) { shiftStart = gEditor.offset; shiftEnd = gEditor.offset+shift + 1; } else { shiftStart = 0; shiftEnd = gEditor.offset+shift + 1; } gEditor.offset += shift; if(bSelecting) gEditor.selectEnd = gEditor.offset; GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(shiftStart); while(pEv && pEv->tick < shiftEnd) { if(pEv->event == GHE_BPM || pEv->event == GHE_Anchor) gEditor.currentBPM = pEv->parameter; else if(pEv->event == GHE_TimeSignature) { gEditor.currentTimeSignature = pEv->parameter; gEditor.lastTimeSignature = pEv->tick; } pEv = pEv->Next(); } gEditor.playingTime = gEditor.pSong->CalculateTimeOfTick(gEditor.offset); MFVoice *pVoice = MFSound_Play(gEditor.pStepSound, MFPF_BeginPaused); if(pVoice) { MFSound_SetVolume(pVoice, gConfig.sound.fxLevel); MFSound_Pause(pVoice, false); } } // increase/decrease BPM if(gEditor.currentBPM > 1000 && TestControl(dBCtrl_Edit_DecreaseBPM, GHCT_Delay)) { // reduce BPM int inc = 1000; if(shiftState) inc /= 10; if(ctrlState) inc /= 100; if(altState) inc *= 10; GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0); if(!pEv) pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0); if(!pEv) pEv = gEditor.pSong->sync.AddEvent(GHE_BPM, gEditor.offset, 0, MFMax(gEditor.currentBPM - inc, 1000)); else pEv->parameter = MFMax(pEv->parameter - inc, 1000); gEditor.currentBPM = pEv->parameter; // remove this BPM marker if its the same as the previous one.. if(pEv->event != GHE_Anchor) { GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_BPM, gEditor.offset); if(pPrev && pPrev->parameter == pEv->parameter) gEditor.pSong->sync.RemoveEvent(pEv); } // recalculate the note times from this point on gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], gEditor.offset); if(gEditor.currentStream[1] != -1) gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], gEditor.offset); } if(gEditor.currentBPM < 9999000 && TestControl(dBCtrl_Edit_IncreaseBPM, GHCT_Delay)) { // increase BPM int inc = 1000; if(shiftState) inc /= 10; if(ctrlState) inc /= 100; if(altState) inc *= 10; GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0); if(!pEv) pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0); if(!pEv) pEv = gEditor.pSong->sync.AddEvent(GHE_BPM, gEditor.offset, 0, MFMin(gEditor.currentBPM + inc, 9999000)); else pEv->parameter = MFMin(pEv->parameter + inc, 9999000); gEditor.currentBPM = pEv->parameter; // remove this BPM marker if its the same as the previous one.. if(pEv->event != GHE_Anchor) { GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_BPM, gEditor.offset); if(pEv->event != GHE_Anchor && pPrev && pPrev->parameter == pEv->parameter) gEditor.pSong->sync.RemoveEvent(pEv); } // recalculate the note times from this point on gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], gEditor.offset); if(gEditor.currentStream[1] != -1) gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], gEditor.offset); } // place anchor if(TestControl(dBCtrl_Edit_Anchor, GHCT_Once) && gEditor.offset > 0) { GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0); if(pEv) { pEv->event = GHE_Anchor; pEv->time = gEditor.pSong->CalculateTimeOfTick(pEv->tick); } else { pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0); if(!pEv) { pEv = gEditor.pSong->sync.AddEvent(GHE_Anchor, gEditor.offset, 0, gEditor.currentBPM); pEv->time = gEditor.pSong->CalculateTimeOfTick(pEv->tick); } else { GHEvent *pLast = gEditor.pSong->sync.GetMostRecentSyncEvent(pEv->tick); if(pLast && pLast->parameter == pEv->parameter) gEditor.pSong->sync.RemoveEvent(pEv); else pEv->event = GHE_BPM; } } // recalculate the note times gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], 0); if(gEditor.currentStream[1] != -1) gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], 0); } // change time signature if(TestControl(dBCtrl_Edit_DecreaseTS, GHCT_Delay) && gEditor.currentTimeSignature > 1) { int tsTime = gEditor.offset - ((gEditor.offset - gEditor.lastTimeSignature) % (res*gEditor.currentTimeSignature)); GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_TimeSignature, tsTime, 0); if(!pEv) pEv = gEditor.pSong->sync.AddEvent(GHE_TimeSignature, tsTime, 0, gEditor.currentTimeSignature - 1); else --pEv->parameter; gEditor.currentTimeSignature = pEv->parameter; gEditor.lastTimeSignature = tsTime; // remove this BPM marker if its the same as the previous one.. GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_TimeSignature, tsTime); if(pPrev && pPrev->parameter == pEv->parameter) { gEditor.lastTimeSignature = pPrev->tick; gEditor.pSong->sync.RemoveEvent(pEv); } // recalculate the note times from this point on gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], tsTime); if(gEditor.currentStream[1] != -1) gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], tsTime); } else if(TestControl(dBCtrl_Edit_IncreaseTS, GHCT_Delay) && gEditor.currentTimeSignature < 99) { int tsTime = gEditor.offset - ((gEditor.offset - gEditor.lastTimeSignature) % (res*gEditor.currentTimeSignature)); GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_TimeSignature, tsTime, 0); if(!pEv) pEv = gEditor.pSong->sync.AddEvent(GHE_TimeSignature, tsTime, 0, gEditor.currentTimeSignature + 1); else ++pEv->parameter; gEditor.currentTimeSignature = pEv->parameter; gEditor.lastTimeSignature = tsTime; // remove this BPM marker if its the same as the previous one.. GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_TimeSignature, tsTime); if(pPrev && pPrev->parameter == pEv->parameter) { gEditor.lastTimeSignature = pPrev->tick; gEditor.pSong->sync.RemoveEvent(pEv); } // recalculate the note times from this point on gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], tsTime); if(gEditor.currentStream[1] != -1) gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], tsTime); } // add/remove notes GHEventManager ¬eStream = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]]; dBControlType keys_righty[] = { dBCtrl_Edit_Note0, dBCtrl_Edit_Note1, dBCtrl_Edit_Note2, dBCtrl_Edit_Note3, dBCtrl_Edit_Note4, dBCtrl_Edit_PS1, dBCtrl_Edit_PS2, dBCtrl_Edit_Note5 }; dBControlType keys_lefty[] = { dBCtrl_Edit_Note4, dBCtrl_Edit_Note3, dBCtrl_Edit_Note2, dBCtrl_Edit_Note1, dBCtrl_Edit_Note0, dBCtrl_Edit_PS1, dBCtrl_Edit_PS2, dBCtrl_Edit_Note5 }; dBControlType *keys = gConfig.controls.leftyFlip[0] ? keys_lefty : keys_righty; for(int a=0; a<8; a++) { GHEventType ev = a < 5 ? GHE_Note : GHE_Special; int key = a < 5 ? GHEK_Green + a : GHS_Player1 + (a - 5); if(TestControl(keys[a], GHCT_Hold)) { if(pHold[a]) { if(pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF) { pHold[a]->parameter = MFMax(gEditor.offset - pHold[a]->tick, 0); } } else { GHEvent *pEv = noteStream.FindEvent(ev, gEditor.offset, key); if(pEv) { noteStream.RemoveEvent(pEv); pHold[a] = (GHEvent*)(size_t)0xFFFFFFFF; for(int i=0; i<8; ++i) { if(pHold[i] && pHold[i] != (GHEvent*)(size_t)0xFFFFFFFF) { if(pHold[i] > pEv) --pHold[i]; } } } else { // check if we are intersecting a hold note pEv = noteStream.GetMostRecentEvent(GHE_Note, gEditor.offset); if(pEv && pEv->parameter > gEditor.offset - pEv->tick) { // the last note was a hold note, we'll cut it short... do { pEv->parameter = gEditor.offset - pEv->tick; pEv = pEv->Prev(); } while(pEv && pEv->tick == pEv->Next()->tick); } pEv = noteStream.AddEvent(ev, gEditor.offset, key); pEv->time = gEditor.pSong->CalculateTimeOfTick(gEditor.offset); pHold[a] = pEv; } } } else { if(a<5) { // check if we have just released a hold note if(pHold[a] && pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF && pHold[a]->parameter != 0) { // remove any other notes within the hold range GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(pHold[a]->tick); while(pEv && pEv->tick < pHold[a]->tick+pHold[a]->parameter) { GHEvent *pNext = pEv->Next(); if(pEv->event == GHE_Note) { // and make sure we dont remove chords if(pHold[a]->tick != pEv->tick || pHold[a]->parameter != pEv->parameter) { pNext = noteStream.RemoveEvent(pEv); for(int i=0; i<8; ++i) { if(pHold[i] && pHold[i] != (GHEvent*)(size_t)0xFFFFFFFF) { if(pHold[i] > pEv) --pHold[i]; } } } } pEv = pNext; } } } else { // remove zero length special events... if(pHold[a] && pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF && pHold[a]->parameter == 0) { noteStream.RemoveEvent(pHold[a]); } } pHold[a] = NULL; } } }