Mercurial > hg > svapp
comparison framework/MainWindowBase.cpp @ 481:52c0aff69478 3.0-integration
Merge from branch recording
author | Chris Cannam |
---|---|
date | Thu, 20 Aug 2015 13:15:19 +0100 |
parents | 4480b031fe38 6f475df6f833 |
children | 01aeda073720 111e976f9ed4 |
comparison
equal
deleted
inserted
replaced
472:6ec35c1690c0 | 481:52c0aff69478 |
---|---|
14 */ | 14 */ |
15 | 15 |
16 #include "MainWindowBase.h" | 16 #include "MainWindowBase.h" |
17 #include "Document.h" | 17 #include "Document.h" |
18 | 18 |
19 | |
20 #include "view/Pane.h" | 19 #include "view/Pane.h" |
21 #include "view/PaneStack.h" | 20 #include "view/PaneStack.h" |
22 #include "data/model/WaveFileModel.h" | 21 #include "data/model/ReadOnlyWaveFileModel.h" |
22 #include "data/model/WritableWaveFileModel.h" | |
23 #include "data/model/SparseOneDimensionalModel.h" | 23 #include "data/model/SparseOneDimensionalModel.h" |
24 #include "data/model/NoteModel.h" | 24 #include "data/model/NoteModel.h" |
25 #include "data/model/FlexiNoteModel.h" | 25 #include "data/model/FlexiNoteModel.h" |
26 #include "data/model/Labeller.h" | 26 #include "data/model/Labeller.h" |
27 #include "data/model/TabularModel.h" | 27 #include "data/model/TabularModel.h" |
46 #include "widgets/CSVFormatDialog.h" | 46 #include "widgets/CSVFormatDialog.h" |
47 #include "widgets/ModelDataTableDialog.h" | 47 #include "widgets/ModelDataTableDialog.h" |
48 #include "widgets/InteractiveFileFinder.h" | 48 #include "widgets/InteractiveFileFinder.h" |
49 | 49 |
50 #include "audio/AudioCallbackPlaySource.h" | 50 #include "audio/AudioCallbackPlaySource.h" |
51 #include "audio/AudioRecordTarget.h" | |
51 #include "audio/PlaySpeedRangeMapper.h" | 52 #include "audio/PlaySpeedRangeMapper.h" |
53 | |
52 #include "data/fileio/DataFileReaderFactory.h" | 54 #include "data/fileio/DataFileReaderFactory.h" |
53 #include "data/fileio/PlaylistFileReader.h" | 55 #include "data/fileio/PlaylistFileReader.h" |
54 #include "data/fileio/WavFileWriter.h" | 56 #include "data/fileio/WavFileWriter.h" |
55 #include "data/fileio/MIDIFileWriter.h" | 57 #include "data/fileio/MIDIFileWriter.h" |
56 #include "data/fileio/BZipFileDevice.h" | 58 #include "data/fileio/BZipFileDevice.h" |
70 | 72 |
71 #include "data/osc/OSCQueue.h" | 73 #include "data/osc/OSCQueue.h" |
72 #include "data/midi/MIDIInput.h" | 74 #include "data/midi/MIDIInput.h" |
73 | 75 |
74 #include <bqaudioio/SystemPlaybackTarget.h> | 76 #include <bqaudioio/SystemPlaybackTarget.h> |
77 #include <bqaudioio/SystemAudioIO.h> | |
75 #include <bqaudioio/AudioFactory.h> | 78 #include <bqaudioio/AudioFactory.h> |
76 | 79 |
77 #include <QApplication> | 80 #include <QApplication> |
78 #include <QMessageBox> | 81 #include <QMessageBox> |
79 #include <QGridLayout> | 82 #include <QGridLayout> |
128 return 0; | 131 return 0; |
129 } | 132 } |
130 #undef Window | 133 #undef Window |
131 #endif | 134 #endif |
132 | 135 |
133 MainWindowBase::MainWindowBase(bool withAudioOutput, | 136 MainWindowBase::MainWindowBase(SoundOptions options) : |
134 bool withMIDIInput) : | |
135 m_document(0), | 137 m_document(0), |
136 m_paneStack(0), | 138 m_paneStack(0), |
137 m_viewManager(0), | 139 m_viewManager(0), |
138 m_timeRulerLayer(0), | 140 m_timeRulerLayer(0), |
139 m_audioOutput(withAudioOutput), | 141 m_soundOptions(options), |
140 m_playSource(0), | 142 m_playSource(0), |
143 m_recordTarget(0), | |
141 m_playTarget(0), | 144 m_playTarget(0), |
145 m_audioIO(0), | |
142 m_oscQueue(0), | 146 m_oscQueue(0), |
143 m_oscQueueStarter(0), | 147 m_oscQueueStarter(0), |
144 m_midiInput(0), | 148 m_midiInput(0), |
145 m_recentFiles("RecentFiles", 20), | 149 m_recentFiles("RecentFiles", 20), |
146 m_recentTransforms("RecentTransforms", 20), | 150 m_recentTransforms("RecentTransforms", 20), |
154 m_statusLabel(0), | 158 m_statusLabel(0), |
155 m_menuShortcutMapper(0) | 159 m_menuShortcutMapper(0) |
156 { | 160 { |
157 Profiler profiler("MainWindowBase::MainWindowBase"); | 161 Profiler profiler("MainWindowBase::MainWindowBase"); |
158 | 162 |
163 if (options & WithAudioInput) { | |
164 if (!(options & WithAudioOutput)) { | |
165 cerr << "WARNING: MainWindowBase: WithAudioInput requires WithAudioOutput -- recording will not work" << endl; | |
166 } | |
167 } | |
168 | |
159 qRegisterMetaType<sv_frame_t>("sv_frame_t"); | 169 qRegisterMetaType<sv_frame_t>("sv_frame_t"); |
160 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t"); | 170 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t"); |
161 | 171 |
162 #ifdef Q_WS_X11 | 172 #ifdef Q_WS_X11 |
163 XSetErrorHandler(handle_x11_error); | 173 XSetErrorHandler(handle_x11_error); |
215 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), | 225 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), |
216 this, SLOT(paneDeleteButtonClicked(Pane *))); | 226 this, SLOT(paneDeleteButtonClicked(Pane *))); |
217 | 227 |
218 m_playSource = new AudioCallbackPlaySource(m_viewManager, | 228 m_playSource = new AudioCallbackPlaySource(m_viewManager, |
219 QApplication::applicationName()); | 229 QApplication::applicationName()); |
230 if (m_soundOptions & WithAudioInput) { | |
231 m_recordTarget = new AudioRecordTarget(m_viewManager, | |
232 QApplication::applicationName()); | |
233 } | |
220 | 234 |
221 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)), | 235 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)), |
222 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool))); | 236 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool))); |
223 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), | 237 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), |
224 this, SLOT(audioOverloadPluginDisabled())); | 238 this, SLOT(audioOverloadPluginDisabled())); |
255 settings.endGroup(); | 269 settings.endGroup(); |
256 | 270 |
257 m_labeller = new Labeller(labellerType); | 271 m_labeller = new Labeller(labellerType); |
258 m_labeller->setCounterCycleSize(cycle); | 272 m_labeller->setCounterCycleSize(cycle); |
259 | 273 |
260 if (withMIDIInput) { | 274 if (m_soundOptions & WithMIDIInput) { |
261 m_midiInput = new MIDIInput(QApplication::applicationName(), this); | 275 m_midiInput = new MIDIInput(QApplication::applicationName(), this); |
262 } | 276 } |
263 | 277 |
264 QTimer::singleShot(1500, this, SIGNAL(hideSplash())); | 278 QTimer::singleShot(1500, this, SIGNAL(hideSplash())); |
265 } | 279 } |
267 MainWindowBase::~MainWindowBase() | 281 MainWindowBase::~MainWindowBase() |
268 { | 282 { |
269 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl; | 283 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl; |
270 delete m_playTarget; | 284 delete m_playTarget; |
271 delete m_playSource; | 285 delete m_playSource; |
286 delete m_audioIO; | |
287 delete m_recordTarget; | |
272 delete m_viewManager; | 288 delete m_viewManager; |
273 delete m_oscQueue; | 289 delete m_oscQueue; |
274 delete m_oscQueueStarter; | 290 delete m_oscQueueStarter; |
275 delete m_midiInput; | 291 delete m_midiInput; |
276 Profiles::getInstance()->dump(); | 292 Profiles::getInstance()->dump(); |
548 (haveCurrentPane && | 564 (haveCurrentPane && |
549 (currentLayer != 0)); | 565 (currentLayer != 0)); |
550 bool haveMainModel = | 566 bool haveMainModel = |
551 (getMainModel() != 0); | 567 (getMainModel() != 0); |
552 bool havePlayTarget = | 568 bool havePlayTarget = |
553 (m_playTarget != 0); | 569 (m_playTarget != 0 || m_audioIO != 0); |
554 bool haveSelection = | 570 bool haveSelection = |
555 (m_viewManager && | 571 (m_viewManager && |
556 !m_viewManager->getSelections().empty()); | 572 !m_viewManager->getSelections().empty()); |
557 bool haveCurrentEditableLayer = | 573 bool haveCurrentEditableLayer = |
558 (haveCurrentLayer && | 574 (haveCurrentLayer && |
593 emit canEditLayer(haveCurrentEditableLayer); | 609 emit canEditLayer(haveCurrentEditableLayer); |
594 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer); | 610 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer); |
595 emit canMeasureLayer(haveCurrentLayer); | 611 emit canMeasureLayer(haveCurrentLayer); |
596 emit canSelect(haveMainModel && haveCurrentPane); | 612 emit canSelect(haveMainModel && haveCurrentPane); |
597 emit canPlay(haveMainModel && havePlayTarget); | 613 emit canPlay(haveMainModel && havePlayTarget); |
614 emit canRecord(m_soundOptions & WithAudioInput); // always possible then | |
598 emit canFfwd(haveMainModel); | 615 emit canFfwd(haveMainModel); |
599 emit canRewind(haveMainModel); | 616 emit canRewind(haveMainModel); |
600 emit canPaste(haveClipboardContents); | 617 emit canPaste(haveClipboardContents); |
601 emit canInsertInstant(haveCurrentPane); | 618 emit canInsertInstant(haveCurrentPane); |
602 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); | 619 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); |
1309 rate = Preferences::getInstance()->getFixedSampleRate(); | 1326 rate = Preferences::getInstance()->getFixedSampleRate(); |
1310 } else if (Preferences::getInstance()->getResampleOnLoad()) { | 1327 } else if (Preferences::getInstance()->getResampleOnLoad()) { |
1311 rate = m_playSource->getSourceSampleRate(); | 1328 rate = m_playSource->getSourceSampleRate(); |
1312 } | 1329 } |
1313 | 1330 |
1314 WaveFileModel *newModel = new WaveFileModel(source, rate); | 1331 ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate); |
1315 | 1332 |
1316 if (!newModel->isOK()) { | 1333 if (!newModel->isOK()) { |
1317 delete newModel; | 1334 delete newModel; |
1318 m_openingAudioFile = false; | 1335 m_openingAudioFile = false; |
1319 if (source.wasCancelled()) { | 1336 if (source.wasCancelled()) { |
2156 m_recentFiles.addFile(source.getLocation()); | 2173 m_recentFiles.addFile(source.getLocation()); |
2157 return FileOpenSucceeded; | 2174 return FileOpenSucceeded; |
2158 } | 2175 } |
2159 | 2176 |
2160 void | 2177 void |
2161 MainWindowBase::createPlayTarget() | 2178 MainWindowBase::createAudioIO() |
2162 { | 2179 { |
2163 if (m_playTarget) return; | 2180 if (m_playTarget || m_audioIO) return; |
2181 | |
2182 if (!(m_soundOptions & WithAudioOutput)) return; | |
2164 | 2183 |
2165 //!!! how to handle preferences | 2184 //!!! how to handle preferences |
2166 /* | 2185 /* |
2167 QSettings settings; | 2186 QSettings settings; |
2168 settings.beginGroup("Preferences"); | 2187 settings.beginGroup("Preferences"); |
2170 settings.endGroup(); | 2189 settings.endGroup(); |
2171 AudioTargetFactory *factory = AudioTargetFactory::getInstance(); | 2190 AudioTargetFactory *factory = AudioTargetFactory::getInstance(); |
2172 | 2191 |
2173 factory->setDefaultCallbackTarget(targetName); | 2192 factory->setDefaultCallbackTarget(targetName); |
2174 */ | 2193 */ |
2175 | 2194 |
2176 m_playTarget = | 2195 if (m_soundOptions & WithAudioInput) { |
2177 breakfastquay::AudioFactory::createCallbackPlayTarget(m_playSource); | 2196 m_audioIO = breakfastquay::AudioFactory:: |
2178 | 2197 createCallbackIO(m_recordTarget, m_playSource); |
2179 m_playSource->setSystemPlaybackTarget(m_playTarget); | 2198 m_playSource->setSystemPlaybackTarget(m_audioIO); |
2180 | 2199 } else { |
2181 if (!m_playTarget) { | 2200 m_playTarget = breakfastquay::AudioFactory:: |
2201 createCallbackPlayTarget(m_playSource); | |
2202 m_playSource->setSystemPlaybackTarget(m_playTarget); | |
2203 } | |
2204 | |
2205 if (!m_playTarget && !m_audioIO) { | |
2182 emit hideSplash(); | 2206 emit hideSplash(); |
2183 | 2207 |
2184 // if (factory->isAutoCallbackTarget(targetName)) { | 2208 // if (factory->isAutoCallbackTarget(targetName)) { |
2185 QMessageBox::warning | 2209 QMessageBox::warning |
2186 (this, tr("Couldn't open audio device"), | 2210 (this, tr("Couldn't open audio device"), |
2193 tr("<b>No audio available</b><p>Failed to open your preferred audio device (\"%1\").<p>Audio playback will not be available during this session.</p>") | 2217 tr("<b>No audio available</b><p>Failed to open your preferred audio device (\"%1\").<p>Audio playback will not be available during this session.</p>") |
2194 .arg(factory->getCallbackTargetDescription(targetName)), | 2218 .arg(factory->getCallbackTargetDescription(targetName)), |
2195 QMessageBox::Ok); | 2219 QMessageBox::Ok); |
2196 } | 2220 } |
2197 */ | 2221 */ |
2222 return; | |
2198 } | 2223 } |
2199 } | 2224 } |
2200 | 2225 |
2201 WaveFileModel * | 2226 WaveFileModel * |
2202 MainWindowBase::getMainModel() | 2227 MainWindowBase::getMainModel() |
2624 } | 2649 } |
2625 | 2650 |
2626 void | 2651 void |
2627 MainWindowBase::play() | 2652 MainWindowBase::play() |
2628 { | 2653 { |
2629 if (m_playSource->isPlaying()) { | 2654 if (m_recordTarget->isRecording() || m_playSource->isPlaying()) { |
2630 stop(); | 2655 stop(); |
2656 QAction *action = qobject_cast<QAction *>(sender()); | |
2657 if (action) action->setChecked(false); | |
2631 } else { | 2658 } else { |
2632 playbackFrameChanged(m_viewManager->getPlaybackFrame()); | 2659 playbackFrameChanged(m_viewManager->getPlaybackFrame()); |
2633 m_playSource->play(m_viewManager->getPlaybackFrame()); | 2660 m_playSource->play(m_viewManager->getPlaybackFrame()); |
2634 } | 2661 } |
2662 } | |
2663 | |
2664 void | |
2665 MainWindowBase::record() | |
2666 { | |
2667 if (!(m_soundOptions & WithAudioInput)) { | |
2668 return; | |
2669 } | |
2670 | |
2671 if (!m_recordTarget) { | |
2672 //!!! report | |
2673 return; | |
2674 } | |
2675 | |
2676 if (!m_audioIO) { | |
2677 createAudioIO(); | |
2678 } | |
2679 | |
2680 if (m_recordTarget->isRecording()) { | |
2681 m_recordTarget->stopRecording(); | |
2682 emit audioFileLoaded(); | |
2683 return; | |
2684 } | |
2685 | |
2686 WritableWaveFileModel *model = m_recordTarget->startRecording(); | |
2687 if (!model) { | |
2688 cerr << "ERROR: MainWindowBase::record: Recording failed" << endl; | |
2689 //!!! report | |
2690 return; | |
2691 } | |
2692 | |
2693 if (!model->isOK()) { | |
2694 m_recordTarget->stopRecording(); | |
2695 delete model; | |
2696 //!!! ??? | |
2697 return; | |
2698 } | |
2699 | |
2700 PlayParameterRepository::getInstance()->addPlayable(model); | |
2701 | |
2702 if (!getMainModel()) { | |
2703 | |
2704 //!!! duplication with openAudio here | |
2705 | |
2706 QString templateName = getDefaultSessionTemplate(); | |
2707 bool loadedTemplate = false; | |
2708 | |
2709 if (templateName != "") { | |
2710 FileOpenStatus tplStatus = openSessionTemplate(templateName); | |
2711 if (tplStatus == FileOpenCancelled) { | |
2712 return; | |
2713 } | |
2714 if (tplStatus != FileOpenFailed) { | |
2715 loadedTemplate = true; | |
2716 } | |
2717 } | |
2718 | |
2719 if (!loadedTemplate) { | |
2720 closeSession(); | |
2721 createDocument(); | |
2722 } | |
2723 | |
2724 Model *prevMain = getMainModel(); | |
2725 if (prevMain) { | |
2726 m_playSource->removeModel(prevMain); | |
2727 PlayParameterRepository::getInstance()->removePlayable(prevMain); | |
2728 } | |
2729 | |
2730 m_document->setMainModel(model); | |
2731 setupMenus(); | |
2732 | |
2733 if (loadedTemplate || (m_sessionFile == "")) { | |
2734 //!!! shouldn't be dealing directly with title from here -- call a method | |
2735 setWindowTitle(tr("%1: %2") | |
2736 .arg(QApplication::applicationName()) | |
2737 .arg(model->getLocation())); | |
2738 CommandHistory::getInstance()->clear(); | |
2739 CommandHistory::getInstance()->documentSaved(); | |
2740 m_documentModified = false; | |
2741 } else { | |
2742 setWindowTitle(tr("%1: %2 [%3]") | |
2743 .arg(QApplication::applicationName()) | |
2744 .arg(QFileInfo(m_sessionFile).fileName()) | |
2745 .arg(model->getLocation())); | |
2746 if (m_documentModified) { | |
2747 m_documentModified = false; | |
2748 documentModified(); // so as to restore "(modified)" window title | |
2749 } | |
2750 } | |
2751 | |
2752 } else { | |
2753 | |
2754 CommandHistory::getInstance()->startCompoundOperation | |
2755 (tr("Import Recorded Audio"), true); | |
2756 | |
2757 m_document->addImportedModel(model); | |
2758 | |
2759 AddPaneCommand *command = new AddPaneCommand(this); | |
2760 CommandHistory::getInstance()->addCommand(command); | |
2761 | |
2762 Pane *pane = command->getPane(); | |
2763 | |
2764 if (m_timeRulerLayer) { | |
2765 m_document->addLayerToView(pane, m_timeRulerLayer); | |
2766 } | |
2767 | |
2768 Layer *newLayer = m_document->createImportedLayer(model); | |
2769 | |
2770 if (newLayer) { | |
2771 m_document->addLayerToView(pane, newLayer); | |
2772 } | |
2773 | |
2774 CommandHistory::getInstance()->endCompoundOperation(); | |
2775 } | |
2776 | |
2777 updateMenuStates(); | |
2778 m_recentFiles.addFile(model->getLocation()); | |
2779 currentPaneChanged(m_paneStack->getCurrentPane()); | |
2635 } | 2780 } |
2636 | 2781 |
2637 void | 2782 void |
2638 MainWindowBase::ffwd() | 2783 MainWindowBase::ffwd() |
2639 { | 2784 { |
2860 } | 3005 } |
2861 | 3006 |
2862 void | 3007 void |
2863 MainWindowBase::stop() | 3008 MainWindowBase::stop() |
2864 { | 3009 { |
3010 if (m_recordTarget->isRecording()) { | |
3011 m_recordTarget->stopRecording(); | |
3012 emit audioFileLoaded(); | |
3013 } | |
3014 | |
2865 m_playSource->stop(); | 3015 m_playSource->stop(); |
2866 | 3016 |
2867 if (m_paneStack && m_paneStack->getCurrentPane()) { | 3017 if (m_paneStack && m_paneStack->getCurrentPane()) { |
2868 updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); | 3018 updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); |
2869 } else { | 3019 } else { |
3330 MainWindowBase::mainModelChanged(WaveFileModel *model) | 3480 MainWindowBase::mainModelChanged(WaveFileModel *model) |
3331 { | 3481 { |
3332 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl; | 3482 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl; |
3333 updateDescriptionLabel(); | 3483 updateDescriptionLabel(); |
3334 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); | 3484 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); |
3335 if (model && !m_playTarget && m_audioOutput) { | 3485 if (model && !(m_playTarget || m_audioIO) && |
3336 createPlayTarget(); | 3486 (m_soundOptions & WithAudioOutput)) { |
3487 createAudioIO(); | |
3337 } | 3488 } |
3338 } | 3489 } |
3339 | 3490 |
3340 void | 3491 void |
3341 MainWindowBase::modelAboutToBeDeleted(Model *model) | 3492 MainWindowBase::modelAboutToBeDeleted(Model *model) |