comparison framework/MainWindowBase.cpp @ 548:baa11365ebdd bqaudioio

Merge from branch bqresample
author Chris Cannam
date Wed, 07 Dec 2016 11:51:42 +0000
parents 82d7e5cf7517 0d5c3abc9658
children b9d8c7a690d6
comparison
equal deleted inserted replaced
547:82d7e5cf7517 548:baa11365ebdd
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"
57 #include "data/fileio/FileSource.h" 59 #include "data/fileio/FileSource.h"
58 #include "data/fileio/AudioFileReaderFactory.h" 60 #include "data/fileio/AudioFileReaderFactory.h"
59 #include "rdf/RDFImporter.h" 61 #include "rdf/RDFImporter.h"
60 62
61 #include "data/fft/FFTDataServer.h"
62
63 #include "base/RecentFiles.h" 63 #include "base/RecentFiles.h"
64 64
65 #include "base/PlayParameterRepository.h" 65 #include "base/PlayParameterRepository.h"
66 #include "base/XmlExportable.h" 66 #include "base/XmlExportable.h"
67 #include "base/Profiler.h" 67 #include "base/Profiler.h"
72 72
73 #include "data/osc/OSCQueue.h" 73 #include "data/osc/OSCQueue.h"
74 #include "data/midi/MIDIInput.h" 74 #include "data/midi/MIDIInput.h"
75 75
76 #include <bqaudioio/SystemPlaybackTarget.h> 76 #include <bqaudioio/SystemPlaybackTarget.h>
77 #include <bqaudioio/SystemAudioIO.h>
77 #include <bqaudioio/AudioFactory.h> 78 #include <bqaudioio/AudioFactory.h>
78 79
79 #include <QApplication> 80 #include <QApplication>
80 #include <QMessageBox> 81 #include <QMessageBox>
81 #include <QGridLayout> 82 #include <QGridLayout>
130 return 0; 131 return 0;
131 } 132 }
132 #undef Window 133 #undef Window
133 #endif 134 #endif
134 135
135 MainWindowBase::MainWindowBase(bool withAudioOutput, 136 MainWindowBase::MainWindowBase(SoundOptions options) :
136 bool withMIDIInput) :
137 m_document(0), 137 m_document(0),
138 m_paneStack(0), 138 m_paneStack(0),
139 m_viewManager(0), 139 m_viewManager(0),
140 m_timeRulerLayer(0), 140 m_timeRulerLayer(0),
141 m_audioOutput(withAudioOutput), 141 m_soundOptions(options),
142 m_playSource(0), 142 m_playSource(0),
143 m_recordTarget(0),
143 m_playTarget(0), 144 m_playTarget(0),
145 m_audioIO(0),
144 m_oscQueue(0), 146 m_oscQueue(0),
145 m_oscQueueStarter(0), 147 m_oscQueueStarter(0),
146 m_midiInput(0), 148 m_midiInput(0),
147 m_recentFiles("RecentFiles", 20), 149 m_recentFiles("RecentFiles", 20),
148 m_recentTransforms("RecentTransforms", 20), 150 m_recentTransforms("RecentTransforms", 20),
151 m_abandoning(false), 153 m_abandoning(false),
152 m_labeller(0), 154 m_labeller(0),
153 m_lastPlayStatusSec(0), 155 m_lastPlayStatusSec(0),
154 m_initialDarkBackground(false), 156 m_initialDarkBackground(false),
155 m_defaultFfwdRwdStep(2, 0), 157 m_defaultFfwdRwdStep(2, 0),
158 m_audioRecordMode(RecordCreateAdditionalModel),
156 m_statusLabel(0), 159 m_statusLabel(0),
160 m_iconsVisibleInMenus(true),
157 m_menuShortcutMapper(0) 161 m_menuShortcutMapper(0)
158 { 162 {
159 Profiler profiler("MainWindowBase::MainWindowBase"); 163 Profiler profiler("MainWindowBase::MainWindowBase");
160 164
165 if (options & WithAudioInput) {
166 if (!(options & WithAudioOutput)) {
167 cerr << "WARNING: MainWindowBase: WithAudioInput requires WithAudioOutput -- recording will not work" << endl;
168 }
169 }
170
161 qRegisterMetaType<sv_frame_t>("sv_frame_t"); 171 qRegisterMetaType<sv_frame_t>("sv_frame_t");
162 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t"); 172 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t");
163 173
164 #ifdef Q_WS_X11 174 #ifdef Q_WS_X11
165 XSetErrorHandler(handle_x11_error); 175 XSetErrorHandler(handle_x11_error);
185 settings.beginGroup("Preferences"); 195 settings.beginGroup("Preferences");
186 viewFontSize = settings.value("view-font-size", viewFontSize).toInt(); 196 viewFontSize = settings.value("view-font-size", viewFontSize).toInt();
187 settings.setValue("view-font-size", viewFontSize); 197 settings.setValue("view-font-size", viewFontSize);
188 settings.endGroup(); 198 settings.endGroup();
189 199
200 #ifdef NOT_DEFINED // This no longer works correctly on any platform AFAICS
190 Preferences::BackgroundMode mode = 201 Preferences::BackgroundMode mode =
191 Preferences::getInstance()->getBackgroundMode(); 202 Preferences::getInstance()->getBackgroundMode();
192 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground(); 203 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
193 if (mode != Preferences::BackgroundFromTheme) { 204 if (mode != Preferences::BackgroundFromTheme) {
194 m_viewManager->setGlobalDarkBackground 205 m_viewManager->setGlobalDarkBackground
195 (mode == Preferences::DarkBackground); 206 (mode == Preferences::DarkBackground);
196 } 207 }
208 #endif
197 209
198 m_paneStack = new PaneStack(0, m_viewManager); 210 m_paneStack = new PaneStack(0, m_viewManager);
199 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), 211 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
200 this, SLOT(currentPaneChanged(Pane *))); 212 this, SLOT(currentPaneChanged(Pane *)));
201 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), 213 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
217 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)), 229 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)),
218 this, SLOT(paneDeleteButtonClicked(Pane *))); 230 this, SLOT(paneDeleteButtonClicked(Pane *)));
219 231
220 m_playSource = new AudioCallbackPlaySource(m_viewManager, 232 m_playSource = new AudioCallbackPlaySource(m_viewManager,
221 QApplication::applicationName()); 233 QApplication::applicationName());
234 if (m_soundOptions & WithAudioInput) {
235 m_recordTarget = new AudioRecordTarget(m_viewManager,
236 QApplication::applicationName());
237 connect(m_recordTarget, SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)),
238 this, SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t)));
239 }
222 240
223 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)), 241 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)),
224 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool))); 242 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)));
225 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()), 243 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
226 this, SLOT(audioOverloadPluginDisabled())); 244 this, SLOT(audioOverloadPluginDisabled()));
257 settings.endGroup(); 275 settings.endGroup();
258 276
259 m_labeller = new Labeller(labellerType); 277 m_labeller = new Labeller(labellerType);
260 m_labeller->setCounterCycleSize(cycle); 278 m_labeller->setCounterCycleSize(cycle);
261 279
262 if (withMIDIInput) { 280 if (m_soundOptions & WithMIDIInput) {
263 m_midiInput = new MIDIInput(QApplication::applicationName(), this); 281 m_midiInput = new MIDIInput(QApplication::applicationName(), this);
264 } 282 }
265 283
266 QTimer::singleShot(1500, this, SIGNAL(hideSplash())); 284 QTimer::singleShot(1500, this, SIGNAL(hideSplash()));
267 } 285 }
268 286
269 MainWindowBase::~MainWindowBase() 287 MainWindowBase::~MainWindowBase()
270 { 288 {
271 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl; 289 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl;
290
291 // We have to delete the breakfastquay::SystemPlaybackTarget or
292 // breakfastquay::SystemAudioIO object (whichever we have -- it
293 // depends on whether we handle recording or not) before we delete
294 // the ApplicationPlaybackSource and ApplicationRecordTarget that
295 // they refer to.
296
297 // First prevent this trying to call target.
298 if (m_playSource) m_playSource->setSystemPlaybackTarget(0);
299
300 // Then delete the breakfastquay::System object.
301 // Only one of these two exists!
302 delete m_audioIO;
272 delete m_playTarget; 303 delete m_playTarget;
304
305 // Then delete the Application objects.
273 delete m_playSource; 306 delete m_playSource;
307 delete m_recordTarget;
308
274 delete m_viewManager; 309 delete m_viewManager;
275 delete m_oscQueue; 310 delete m_oscQueue;
276 delete m_oscQueueStarter; 311 delete m_oscQueueStarter;
277 delete m_midiInput; 312 delete m_midiInput;
278 Profiles::getInstance()->dump(); 313 Profiles::getInstance()->dump();
313 if (menu) finaliseMenu(menu); 348 if (menu) finaliseMenu(menu);
314 } 349 }
315 } 350 }
316 351
317 void 352 void
318 MainWindowBase::finaliseMenu(QMenu * 353 MainWindowBase::finaliseMenu(QMenu *menu)
319 #ifdef Q_OS_MAC 354 {
320 menu 355 foreach (QAction *a, menu->actions()) {
321 #endif 356 a->setIconVisibleInMenu(m_iconsVisibleInMenus);
322 ) 357 }
323 { 358
324 #ifdef Q_OS_MAC 359 #ifdef Q_OS_MAC
325 // See https://bugreports.qt-project.org/browse/QTBUG-38256 and 360 // See https://bugreports.qt-project.org/browse/QTBUG-38256 and
326 // our issue #890 http://code.soundsoftware.ac.uk/issues/890 -- 361 // our issue #890 http://code.soundsoftware.ac.uk/issues/890 --
327 // single-key shortcuts that are associated only with a menu 362 // single-key shortcuts that are associated only with a menu
328 // action (and not with a toolbar button) do not work with Qt 5.x 363 // action (and not with a toolbar button) do not work with Qt 5.x
550 (haveCurrentPane && 585 (haveCurrentPane &&
551 (currentLayer != 0)); 586 (currentLayer != 0));
552 bool haveMainModel = 587 bool haveMainModel =
553 (getMainModel() != 0); 588 (getMainModel() != 0);
554 bool havePlayTarget = 589 bool havePlayTarget =
555 (m_playTarget != 0); 590 (m_playTarget != 0 || m_audioIO != 0);
556 bool haveSelection = 591 bool haveSelection =
557 (m_viewManager && 592 (m_viewManager &&
558 !m_viewManager->getSelections().empty()); 593 !m_viewManager->getSelections().empty());
559 bool haveCurrentEditableLayer = 594 bool haveCurrentEditableLayer =
560 (haveCurrentLayer && 595 (haveCurrentLayer &&
595 emit canEditLayer(haveCurrentEditableLayer); 630 emit canEditLayer(haveCurrentEditableLayer);
596 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer); 631 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer);
597 emit canMeasureLayer(haveCurrentLayer); 632 emit canMeasureLayer(haveCurrentLayer);
598 emit canSelect(haveMainModel && haveCurrentPane); 633 emit canSelect(haveMainModel && haveCurrentPane);
599 emit canPlay(haveMainModel && havePlayTarget); 634 emit canPlay(haveMainModel && havePlayTarget);
635 emit canRecord(m_recordTarget != 0);
600 emit canFfwd(haveMainModel); 636 emit canFfwd(haveMainModel);
601 emit canRewind(haveMainModel); 637 emit canRewind(haveMainModel);
602 emit canPaste(haveClipboardContents); 638 emit canPaste(haveClipboardContents);
603 emit canInsertInstant(haveCurrentPane); 639 emit canInsertInstant(haveCurrentPane);
604 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection); 640 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
605 emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer); 641 emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer);
606 emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection); 642 emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection);
643 emit canSubdivideInstants(haveCurrentTimeInstantsLayer && haveSelection);
644 emit canWinnowInstants(haveCurrentTimeInstantsLayer && haveSelection);
607 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); 645 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
608 emit canClearSelection(haveSelection); 646 emit canClearSelection(haveSelection);
609 emit canEditSelection(haveSelection && haveCurrentEditableLayer); 647 emit canEditSelection(haveSelection && haveCurrentEditableLayer);
610 emit canSave(m_sessionFile != "" && m_documentModified); 648 emit canSave(m_sessionFile != "" && m_documentModified);
611 emit canSaveAs(haveMainModel); 649 emit canSaveAs(haveMainModel);
1194 if (!m_labeller) return; 1232 if (!m_labeller) return;
1195 1233
1196 Labeller labeller(*m_labeller); 1234 Labeller labeller(*m_labeller);
1197 labeller.setSampleRate(sodm->getSampleRate()); 1235 labeller.setSampleRate(sodm->getSampleRate());
1198 1236
1199 // This uses a command 1237 Command *c = labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
1200 1238 if (c) CommandHistory::getInstance()->addCommand(c, false);
1201 labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms); 1239 }
1240
1241 void
1242 MainWindowBase::subdivideInstantsBy(int n)
1243 {
1244 Pane *pane = m_paneStack->getCurrentPane();
1245 if (!pane) return;
1246
1247 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
1248 if (!layer) return;
1249
1250 MultiSelection ms(m_viewManager->getSelection());
1251
1252 Model *model = layer->getModel();
1253 SparseOneDimensionalModel *sodm =
1254 dynamic_cast<SparseOneDimensionalModel *>(model);
1255 if (!sodm) return;
1256
1257 if (!m_labeller) return;
1258
1259 Labeller labeller(*m_labeller);
1260 labeller.setSampleRate(sodm->getSampleRate());
1261
1262 Command *c = labeller.subdivide<SparseOneDimensionalModel::Point>
1263 (*sodm, &ms, n);
1264 if (c) CommandHistory::getInstance()->addCommand(c, false);
1265 }
1266
1267 void
1268 MainWindowBase::winnowInstantsBy(int n)
1269 {
1270 Pane *pane = m_paneStack->getCurrentPane();
1271 if (!pane) return;
1272
1273 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
1274 if (!layer) return;
1275
1276 MultiSelection ms(m_viewManager->getSelection());
1277
1278 Model *model = layer->getModel();
1279 SparseOneDimensionalModel *sodm =
1280 dynamic_cast<SparseOneDimensionalModel *>(model);
1281 if (!sodm) return;
1282
1283 if (!m_labeller) return;
1284
1285 Labeller labeller(*m_labeller);
1286 labeller.setSampleRate(sodm->getSampleRate());
1287
1288 Command *c = labeller.winnow<SparseOneDimensionalModel::Point>
1289 (*sodm, &ms, n);
1290 if (c) CommandHistory::getInstance()->addCommand(c, false);
1202 } 1291 }
1203 1292
1204 MainWindowBase::FileOpenStatus 1293 MainWindowBase::FileOpenStatus
1205 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode) 1294 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
1206 { 1295 {
1311 rate = Preferences::getInstance()->getFixedSampleRate(); 1400 rate = Preferences::getInstance()->getFixedSampleRate();
1312 } else if (Preferences::getInstance()->getResampleOnLoad()) { 1401 } else if (Preferences::getInstance()->getResampleOnLoad()) {
1313 rate = m_playSource->getSourceSampleRate(); 1402 rate = m_playSource->getSourceSampleRate();
1314 } 1403 }
1315 1404
1316 WaveFileModel *newModel = new WaveFileModel(source, rate); 1405 ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate);
1317 1406
1318 if (!newModel->isOK()) { 1407 if (!newModel->isOK()) {
1319 delete newModel; 1408 delete newModel;
1320 m_openingAudioFile = false; 1409 m_openingAudioFile = false;
1321 if (source.wasCancelled()) { 1410 if (source.wasCancelled()) {
1765 1854
1766 return FileOpenSucceeded; 1855 return FileOpenSucceeded;
1767 } 1856 }
1768 1857
1769 MainWindowBase::FileOpenStatus 1858 MainWindowBase::FileOpenStatus
1859 MainWindowBase::openDirOfAudio(QString dirPath)
1860 {
1861 QDir dir(dirPath);
1862 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
1863 files.sort();
1864
1865 FileOpenStatus status = FileOpenFailed;
1866 bool first = true;
1867 bool cancelled = false;
1868
1869 foreach (QString file, files) {
1870
1871 FileSource source(dir.filePath(file));
1872 if (!source.isAvailable()) {
1873 continue;
1874 }
1875
1876 if (AudioFileReaderFactory::getKnownExtensions().contains
1877 (source.getExtension().toLower())) {
1878
1879 AudioFileOpenMode mode = CreateAdditionalModel;
1880 if (first) mode = ReplaceSession;
1881
1882 switch (openAudio(source, mode)) {
1883 case FileOpenSucceeded:
1884 status = FileOpenSucceeded;
1885 first = false;
1886 break;
1887 case FileOpenFailed:
1888 break;
1889 case FileOpenCancelled:
1890 cancelled = true;
1891 break;
1892 case FileOpenWrongMode:
1893 break;
1894 }
1895 }
1896
1897 if (cancelled) break;
1898 }
1899
1900 return status;
1901 }
1902
1903 MainWindowBase::FileOpenStatus
1770 MainWindowBase::openSessionPath(QString fileOrUrl) 1904 MainWindowBase::openSessionPath(QString fileOrUrl)
1771 { 1905 {
1772 ProgressDialog dialog(tr("Opening session..."), true, 2000, this); 1906 ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
1773 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash())); 1907 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
1774 return openSession(FileSource(fileOrUrl, &dialog)); 1908 return openSession(FileSource(fileOrUrl, &dialog));
2158 m_recentFiles.addFile(source.getLocation()); 2292 m_recentFiles.addFile(source.getLocation());
2159 return FileOpenSucceeded; 2293 return FileOpenSucceeded;
2160 } 2294 }
2161 2295
2162 void 2296 void
2163 MainWindowBase::createPlayTarget() 2297 MainWindowBase::createAudioIO()
2164 { 2298 {
2165 if (m_playTarget) return; 2299 if (m_playTarget || m_audioIO) return;
2300
2301 if (!(m_soundOptions & WithAudioOutput)) return;
2166 2302
2167 QSettings settings; 2303 QSettings settings;
2168 settings.beginGroup("Preferences"); 2304 settings.beginGroup("Preferences");
2169 QString implementation = settings.value 2305 QString implementation = settings.value
2170 ("audio-target", "").toString(); 2306 ("audio-target", "").toString();
2268 this, SLOT(modelRegenerationWarning(QString, QString, QString))); 2404 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
2269 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)), 2405 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
2270 this, SLOT(modelGenerationFailed(QString, QString))); 2406 this, SLOT(modelGenerationFailed(QString, QString)));
2271 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)), 2407 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
2272 this, SLOT(modelRegenerationWarning(QString, QString, QString))); 2408 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
2273 connect(m_document, SIGNAL(alignmentFailed(QString, QString)), 2409 connect(m_document, SIGNAL(alignmentComplete(AlignmentModel *)),
2274 this, SLOT(alignmentFailed(QString, QString))); 2410 this, SLOT(alignmentComplete(AlignmentModel *)));
2411 connect(m_document, SIGNAL(alignmentFailed(QString)),
2412 this, SLOT(alignmentFailed(QString)));
2275 2413
2276 emit replacedDocument(); 2414 emit replacedDocument();
2277 } 2415 }
2278 2416
2279 bool 2417 bool
2653 } 2791 }
2654 2792
2655 void 2793 void
2656 MainWindowBase::play() 2794 MainWindowBase::play()
2657 { 2795 {
2658 if (m_playSource->isPlaying()) { 2796 if ((m_recordTarget && m_recordTarget->isRecording()) ||
2797 (m_playSource && m_playSource->isPlaying())) {
2659 stop(); 2798 stop();
2799 QAction *action = qobject_cast<QAction *>(sender());
2800 if (action) action->setChecked(false);
2660 } else { 2801 } else {
2802 if (m_audioIO) m_audioIO->resume();
2803 else if (m_playTarget) m_playTarget->resume();
2661 playbackFrameChanged(m_viewManager->getPlaybackFrame()); 2804 playbackFrameChanged(m_viewManager->getPlaybackFrame());
2662 m_playSource->play(m_viewManager->getPlaybackFrame()); 2805 m_playSource->play(m_viewManager->getPlaybackFrame());
2663 } 2806 }
2807 }
2808
2809 void
2810 MainWindowBase::record()
2811 {
2812 if (!(m_soundOptions & WithAudioInput)) {
2813 return;
2814 }
2815
2816 if (!m_recordTarget) {
2817 //!!! report
2818 return;
2819 }
2820
2821 if (!m_audioIO) {
2822 createAudioIO();
2823 }
2824
2825 if (!m_audioIO) {
2826 //!!! report
2827 return;
2828 }
2829
2830 if (m_recordTarget->isRecording()) {
2831 stop();
2832 return;
2833 }
2834
2835 QAction *action = qobject_cast<QAction *>(sender());
2836
2837 if (m_audioRecordMode == RecordReplaceSession) {
2838 if (!checkSaveModified()) {
2839 if (action) action->setChecked(false);
2840 return;
2841 }
2842 }
2843
2844 m_audioIO->resume();
2845
2846 WritableWaveFileModel *model = m_recordTarget->startRecording();
2847 if (!model) {
2848 cerr << "ERROR: MainWindowBase::record: Recording failed" << endl;
2849 //!!! report
2850 if (action) action->setChecked(false);
2851 return;
2852 }
2853
2854 if (!model->isOK()) {
2855 m_recordTarget->stopRecording();
2856 m_audioIO->suspend();
2857 delete model;
2858 return;
2859 }
2860
2861 PlayParameterRepository::getInstance()->addPlayable(model);
2862
2863 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
2864
2865 //!!! duplication with openAudio here
2866
2867 QString templateName = getDefaultSessionTemplate();
2868 bool loadedTemplate = false;
2869
2870 if (templateName != "") {
2871 FileOpenStatus tplStatus = openSessionTemplate(templateName);
2872 if (tplStatus == FileOpenCancelled) {
2873 m_recordTarget->stopRecording();
2874 m_audioIO->suspend();
2875 PlayParameterRepository::getInstance()->removePlayable(model);
2876 return;
2877 }
2878 if (tplStatus != FileOpenFailed) {
2879 loadedTemplate = true;
2880 }
2881 }
2882
2883 if (!loadedTemplate) {
2884 closeSession();
2885 createDocument();
2886 }
2887
2888 Model *prevMain = getMainModel();
2889 if (prevMain) {
2890 m_playSource->removeModel(prevMain);
2891 PlayParameterRepository::getInstance()->removePlayable(prevMain);
2892 }
2893
2894 m_document->setMainModel(model);
2895 setupMenus();
2896
2897 if (loadedTemplate || (m_sessionFile == "")) {
2898 //!!! shouldn't be dealing directly with title from here -- call a method
2899 setWindowTitle(tr("%1: %2")
2900 .arg(QApplication::applicationName())
2901 .arg(model->getLocation()));
2902 CommandHistory::getInstance()->clear();
2903 CommandHistory::getInstance()->documentSaved();
2904 m_documentModified = false;
2905 } else {
2906 setWindowTitle(tr("%1: %2 [%3]")
2907 .arg(QApplication::applicationName())
2908 .arg(QFileInfo(m_sessionFile).fileName())
2909 .arg(model->getLocation()));
2910 if (m_documentModified) {
2911 m_documentModified = false;
2912 documentModified(); // so as to restore "(modified)" window title
2913 }
2914 }
2915
2916 } else {
2917
2918 CommandHistory::getInstance()->startCompoundOperation
2919 (tr("Import Recorded Audio"), true);
2920
2921 m_document->addImportedModel(model);
2922
2923 AddPaneCommand *command = new AddPaneCommand(this);
2924 CommandHistory::getInstance()->addCommand(command);
2925
2926 Pane *pane = command->getPane();
2927
2928 if (m_timeRulerLayer) {
2929 m_document->addLayerToView(pane, m_timeRulerLayer);
2930 }
2931
2932 Layer *newLayer = m_document->createImportedLayer(model);
2933
2934 if (newLayer) {
2935 m_document->addLayerToView(pane, newLayer);
2936 }
2937
2938 CommandHistory::getInstance()->endCompoundOperation();
2939 }
2940
2941 updateMenuStates();
2942 m_recentFiles.addFile(model->getLocation());
2943 currentPaneChanged(m_paneStack->getCurrentPane());
2944
2945 emit audioFileLoaded();
2664 } 2946 }
2665 2947
2666 void 2948 void
2667 MainWindowBase::ffwd() 2949 MainWindowBase::ffwd()
2668 { 2950 {
2889 } 3171 }
2890 3172
2891 void 3173 void
2892 MainWindowBase::stop() 3174 MainWindowBase::stop()
2893 { 3175 {
3176 if (m_recordTarget &&
3177 m_recordTarget->isRecording()) {
3178 m_recordTarget->stopRecording();
3179 }
3180
3181 if (!m_playSource) return;
3182
2894 m_playSource->stop(); 3183 m_playSource->stop();
2895 3184
3185 if (m_audioIO) m_audioIO->suspend();
3186 else if (m_playTarget) m_playTarget->suspend();
3187
2896 if (m_paneStack && m_paneStack->getCurrentPane()) { 3188 if (m_paneStack && m_paneStack->getCurrentPane()) {
2897 updateVisibleRangeDisplay(m_paneStack->getCurrentPane()); 3189 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
2898 } else { 3190 } else {
2899 m_myStatusMessage = ""; 3191 m_myStatusMessage = "";
2900 getStatusLabel()->setText(""); 3192 getStatusLabel()->setText("");
3224 remainingStr = (then - now).toText(true).c_str(); 3516 remainingStr = (then - now).toText(true).c_str();
3225 } 3517 }
3226 3518
3227 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)") 3519 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
3228 .arg(nowStr).arg(thenStr).arg(remainingStr); 3520 .arg(nowStr).arg(thenStr).arg(remainingStr);
3521
3522 getStatusLabel()->setText(m_myStatusMessage);
3523 }
3524
3525 void
3526 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
3527 {
3528 RealTime duration = RealTime::frame2RealTime(frame, rate);
3529 QString durStr = duration.toSecText().c_str();
3530
3531 m_myStatusMessage = tr("Recording: %1").arg(durStr);
3229 3532
3230 getStatusLabel()->setText(m_myStatusMessage); 3533 getStatusLabel()->setText(m_myStatusMessage);
3231 } 3534 }
3232 3535
3233 void 3536 void
3359 MainWindowBase::mainModelChanged(WaveFileModel *model) 3662 MainWindowBase::mainModelChanged(WaveFileModel *model)
3360 { 3663 {
3361 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl; 3664 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
3362 updateDescriptionLabel(); 3665 updateDescriptionLabel();
3363 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); 3666 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
3364 if (model && !m_playTarget && m_audioOutput) { 3667 if (model && !(m_playTarget || m_audioIO) &&
3365 createPlayTarget(); 3668 (m_soundOptions & WithAudioOutput)) {
3669 createAudioIO();
3366 } 3670 }
3367 } 3671 }
3368 3672
3369 void 3673 void
3370 MainWindowBase::modelAboutToBeDeleted(Model *model) 3674 MainWindowBase::modelAboutToBeDeleted(Model *model)
3372 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl; 3676 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl;
3373 if (model == m_viewManager->getPlaybackModel()) { 3677 if (model == m_viewManager->getPlaybackModel()) {
3374 m_viewManager->setPlaybackModel(0); 3678 m_viewManager->setPlaybackModel(0);
3375 } 3679 }
3376 m_playSource->removeModel(model); 3680 m_playSource->removeModel(model);
3377 FFTDataServer::modelAboutToBeDeleted(model);
3378 } 3681 }
3379 3682
3380 void 3683 void
3381 MainWindowBase::paneDeleteButtonClicked(Pane *pane) 3684 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
3382 { 3685 {
3409 CommandHistory::getInstance()->addCommand(command); 3712 CommandHistory::getInstance()->addCommand(command);
3410 3713
3411 CommandHistory::getInstance()->endCompoundOperation(); 3714 CommandHistory::getInstance()->endCompoundOperation();
3412 3715
3413 updateMenuStates(); 3716 updateMenuStates();
3717 }
3718
3719 void
3720 MainWindowBase::alignmentComplete(AlignmentModel *model)
3721 {
3722 cerr << "MainWindowBase::alignmentComplete(" << model << ")" << endl;
3414 } 3723 }
3415 3724
3416 void 3725 void
3417 MainWindowBase::pollOSC() 3726 MainWindowBase::pollOSC()
3418 { 3727 {
3490 } 3799 }
3491 #endif 3800 #endif
3492 #endif 3801 #endif
3493 } 3802 }
3494 3803
3495 3804 void
3805 MainWindowBase::openLocalFolder(QString path)
3806 {
3807 QDir d(path);
3808 if (d.exists()) {
3809 QStringList args;
3810 QString path = d.canonicalPath();
3811 #if defined Q_OS_WIN32
3812 // Although the Win32 API is quite happy to have
3813 // forward slashes as directory separators, Windows
3814 // Explorer is not
3815 path = path.replace('/', '\\');
3816 args << path;
3817 QProcess::execute("c:/windows/explorer.exe", args);
3818 #else
3819 args << path;
3820 QProcess::execute(
3821 #if defined Q_OS_MAC
3822 "/usr/bin/open",
3823 #else
3824 "/usr/bin/xdg-open",
3825 #endif
3826 args);
3827 #endif
3828 }
3829 }
3830