📄 sequencemanager.cpp
字号:
// won't actually be added to the composition until the // first recorded event arrives. We don't have to do this // from here for audio, because for audio the sequencer // calls back on createRecordAudioFiles so as to find out // what files it needs to write to. /*m_doc->addRecordMIDISegment(recordMIDITrack);*/ for (Composition::recordtrackcontainer::const_iterator i = comp.getRecordTracks().begin(); i != comp.getRecordTracks().end(); ++i) { InstrumentId iid = comp.getTrackById(*i)->getInstrument(); Instrument *inst = studio.getInstrumentById(iid); if (inst && (inst->getType() != Instrument::Audio)) { SEQMAN_DEBUG << "SequenceManager: mdoc->addRecordMIDISegment(" << *i << ")" << endl; m_doc->addRecordMIDISegment(*i); } } } // set the buttons m_transport->RecordButton()->setOn(true); m_transport->PlayButton()->setOn(true); if (comp.getCurrentTempo() == 0) { SEQMAN_DEBUG << "SequenceManager::play() - setting Tempo to Default value of 120.000\n"; comp.setCompositionDefaultTempo(comp.getTempoForQpm(120.0)); } else { SEQMAN_DEBUG << "SequenceManager::record() - starting to record\n"; } // set the tempo in the transport // m_transport->setTempo(comp.getCurrentTempo()); // The arguments for the Sequencer - record is similar to playback, // we must being playing to record. // RealTime startPos = comp.getElapsedRealTime(comp.getPosition()); bool lowLat = config->readBoolEntry("audiolowlatencymonitoring", true); if (lowLat != m_lastLowLatencySwitchSent) { QByteArray data; QDataStream streamOut(data, IO_WriteOnly); streamOut << lowLat; rgapp->sequencerSend("setLowLatencyMode(bool)", data); m_lastLowLatencySwitchSent = lowLat; } QByteArray data; QCString replyType; QByteArray replyData; QDataStream streamOut(data, IO_WriteOnly); // playback start position streamOut << (long)startPos.sec; streamOut << (long)startPos.nsec; // Apart from perhaps the small file size, I think with hindsight // that these options are more easily set to reasonable defaults // here than left to the user. Mostly. //!!! Duplicates code in play() //!!! need some cleverness somewhere to ensure the read-ahead //is larger than the JACK period size if (lowLat) { streamOut << 0L; // read-ahead sec streamOut << 160000000L; // read-ahead nsec streamOut << 0L; // audio mix sec streamOut << 60000000L; // audio mix nsec: ignored in lowlat mode streamOut << 2L; // audio read sec streamOut << 500000000L; // audio read nsec streamOut << 4L; // audio write sec streamOut << 0L; // audio write nsec streamOut << 256L; // cacheable small file size in K } else { streamOut << 0L; // read-ahead sec streamOut << 500000000L; // read-ahead nsec streamOut << 0L; // audio mix sec streamOut << 400000000L; // audio mix nsec streamOut << 2L; // audio read sec streamOut << 500000000L; // audio read nsec streamOut << 4L; // audio write sec streamOut << 0L; // audio write nsec streamOut << 256L; // cacheable small file size in K } // record type streamOut << (long)STARTING_TO_RECORD; // Send Play to the Sequencer if (!rgapp->sequencerCall("record(long int, long int, long int, long int, long int, long int, long int, long int, long int, long int, long int, long int)", replyType, replyData, data)) { // failed m_transportStatus = STOPPED; return ; } // ensure the return type is ok QDataStream streamIn(replyData, IO_ReadOnly); int result; streamIn >> result; if (result) { // completed successfully m_transportStatus = STARTING_TO_RECORD; // Create the countdown timer dialog to show recording time // remaining. (Note (dmm) this has changed, and it now reports // the time remaining during both MIDI and audio recording.) // timeT p = comp.getPosition(); timeT d = comp.getEndMarker(); // end marker less current position == available duration d -= p; // set seconds to total possible time, initially RealTime rtd = comp.getElapsedRealTime(d); int seconds = rtd.sec; // re-initialise m_countdownDialog->setTotalTime(seconds); // Create the timer // m_recordTime->start(); // Start an elapse timer for updating the dialog - // it will fire every second. // m_countdownTimer->start(1000); // Pop-up the dialog (don't use exec()) // // bug #1505805, abolish recording countdown dialog //m_countdownDialog->show(); } else { // Stop immediately - turn off buttons in parent // m_transportStatus = STOPPED; if (haveAudioInstrument) { throw(Exception("Couldn't start recording audio.\nPlease set a valid file path in the Document Properties\n(Composition menu -> Edit Document Properties -> Audio).")); } } }}voidSequenceManager::processAsynchronousMidi(const MappedComposition &mC, AudioManagerDialog *audioManagerDialog){ static bool boolShowingWarning = false; static bool boolShowingALSAWarning = false; static long warningShownAt = 0; if (m_doc == 0 || mC.size() == 0) return ; MappedComposition::const_iterator i; // Thru filtering is done at the sequencer for the actual sound // output, but here we need both filtered (for OUT display) and // unfiltered (for insertable note callbacks) compositions, so // we've received the unfiltered copy and will filter here MappedComposition tempMC = applyFiltering(mC, MappedEvent::MappedEventType( m_doc->getStudio().getMIDIThruFilter())); // send to the MIDI labels (which can only hold one event at a time) i = mC.begin(); if (i != mC.end()) { m_transport->setMidiInLabel(*i); } i = tempMC.begin(); while (i != tempMC.end()) { if ((*i)->getRecordedDevice() != Device::CONTROL_DEVICE) { m_transport->setMidiOutLabel(*i); break; } ++i; } for (i = mC.begin(); i != mC.end(); ++i ) { if ((*i)->getType() >= MappedEvent::Audio) { if ((*i)->getType() == MappedEvent::AudioStopped) { /* SEQMAN_DEBUG << "AUDIO FILE ID = " << int((*i)->getData1()) << " - FILE STOPPED - " << "INSTRUMENT = " << (*i)->getInstrument() << endl; */ if (audioManagerDialog && (*i)->getInstrument() == m_doc->getStudio().getAudioPreviewInstrument()) { audioManagerDialog-> closePlayingDialog( AudioFileId((*i)->getData1())); } } if ((*i)->getType() == MappedEvent::AudioLevel) sendAudioLevel(*i); if ((*i)->getType() == MappedEvent::AudioGeneratePreview) { SEQMAN_DEBUG << "Received AudioGeneratePreview: data1 is " << int((*i)->getData1()) << ", data2 " << int((*i)->getData2()) << ", instrument is " << (*i)->getInstrument() << endl; m_doc->finalizeAudioFile((int)(*i)->getData1() + (int)(*i)->getData2() * 256); } if ((*i)->getType() == MappedEvent::SystemUpdateInstruments) { // resync Devices and Instruments // m_doc->syncDevices(); /*KConfig* config = kapp->config(); config->setGroup(SequencerOptionsConfigGroup); QString recordDeviceStr = config->readEntry("midirecorddevice"); sendMIDIRecordingDevice(recordDeviceStr);*/ restoreRecordSubscriptions(); } if (m_transportStatus == PLAYING || m_transportStatus == RECORDING) { if ((*i)->getType() == MappedEvent::SystemFailure) { SEQMAN_DEBUG << "Failure of some sort..." << endl; bool handling = true; /* These are the ones that we always report or handle. */ if ((*i)->getData1() == MappedEvent::FailureJackDied) { // Something horrible has happened to JACK or we got // bumped out of the graph. Either way stop playback. // stopping(); } else if ((*i)->getData1() == MappedEvent::FailureJackRestartFailed) { KMessageBox::error( dynamic_cast<QWidget*>(m_doc->parent())->parentWidget(), i18n("The JACK Audio subsystem has failed or it has stopped Rosegarden from processing audio.\nPlease restart Rosegarden to continue working with audio.\nQuitting other running applications may improve Rosegarden's performance.")); } else if ((*i)->getData1() == MappedEvent::FailureJackRestart) { KMessageBox::error( dynamic_cast<QWidget*>(m_doc->parent())->parentWidget(), i18n("The JACK Audio subsystem has stopped Rosegarden from processing audio, probably because of a processing overload.\nAn attempt to restart the audio service has been made, but some problems may remain.\nQuitting other running applications may improve Rosegarden's performance.")); } else if ((*i)->getData1() == MappedEvent::FailureCPUOverload) {#define REPORT_CPU_OVERLOAD 1#ifdef REPORT_CPU_OVERLOAD stopping(); KMessageBox::error( dynamic_cast<QWidget*>(m_doc->parent())->parentWidget(), i18n("Run out of processor power for real-time audio processing. Cannot continue."));#endif } else { handling = false; } if (handling) continue; if (!m_canReport) { SEQMAN_DEBUG << "Not reporting it to user just yet" << endl; continue; } if ((*i)->getData1() == MappedEvent::FailureALSACallFailed) { struct timeval tv; (void)gettimeofday(&tv, 0); if (tv.tv_sec - warningShownAt >= 5 && !boolShowingALSAWarning) { QString message = i18n("A serious error has occurred in the ALSA MIDI subsystem. It may not be possible to continue sequencing. Please check console output for more information."); boolShowingALSAWarning = true; KMessageBox::information(0, message); boolShowingALSAWarning = false; (void)gettimeofday(&tv, 0); warningShownAt = tv.tv_sec; } } else if ((*i)->getData1() == MappedEvent::FailureXRuns) { //#define REPORT_XRUNS 1#ifdef REPORT_XRUNS struct timeval tv; (void)gettimeofday(&tv, 0); if (tv.tv_sec - warningShownAt >= 5 && !boolShowingWarning) { QString message = i18n("JACK Audio subsystem is losing sample frames."); boolShowingWarning = true; KMessageBox::information(0, message); boolShowingWarning = false; (void)gettimeofday(&tv, 0); warningShownAt = tv.tv_sec; }#endif } else if (!m_shownOverrunWarning) { QString message; switch ((*i)->getData1()) { case MappedEvent::FailureDiscUnderrun: message = i18n("Failed to read audio data from disc in time to service the audio subsystem."); break; case MappedEvent::FailureDiscOverrun: message = i18n("Failed to write audio data to disc fast enough to service the audio subsystem."); break; case MappedEvent::FailureBussMixUnderrun: message = i18n("The audio mixing subsystem is failing to keep up."); break; case MappedEvent::FailureMixUnderrun: message = i18n("The audio subsystem is failing to keep up."); break; default: message = i18n("Unknown sequencer failure mode!"); break; } m_shownOverrunWarning = true;#ifdef REPORT_XRUNS KMessageBox::information(0, message);#else if ((*i)->getData1() == MappedEvent::FailureDiscOverrun) { // the error you can't hear KMessageBox::information(0, message); } else { std::cerr << message << std::endl; }#endif } // Turn off the report flag and set off a one-shot // timer for 5 seconds. // if (!m_reportTimer->isActive()) { m_canReport = false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -