// MIDI clip export method. bool qtractorMidiClip::clipExport ( ClipExport pfnClipExport, void *pvArg, unsigned long iOffset, unsigned long iLength ) const { qtractorTrack *pTrack = track(); if (pTrack == NULL) return false; qtractorSession *pSession = pTrack->session(); if (pSession == NULL) return false; qtractorMidiSequence *pSeq = sequence(); if (pSeq == NULL) return false; if (iLength < 1) iLength = clipLength(); const unsigned short iTicksPerBeat = pSession->ticksPerBeat(); const unsigned long iTimeOffset = pSeq->timeOffset(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(clipStart()); const unsigned long t0 = pNode->tickFromFrame(clipStart()); unsigned long f1 = clipStart() + clipOffset() + iOffset; pNode = cursor.seekFrame(f1); const unsigned long t1 = pNode->tickFromFrame(f1); unsigned long iTimeStart = t1 - t0; iTimeStart = (iTimeStart > iTimeOffset ? iTimeStart - iTimeOffset : 0); pNode = cursor.seekFrame(f1 += iLength); const unsigned long iTimeEnd = iTimeStart + pNode->tickFromFrame(f1) - t1; qtractorMidiSequence seq(pSeq->name(), pSeq->channel(), iTicksPerBeat); seq.setBank(pTrack->midiBank()); seq.setProg(pTrack->midiProg()); for (qtractorMidiEvent *pEvent = pSeq->events().first(); pEvent; pEvent = pEvent->next()) { const unsigned long iTime = pEvent->time(); if (iTime >= iTimeStart && iTime < iTimeEnd) { qtractorMidiEvent *pNewEvent = new qtractorMidiEvent(*pEvent); pNewEvent->setTime(iTime - iTimeStart); if (pNewEvent->type() == qtractorMidiEvent::NOTEON) { pNewEvent->setVelocity((unsigned char) (clipGain() * float(pNewEvent->velocity())) & 0x7f); if (iTime + pEvent->duration() > iTimeEnd) pNewEvent->setDuration(iTimeEnd - iTime); } seq.insertEvent(pNewEvent); } } (*pfnClipExport)(&seq, pvArg); return true; }
// Clip close-commit (record specific) void qtractorMidiClip::close (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::close(%d)\n", this); #endif qtractorTrack *pTrack = track(); if (pTrack == NULL) return; qtractorSession *pSession = pTrack->session(); if (pSession == NULL) return; // Take pretended clip-length... unsigned long iClipLength = clipLength(); qtractorMidiSequence *pSeq = sequence(); if (pSeq) { if (iClipLength > 0) pSeq->setTimeLength(pSession->tickFromFrame(iClipLength)); // Final read statistics... m_noteMin = pSeq->noteMin(); m_noteMax = pSeq->noteMax(); // Actual sequence closure... pSeq->close(); // Commit the final clip length... if (iClipLength < 1) { iClipLength = pSession->frameFromTick(pSeq->duration()); setClipLength(iClipLength); } } // Now's time to write the whole thing... const bool bNewFile = (m_pFile && m_pFile->mode() == qtractorMidiFile::Write); if (bNewFile && iClipLength > 0 && pSeq) { // Write channel tracks... if (m_iFormat == 1) m_pFile->writeTrack(NULL); // Setup track (SMF format 1). m_pFile->writeTrack(pSeq); // Channel track. m_pFile->close(); } // Sure close MIDI clip editor if any... if (m_pMidiEditorForm) { m_pMidiEditorForm->close(); delete m_pMidiEditorForm; m_pMidiEditorForm = NULL; } // Just to be sure things get deallocated.. closeMidiFile(); // If proven empty, remove the file. if (bNewFile && iClipLength < 1) QFile::remove(filename()); }
// returns the duration of the source clip TTimeIntervalMicroSeconds CMMFRawFormatRead::Duration(TMediaId aMediaId) const { if ((aMediaId.iMediaType == KUidMediaTypeAudio) && (iClipLength) && (iSampleRate) && (iBitsPerSample) && (iChannels)) {//we have enough values to calculate the duration TInt64 clipLength(iClipLength); clipLength*=KOneSecondInMicroSeconds; TTimeIntervalMicroSeconds duration = TTimeIntervalMicroSeconds(clipLength/iSampleRate); duration = TTimeIntervalMicroSeconds(duration.Int64()/(iBitsPerSample*iChannels)); duration = TTimeIntervalMicroSeconds(duration.Int64()*8); return duration; } else return TTimeIntervalMicroSeconds(0); }
// Clip editor update. void qtractorMidiClip::updateEditor ( bool bSelectClear ) { if (m_pMidiEditorForm == NULL) return; qtractorMidiEditor *pMidiEditor = m_pMidiEditorForm->editor(); if (pMidiEditor) { pMidiEditor->reset(bSelectClear); pMidiEditor->setOffset(clipStart()); pMidiEditor->setLength(clipLength()); qtractorTrack *pTrack = track(); if (pTrack) { pMidiEditor->setForeground(pTrack->foreground()); pMidiEditor->setBackground(pTrack->background()); } pMidiEditor->updateContents(); } m_pMidiEditorForm->resetDirtyCount(); m_pMidiEditorForm->updateInstrumentNames(); m_pMidiEditorForm->stabilizeForm(); }
/* ** SQL Trace callback */ static void sqlTraceCallback(void *NotUsed1, const char *zSql){ UNUSED_PARAMETER(NotUsed1); logMessage("[%.*s]", clipLength(zSql), zSql); }
// The main use method. bool qtractorMidiClip::openMidiFile ( const QString& sFilename, int iTrackChannel, int iMode ) { closeMidiFile(); qtractorTrack *pTrack = track(); if (pTrack == NULL) return false; qtractorSession *pSession = pTrack->session(); if (pSession == NULL) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::openMidiFile(\"%s\", %d, %d)", this, sFilename.toUtf8().constData(), iTrackChannel, iMode); #endif // Check file primordial state... const bool bWrite = (iMode & qtractorMidiFile::Write); // Set local properties... setFilename(sFilename); setDirty(false); // Register file path... pSession->files()->addClipItem(qtractorFileList::Midi, this, bWrite); // New key-data sequence... if (!bWrite) { m_pKey = new Key(this); m_pData = g_hashTable.value(*m_pKey, NULL); if (m_pData) { m_pData->attach(this); qtractorMidiSequence *pSeq = m_pData->sequence(); // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(pSeq->name()); if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Uh oh... m_playCursor.reset(pSeq); m_drawCursor.reset(pSeq); return true; } } // Create and open up the real MIDI file... m_pFile = new qtractorMidiFile(); if (!m_pFile->open(sFilename, iMode)) { delete m_pFile; m_pFile = NULL; return false; } // Initialize MIDI event container... m_pData = new Data(); m_pData->attach(this); qtractorMidiSequence *pSeq = m_pData->sequence(); pSeq->clear(); pSeq->setTicksPerBeat(pSession->ticksPerBeat()); const unsigned long iClipStart = clipStart(); const unsigned long iClipOffset = clipOffset(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(iClipStart); const unsigned long t0 = pNode->tickFromFrame(iClipStart); const unsigned long iClipOffset2 = iClipStart + iClipOffset; // pNode = cursor.seekFrame(iClipOffset2); pSeq->setTimeOffset(pNode->tickFromFrame(iClipOffset2) - t0); const unsigned long iClipLength = clipLength(); const unsigned long iClipEnd = iClipStart + iClipLength; pNode = cursor.seekFrame(iClipEnd); pSeq->setTimeLength(pNode->tickFromFrame(iClipEnd) - t0); // Initial statistics... pSeq->setNoteMin(m_noteMin); pSeq->setNoteMax(m_noteMax); // Are we on a pre-writing status? if (bWrite) { // On write mode, iTrackChannel holds the SMF format, // so we'll convert it here as properly. const unsigned short iFormat = qtractorMidiClip::defaultFormat(); unsigned short iTracks = 1; if (iFormat == 1) { // SMF format 1 (2 tracks, 1 channel) iTrackChannel = 1; ++iTracks; } // That's it. setFormat(iFormat); // Write SMF header... if (m_pFile->writeHeader(iFormat, iTracks, pSeq->ticksPerBeat())) { // Set initial local properties... if (m_pFile->tempoMap()) { m_pFile->tempoMap()->fromTimeScale( pSession->timeScale(), pSeq->timeOffset()); } } // And initial clip name... pSeq->setName(shortClipName(QFileInfo(m_pFile->filename()).baseName())); pSeq->setChannel(pTrack->midiChannel()); // Nothing more as for writing... } else { // On read mode, SMF format is properly given by open file. setFormat(m_pFile->format()); // Read the event sequence in... m_pFile->readTrack(pSeq, iTrackChannel); // For immediate feedback, once... m_noteMin = pSeq->noteMin(); m_noteMax = pSeq->noteMax(); // FIXME: On demand, set session time properties from MIDI file... if (m_bSessionFlag) { #if 0 // Import eventual SysEx setup... // - take care that given track might not be currently open, // so that we'll resolve MIDI output bus somehow... qtractorMidiBus *pMidiBus = NULL; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { pMidiBus = static_cast<qtractorMidiBus *> ( pMidiEngine->findOutputBus(pTrack->outputBusName())); if (pMidiBus == NULL) { for (qtractorBus *pBus = pMidiEngine->buses().first(); pBus; pBus = pBus->next()) { if (pBus->busMode() & qtractorBus::Output) { pMidiBus = static_cast<qtractorMidiBus *> (pBus); break; } } } } // Import eventual SysEx setup... if (pMidiBus) pMidiBus->importSysexList(pSeq); #endif // Import tempo map as well... qtractorMidiFileTempo *pTempoMap = m_pFile->tempoMap(); if (pTempoMap) { pTempoMap->intoTimeScale(pSession->timeScale(), t0); pSession->updateTimeScale(); } // Reset session flag now. m_bSessionFlag = false; } // We should have events, otherwise this clip is of no use... //if (m_pSeq->events().count() < 1) // return false; } // Actual track-channel is set by now... setTrackChannel(iTrackChannel); // Make it a brand new revision... // setRevision(1); // Default clip length will be whole sequence duration. if (iClipLength == 0) { const unsigned long t1 = t0 + pSeq->timeLength(); setClipLength(pSession->frameFromTick(t1) - iClipStart); } // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(pSeq->name()); if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Uh oh... m_playCursor.reset(pSeq); m_drawCursor.reset(pSeq); // Something might have changed... updateHashKey(); insertHashKey(); // Update/reset MIDI clip editor if any... if (m_pMidiEditorForm) m_pMidiEditorForm->setup(this); return true; }