1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 
19 #define RG_MODULE_STRING "[AudioManagerDialog]"
20 
21 #include "AudioManagerDialog.h"
22 
23 #include "base/Event.h"
24 #include "misc/Debug.h"
25 #include "misc/Strings.h"
26 #include "AudioPlayingDialog.h"
27 #include "base/Composition.h"
28 #include "base/Exception.h"
29 #include "base/Instrument.h"
30 #include "base/MidiProgram.h"
31 #include "base/NotationTypes.h"
32 #include "base/RealTime.h"
33 #include "base/Segment.h"
34 #include "base/Selection.h"
35 #include "base/Studio.h"
36 #include "base/Track.h"
37 #include "document/CommandHistory.h"
38 #include "document/RosegardenDocument.h"
39 #include "misc/ConfigGroups.h"
40 #include "gui/application/RosegardenMainWindow.h"
41 #include "gui/application/RosegardenMainViewWidget.h"
42 #include "sequencer/RosegardenSequencer.h"
43 #include "gui/widgets/AudioListItem.h"
44 #include "gui/widgets/AudioListView.h"
45 #include "gui/widgets/TmpStatusMsg.h"
46 #include "gui/widgets/LineEdit.h"
47 #include "gui/widgets/InputDialog.h"
48 #include "gui/widgets/WarningGroupBox.h"
49 #include "gui/general/IconLoader.h"
50 #include "gui/dialogs/AboutDialog.h"
51 #include "sound/AudioFile.h"
52 #include "sound/AudioFileManager.h"
53 #include "sound/WAVAudioFile.h"
54 #include "UnusedAudioSelectionDialog.h"
55 #include "document/Command.h"
56 #include "gui/widgets/FileDialog.h"
57 
58 #include <QApplication>
59 #include <QMainWindow>
60 #include <QTreeWidget>
61 #include <QTreeWidgetItem>
62 #include <QTreeWidgetItemIterator>
63 #include <QMessageBox>
64 #include <QAction>
65 #include <QByteArray>
66 #include <QDataStream>
67 #include <QDialog>
68 #include <QFile>
69 #include <QFileInfo>
70 #include <QIcon>
71 #include <QLabel>
72 #include <QTreeWidget>
73 #include <QPainter>
74 #include <QPixmap>
75 #include <QString>
76 #include <QStringList>
77 #include <QTimer>
78 #include <QWidget>
79 #include <QVBoxLayout>
80 #include <QUrl>
81 #include <QShortcut>
82 #include <QKeySequence>
83 #include <QSettings>
84 #include <QDrag>
85 #include <QDropEvent>
86 #include <QMimeData>
87 #include <QDesktopServices>
88 #include <QPointer>
89 
90 
91 
92 
93 namespace Rosegarden
94 {
95 
96 const int AudioManagerDialog::m_maxPreviewWidth            = 100;
97 const int AudioManagerDialog::m_previewHeight              = 30;
98 const char* const AudioManagerDialog::m_listViewLayoutName = "AudioManagerDialog Layout";
99 
AudioManagerDialog(QWidget * parent,RosegardenDocument * doc)100 AudioManagerDialog::AudioManagerDialog(QWidget *parent,
101                                        RosegardenDocument *doc):
102         QMainWindow(parent),
103         m_doc(doc),
104         m_playingAudioFile(0),
105         m_audioPlayingDialog(nullptr),
106         m_playTimer(new QTimer(this)),
107         m_audiblePreview(true)
108 {
109     setWindowTitle(tr("Audio File Manager"));
110     this->setAttribute(Qt::WA_DeleteOnClose);
111     setWindowIcon(IconLoader::loadPixmap("window-audio-manager"));
112     setMinimumWidth(800);
113 
114     QWidget *centralWidget = new QWidget;
115     setCentralWidget(centralWidget);
116 
117     QVBoxLayout *boxLayout = new QVBoxLayout;
118     centralWidget->setLayout(boxLayout);
119 
120     boxLayout->setContentsMargins(10, 10, 10, 10);
121     boxLayout->setSpacing(5);
122 
123     m_sampleRate = RosegardenSequencer::getInstance()->getSampleRate();
124 
125     m_fileList = new AudioListView(centralWidget); // internal class needs parent (?)
126     m_fileList->setSelectionMode(QAbstractItemView::SingleSelection);
127     m_fileList->setSelectionBehavior(QAbstractItemView::SelectRows);
128     m_fileList->setIconSize(QSize(m_maxPreviewWidth, m_previewHeight));
129 
130     boxLayout->addWidget(m_fileList);
131 
132     m_wrongSampleRates = new WarningGroupBox;
133     QVBoxLayout *warningBox = new QVBoxLayout;
134     m_wrongSampleRates->setLayout(warningBox);
135     QLabel *warning = new QLabel(tr("<qt><p><img src=\":pixmaps/tooltip/warning.png\"></img> <b>Audio files marked with an asterisk (*) are encoded at a sample rate different from that of the JACK audio server.</b></p><p>Rosegarden will play them at the correct speed, but they will sound terrible.  Please consider resampling these files externally, or adjusting the sample rate of the JACK server.</p></qt>"));
136     warning->setWordWrap(true);
137     warningBox->addWidget(warning);
138 
139     boxLayout->addWidget(m_wrongSampleRates);
140     m_wrongSampleRates->hide();
141 
142     // file menu
143     createAction("add_audio", SLOT(slotAdd()));
144     createAction("export_audio", SLOT(slotExportAudio()));
145     createAction("file_close", SLOT(slotClose()));
146 
147     // edit menu
148     createAction("remove_audio", SLOT(slotRemove()));
149     createAction("remove_all_audio", SLOT(slotRemoveAll()));
150     createAction("remove_all_unused_audio", SLOT(slotRemoveAllUnused()));
151     createAction("delete_unused_audio", SLOT(slotDeleteUnused()));
152 
153     // action menu
154     createAction("preview_audio", SLOT(slotPlayPreview()));
155     createAction("insert_audio", SLOT(slotInsert()));
156     createAction("distribute_audio", SLOT(slotDistributeOnMidiSegment()));
157 
158     // help menu
159     createAction("audio_help", SLOT(slotHelpRequested()));
160     createAction("help_about_app", SLOT(slotHelpAbout()));
161 
162 
163     //!!! oh now hang on, does this one work?
164 
165     // (No, I've never heard of it until poking around in this code.  Julie
166     // hadn't either when she redid the old KXMLGUI stuff for us.  I think we've
167     // stumbled across a half eaten bagel here)
168     createAction("distribute_audio", SLOT(slotDistributeOnMidiSegment()));
169 
170     // Set the column names
171     //
172     //
173     QStringList sl;
174 
175     sl << tr("Name");           // 0
176     sl << tr("Duration");       // 1
177     sl << tr("Envelope");       // 2
178     sl << tr("Sample rate");    // 3
179     sl << tr("Channels");       // 4
180     sl << tr("Resolution");     // 5
181     sl << tr("File");           // 6
182 
183     m_fileList->setColumnCount(7);
184     m_fileList->setHeaderItem(new QTreeWidgetItem(sl));
185 
186     m_fileList->setSortingEnabled(true);
187 
188     //
189     // connect selection mechanism
190     connect(m_fileList, &QTreeWidget::itemSelectionChanged,
191             this, &AudioManagerDialog::slotSelectionChanged);
192 
193     connect(m_fileList, SIGNAL(dropped(QDropEvent*, QTreeWidget*, QList<QUrl>)),
194             SLOT(slotDropped(QDropEvent*, QTreeWidget*, QList<QUrl>)));
195 
196 
197     //
198     // setup local shortcuts
199     //
200 
201     // delete
202     //
203     m_shortcuts = new QShortcut(QKeySequence(Qt::Key_Delete), this);
204     connect(m_shortcuts, &QShortcut::activated, this, &AudioManagerDialog::slotRemove);
205 
206 
207     slotPopulateFileList();
208 
209     // Connect command history for updates
210     //
211     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
212             this, SLOT(slotCommandExecuted()));
213 
214     connect(m_playTimer, &QTimer::timeout,
215             this, &AudioManagerDialog::slotCancelPlayingAudio);
216 
217 
218     createMenusAndToolbars("audiomanager.rc"); //@@@ JAS orig. 0
219 
220     updateActionState(false);
221 
222     QSettings settings;
223     settings.beginGroup(WindowGeometryConfigGroup);
224     this->restoreGeometry(settings.value("Audio_File_Manager").toByteArray());
225     settings.endGroup();
226 
227     setAttribute(Qt::WA_DeleteOnClose);
228 }
229 
230 
slotFileItemActivated()231 void slotFileItemActivated(){
232 
233     return;
234 }
235 
236 
~AudioManagerDialog()237 AudioManagerDialog::~AudioManagerDialog()
238 {
239     //RG_DEBUG << "*** dtor";
240 
241 //    m_fileList->saveLayout(m_listViewLayoutName);    //&&&
242     QSettings settings;
243     settings.beginGroup(WindowGeometryConfigGroup);
244     settings.setValue("Audio_File_Manager", this->saveGeometry());
245     settings.endGroup();
246 }
247 
248 void
slotPopulateFileList()249 AudioManagerDialog::slotPopulateFileList()
250 {
251     // create pixmap of given size
252     QPixmap *audioPixmap = new QPixmap(m_maxPreviewWidth, m_previewHeight);
253 
254     // Store last selected item if we have one
255     //
256     AudioListItem *selectedItem =
257             dynamic_cast<AudioListItem *>(m_fileList->currentItem());
258 
259     AudioFileId lastId = 0;
260     Segment *lastSegment = nullptr;
261     bool findSelection = false;
262     bool foundSelection = false;
263 
264     if (selectedItem) {
265         lastId = selectedItem->getId();
266         lastSegment = selectedItem->getSegment();
267         findSelection = true;
268     }
269 
270     // We don't want the selection changes to be propagated
271     // to the main view
272     //
273     m_fileList->blockSignals(true);
274 
275     // clear file list and disable associated action buttons
276     m_fileList->clear();
277     // AudioListItem* auItem;
278     if (m_doc->getAudioFileManager().begin() ==
279             m_doc->getAudioFileManager().end()) {
280         // Turn off selection and report empty list
281         //
282         // auItem = new AudioListItem(m_fileList, QStringList(tr("<no audio files>")));
283 
284         m_fileList->setSelectionMode(QAbstractItemView::NoSelection);
285 
286         m_fileList->blockSignals(false);
287         updateActionState(false);
288         return ;
289     }
290 
291     // show tree hierarchy
292 
293     // enable selection
294     m_fileList->setSelectionMode(QAbstractItemView::SingleSelection);
295 
296     // for the sample file length
297     RealTime length;
298 
299     // Create a vector of audio Segments only
300     //
301     std::vector<Segment*> segments;
302     std::vector<Segment*>::const_iterator iit;
303 
304     for (Composition::iterator it = m_doc->getComposition().begin();
305             it != m_doc->getComposition().end(); ++it) {
306         if ((*it)->getType() == Segment::Audio)
307             segments.push_back(*it);
308     }
309 
310     // duration
311     RealTime segmentDuration;
312     bool wrongSampleRates = false;
313 
314     for (std::vector<AudioFile*>::const_iterator
315             it = m_doc->getAudioFileManager().begin();
316             it != m_doc->getAudioFileManager().end();
317             ++it) {
318         try {
319             //RG_DEBUG << "slotPopulateFileList(): 1";
320             m_doc->getAudioFileManager().
321             drawPreview((*it)->getId(),
322                         RealTime::zeroTime,
323                         (*it)->getLength(),
324                         audioPixmap);
325             //RG_DEBUG << "slotPopulateFileList(): 2";
326         } catch (const Exception &e) {
327             //RG_DEBUG << "slotPopulateFileList(): 3";
328             audioPixmap->fill(); // white
329             QPainter p(audioPixmap);
330             p.setPen(QColor(Qt::black));
331             p.drawText(10, m_previewHeight / 2, QString("<no preview>"));
332         }
333         //RG_DEBUG << "slotPopulateFileList(): 4";
334 
335         //!!! Why isn't the label the label the user assigned to the file?
336         // Why do we allow the user to assign a label at all, then?
337 
338         QString label = (*it)->getShortFilename();
339 
340         // Set the label, duration, envelope pixmap and filename
341         //
342 
343         AudioListItem *item = new AudioListItem(m_fileList, QStringList(label), (*it)->getId());
344         //AudioListItem *item = new AudioListItem(m_fileList, QStringList(label)); //, (*it)->getId());
345 
346         // Duration
347         //
348         length = (*it)->getLength();
349         const QString msecs = QString::asprintf("%03d", length.nsec / 1000000);
350         item->setText(1,QString("%1.%2s").arg(length.sec).arg(msecs));    // row, col
351 
352         // set start time and duration
353         item->setStartTime(RealTime::zeroTime);
354         item->setDuration(length);
355 
356         // Envelope pixmap
357         //
358         item->setIcon(2, QIcon(*audioPixmap));    // row, col
359 
360 
361         // File location
362         //
363         item->setText(6,  m_doc->getAudioFileManager().
364                 homeToTilde((*it)->getFilename()));
365 
366         // Resolution
367         //
368         item->setText(5, QString("%1 bits").arg((*it)->getBitsPerSample()));
369 
370         // Channels
371         //
372         item->setText(4, QString("%1").arg((*it)->getChannels()));
373 
374         // Sample rate
375         //
376         if (m_sampleRate != 0 && int((*it)->getSampleRate()) != m_sampleRate) {
377             const QString sRate =
378                 QString::asprintf("%.1f KHz *",
379                                   float((*it)->getSampleRate()) / 1000.0);
380             wrongSampleRates = true;
381             item->setText(3, sRate);
382         } else {
383             const QString sRate =
384                 QString::asprintf("%.1f KHz",
385                                   float((*it)->getSampleRate()) / 1000.0);
386             item->setText(3, sRate);
387         }
388 
389         // Test audio file element for selection criteria
390         //
391         if (findSelection && lastSegment == nullptr && lastId == (*it)->getId()) {
392             //m_fileList->setSelected(item, true);
393             m_fileList->setCurrentItem(item);
394 
395             findSelection = false;
396         }
397 
398         // Add children
399         //
400         for (iit = segments.begin(); iit != segments.end(); ++iit) {
401             if ((*iit)->getAudioFileId() == (*it)->getId()) {
402                 AudioListItem *childItem =
403                     new AudioListItem(item,
404                                       QStringList(QString((*iit)->getLabel().c_str())),
405                                       (*it)->getId());
406                 segmentDuration = (*iit)->getAudioEndTime() -
407                                   (*iit)->getAudioStartTime();
408 
409                 // store the start time
410                 //
411                 childItem->setStartTime((*iit)->getAudioStartTime());
412                 childItem->setDuration(segmentDuration);
413 
414                 // Write segment duration
415                 //
416                 const QString msecs =
417                     QString::asprintf("%03d", segmentDuration.nsec / 1000000);
418                 childItem->setText(1, QString("%1.%2s")
419                                    .arg(segmentDuration.sec)
420                                    .arg(msecs));
421 
422                 try {
423                     m_doc->getAudioFileManager().
424                     drawHighlightedPreview((*it)->getId(),
425                                            RealTime::zeroTime,
426                                            (*it)->getLength(),
427                                            (*iit)->getAudioStartTime(),
428                                            (*iit)->getAudioEndTime(),
429                                            audioPixmap);
430                 } catch (const Exception &e) {
431                     // should already be set to "no file"
432                 }
433 
434                 // set pixmap
435                 //
436                 //childItem->setPixmap(2, *audioPixmap);
437                 childItem->setIcon(2, QIcon(*audioPixmap));
438 
439                 // set segment
440                 //
441                 childItem->setSegment(*iit);
442 
443                 if (findSelection && lastSegment == (*iit)) {
444                     m_fileList->setCurrentItem(childItem);    // select
445                     findSelection = false;
446                     foundSelection = true;
447                 }
448 
449                 // Add children
450             }
451         }
452     }
453 
454     updateActionState(foundSelection);
455 
456     if (wrongSampleRates) {
457         m_wrongSampleRates->show();
458     } else {
459         m_wrongSampleRates->hide();
460     }
461 
462     m_fileList->blockSignals(false);
463 }
464 
465 AudioFile*
getCurrentSelection()466 AudioManagerDialog::getCurrentSelection()
467 {
468     // try and get the selected item
469     QList<QTreeWidgetItem *> til= m_fileList->selectedItems();
470     if (til.isEmpty()){
471         RG_WARNING << "AudioManagerDialog::getCurrentSelection() - til.isEmpty() so we're returning 0 and this game is over. Fail.";
472         return nullptr;
473     }
474     AudioListItem *item = dynamic_cast<AudioListItem*>(til[0]);
475     if (item == nullptr) {
476         RG_WARNING << "AudioManagerDialog::getCurrentSelection() - item == 0 so we're returning 0 and this game is over. Epic fail.";
477         return nullptr;
478     }
479 
480     std::vector<AudioFile*>::const_iterator it;
481 
482     for (it = m_doc->getAudioFileManager().begin();
483             it != m_doc->getAudioFileManager().end();
484             ++it) {
485         // If we match then return the valid AudioFile
486         //
487         if (item->getId() == (*it)->getId()) {
488             return (*it);
489         } else {
490             RG_WARNING << "AudioManagerDialog::getCurrentSelection() - item->getId() of "
491                       << item->getId() << " does not match (*it)->getId() of "
492                       << (*it)->getId() << " so you are basically screwed.  Sorry about that."
493                      ;
494         }
495     }
496 
497     RG_WARNING << "AudioManagerDialog::getCurrentSelection() - we tried so hard, but in the end it was all just bricks in the wall.";
498     return nullptr;
499 }
500 
501 void
slotExportAudio()502 AudioManagerDialog::slotExportAudio()
503 {
504     WAVAudioFile *sourceFile =
505             dynamic_cast<WAVAudioFile *>(getCurrentSelection());
506 
507     if (!sourceFile)
508         return;
509 
510     QList<QTreeWidgetItem *> selectedItems = m_fileList->selectedItems();
511 
512     if (selectedItems.isEmpty()) {
513         RG_WARNING << "slotExportAudio() - nothing selected!";
514         return;
515     }
516 
517     // ??? All we ever look at is the first one.
518     AudioListItem *item = dynamic_cast<AudioListItem *>(selectedItems[0]);
519 
520     if (!item)
521         return;
522 
523     Segment *segment = item->getSegment();
524 
525     QString destFileName =
526             FileDialog::getSaveFileName(
527                     this,  // parent
528                     tr("Save File As"),  // caption
529                     QDir::currentPath(),  // dir
530                     sourceFile->getFilename(),  // defaultName
531                     tr("*.wav|WAV files (*.wav)"));  // filter
532 
533     if (destFileName.isEmpty())
534         return;
535 
536     // Check for a dot extension and append ".wav" if not found
537     // ??? Should use QFileInfo::suffix() to check for an extension.
538     if (destFileName.contains(".") == 0)
539         destFileName += ".wav";
540 
541     // Progress Dialog
542     QProgressDialog progressDialog(
543             tr("Exporting audio file..."),  // labelText
544             tr("Cancel"),  // cancelButtonText
545             0, 0,  // min, max
546             this);  // parent
547     progressDialog.setWindowTitle(tr("Rosegarden"));
548     progressDialog.setWindowModality(Qt::WindowModal);
549     // We will close anyway when this object goes out of scope.
550     progressDialog.setAutoClose(false);
551     // No cancel button since appendSamples() doesn't support progress.
552     progressDialog.setCancelButton(nullptr);
553     // Just force the progress dialog up.
554     // Both Qt4 and Qt5 have bugs related to delayed showing of progress
555     // dialogs.  In Qt4, the dialog sometimes won't show.  In Qt5, KDE
556     // based distros might lock up.  See Bug #1546.
557     progressDialog.show();
558 
559     RealTime clipStartTime = RealTime::zeroTime;
560     RealTime clipDuration = sourceFile->getLength();
561 
562     if (segment) {
563         clipStartTime = segment->getAudioStartTime();
564         clipDuration = segment->getAudioEndTime() - clipStartTime;
565     }
566 
567     WAVAudioFile destFile(
568             destFileName,
569             sourceFile->getChannels(),
570             sourceFile->getSampleRate(),
571             sourceFile->getBytesPerSecond(),
572             sourceFile->getBytesPerFrame(),
573             sourceFile->getBitsPerSample());
574 
575     if (sourceFile->open() == false)
576         return;
577 
578     destFile.write();
579 
580     sourceFile->scanTo(clipStartTime);
581 
582     // appendSamples() takes the longest.  Would be nice if it would
583     // take a progress dialog.
584     destFile.appendSamples(sourceFile->getSampleFrameSlice(clipDuration));
585 
586     destFile.close();
587     sourceFile->close();
588 }
589 
590 void
slotRemove()591 AudioManagerDialog::slotRemove()
592 {
593     QList<QTreeWidgetItem *> selectedTreeItems = m_fileList->selectedItems();
594 
595     // If nothing is selected, bail.
596     // ??? Why not disable the action when there is no selection?
597     //     Then this will never happen.
598     if (selectedTreeItems.isEmpty())
599         return;
600 
601     AudioListItem *item = dynamic_cast<AudioListItem *>(selectedTreeItems[0]);
602     if (!item)
603         return;
604 
605     // If we're on a Segment then delete it from the Composition
606     // and refresh the list.
607     if (item->getSegment()) {
608         // Get the next item to highlight
609         QTreeWidgetItem *newTreeItem = m_fileList->itemBelow(item);
610 
611         // Nothing below?  Try above.
612         if (!newTreeItem)
613             newTreeItem = m_fileList->itemAbove(item);
614 
615         const AudioListItem *newAudioItem =
616                 dynamic_cast<const AudioListItem *>(newTreeItem);
617 
618         // If the item was an AudioListItem, and it is a Segment item...
619         if (newAudioItem  &&  newAudioItem->getSegment()) {
620             // Jump to new selection
621             setSelected(newAudioItem->getId(),
622                         newAudioItem->getSegment(),
623                         true);  // propagate
624         }
625 
626         // Delete the Segment from the Composition.
627         SegmentSelection selection;
628         selection.insert(item->getSegment());
629         emit deleteSegments(selection);
630 
631         return;
632     }
633 
634     // An audio file item is selected in the tree...
635 
636     const AudioFile *audioFile = getCurrentSelection();
637     if (!audioFile)
638         return;
639 
640     // remove segments along with audio file
641     //
642     AudioFileId id = audioFile->getId();
643     SegmentSelection selection;
644     Composition &comp = m_doc->getComposition();
645 
646     bool haveSegments = false;
647     for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
648         if ((*it)->getType() == Segment::Audio &&
649                 (*it)->getAudioFileId() == id) {
650             haveSegments = true;
651             break;
652         }
653     }
654 
655     if (haveSegments) {
656 
657         QString question = tr("This will unload audio file \"%1\" and remove all associated segments.  Are you sure?")
658                            .arg(audioFile->getFilename());
659 
660         // Ask the question
661         int reply = QMessageBox::warning(this, tr("Rosegarden"), question, QMessageBox::Yes | QMessageBox::Cancel , QMessageBox::Cancel);
662 
663         if (reply != QMessageBox::Yes)
664             return ;
665     }
666 
667     for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
668         if ((*it)->getType() == Segment::Audio &&
669                 (*it)->getAudioFileId() == id)
670             selection.insert(*it);
671     }
672     emit deleteSegments(selection);
673 
674     m_doc->notifyAudioFileRemoval(id);
675 
676     m_doc->getAudioFileManager().removeFile(id);
677 
678     // tell the sequencer
679     emit deleteAudioFile(id);
680 
681     // repopulate
682     slotPopulateFileList();
683 }
684 
685 void
slotPlayPreview()686 AudioManagerDialog::slotPlayPreview()
687 {
688     AudioFile *audioFile = getCurrentSelection();
689 
690     QList<QTreeWidgetItem*> til = m_fileList->selectedItems();
691     if (til.isEmpty()) {
692         RG_WARNING << "AudioManagerDialog::slotPlayPreview() - nothing selected!";
693         return;
694     }
695     AudioListItem *item = dynamic_cast<AudioListItem*>(til[0]);
696 
697     if (item == nullptr || audioFile == nullptr)
698         return ;
699 
700     // store the audio file we're playing
701     m_playingAudioFile = audioFile->getId();
702 
703     // tell the sequencer
704     emit playAudioFile(audioFile->getId(),
705                        item->getStartTime(),
706                        item->getDuration());
707 
708     // now open up the playing dialog
709     //
710     m_audioPlayingDialog =
711         new AudioPlayingDialog(this, audioFile->getFilename());
712 
713     // Setup timer to pop down dialog after file has completed
714     //
715     int msecs = item->getDuration().sec * 1000 +
716                 item->getDuration().nsec / 1000000;
717     m_playTimer->setSingleShot(true);
718     m_playTimer->start(msecs);
719 
720     // just execute
721     //
722     if (m_audioPlayingDialog->exec() == QDialog::Rejected)
723         emit cancelPlayingAudioFile(m_playingAudioFile);
724 
725     delete m_audioPlayingDialog;
726     m_audioPlayingDialog = nullptr;
727 
728     m_playTimer->stop();
729 
730 }
731 
732 void
slotCancelPlayingAudio()733 AudioManagerDialog::slotCancelPlayingAudio()
734 {
735     //std::cout << "AudioManagerDialog::slotCancelPlayingAudio";
736     if (m_audioPlayingDialog) {
737         m_playTimer->stop();
738         delete m_audioPlayingDialog;
739         m_audioPlayingDialog = nullptr;
740     }
741 }
742 
743 void
slotAdd()744 AudioManagerDialog::slotAdd()
745 {
746     QString extensionList = tr("WAV files") + " (*.wav *.WAV);;" +
747                             tr("All files") + " (*)";
748 
749     if (RosegardenMainWindow::self()->haveAudioImporter()) {
750         //!!! This list really needs to come from the importer helper program
751         // (which has an option to supply it -- we just haven't recorded it)
752         //
753         extensionList = tr("Audio files") + " (*.wav *.flac *.ogg *.mp3 *.WAV *.FLAC *.OGG *.MP3)" + ";;" +
754                         tr("WAV files") + " (*.wav *.WAV)" + ";;" +
755                         tr("FLAC files") + " (*.flac *.FLAC)" + ";;" +
756                         tr("Ogg files") + " (*.ogg *.OGG)" + ";;" +
757                         tr("MP3 files") + " (*.mp3 *.MP3)" + ";;" +
758                         tr("All files") + " (*)";
759     }
760 
761     // default to ~ for files if nothing previously stored in settings
762     QSettings settings;
763     settings.beginGroup(LastUsedPathsConfigGroup);
764     QString directory = settings.value("add_audio_file", QDir::homePath()).toString();
765 
766     //RG_DEBUG << "slotAdd(): using stored/default path: " << qstrtostr(directory);
767 
768     const QStringList fileList = FileDialog::getOpenFileNames(this, tr("Select one or more audio files"), directory, extensionList);
769 
770     QDir d;
771     for (int i = 0 ; i < fileList.size(); i++) {
772         addFile(QUrl::fromLocalFile(fileList.at(i)));
773         d = QFileInfo(fileList.at(i)).dir();
774     }
775 
776     // pick the directory from the last URL encountered to save for future
777     // reference, but don't store anything if no URLs were encountered (ie. the
778     // user hit cancel on the file dialog without choosing anything)
779     directory = d.canonicalPath();
780 
781     if (!fileList.isEmpty()) {
782         settings.setValue("add_audio_file", directory);
783 
784         //RG_DEBUG << "slotAdd(): storing path: " << qstrtostr(directory);
785     } else {
786         //RG_DEBUG << "slotAdd(): URL list was empty.  No files added.  Not storing path.";
787     }
788 
789     settings.endGroup();
790 }
791 
792 void
updateActionState(bool haveSelection)793 AudioManagerDialog::updateActionState(bool haveSelection)
794 {
795     //RG_DEBUG << "updateActionState(" << (haveSelection ? "true" : "false") << ")";
796 
797     if (m_doc->getAudioFileManager().begin() ==
798             m_doc->getAudioFileManager().end()) {
799         leaveActionState("have_audio_files"); //@@@ JAS orig. KXMLGUIClient::StateReverse
800     } else {
801         enterActionState("have_audio_files"); //@@@ JAS orig. KXMLGUIClient::StateNoReverse
802     }
803 
804     if (haveSelection) {
805 
806         enterActionState("have_audio_selected"); //@@@ JAS orig. KXMLGUIClient::StateNoReverse
807 
808         if (m_audiblePreview) {
809             enterActionState("have_audible_preview"); //@@@ JAS orig. KXMLGUIClient::StateNoReverse
810         } else {
811             leaveActionState("have_audible_preview"); //@@@ JAS orig. KXMLGUIClient::StateReverse
812         }
813 
814         if (isSelectedTrackAudio()) {
815             enterActionState("have_audio_insertable"); //@@@ JAS orig. KXMLGUIClient::StateNoReverse
816         } else {
817             leaveActionState("have_audio_insertable"); //@@@ JAS orig. KXMLGUIClient::StateReverse
818         }
819 
820     } else {
821         leaveActionState("have_audio_selected"); //@@@ JAS orig. KXMLGUIClient::StateReverse
822         leaveActionState("have_audio_insertable"); //@@@ JAS orig. KXMLGUIClient::StateReverse
823         leaveActionState("have_audible_preview"); //@@@ JAS orig. KXMLGUIClient::StateReverse
824     }
825 }
826 
827 void
slotInsert()828 AudioManagerDialog::slotInsert()
829 {
830     //RG_DEBUG << "slotInsert(): begin...";
831 
832     AudioFile *audioFile = getCurrentSelection();
833     if (audioFile == nullptr)
834         return ;
835 
836     //RG_DEBUG << "slotInsert(): emitting insertAudioSegment()";
837 
838     emit insertAudioSegment(audioFile->getId(),
839                             RealTime::zeroTime,
840                             audioFile->getLength());
841 }
842 
843 void
slotRemoveAll()844 AudioManagerDialog::slotRemoveAll()
845 {
846     QString question =
847         tr("This will unload all audio files and remove their associated segments.\nThis action cannot be undone, and associations with these files will be lost.\nFiles will not be removed from your disk.\nAre you sure?");
848 
849     int reply = QMessageBox::warning(this, tr("Rosegarden"), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
850 
851     if (reply != QMessageBox::Yes)
852         return ;
853 
854     SegmentSelection selection;
855     Composition &comp = m_doc->getComposition();
856 
857     for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
858         if ((*it)->getType() == Segment::Audio)
859             selection.insert(*it);
860     }
861     // delete segments
862     emit deleteSegments(selection);
863 
864     for (std::vector<AudioFile*>::const_iterator
865             aIt = m_doc->getAudioFileManager().begin();
866             aIt != m_doc->getAudioFileManager().end(); ++aIt) {
867         m_doc->notifyAudioFileRemoval((*aIt)->getId());
868     }
869 
870     m_doc->getAudioFileManager().clear();
871 
872     // and now the audio files
873     emit deleteAllAudioFiles();
874 
875     // clear the file list
876     m_fileList->clear();
877     slotPopulateFileList();
878 }
879 
880 void
slotRemoveAllUnused()881 AudioManagerDialog::slotRemoveAllUnused()
882 {
883     QString question =
884         tr("This will unload all audio files that are not associated with any segments in this composition.\nThis action cannot be undone, and associations with these files will be lost.\nFiles will not be removed from your disk.\nAre you sure?");
885 
886     int reply = QMessageBox::warning(this, tr("Rosegarden"), question,QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
887 
888     if (reply != QMessageBox::Yes)
889         return ;
890 
891     std::set
892         <AudioFileId> audioFiles;
893     Composition &comp = m_doc->getComposition();
894 
895     for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
896         if ((*it)->getType() == Segment::Audio)
897             audioFiles.insert((*it)->getAudioFileId());
898     }
899 
900     std::vector<AudioFileId> toDelete;
901     for (std::vector<AudioFile*>::const_iterator
902             aIt = m_doc->getAudioFileManager().begin();
903             aIt != m_doc->getAudioFileManager().end(); ++aIt) {
904         if (audioFiles.find((*aIt)->getId()) == audioFiles.end())
905             toDelete.push_back((*aIt)->getId());
906     }
907 
908     // Delete the audio files from the AFM
909     //
910     for (std::vector<AudioFileId>::iterator dIt = toDelete.begin();
911             dIt != toDelete.end(); ++dIt) {
912 
913         m_doc->notifyAudioFileRemoval(*dIt);
914         m_doc->getAudioFileManager().removeFile(*dIt);
915         emit deleteAudioFile(*dIt);
916     }
917 
918     // clear the file list
919     m_fileList->clear();
920     slotPopulateFileList();
921 }
922 
923 void
slotDeleteUnused()924 AudioManagerDialog::slotDeleteUnused()
925 {
926     std::set
927         <AudioFileId> audioFiles;
928     Composition &comp = m_doc->getComposition();
929 
930     for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
931         if ((*it)->getType() == Segment::Audio)
932             audioFiles.insert((*it)->getAudioFileId());
933     }
934 
935     std::vector<QString> toDelete;
936     std::map<QString, AudioFileId> nameMap;
937 
938     for (std::vector<AudioFile*>::const_iterator
939             aIt = m_doc->getAudioFileManager().begin();
940             aIt != m_doc->getAudioFileManager().end(); ++aIt) {
941         if (audioFiles.find((*aIt)->getId()) == audioFiles.end()) {
942             toDelete.push_back((*aIt)->getFilename());
943             nameMap[(*aIt)->getFilename()] = (*aIt)->getId();
944         }
945     }
946 
947     UnusedAudioSelectionDialog *dialog = new UnusedAudioSelectionDialog
948                                          (this,
949                                           tr("The following audio files are not used in the current composition.\n\nPlease select the ones you wish to delete permanently from the hard disk.\n"),
950                                           toDelete);
951 
952     if (dialog->exec() == QDialog::Accepted) {
953 
954         std::vector<QString> names = dialog->getSelectedAudioFileNames();
955 
956         if (names.size() > 0) {
957 
958             QString question =
959                 tr("<qt>About to delete %n audio file(s) permanently from the hard disk.<br>This action cannot be undone, and there will be no way to recover the files.<br>Are you sure?</qt>", "", names.size());
960 
961             int reply = QMessageBox::warning(this, tr("Rosegarden"), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
962 
963             if (reply != QMessageBox::Yes) {
964                 delete dialog;
965                 return ;
966             }
967 
968             for (unsigned int i = 0; i < names.size(); ++i) {
969                 //RG_DEBUG << i << ": " << names[i];
970                 QFile file(names[i]);
971                 if (!file.remove()) {
972                     QMessageBox::critical(this, tr("Rosegarden"), tr("File %1 could not be deleted.").arg(names[i]));
973                 } else {
974                     if (nameMap.find(names[i]) != nameMap.end()) {
975                         m_doc->getAudioFileManager().removeFile(nameMap[names[i]]);
976                         emit deleteAudioFile(nameMap[names[i]]);
977                     } else {
978                         RG_WARNING << "slotDeleteUnused(): WARNING: Audio file name " << names[i] << " not in name map";
979                     }
980 
981                     QFile peakFile(QString("%1.pk").arg(names[i]));
982                     peakFile.remove();
983                 }
984             }
985         }
986     }
987 
988     m_fileList->clear();
989     slotPopulateFileList();
990 
991     delete dialog;
992 }
993 
994 void
slotRename()995 AudioManagerDialog::slotRename()
996 {
997     AudioFile *audioFile = getCurrentSelection();
998 
999     if (audioFile == nullptr)
1000         return ;
1001 
1002     bool ok = false;
1003 
1004     QString newText = InputDialog::getText(this,
1005                           tr("Change Audio File label"),
1006                           tr("Enter new label"),
1007                           LineEdit::Normal,
1008                           QString(audioFile->getLabel().c_str()),
1009                           &ok);
1010 
1011     if (ok && !newText.isEmpty())
1012         audioFile->setLabel(qstrtostr(newText));
1013 
1014     slotPopulateFileList();
1015 }
1016 
1017 
1018 /*
1019 void AudioManagerDialog::slotItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
1020 {
1021     //RG_DEBUG << "Warning: AudioManagerDialog::slotItemChanged not implemented";
1022     return;
1023 }
1024 */
1025 
1026 //void AudioManagerDialog::slotSelectionChanged(QTreeWidgetItem *item)
slotSelectionChanged()1027 void AudioManagerDialog::slotSelectionChanged()
1028 {
1029     AudioListItem *aItem = nullptr;
1030     //AudioListItem *aItem = dynamic_cast<AudioListItem*>(item);
1031 
1032     QList<QTreeWidgetItem *> itemsx = m_fileList->selectedItems();
1033     if (itemsx.count() > 0){
1034         aItem = dynamic_cast<AudioListItem*>(itemsx.at(0));
1035     }
1036 
1037     // en/disable Actions
1038     //QAction *ea = findAction("export_audio");
1039     //if (ea) ea->setEnabled("false");
1040 
1041     // If we're on a segment then send a "select" signal
1042     // and enable appropriate buttons.
1043     //
1044     if (aItem && aItem->getSegment()) {
1045 
1046         //### required to enable it?
1047         //if (ea) ea->setEnabled("true");
1048 
1049         SegmentSelection selection;
1050         selection.insert(aItem->getSegment());
1051         emit segmentsSelected(selection);
1052     }
1053 
1054     updateActionState(aItem != nullptr);
1055 }
1056 
1057 void
setSelected(AudioFileId id,const Segment * segment,bool propagate)1058 AudioManagerDialog::setSelected(AudioFileId id,
1059                                 const Segment *segment,
1060                                 bool propagate)
1061 {
1062     // note: this iterates over topLevelItems and childItems too.
1063     // I hope that's what we want to do (?)
1064     // otherwise re-code to iterate over topLevelItems only.
1065     QTreeWidgetItemIterator treeItemIter(m_fileList,
1066                                          QTreeWidgetItemIterator::All);
1067 
1068     QTreeWidgetItem *treeItem = *treeItemIter;
1069 
1070     while (treeItem) {
1071 
1072         AudioListItem *audioItem = dynamic_cast<AudioListItem *>(treeItem);
1073 
1074         if (audioItem) {
1075             if ((audioItem->getId() == id)  &&
1076                 (audioItem->getSegment() == segment)) {
1077 
1078                 selectFileListItemNoSignal(treeItem);
1079 
1080                 // Only propagate to compositionview if asked to
1081                 if (propagate) {
1082                     SegmentSelection selection;
1083                     selection.insert(audioItem->getSegment());
1084                     emit segmentsSelected(selection);
1085                 }
1086 
1087                 return;
1088 
1089             }
1090 
1091         }
1092 
1093         ++treeItemIter;
1094         treeItem = *treeItemIter;
1095 
1096     }
1097 
1098 }
1099 
1100 
1101 void
selectFileListItemNoSignal(QTreeWidgetItem * it)1102 AudioManagerDialog::selectFileListItemNoSignal(QTreeWidgetItem* it)
1103 {
1104     m_fileList->blockSignals(true);
1105 
1106     if (it) {
1107 //        m_fileList->ensureItemVisible(it);
1108         m_fileList->scrollToItem(it, QAbstractItemView::PositionAtTop);
1109 //        m_fileList->setSelected(it, true);
1110         m_fileList->setCurrentItem(it);
1111         updateActionState(true);
1112     } else {
1113         m_fileList->clearSelection();
1114     }
1115 
1116     m_fileList->blockSignals(false);
1117 }
1118 
1119 void
slotCommandExecuted()1120 AudioManagerDialog::slotCommandExecuted()
1121 {
1122     slotPopulateFileList();
1123 }
1124 
1125 void
slotSegmentSelection(const SegmentSelection & segments)1126 AudioManagerDialog::slotSegmentSelection(
1127     const SegmentSelection &segments)
1128 {
1129     const Segment *segment = nullptr;
1130 
1131     for (SegmentSelection::const_iterator it = segments.begin();
1132             it != segments.end(); ++it) {
1133         if ((*it)->getType() == Segment::Audio) {
1134             // Only get one audio segment
1135             if (segment == nullptr)
1136                 segment = *it;
1137             else
1138                 segment = nullptr;
1139         }
1140 
1141     }
1142 
1143     if (segment) {
1144         // We don't propagate this segment setting to the canvas
1145         // as we probably got called from there.
1146         //
1147         setSelected(segment->getAudioFileId(), segment, false);
1148     } else {
1149         selectFileListItemNoSignal(nullptr);
1150     }
1151 
1152 }
1153 
1154 void
slotCancelPlayingAudioFile()1155 AudioManagerDialog::slotCancelPlayingAudioFile()
1156 {
1157     emit cancelPlayingAudioFile(m_playingAudioFile);
1158 }
1159 
1160 void
closePlayingDialog(AudioFileId id)1161 AudioManagerDialog::closePlayingDialog(AudioFileId id)
1162 {
1163     //std::cout << "AudioManagerDialog::closePlayingDialog";
1164     if (m_audioPlayingDialog && id == m_playingAudioFile) {
1165         m_playTimer->stop();
1166         delete m_audioPlayingDialog;
1167         m_audioPlayingDialog = nullptr;
1168     }
1169 
1170 }
1171 
1172 bool
addFile(const QUrl & kurl)1173 AudioManagerDialog::addFile(const QUrl& kurl)
1174 {
1175     AudioFileId id = 0;
1176 
1177     AudioFileManager &aFM = m_doc->getAudioFileManager();
1178 
1179     if (!RosegardenMainWindow::self()->testAudioPath(tr("importing an audio file that needs to be converted or resampled"))) {
1180         return false;
1181     }
1182 
1183     // If multiple audio files are added concurrently, this implementation
1184     // looks funny to the user, but it is functional for now.  NO time for
1185     // a more robust solution.
1186     // ??? By "looks funny" I assume this means that it pops up a new
1187     //     progress dialog for each file being added.  Moving the
1188     //     QProgressDialog up the call stack shouldn't be too hard.
1189     //     That might cover one of the cases anyway.
1190 
1191     // Progress Dialog
1192     // Note: The label text and range will be set later as needed.
1193     QProgressDialog progressDialog(
1194             tr("Adding audio file..."),  // labelText
1195             tr("Cancel"),  // cancelButtonText
1196             0, 100,  // min, max
1197             this);  // parent
1198     progressDialog.setWindowTitle(tr("Rosegarden"));
1199     progressDialog.setWindowModality(Qt::WindowModal);
1200     // Don't want to auto close since this is a multi-step
1201     // process.  Any of the steps may set progress to 100.  We
1202     // will close anyway when this object goes out of scope.
1203     progressDialog.setAutoClose(false);
1204     // Just force the progress dialog up.
1205     // Both Qt4 and Qt5 have bugs related to delayed showing of progress
1206     // dialogs.  In Qt4, the dialog sometimes won't show.  In Qt5, KDE
1207     // based distros might lock up.  See Bug #1546.
1208     progressDialog.show();
1209 
1210     aFM.setProgressDialog(&progressDialog);
1211 
1212     // Flush the event queue.
1213     qApp->processEvents(QEventLoop::AllEvents);
1214 
1215     try {
1216         id = aFM.importURL(kurl, m_sampleRate);
1217     } catch (const AudioFileManager::BadAudioPathException &e) {
1218         QString errorString = tr("Failed to add audio file. ") + strtoqstr(e.getMessage());
1219         QMessageBox::warning(this, tr("Rosegarden"), errorString);
1220         return false;
1221     } catch (const SoundFile::BadSoundFileException &e) {
1222         QString errorString = tr("Failed to add audio file. ") + strtoqstr(e.getMessage());
1223         QMessageBox::warning(this, tr("Rosegarden"), errorString);
1224         return false;
1225     }
1226 
1227     try {
1228         aFM.generatePreview(id);
1229     } catch (const Exception &e) {
1230         QString message = strtoqstr(e.getMessage()) + "\n\n" +
1231                           tr("Try copying this file to a directory where you have write permission and re-add it");
1232         QMessageBox::information(this, tr("Rosegarden"), message);
1233     }
1234 
1235     slotPopulateFileList();
1236 
1237     // tell the sequencer
1238     emit addAudioFile(id);
1239 
1240     return true;
1241 }
1242 
1243 
1244 void
slotDropped(QDropEvent *,QTreeWidget *,const QList<QUrl> & sl)1245 AudioManagerDialog::slotDropped(QDropEvent* /* event */, QTreeWidget*, const QList<QUrl> &sl){
1246     /// signaled from AudioListView on dropEvent, sl = list of items (URLs)
1247     if( sl.empty() ) return;
1248 
1249     // iterate over dropped URIs
1250     for( int i=0; i<sl.count(); i++ ) {
1251         //RG_DEBUG << "slotDropped() - Adding DroppedFile " << sl.at(i);
1252         addFile( sl.at(i) );
1253     }
1254 }
1255 
1256 //void
1257 //AudioManagerDialog::slotDropped(QDropEvent *event, QTreeWidgetItem*)
1258 //{
1259 
1260 /*
1261     //QStrList uri;
1262     QList<QString> uri;
1263 
1264     // see if we can decode a URI.. if not, just ignore it
1265 //    if (QUriDrag::decode(event, uri)) {            //&&& QUriDrag, implement drag/drop
1266 
1267         // okay, we have a URI.. process it
1268 //        for (QString url = uri.first(); !url.isEmpty(); url = uri.next()) { //!!! this one is really weird and uncertain
1269         for (int i=0; i < uri.size(); i++){
1270             QString url = uri.at(i);
1271 
1272             RG_DEBUG << "AudioManagerDialog::dropEvent() : got " << url;
1273 
1274             addFile(QUrl(url));
1275         }
1276 //    }// end if QUriDrag
1277 */
1278 //}
1279 
1280 void
closeEvent(QCloseEvent * e)1281 AudioManagerDialog::closeEvent(QCloseEvent *e)
1282 {
1283     //RG_DEBUG << "closeEvent()\n";
1284     emit closing();
1285     QMainWindow::closeEvent(e);
1286 }
1287 
1288 void
slotClose()1289 AudioManagerDialog::slotClose()
1290 {
1291     //RG_DEBUG << "slotClose()";
1292     emit closing();
1293     close();
1294 }
1295 
1296 void
setAudioSubsystemStatus(bool ok)1297 AudioManagerDialog::setAudioSubsystemStatus(bool ok)
1298 {
1299     // We can do something more fancy in the future but for the moment
1300     // this will suffice.
1301     //
1302     m_audiblePreview = ok;
1303 }
1304 
1305 bool
addAudioFile(const QString & filePath)1306 AudioManagerDialog::addAudioFile(const QString &filePath)
1307 {
1308     QString fp = QFileInfo(filePath).absoluteFilePath();
1309     //RG_DEBUG << "addAudioFile(): fp =" << fp;
1310     return addFile(QUrl::fromLocalFile(fp));
1311 }
1312 
1313 bool
isSelectedTrackAudio()1314 AudioManagerDialog::isSelectedTrackAudio()
1315 {
1316     Composition &comp = m_doc->getComposition();
1317     Studio &studio = m_doc->getStudio();
1318 
1319     TrackId currentTrackId = comp.getSelectedTrack();
1320     Track *track = comp.getTrackById(currentTrackId);
1321 
1322     if (track) {
1323         InstrumentId ii = track->getInstrument();
1324         Instrument *instrument = studio.getInstrumentById(ii);
1325 
1326         if (instrument &&
1327                 instrument->getType() == Instrument::Audio)
1328             return true;
1329     }
1330 
1331     return false;
1332 
1333 }
1334 
1335 void
slotDistributeOnMidiSegment()1336 AudioManagerDialog::slotDistributeOnMidiSegment()
1337 {
1338     //RG_DEBUG << "slotDistributeOnMidiSegment()";
1339 
1340     //Composition &comp = m_doc->getComposition();
1341 
1342     QList<RosegardenMainViewWidget*> viewList_ = m_doc->getViewList();
1343     QListIterator<RosegardenMainViewWidget*> viewList(viewList_);
1344 
1345     RosegardenMainViewWidget *w = nullptr;
1346     SegmentSelection selection;
1347 
1348     viewList.toFront();
1349     while (viewList.hasNext()){
1350         w = viewList.next();
1351         selection = w->getSelection();
1352     }
1353 
1354     // Store the insert times in a local vector
1355     //
1356     std::vector<timeT> insertTimes;
1357 
1358     for (SegmentSelection::iterator i = selection.begin();
1359             i != selection.end(); ++i) {
1360         // For MIDI (Internal) Segments only of course
1361         //
1362         if ((*i)->getType() == Segment::Internal) {
1363             for (Segment::iterator it = (*i)->begin(); it != (*i)->end(); ++it) {
1364                 if ((*it)->isa(Note::EventType))
1365                     insertTimes.push_back((*it)->getAbsoluteTime());
1366             }
1367         }
1368     }
1369 
1370 #if 0
1371     for (unsigned int i = 0; i < insertTimes.size(); ++i) {
1372         RG_DEBUG << "slotDistributeOnMidiSegment(): Insert audio segment at " << insertTimes[i];
1373     }
1374 #endif
1375 }
1376 
1377 void
slotHelpRequested()1378 AudioManagerDialog::slotHelpRequested()
1379 {
1380     // TRANSLATORS: if the manual is translated into your language, you can
1381     // change the two-letter language code in this URL to point to your language
1382     // version, eg. "http://rosegardenmusic.com/wiki/doc:audioManager-es" for the
1383     // Spanish version. If your language doesn't yet have a translation, feel
1384     // free to create one.
1385     QString helpURL = tr("http://rosegardenmusic.com/wiki/doc:audioManager-en");
1386     QDesktopServices::openUrl(QUrl(helpURL));
1387 }
1388 
1389 
1390 void
slotHelpAbout()1391 AudioManagerDialog::slotHelpAbout()
1392 {
1393     new AboutDialog(this);
1394 }
1395 }
1396