You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1256 lines
38 KiB
1256 lines
38 KiB
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
|
Bown to claim authorship of this work have been asserted.
|
|
|
|
Other copyrights also apply to some parts of this work. Please
|
|
see the AUTHORS file and individual file headers for details.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version. See the file
|
|
COPYING included with this distribution for more information.
|
|
*/
|
|
|
|
|
|
#include "AudioManagerDialog.h"
|
|
#include <tdeapplication.h>
|
|
|
|
#include "base/Event.h"
|
|
#include "misc/Debug.h"
|
|
#include "misc/Strings.h"
|
|
#include "AudioPlayingDialog.h"
|
|
#include "base/Composition.h"
|
|
#include "base/Exception.h"
|
|
#include "base/Instrument.h"
|
|
#include "base/MidiProgram.h"
|
|
#include "base/NotationTypes.h"
|
|
#include "base/RealTime.h"
|
|
#include "base/Segment.h"
|
|
#include "base/Selection.h"
|
|
#include "base/Studio.h"
|
|
#include "base/Track.h"
|
|
#include "document/MultiViewCommandHistory.h"
|
|
#include "document/RosegardenGUIDoc.h"
|
|
#include "document/ConfigGroups.h"
|
|
#include "gui/application/RosegardenGUIView.h"
|
|
#include "gui/application/RosegardenApplication.h"
|
|
#include "gui/widgets/AudioListItem.h"
|
|
#include "gui/widgets/AudioListView.h"
|
|
#include "gui/widgets/CurrentProgressDialog.h"
|
|
#include "gui/widgets/ProgressDialog.h"
|
|
#include "sound/AudioFile.h"
|
|
#include "sound/AudioFileManager.h"
|
|
#include "sound/WAVAudioFile.h"
|
|
#include "UnusedAudioSelectionDialog.h"
|
|
#include <tdelocale.h>
|
|
#include <kstddirs.h>
|
|
#include <tdeaction.h>
|
|
#include <kcommand.h>
|
|
#include <tdefiledialog.h>
|
|
#include <tdeglobal.h>
|
|
#include <klineeditdlg.h>
|
|
#include <tdelistview.h>
|
|
#include <tdemainwindow.h>
|
|
#include <tdemessagebox.h>
|
|
#include <kstdaction.h>
|
|
#include <kurl.h>
|
|
#include <kxmlguiclient.h>
|
|
#include <tdeio/netaccess.h>
|
|
#include <tqaccel.h>
|
|
#include <tqcstring.h>
|
|
#include <tqdatastream.h>
|
|
#include <tqdialog.h>
|
|
#include <tqdragobject.h>
|
|
#include <tqfile.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqiconset.h>
|
|
#include <tqlabel.h>
|
|
#include <tqlistview.h>
|
|
#include <tqpainter.h>
|
|
#include <tqpixmap.h>
|
|
#include <tqstring.h>
|
|
#include <tqstrlist.h>
|
|
#include <tqtimer.h>
|
|
#include <tqvbox.h>
|
|
#include <tqwidget.h>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
const int AudioManagerDialog::m_maxPreviewWidth = 100;
|
|
const int AudioManagerDialog::m_previewHeight = 30;
|
|
const char* const AudioManagerDialog::m_listViewLayoutName = "AudioManagerDialog Layout";
|
|
|
|
AudioManagerDialog::AudioManagerDialog(TQWidget *parent,
|
|
RosegardenGUIDoc *doc):
|
|
TDEMainWindow(parent, "audioManagerDialog"),
|
|
m_doc(doc),
|
|
m_playingAudioFile(0),
|
|
m_audioPlayingDialog(0),
|
|
m_playTimer(new TQTimer(this)),
|
|
m_audiblePreview(true)
|
|
{
|
|
setCaption(i18n("Audio File Manager"));
|
|
setWFlags(WDestructiveClose);
|
|
|
|
TQVBox *box = new TQVBox(this);
|
|
setCentralWidget(box);
|
|
box->setMargin(10);
|
|
box->setSpacing(5);
|
|
|
|
m_sampleRate = 0;
|
|
|
|
TQCString replyType;
|
|
TQByteArray replyData;
|
|
if (rgapp->sequencerCall("getSampleRate()", replyType, replyData)) {
|
|
TQDataStream streamIn(replyData, IO_ReadOnly);
|
|
unsigned int result;
|
|
streamIn >> result;
|
|
m_sampleRate = result;
|
|
}
|
|
|
|
m_fileList = new AudioListView(box);
|
|
|
|
m_wrongSampleRates = new TQLabel(i18n("* Some audio files are encoded at a sample rate different from that of the JACK audio server.\nRosegarden will play them at the correct speed, but they will sound terrible.\nPlease consider resampling such files externally, or adjusting the sample rate of the JACK server."), box);
|
|
m_wrongSampleRates->hide();
|
|
|
|
TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
|
|
TQIconSet icon(TQPixmap(pixmapDir + "/toolbar/transport-play.xpm"));
|
|
|
|
new TDEAction(i18n("&Add Audio File..."), "document-open", 0, this,
|
|
TQT_SLOT(slotAdd()), actionCollection(), "add_audio");
|
|
|
|
new TDEAction(i18n("&Unload Audio File"), "edit-delete", 0, this,
|
|
TQT_SLOT(slotRemove()),
|
|
actionCollection(), "remove_audio");
|
|
|
|
icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/transport-play.xpm"));
|
|
new TDEAction(i18n("&Play Preview"), icon, 0, this,
|
|
TQT_SLOT(slotPlayPreview()),
|
|
actionCollection(), "preview_audio");
|
|
|
|
/*!!! Not actually implemented -- this never worked right!
|
|
new TDEAction(i18n("Re&label"), 0, 0, this,
|
|
TQT_SLOT(slotRename()),
|
|
actionCollection(), "rename_audio");
|
|
*/
|
|
|
|
icon = TQIconSet(TQPixmap(pixmapDir + "/toolbar/insert_audio_into_track.xpm"));
|
|
new TDEAction(i18n("&Insert into Selected Audio Track"),
|
|
icon, 0, this, TQT_SLOT(slotInsert()),
|
|
actionCollection(), "insert_audio");
|
|
|
|
new TDEAction(i18n("Unload &all Audio Files"), 0, 0, this,
|
|
TQT_SLOT(slotRemoveAll()),
|
|
actionCollection(), "remove_all_audio");
|
|
|
|
new TDEAction(i18n("Unload all &Unused Audio Files"), 0, 0, this,
|
|
TQT_SLOT(slotRemoveAllUnused()),
|
|
actionCollection(), "remove_all_unused_audio");
|
|
|
|
new TDEAction(i18n("&Delete Unused Audio Files..."), 0, 0, this,
|
|
TQT_SLOT(slotDeleteUnused()),
|
|
actionCollection(), "delete_unused_audio");
|
|
|
|
new TDEAction(i18n("&Export Audio File..."), "fileexport", 0, this,
|
|
TQT_SLOT(slotExportAudio()),
|
|
actionCollection(), "export_audio");
|
|
/*
|
|
new TDEAction(i18n("Distribute Audio on &MIDI"),
|
|
0, 0, this,
|
|
TQT_SLOT(slotDistributeOnMidiSegment()),
|
|
actionCollection(),
|
|
"distribute_audio");
|
|
*/
|
|
// Set the column names
|
|
//
|
|
m_fileList->addColumn(i18n("Name")); // 0
|
|
m_fileList->addColumn(i18n("Duration")); // 1
|
|
m_fileList->addColumn(i18n("Envelope")); // 2
|
|
m_fileList->addColumn(i18n("Sample rate")); // 3
|
|
m_fileList->addColumn(i18n("Channels")); // 4
|
|
m_fileList->addColumn(i18n("Resolution")); // 5
|
|
m_fileList->addColumn(i18n("File")); // 6
|
|
|
|
m_fileList->setColumnAlignment(1, TQt::AlignHCenter);
|
|
m_fileList->setColumnAlignment(2, TQt::AlignHCenter);
|
|
m_fileList->setColumnAlignment(3, TQt::AlignHCenter);
|
|
m_fileList->setColumnAlignment(4, TQt::AlignHCenter);
|
|
m_fileList->setColumnAlignment(5, TQt::AlignHCenter);
|
|
|
|
m_fileList->restoreLayout(kapp->config(), m_listViewLayoutName);
|
|
|
|
// a minimum width for the list box
|
|
//m_fileList->setMinimumWidth(300);
|
|
|
|
// show focus across all columns
|
|
m_fileList->setAllColumnsShowFocus(true);
|
|
|
|
// show tooltips when columns are partially hidden
|
|
m_fileList->setShowToolTips(true);
|
|
|
|
// connect selection mechanism
|
|
connect(m_fileList, TQT_SIGNAL(selectionChanged(TQListViewItem*)),
|
|
TQT_SLOT(slotSelectionChanged(TQListViewItem*)));
|
|
|
|
connect(m_fileList, TQT_SIGNAL(dropped(TQDropEvent*, TQListViewItem*)),
|
|
TQT_SLOT(slotDropped(TQDropEvent*, TQListViewItem*)));
|
|
|
|
// setup local accelerators
|
|
//
|
|
m_accelerators = new TQAccel(this);
|
|
|
|
// delete
|
|
//
|
|
m_accelerators->connectItem(m_accelerators->insertItem(Key_Delete),
|
|
this,
|
|
TQT_SLOT(slotRemove()));
|
|
|
|
slotPopulateFileList();
|
|
|
|
// Connect command history for updates
|
|
//
|
|
connect(getCommandHistory(), TQT_SIGNAL(commandExecuted(KCommand *)),
|
|
this, TQT_SLOT(slotCommandExecuted(KCommand *)));
|
|
|
|
//setInitialSize(configDialogSize(AudioManagerDialogConfigGroup));
|
|
|
|
connect(m_playTimer, TQT_SIGNAL(timeout()),
|
|
this, TQT_SLOT(slotCancelPlayingAudio()));
|
|
|
|
KStdAction::close(this,
|
|
TQT_SLOT(slotClose()),
|
|
actionCollection());
|
|
|
|
createGUI("audiomanager.rc");
|
|
|
|
updateActionState(false);
|
|
}
|
|
|
|
AudioManagerDialog::~AudioManagerDialog()
|
|
{
|
|
RG_DEBUG << "\n*** AudioManagerDialog::~AudioManagerDialog\n" << endl;
|
|
m_fileList->saveLayout(kapp->config(), m_listViewLayoutName);
|
|
//saveDialogSize(AudioManagerDialogConfigGroup);
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotPopulateFileList()
|
|
{
|
|
// create pixmap of given size
|
|
TQPixmap *audioPixmap = new TQPixmap(m_maxPreviewWidth, m_previewHeight);
|
|
|
|
// Store last selected item if we have one
|
|
//
|
|
AudioListItem *selectedItem =
|
|
dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
|
|
AudioFileId lastId = 0;
|
|
Segment *lastSegment = 0;
|
|
bool findSelection = false;
|
|
bool foundSelection = false;
|
|
|
|
if (selectedItem) {
|
|
lastId = selectedItem->getId();
|
|
lastSegment = selectedItem->getSegment();
|
|
findSelection = true;
|
|
}
|
|
|
|
// We don't want the selection changes to be propagated
|
|
// to the main view
|
|
//
|
|
m_fileList->blockSignals(true);
|
|
|
|
// clear file list and disable associated action buttons
|
|
m_fileList->clear();
|
|
|
|
if (m_doc->getAudioFileManager().begin() ==
|
|
m_doc->getAudioFileManager().end()) {
|
|
// Turn off selection and report empty list
|
|
//
|
|
new AudioListItem(m_fileList, i18n("<no audio files>"), 0);
|
|
m_fileList->setSelectionMode(TQListView::NoSelection);
|
|
m_fileList->setRootIsDecorated(false);
|
|
|
|
m_fileList->blockSignals(false);
|
|
updateActionState(false);
|
|
return ;
|
|
}
|
|
|
|
// show tree hierarchy
|
|
m_fileList->setRootIsDecorated(true);
|
|
|
|
// enable selection
|
|
m_fileList->setSelectionMode(TQListView::Single);
|
|
|
|
// for the sample file length
|
|
TQString msecs, sRate;
|
|
RealTime length;
|
|
|
|
// Create a vector of audio Segments only
|
|
//
|
|
std::vector<Segment*> segments;
|
|
std::vector<Segment*>::const_iterator iit;
|
|
|
|
for (Composition::iterator it = m_doc->getComposition().begin();
|
|
it != m_doc->getComposition().end(); ++it) {
|
|
if ((*it)->getType() == Segment::Audio)
|
|
segments.push_back(*it);
|
|
}
|
|
|
|
// duration
|
|
RealTime segmentDuration;
|
|
bool wrongSampleRates = false;
|
|
|
|
for (std::vector<AudioFile*>::const_iterator
|
|
it = m_doc->getAudioFileManager().begin();
|
|
it != m_doc->getAudioFileManager().end();
|
|
++it) {
|
|
try {
|
|
m_doc->getAudioFileManager().
|
|
drawPreview((*it)->getId(),
|
|
RealTime::zeroTime,
|
|
(*it)->getLength(),
|
|
audioPixmap);
|
|
} catch (Exception e) {
|
|
audioPixmap->fill(); // white
|
|
TQPainter p(audioPixmap);
|
|
p.setPen(TQt::black);
|
|
p.drawText(10, m_previewHeight / 2, TQString("<no preview>"));
|
|
}
|
|
|
|
//!!! Why isn't the label the label the user assigned to the file?
|
|
// Why do we allow the user to assign a label at all, then?
|
|
|
|
TQString label = TQString((*it)->getShortFilename().c_str());
|
|
|
|
// Set the label, duration, envelope pixmap and filename
|
|
//
|
|
AudioListItem *item = new AudioListItem(m_fileList, label,
|
|
(*it)->getId());
|
|
// Duration
|
|
//
|
|
length = (*it)->getLength();
|
|
msecs.sprintf("%03d", length.nsec / 1000000);
|
|
item->setText(1, TQString("%1.%2s").arg(length.sec).arg(msecs));
|
|
|
|
// set start time and duration
|
|
item->setStartTime(RealTime::zeroTime);
|
|
item->setDuration(length);
|
|
|
|
// Envelope pixmap
|
|
//
|
|
item->setPixmap(2, *audioPixmap);
|
|
|
|
// File location
|
|
//
|
|
item->setText(6, TQString(
|
|
m_doc->getAudioFileManager().
|
|
substituteHomeForTilde((*it)->getFilename()).c_str()));
|
|
|
|
// Resolution
|
|
//
|
|
item->setText(5, TQString("%1 bits").arg((*it)->getBitsPerSample()));
|
|
|
|
// Channels
|
|
//
|
|
item->setText(4, TQString("%1").arg((*it)->getChannels()));
|
|
|
|
// Sample rate
|
|
//
|
|
if (m_sampleRate != 0 && (*it)->getSampleRate() != m_sampleRate) {
|
|
sRate.sprintf("%.1f KHz *", float((*it)->getSampleRate()) / 1000.0);
|
|
wrongSampleRates = true;
|
|
} else {
|
|
sRate.sprintf("%.1f KHz", float((*it)->getSampleRate()) / 1000.0);
|
|
}
|
|
item->setText(3, sRate);
|
|
|
|
// Test audio file element for selection criteria
|
|
//
|
|
if (findSelection && lastSegment == 0 && lastId == (*it)->getId()) {
|
|
m_fileList->setSelected(item, true);
|
|
findSelection = false;
|
|
}
|
|
|
|
// Add children
|
|
//
|
|
for (iit = segments.begin(); iit != segments.end(); iit++) {
|
|
if ((*iit)->getAudioFileId() == (*it)->getId()) {
|
|
AudioListItem *childItem =
|
|
new AudioListItem(item,
|
|
TQString((*iit)->getLabel().c_str()),
|
|
(*it)->getId());
|
|
segmentDuration = (*iit)->getAudioEndTime() -
|
|
(*iit)->getAudioStartTime();
|
|
|
|
// store the start time
|
|
//
|
|
childItem->setStartTime((*iit)->getAudioStartTime());
|
|
childItem->setDuration(segmentDuration);
|
|
|
|
// Write segment duration
|
|
//
|
|
msecs.sprintf("%03d", segmentDuration.nsec / 1000000);
|
|
childItem->setText(1, TQString("%1.%2s")
|
|
.arg(segmentDuration.sec)
|
|
.arg(msecs));
|
|
|
|
try {
|
|
m_doc->getAudioFileManager().
|
|
drawHighlightedPreview((*it)->getId(),
|
|
RealTime::zeroTime,
|
|
(*it)->getLength(),
|
|
(*iit)->getAudioStartTime(),
|
|
(*iit)->getAudioEndTime(),
|
|
audioPixmap);
|
|
} catch (Exception e) {
|
|
// should already be set to "no file"
|
|
}
|
|
|
|
// set pixmap
|
|
//
|
|
childItem->setPixmap(2, *audioPixmap);
|
|
|
|
// set segment
|
|
//
|
|
childItem->setSegment(*iit);
|
|
|
|
if (findSelection && lastSegment == (*iit)) {
|
|
m_fileList->setSelected(childItem, true);
|
|
findSelection = false;
|
|
foundSelection = true;
|
|
}
|
|
|
|
// Add children
|
|
}
|
|
}
|
|
}
|
|
|
|
updateActionState(foundSelection);
|
|
|
|
if (wrongSampleRates) {
|
|
m_wrongSampleRates->show();
|
|
} else {
|
|
m_wrongSampleRates->hide();
|
|
}
|
|
|
|
m_fileList->blockSignals(false);
|
|
}
|
|
|
|
AudioFile*
|
|
AudioManagerDialog::getCurrentSelection()
|
|
{
|
|
// try and get the selected item
|
|
AudioListItem *item =
|
|
dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
|
|
if (item == 0)
|
|
return 0;
|
|
|
|
std::vector<AudioFile*>::const_iterator it;
|
|
|
|
for (it = m_doc->getAudioFileManager().begin();
|
|
it != m_doc->getAudioFileManager().end();
|
|
++it) {
|
|
// If we match then return the valid AudioFile
|
|
//
|
|
if (item->getId() == (*it)->getId())
|
|
return (*it);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotExportAudio()
|
|
{
|
|
WAVAudioFile *sourceFile
|
|
= dynamic_cast<WAVAudioFile*>(getCurrentSelection());
|
|
|
|
AudioListItem *item =
|
|
dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
|
|
|
|
Segment *segment = item->getSegment();
|
|
|
|
TQString saveFile =
|
|
KFileDialog::getSaveFileName(":WAVS",
|
|
i18n("*.wav|WAV files (*.wav)"),
|
|
this, i18n("Choose a name to save this file as"));
|
|
|
|
if (sourceFile == 0 || item == 0 || saveFile.isEmpty())
|
|
return ;
|
|
|
|
// Check for a dot extension and append ".wav" if not found
|
|
//
|
|
if (saveFile.contains(".") == 0)
|
|
saveFile += ".wav";
|
|
|
|
ProgressDialog progressDlg(i18n("Exporting audio file..."),
|
|
100,
|
|
this);
|
|
|
|
progressDlg.progressBar()->setProgress(0);
|
|
|
|
RealTime clipStartTime = RealTime::zeroTime;
|
|
RealTime clipDuration = sourceFile->getLength();
|
|
|
|
if (segment) {
|
|
clipStartTime = segment->getAudioStartTime();
|
|
clipDuration = segment->getAudioEndTime() - clipStartTime;
|
|
}
|
|
|
|
WAVAudioFile *destFile
|
|
= new WAVAudioFile(qstrtostr(saveFile),
|
|
sourceFile->getChannels(),
|
|
sourceFile->getSampleRate(),
|
|
sourceFile->getBytesPerSecond(),
|
|
sourceFile->getBytesPerFrame(),
|
|
sourceFile->getBitsPerSample());
|
|
|
|
if (sourceFile->open() == false) {
|
|
delete destFile;
|
|
return ;
|
|
}
|
|
|
|
destFile->write();
|
|
|
|
sourceFile->scanTo(clipStartTime);
|
|
destFile->appendSamples(sourceFile->getSampleFrameSlice(clipDuration));
|
|
|
|
destFile->close();
|
|
sourceFile->close();
|
|
delete destFile;
|
|
|
|
progressDlg.progressBar()->setProgress(100);
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotRemove()
|
|
{
|
|
AudioFile *audioFile = getCurrentSelection();
|
|
AudioListItem *item =
|
|
dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
|
|
|
|
if (audioFile == 0 || item == 0)
|
|
return ;
|
|
|
|
// If we're on a Segment then delete it at the Composition
|
|
// and refresh the list.
|
|
//
|
|
if (item->getSegment()) {
|
|
// Get the next item to highlight
|
|
//
|
|
TQListViewItem *newItem = item->itemBelow();
|
|
|
|
// Or try above
|
|
//
|
|
if (newItem == 0)
|
|
newItem = item->itemAbove();
|
|
|
|
// Or the parent
|
|
//
|
|
if (newItem == 0)
|
|
newItem = item->parent();
|
|
|
|
// Get the id and segment of the next item so that we can
|
|
// match against it
|
|
//
|
|
AudioFileId id = 0;
|
|
Segment *segment = 0;
|
|
AudioListItem *aItem = dynamic_cast<AudioListItem*>(newItem);
|
|
|
|
if (aItem) {
|
|
segment = aItem->getSegment();
|
|
id = aItem->getId();
|
|
}
|
|
|
|
// Jump to new selection
|
|
//
|
|
if (newItem)
|
|
setSelected(id, segment, true); // propagate
|
|
|
|
// Do it - will force update
|
|
//
|
|
SegmentSelection selection;
|
|
selection.insert(item->getSegment());
|
|
emit deleteSegments(selection);
|
|
|
|
return ;
|
|
}
|
|
|
|
// remove segments along with audio file
|
|
//
|
|
AudioFileId id = audioFile->getId();
|
|
SegmentSelection selection;
|
|
Composition &comp = m_doc->getComposition();
|
|
|
|
bool haveSegments = false;
|
|
for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
|
|
if ((*it)->getType() == Segment::Audio &&
|
|
(*it)->getAudioFileId() == id) {
|
|
haveSegments = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (haveSegments) {
|
|
|
|
TQString question = i18n("This will unload audio file \"%1\" and remove all associated segments. Are you sure?")
|
|
.arg(TQString(audioFile->getFilename().c_str()));
|
|
|
|
// Ask the question
|
|
int reply = KMessageBox::warningContinueCancel(this, question);
|
|
|
|
if (reply != KMessageBox::Continue)
|
|
return ;
|
|
}
|
|
|
|
for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
|
|
if ((*it)->getType() == Segment::Audio &&
|
|
(*it)->getAudioFileId() == id)
|
|
selection.insert(*it);
|
|
}
|
|
emit deleteSegments(selection);
|
|
|
|
m_doc->notifyAudioFileRemoval(id);
|
|
|
|
m_doc->getAudioFileManager().removeFile(id);
|
|
|
|
// tell the sequencer
|
|
emit deleteAudioFile(id);
|
|
|
|
// repopulate
|
|
slotPopulateFileList();
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotPlayPreview()
|
|
{
|
|
AudioFile *audioFile = getCurrentSelection();
|
|
AudioListItem *item =
|
|
dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
|
|
|
|
if (item == 0 || audioFile == 0)
|
|
return ;
|
|
|
|
// store the audio file we're playing
|
|
m_playingAudioFile = audioFile->getId();
|
|
|
|
// tell the sequencer
|
|
emit playAudioFile(audioFile->getId(),
|
|
item->getStartTime(),
|
|
item->getDuration());
|
|
|
|
// now open up the playing dialog
|
|
//
|
|
m_audioPlayingDialog =
|
|
new AudioPlayingDialog(this, TQString(audioFile->getFilename().c_str()));
|
|
|
|
// Setup timer to pop down dialog after file has completed
|
|
//
|
|
int msecs = item->getDuration().sec * 1000 +
|
|
item->getDuration().nsec / 1000000;
|
|
m_playTimer->start(msecs, true); // single shot
|
|
|
|
// just execute
|
|
//
|
|
if (m_audioPlayingDialog->exec() == TQDialog::Rejected)
|
|
emit cancelPlayingAudioFile(m_playingAudioFile);
|
|
|
|
delete m_audioPlayingDialog;
|
|
m_audioPlayingDialog = 0;
|
|
|
|
m_playTimer->stop();
|
|
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotCancelPlayingAudio()
|
|
{
|
|
//std::cout << "AudioManagerDialog::slotCancelPlayingAudio" << std::endl;
|
|
if (m_audioPlayingDialog) {
|
|
m_playTimer->stop();
|
|
delete m_audioPlayingDialog;
|
|
m_audioPlayingDialog = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotAdd()
|
|
{
|
|
TQString extensionList = i18n("*.wav|WAV files (*.wav)\n*.*|All files");
|
|
|
|
if (RosegardenGUIApp::self()->haveAudioImporter()) {
|
|
//!!! This list really needs to come from the importer helper program
|
|
// (which has an option to supply it -- we just haven't recorded it)
|
|
extensionList = i18n("*.wav *.flac *.ogg *.mp3|Audio files (*.wav *.flac *.ogg *.mp3)\n*.wav|WAV files (*.wav)\n*.flac|FLAC files (*.flac)\n*.ogg|Ogg files (*.ogg)\n*.mp3|MP3 files (*.mp3)\n*.*|All files");
|
|
}
|
|
|
|
KURL::List kurlList =
|
|
KFileDialog::getOpenURLs(":WAVS",
|
|
extensionList,
|
|
// i18n("*.wav|WAV files (*.wav)\n*.mp3|MP3 files (*.mp3)"),
|
|
this, i18n("Select one or more audio files"));
|
|
|
|
KURL::List::iterator it;
|
|
|
|
for (it = kurlList.begin(); it != kurlList.end(); ++it)
|
|
addFile(*it);
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::updateActionState(bool haveSelection)
|
|
{
|
|
if (m_doc->getAudioFileManager().begin() ==
|
|
m_doc->getAudioFileManager().end()) {
|
|
stateChanged("have_audio_files", KXMLGUIClient::StateReverse);
|
|
} else {
|
|
stateChanged("have_audio_files", KXMLGUIClient::StateNoReverse);
|
|
}
|
|
|
|
if (haveSelection) {
|
|
|
|
stateChanged("have_audio_selected", KXMLGUIClient::StateNoReverse);
|
|
|
|
if (m_audiblePreview) {
|
|
stateChanged("have_audible_preview", KXMLGUIClient::StateNoReverse);
|
|
} else {
|
|
stateChanged("have_audible_preview", KXMLGUIClient::StateReverse);
|
|
}
|
|
|
|
if (isSelectedTrackAudio()) {
|
|
stateChanged("have_audio_insertable", KXMLGUIClient::StateNoReverse);
|
|
} else {
|
|
stateChanged("have_audio_insertable", KXMLGUIClient::StateReverse);
|
|
}
|
|
|
|
} else {
|
|
stateChanged("have_audio_selected", KXMLGUIClient::StateReverse);
|
|
stateChanged("have_audio_insertable", KXMLGUIClient::StateReverse);
|
|
stateChanged("have_audible_preview", KXMLGUIClient::StateReverse);
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotInsert()
|
|
{
|
|
AudioFile *audioFile = getCurrentSelection();
|
|
if (audioFile == 0)
|
|
return ;
|
|
|
|
RG_DEBUG << "AudioManagerDialog::slotInsert\n";
|
|
|
|
emit insertAudioSegment(audioFile->getId(),
|
|
RealTime::zeroTime,
|
|
audioFile->getLength());
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotRemoveAll()
|
|
{
|
|
TQString question =
|
|
i18n("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?");
|
|
|
|
int reply = KMessageBox::warningContinueCancel(this, question);
|
|
|
|
if (reply != KMessageBox::Continue)
|
|
return ;
|
|
|
|
SegmentSelection selection;
|
|
Composition &comp = m_doc->getComposition();
|
|
|
|
for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
|
|
if ((*it)->getType() == Segment::Audio)
|
|
selection.insert(*it);
|
|
}
|
|
// delete segments
|
|
emit deleteSegments(selection);
|
|
|
|
for (std::vector<AudioFile*>::const_iterator
|
|
aIt = m_doc->getAudioFileManager().begin();
|
|
aIt != m_doc->getAudioFileManager().end(); ++aIt) {
|
|
m_doc->notifyAudioFileRemoval((*aIt)->getId());
|
|
}
|
|
|
|
m_doc->getAudioFileManager().clear();
|
|
|
|
// and now the audio files
|
|
emit deleteAllAudioFiles();
|
|
|
|
// clear the file list
|
|
m_fileList->clear();
|
|
slotPopulateFileList();
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotRemoveAllUnused()
|
|
{
|
|
TQString question =
|
|
i18n("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?");
|
|
|
|
int reply = KMessageBox::warningContinueCancel(this, question);
|
|
|
|
if (reply != KMessageBox::Continue)
|
|
return ;
|
|
|
|
std::set
|
|
<AudioFileId> audioFiles;
|
|
Composition &comp = m_doc->getComposition();
|
|
|
|
for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
|
|
if ((*it)->getType() == Segment::Audio)
|
|
audioFiles.insert((*it)->getAudioFileId());
|
|
}
|
|
|
|
std::vector<AudioFileId> toDelete;
|
|
for (std::vector<AudioFile*>::const_iterator
|
|
aIt = m_doc->getAudioFileManager().begin();
|
|
aIt != m_doc->getAudioFileManager().end(); ++aIt) {
|
|
if (audioFiles.find((*aIt)->getId()) == audioFiles.end())
|
|
toDelete.push_back((*aIt)->getId());
|
|
}
|
|
|
|
// Delete the audio files from the AFM
|
|
//
|
|
for (std::vector<AudioFileId>::iterator dIt = toDelete.begin();
|
|
dIt != toDelete.end(); ++dIt) {
|
|
|
|
m_doc->notifyAudioFileRemoval(*dIt);
|
|
m_doc->getAudioFileManager().removeFile(*dIt);
|
|
emit deleteAudioFile(*dIt);
|
|
}
|
|
|
|
// clear the file list
|
|
m_fileList->clear();
|
|
slotPopulateFileList();
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotDeleteUnused()
|
|
{
|
|
std::set
|
|
<AudioFileId> audioFiles;
|
|
Composition &comp = m_doc->getComposition();
|
|
|
|
for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
|
|
if ((*it)->getType() == Segment::Audio)
|
|
audioFiles.insert((*it)->getAudioFileId());
|
|
}
|
|
|
|
std::vector<TQString> toDelete;
|
|
std::map<TQString, AudioFileId> nameMap;
|
|
|
|
for (std::vector<AudioFile*>::const_iterator
|
|
aIt = m_doc->getAudioFileManager().begin();
|
|
aIt != m_doc->getAudioFileManager().end(); ++aIt) {
|
|
if (audioFiles.find((*aIt)->getId()) == audioFiles.end()) {
|
|
toDelete.push_back(strtoqstr((*aIt)->getFilename()));
|
|
nameMap[strtoqstr((*aIt)->getFilename())] = (*aIt)->getId();
|
|
}
|
|
}
|
|
|
|
UnusedAudioSelectionDialog *dialog = new UnusedAudioSelectionDialog
|
|
(this,
|
|
i18n("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"),
|
|
toDelete);
|
|
|
|
if (dialog->exec() == TQDialog::Accepted) {
|
|
|
|
std::vector<TQString> names = dialog->getSelectedAudioFileNames();
|
|
|
|
if (names.size() > 0) {
|
|
|
|
TQString question =
|
|
i18n("<qt>About to delete 1 audio file permanently from the hard disk.<br>This action cannot be undone, and there will be no way to recover this file.<br>Are you sure?</qt>\n", "<qt>About to delete %n audio files permanently from the hard disk.<br>This action cannot be undone, and there will be no way to recover these files.<br>Are you sure?</qt>", names.size());
|
|
|
|
int reply = KMessageBox::warningContinueCancel(this, question);
|
|
|
|
if (reply != KMessageBox::Continue) {
|
|
delete dialog;
|
|
return ;
|
|
}
|
|
|
|
for (int i = 0; i < names.size(); ++i) {
|
|
std::cerr << i << ": " << names[i].ascii() << std::endl;
|
|
TQFile file(names[i]);
|
|
if (!file.remove()) {
|
|
KMessageBox::error(this, i18n("File %1 could not be deleted.").arg(names[i]));
|
|
} else {
|
|
if (nameMap.find(names[i]) != nameMap.end()) {
|
|
m_doc->getAudioFileManager().removeFile(nameMap[names[i]]);
|
|
emit deleteAudioFile(nameMap[names[i]]);
|
|
} else {
|
|
std::cerr << "WARNING: Audio file name " << names[i].ascii() << " not in name map" << std::endl;
|
|
}
|
|
|
|
TQFile peakFile(TQString("%1.pk").arg(names[i]));
|
|
peakFile.remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_fileList->clear();
|
|
slotPopulateFileList();
|
|
|
|
delete dialog;
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotRename()
|
|
{
|
|
AudioFile *audioFile = getCurrentSelection();
|
|
|
|
if (audioFile == 0)
|
|
return ;
|
|
|
|
bool ok = false;
|
|
|
|
TQString newText = KLineEditDlg::getText(
|
|
i18n("Change Audio File label"),
|
|
i18n("Enter new label"),
|
|
TQString(audioFile->getName().c_str()),
|
|
&ok,
|
|
this);
|
|
|
|
if ( ok && !newText.isEmpty() )
|
|
audioFile->setName(qstrtostr(newText));
|
|
|
|
slotPopulateFileList();
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotSelectionChanged(TQListViewItem *item)
|
|
{
|
|
AudioListItem *aItem = dynamic_cast<AudioListItem*>(item);
|
|
|
|
// If we're on a segment then send a "select" signal
|
|
// and enable appropriate buttons.
|
|
//
|
|
if (aItem && aItem->getSegment()) {
|
|
SegmentSelection selection;
|
|
selection.insert(aItem->getSegment());
|
|
emit segmentsSelected(selection);
|
|
}
|
|
|
|
updateActionState(aItem != 0);
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::setSelected(AudioFileId id,
|
|
const Segment *segment,
|
|
bool propagate)
|
|
{
|
|
TQListViewItem *it = m_fileList->firstChild();
|
|
TQListViewItem *chIt = 0;
|
|
AudioListItem *aItem;
|
|
|
|
while (it) {
|
|
// If we're looking for a top level audio file
|
|
if (segment == 0) {
|
|
aItem = dynamic_cast<AudioListItem*>(it);
|
|
|
|
if (aItem->getId() == id) {
|
|
selectFileListItemNoSignal(it);
|
|
return ;
|
|
}
|
|
} else // look for a child
|
|
{
|
|
if (it->childCount() > 0)
|
|
chIt = it->firstChild();
|
|
|
|
while (chIt) {
|
|
aItem = dynamic_cast<AudioListItem*>(chIt);
|
|
|
|
if (aItem) {
|
|
if (aItem->getId() == id && aItem->getSegment() == segment) {
|
|
selectFileListItemNoSignal(chIt);
|
|
|
|
// Only propagate to segmentcanvas if asked to
|
|
if (propagate) {
|
|
SegmentSelection selection;
|
|
selection.insert(aItem->getSegment());
|
|
emit segmentsSelected(selection);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
}
|
|
chIt = chIt->nextSibling();
|
|
}
|
|
}
|
|
|
|
it = it->nextSibling();
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::selectFileListItemNoSignal(TQListViewItem* it)
|
|
{
|
|
m_fileList->blockSignals(true);
|
|
|
|
if (it) {
|
|
m_fileList->ensureItemVisible(it);
|
|
m_fileList->setSelected(it, true);
|
|
} else {
|
|
m_fileList->clearSelection();
|
|
}
|
|
|
|
m_fileList->blockSignals(false);
|
|
}
|
|
|
|
MultiViewCommandHistory*
|
|
AudioManagerDialog::getCommandHistory()
|
|
{
|
|
return m_doc->getCommandHistory();
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotCommandExecuted(KCommand*)
|
|
{
|
|
slotPopulateFileList();
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotSegmentSelection(
|
|
const SegmentSelection &segments)
|
|
{
|
|
const Segment *segment = 0;
|
|
|
|
for (SegmentSelection::const_iterator it = segments.begin();
|
|
it != segments.end(); ++it) {
|
|
if ((*it)->getType() == Segment::Audio) {
|
|
// Only get one audio segment
|
|
if (segment == 0)
|
|
segment = *it;
|
|
else
|
|
segment = 0;
|
|
}
|
|
|
|
}
|
|
|
|
if (segment) {
|
|
// We don't propagate this segment setting to the canvas
|
|
// as we probably got called from there.
|
|
//
|
|
setSelected(segment->getAudioFileId(), segment, false);
|
|
} else {
|
|
selectFileListItemNoSignal(0);
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotCancelPlayingAudioFile()
|
|
{
|
|
emit cancelPlayingAudioFile(m_playingAudioFile);
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::closePlayingDialog(AudioFileId id)
|
|
{
|
|
//std::cout << "AudioManagerDialog::closePlayingDialog" << std::endl;
|
|
if (m_audioPlayingDialog && id == m_playingAudioFile) {
|
|
m_playTimer->stop();
|
|
delete m_audioPlayingDialog;
|
|
m_audioPlayingDialog = 0;
|
|
}
|
|
|
|
}
|
|
|
|
bool
|
|
AudioManagerDialog::addFile(const KURL& kurl)
|
|
{
|
|
AudioFileId id = 0;
|
|
|
|
AudioFileManager &aFM = m_doc->getAudioFileManager();
|
|
|
|
if (!kurl.isLocalFile()) {
|
|
if (!RosegardenGUIApp::self()->testAudioPath("importing a remote audio file")) return false;
|
|
} else if (aFM.fileNeedsConversion(qstrtostr(kurl.path()), m_sampleRate)) {
|
|
if (!RosegardenGUIApp::self()->testAudioPath("importing an audio file that needs to be converted or resampled")) return false;
|
|
}
|
|
|
|
ProgressDialog progressDlg(i18n("Adding audio file..."),
|
|
100,
|
|
this);
|
|
|
|
CurrentProgressDialog::set(&progressDlg);
|
|
progressDlg.progressBar()->hide();
|
|
progressDlg.show();
|
|
|
|
// Connect the progress dialog
|
|
//
|
|
connect(&aFM, TQT_SIGNAL(setProgress(int)),
|
|
progressDlg.progressBar(), TQT_SLOT(setValue(int)));
|
|
connect(&aFM, TQT_SIGNAL(setOperationName(TQString)),
|
|
&progressDlg, TQT_SLOT(slotSetOperationName(TQString)));
|
|
connect(&progressDlg, TQT_SIGNAL(cancelClicked()),
|
|
&aFM, TQT_SLOT(slotStopImport()));
|
|
|
|
try {
|
|
id = aFM.importURL(kurl, m_sampleRate);
|
|
} catch (AudioFileManager::BadAudioPathException e) {
|
|
CurrentProgressDialog::freeze();
|
|
TQString errorString = i18n("Failed to add audio file. ") + strtoqstr(e.getMessage());
|
|
KMessageBox::sorry(this, errorString);
|
|
return false;
|
|
} catch (SoundFile::BadSoundFileException e) {
|
|
CurrentProgressDialog::freeze();
|
|
TQString errorString = i18n("Failed to add audio file. ") + strtoqstr(e.getMessage());
|
|
KMessageBox::sorry(this, errorString);
|
|
return false;
|
|
}
|
|
|
|
disconnect(&progressDlg, TQT_SIGNAL(cancelClicked()),
|
|
&aFM, TQT_SLOT(slotStopImport()));
|
|
connect(&progressDlg, TQT_SIGNAL(cancelClicked()),
|
|
&aFM, TQT_SLOT(slotStopPreview()));
|
|
progressDlg.progressBar()->show();
|
|
progressDlg.slotSetOperationName(i18n("Generating audio preview..."));
|
|
|
|
try {
|
|
aFM.generatePreview(id);
|
|
} catch (Exception e) {
|
|
CurrentProgressDialog::freeze();
|
|
|
|
TQString message = strtoqstr(e.getMessage()) + "\n\n" +
|
|
i18n("Try copying this file to a directory where you have write permission and re-add it");
|
|
KMessageBox::information(this, message);
|
|
}
|
|
|
|
disconnect(&progressDlg, TQT_SIGNAL(cancelClicked()),
|
|
&aFM, TQT_SLOT(slotStopPreview()));
|
|
|
|
slotPopulateFileList();
|
|
|
|
// tell the sequencer
|
|
emit addAudioFile(id);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotDropped(TQDropEvent *event, TQListViewItem*)
|
|
{
|
|
TQStrList uri;
|
|
|
|
// see if we can decode a URI.. if not, just ignore it
|
|
if (TQUriDrag::decode(event, uri)) {
|
|
// okay, we have a URI.. process it
|
|
for (TQString url = uri.first(); !url.isNull(); url = uri.next()) {
|
|
|
|
RG_DEBUG << "AudioManagerDialog::dropEvent() : got "
|
|
<< url << endl;
|
|
|
|
addFile(KURL(url));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::closeEvent(TQCloseEvent *e)
|
|
{
|
|
RG_DEBUG << "AudioManagerDialog::closeEvent()\n";
|
|
emit closing();
|
|
TDEMainWindow::closeEvent(e);
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotClose()
|
|
{
|
|
emit closing();
|
|
close();
|
|
//KDockMainWindow::slotClose();
|
|
// delete this;
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::setAudioSubsystemStatus(bool ok)
|
|
{
|
|
// We can do something more fancy in the future but for the moment
|
|
// this will suffice.
|
|
//
|
|
m_audiblePreview = ok;
|
|
}
|
|
|
|
bool
|
|
AudioManagerDialog::addAudioFile(const TQString &filePath)
|
|
{
|
|
return addFile(TQFileInfo(filePath).absFilePath());
|
|
}
|
|
|
|
bool
|
|
AudioManagerDialog::isSelectedTrackAudio()
|
|
{
|
|
Composition &comp = m_doc->getComposition();
|
|
Studio &studio = m_doc->getStudio();
|
|
|
|
TrackId currentTrackId = comp.getSelectedTrack();
|
|
Track *track = comp.getTrackById(currentTrackId);
|
|
|
|
if (track) {
|
|
InstrumentId ii = track->getInstrument();
|
|
Instrument *instrument = studio.getInstrumentById(ii);
|
|
|
|
if (instrument &&
|
|
instrument->getType() == Instrument::Audio)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void
|
|
AudioManagerDialog::slotDistributeOnMidiSegment()
|
|
{
|
|
RG_DEBUG << "AudioManagerDialog::slotDistributeOnMidiSegment" << endl;
|
|
|
|
//Composition &comp = m_doc->getComposition();
|
|
|
|
TQPtrList<RosegardenGUIView>& viewList = m_doc->getViewList();
|
|
RosegardenGUIView *w = 0;
|
|
SegmentSelection selection;
|
|
|
|
for (w = viewList.first(); w != 0; w = viewList.next()) {
|
|
selection = w->getSelection();
|
|
}
|
|
|
|
// Store the insert times in a local vector
|
|
//
|
|
std::vector<timeT> insertTimes;
|
|
|
|
for (SegmentSelection::iterator i = selection.begin();
|
|
i != selection.end(); ++i) {
|
|
// For MIDI (Internal) Segments only of course
|
|
//
|
|
if ((*i)->getType() == Segment::Internal) {
|
|
for (Segment::iterator it = (*i)->begin(); it != (*i)->end(); ++it) {
|
|
if ((*it)->isa(Note::EventType))
|
|
insertTimes.push_back((*it)->getAbsoluteTime());
|
|
}
|
|
}
|
|
}
|
|
|
|
for (unsigned int i = 0; i < insertTimes.size(); ++i) {
|
|
RG_DEBUG << "AudioManagerDialog::slotDistributeOnMidiSegment - "
|
|
<< "insert audio segment at " << insertTimes[i]
|
|
<< endl;
|
|
}
|
|
}
|
|
|
|
}
|
|
#include "AudioManagerDialog.moc"
|