summaryrefslogtreecommitdiffstats
path: root/src/gui/studio
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/gui/studio
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gui/studio')
-rw-r--r--src/gui/studio/AudioMixerWindow.cpp1734
-rw-r--r--src/gui/studio/AudioMixerWindow.h191
-rw-r--r--src/gui/studio/AudioPlugin.cpp78
-rw-r--r--src/gui/studio/AudioPlugin.h117
-rw-r--r--src/gui/studio/AudioPluginClipboard.cpp32
-rw-r--r--src/gui/studio/AudioPluginClipboard.h52
-rw-r--r--src/gui/studio/AudioPluginManager.cpp307
-rw-r--r--src/gui/studio/AudioPluginManager.h118
-rw-r--r--src/gui/studio/AudioPluginOSCGUI.cpp234
-rw-r--r--src/gui/studio/AudioPluginOSCGUI.h77
-rw-r--r--src/gui/studio/AudioPluginOSCGUIManager.cpp711
-rw-r--r--src/gui/studio/AudioPluginOSCGUIManager.h104
-rw-r--r--src/gui/studio/BankEditorDialog.cpp1713
-rw-r--r--src/gui/studio/BankEditorDialog.h211
-rw-r--r--src/gui/studio/ChangeRecordDeviceCommand.cpp66
-rw-r--r--src/gui/studio/ChangeRecordDeviceCommand.h54
-rw-r--r--src/gui/studio/DeviceEditorDialog.cpp406
-rw-r--r--src/gui/studio/DeviceEditorDialog.h87
-rw-r--r--src/gui/studio/DeviceManagerDialog.cpp833
-rw-r--r--src/gui/studio/DeviceManagerDialog.h121
-rw-r--r--src/gui/studio/MidiBankListViewItem.cpp98
-rw-r--r--src/gui/studio/MidiBankListViewItem.h70
-rw-r--r--src/gui/studio/MidiDeviceListViewItem.cpp88
-rw-r--r--src/gui/studio/MidiDeviceListViewItem.h69
-rw-r--r--src/gui/studio/MidiKeyMapListViewItem.cpp56
-rw-r--r--src/gui/studio/MidiKeyMapListViewItem.h59
-rw-r--r--src/gui/studio/MidiKeyMappingEditor.cpp197
-rw-r--r--src/gui/studio/MidiKeyMappingEditor.h78
-rw-r--r--src/gui/studio/MidiMixerVUMeter.cpp53
-rw-r--r--src/gui/studio/MidiMixerVUMeter.h61
-rw-r--r--src/gui/studio/MidiMixerWindow.cpp742
-rw-r--r--src/gui/studio/MidiMixerWindow.h125
-rw-r--r--src/gui/studio/MidiProgramsEditor.cpp631
-rw-r--r--src/gui/studio/MidiProgramsEditor.h119
-rw-r--r--src/gui/studio/MixerWindow.cpp75
-rw-r--r--src/gui/studio/MixerWindow.h77
-rw-r--r--src/gui/studio/NameSetEditor.cpp190
-rw-r--r--src/gui/studio/NameSetEditor.h90
-rw-r--r--src/gui/studio/OSCMessage.cpp87
-rw-r--r--src/gui/studio/OSCMessage.h75
-rw-r--r--src/gui/studio/RemapInstrumentDialog.cpp184
-rw-r--r--src/gui/studio/RemapInstrumentDialog.h84
-rw-r--r--src/gui/studio/StudioControl.cpp582
-rw-r--r--src/gui/studio/StudioControl.h152
-rw-r--r--src/gui/studio/SynthPluginManagerDialog.cpp360
-rw-r--r--src/gui/studio/SynthPluginManagerDialog.h98
-rw-r--r--src/gui/studio/TimerCallbackAssistant.cpp57
-rw-r--r--src/gui/studio/TimerCallbackAssistant.h61
48 files changed, 11864 insertions, 0 deletions
diff --git a/src/gui/studio/AudioMixerWindow.cpp b/src/gui/studio/AudioMixerWindow.cpp
new file mode 100644
index 0000000..e8d09b3
--- /dev/null
+++ b/src/gui/studio/AudioMixerWindow.cpp
@@ -0,0 +1,1734 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "AudioMixerWindow.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include "AudioPlugin.h"
+#include "AudioPluginManager.h"
+#include "MixerWindow.h"
+#include "StudioControl.h"
+#include "sound/Midi.h"
+#include "misc/Debug.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "base/AudioLevel.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/seqmanager/SequencerMapper.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/widgets/AudioRouteMenu.h"
+#include "gui/widgets/AudioVUMeter.h"
+#include "gui/widgets/Fader.h"
+#include "gui/widgets/Rotary.h"
+#include "gui/widgets/VUMeter.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedStudio.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kaction.h>
+#include <kglobal.h>
+#include <kmainwindow.h>
+#include <kstdaction.h>
+#include <qaccel.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qhbox.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpalette.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+// We define these such that the default of no-bits-set for the
+// studio's mixer display options produces the most sensible result
+static const unsigned int MIXER_OMIT_FADERS = 1 << 0;
+static const unsigned int MIXER_OMIT_SUBMASTERS = 1 << 1;
+static const unsigned int MIXER_OMIT_PLUGINS = 1 << 2;
+static const unsigned int MIXER_SHOW_UNASSIGNED_FADERS = 1 << 3;
+static const unsigned int MIXER_OMIT_SYNTH_FADERS = 1 << 4;
+
+
+AudioMixerWindow::AudioMixerWindow(QWidget *parent,
+ RosegardenGUIDoc *document):
+ MixerWindow(parent, document),
+ m_mainBox (0)
+{
+ populate();
+
+ KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-play")));
+ KAction *play = new KAction(i18n("&Play"), icon, Key_Enter, this,
+ SIGNAL(play()), actionCollection(), "play");
+ // Alternative shortcut for Play
+ KShortcut playShortcut = play->shortcut();
+ playShortcut.append( KKey(Key_Return + CTRL) );
+ play->setShortcut(playShortcut);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-stop")));
+ new KAction(i18n("&Stop"), icon, Key_Insert, this,
+ SIGNAL(stop()), actionCollection(), "stop");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind")));
+ new KAction(i18n("Re&wind"), icon, Key_End, this,
+ SIGNAL(rewindPlayback()), actionCollection(),
+ "playback_pointer_back_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd")));
+ new KAction(i18n("&Fast Forward"), icon, Key_PageDown, this,
+ SIGNAL(fastForwardPlayback()), actionCollection(),
+ "playback_pointer_forward_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind-end")));
+ new KAction(i18n("Rewind to &Beginning"), icon, 0, this,
+ SIGNAL(rewindPlaybackToBeginning()), actionCollection(),
+ "playback_pointer_start");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd-end")));
+ new KAction(i18n("Fast Forward to &End"), icon, 0, this,
+ SIGNAL(fastForwardPlaybackToEnd()), actionCollection(),
+ "playback_pointer_end");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-record")));
+ new KAction(i18n("&Record"), icon, 0, this,
+ SIGNAL(record()), actionCollection(),
+ "record");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-panic")));
+ new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
+ SIGNAL(panic()), actionCollection(),
+ "panic");
+
+ unsigned int mixerOptions = m_studio->getMixerDisplayOptions();
+
+ (new KToggleAction(i18n("Show Audio &Faders"), 0, this,
+ SLOT(slotToggleFaders()), actionCollection(),
+ "show_audio_faders"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_FADERS));
+
+ (new KToggleAction(i18n("Show Synth &Faders"), 0, this,
+ SLOT(slotToggleSynthFaders()), actionCollection(),
+ "show_synth_faders"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_SYNTH_FADERS));
+
+ (new KToggleAction(i18n("Show &Submasters"), 0, this,
+ SLOT(slotToggleSubmasters()), actionCollection(),
+ "show_audio_submasters"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_SUBMASTERS));
+
+ (new KToggleAction(i18n("Show &Plugin Buttons"), 0, this,
+ SLOT(slotTogglePluginButtons()), actionCollection(),
+ "show_plugin_buttons"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_PLUGINS));
+
+ (new KToggleAction(i18n("Show &Unassigned Faders"), 0, this,
+ SLOT(slotToggleUnassignedFaders()), actionCollection(),
+ "show_unassigned_faders"))->setChecked
+ (mixerOptions & MIXER_SHOW_UNASSIGNED_FADERS);
+
+ KRadioAction *action = 0;
+
+ for (int i = 1; i <= 16; i *= 2) {
+ action =
+ new KRadioAction(i18n("1 Input", "%n Inputs", i),
+ 0, this,
+ SLOT(slotSetInputCountFromAction()), actionCollection(),
+ QString("inputs_%1").arg(i));
+ action->setExclusiveGroup("inputs");
+ if (i == int(m_studio->getRecordIns().size()))
+ action->setChecked(true);
+ }
+
+ action = new KRadioAction
+ (i18n("No Submasters"),
+ 0, this,
+ SLOT(slotSetSubmasterCountFromAction()), actionCollection(),
+ QString("submasters_0"));
+ action->setExclusiveGroup("submasters");
+ action->setChecked(true);
+
+ for (int i = 2; i <= 8; i *= 2) {
+ action = new KRadioAction
+ (i18n("1 Submaster", "%n Submasters", i),
+ 0, this,
+ SLOT(slotSetSubmasterCountFromAction()), actionCollection(),
+ QString("submasters_%1").arg(i));
+ action->setExclusiveGroup("submasters");
+ if (i == int(m_studio->getBusses().size()) - 1)
+ action->setChecked(true);
+ }
+
+ createGUI("mixer.rc");
+}
+
+AudioMixerWindow::~AudioMixerWindow()
+{
+ RG_DEBUG << "AudioMixerWindow::~AudioMixerWindow\n";
+ depopulate();
+}
+
+void
+AudioMixerWindow::depopulate()
+{
+ if (!m_mainBox)
+ return ;
+
+ // All the widgets will be deleted when the main box goes,
+ // except that we need to delete the AudioRouteMenus first
+ // because they aren't actually widgets but do contain them
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+ delete i->second.m_input;
+ delete i->second.m_output;
+ }
+
+ m_faders.clear();
+ m_submasters.clear();
+
+ delete m_mainBox;
+ m_mainBox = 0;
+}
+
+void
+AudioMixerWindow::populate()
+{
+ if (m_mainBox) {
+
+ depopulate();
+
+ } else {
+
+ m_surroundBox = new QHBox(this);
+ setCentralWidget(m_surroundBox);
+ }
+
+ QFont font;
+ font.setPointSize(font.pointSize() * 8 / 10);
+ font.setBold(false);
+ setFont(font);
+
+ QFont boldFont(font);
+ boldFont.setBold(true);
+
+ m_mainBox = new QFrame(m_surroundBox);
+
+ InstrumentList instruments = m_studio->getPresentationInstruments();
+ BussList busses = m_studio->getBusses();
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ m_monoPixmap.load(QString("%1/misc/mono.xpm").arg(pixmapDir));
+ m_stereoPixmap.load(QString("%1/misc/stereo.xpm").arg(pixmapDir));
+
+ // Total cols: is 2 for each fader, submaster or master, plus 1
+ // for each spacer.
+ QGridLayout *mainLayout = new QGridLayout
+ (m_mainBox, (instruments.size() + busses.size()) * 3, 7);
+
+ setCaption(i18n("Audio Mixer"));
+
+ int count = 1;
+ int col = 0;
+
+ unsigned int mixerOptions = m_studio->getMixerDisplayOptions();
+
+ bool showUnassigned = (mixerOptions & MIXER_SHOW_UNASSIGNED_FADERS);
+
+ for (InstrumentList::iterator i = instruments.begin();
+ i != instruments.end(); ++i) {
+
+ if ((*i)->getType() != Instrument::Audio &&
+ (*i)->getType() != Instrument::SoftSynth)
+ continue;
+
+ FaderRec rec;
+
+ if (!showUnassigned) {
+ // Do any tracks use this instrument?
+ if (!isInstrumentAssigned((*i)->getId())) {
+ continue;
+ }
+ }
+ rec.m_populated = true;
+
+ if ((*i)->getType() == Instrument::Audio) {
+ rec.m_input = new AudioRouteMenu(m_mainBox,
+ AudioRouteMenu::In,
+ AudioRouteMenu::Compact,
+ m_studio, *i);
+ QToolTip::add
+ (rec.m_input->getWidget(), i18n("Record input source"));
+ rec.m_input->getWidget()->setMaximumWidth(45);
+ } else {
+ rec.m_input = 0;
+ }
+
+ rec.m_output = new AudioRouteMenu(m_mainBox,
+ AudioRouteMenu::Out,
+ AudioRouteMenu::Compact,
+ m_studio, *i);
+ QToolTip::add
+ (rec.m_output->getWidget(), i18n("Output destination"));
+ rec.m_output->getWidget()->setMaximumWidth(45);
+
+ rec.m_pan = new Rotary
+ (m_mainBox, -100.0, 100.0, 1.0, 5.0, 0.0, 20,
+ Rotary::NoTicks, false, true);
+
+ if ((*i)->getType() == Instrument::Audio) {
+ rec.m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelGreen));
+ } else {
+ rec.m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelYellow));
+ }
+
+ QToolTip::add
+ (rec.m_pan, i18n("Pan"));
+
+ rec.m_fader = new Fader
+ (AudioLevel::LongFader, 20, 240, m_mainBox);
+ rec.m_meter = new AudioVUMeter
+ (m_mainBox, VUMeter::AudioPeakHoldIECLong, true, rec.m_input != 0,
+ 20, 240);
+
+ QToolTip::add
+ (rec.m_fader, i18n("Audio level"));
+ QToolTip::add
+ (rec.m_meter, i18n("Audio level"));
+
+ rec.m_stereoButton = new QPushButton(m_mainBox);
+ rec.m_stereoButton->setPixmap(m_monoPixmap);
+ rec.m_stereoButton->setFixedSize(20, 20);
+ rec.m_stereoButton->setFlat(true);
+ rec.m_stereoness = false;
+ QToolTip::add
+ (rec.m_stereoButton, i18n("Mono or stereo"));
+
+ rec.m_muteButton = new QPushButton(m_mainBox);
+ rec.m_muteButton->setText("M");
+ rec.m_muteButton->setToggleButton(true);
+ rec.m_muteButton->setFixedWidth(rec.m_stereoButton->width());
+ rec.m_muteButton->setFixedHeight(rec.m_stereoButton->height());
+ rec.m_muteButton->setFlat(true);
+ QToolTip::add
+ (rec.m_muteButton, i18n("Mute"));
+
+ rec.m_soloButton = new QPushButton(m_mainBox);
+ rec.m_soloButton->setText("S");
+ rec.m_soloButton->setToggleButton(true);
+ rec.m_soloButton->setFixedWidth(rec.m_stereoButton->width());
+ rec.m_soloButton->setFixedHeight(rec.m_stereoButton->height());
+ rec.m_soloButton->setFlat(true);
+ QToolTip::add
+ (rec.m_soloButton, i18n("Solo"));
+
+ rec.m_recordButton = new QPushButton(m_mainBox);
+ rec.m_recordButton->setText("R");
+ rec.m_recordButton->setToggleButton(true);
+ rec.m_recordButton->setFixedWidth(rec.m_stereoButton->width());
+ rec.m_recordButton->setFixedHeight(rec.m_stereoButton->height());
+ rec.m_recordButton->setFlat(true);
+ QToolTip::add
+ (rec.m_recordButton, i18n("Arm recording"));
+
+ rec.m_pluginBox = new QVBox(m_mainBox);
+
+ for (int p = 0; p < 5; ++p) {
+ QPushButton *plugin = new QPushButton(rec.m_pluginBox, "pluginButton");
+ plugin->setText(i18n("<none>"));
+ plugin->setMaximumWidth(45);
+ QToolTip::add
+ (plugin, i18n("Audio plugin button"));
+ rec.m_plugins.push_back(plugin);
+ connect(plugin, SIGNAL(clicked()),
+ this, SLOT(slotSelectPlugin()));
+ }
+
+ QLabel *idLabel;
+ QString idString;
+ if ((*i)->getType() == Instrument::Audio) {
+ idString = i18n("Audio %1").arg((*i)->getId() -
+ AudioInstrumentBase + 1);
+ idLabel = new QLabel(idString, m_mainBox, "audioIdLabel");
+ } else {
+ idString = i18n("Synth %1").arg((*i)->getId() -
+ SoftSynthInstrumentBase + 1);
+ idLabel = new QLabel(idString, m_mainBox, "synthIdLabel");
+ }
+ idLabel->setFont(boldFont);
+
+ if (rec.m_input) {
+ mainLayout->addMultiCellWidget(rec.m_input->getWidget(), 1, 1, col, col + 1);
+ }
+ mainLayout->addMultiCellWidget(rec.m_output->getWidget(), 2, 2, col, col + 1);
+ // mainLayout->addWidget(idLabel, 2, col, Qt::AlignCenter);
+ // mainLayout->addWidget(rec.m_pan, 2, col+1, Qt::AlignLeft);
+
+ mainLayout->addMultiCellWidget(idLabel, 0, 0, col, col + 1, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_pan, 5, col, Qt::AlignCenter);
+
+ mainLayout->addWidget(rec.m_fader, 3, col, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_meter, 3, col + 1, Qt::AlignCenter);
+
+ // commented out until implemented
+ // mainLayout->addWidget(rec.m_muteButton, 4, col);
+ // mainLayout->addWidget(rec.m_soloButton, 4, col+1);
+ rec.m_muteButton->hide();
+ rec.m_soloButton->hide();
+
+ // mainLayout->addWidget(rec.m_recordButton, 5, col);
+ // mainLayout->addWidget(rec.m_stereoButton, 5, col+1);
+
+ rec.m_recordButton->hide();
+ mainLayout->addWidget(rec.m_stereoButton, 5, col + 1);
+
+ if (rec.m_pluginBox) {
+ mainLayout->addMultiCellWidget(rec.m_pluginBox, 6, 6, col, col + 1);
+ }
+
+ m_faders[(*i)->getId()] = rec;
+ updateFader((*i)->getId());
+ updateRouteButtons((*i)->getId());
+ updateStereoButton((*i)->getId());
+ updatePluginButtons((*i)->getId());
+
+ if (rec.m_input) {
+ connect(rec.m_input, SIGNAL(changed()),
+ this, SLOT(slotInputChanged()));
+ }
+
+ connect(rec.m_output, SIGNAL(changed()),
+ this, SLOT(slotOutputChanged()));
+
+ connect(rec.m_fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ connect(rec.m_pan, SIGNAL(valueChanged(float)),
+ this, SLOT(slotPanChanged(float)));
+
+ connect(rec.m_soloButton, SIGNAL(clicked()),
+ this, SLOT(slotSoloChanged()));
+
+ connect(rec.m_muteButton, SIGNAL(clicked()),
+ this, SLOT(slotMuteChanged()));
+
+ connect(rec.m_stereoButton, SIGNAL(clicked()),
+ this, SLOT(slotChannelsChanged()));
+
+ connect(rec.m_recordButton, SIGNAL(clicked()),
+ this, SLOT(slotRecordChanged()));
+
+ ++count;
+
+ mainLayout->addMultiCell(new QSpacerItem(2, 0), 0, 6, col + 2, col + 2);
+
+ col += 3;
+ }
+
+ count = 1;
+
+ for (BussList::iterator i = busses.begin();
+ i != busses.end(); ++i) {
+
+ if (i == busses.begin())
+ continue; // that one's the master
+
+ FaderRec rec;
+ rec.m_populated = true;
+
+ rec.m_pan = new Rotary
+ (m_mainBox, -100.0, 100.0, 1.0, 5.0, 0.0, 20,
+ Rotary::NoTicks, false, true);
+ rec.m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelBlue));
+
+ QToolTip::add
+ (rec.m_pan, i18n("Pan"));
+
+ rec.m_fader = new Fader
+ (AudioLevel::LongFader, 20, 240, m_mainBox);
+ rec.m_meter = new AudioVUMeter
+ (m_mainBox, VUMeter::AudioPeakHoldIECLong, true, false, 20, 240);
+
+ QToolTip::add
+ (rec.m_fader, i18n("Audio level"));
+ QToolTip::add
+ (rec.m_meter, i18n("Audio level"));
+
+ rec.m_muteButton = new QPushButton(m_mainBox);
+ rec.m_muteButton->setText("M");
+ rec.m_muteButton->setToggleButton(true);
+ rec.m_muteButton->setFlat(true);
+
+ QToolTip::add
+ (rec.m_muteButton, i18n("Mute"));
+
+ rec.m_pluginBox = new QVBox(m_mainBox);
+
+ for (int p = 0; p < 5; ++p) {
+ QPushButton *plugin = new QPushButton(rec.m_pluginBox, "pluginButton");
+ plugin->setText(i18n("<none>"));
+ plugin->setMaximumWidth(45);
+ QToolTip::add
+ (plugin, i18n("Audio plugin button"));
+ rec.m_plugins.push_back(plugin);
+ connect(plugin, SIGNAL(clicked()),
+ this, SLOT(slotSelectPlugin()));
+ }
+
+ QLabel *idLabel = new QLabel(i18n("Sub %1").arg(count), m_mainBox, "subMaster");
+ idLabel->setFont(boldFont);
+
+ // mainLayout->addWidget(idLabel, 2, col, Qt::AlignCenter);
+ mainLayout->addMultiCellWidget(idLabel, 0, 0, col, col + 1, Qt::AlignCenter);
+
+ // mainLayout->addWidget(rec.m_pan, 2, col+1, Qt::AlignLeft);
+ mainLayout->addMultiCellWidget(rec.m_pan, 5, 5, col, col + 1, Qt::AlignCenter);
+
+ mainLayout->addWidget(rec.m_fader, 3, col, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_meter, 3, col + 1, Qt::AlignCenter);
+
+ // mainLayout->addMultiCellWidget(rec.m_muteButton, 4, 4, col, col+1);
+ rec.m_muteButton->hide();
+
+ if (rec.m_pluginBox) {
+ mainLayout->addMultiCellWidget(rec.m_pluginBox, 6, 6, col, col + 1);
+ }
+
+ m_submasters.push_back(rec);
+ updateFader(count);
+ updatePluginButtons(count);
+
+ connect(rec.m_fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ connect(rec.m_pan, SIGNAL(valueChanged(float)),
+ this, SLOT(slotPanChanged(float)));
+
+ connect(rec.m_muteButton, SIGNAL(clicked()),
+ this, SLOT(slotMuteChanged()));
+
+ ++count;
+
+ mainLayout->addMultiCell(new QSpacerItem(2, 0), 0, 6, col + 2, col + 2);
+
+ col += 3;
+ }
+
+ if (busses.size() > 0) {
+
+ FaderRec rec;
+ rec.m_populated = true;
+
+ rec.m_fader = new Fader
+ (AudioLevel::LongFader, 20, 240, m_mainBox);
+ rec.m_meter = new AudioVUMeter
+ (m_mainBox, VUMeter::AudioPeakHoldIEC, true, false, 20, 240);
+
+ QToolTip::add
+ (rec.m_fader, i18n("Audio master output level"));
+ QToolTip::add
+ (rec.m_meter, i18n("Audio master output level"));
+
+ rec.m_muteButton = new QPushButton(m_mainBox);
+ rec.m_muteButton->setText("M");
+ rec.m_muteButton->setToggleButton(true);
+ rec.m_muteButton->setFlat(true);
+
+ QLabel *idLabel = new QLabel(i18n("Master"), m_mainBox);
+ idLabel->setFont(boldFont);
+
+ mainLayout->addMultiCellWidget(idLabel, 0, 0, col, col + 1, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_fader, 3, col, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_meter, 3, col + 1, Qt::AlignCenter);
+
+ // mainLayout->addMultiCellWidget(rec.m_muteButton, 4, 4, col, col+1);
+ rec.m_muteButton->hide();
+
+ mainLayout->addMultiCell(new QSpacerItem(2, 0), 0, 6, col + 2, col + 2);
+
+ m_master = rec;
+ updateFader(0);
+
+ connect(rec.m_fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ connect(rec.m_muteButton, SIGNAL(clicked()),
+ this, SLOT(slotMuteChanged()));
+ }
+
+ m_mainBox->show();
+
+ slotUpdateFaderVisibility();
+ slotUpdateSynthFaderVisibility();
+ slotUpdateSubmasterVisibility();
+ slotUpdatePluginButtonVisibility();
+
+ adjustSize();
+}
+
+bool
+AudioMixerWindow::isInstrumentAssigned(InstrumentId id)
+{
+ Composition::trackcontainer &tracks =
+ m_document->getComposition().getTracks();
+
+ for (Composition::trackcontainer::iterator ti =
+ tracks.begin(); ti != tracks.end(); ++ti) {
+ if (ti->second->getInstrument() == id) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+AudioMixerWindow::slotTrackAssignmentsChanged()
+{
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ InstrumentId id = i->first;
+ bool assigned = isInstrumentAssigned(id);
+
+ if (assigned != i->second.m_populated) {
+ // found an inconsistency
+ populate();
+ return ;
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotUpdateInstrument(InstrumentId id)
+{
+ RG_DEBUG << "AudioMixerWindow::slotUpdateInstrument(" << id << ")" << endl;
+
+ blockSignals(true);
+
+ updateFader(id);
+ updateStereoButton(id);
+ updateRouteButtons(id);
+ updatePluginButtons(id);
+ updateMiscButtons(id);
+
+ blockSignals(false);
+}
+
+void
+AudioMixerWindow::slotPluginSelected(InstrumentId id,
+ int index, int plugin)
+{
+ if (id >= (int)AudioInstrumentBase) {
+
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated || !rec.m_pluginBox)
+ return ;
+
+ // nowhere to display synth plugin info yet
+ if (index >= rec.m_plugins.size())
+ return ;
+
+ if (plugin == -1) {
+
+ rec.m_plugins[index]->setText(i18n("<none>"));
+ QToolTip::add
+ (rec.m_plugins[index], i18n("<no plugin>"));
+
+ rec.m_plugins[index]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+
+ } else {
+
+ AudioPlugin *pluginClass
+ = m_document->getPluginManager()->getPlugin(plugin);
+
+ QColor pluginBgColour =
+ kapp->palette().color(QPalette::Active, QColorGroup::Light);
+
+ if (pluginClass) {
+ rec.m_plugins[index]->
+ setText(pluginClass->getLabel());
+ QToolTip::add
+ (rec.m_plugins[index], pluginClass->getLabel());
+
+ pluginBgColour = pluginClass->getColour();
+ }
+
+
+ rec.m_plugins[index]->setPaletteForegroundColor(Qt::white);
+ rec.m_plugins[index]->setPaletteBackgroundColor(pluginBgColour);
+ }
+ } else if (id > 0 && id <= m_submasters.size()) {
+
+ FaderRec &rec = m_submasters[id - 1];
+ if (!rec.m_populated || !rec.m_pluginBox)
+ return ;
+ if (index >= rec.m_plugins.size())
+ return ;
+
+ if (plugin == -1) {
+
+ rec.m_plugins[index]->setText(i18n("<none>"));
+ QToolTip::add
+ (rec.m_plugins[index], i18n("<no plugin>"));
+
+ rec.m_plugins[index]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+
+ } else {
+
+ AudioPlugin *pluginClass
+ = m_document->getPluginManager()->getPlugin(plugin);
+
+ QColor pluginBgColour =
+ kapp->palette().color(QPalette::Active, QColorGroup::Light);
+
+ if (pluginClass) {
+ rec.m_plugins[index]->
+ setText(pluginClass->getLabel());
+ QToolTip::add
+ (rec.m_plugins[index], pluginClass->getLabel());
+
+ pluginBgColour = pluginClass->getColour();
+ }
+
+
+ rec.m_plugins[index]->setPaletteForegroundColor(Qt::white);
+ rec.m_plugins[index]->setPaletteBackgroundColor(pluginBgColour);
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotPluginBypassed(InstrumentId instrumentId,
+ int , bool )
+{
+ RG_DEBUG << "AudioMixerWindow::slotPluginBypassed(" << instrumentId << ")" << endl;
+
+ updatePluginButtons(instrumentId);
+}
+
+void
+AudioMixerWindow::updateFader(int id)
+{
+ if (id == -1) {
+
+ // This used to be the special code for updating the monitor fader.
+ return ;
+
+ } else if (id >= (int)AudioInstrumentBase) {
+
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated)
+ return ;
+ Instrument *instrument = m_studio->getInstrumentById(id);
+
+ rec.m_fader->blockSignals(true);
+ rec.m_fader->setFader(instrument->getLevel());
+ rec.m_fader->blockSignals(false);
+
+ rec.m_pan->blockSignals(true);
+ rec.m_pan->setPosition(instrument->getPan() - 100);
+ rec.m_pan->blockSignals(false);
+
+ } else {
+
+ FaderRec &rec = (id == 0 ? m_master : m_submasters[id - 1]);
+ BussList busses = m_studio->getBusses();
+ Buss *buss = busses[id];
+
+ rec.m_fader->blockSignals(true);
+ rec.m_fader->setFader(buss->getLevel());
+ rec.m_fader->blockSignals(false);
+
+ if (rec.m_pan) {
+ rec.m_pan->blockSignals(true);
+ rec.m_pan->setPosition(buss->getPan() - 100);
+ rec.m_pan->blockSignals(false);
+ }
+ }
+}
+
+void
+AudioMixerWindow::updateRouteButtons(int id)
+{
+ if (id >= (int)AudioInstrumentBase) {
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated)
+ return ;
+ if (rec.m_input)
+ rec.m_input->slotRepopulate();
+ rec.m_output->slotRepopulate();
+ }
+}
+
+void
+AudioMixerWindow::updateStereoButton(int id)
+{
+ if (id >= (int)AudioInstrumentBase) {
+
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated)
+ return ;
+ Instrument *i = m_studio->getInstrumentById(id);
+
+ bool stereo = (i->getAudioChannels() > 1);
+ if (stereo == rec.m_stereoness)
+ return ;
+
+ rec.m_stereoness = stereo;
+
+ if (stereo)
+ rec.m_stereoButton->setPixmap(m_stereoPixmap);
+ else
+ rec.m_stereoButton->setPixmap(m_monoPixmap);
+ }
+}
+
+void
+AudioMixerWindow::updateMiscButtons(int )
+{
+ //... complications here, because the mute/solo status is actually
+ // per-track rather than per-instrument... doh.
+}
+
+void
+AudioMixerWindow::updatePluginButtons(int id)
+{
+ FaderRec *rec = 0;
+ PluginContainer *container = 0;
+
+ if (id >= (int)AudioInstrumentBase) {
+
+ container = m_studio->getInstrumentById(id);
+ rec = &m_faders[id];
+ if (!rec->m_populated || !rec->m_pluginBox)
+ return ;
+
+ } else {
+
+ BussList busses = m_studio->getBusses();
+ if (busses.size() > id) {
+ container = busses[id];
+ }
+ rec = &m_submasters[id - 1];
+ if (!rec->m_populated || !rec->m_pluginBox)
+ return ;
+ }
+
+ if (rec && container) {
+
+ for (unsigned int i = 0; i < rec->m_plugins.size(); i++) {
+
+ bool used = false;
+ bool bypass = false;
+ QColor pluginBgColour =
+ kapp->palette().color(QPalette::Active, QColorGroup::Light);
+
+ rec->m_plugins[i]->show();
+
+ AudioPluginInstance *inst = container->getPlugin(i);
+
+ if (inst && inst->isAssigned()) {
+
+ AudioPlugin *pluginClass
+ = m_document->getPluginManager()->getPlugin(
+ m_document->getPluginManager()->
+ getPositionByIdentifier(inst->getIdentifier().c_str()));
+
+ if (pluginClass) {
+ rec->m_plugins[i]->setText(pluginClass->getLabel());
+ QToolTip::add
+ (rec->m_plugins[i], pluginClass->getLabel());
+
+ pluginBgColour = pluginClass->getColour();
+ }
+
+ used = true;
+ bypass = inst->isBypassed();
+
+ } else {
+
+ rec->m_plugins[i]->setText(i18n("<none>"));
+ QToolTip::add
+ (rec->m_plugins[i], i18n("<no plugin>"));
+
+ if (inst)
+ bypass = inst->isBypassed();
+ }
+
+ if (bypass) {
+
+ rec->m_plugins[i]->setPaletteForegroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+
+ rec->m_plugins[i]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::ButtonText));
+
+ } else if (used) {
+
+ rec->m_plugins[i]->setPaletteForegroundColor(Qt::white);
+ rec->m_plugins[i]->setPaletteBackgroundColor(pluginBgColour);
+
+
+ } else {
+
+ rec->m_plugins[i]->setPaletteForegroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::ButtonText));
+
+ rec->m_plugins[i]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+ }
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotSelectPlugin()
+{
+ const QObject *s = sender();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ int index = 0;
+ if (!i->second.m_populated || !i->second.m_pluginBox)
+ continue;
+
+ for (std::vector<QPushButton *>::iterator pli = i->second.m_plugins.begin();
+ pli != i->second.m_plugins.end(); ++pli) {
+
+ if (*pli == s) {
+
+ emit selectPlugin(this, i->first, index);
+ return ;
+ }
+
+ ++index;
+ }
+ }
+
+
+ int b = 1;
+
+ for (FaderVector::iterator i = m_submasters.begin();
+ i != m_submasters.end(); ++i) {
+
+ int index = 0;
+ if (!i->m_populated || !i->m_pluginBox)
+ continue;
+
+ for (std::vector<QPushButton *>::iterator pli = i->m_plugins.begin();
+ pli != i->m_plugins.end(); ++pli) {
+
+ if (*pli == s) {
+
+ emit selectPlugin(this, b, index);
+ return ;
+ }
+
+ ++index;
+ }
+
+ ++b;
+ }
+}
+
+void
+AudioMixerWindow::slotInputChanged()
+{
+ const QObject *s = sender();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_input == s)
+ emit instrumentParametersChanged(i->first);
+ }
+}
+
+void
+AudioMixerWindow::slotOutputChanged()
+{
+ const QObject *s = sender();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_output == s)
+ emit instrumentParametersChanged(i->first);
+ }
+}
+
+void
+AudioMixerWindow::sendControllerRefresh()
+{
+ //!!! really want some notification of whether we have an external controller!
+ int controllerChannel = 0;
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ if (controllerChannel >= 16)
+ break;
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ int value = AudioLevel::dB_to_fader
+ (instrument->getLevel(), 127, AudioLevel::LongFader);
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ MidiByte(value));
+ mE.setRecordedChannel(controllerChannel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+
+ int ipan = (int(instrument->getPan()) * 64) / 100;
+ if (ipan < 0)
+ ipan = 0;
+ if (ipan > 127)
+ ipan = 127;
+ MappedEvent mEp(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_PAN,
+ MidiByte(ipan));
+ mEp.setRecordedChannel(controllerChannel);
+ mEp.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mEp);
+
+ ++controllerChannel;
+ }
+}
+
+void
+AudioMixerWindow::slotFaderLevelChanged(float dB)
+{
+ const QObject *s = sender();
+
+ BussList busses = m_studio->getBusses();
+
+ if (m_master.m_fader == s) {
+
+ if (busses.size() > 0) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(busses[0]->getMappedId()),
+ MappedAudioBuss::Level,
+ MappedObjectValue(dB));
+ busses[0]->setLevel(dB);
+ }
+
+ return ;
+ }
+
+ int index = 1;
+
+ for (FaderVector::iterator i = m_submasters.begin();
+ i != m_submasters.end(); ++i) {
+
+ if (i->m_fader == s) {
+ if ((int)busses.size() > index) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(busses[index]->getMappedId()),
+ MappedAudioBuss::Level,
+ MappedObjectValue(dB));
+ busses[index]->setLevel(dB);
+ }
+
+ return ;
+ }
+
+ ++index;
+ }
+
+ int controllerChannel = 0;
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_fader == s) {
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ if (instrument) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId
+ (instrument->getMappedId()),
+ MappedAudioFader::FaderLevel,
+ MappedObjectValue(dB));
+ instrument->setLevel(dB);
+ }
+
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ if (controllerChannel < 16) {
+ int value = AudioLevel::dB_to_fader
+ (dB, 127, AudioLevel::LongFader);
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ MidiByte(value));
+ mE.setRecordedChannel(controllerChannel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ emit instrumentParametersChanged(i->first);
+ }
+
+ ++controllerChannel;
+ }
+}
+
+void
+AudioMixerWindow::slotPanChanged(float pan)
+{
+ const QObject *s = sender();
+
+ BussList busses = m_studio->getBusses();
+
+ int index = 1;
+
+ for (FaderVector::iterator i = m_submasters.begin();
+ i != m_submasters.end(); ++i) {
+
+ if (i->m_pan == s) {
+ if ((int)busses.size() > index) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(busses[index]->getMappedId()),
+ MappedAudioBuss::Pan,
+ MappedObjectValue(pan));
+ busses[index]->setPan(MidiByte(pan + 100.0));
+ }
+ return ;
+ }
+
+ ++index;
+ }
+
+ int controllerChannel = 0;
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_pan == s) {
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ if (instrument) {
+ StudioControl::setStudioObjectProperty
+ (instrument->getMappedId(),
+ MappedAudioFader::Pan,
+ MappedObjectValue(pan));
+ instrument->setPan(MidiByte(pan + 100.0));
+ }
+
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ if (controllerChannel < 16) {
+ int ipan = (int(instrument->getPan()) * 64) / 100;
+ if (ipan < 0)
+ ipan = 0;
+ if (ipan > 127)
+ ipan = 127;
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_PAN,
+ MidiByte(ipan));
+ mE.setRecordedChannel(controllerChannel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ emit instrumentParametersChanged(i->first);
+ }
+
+ ++controllerChannel;
+ }
+}
+
+void
+AudioMixerWindow::slotChannelsChanged()
+{
+ const QObject *s = sender();
+
+ // channels are only switchable on instruments
+
+ //!!! need to reconnect input, or change input channel number anyway
+
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (s == i->second.m_stereoButton) {
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ if (instrument) {
+ instrument->setAudioChannels
+ ((instrument->getAudioChannels() > 1) ? 1 : 2);
+ updateStereoButton(instrument->getId());
+ updateRouteButtons(instrument->getId());
+
+ emit instrumentParametersChanged(i->first);
+
+ return ;
+ }
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotSoloChanged()
+{
+ //...
+}
+
+void
+AudioMixerWindow::slotMuteChanged()
+{
+ //...
+}
+
+void
+AudioMixerWindow::slotRecordChanged()
+{
+ //...
+}
+
+void
+AudioMixerWindow::updateMeters(SequencerMapper *mapper)
+{
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ InstrumentId id = i->first;
+ FaderRec &rec = i->second;
+ if (!rec.m_populated)
+ continue;
+
+ LevelInfo info;
+
+ if (mapper->getInstrumentLevelForMixer(id, info)) {
+
+ // The values passed through are long-fader values
+ float dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+
+ if (rec.m_stereoness) {
+ float dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+
+ rec.m_meter->setLevel(dBleft, dBright);
+
+ } else {
+ rec.m_meter->setLevel(dBleft);
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < m_submasters.size(); ++i) {
+
+ FaderRec &rec = m_submasters[i];
+
+ LevelInfo info;
+ if (!mapper->getSubmasterLevel(i, info))
+ continue;
+
+ // The values passed through are long-fader values
+ float dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+ float dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+
+ rec.m_meter->setLevel(dBleft, dBright);
+ }
+
+ updateMonitorMeters(mapper);
+
+ LevelInfo masterInfo;
+ if (mapper->getMasterLevel(masterInfo)) {
+
+ float dBleft = AudioLevel::fader_to_dB
+ (masterInfo.level, 127, AudioLevel::LongFader);
+ float dBright = AudioLevel::fader_to_dB
+ (masterInfo.levelRight, 127, AudioLevel::LongFader);
+
+ m_master.m_meter->setLevel(dBleft, dBright);
+ }
+}
+
+void
+AudioMixerWindow::updateMonitorMeters(SequencerMapper *mapper)
+{
+ // only show monitor levels when quiescent or when recording (as
+ // record levels)
+ if (m_document->getSequenceManager() &&
+ m_document->getSequenceManager()->getTransportStatus() == PLAYING) {
+ return ;
+ }
+
+ Composition &comp = m_document->getComposition();
+ Composition::trackcontainer &tracks = comp.getTracks();
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ InstrumentId id = i->first;
+ FaderRec &rec = i->second;
+ if (!rec.m_populated)
+ continue;
+
+ LevelInfo info;
+
+ if (mapper->getInstrumentRecordLevelForMixer(id, info)) {
+
+ bool armed = false;
+
+ for (Composition::trackcontainer::iterator ti =
+ tracks.begin(); ti != tracks.end(); ++ti) {
+ if (ti->second->getInstrument() == id) {
+ if (comp.isTrackRecording(ti->second->getId())) {
+ armed = true;
+ break;
+ }
+ }
+ }
+
+ if (!armed)
+ continue;
+
+ // The values passed through are long-fader values
+ float dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+
+ if (rec.m_stereoness) {
+ float dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+
+ rec.m_meter->setRecordLevel(dBleft, dBright);
+
+ } else {
+ rec.m_meter->setRecordLevel(dBleft);
+ }
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotControllerDeviceEventReceived(MappedEvent *e,
+ const void *preferredCustomer)
+{
+ if (preferredCustomer != this)
+ return ;
+ RG_DEBUG << "AudioMixerWindow::slotControllerDeviceEventReceived: this one's for me" << endl;
+ raise();
+
+ // get channel number n from event
+ // update instrument for nth fader in m_faders
+
+ if (e->getType() != MappedEvent::MidiController)
+ return ;
+ unsigned int channel = e->getRecordedChannel();
+ MidiByte controller = e->getData1();
+ MidiByte value = e->getData2();
+
+ int count = 0;
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ if (count < channel) {
+ ++count;
+ continue;
+ }
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+ if (!instrument)
+ continue;
+
+ switch (controller) {
+
+ case MIDI_CONTROLLER_VOLUME: {
+ float level = AudioLevel::fader_to_dB
+ (value, 127, AudioLevel::LongFader);
+
+ StudioControl::setStudioObjectProperty
+ (instrument->getMappedId(),
+ MappedAudioFader::FaderLevel,
+ MappedObjectValue(level));
+
+ instrument->setLevel(level);
+ break;
+ }
+
+ case MIDI_CONTROLLER_PAN: {
+ MidiByte ipan = MidiByte((value / 64.0) * 100.0 + 0.01);
+
+ StudioControl::setStudioObjectProperty
+ (instrument->getMappedId(),
+ MappedAudioFader::Pan,
+ MappedObjectValue(float(ipan) - 100.0));
+
+ instrument->setPan(ipan);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ slotUpdateInstrument(i->first);
+ emit instrumentParametersChanged(i->first);
+
+ break;
+ }
+}
+
+void
+AudioMixerWindow::slotSetInputCountFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(7) == "inputs_") {
+
+ int count = name.right(name.length() - 7).toInt();
+
+ RecordInList ins = m_studio->getRecordIns();
+ int current = ins.size();
+
+ if (count == current)
+ return ;
+
+ m_studio->clearRecordIns(); // leaves the default 1
+
+ for (int i = 1; i < count; ++i) {
+ m_studio->addRecordIn(new RecordIn());
+ }
+ }
+
+ m_document->initialiseStudio();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+ updateRouteButtons(i->first);
+ }
+}
+
+void
+AudioMixerWindow::slotSetSubmasterCountFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(11) == "submasters_") {
+
+ int count = name.right(name.length() - 11).toInt();
+
+ BussList busses = m_studio->getBusses();
+ int current = busses.size();
+
+ // offset by 1 generally to take into account the fact that
+ // the first buss in the studio is the master, not a submaster
+
+ if (count + 1 == current)
+ return ;
+
+ BussList dups;
+ for (int i = 0; i < count; ++i) {
+ if (i + 1 < int(busses.size())) {
+ dups.push_back(new Buss(*busses[i + 1]));
+ } else {
+ dups.push_back(new Buss(i + 1));
+ }
+ }
+
+ m_studio->clearBusses();
+
+ for (BussList::iterator i = dups.begin();
+ i != dups.end(); ++i) {
+ m_studio->addBuss(*i);
+ }
+ }
+
+ m_document->initialiseStudio();
+
+ populate();
+}
+
+void AudioMixerWindow::FaderRec::setVisible(bool visible)
+{
+ if (visible) {
+ if (m_input)
+ m_input->getWidget()->show();
+ if (m_output)
+ m_output->getWidget()->show();
+ if (m_pan)
+ m_pan->show();
+ if (m_fader)
+ m_fader->show();
+ if (m_meter)
+ m_meter->show();
+ // commented out until implemented
+ // if (m_muteButton) m_muteButton->show();
+ // if (m_soloButton) m_soloButton->show();
+ // if (m_recordButton) m_recordButton->show();
+ if (m_stereoButton)
+ m_stereoButton->show();
+
+ } else {
+
+ if (m_input)
+ m_input->getWidget()->hide();
+ if (m_output)
+ m_output->getWidget()->hide();
+ if (m_pan)
+ m_pan->hide();
+ if (m_fader)
+ m_fader->hide();
+ if (m_meter)
+ m_meter->hide();
+ // commented out until implemented
+ // if (m_muteButton) m_muteButton->hide();
+ // if (m_soloButton) m_soloButton->hide();
+ // if (m_recordButton) m_recordButton->hide();
+ if (m_stereoButton)
+ m_stereoButton->hide();
+ }
+
+ setPluginButtonsVisible(visible);
+
+}
+
+void
+AudioMixerWindow::FaderRec::setPluginButtonsVisible(bool visible)
+{
+ if (!m_pluginBox)
+ return ;
+
+ if (visible) {
+ m_pluginBox->show();
+ } else {
+ m_pluginBox->hide();
+ }
+}
+
+void
+AudioMixerWindow::slotToggleFaders()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_FADERS);
+
+ slotUpdateFaderVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdateFaderVisibility()
+{
+ bool d = !(m_studio->getMixerDisplayOptions() & MIXER_OMIT_FADERS);
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_audio_faders"));
+ if (action) {
+ action->setChecked(d);
+ }
+
+ RG_DEBUG << "AudioMixerWindow::slotUpdateFaderVisibility: visiblility is " << d << " (options " << m_studio->getMixerDisplayOptions() << ")" << endl;
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+ if (i->first < SoftSynthInstrumentBase) {
+ FaderRec rec = i->second;
+ rec.setVisible(d);
+ }
+ }
+
+ toggleNamedWidgets(d, "audioIdLabel");
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotToggleSynthFaders()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_SYNTH_FADERS);
+
+ slotUpdateSynthFaderVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdateSynthFaderVisibility()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_synth_faders"));
+ if (!action)
+ return ;
+
+ action->setChecked(!(m_studio->getMixerDisplayOptions() &
+ MIXER_OMIT_SYNTH_FADERS));
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+ if (i->first >= SoftSynthInstrumentBase) {
+ FaderRec rec = i->second;
+ rec.setVisible(action->isChecked());
+ }
+ }
+
+ toggleNamedWidgets(action->isChecked(), "synthIdLabel");
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotToggleSubmasters()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_SUBMASTERS);
+
+ slotUpdateSubmasterVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdateSubmasterVisibility()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_audio_submasters"));
+ if (!action)
+ return ;
+
+ action->setChecked(!(m_studio->getMixerDisplayOptions() &
+ MIXER_OMIT_SUBMASTERS));
+
+ for (FaderVector::iterator i = m_submasters.begin(); i != m_submasters.end(); ++i) {
+ FaderRec rec = *i;
+ rec.setVisible(action->isChecked());
+ }
+
+ toggleNamedWidgets(action->isChecked(), "subMaster");
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotTogglePluginButtons()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_PLUGINS);
+
+ slotUpdatePluginButtonVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdatePluginButtonVisibility()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_plugin_buttons"));
+ if (!action)
+ return ;
+
+ action->setChecked(!(m_studio->getMixerDisplayOptions() &
+ MIXER_OMIT_PLUGINS));
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+ FaderRec rec = i->second;
+ rec.setPluginButtonsVisible(action->isChecked());
+ }
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotToggleUnassignedFaders()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_unassigned_faders"));
+ if (!action)
+ return ;
+
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_SHOW_UNASSIGNED_FADERS);
+
+ action->setChecked(m_studio->getMixerDisplayOptions() &
+ MIXER_SHOW_UNASSIGNED_FADERS);
+
+ populate();
+}
+
+void
+AudioMixerWindow::toggleNamedWidgets(bool show, const char* const name)
+{
+ QLayoutIterator it = m_mainBox->layout()->iterator();
+ QLayoutItem *child;
+ while ( (child = it.current()) != 0 ) {
+ QWidget * widget = child->widget();
+ if (widget && widget->name() && !strcmp(widget->name(), name)) {
+ if (show)
+ widget->show();
+ else
+ widget->hide();
+ }
+
+ ++it;
+ }
+
+}
+
+}
+#include "AudioMixerWindow.moc"
diff --git a/src/gui/studio/AudioMixerWindow.h b/src/gui/studio/AudioMixerWindow.h
new file mode 100644
index 0000000..99829de
--- /dev/null
+++ b/src/gui/studio/AudioMixerWindow.h
@@ -0,0 +1,191 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_AUDIOMIXERWINDOW_H_
+#define _RG_AUDIOMIXERWINDOW_H_
+
+#include "base/MidiProgram.h"
+#include <map>
+#include "MixerWindow.h"
+#include <qpixmap.h>
+#include <vector>
+
+
+class QWidget;
+class QVBox;
+class QPushButton;
+class QHBox;
+class QFrame;
+
+
+namespace Rosegarden
+{
+
+class SequencerMapper;
+class Rotary;
+class RosegardenGUIDoc;
+class MappedEvent;
+class Fader;
+class AudioVUMeter;
+class AudioRouteMenu;
+
+
+class AudioMixerWindow : public MixerWindow
+{
+ Q_OBJECT
+
+public:
+ AudioMixerWindow(QWidget *parent, RosegardenGUIDoc *document);
+ ~AudioMixerWindow();
+
+ void updateMeters(SequencerMapper *mapper);
+ void updateMonitorMeters(SequencerMapper *mapper);
+
+public slots:
+ void slotControllerDeviceEventReceived(MappedEvent *,
+ const void *);
+
+signals:
+ void selectPlugin(QWidget *, InstrumentId id, int index);
+
+ void play();
+ void stop();
+ void fastForwardPlayback();
+ void rewindPlayback();
+ void fastForwardPlaybackToEnd();
+ void rewindPlaybackToBeginning();
+ void record();
+ void panic();
+
+ // to be redirected to the instrument parameter box if necessary
+ void instrumentParametersChanged(InstrumentId);
+
+protected slots:
+ void slotFaderLevelChanged(float level);
+ void slotPanChanged(float value);
+ void slotInputChanged();
+ void slotOutputChanged();
+ void slotChannelsChanged();
+ void slotSoloChanged();
+ void slotMuteChanged();
+ void slotRecordChanged();
+ void slotSelectPlugin();
+
+ // to be called if something changes in an instrument parameter box
+ void slotUpdateInstrument(InstrumentId);
+
+ void slotTrackAssignmentsChanged();
+
+ // from Plugin dialog
+ void slotPluginSelected(InstrumentId id, int index, int plugin);
+ void slotPluginBypassed(InstrumentId id, int pluginIndex, bool bp);
+
+ void slotSetInputCountFromAction();
+ void slotSetSubmasterCountFromAction();
+
+ void slotToggleFaders();
+ void slotToggleSynthFaders();
+ void slotToggleSubmasters();
+ void slotTogglePluginButtons();
+ void slotToggleUnassignedFaders();
+
+ void slotUpdateFaderVisibility();
+ void slotUpdateSynthFaderVisibility();
+ void slotUpdateSubmasterVisibility();
+ void slotUpdatePluginButtonVisibility();
+
+protected:
+ virtual void sendControllerRefresh();
+
+private:
+
+ void toggleNamedWidgets(bool show, const char* const);
+
+
+ // manage the various bits of it in horizontal/vertical slices
+ // with other faders:
+
+ struct FaderRec {
+
+ FaderRec() :
+ m_populated(false),
+ m_input(0), m_output(0), m_pan(0), m_fader(0), m_meter(0),
+ m_muteButton(0), m_soloButton(0), m_recordButton(0),
+ m_stereoButton(0), m_stereoness(false), m_pluginBox(0)
+ { }
+
+ void setVisible(bool);
+ void setPluginButtonsVisible(bool);
+
+ bool m_populated;
+
+ AudioRouteMenu *m_input;
+ AudioRouteMenu *m_output;
+
+ Rotary *m_pan;
+ Fader *m_fader;
+ AudioVUMeter *m_meter;
+
+ QPushButton *m_muteButton;
+ QPushButton *m_soloButton;
+ QPushButton *m_recordButton;
+ QPushButton *m_stereoButton;
+ bool m_stereoness;
+
+ QVBox *m_pluginBox;
+ std::vector<QPushButton *> m_plugins;
+ };
+
+ QHBox *m_surroundBox;
+ QFrame *m_mainBox;
+
+ typedef std::map<InstrumentId, FaderRec> FaderMap;
+ FaderMap m_faders;
+
+ typedef std::vector<FaderRec> FaderVector;
+ FaderVector m_submasters;
+ FaderRec m_monitor;
+ FaderRec m_master;
+
+ void depopulate();
+ void populate();
+
+ bool isInstrumentAssigned(InstrumentId id);
+
+ void updateFader(int id); // instrument id if large enough, monitor if -1, master/sub otherwise
+ void updateRouteButtons(int id);
+ void updateStereoButton(int id);
+ void updatePluginButtons(int id);
+ void updateMiscButtons(int id);
+
+ QPixmap m_monoPixmap;
+ QPixmap m_stereoPixmap;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPlugin.cpp b/src/gui/studio/AudioPlugin.cpp
new file mode 100644
index 0000000..2cf3db2
--- /dev/null
+++ b/src/gui/studio/AudioPlugin.cpp
@@ -0,0 +1,78 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "AudioPlugin.h"
+
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include <qcolor.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+AudioPlugin::AudioPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category):
+ m_identifier(identifier),
+ m_name(name),
+ m_uniqueId(uniqueId),
+ m_label(label),
+ m_author(author),
+ m_copyright(copyright),
+ m_isSynth(isSynth),
+ m_isGrouped(isGrouped),
+ m_category(category),
+ m_colour(Qt::darkRed)
+{}
+
+void
+AudioPlugin::addPort(int number,
+ const QString &name,
+ PluginPort::PortType type,
+ PluginPort::PortDisplayHint hint,
+ PortData lowerBound,
+ PortData upperBound,
+ PortData defaultValue)
+{
+ PluginPort *port = new PluginPort(number,
+ qstrtostr(name),
+ type,
+ hint,
+ lowerBound,
+ upperBound,
+ defaultValue);
+ m_ports.push_back(port);
+
+}
+
+}
diff --git a/src/gui/studio/AudioPlugin.h b/src/gui/studio/AudioPlugin.h
new file mode 100644
index 0000000..591a43b
--- /dev/null
+++ b/src/gui/studio/AudioPlugin.h
@@ -0,0 +1,117 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_AUDIOPLUGIN_H_
+#define _RG_AUDIOPLUGIN_H_
+
+#include "base/AudioPluginInstance.h"
+#include <qcolor.h>
+#include <qstring.h>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class AudioPlugin
+{
+public:
+ AudioPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category);
+
+ QString getIdentifier() const { return m_identifier; }
+
+ QString getName() const { return m_name; }
+ unsigned long getUniqueId() const { return m_uniqueId; }
+ QString getLabel() const { return m_label; }
+ QString getAuthor() const { return m_author; }
+ QString getCopyright() const { return m_copyright; }
+ bool isSynth() const { return m_isSynth; }
+ bool isEffect() const { // true if >0 audio inputs
+ for (unsigned int i = 0; i < m_ports.size(); ++i) {
+ if ((m_ports[i]->getType() & PluginPort::Input) &&
+ (m_ports[i]->getType() & PluginPort::Audio)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ bool isGrouped() const { return m_isGrouped; }
+ QString getCategory() const { return m_category; }
+
+ void addPort(int number,
+ const QString &name,
+ PluginPort::PortType type,
+ PluginPort::PortDisplayHint hint,
+ PortData lowerBound,
+ PortData upperBound,
+ PortData defaultVale);
+
+ typedef std::vector<PluginPort*>::iterator PortIterator;
+
+ PortIterator begin() { return m_ports.begin(); }
+ PortIterator end() { return m_ports.end(); }
+
+ QColor getColour() const { return m_colour; }
+ void setColour(const QColor &colour) { m_colour = colour; }
+
+protected:
+
+ QString m_identifier;
+
+ QString m_name;
+ unsigned long m_uniqueId;
+ QString m_label;
+ QString m_author;
+ QString m_copyright;
+ bool m_isSynth;
+ bool m_isGrouped;
+ QString m_category;
+
+ // our ports and associated hints
+ std::vector<PluginPort*> m_ports;
+
+ // Colour of this activated plugin
+ //
+ QColor m_colour;
+};
+
+typedef std::vector<AudioPlugin*>::iterator PluginIterator;
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginClipboard.cpp b/src/gui/studio/AudioPluginClipboard.cpp
new file mode 100644
index 0000000..54f5612
--- /dev/null
+++ b/src/gui/studio/AudioPluginClipboard.cpp
@@ -0,0 +1,32 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "AudioPluginClipboard.h"
+
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/studio/AudioPluginClipboard.h b/src/gui/studio/AudioPluginClipboard.h
new file mode 100644
index 0000000..e31ed90
--- /dev/null
+++ b/src/gui/studio/AudioPluginClipboard.h
@@ -0,0 +1,52 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_AUDIOPLUGINCLIPBOARD_H_
+#define _RG_AUDIOPLUGINCLIPBOARD_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+struct AudioPluginClipboard
+{
+ int m_pluginNumber;
+ std::map<std::string, std::string> m_configuration;
+ std::string m_program;
+ std::vector<float> m_controlValues;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginManager.cpp b/src/gui/studio/AudioPluginManager.cpp
new file mode 100644
index 0000000..6b64085
--- /dev/null
+++ b/src/gui/studio/AudioPluginManager.cpp
@@ -0,0 +1,307 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "AudioPluginManager.h"
+
+#include "misc/Debug.h"
+#include "AudioPluginClipboard.h"
+#include "AudioPlugin.h"
+#include "base/AudioPluginInstance.h"
+#include "gui/application/RosegardenApplication.h"
+#include "sound/PluginFactory.h"
+#include "sound/PluginIdentifier.h"
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qmutex.h>
+#include <qstring.h>
+#include <qthread.h>
+
+
+namespace Rosegarden
+{
+
+AudioPluginManager::AudioPluginManager() :
+ m_sampleRate(0),
+ m_enumerator(this)
+{
+// std::cerr << "AudioPluginManager[" << this << "]::AudioPluginManager - "
+// << "trace is ";
+// std::cerr << kdBacktrace() << std::endl;
+
+
+ // fetch from sequencer
+ fetchSampleRate();
+
+ // Clear the plugin clipboard
+ //
+ m_pluginClipboard.m_pluginNumber = -1;
+ m_pluginClipboard.m_program = "";
+ m_pluginClipboard.m_controlValues.clear();
+
+ m_enumerator.start();
+}
+
+AudioPluginManager::Enumerator::Enumerator(AudioPluginManager *manager) :
+ m_manager(manager),
+ m_done(false)
+{}
+
+void
+AudioPluginManager::Enumerator::run()
+{
+ QMutexLocker locker(&(m_manager->m_mutex));
+ MappedObjectPropertyList rawPlugins;
+
+ RG_DEBUG << "\n\nAudioPluginManager::Enumerator::run()\n\n" << endl;
+
+ if (!rgapp->noSequencerMode()) {
+ // We only waste the time looking for plugins here if we
+ // know we're actually going to be able to use them.
+ PluginFactory::enumerateAllPlugins(rawPlugins);
+ }
+
+ unsigned int i = 0;
+
+ while (i < rawPlugins.size()) {
+
+ QString identifier = rawPlugins[i++];
+ QString name = rawPlugins[i++];
+ unsigned long uniqueId = rawPlugins[i++].toLong();
+ QString label = rawPlugins[i++];
+ QString author = rawPlugins[i++];
+ QString copyright = rawPlugins[i++];
+ bool isSynth = ((rawPlugins[i++]).lower() == "true");
+ bool isGrouped = ((rawPlugins[i++]).lower() == "true");
+ QString category = rawPlugins[i++];
+ unsigned int portCount = rawPlugins[i++].toInt();
+
+ // std::cerr << "PLUGIN: " << i << ": " << (identifier ? identifier : "(null)") << " unique id " << uniqueId << " / CATEGORY: \"" << (category ? category : "(null)") << "\"" << std::endl;
+
+ AudioPlugin *aP = m_manager->addPlugin(identifier,
+ name,
+ uniqueId,
+ label,
+ author,
+ copyright,
+ isSynth,
+ isGrouped,
+ category);
+
+ for (unsigned int j = 0; j < portCount; j++) {
+
+ int number = rawPlugins[i++].toInt();
+ name = rawPlugins[i++];
+ PluginPort::PortType type =
+ PluginPort::PortType(rawPlugins[i++].toInt());
+ PluginPort::PortDisplayHint hint =
+ PluginPort::PortDisplayHint(rawPlugins[i++].toInt());
+ PortData lowerBound = rawPlugins[i++].toFloat();
+ PortData upperBound = rawPlugins[i++].toFloat();
+ PortData defaultValue = rawPlugins[i++].toFloat();
+
+ aP->addPort(number,
+ name,
+ type,
+ hint,
+ lowerBound,
+ upperBound,
+ defaultValue);
+ }
+ }
+
+ m_done = true;
+
+ RG_DEBUG << "\n\nAudioPluginManager::Enumerator::run() - done\n\n" << endl;
+}
+
+AudioPlugin*
+AudioPluginManager::addPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category)
+{
+ AudioPlugin *newPlugin = new AudioPlugin(identifier,
+ name,
+ uniqueId,
+ label,
+ author,
+ copyright,
+ isSynth,
+ isGrouped,
+ category);
+ m_plugins.push_back(newPlugin);
+
+ return newPlugin;
+}
+
+bool
+AudioPluginManager::removePlugin(const QString &identifier)
+{
+ std::vector<AudioPlugin*>::iterator it = m_plugins.begin();
+
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getIdentifier() == identifier) {
+ delete *it;
+ m_plugins.erase(it);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::vector<QString>
+AudioPluginManager::getPluginNames()
+{
+ awaitEnumeration();
+
+ std::vector<QString> names;
+
+ PluginIterator it = m_plugins.begin();
+
+ for (; it != m_plugins.end(); ++it)
+ names.push_back((*it)->getName());
+
+ return names;
+}
+
+AudioPlugin*
+AudioPluginManager::getPlugin(int number)
+{
+ awaitEnumeration();
+
+ if (number < 0 || number > (int(m_plugins.size()) - 1))
+ return 0;
+
+ return m_plugins[number];
+}
+
+int
+AudioPluginManager::getPositionByIdentifier(QString identifier)
+{
+ awaitEnumeration();
+
+ int pos = 0;
+ PluginIterator it = m_plugins.begin();
+
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getIdentifier() == identifier)
+ return pos;
+
+ pos++;
+ }
+
+ pos = 0;
+ it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if (PluginIdentifier::areIdentifiersSimilar((*it)->getIdentifier(), identifier))
+ return pos;
+
+ pos++;
+ }
+
+ return -1;
+}
+
+AudioPlugin*
+AudioPluginManager::getPluginByIdentifier(QString identifier)
+{
+ awaitEnumeration();
+
+ PluginIterator it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getIdentifier() == identifier)
+ return (*it);
+ }
+
+ it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if (PluginIdentifier::areIdentifiersSimilar((*it)->getIdentifier(), identifier))
+ return (*it);
+ }
+
+ return 0;
+}
+
+AudioPlugin*
+AudioPluginManager::getPluginByUniqueId(unsigned long uniqueId)
+{
+ awaitEnumeration();
+
+ PluginIterator it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getUniqueId() == uniqueId)
+ return (*it);
+ }
+
+ return 0;
+}
+
+PluginIterator
+AudioPluginManager::begin()
+{
+ awaitEnumeration();
+ return m_plugins.begin();
+}
+
+PluginIterator
+AudioPluginManager::end()
+{
+ awaitEnumeration();
+ return m_plugins.end();
+}
+
+void
+AudioPluginManager::awaitEnumeration()
+{
+ while (!m_enumerator.isDone()) {
+ RG_DEBUG << "\n\nAudioPluginManager::awaitEnumeration() - waiting\n\n" << endl;
+// m_mutex.lock();
+ usleep(100000);
+// m_mutex.unlock();
+ }
+}
+
+void
+AudioPluginManager::fetchSampleRate()
+{
+ QCString replyType;
+ QByteArray replyData;
+
+ if (rgapp->sequencerCall("getSampleRate()", replyType, replyData)) {
+
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ unsigned int result;
+ streamIn >> result;
+ m_sampleRate = result;
+ }
+}
+
+}
diff --git a/src/gui/studio/AudioPluginManager.h b/src/gui/studio/AudioPluginManager.h
new file mode 100644
index 0000000..f8574f8
--- /dev/null
+++ b/src/gui/studio/AudioPluginManager.h
@@ -0,0 +1,118 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_AUDIOPLUGINMANAGER_H_
+#define _RG_AUDIOPLUGINMANAGER_H_
+
+#include "AudioPluginClipboard.h"
+#include <qmutex.h>
+#include <qstring.h>
+#include <qthread.h>
+#include <vector>
+#include "AudioPlugin.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class AudioPlugin;
+
+
+class AudioPluginManager
+{
+public:
+ AudioPluginManager();
+
+ // Get a straight list of names
+ //
+ std::vector<QString> getPluginNames();
+
+ // Some useful members
+ //
+ AudioPlugin* getPlugin(int number);
+
+ AudioPlugin* getPluginByIdentifier(QString identifier);
+ int getPositionByIdentifier(QString identifier);
+
+ // Deprecated -- the GUI shouldn't be using unique ID because it's
+ // bound to a particular plugin type (and not necessarily unique
+ // anyway). It should use the identifier instead, which is a
+ // structured string managed by the sequencer. Keep this in only
+ // for compatibility with old .rg files.
+ //
+ AudioPlugin* getPluginByUniqueId(unsigned long uniqueId);
+
+ PluginIterator begin();
+ PluginIterator end();
+
+ // Sample rate
+ //
+ void setSampleRate(unsigned int rate) { m_sampleRate = rate; }
+ unsigned int getSampleRate() const { return m_sampleRate; }
+
+ AudioPluginClipboard* getPluginClipboard() { return &m_pluginClipboard; }
+
+protected:
+ AudioPlugin* addPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category);
+
+ bool removePlugin(const QString &identifier);
+
+ class Enumerator : public QThread
+ {
+ public:
+ Enumerator(AudioPluginManager *);
+ virtual void run();
+ bool isDone() const { return m_done; }
+
+ protected:
+ AudioPluginManager *m_manager;
+ bool m_done;
+ };
+
+ void awaitEnumeration();
+ void fetchSampleRate();
+
+ std::vector<AudioPlugin*> m_plugins;
+ unsigned int m_sampleRate;
+ AudioPluginClipboard m_pluginClipboard;
+ Enumerator m_enumerator;
+ QMutex m_mutex;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUI.cpp b/src/gui/studio/AudioPluginOSCGUI.cpp
new file mode 100644
index 0000000..106cbbe
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUI.cpp
@@ -0,0 +1,234 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifdef HAVE_LIBLO
+
+#include "AudioPluginOSCGUI.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Exception.h"
+#include "sound/PluginIdentifier.h"
+#include <kprocess.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+AudioPluginOSCGUI::AudioPluginOSCGUI(AudioPluginInstance *instance,
+ QString serverURL, QString friendlyName) :
+ m_gui(0),
+ m_address(0),
+ m_basePath(""),
+ m_serverUrl(serverURL)
+{
+ QString identifier = strtoqstr(instance->getIdentifier());
+
+ QString filePath = getGUIFilePath(identifier);
+ if (!filePath) {
+ throw Exception("No GUI found");
+ }
+
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+ QFileInfo soInfo(soName);
+
+ // arguments: osc url, dll name, label, instance tag
+
+ m_gui = new KProcess();
+
+ *m_gui << filePath
+ << m_serverUrl
+ << soInfo.fileName()
+ << label
+ << friendlyName;
+
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: Starting process "
+ << filePath << " " << m_serverUrl << " "
+ << soInfo.fileName() << " " << label << " " << friendlyName << endl;
+
+ if (!m_gui->start(KProcess::NotifyOnExit, KProcess::NoCommunication)) {
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: Couldn't start process " << filePath << endl;
+ delete m_gui;
+ m_gui = 0;
+ throw Exception("Failed to start GUI");
+ }
+}
+
+AudioPluginOSCGUI::~AudioPluginOSCGUI()
+{
+ quit();
+}
+
+QString
+AudioPluginOSCGUI::getGUIFilePath(QString identifier)
+{
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+
+ RG_DEBUG << "AudioPluginOSCGUI::getGUIFilePath(" << identifier << ")" << endl;
+
+ QFileInfo soInfo(soName);
+ if (soInfo.isRelative()) {
+ //!!!
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: Unable to deal with relative .so path \"" << soName << "\" in identifier \"" << identifier << "\" yet" << endl;
+ throw Exception("Can't deal with relative .soname");
+ }
+
+ QDir dir(soInfo.dir());
+ QString fileBase(soInfo.baseName(TRUE));
+
+ if (!dir.cd(fileBase)) {
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: No GUI subdir for plugin .so " << soName << endl;
+ throw Exception("No GUI subdir available");
+ }
+
+ const QFileInfoList *list = dir.entryInfoList();
+
+ // in order of preference:
+ const char *suffixes[] = { "_rg", "_kde", "_qt", "_gtk2", "_gtk", "_x11", "_gui"
+ };
+ int nsuffixes = sizeof(suffixes) / sizeof(suffixes[0]);
+
+ for (int k = 0; k <= nsuffixes; ++k) {
+
+ for (int fuzzy = 0; fuzzy <= 1; ++fuzzy) {
+
+ QFileInfoListIterator i(*list);
+ QFileInfo *info;
+
+ while ((info = i.current()) != 0) {
+
+ RG_DEBUG << "Looking at " << info->fileName() << " in path "
+ << info->filePath() << " for suffix " << (k == nsuffixes ? "(none)" : suffixes[k]) << ", fuzzy " << fuzzy << endl;
+
+ ++i;
+
+ if (!(info->isFile() || info->isSymLink())
+ || !info->isExecutable()) {
+ RG_DEBUG << "(not executable)" << endl;
+ continue;
+ }
+
+ if (fuzzy) {
+ if (info->fileName().left(fileBase.length()) != fileBase)
+ continue;
+ RG_DEBUG << "(is file base)" << endl;
+ } else {
+ if (info->fileName().left(label.length()) != label)
+ continue;
+ RG_DEBUG << "(is label)" << endl;
+ }
+
+ if (k == nsuffixes || info->fileName().lower().endsWith(suffixes[k])) {
+ RG_DEBUG << "(ends with suffix " << (k == nsuffixes ? "(none)" : suffixes[k]) << " or out of suffixes)" << endl;
+ return info->filePath();
+ }
+ RG_DEBUG << "(doesn't end with suffix " << (k == nsuffixes ? "(none)" : suffixes[k]) << ")" << endl;
+ }
+ }
+ }
+
+ return QString();
+}
+
+void
+AudioPluginOSCGUI::setGUIUrl(QString url)
+{
+ if (m_address)
+ lo_address_free(m_address);
+
+ char *host = lo_url_get_hostname(url);
+ char *port = lo_url_get_port(url);
+ m_address = lo_address_new(host, port);
+ free(host);
+ free(port);
+
+ m_basePath = lo_url_get_path(url);
+}
+
+void
+AudioPluginOSCGUI::show()
+{
+ RG_DEBUG << "AudioPluginOSCGUI::show" << endl;
+
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/show";
+ lo_send(m_address, path, "");
+}
+
+void
+AudioPluginOSCGUI::hide()
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/hide";
+ lo_send(m_address, path, "");
+}
+
+void
+AudioPluginOSCGUI::quit()
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/quit";
+ lo_send(m_address, path, "");
+}
+
+void
+AudioPluginOSCGUI::sendProgram(int bank, int program)
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/program";
+ lo_send(m_address, path, "ii", bank, program);
+}
+
+void
+AudioPluginOSCGUI::sendPortValue(int port, float value)
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/control";
+ lo_send(m_address, path, "if", port, value);
+}
+
+void
+AudioPluginOSCGUI::sendConfiguration(QString key, QString value)
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/configure";
+ lo_send(m_address, path, "ss", key.data(), value.data());
+}
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUI.h b/src/gui/studio/AudioPluginOSCGUI.h
new file mode 100644
index 0000000..d1982f4
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUI.h
@@ -0,0 +1,77 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_AUDIOPLUGINOSCGUI_H_
+#define _RG_AUDIOPLUGINOSCGUI_H_
+
+#ifdef HAVE_LIBLO
+
+#include <lo/lo.h>
+
+#include <qstring.h>
+
+
+class KProcess;
+
+
+namespace Rosegarden
+{
+
+class AudioPluginInstance;
+
+
+class AudioPluginOSCGUI
+{
+public:
+ AudioPluginOSCGUI(AudioPluginInstance *instance,
+ QString serverURL, QString friendlyName);
+ virtual ~AudioPluginOSCGUI();
+
+ void setGUIUrl(QString url);
+
+ void show();
+ void hide();
+ void quit();
+ void sendProgram(int bank, int program);
+ void sendPortValue(int port, float value);
+ void sendConfiguration(QString key, QString value);
+
+ static QString getGUIFilePath(QString identifier);
+
+protected:
+ KProcess *m_gui;
+ lo_address m_address;
+ QString m_basePath;
+ QString m_serverUrl;
+};
+
+
+
+}
+
+
+#endif
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUIManager.cpp b/src/gui/studio/AudioPluginOSCGUIManager.cpp
new file mode 100644
index 0000000..54c23d7
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUIManager.cpp
@@ -0,0 +1,711 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifdef HAVE_LIBLO
+
+#include <lo/lo.h>
+
+#include "AudioPluginOSCGUIManager.h"
+
+#include "sound/Midi.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "AudioPluginOSCGUI.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/Studio.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "OSCMessage.h"
+#include "sound/MappedEvent.h"
+#include "sound/PluginIdentifier.h"
+#include "StudioControl.h"
+#include "TimerCallbackAssistant.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+static void osc_error(int num, const char *msg, const char *path)
+{
+ std::cerr << "Rosegarden: ERROR: liblo server error " << num
+ << " in path " << path << ": " << msg << std::endl;
+}
+
+static int osc_message_handler(const char *path, const char *types, lo_arg **argv,
+ int argc, lo_message, void *user_data)
+{
+ AudioPluginOSCGUIManager *manager = (AudioPluginOSCGUIManager *)user_data;
+
+ InstrumentId instrument;
+ int position;
+ QString method;
+
+ if (!manager->parseOSCPath(path, instrument, position, method)) {
+ return 1;
+ }
+
+ OSCMessage *message = new OSCMessage();
+ message->setTarget(instrument);
+ message->setTargetData(position);
+ message->setMethod(qstrtostr(method));
+
+ int arg = 0;
+ while (types && arg < argc && types[arg]) {
+ message->addArg(types[arg], argv[arg]);
+ ++arg;
+ }
+
+ manager->postMessage(message);
+ return 0;
+}
+
+AudioPluginOSCGUIManager::AudioPluginOSCGUIManager(RosegardenGUIApp *app) :
+ m_app(app),
+ m_studio(0),
+ m_haveOSCThread(false),
+ m_oscBuffer(1023),
+ m_dispatchTimer(0)
+{}
+
+AudioPluginOSCGUIManager::~AudioPluginOSCGUIManager()
+{
+ delete m_dispatchTimer;
+
+ for (TargetGUIMap::iterator i = m_guis.begin(); i != m_guis.end(); ++i) {
+ for (IntGUIMap::iterator j = i->second.begin(); j != i->second.end();
+ ++j) {
+ delete j->second;
+ }
+ }
+ m_guis.clear();
+
+#ifdef HAVE_LIBLO_THREADSTOP
+
+ if (m_haveOSCThread)
+ lo_server_thread_stop(m_serverThread);
+#endif
+}
+
+void
+AudioPluginOSCGUIManager::checkOSCThread()
+{
+ if (m_haveOSCThread)
+ return ;
+
+ m_serverThread = lo_server_thread_new(NULL, osc_error);
+
+ lo_server_thread_add_method(m_serverThread, NULL, NULL,
+ osc_message_handler, this);
+
+ lo_server_thread_start(m_serverThread);
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: Base OSC URL is "
+ << lo_server_thread_get_url(m_serverThread) << endl;
+
+ m_dispatchTimer = new TimerCallbackAssistant(20, timerCallback, this);
+
+ m_haveOSCThread = true;
+}
+
+bool
+AudioPluginOSCGUIManager::hasGUI(InstrumentId instrument, int position)
+{
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) return false;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) return false;
+
+ try {
+ QString filePath = AudioPluginOSCGUI::getGUIFilePath
+ (strtoqstr(pluginInstance->getIdentifier()));
+ return (filePath && filePath != "");
+ } catch (Exception e) { // that's OK
+ return false;
+ }
+}
+
+void
+AudioPluginOSCGUIManager::startGUI(InstrumentId instrument, int position)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: " << instrument << "," << position
+ << endl;
+
+ checkOSCThread();
+
+ if (m_guis.find(instrument) != m_guis.end() &&
+ m_guis[instrument].find(position) != m_guis[instrument].end()) {
+ RG_DEBUG << "stopping GUI first" << endl;
+ stopGUI(instrument, position);
+ }
+
+ // check the label
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: no such instrument or buss as "
+ << instrument << endl;
+ return;
+ }
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: no plugin at position "
+ << position << " for instrument " << instrument << endl;
+ return ;
+ }
+
+ try {
+ AudioPluginOSCGUI *gui =
+ new AudioPluginOSCGUI(pluginInstance,
+ getOSCUrl(instrument,
+ position,
+ strtoqstr(pluginInstance->getIdentifier())),
+ getFriendlyName(instrument,
+ position,
+ strtoqstr(pluginInstance->getIdentifier())));
+ m_guis[instrument][position] = gui;
+
+ } catch (Exception e) {
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: failed to start GUI: "
+ << e.getMessage() << endl;
+ }
+}
+
+void
+AudioPluginOSCGUIManager::showGUI(InstrumentId instrument, int position)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::showGUI: " << instrument << "," << position
+ << endl;
+
+ if (m_guis.find(instrument) != m_guis.end() &&
+ m_guis[instrument].find(position) != m_guis[instrument].end()) {
+ m_guis[instrument][position]->show();
+ } else {
+ startGUI(instrument, position);
+ }
+}
+
+void
+AudioPluginOSCGUIManager::stopGUI(InstrumentId instrument, int position)
+{
+ if (m_guis.find(instrument) != m_guis.end() &&
+ m_guis[instrument].find(position) != m_guis[instrument].end()) {
+ delete m_guis[instrument][position];
+ m_guis[instrument].erase(position);
+ if (m_guis[instrument].empty())
+ m_guis.erase(instrument);
+ }
+}
+
+void
+AudioPluginOSCGUIManager::stopAllGUIs()
+{
+ while (!m_guis.empty()) {
+ while (!m_guis.begin()->second.empty()) {
+ delete (m_guis.begin()->second.begin()->second);
+ m_guis.begin()->second.erase(m_guis.begin()->second.begin());
+ }
+ m_guis.erase(m_guis.begin());
+ }
+}
+
+void
+AudioPluginOSCGUIManager::postMessage(OSCMessage *message)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::postMessage" << endl;
+ m_oscBuffer.write(&message, 1);
+}
+
+void
+AudioPluginOSCGUIManager::updateProgram(InstrumentId instrument, int position)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::updateProgram(" << instrument << ","
+ << position << ")" << endl;
+
+ if (m_guis.find(instrument) == m_guis.end() ||
+ m_guis[instrument].find(position) == m_guis[instrument].end())
+ return ;
+
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) return;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) return;
+
+ unsigned long rv = StudioControl::getPluginProgram
+ (pluginInstance->getMappedId(),
+ strtoqstr(pluginInstance->getProgram()));
+
+ int bank = rv >> 16;
+ int program = rv - (bank << 16);
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::updateProgram(" << instrument << ","
+ << position << "): rv " << rv << ", bank " << bank << ", program " << program << endl;
+
+ m_guis[instrument][position]->sendProgram(bank, program);
+}
+
+void
+AudioPluginOSCGUIManager::updatePort(InstrumentId instrument, int position,
+ int port)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::updatePort(" << instrument << ","
+ << position << "," << port << ")" << endl;
+
+ if (m_guis.find(instrument) == m_guis.end() ||
+ m_guis[instrument].find(position) == m_guis[instrument].end())
+ return ;
+
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) return;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance)
+ return ;
+
+ PluginPortInstance *porti = pluginInstance->getPort(port);
+ if (!porti)
+ return ;
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::updatePort(" << instrument << ","
+ << position << "," << port << "): value " << porti->value << endl;
+
+ m_guis[instrument][position]->sendPortValue(port, porti->value);
+}
+
+void
+AudioPluginOSCGUIManager::updateConfiguration(InstrumentId instrument, int position,
+ QString key)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::updateConfiguration(" << instrument << ","
+ << position << "," << key << ")" << endl;
+
+ if (m_guis.find(instrument) == m_guis.end() ||
+ m_guis[instrument].find(position) == m_guis[instrument].end())
+ return ;
+
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container) return;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) return;
+
+ QString value = strtoqstr(pluginInstance->getConfigurationValue(qstrtostr(key)));
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::updatePort(" << instrument << ","
+ << position << "," << key << "): value " << value << endl;
+
+ m_guis[instrument][position]->sendConfiguration(key, value);
+}
+
+QString
+AudioPluginOSCGUIManager::getOSCUrl(InstrumentId instrument, int position,
+ QString identifier)
+{
+ // OSC URL will be of the form
+ // osc.udp://localhost:54343/plugin/dssi/<instrument>/<position>/<label>
+ // where <position> will be "synth" for synth plugins
+
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+
+ QString baseUrl = lo_server_thread_get_url(m_serverThread);
+ if (!baseUrl.endsWith("/"))
+ baseUrl += '/';
+
+ QString url = QString("%1%2/%3/%4/%5/%6")
+ .arg(baseUrl)
+ .arg("plugin")
+ .arg(type)
+ .arg(instrument);
+
+ if (position == int(Instrument::SYNTH_PLUGIN_POSITION)) {
+ url = url.arg("synth");
+ } else {
+ url = url.arg(position);
+ }
+
+ url = url.arg(label);
+
+ return url;
+}
+
+bool
+AudioPluginOSCGUIManager::parseOSCPath(QString path, InstrumentId &instrument,
+ int &position, QString &method)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath(" << path << ")" << endl;
+ if (!m_studio)
+ return false;
+
+ QString pluginStr("/plugin/");
+
+ if (path.startsWith("//")) {
+ path = path.right(path.length() - 1);
+ }
+
+ if (!path.startsWith(pluginStr)) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: malformed path "
+ << path << endl;
+ return false;
+ }
+
+ path = path.right(path.length() - pluginStr.length());
+
+ QString type = path.section('/', 0, 0);
+ QString instrumentStr = path.section('/', 1, 1);
+ QString positionStr = path.section('/', 2, 2);
+ QString label = path.section('/', 3, -2);
+ method = path.section('/', -1, -1);
+
+ if (!instrumentStr || !positionStr) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: no instrument or position in " << path << endl;
+ return false;
+ }
+
+ instrument = instrumentStr.toUInt();
+
+ if (positionStr == "synth") {
+ position = Instrument::SYNTH_PLUGIN_POSITION;
+ } else {
+ position = positionStr.toInt();
+ }
+
+ // check the label
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: no such instrument or buss as "
+ << instrument << " in path " << path << endl;
+ return false;
+ }
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: no plugin at position "
+ << position << " for instrument " << instrument << " in path "
+ << path << endl;
+ return false;
+ }
+
+ QString identifier = strtoqstr(pluginInstance->getIdentifier());
+ QString iType, iSoName, iLabel;
+ PluginIdentifier::parseIdentifier(identifier, iType, iSoName, iLabel);
+ if (iLabel != label) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: wrong label for plugin"
+ << " at position " << position << " for instrument " << instrument
+ << " in path " << path << " (actual label is " << iLabel
+ << ")" << endl;
+ return false;
+ }
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: good path " << path
+ << ", got mapped id " << pluginInstance->getMappedId() << endl;
+
+ return true;
+}
+
+QString
+AudioPluginOSCGUIManager::getFriendlyName(InstrumentId instrument, int position,
+ QString)
+{
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container)
+ return i18n("Rosegarden Plugin");
+ else {
+ if (position == int(Instrument::SYNTH_PLUGIN_POSITION)) {
+ return i18n("Rosegarden: %1").arg(strtoqstr(container->getPresentationName()));
+ } else {
+ return i18n("Rosegarden: %1: %2").arg(strtoqstr(container->getPresentationName()))
+ .arg(i18n("Plugin slot %1").arg(position));
+ }
+ }
+}
+
+void
+AudioPluginOSCGUIManager::timerCallback(void *data)
+{
+ AudioPluginOSCGUIManager *manager = (AudioPluginOSCGUIManager *)data;
+ manager->dispatch();
+}
+
+void
+AudioPluginOSCGUIManager::dispatch()
+{
+ if (!m_studio)
+ return ;
+
+ while (m_oscBuffer.getReadSpace() > 0) {
+
+ OSCMessage *message = 0;
+ m_oscBuffer.read(&message, 1);
+
+ int instrument = message->getTarget();
+ int position = message->getTargetData();
+
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container) continue;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) continue;
+
+ AudioPluginOSCGUI *gui = 0;
+
+ if (m_guis.find(instrument) == m_guis.end()) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: no GUI for instrument "
+ << instrument << endl;
+ } else if (m_guis[instrument].find(position) == m_guis[instrument].end()) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: no GUI for instrument "
+ << instrument << ", position " << position << endl;
+ } else {
+ gui = m_guis[instrument][position];
+ }
+
+ std::string method = message->getMethod();
+
+ char type;
+ const lo_arg *arg;
+
+ // These generally call back on the RosegardenGUIApp. We'd
+ // like to emit signals, but making AudioPluginOSCGUIManager a
+ // QObject is problematic if it's only conditionally compiled.
+
+ if (method == "control") {
+
+ if (message->getArgCount() != 2) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for control method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 'i') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get port number"
+ << endl;
+ goto done;
+ }
+ int port = arg->i;
+ if (!(arg = message->getArg(1, type)) || type != 'f') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get port value"
+ << endl;
+ goto done;
+ }
+ float value = arg->f;
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: setting port " << port
+ << " to value " << value << endl;
+
+ m_app->slotChangePluginPort(instrument, position, port, value);
+
+ } else if (method == "program") {
+
+ if (message->getArgCount() != 2) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for program method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 'i') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get bank number"
+ << endl;
+ goto done;
+ }
+ int bank = arg->i;
+ if (!(arg = message->getArg(1, type)) || type != 'i') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get program number"
+ << endl;
+ goto done;
+ }
+ int program = arg->i;
+
+ QString programName = StudioControl::getPluginProgram
+ (pluginInstance->getMappedId(), bank, program);
+
+ m_app->slotChangePluginProgram(instrument, position, programName);
+
+ } else if (method == "update") {
+
+ if (message->getArgCount() != 1) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for update method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 's') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get GUI URL"
+ << endl;
+ goto done;
+ }
+ QString url = &arg->s;
+
+ if (!gui) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: no GUI for update method"
+ << endl;
+ goto done;
+ }
+
+ gui->setGUIUrl(url);
+
+ for (AudioPluginInstance::ConfigMap::const_iterator i =
+ pluginInstance->getConfiguration().begin();
+ i != pluginInstance->getConfiguration().end(); ++i) {
+
+ QString key = strtoqstr(i->first);
+ QString value = strtoqstr(i->second);
+
+#ifdef DSSI_PROJECT_DIRECTORY_KEY
+
+ if (key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY) {
+ key = DSSI_PROJECT_DIRECTORY_KEY;
+ }
+#endif
+
+ RG_DEBUG << "update: configuration: " << key << " -> "
+ << value << endl;
+
+ gui->sendConfiguration(key, value);
+ }
+
+ unsigned long rv = StudioControl::getPluginProgram
+ (pluginInstance->getMappedId(), strtoqstr(pluginInstance->getProgram()));
+
+ int bank = rv >> 16;
+ int program = rv - (bank << 16);
+ gui->sendProgram(bank, program);
+
+ int controlCount = 0;
+ for (PortInstanceIterator i = pluginInstance->begin();
+ i != pluginInstance->end(); ++i) {
+ gui->sendPortValue((*i)->number, (*i)->value);
+ /* Avoid overloading the GUI if there are lots and lots of ports */
+ if (++controlCount % 50 == 0)
+ usleep(300000);
+ }
+
+ gui->show();
+
+ } else if (method == "configure") {
+
+ if (message->getArgCount() != 2) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for configure method"
+ << endl;
+ goto done;
+ }
+
+ if (!(arg = message->getArg(0, type)) || type != 's') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get configure key"
+ << endl;
+ goto done;
+ }
+ QString key = &arg->s;
+
+ if (!(arg = message->getArg(1, type)) || type != 's') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get configure value"
+ << endl;
+ goto done;
+ }
+ QString value = &arg->s;
+
+#ifdef DSSI_RESERVED_CONFIGURE_PREFIX
+
+ if (key.startsWith(DSSI_RESERVED_CONFIGURE_PREFIX) ||
+ key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: illegal reserved configure call from gui: " << key << " -> " << value << endl;
+ goto done;
+ }
+#endif
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: configure(" << key << "," << value
+ << ")" << endl;
+
+ m_app->slotChangePluginConfiguration(instrument, position,
+#ifdef DSSI_GLOBAL_CONFIGURE_PREFIX
+ key.startsWith(DSSI_GLOBAL_CONFIGURE_PREFIX),
+#else
+ false,
+#endif
+ key, value);
+
+ } else if (method == "midi") {
+
+ if (message->getArgCount() != 1) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for midi method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 'm') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get MIDI event"
+ << endl;
+ goto done;
+ }
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: handling MIDI message" << endl;
+
+ // let's only handle note on and note off
+
+ int eventCode = arg->m[1];
+ int eventType = eventCode & MIDI_MESSAGE_TYPE_MASK;
+ if (eventType == MIDI_NOTE_ON ||
+ eventType == MIDI_NOTE_OFF) {
+ MappedEvent ev(instrument,
+ MappedEvent::MidiNote,
+ MidiByte(arg->m[2]),
+ MidiByte(arg->m[3]),
+ RealTime::zeroTime,
+ RealTime::zeroTime,
+ RealTime::zeroTime);
+ if (eventType == MIDI_NOTE_OFF)
+ ev.setVelocity(0);
+ StudioControl::sendMappedEvent(ev);
+ }
+
+ } else if (method == "exiting") {
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: GUI exiting" << endl;
+ stopGUI(instrument, position);
+ m_app->slotPluginGUIExited(instrument, position);
+
+ } else {
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: unknown method " << method << endl;
+ }
+
+done:
+ delete message;
+ }
+}
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUIManager.h b/src/gui/studio/AudioPluginOSCGUIManager.h
new file mode 100644
index 0000000..0bef2a2
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUIManager.h
@@ -0,0 +1,104 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_AUDIOPLUGINOSCGUIMANAGER_H_
+#define _RG_AUDIOPLUGINOSCGUIMANAGER_H_
+
+#ifdef HAVE_LIBLO
+
+#include <lo/lo.h>
+#include "base/MidiProgram.h"
+#include <map>
+#include "sound/RingBuffer.h"
+#include <qstring.h>
+
+
+
+namespace Rosegarden
+{
+
+class TimerCallbackAssistant;
+class Studio;
+class RosegardenGUIApp;
+class OSCMessage;
+class AudioPluginOSCGUI;
+
+
+class AudioPluginOSCGUIManager
+{
+public:
+ AudioPluginOSCGUIManager(RosegardenGUIApp *app);
+ virtual ~AudioPluginOSCGUIManager();
+
+ void setStudio(Studio *studio) { m_studio = studio; }
+
+ bool hasGUI(InstrumentId id, int position);
+ void startGUI(InstrumentId id, int position);
+ void showGUI(InstrumentId id, int position);
+ void stopGUI(InstrumentId id, int position);
+ void stopAllGUIs();
+
+ void postMessage(OSCMessage *message); // I take over ownership of message
+ void dispatch();
+
+ void updateProgram(InstrumentId id, int position);
+ void updatePort(InstrumentId id, int position, int port);
+ void updateConfiguration(InstrumentId id, int position,
+ QString key);
+
+ QString getOSCUrl(InstrumentId instrument, int position,
+ QString identifier);
+ QString getFriendlyName(InstrumentId instrument, int position,
+ QString identifier);
+ bool parseOSCPath(QString path, InstrumentId &instrument, int &position,
+ QString &method);
+
+ static void timerCallback(void *data);
+ static void guiExitedCallback(void *data);
+
+protected:
+ RosegardenGUIApp *m_app;
+ Studio *m_studio;
+
+ bool m_haveOSCThread;
+ void checkOSCThread();
+
+ lo_server_thread m_serverThread;
+ RingBuffer<OSCMessage *> m_oscBuffer;
+
+ typedef std::map<int, AudioPluginOSCGUI *> IntGUIMap;
+ typedef std::map<int, IntGUIMap> TargetGUIMap;
+ TargetGUIMap m_guis;
+
+ TimerCallbackAssistant *m_dispatchTimer;
+};
+
+
+
+}
+
+#endif
+
+#endif
diff --git a/src/gui/studio/BankEditorDialog.cpp b/src/gui/studio/BankEditorDialog.cpp
new file mode 100644
index 0000000..20aaf2d
--- /dev/null
+++ b/src/gui/studio/BankEditorDialog.cpp
@@ -0,0 +1,1713 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "BankEditorDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Studio.h"
+#include "commands/studio/ModifyDeviceCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/ExportDeviceDialog.h"
+#include "gui/dialogs/ImportDeviceDialog.h"
+#include "MidiBankListViewItem.h"
+#include "MidiDeviceListViewItem.h"
+#include "MidiKeyMapListViewItem.h"
+#include "MidiKeyMappingEditor.h"
+#include "MidiProgramsEditor.h"
+#include <kaction.h>
+#include <kcombobox.h>
+#include <kcommand.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kxmlguiclient.h>
+#include <qcheckbox.h>
+#include <qdialog.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qsplitter.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+BankEditorDialog::BankEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId defaultDevice):
+ KMainWindow(parent, "bankeditordialog"),
+ m_studio(&doc->getStudio()),
+ m_doc(doc),
+ m_copyBank(Device::NO_DEVICE, -1),
+ m_modified(false),
+ m_keepBankList(false),
+ m_deleteAllReally(false),
+ m_lastDevice(Device::NO_DEVICE),
+ m_updateDeviceList(false)
+{
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage MIDI Banks and Programs"));
+
+ QSplitter* splitter = new QSplitter(mainFrame);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_closeButton = new QPushButton(btnBox);
+ m_applyButton = new QPushButton(i18n("Apply"), btnBox);
+ m_resetButton = new QPushButton(i18n("Reset"), btnBox);
+
+ layout->addStretch(10);
+ layout->addWidget(m_applyButton);
+ layout->addWidget(m_resetButton);
+ layout->addSpacing(15);
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_applyButton, SIGNAL(clicked()),
+ this, SLOT(slotApply()));
+ connect(m_resetButton, SIGNAL(clicked()),
+ this, SLOT(slotReset()));
+
+ //
+ // Left-side list view
+ //
+ QVBox* leftPart = new QVBox(splitter);
+ m_listView = new KListView(leftPart);
+ m_listView->addColumn(i18n("MIDI Device"));
+ m_listView->addColumn(i18n("Type"));
+ m_listView->addColumn(i18n("MSB"));
+ m_listView->addColumn(i18n("LSB"));
+ m_listView->setRootIsDecorated(true);
+ m_listView->setShowSortIndicator(true);
+ m_listView->setItemsRenameable(true);
+ m_listView->restoreLayout(kapp->config(), BankEditorConfigGroup);
+
+ QFrame *bankBox = new QFrame(leftPart);
+ QGridLayout *gridLayout = new QGridLayout(bankBox, 4, 2, 6, 6);
+
+ m_addBank = new QPushButton(i18n("Add Bank"), bankBox);
+ m_addKeyMapping = new QPushButton(i18n("Add Key Mapping"), bankBox);
+ m_delete = new QPushButton(i18n("Delete"), bankBox);
+ m_deleteAll = new QPushButton(i18n("Delete All"), bankBox);
+ gridLayout->addWidget(m_addBank, 0, 0);
+ gridLayout->addWidget(m_addKeyMapping, 0, 1);
+ gridLayout->addWidget(m_delete, 1, 0);
+ gridLayout->addWidget(m_deleteAll, 1, 1);
+
+ // Tips
+ //
+ QToolTip::add
+ (m_addBank,
+ i18n("Add a Bank to the current device"));
+
+ QToolTip::add
+ (m_addKeyMapping,
+ i18n("Add a Percussion Key Mapping to the current device"));
+
+ QToolTip::add
+ (m_delete,
+ i18n("Delete the current Bank or Key Mapping"));
+
+ QToolTip::add
+ (m_deleteAll,
+ i18n("Delete all Banks and Key Mappings from the current Device"));
+
+ m_importBanks = new QPushButton(i18n("Import..."), bankBox);
+ m_exportBanks = new QPushButton(i18n("Export..."), bankBox);
+ gridLayout->addWidget(m_importBanks, 2, 0);
+ gridLayout->addWidget(m_exportBanks, 2, 1);
+
+ // Tips
+ //
+ QToolTip::add
+ (m_importBanks,
+ i18n("Import Bank and Program data from a Rosegarden file to the current Device"));
+ QToolTip::add
+ (m_exportBanks,
+ i18n("Export all Device and Bank information to a Rosegarden format interchange file"));
+
+ m_copyPrograms = new QPushButton(i18n("Copy"), bankBox);
+ m_pastePrograms = new QPushButton(i18n("Paste"), bankBox);
+ gridLayout->addWidget(m_copyPrograms, 3, 0);
+ gridLayout->addWidget(m_pastePrograms, 3, 1);
+
+ // Tips
+ //
+ QToolTip::add
+ (m_copyPrograms,
+ i18n("Copy all Program names from current Bank to clipboard"));
+
+ QToolTip::add
+ (m_pastePrograms,
+ i18n("Paste Program names from clipboard to current Bank"));
+
+ connect(m_listView, SIGNAL(currentChanged(QListViewItem*)),
+ this, SLOT(slotPopulateDevice(QListViewItem*)));
+
+ QFrame *vbox = new QFrame(splitter);
+ QVBoxLayout *vboxLayout = new QVBoxLayout(vbox, 8, 6);
+
+ m_programEditor = new MidiProgramsEditor(this, vbox);
+ vboxLayout->addWidget(m_programEditor);
+
+ m_keyMappingEditor = new MidiKeyMappingEditor(this, vbox);
+ vboxLayout->addWidget(m_keyMappingEditor);
+ m_keyMappingEditor->hide();
+
+ m_programEditor->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
+ m_keyMappingEditor->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
+
+ m_optionBox = new QVGroupBox(i18n("Options"), vbox);
+ vboxLayout->addWidget(m_optionBox);
+
+ QHBox *variationBox = new QHBox(m_optionBox);
+ m_variationToggle = new QCheckBox(i18n("Show Variation list based on "), variationBox);
+ m_variationCombo = new KComboBox(variationBox);
+ m_variationCombo->insertItem(i18n("LSB"));
+ m_variationCombo->insertItem(i18n("MSB"));
+
+ // device/bank modification
+ connect(m_listView, SIGNAL(itemRenamed (QListViewItem*, const QString&, int)),
+ this, SLOT(slotModifyDeviceOrBankName(QListViewItem*, const QString&, int)));
+
+ connect(m_addBank, SIGNAL(clicked()),
+ this, SLOT(slotAddBank()));
+
+ connect(m_addKeyMapping, SIGNAL(clicked()),
+ this, SLOT(slotAddKeyMapping()));
+
+ connect(m_delete, SIGNAL(clicked()),
+ this, SLOT(slotDelete()));
+
+ connect(m_deleteAll, SIGNAL(clicked()),
+ this, SLOT(slotDeleteAll()));
+
+ connect(m_importBanks, SIGNAL(clicked()),
+ this, SLOT(slotImport()));
+
+ connect(m_exportBanks, SIGNAL(clicked()),
+ this, SLOT(slotExport()));
+
+ connect(m_copyPrograms, SIGNAL(clicked()),
+ this, SLOT(slotEditCopy()));
+
+ connect(m_pastePrograms, SIGNAL(clicked()),
+ this, SLOT(slotEditPaste()));
+
+ connect(m_variationToggle, SIGNAL(clicked()),
+ this, SLOT(slotVariationToggled()));
+
+ connect(m_variationCombo, SIGNAL(activated(int)),
+ this, SLOT(slotVariationChanged(int)));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ // Initialise the dialog
+ //
+ initDialog();
+ setModified(false);
+
+ // Check for no Midi devices and disable everything
+ //
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+ bool haveMidiPlayDevice = false;
+ for (it = devices->begin(); it != devices->end(); ++it) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+ if (md && md->getDirection() == MidiDevice::Play) {
+ haveMidiPlayDevice = true;
+ break;
+ }
+ }
+ if (!haveMidiPlayDevice) {
+ leftPart->setDisabled(true);
+ m_programEditor->setDisabled(true);
+ m_keyMappingEditor->setDisabled(true);
+ m_optionBox->setDisabled(true);
+ }
+
+ if (defaultDevice != Device::NO_DEVICE) {
+ setCurrentDevice(defaultDevice);
+ }
+
+ setAutoSaveSettings(BankEditorConfigGroup, true);
+}
+
+BankEditorDialog::~BankEditorDialog()
+{
+ RG_DEBUG << "~BankEditorDialog()\n";
+
+ m_listView->saveLayout(kapp->config(), BankEditorConfigGroup);
+
+ if (m_doc) // see slotFileClose() for an explanation on why we need to test m_doc
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+BankEditorDialog::setupActions()
+{
+ KAction* close = KStdAction::close (this, SLOT(slotFileClose()), actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(clicked()),
+ this, SLOT(slotFileClose()));
+
+ KStdAction::copy (this, SLOT(slotEditCopy()), actionCollection());
+ KStdAction::paste (this, SLOT(slotEditPaste()), actionCollection());
+
+ // some adjustments
+
+
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ createGUI("bankeditor.rc");
+}
+
+void
+BankEditorDialog::initDialog()
+{
+ // Clear down
+ //
+ m_deviceNameMap.clear();
+ m_listView->clear();
+
+ // Fill list view
+ //
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+
+ for (it = devices->begin(); it != devices->end(); ++it) {
+ if ((*it)->getType() == Device::Midi) {
+ MidiDevice* midiDevice =
+ dynamic_cast<MidiDevice*>(*it);
+ if (!midiDevice)
+ continue;
+
+ // skip read-only devices
+ if (midiDevice->getDirection() == MidiDevice::Record)
+ continue;
+
+ m_deviceNameMap[midiDevice->getId()] = midiDevice->getName();
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ RG_DEBUG << "BankEditorDialog::initDialog - adding "
+ << itemName << endl;
+
+ QListViewItem* deviceItem = new MidiDeviceListViewItem
+ (midiDevice->getId(), m_listView, itemName);
+ deviceItem->setOpen(true);
+
+ populateDeviceItem(deviceItem, midiDevice);
+ }
+ }
+
+ // Select the first Device
+ //
+ populateDevice(m_listView->firstChild());
+ m_listView->setSelected(m_listView->firstChild(), true);
+
+}
+
+void
+BankEditorDialog::updateDialog()
+{
+ // Update list view
+ //
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+ bool deviceLabelUpdate = false;
+
+ for (it = devices->begin(); it != devices->end(); ++it) {
+
+ if ((*it)->getType() != Device::Midi)
+ continue;
+
+ MidiDevice* midiDevice =
+ dynamic_cast<MidiDevice*>(*it);
+ if (!midiDevice)
+ continue;
+
+ // skip read-only devices
+ if (midiDevice->getDirection() == MidiDevice::Record)
+ continue;
+
+ if (m_deviceNameMap.find(midiDevice->getId()) != m_deviceNameMap.end()) {
+ // Device already displayed but make sure the label is up to date
+ //
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ if (currentItem) {
+ MidiDeviceListViewItem* deviceItem =
+ getParentDeviceItem(currentItem);
+
+ if (deviceItem &&
+ deviceItem->getDeviceId() == midiDevice->getId()) {
+ if (deviceItem->text(0) != strtoqstr(midiDevice->getName())) {
+ deviceItem->setText(0,
+ strtoqstr(midiDevice->getName()));
+ m_deviceNameMap[midiDevice->getId()] =
+ midiDevice->getName();
+
+ /*
+ cout << "NEW TEXT FOR DEVICE " << midiDevice->getId()
+ << " IS " << midiDevice->getName() << endl;
+ cout << "LIST ITEM ID = "
+ << deviceItem->getDeviceId() << endl;
+ */
+
+ deviceLabelUpdate = true;
+ }
+
+ QListViewItem *child = deviceItem->firstChild();
+
+ while (child) {
+
+ MidiBankListViewItem *bankItem =
+ dynamic_cast<MidiBankListViewItem *>(child);
+
+ if (bankItem) {
+ bool percussion = bankItem->isPercussion();
+ int msb = bankItem->text(2).toInt();
+ int lsb = bankItem->text(3).toInt();
+ std::string bankName =
+ midiDevice->getBankName
+ (MidiBank(percussion, msb, lsb));
+ if (bankName != "" &&
+ bankItem->text(0) != strtoqstr(bankName)) {
+ bankItem->setText(0, strtoqstr(bankName));
+ }
+ }
+
+ child = child->nextSibling();
+ }
+ }
+ }
+
+ continue;
+ }
+
+ m_deviceNameMap[midiDevice->getId()] = midiDevice->getName();
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ RG_DEBUG << "BankEditorDialog::updateDialog - adding "
+ << itemName << endl;
+
+ QListViewItem* deviceItem = new MidiDeviceListViewItem
+ (midiDevice->getId(), m_listView, itemName);
+ deviceItem->setOpen(true);
+
+ populateDeviceItem(deviceItem, midiDevice);
+ }
+
+ // delete items whose corresponding devices are no longer present,
+ // and update the other ones
+ //
+ std::vector<MidiDeviceListViewItem*> itemsToDelete;
+
+ MidiDeviceListViewItem* sibling = dynamic_cast<MidiDeviceListViewItem*>
+ (m_listView->firstChild());
+
+ while (sibling) {
+
+ if (m_deviceNameMap.find(sibling->getDeviceId()) == m_deviceNameMap.end())
+ itemsToDelete.push_back(sibling);
+ else
+ updateDeviceItem(sibling);
+
+ sibling = dynamic_cast<MidiDeviceListViewItem*>(sibling->nextSibling());
+ }
+
+ for (unsigned int i = 0; i < itemsToDelete.size(); ++i)
+ delete itemsToDelete[i];
+
+ m_listView->sort();
+
+ if (deviceLabelUpdate)
+ emit deviceNamesChanged();
+}
+
+void
+BankEditorDialog::setCurrentDevice(DeviceId device)
+{
+ for (QListViewItem *item = m_listView->firstChild(); item;
+ item = item->nextSibling()) {
+ MidiDeviceListViewItem * deviceItem =
+ dynamic_cast<MidiDeviceListViewItem *>(item);
+ if (deviceItem && deviceItem->getDeviceId() == device) {
+ m_listView->setSelected(item, true);
+ break;
+ }
+ }
+}
+
+void
+BankEditorDialog::populateDeviceItem(QListViewItem* deviceItem, MidiDevice* midiDevice)
+{
+ clearItemChildren(deviceItem);
+
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ BankList banks = midiDevice->getBanks();
+ // add banks for this device
+ for (unsigned int i = 0; i < banks.size(); ++i) {
+ RG_DEBUG << "BankEditorDialog::populateDeviceItem - adding "
+ << itemName << " - " << strtoqstr(banks[i].getName())
+ << endl;
+ new MidiBankListViewItem(midiDevice->getId(), i, deviceItem,
+ strtoqstr(banks[i].getName()),
+ banks[i].isPercussion(),
+ banks[i].getMSB(), banks[i].getLSB());
+ }
+
+ const KeyMappingList &mappings = midiDevice->getKeyMappings();
+ for (unsigned int i = 0; i < mappings.size(); ++i) {
+ RG_DEBUG << "BankEditorDialog::populateDeviceItem - adding key mapping "
+ << itemName << " - " << strtoqstr(mappings[i].getName())
+ << endl;
+ new MidiKeyMapListViewItem(midiDevice->getId(), deviceItem,
+ strtoqstr(mappings[i].getName()));
+ }
+}
+
+void
+BankEditorDialog::updateDeviceItem(MidiDeviceListViewItem* deviceItem)
+{
+ MidiDevice* midiDevice = getMidiDevice(deviceItem->getDeviceId());
+ if (!midiDevice) {
+ RG_DEBUG << "BankEditorDialog::updateDeviceItem : WARNING no midi device for this item\n";
+ return ;
+ }
+
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ BankList banks = midiDevice->getBanks();
+ KeyMappingList keymaps = midiDevice->getKeyMappings();
+
+ // add missing banks for this device
+ //
+ for (unsigned int i = 0; i < banks.size(); ++i) {
+ if (deviceItemHasBank(deviceItem, i))
+ continue;
+
+ RG_DEBUG << "BankEditorDialog::updateDeviceItem - adding "
+ << itemName << " - " << strtoqstr(banks[i].getName())
+ << endl;
+ new MidiBankListViewItem(midiDevice->getId(), i, deviceItem,
+ strtoqstr(banks[i].getName()),
+ banks[i].isPercussion(),
+ banks[i].getMSB(), banks[i].getLSB());
+ }
+
+ for (unsigned int i = 0; i < keymaps.size(); ++i) {
+
+ QListViewItem *child = deviceItem->firstChild();
+ bool have = false;
+
+ while (child) {
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem*>(child);
+ if (keyItem) {
+ if (keyItem->getName() == strtoqstr(keymaps[i].getName())) {
+ have = true;
+ }
+ }
+ child = child->nextSibling();
+ }
+
+ if (have)
+ continue;
+
+ RG_DEBUG << "BankEditorDialog::updateDeviceItem - adding "
+ << itemName << " - " << strtoqstr(keymaps[i].getName())
+ << endl;
+ new MidiKeyMapListViewItem(midiDevice->getId(), deviceItem,
+ strtoqstr(keymaps[i].getName()));
+ }
+
+ // delete banks which are no longer present
+ //
+ std::vector<QListViewItem*> childrenToDelete;
+
+ QListViewItem* child = deviceItem->firstChild();
+
+ while (child) {
+
+ MidiBankListViewItem *bankItem =
+ dynamic_cast<MidiBankListViewItem *>(child);
+ if (bankItem) {
+ if (bankItem->getBank() >= int(banks.size()))
+ childrenToDelete.push_back(child);
+ else { // update the banks MSB/LSB which might have changed
+ bankItem->setPercussion(banks[bankItem->getBank()].isPercussion());
+ bankItem->setMSB(banks[bankItem->getBank()].getMSB());
+ bankItem->setLSB(banks[bankItem->getBank()].getLSB());
+ }
+ }
+
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem *>(child);
+ if (keyItem) {
+ if (!midiDevice->getKeyMappingByName(qstrtostr(keyItem->getName()))) {
+ childrenToDelete.push_back(child);
+ }
+ }
+
+ child = child->nextSibling();
+ }
+
+ for (unsigned int i = 0; i < childrenToDelete.size(); ++i)
+ delete childrenToDelete[i];
+}
+
+bool
+BankEditorDialog::deviceItemHasBank(MidiDeviceListViewItem* deviceItem, int bankNb)
+{
+ QListViewItem *child = deviceItem->firstChild();
+
+ while (child) {
+ MidiBankListViewItem *bankItem =
+ dynamic_cast<MidiBankListViewItem*>(child);
+ if (bankItem) {
+ if (bankItem->getBank() == bankNb)
+ return true;
+ }
+ child = child->nextSibling();
+ }
+
+ return false;
+}
+
+void
+BankEditorDialog::clearItemChildren(QListViewItem* item)
+{
+ QListViewItem* child = 0;
+
+ while ((child = item->firstChild()))
+ delete child;
+}
+
+MidiDevice*
+BankEditorDialog::getCurrentMidiDevice()
+{
+ return getMidiDevice(m_listView->currentItem());
+}
+
+void
+BankEditorDialog::checkModified()
+{
+ if (!m_modified)
+ return ;
+
+ setModified(false);
+
+ // // then ask if we want to apply the changes
+
+ // int reply = KMessageBox::questionYesNo(this,
+ // i18n("Apply pending changes?"));
+
+ ModifyDeviceCommand *command = 0;
+ MidiDevice *device = getMidiDevice(m_lastDevice);
+ if (!device) {
+ RG_DEBUG << "%%% WARNING : BankEditorDialog::checkModified() - NO MIDI DEVICE for device "
+ << m_lastDevice << endl;
+ return ;
+ }
+
+ if (m_bankList.size() == 0 && m_programList.size() == 0) {
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail()); // rename
+
+ command->clearBankAndProgramList();
+
+ } else {
+
+ MidiDevice::VariationType variation =
+ MidiDevice::NoVariations;
+ if (m_variationToggle->isChecked()) {
+ if (m_variationCombo->currentItem() == 0) {
+ variation = MidiDevice::VariationFromLSB;
+ } else {
+ variation = MidiDevice::VariationFromMSB;
+ }
+ }
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ command->setVariation(variation);
+ command->setBankList(m_bankList);
+ command->setProgramList(m_programList);
+ }
+
+ addCommandToHistory(command);
+
+ setModified(false);
+}
+
+void
+BankEditorDialog::slotPopulateDevice(QListViewItem* item)
+{
+ RG_DEBUG << "BankEditorDialog::slotPopulateDevice" << endl;
+
+ if (!item)
+ return ;
+
+ checkModified();
+
+ populateDevice(item);
+}
+
+void
+BankEditorDialog::populateDevice(QListViewItem* item)
+{
+ RG_DEBUG << "BankEditorDialog::populateDevice\n";
+
+ if (!item)
+ return ;
+
+ MidiKeyMapListViewItem *keyItem = dynamic_cast<MidiKeyMapListViewItem *>(item);
+
+ if (keyItem) {
+
+ stateChanged("on_key_item");
+ stateChanged("on_bank_item", KXMLGUIClient::StateReverse);
+
+ m_delete->setEnabled(true);
+
+ MidiDevice *device = getMidiDevice(keyItem->getDeviceId());
+ if (!device)
+ return ;
+
+ setProgramList(device);
+
+ m_keyMappingEditor->populate(item);
+
+ m_programEditor->hide();
+ m_keyMappingEditor->show();
+
+ m_lastDevice = keyItem->getDeviceId();
+
+ return ;
+ }
+
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(item);
+
+ if (bankItem) {
+
+ stateChanged("on_bank_item");
+ stateChanged("on_key_item", KXMLGUIClient::StateReverse);
+
+ m_delete->setEnabled(true);
+ m_copyPrograms->setEnabled(true);
+
+ if (m_copyBank.first != Device::NO_DEVICE)
+ m_pastePrograms->setEnabled(true);
+
+ MidiDevice *device = getMidiDevice(bankItem->getDeviceId());
+ if (!device)
+ return ;
+
+ if (!m_keepBankList || m_bankList.size() == 0)
+ m_bankList = device->getBanks();
+ else
+ m_keepBankList = false;
+
+ setProgramList(device);
+
+ m_variationToggle->setChecked(device->getVariationType() !=
+ MidiDevice::NoVariations);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+ m_variationCombo->setCurrentItem
+ (device->getVariationType() ==
+ MidiDevice::VariationFromLSB ? 0 : 1);
+
+ m_lastBank = m_bankList[bankItem->getBank()];
+
+ m_programEditor->populate(item);
+
+ m_keyMappingEditor->hide();
+ m_programEditor->show();
+
+ m_lastDevice = bankItem->getDeviceId();
+
+ return ;
+ }
+
+ // Device, not bank or key mapping
+ // Ensure we fill these lists for the new device
+ //
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(item);
+
+ m_lastDevice = deviceItem->getDeviceId();
+
+ MidiDevice *device = getMidiDevice(deviceItem);
+ if (!device) {
+ RG_DEBUG << "BankEditorDialog::populateDevice - no device for this item\n";
+ return ;
+ }
+
+ m_bankList = device->getBanks();
+ setProgramList(device);
+
+ RG_DEBUG << "BankEditorDialog::populateDevice : not a bank item - disabling" << endl;
+ m_delete->setEnabled(false);
+ m_copyPrograms->setEnabled(false);
+ m_pastePrograms->setEnabled(false);
+
+ m_variationToggle->setChecked(device->getVariationType() !=
+ MidiDevice::NoVariations);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+ m_variationCombo->setCurrentItem
+ (device->getVariationType() ==
+ MidiDevice::VariationFromLSB ? 0 : 1);
+
+ stateChanged("on_bank_item", KXMLGUIClient::StateReverse);
+ stateChanged("on_key_item", KXMLGUIClient::StateReverse);
+ m_programEditor->clearAll();
+ m_keyMappingEditor->clearAll();
+}
+
+void
+BankEditorDialog::slotApply()
+{
+ RG_DEBUG << "BankEditorDialog::slotApply()\n";
+
+ ModifyDeviceCommand *command = 0;
+
+ MidiDevice *device = getMidiDevice(m_lastDevice);
+
+ // Make sure that we don't delete all the banks and programs
+ // if we've not populated them here yet.
+ //
+ if (m_bankList.size() == 0 && m_programList.size() == 0 &&
+ m_deleteAllReally == false) {
+ RG_DEBUG << "BankEditorDialog::slotApply() : m_bankList size = 0\n";
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ command->clearBankAndProgramList();
+ } else {
+ MidiDevice::VariationType variation =
+ MidiDevice::NoVariations;
+ if (m_variationToggle->isChecked()) {
+ if (m_variationCombo->currentItem() == 0) {
+ variation = MidiDevice::VariationFromLSB;
+ } else {
+ variation = MidiDevice::VariationFromMSB;
+ }
+ }
+
+ RG_DEBUG << "BankEditorDialog::slotApply() : m_bankList size = "
+ << m_bankList.size() << endl;
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ MidiKeyMapListViewItem *keyItem = dynamic_cast<MidiKeyMapListViewItem*>
+ (m_listView->currentItem());
+ if (keyItem) {
+ KeyMappingList kml(device->getKeyMappings());
+ for (int i = 0; i < kml.size(); ++i) {
+ if (kml[i].getName() == qstrtostr(keyItem->getName())) {
+ kml[i] = m_keyMappingEditor->getMapping();
+ break;
+ }
+ }
+ command->setKeyMappingList(kml);
+ }
+
+ command->setVariation(variation);
+ command->setBankList(m_bankList);
+ command->setProgramList(m_programList);
+ }
+
+ addCommandToHistory(command);
+
+ // Our freaky fudge to update instrument/device names externally
+ //
+ if (m_updateDeviceList) {
+ emit deviceNamesChanged();
+ m_updateDeviceList = false;
+ }
+
+ setModified(false);
+}
+
+void
+BankEditorDialog::slotReset()
+{
+ resetProgramList();
+
+ m_programEditor->reset();
+ m_programEditor->populate(m_listView->currentItem());
+ m_keyMappingEditor->reset();
+ m_keyMappingEditor->populate(m_listView->currentItem());
+
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem
+ (m_listView->currentItem());
+
+ if (deviceItem) {
+ MidiDevice *device = getMidiDevice(deviceItem);
+ m_variationToggle->setChecked(device->getVariationType() !=
+ MidiDevice::NoVariations);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+ m_variationCombo->setCurrentItem
+ (device->getVariationType() ==
+ MidiDevice::VariationFromLSB ? 0 : 1);
+ }
+
+ updateDialog();
+
+ setModified(false);
+}
+
+void
+BankEditorDialog::resetProgramList()
+{
+ m_programList = m_oldProgramList;
+}
+
+void
+BankEditorDialog::setProgramList(MidiDevice *device)
+{
+ m_programList = device->getPrograms();
+ m_oldProgramList = m_programList;
+}
+
+void
+BankEditorDialog::slotUpdate()
+{
+ updateDialog();
+}
+
+MidiDeviceListViewItem*
+BankEditorDialog::getParentDeviceItem(QListViewItem* item)
+{
+ if (!item)
+ return 0;
+
+ if (dynamic_cast<MidiBankListViewItem*>(item))
+ // go up to the parent device item
+ item = item->parent();
+
+ if (dynamic_cast<MidiKeyMapListViewItem*>(item))
+ // go up to the parent device item
+ item = item->parent();
+
+ if (!item) {
+ RG_DEBUG << "BankEditorDialog::getParentDeviceItem : missing parent device item for bank item - this SHOULD NOT HAPPEN" << endl;
+ return 0;
+ }
+
+ return dynamic_cast<MidiDeviceListViewItem*>(item);
+}
+
+void
+BankEditorDialog::slotAddBank()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(currentItem);
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device) {
+ // If the bank and program lists are empty then try to
+ // populate them.
+ //
+ if (m_bankList.size() == 0 && m_programList.size() == 0) {
+ m_bankList = device->getBanks();
+ setProgramList(device);
+ }
+
+ std::pair<int, int> bank = getFirstFreeBank(m_listView->currentItem());
+
+ MidiBank newBank(false,
+ bank.first, bank.second,
+ qstrtostr(i18n("<new bank>")));
+ m_bankList.push_back(newBank);
+
+ QListViewItem* newBankItem =
+ new MidiBankListViewItem(deviceItem->getDeviceId(),
+ m_bankList.size() - 1,
+ deviceItem,
+ strtoqstr(newBank.getName()),
+ newBank.isPercussion(),
+ newBank.getMSB(), newBank.getLSB());
+ keepBankListForNextPopulate();
+ m_listView->setCurrentItem(newBankItem);
+
+ slotApply();
+ selectDeviceItem(device);
+ }
+}
+
+void
+BankEditorDialog::slotAddKeyMapping()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(currentItem);
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device) {
+
+ QString name = "";
+ int n = 0;
+ while (name == "" || device->getKeyMappingByName(qstrtostr(name)) != 0) {
+ ++n;
+ if (n == 1)
+ name = i18n("<new mapping>");
+ else
+ name = i18n("<new mapping %1>").arg(n);
+ }
+
+ MidiKeyMapping newKeyMapping(qstrtostr(name));
+
+ ModifyDeviceCommand *command = new ModifyDeviceCommand
+ (m_studio,
+ device->getId(),
+ device->getName(),
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ KeyMappingList kml;
+ kml.push_back(newKeyMapping);
+ command->setKeyMappingList(kml);
+ command->setOverwrite(false);
+ command->setRename(false);
+
+ addCommandToHistory(command);
+
+ updateDialog();
+ selectDeviceItem(device);
+ }
+}
+
+void
+BankEditorDialog::slotDelete()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(currentItem);
+
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device && bankItem) {
+ int currentBank = bankItem->getBank();
+
+ int reply =
+ KMessageBox::warningYesNo(this, i18n("Really delete this bank?"));
+
+ if (reply == KMessageBox::Yes) {
+ MidiBank bank = m_bankList[currentBank];
+
+ // Copy across all programs that aren't in the doomed bank
+ //
+ ProgramList::iterator it;
+ ProgramList tempList;
+ for (it = m_programList.begin(); it != m_programList.end(); it++)
+ if (!(it->getBank() == bank))
+ tempList.push_back(*it);
+
+ // Erase the bank and repopulate
+ //
+ BankList::iterator er =
+ m_bankList.begin();
+ er += currentBank;
+ m_bankList.erase(er);
+ m_programList = tempList;
+ keepBankListForNextPopulate();
+
+ // the listview automatically selects a new current item
+ m_listView->blockSignals(true);
+ delete currentItem;
+ m_listView->blockSignals(false);
+
+ // Don't allow pasting from this defunct device
+ //
+ if (m_copyBank.first == bankItem->getDeviceId() &&
+ m_copyBank.second == bankItem->getBank()) {
+ m_pastePrograms->setEnabled(false);
+ m_copyBank = std::pair<DeviceId, int>
+ (Device::NO_DEVICE, -1);
+ }
+
+ slotApply();
+ selectDeviceItem(device);
+ }
+
+ return ;
+ }
+
+ MidiKeyMapListViewItem* keyItem = dynamic_cast<MidiKeyMapListViewItem*>(currentItem);
+
+ if (keyItem && device) {
+
+ int reply =
+ KMessageBox::warningYesNo(this, i18n("Really delete this key mapping?"));
+
+ if (reply == KMessageBox::Yes) {
+
+ std::string keyMappingName = qstrtostr(keyItem->getName());
+
+ ModifyDeviceCommand *command = new ModifyDeviceCommand
+ (m_studio,
+ device->getId(),
+ device->getName(),
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ KeyMappingList kml = device->getKeyMappings();
+
+ for (KeyMappingList::iterator i = kml.begin();
+ i != kml.end(); ++i) {
+ if (i->getName() == keyMappingName) {
+ RG_DEBUG << "erasing " << keyMappingName << endl;
+ kml.erase(i);
+ break;
+ }
+ }
+
+ RG_DEBUG << " setting " << kml.size() << " key mappings to device " << endl;
+
+ command->setKeyMappingList(kml);
+ command->setOverwrite(true);
+
+ addCommandToHistory(command);
+
+ RG_DEBUG << " device has " << device->getKeyMappings().size() << " key mappings now " << endl;
+
+ updateDialog();
+ }
+
+ return ;
+ }
+}
+
+void
+BankEditorDialog::slotDeleteAll()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(currentItem);
+ MidiDevice *device = getMidiDevice(deviceItem);
+
+ QString question = i18n("Really delete all banks for ") +
+ strtoqstr(device->getName()) + QString(" ?");
+
+ int reply = KMessageBox::warningYesNo(this, question);
+
+ if (reply == KMessageBox::Yes) {
+
+ // erase all bank items
+ QListViewItem* child = 0;
+ while ((child = deviceItem->firstChild()))
+ delete child;
+
+ m_bankList.clear();
+ m_programList.clear();
+
+ // Don't allow pasting from this defunct device
+ //
+ if (m_copyBank.first == deviceItem->getDeviceId()) {
+ m_pastePrograms->setEnabled(false);
+ m_copyBank = std::pair<DeviceId, int>
+ (Device::NO_DEVICE, -1);
+ }
+
+ // Urgh, we have this horrible flag that we're using to frig this.
+ // (we might not need this anymore but I'm too scared to remove it
+ // now).
+ //
+ m_deleteAllReally = true;
+ slotApply();
+ m_deleteAllReally = false;
+
+ selectDeviceItem(device);
+
+ }
+}
+
+MidiDevice*
+BankEditorDialog::getMidiDevice(DeviceId id)
+{
+ Device *device = m_studio->getDevice(id);
+ MidiDevice *midiDevice =
+ dynamic_cast<MidiDevice *>(device);
+
+ /*
+ if (device) {
+ if (!midiDevice) {
+ std::cerr << "ERROR: BankEditorDialog::getMidiDevice: device "
+ << id << " is not a MIDI device" << std::endl;
+ }
+ } else {
+ std::cerr
+ << "ERROR: BankEditorDialog::getMidiDevice: no such device as "
+ << id << std::endl;
+ }
+ */
+
+ return midiDevice;
+}
+
+MidiDevice*
+BankEditorDialog::getMidiDevice(QListViewItem* item)
+{
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>(item);
+ if (!deviceItem)
+ return 0;
+
+ return getMidiDevice(deviceItem->getDeviceId());
+}
+
+std::pair<int, int>
+BankEditorDialog::getFirstFreeBank(QListViewItem* item)
+{
+ //!!! percussion? this is actually only called in the expectation
+ // that percussion==false at the moment
+
+ for (int msb = 0; msb < 128; ++msb) {
+ for (int lsb = 0; lsb < 128; ++lsb) {
+ BankList::iterator i = m_bankList.begin();
+ for ( ; i != m_bankList.end(); ++i) {
+ if (i->getLSB() == lsb && i->getMSB() == msb) {
+ break;
+ }
+ }
+ if (i == m_bankList.end())
+ return std::pair<int, int>(msb, lsb);
+ }
+ }
+
+ return std::pair<int, int>(0, 0);
+}
+
+void
+BankEditorDialog::slotModifyDeviceOrBankName(QListViewItem* item, const QString &label, int)
+{
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName" << endl;
+
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>(item);
+ MidiBankListViewItem* bankItem =
+ dynamic_cast<MidiBankListViewItem*>(item);
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem*>(item);
+
+ if (bankItem) {
+
+ // renaming a bank item
+
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
+ << "modify bank name to " << label << endl;
+
+ if (m_bankList[bankItem->getBank()].getName() != qstrtostr(label)) {
+ m_bankList[bankItem->getBank()].setName(qstrtostr(label));
+ setModified(true);
+ }
+
+ } else if (keyItem) {
+
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
+ << "modify key mapping name to " << label << endl;
+
+ QString oldName = keyItem->getName();
+
+ QListViewItem* currentItem = m_listView->currentItem();
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device) {
+
+ ModifyDeviceCommand *command = new ModifyDeviceCommand
+ (m_studio,
+ device->getId(),
+ device->getName(),
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ KeyMappingList kml = device->getKeyMappings();
+
+ for (KeyMappingList::iterator i = kml.begin();
+ i != kml.end(); ++i) {
+ if (i->getName() == qstrtostr(oldName)) {
+ i->setName(qstrtostr(label));
+ break;
+ }
+ }
+
+ command->setKeyMappingList(kml);
+ command->setOverwrite(true);
+
+ addCommandToHistory(command);
+
+ updateDialog();
+ }
+
+ } else if (deviceItem) { // must be last, as the others are subclasses
+
+ // renaming a device item
+
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
+ << "modify device name to " << label << endl;
+
+ if (m_deviceNameMap[deviceItem->getDeviceId()] != qstrtostr(label)) {
+ m_deviceNameMap[deviceItem->getDeviceId()] = qstrtostr(label);
+ setModified(true);
+
+ m_updateDeviceList = true;
+ }
+
+ }
+
+}
+
+void
+BankEditorDialog::selectDeviceItem(MidiDevice *device)
+{
+ QListViewItem *child = m_listView->firstChild();
+ MidiDeviceListViewItem *midiDeviceItem;
+ MidiDevice *midiDevice;
+
+ do {
+ midiDeviceItem = dynamic_cast<MidiDeviceListViewItem*>(child);
+
+ if (midiDeviceItem) {
+ midiDevice = getMidiDevice(midiDeviceItem);
+
+ if (midiDevice == device) {
+ m_listView->setSelected(child, true);
+ return ;
+ }
+ }
+
+ } while ((child = child->nextSibling()));
+}
+
+void
+BankEditorDialog::selectDeviceBankItem(DeviceId deviceId,
+ int bank)
+{
+ QListViewItem *deviceChild = m_listView->firstChild();
+ QListViewItem *bankChild;
+ int deviceCount = 0, bankCount = 0;
+
+ do {
+ bankChild = deviceChild->firstChild();
+
+ MidiDeviceListViewItem *midiDeviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>(deviceChild);
+
+ if (midiDeviceItem && bankChild) {
+ do {
+ if (deviceId == midiDeviceItem->getDeviceId() &
+ bank == bankCount) {
+ m_listView->setSelected(bankChild, true);
+ return ;
+ }
+ bankCount++;
+
+ } while ((bankChild = bankChild->nextSibling()));
+ }
+
+ deviceCount++;
+ bankCount = 0;
+ } while ((deviceChild = deviceChild->nextSibling()));
+}
+
+void
+BankEditorDialog::slotVariationToggled()
+{
+ setModified(true);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+}
+
+void
+BankEditorDialog::slotVariationChanged(int)
+{
+ setModified(true);
+}
+
+void
+BankEditorDialog::setModified(bool modified)
+{
+ RG_DEBUG << "BankEditorDialog::setModified("
+ << modified << ")" << endl;
+
+ if (modified) {
+
+ m_applyButton->setEnabled(true);
+ m_resetButton->setEnabled(true);
+ m_closeButton->setEnabled(false);
+ m_listView->setEnabled(false);
+
+ } else {
+
+ m_applyButton->setEnabled(false);
+ m_resetButton->setEnabled(false);
+ m_closeButton->setEnabled(true);
+ m_listView->setEnabled(true);
+
+ }
+
+ m_modified = modified;
+}
+
+void
+BankEditorDialog::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+BankEditorDialog::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+BankEditorDialog::slotImport()
+{
+ QString deviceDir = KGlobal::dirs()->findResource("appdata", "library/");
+ QDir dir(deviceDir);
+ if (!dir.exists()) {
+ deviceDir = ":ROSEGARDENDEVICE";
+ } else {
+ deviceDir = "file://" + deviceDir;
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (deviceDir,
+ "audio/x-rosegarden-device audio/x-rosegarden audio/x-soundfont",
+ this, i18n("Import Banks from Device in File"));
+
+ if (url.isEmpty())
+ return ;
+
+ ImportDeviceDialog *dialog = new ImportDeviceDialog(this, url);
+ if (dialog->doImport() && dialog->exec() == QDialog::Accepted) {
+
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>
+ (m_listView->selectedItem());
+
+ if (!deviceItem) {
+ KMessageBox::error(this, "Some internal error: cannot locate selected device");
+ return ;
+ }
+
+ ModifyDeviceCommand *command = 0;
+
+ BankList banks(dialog->getBanks());
+ ProgramList programs(dialog->getPrograms());
+ ControlList controls(dialog->getControllers());
+ KeyMappingList keyMappings(dialog->getKeyMappings());
+ MidiDevice::VariationType variation(dialog->getVariationType());
+ std::string librarianName(dialog->getLibrarianName());
+ std::string librarianEmail(dialog->getLibrarianEmail());
+
+ // don't record the librarian when
+ // merging banks -- it's misleading.
+ // (also don't use variation type)
+ if (!dialog->shouldOverwriteBanks()) {
+ librarianName = "";
+ librarianEmail = "";
+ }
+
+ command = new ModifyDeviceCommand(m_studio,
+ deviceItem->getDeviceId(),
+ dialog->getDeviceName(),
+ librarianName,
+ librarianEmail);
+
+ if (dialog->shouldOverwriteBanks()) {
+ command->setVariation(variation);
+ }
+ if (dialog->shouldImportBanks()) {
+ command->setBankList(banks);
+ command->setProgramList(programs);
+ }
+ if (dialog->shouldImportControllers()) {
+ command->setControlList(controls);
+ }
+ if (dialog->shouldImportKeyMappings()) {
+ command->setKeyMappingList(keyMappings);
+ }
+ command->setOverwrite(dialog->shouldOverwriteBanks());
+ command->setRename(dialog->shouldRename());
+
+ addCommandToHistory(command);
+
+ // No need to redraw the dialog, this is done by
+ // slotUpdate, signalled by the MultiViewCommandHistory
+ MidiDevice *device = getMidiDevice(deviceItem);
+ if (device)
+ selectDeviceItem(device);
+ }
+
+ delete dialog;
+ updateDialog();
+}
+
+void
+BankEditorDialog::slotEditCopy()
+{
+ MidiBankListViewItem* bankItem
+ = dynamic_cast<MidiBankListViewItem*>(m_listView->currentItem());
+
+ if (bankItem) {
+ m_copyBank = std::pair<DeviceId, int>(bankItem->getDeviceId(),
+ bankItem->getBank());
+ m_pastePrograms->setEnabled(true);
+ }
+}
+
+void
+BankEditorDialog::slotEditPaste()
+{
+ MidiBankListViewItem* bankItem
+ = dynamic_cast<MidiBankListViewItem*>(m_listView->currentItem());
+
+ if (bankItem) {
+ // Get the full program and bank list for the source device
+ //
+ MidiDevice *device = getMidiDevice(m_copyBank.first);
+ std::vector<MidiBank> tempBank = device->getBanks();
+
+ ProgramList::iterator it;
+ std::vector<MidiProgram> tempProg;
+
+ // Remove programs that will be overwritten
+ //
+ for (it = m_programList.begin(); it != m_programList.end(); it++) {
+ if (!(it->getBank() == m_lastBank))
+ tempProg.push_back(*it);
+ }
+ m_programList = tempProg;
+
+ // Now get source list and msb/lsb
+ //
+ tempProg = device->getPrograms();
+ MidiBank sourceBank = tempBank[m_copyBank.second];
+
+ // Add the new programs
+ //
+ for (it = tempProg.begin(); it != tempProg.end(); it++) {
+ if (it->getBank() == sourceBank) {
+ // Insert with new MSB and LSB
+ //
+ MidiProgram copyProgram(m_lastBank,
+ it->getProgram(),
+ it->getName());
+
+ m_programList.push_back(copyProgram);
+ }
+ }
+
+ // Save these for post-apply
+ //
+ DeviceId devPos = bankItem->getDeviceId();
+ int bankPos = bankItem->getBank();
+
+ slotApply();
+
+ // Select same bank
+ //
+ selectDeviceBankItem(devPos, bankPos);
+ }
+}
+
+void
+BankEditorDialog::slotExport()
+{
+ QString extension = "rgd";
+
+ QString name =
+ KFileDialog::getSaveFileName(":ROSEGARDEN",
+ (extension.isEmpty() ? QString("*") : ("*." + extension)),
+ this,
+ i18n("Export Device as..."));
+
+ // Check for the existence of the name
+ if (name.isEmpty())
+ return ;
+
+ // Append extension if we don't have one
+ //
+ if (!extension.isEmpty()) {
+ if (!name.endsWith("." + extension)) {
+ name += "." + extension;
+ }
+ }
+
+ QFileInfo info(name);
+
+ if (info.isDir()) {
+ KMessageBox::sorry(this, i18n("You have specified a directory"));
+ return ;
+ }
+
+ if (info.exists()) {
+ int overwrite = KMessageBox::questionYesNo
+ (this, i18n("The specified file exists. Overwrite?"));
+
+ if (overwrite != KMessageBox::Yes)
+ return ;
+
+ }
+
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>
+ (m_listView->selectedItem());
+
+ std::vector<DeviceId> devices;
+ MidiDevice *md = getMidiDevice(deviceItem);
+
+ if (md) {
+ ExportDeviceDialog *ed = new ExportDeviceDialog
+ (this, strtoqstr(md->getName()));
+ if (ed->exec() != QDialog::Accepted)
+ return ;
+ if (ed->getExportType() == ExportDeviceDialog::ExportOne) {
+ devices.push_back(md->getId());
+ }
+ }
+
+ m_doc->exportStudio(name, devices);
+}
+
+void
+BankEditorDialog::slotFileClose()
+{
+ RG_DEBUG << "BankEditorDialog::slotFileClose()\n";
+
+ // We need to do this because we might be here due to a
+ // documentAboutToChange signal, in which case the document won't
+ // be valid by the time we reach the dtor, since it will be
+ // triggered when the closeEvent is actually processed.
+ //
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+ close();
+}
+
+void
+BankEditorDialog::closeEvent(QCloseEvent *e)
+{
+ if (m_modified) {
+
+ int res = KMessageBox::warningYesNoCancel(this,
+ i18n("There are unsaved changes.\n"
+ "Do you want to apply the changes before exiting "
+ "the Bank Editor or discard the changes ?"),
+ i18n("Unsaved Changes"),
+ i18n("&Apply"),
+ i18n("&Discard"));
+ if (res == KMessageBox::Yes) {
+
+ slotApply();
+
+ } else if (res == KMessageBox::Cancel)
+ return ;
+ }
+
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+}
+#include "BankEditorDialog.moc"
diff --git a/src/gui/studio/BankEditorDialog.h b/src/gui/studio/BankEditorDialog.h
new file mode 100644
index 0000000..0e49430
--- /dev/null
+++ b/src/gui/studio/BankEditorDialog.h
@@ -0,0 +1,211 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_BANKEDITORDIALOG_H_
+#define _RG_BANKEDITORDIALOG_H_
+
+#include "base/Device.h"
+#include "base/MidiProgram.h"
+#include <map>
+#include <string>
+#include <kmainwindow.h>
+#include <utility>
+
+
+class QWidget;
+class QString;
+class QPushButton;
+class QListViewItem;
+class QGroupBox;
+class QCloseEvent;
+class QCheckBox;
+class KListView;
+class KCommand;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+class MidiProgramsEditor;
+class MidiKeyMappingEditor;
+class MidiDeviceListViewItem;
+class MidiDevice;
+
+
+class BankEditorDialog : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ BankEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId defaultDevice =
+ Device::NO_DEVICE);
+
+ ~BankEditorDialog();
+
+ // Initialise the devices/banks and programs - the whole lot
+ //
+ void initDialog();
+
+ std::pair<int, int> getFirstFreeBank(QListViewItem*);
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setCurrentDevice(DeviceId device);
+
+ // Get a MidiDevice from an index number
+ //
+ MidiDevice* getMidiDevice(DeviceId);
+ MidiDevice* getMidiDevice(QListViewItem*);
+ MidiDevice* getCurrentMidiDevice();
+ BankList& getBankList() { return m_bankList; }
+ ProgramList&getProgramList() { return m_programList; }
+
+ Studio *getStudio() { return m_studio; }
+
+ void setModified(bool value);
+
+ void checkModified();
+
+ // Set the listview to select a certain device - used after adding
+ // or deleting banks.
+ //
+ void selectDeviceItem(MidiDevice *device);
+
+ // Select a device/bank combination
+ //
+ void selectDeviceBankItem(DeviceId device, int bank);
+
+public slots:
+ void slotPopulateDevice(QListViewItem*);
+
+ void slotApply();
+ void slotReset();
+
+ void slotUpdate();
+
+ void slotAddBank();
+ void slotAddKeyMapping();
+ void slotDelete();
+ void slotDeleteAll();
+
+ void slotImport();
+ void slotExport();
+
+ void slotModifyDeviceOrBankName(QListViewItem*, const QString&,int);
+
+ void slotFileClose();
+
+ void slotEditCopy();
+ void slotEditPaste();
+
+ void slotVariationToggled();
+ void slotVariationChanged(int);
+
+signals:
+ void closing();
+ void deviceNamesChanged();
+
+protected:
+ virtual void closeEvent(QCloseEvent*);
+
+ void resetProgramList();
+ void setProgramList(MidiDevice *device);
+
+ void updateDialog();
+
+ void populateDeviceItem(QListViewItem* deviceItem,
+ MidiDevice* midiDevice);
+
+ void updateDeviceItem(MidiDeviceListViewItem* deviceItem);
+
+ bool deviceItemHasBank(MidiDeviceListViewItem* deviceItem, int bankNb);
+
+ void clearItemChildren(QListViewItem* deviceItem);
+
+ MidiDeviceListViewItem* getParentDeviceItem(QListViewItem*);
+ void keepBankListForNextPopulate() { m_keepBankList = true; }
+
+ void populateDevice(QListViewItem*);
+
+ void setupActions();
+
+ //--------------- Data members ---------------------------------
+ Studio *m_studio;
+ RosegardenGUIDoc *m_doc;
+
+ MidiProgramsEditor *m_programEditor;
+ MidiKeyMappingEditor *m_keyMappingEditor;
+ KListView *m_listView;
+
+ QGroupBox *m_optionBox;
+ QCheckBox *m_variationToggle;
+ KComboBox *m_variationCombo;
+
+ QPushButton *m_closeButton;
+ QPushButton *m_resetButton;
+ QPushButton *m_applyButton;
+
+ QPushButton *m_addBank;
+ QPushButton *m_addKeyMapping;
+ QPushButton *m_delete;
+ QPushButton *m_deleteAll;
+
+ QPushButton *m_importBanks;
+ QPushButton *m_exportBanks;
+
+ QPushButton *m_copyPrograms;
+ QPushButton *m_pastePrograms;
+ std::pair<DeviceId, int> m_copyBank;
+
+ std::map<DeviceId, std::string> m_deviceNameMap;
+ BankList m_bankList;
+ ProgramList m_programList;
+ ProgramList m_oldProgramList;
+
+ bool m_modified;
+ bool m_keepBankList;
+ bool m_deleteAllReally;
+
+ DeviceId m_lastDevice;
+ MidiBank m_lastBank;
+
+ bool m_updateDeviceList;
+};
+
+// ----------------------- RemapInstrumentDialog ------------------------
+//
+//
+
+
+}
+
+#endif
diff --git a/src/gui/studio/ChangeRecordDeviceCommand.cpp b/src/gui/studio/ChangeRecordDeviceCommand.cpp
new file mode 100644
index 0000000..a5a3947
--- /dev/null
+++ b/src/gui/studio/ChangeRecordDeviceCommand.cpp
@@ -0,0 +1,66 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "ChangeRecordDeviceCommand.h"
+#include "StudioControl.h"
+#include "document/ConfigGroups.h"
+#include "sound/MappedEvent.h"
+#include <kapplication.h>
+#include <kconfig.h>
+
+namespace Rosegarden
+{
+
+void
+ChangeRecordDeviceCommand::swap()
+ {
+
+ KConfig *config = kapp->config();
+ config->setGroup(Rosegarden::SequencerOptionsConfigGroup);
+ QStringList devList = config->readListEntry("midirecorddevice");
+ QString sdevice = QString::number(m_deviceId);
+ if (m_action)
+ {
+ if(!devList.contains(sdevice))
+ devList.append(sdevice);
+ }
+ else
+ {
+ if(devList.contains(sdevice))
+ devList.remove(sdevice);
+ }
+ config->writeEntry("midirecorddevice", devList);
+
+ // send the selected device to the sequencer
+ Rosegarden::MappedEvent mEdevice
+ (Rosegarden::MidiInstrumentBase,
+ Rosegarden::MappedEvent::SystemRecordDevice,
+ Rosegarden::MidiByte(m_deviceId),
+ Rosegarden::MidiByte(m_action));
+ Rosegarden::StudioControl::sendMappedEvent(mEdevice);
+
+ m_action = !m_action;
+}
+
+}
diff --git a/src/gui/studio/ChangeRecordDeviceCommand.h b/src/gui/studio/ChangeRecordDeviceCommand.h
new file mode 100644
index 0000000..70819db
--- /dev/null
+++ b/src/gui/studio/ChangeRecordDeviceCommand.h
@@ -0,0 +1,54 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_CHANGERECORDDEVICECOMMAND_H_
+#define _RG_CHANGERECORDDEVICECOMMAND_H_
+
+#include "base/Studio.h"
+#include <klocale.h>
+#include <kcommand.h>
+
+namespace Rosegarden
+{
+
+class ChangeRecordDeviceCommand : public KNamedCommand
+{
+public:
+ ChangeRecordDeviceCommand(Rosegarden::DeviceId deviceId, bool action) :
+ KNamedCommand(i18n("Change Record Device")),
+ m_deviceId(deviceId), m_action(action) { }
+
+ virtual void execute() { swap(); }
+ virtual void unexecute() { swap(); }
+
+private:
+ Rosegarden::DeviceId m_deviceId;
+ bool m_action;
+ void swap();
+
+};
+
+}
+
+#endif /*CHANGERECORDDEVICECOMMAND_H_*/
diff --git a/src/gui/studio/DeviceEditorDialog.cpp b/src/gui/studio/DeviceEditorDialog.cpp
new file mode 100644
index 0000000..29c9dd4
--- /dev/null
+++ b/src/gui/studio/DeviceEditorDialog.cpp
@@ -0,0 +1,406 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "DeviceEditorDialog.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include "base/Studio.h"
+#include "commands/studio/CreateOrDeleteDeviceCommand.h"
+#include "commands/studio/ReconnectDeviceCommand.h"
+#include "commands/studio/RenameDeviceCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenApplication.h"
+#include <kdialogbase.h>
+#include <kmessagebox.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtable.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+DeviceEditorDialog::DeviceEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *document) :
+ KDialogBase(parent, "deviceeditordialog", true,
+ i18n("Manage MIDI Devices"), Ok | Apply | Close, Ok, true),
+ m_document(document),
+ m_studio(&document->getStudio()),
+ m_modified(false)
+{
+ QVBox *mainBox = makeVBoxMainWidget();
+
+ m_table = new QTable(0, 4, mainBox);
+ m_table->setSorting(false);
+ m_table->setRowMovingEnabled(false);
+ m_table->setColumnMovingEnabled(false);
+ m_table->setShowGrid(false);
+ m_table->horizontalHeader()->setLabel(0, i18n("Device"));
+ m_table->horizontalHeader()->setLabel(1, i18n("Name"));
+ m_table->horizontalHeader()->setLabel(2, i18n("Type"));
+ m_table->horizontalHeader()->setLabel(3, i18n("Connection"));
+ m_table->horizontalHeader()->show();
+ m_table->verticalHeader()->hide();
+ m_table->setLeftMargin(0);
+ m_table->setSelectionMode(QTable::SingleRow);
+ m_table->setColumnReadOnly(0, true);
+ m_table->setColumnReadOnly(2, true);
+
+ makeConnectionList((unsigned int)MidiDevice::Play,
+ m_playConnections);
+ makeConnectionList((unsigned int)MidiDevice::Record,
+ m_recordConnections);
+
+ populate();
+
+ QHBox *hbox = new QHBox(mainBox);
+ QPushButton *addButton = new QPushButton(i18n("Add Play Device"), hbox);
+ QPushButton *addRButton = new QPushButton(i18n("Add Record Device"), hbox);
+ QPushButton *deleteButton = new QPushButton(i18n("Delete Device"), hbox);
+ connect(addButton, SIGNAL(clicked()), this, SLOT(slotAddPlayDevice()));
+ connect(addRButton, SIGNAL(clicked()), this, SLOT(slotAddRecordDevice()));
+ connect(deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteDevice()));
+ connect(m_table, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotValueChanged (int, int)));
+
+ setMinimumHeight(250);
+
+ enableButtonOK(false);
+ enableButtonApply(false);
+}
+
+DeviceEditorDialog::~DeviceEditorDialog()
+{
+ // nothing -- don't need to clear device list (the devices were
+ // just aliases for those in the studio)
+}
+
+void
+DeviceEditorDialog::populate()
+{
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+ m_devices.clear();
+
+ for (it = devices->begin(); it != devices->end(); ++it) {
+ if ((*it)->getType() == Device::Midi) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+ if (md)
+ m_devices.push_back(md);
+ }
+ }
+
+ while (m_table->numRows() > 0) {
+ m_table->removeRow(m_table->numRows() - 1);
+ }
+
+ int deviceCount = 0;
+
+#define NAME_COL 0
+#define LABEL_COL 1
+#define DIRECTION_COL 2
+#define CONNECTION_COL 3
+
+ for (it = m_devices.begin(); it != m_devices.end(); ++it) {
+
+ m_table->insertRows(deviceCount, 1);
+
+ // we know we only put MidiDevices in m_devices
+ MidiDevice *md = static_cast<MidiDevice *>(*it);
+
+ // if you change this string ("Device %1"), change test in slotApply
+ QString deviceName = i18n("Device %1").arg(md->getId() + 1);
+ QString deviceLabel = strtoqstr(md->getName());
+ QString connectionName = strtoqstr(md->getConnection());
+
+ m_table->setText(deviceCount, NAME_COL, deviceName);
+ m_table->setText(deviceCount, LABEL_COL, deviceLabel);
+ m_table->setText(deviceCount, DIRECTION_COL,
+ (md->getDirection() == MidiDevice::Play ?
+ i18n("Play") : i18n("Record")));
+
+ QStringList &list(md->getDirection() == MidiDevice::Play ?
+ m_playConnections : m_recordConnections);
+ int currentConnectionIndex = list.size() - 1;
+ for (unsigned int i = 0; i < list.size(); ++i) {
+ if (list[i] == connectionName)
+ currentConnectionIndex = i;
+ }
+
+ QComboTableItem *item = new QComboTableItem(m_table, list, false);
+ item->setCurrentItem(currentConnectionIndex);
+ m_table->setItem(deviceCount, CONNECTION_COL, item);
+
+ m_table->adjustRow(deviceCount);
+ ++deviceCount;
+ }
+
+ int minColumnWidths[] = { 80, 120, 100, 250 };
+ for (int i = 0; i < 4; ++i) {
+ m_table->adjustColumn(i);
+ if (m_table->columnWidth(i) < minColumnWidths[i])
+ m_table->setColumnWidth(i, minColumnWidths[i]);
+ }
+}
+
+void
+DeviceEditorDialog::makeConnectionList(unsigned int direction,
+ QStringList &list)
+{
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+
+ if (!rgapp->sequencerCall("getConnections(int, unsigned int)", replyType, replyData, data)) {
+ RG_DEBUG << "DeviceEditorDialog: can't call Sequencer" << endl;
+ list.append(i18n("No connection"));
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ unsigned int connections = 0;
+ if (replyType == "unsigned int")
+ reply >> connections;
+
+ for (unsigned int i = 0; i < connections; ++i) {
+
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+ arg << i;
+
+
+ if (!rgapp->sequencerCall("getConnection(int, unsigned int, unsigned int)",
+ replyType, replyData, data)) {
+ RG_DEBUG << "DeviceEditorDialog: can't call Sequencer" << endl;
+ list.append(i18n("No connection"));
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ QString connection;
+ if (replyType == "QString") {
+ reply >> connection;
+ list.append(connection);
+ }
+ }
+
+ list.append(i18n("No connection"));
+}
+
+void
+DeviceEditorDialog::setModified(bool m)
+{
+ if (m_modified == m)
+ return ;
+ enableButtonOK(m);
+ enableButtonApply(m);
+ m_modified = m;
+}
+
+void
+DeviceEditorDialog::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+DeviceEditorDialog::slotClose()
+{
+ if (m_modified) {
+
+ int reply = KMessageBox::questionYesNo(this,
+ i18n("Apply pending changes?"));
+
+ if (reply == KMessageBox::Yes)
+ slotApply();
+ }
+
+ reject();
+}
+
+void
+DeviceEditorDialog::slotApply()
+{
+ KMacroCommand *command = new KMacroCommand("Edit Devices");
+
+ // first delete deleted devices, in reverse order of id (so that
+ // if we undo this command we'll get the original ids back... probably)
+
+ std::vector<DeviceId> ids;
+
+ for (DeviceListIterator i = m_devices.begin();
+ i != m_devices.end(); ++i) {
+ if (m_deletedDevices.find((*i)->getId()) != m_deletedDevices.end()) {
+ ids.push_back((*i)->getId());
+ }
+ }
+
+ std::sort(ids.begin(), ids.end());
+
+ for (int i = ids.size() - 1; i >= 0; --i) {
+ command->addCommand(new CreateOrDeleteDeviceCommand(m_studio, ids[i]));
+ }
+
+ // create the new devices, and rename and/or set connections for
+ // any others that have changed
+
+ for (int i = 0; i < m_table->numRows(); ++i) {
+ int deviceId = getDeviceIdAt(i);
+ if (deviceId < 0) { // new device
+ command->addCommand(new CreateOrDeleteDeviceCommand
+ (m_studio,
+ qstrtostr(m_table->text(i, LABEL_COL)),
+ Device::Midi,
+ m_table->text(i, DIRECTION_COL) == "Play" ?
+ MidiDevice::Play :
+ MidiDevice::Record,
+ qstrtostr(m_table->text(i, CONNECTION_COL))));
+ } else { // existing device
+ Device *device = m_studio->getDevice(deviceId);
+ if (!device) {
+ /*
+ std::cerr <<
+ "WARNING: DeviceEditorDialog::slotApply(): device at row "
+ << i << " (id " << deviceId
+ << ") claims not to be new, but isn't in the studio"
+ << std::endl;
+ */
+ } else {
+ std::string name = qstrtostr(m_table->text(i, LABEL_COL));
+ std::string conn = qstrtostr(m_table->text(i, CONNECTION_COL));
+ if (device->getName() != name) {
+ command->addCommand(new RenameDeviceCommand
+ (m_studio, deviceId, name));
+ }
+ if (device->getConnection() != conn) {
+ command->addCommand(new ReconnectDeviceCommand
+ (m_studio, deviceId, conn));
+ }
+ }
+ }
+ }
+
+ m_document->getCommandHistory()->addCommand(command);
+
+ m_deletedDevices.clear();
+
+ populate();
+ setModified(false);
+}
+
+int
+DeviceEditorDialog::getDeviceIdAt(int row) // -1 for new device w/o an id yet
+{
+ QString t(m_table->text(row, 0));
+
+ QRegExp re("^.*(\\d+).*$");
+ re.search(t);
+
+ QString number = re.cap(1);
+ int id = -1;
+
+ if (number && number != "")
+ {
+ id = number.toInt() - 1; // displayed device numbers are 1-based
+ }
+
+ return id;
+}
+
+void
+DeviceEditorDialog::slotAddPlayDevice()
+{
+ int n = m_table->numRows();
+ m_table->insertRows(n, 1);
+ m_table->setText(n, 0, i18n("<new device>"));
+ m_table->setText(n, 1, i18n("New Device"));
+ m_table->setText(n, 2, i18n("Play"));
+
+ QComboTableItem *item =
+ new QComboTableItem(m_table, m_playConnections, false);
+ item->setCurrentItem(m_playConnections.size() - 1);
+ m_table->setItem(n, 3, item);
+ m_table->adjustRow(n);
+
+ setModified(true);
+}
+
+void
+DeviceEditorDialog::slotAddRecordDevice()
+{
+ int n = m_table->numRows();
+ m_table->insertRows(n, 1);
+ m_table->setText(n, 0, i18n("<new device>"));
+ m_table->setText(n, 1, i18n("New Device"));
+ m_table->setText(n, 2, i18n("Record"));
+
+ QComboTableItem *item =
+ new QComboTableItem(m_table, m_recordConnections, false);
+ item->setCurrentItem(m_recordConnections.size() - 1);
+ m_table->setItem(n, 3, item);
+ m_table->adjustRow(n);
+
+ setModified(true);
+}
+
+void
+DeviceEditorDialog::slotDeleteDevice()
+{
+ int n = m_table->currentRow();
+ m_deletedDevices.insert(getDeviceIdAt(n));
+ m_table->removeRow(n);
+ setModified(true);
+}
+
+void
+DeviceEditorDialog::slotValueChanged(int, int)
+{
+ setModified(true);
+}
+
+}
+#include "DeviceEditorDialog.moc"
diff --git a/src/gui/studio/DeviceEditorDialog.h b/src/gui/studio/DeviceEditorDialog.h
new file mode 100644
index 0000000..2bde025
--- /dev/null
+++ b/src/gui/studio/DeviceEditorDialog.h
@@ -0,0 +1,87 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_DEVICEEDITORDIALOG_H_
+#define _RG_DEVICEEDITORDIALOG_H_
+
+#include <base/Studio.h>
+#include <kdialogbase.h>
+#include <qstringlist.h>
+#include <set>
+
+
+class QWidget;
+class QTable;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+
+
+class DeviceEditorDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ DeviceEditorDialog(QWidget *parent, RosegardenGUIDoc *document);
+ ~DeviceEditorDialog();
+
+ void setModified(bool value);
+
+protected slots:
+ void slotOk();
+ void slotApply();
+ void slotClose();
+
+ void slotAddPlayDevice();
+ void slotAddRecordDevice();
+ void slotDeleteDevice();
+ void slotValueChanged(int row, int col);
+
+private:
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+
+ QStringList m_playConnections;
+ QStringList m_recordConnections;
+ void makeConnectionList(unsigned int direction, QStringList &list);
+
+ QTable *m_table;
+
+ DeviceList m_devices;
+ std::set<DeviceId> m_deletedDevices;
+
+ void populate();
+ int getDeviceIdAt(int row); // -1 for new device without an id yet
+
+ bool m_modified;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/DeviceManagerDialog.cpp b/src/gui/studio/DeviceManagerDialog.cpp
new file mode 100644
index 0000000..8f2fa6b
--- /dev/null
+++ b/src/gui/studio/DeviceManagerDialog.cpp
@@ -0,0 +1,833 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "DeviceManagerDialog.h"
+
+#include "ChangeRecordDeviceCommand.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/Studio.h"
+#include "commands/studio/CreateOrDeleteDeviceCommand.h"
+#include "commands/studio/ModifyDeviceCommand.h"
+#include "commands/studio/ReconnectDeviceCommand.h"
+#include "commands/studio/RenameDeviceCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/dialogs/ExportDeviceDialog.h"
+#include "gui/dialogs/ImportDeviceDialog.h"
+#include <kapplication.h>
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kaction.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qdialog.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qframe.h>
+#include <qgrid.h>
+#include <qgroupbox.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtable.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+static const int PLAY_NAME_COL = 0;
+static const int PLAY_CONNECTION_COL = 1;
+
+static const int RECORD_NAME_COL = 0;
+static const int RECORD_CURRENT_COL = 1;
+static const int RECORD_CONNECTION_COL = 2;
+
+
+DeviceManagerDialog::DeviceManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *document) :
+ KMainWindow(parent, "deviceeditordialog"),
+ m_document(document),
+ m_studio(&document->getStudio())
+ {
+ QFrame * mainBox = new QFrame(this);
+ setCentralWidget(mainBox);
+ QVBoxLayout *mainLayout = new QVBoxLayout(mainBox, 10, 10);
+
+ setCaption(i18n("Manage MIDI Devices"));
+
+ QGroupBox *groupBox = new QGroupBox(2, Horizontal, i18n("Play devices"), mainBox);
+
+ m_playTable = new QTable(0, 2, groupBox);
+ m_playTable->setSorting(false);
+ m_playTable->setRowMovingEnabled(false);
+ m_playTable->setColumnMovingEnabled(false);
+ m_playTable->setShowGrid(false);
+ m_playTable->horizontalHeader()->setLabel(PLAY_NAME_COL, i18n("Device"));
+ m_playTable->horizontalHeader()->setLabel(PLAY_CONNECTION_COL, i18n("Connection"));
+ m_playTable->horizontalHeader()->show();
+ m_playTable->verticalHeader()->hide();
+ m_playTable->setLeftMargin(0);
+ m_playTable->setSelectionMode(QTable::SingleRow);
+
+ QFrame *frame = new QFrame(groupBox);
+ QVBoxLayout *vlayout = new QVBoxLayout(frame);
+ QGrid *buttons = new QGrid(2, Horizontal, frame);
+ QPushButton *addButton = new QPushButton(i18n("New"), buttons);
+ m_deletePlayButton = new QPushButton(i18n("Delete"), buttons);
+ m_importButton = new QPushButton(i18n("Import..."), buttons);
+ m_exportButton = new QPushButton(i18n("Export..."), buttons);
+ m_banksButton = new QPushButton(i18n("Banks..."), buttons);
+ m_controllersButton = new QPushButton(i18n("Control Events..."), buttons);
+ vlayout->addWidget(buttons);
+ vlayout->addStretch(10);
+
+ QToolTip::add
+ (addButton,
+ i18n("Create a new Play device"));
+ QToolTip::add
+ (m_deletePlayButton,
+ i18n("Delete the selected device"));
+ QToolTip::add
+ (m_importButton,
+ i18n("Import Bank, Program and Controller data from a Rosegarden file to the selected device"));
+ QToolTip::add
+ (m_exportButton,
+ i18n("Export Bank and Controller data to a Rosegarden interchange file"));
+ QToolTip::add
+ (m_banksButton,
+ i18n("View and edit Banks and Programs for the selected device"));
+ QToolTip::add
+ (m_controllersButton,
+ i18n("View and edit Control Events for the selected device - these are special Event types that you can define against your device and control through Control Rulers or the Instrument Parameter Box "));
+
+ connect(addButton, SIGNAL(clicked()), this, SLOT(slotAddPlayDevice()));
+ connect(m_deletePlayButton, SIGNAL(clicked()), this, SLOT(slotDeletePlayDevice()));
+ connect(m_importButton, SIGNAL(clicked()), this, SLOT(slotImport()));
+ connect(m_exportButton, SIGNAL(clicked()), this, SLOT(slotExport()));
+ connect(m_banksButton, SIGNAL(clicked()), this, SLOT(slotSetBanks()));
+ connect(m_controllersButton, SIGNAL(clicked()), this, SLOT(slotSetControllers()));
+
+ connect(m_playTable, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotPlayValueChanged (int, int)));
+ connect(m_playTable, SIGNAL(currentChanged(int, int)),
+ this, SLOT(slotPlayDeviceSelected (int, int)));
+
+ mainLayout->addWidget(groupBox);
+ groupBox = new QGroupBox(2, Horizontal, i18n("Record devices"), mainBox);
+
+ m_recordTable = new QTable(0, 3, groupBox);
+ m_recordTable->setSorting(false);
+ m_recordTable->setRowMovingEnabled(false);
+ m_recordTable->setColumnMovingEnabled(false);
+ m_recordTable->setShowGrid(false);
+ m_recordTable->horizontalHeader()->setLabel(RECORD_NAME_COL, i18n("Device"));
+ m_recordTable->horizontalHeader()->setLabel(RECORD_CURRENT_COL, i18n("Current"));
+ m_recordTable->horizontalHeader()->setLabel(RECORD_CONNECTION_COL, i18n("Connection"));
+ m_recordTable->horizontalHeader()->show();
+ m_recordTable->verticalHeader()->hide();
+ m_recordTable->setLeftMargin(0);
+ m_recordTable->setSelectionMode(QTable::SingleRow);
+
+ frame = new QFrame(groupBox);
+ vlayout = new QVBoxLayout(frame);
+ buttons = new QGrid(2, Horizontal, frame);
+ addButton = new QPushButton(i18n("New"), buttons);
+ m_deleteRecordButton = new QPushButton(i18n("Delete"), buttons);
+ vlayout->addWidget(buttons);
+ vlayout->addStretch(10);
+
+ QToolTip::add
+ (addButton,
+ i18n("Create a new Record device"));
+ QToolTip::add
+ (m_deleteRecordButton,
+ i18n("Delete the selected device"));
+
+ connect(addButton, SIGNAL(clicked()), this, SLOT(slotAddRecordDevice()));
+ connect(m_deleteRecordButton, SIGNAL(clicked()), this, SLOT(slotDeleteRecordDevice()));
+
+ connect(m_recordTable, SIGNAL(currentChanged(int, int)),
+ this, SLOT(slotRecordDeviceSelected (int, int)));
+ connect(m_recordTable, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotRecordValueChanged (int, int)));
+
+ connect(document, SIGNAL(devicesResyncd()), this, SLOT(slotDevicesResyncd()));
+
+ m_noConnectionString = i18n("No connection");
+
+ slotDevicesResyncd();
+
+ setMinimumHeight(400);
+ setMinimumWidth(600);
+
+ mainLayout->addWidget(groupBox);
+
+ QFrame* btnBox = new QFrame(mainBox);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QPushButton *closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 0, 10);
+ layout->addStretch(10);
+ layout->addWidget(closeButton);
+ layout->addSpacing(5);
+
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ closeButton->setText(close->text());
+ connect(closeButton, SIGNAL(clicked()), this, SLOT(slotClose()));
+
+ mainLayout->addWidget(btnBox);
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::shortcut(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::shortcut(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ createGUI("devicemanager.rc");
+
+ m_document->getCommandHistory()->attachView(actionCollection());
+ connect(m_document->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(populate()));
+
+ m_playTable->setCurrentCell( -1, 0);
+ m_recordTable->setCurrentCell( -1, 0);
+
+ setAutoSaveSettings(DeviceManagerConfigGroup, true);
+
+ // enableButtonOK(false);
+ // enableButtonApply(false);
+
+ }
+
+ DeviceManagerDialog::~DeviceManagerDialog()
+ {
+ if (m_document) {
+ m_document->getCommandHistory()->detachView(actionCollection());
+ m_document = 0;
+ }
+
+ RG_DEBUG << "\n*** DeviceManagerDialog::~DeviceManagerDialog\n" << endl;
+ }
+
+ void
+ DeviceManagerDialog::slotClose()
+ {
+ if (m_document) {
+ m_document->getCommandHistory()->detachView(actionCollection());
+ m_document = 0;
+ }
+
+ close();
+ }
+
+ void
+ DeviceManagerDialog::slotDevicesResyncd()
+ {
+ makeConnectionList((unsigned int)MidiDevice::Play,
+ m_playConnections);
+ makeConnectionList((unsigned int)MidiDevice::Record,
+ m_recordConnections);
+
+ populate();
+ }
+
+ void
+ DeviceManagerDialog::populate()
+ {
+ DeviceList *devices = m_studio->getDevices();
+
+ //KConfig *config = kapp->config();
+ //config->setGroup(SequencerOptionsConfigGroup);
+ //DeviceId recordDevice =
+ //config->readUnsignedNumEntry("midirecorddevice");
+
+ m_playDevices.clear();
+ m_recordDevices.clear();
+
+ for (DeviceList::iterator it = devices->begin();
+ it != devices->end(); ++it) {
+ if ((*it)->getType() == Device::Midi) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+ if (md) {
+ if (md->getDirection() == MidiDevice::Play) {
+ m_playDevices.push_back(md);
+ } else {
+ m_recordDevices.push_back(md);
+ }
+ }
+ }
+ }
+
+ while (m_playTable->numRows() > 0) {
+ m_playTable->removeRow(m_playTable->numRows() - 1);
+ }
+ while (m_recordTable->numRows() > 0) {
+ m_recordTable->removeRow(m_recordTable->numRows() - 1);
+ }
+
+ int deviceCount = 0;
+
+ for (MidiDeviceList::iterator it = m_playDevices.begin();
+ it != m_playDevices.end(); ++it) {
+
+ m_playTable->insertRows(deviceCount, 1);
+
+ QString deviceName = i18n("%1").arg(deviceCount + 1);
+ QString connectionName = strtoqstr((*it)->getConnection());
+
+ m_playTable->setText(deviceCount, PLAY_NAME_COL,
+ strtoqstr((*it)->getName()));
+
+ int currentConnectionIndex = m_playConnections.size() - 1;
+ for (unsigned int i = 0; i < m_playConnections.size(); ++i) {
+ if (m_playConnections[i] == connectionName)
+ currentConnectionIndex = i;
+ }
+
+ QComboTableItem *item = new QComboTableItem(m_playTable, m_playConnections, false);
+ item->setCurrentItem(currentConnectionIndex);
+ m_playTable->setItem(deviceCount, PLAY_CONNECTION_COL, item);
+
+ m_playTable->adjustRow(deviceCount);
+ ++deviceCount;
+ }
+
+ int minPlayColumnWidths[] = { 250, 270 };
+ for (int i = 0; i < 2; ++i) {
+ m_playTable->adjustColumn(i);
+ if (m_playTable->columnWidth(i) < minPlayColumnWidths[i])
+ m_playTable->setColumnWidth(i, minPlayColumnWidths[i]);
+ }
+
+ deviceCount = 0;
+
+ for (MidiDeviceList::iterator it = m_recordDevices.begin();
+ it != m_recordDevices.end(); ++it) {
+
+ m_recordTable->insertRows(deviceCount, 1);
+
+ QString deviceName = i18n("%1").arg(deviceCount + 1);
+ QString connectionName = strtoqstr((*it)->getConnection());
+
+ m_recordTable->setText(deviceCount, RECORD_NAME_COL,
+ strtoqstr((*it)->getName()));
+
+ int currentConnectionIndex = m_recordConnections.size() - 1;
+ for (unsigned int i = 0; i < m_recordConnections.size(); ++i) {
+ if (m_recordConnections[i] == connectionName)
+ currentConnectionIndex = i;
+ }
+
+ QComboTableItem *item = new QComboTableItem(m_recordTable, m_recordConnections, false);
+ item->setCurrentItem(currentConnectionIndex);
+ m_recordTable->setItem(deviceCount, RECORD_CONNECTION_COL, item);
+
+ QCheckTableItem *check = new QCheckTableItem(m_recordTable, QString());
+ //check->setChecked((*it)->getId() == recordDevice);
+ //check->setText(((*it)->getId() == recordDevice) ?
+ // i18n("Yes") : i18n("No"));
+ check->setChecked((*it)->isRecording());
+ check->setText((*it)->isRecording() ? i18n("Yes") : i18n("No"));
+ m_recordTable->setItem(deviceCount, RECORD_CURRENT_COL, check);
+
+ m_recordTable->adjustRow(deviceCount);
+ ++deviceCount;
+ }
+
+ int minRecordColumnWidths[] = { 180, 70, 270 };
+ for (int i = 0; i < 3; ++i) {
+ m_recordTable->adjustColumn(i);
+ if (m_recordTable->columnWidth(i) < minRecordColumnWidths[i])
+ m_recordTable->setColumnWidth(i, minRecordColumnWidths[i]);
+ }
+
+ slotPlayDeviceSelected(m_playTable->currentRow(), m_playTable->currentColumn());
+ slotRecordDeviceSelected(m_recordTable->currentRow(), m_recordTable->currentColumn());
+ }
+
+ void
+ DeviceManagerDialog::makeConnectionList(unsigned int direction,
+ QStringList &list)
+ {
+ list.clear();
+
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+
+ if (!rgapp->sequencerCall("getConnections(int, unsigned int)", replyType, replyData, data)) {
+ RG_DEBUG << "DeviceManagerDialog: can't call Sequencer" << endl;
+ list.append(m_noConnectionString);
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ unsigned int connections = 0;
+ if (replyType == "unsigned int")
+ reply >> connections;
+
+ for (unsigned int i = 0; i < connections; ++i) {
+
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+ arg << i;
+
+
+ if (!rgapp->sequencerCall("getConnection(int, unsigned int, unsigned int)",
+ replyType, replyData, data)) {
+ RG_DEBUG << "DeviceManagerDialog: can't call Sequencer" << endl;
+ list.append(i18n("No connection"));
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ QString connection;
+ if (replyType == "QString") {
+ reply >> connection;
+ list.append(connection);
+ }
+ }
+
+ list.append(i18n("No connection"));
+ }
+
+ void
+ DeviceManagerDialog::closeEvent(QCloseEvent *e)
+ {
+ emit closing();
+ KMainWindow::closeEvent(e);
+ }
+
+ DeviceId
+ DeviceManagerDialog::getPlayDeviceIdAt(int row)
+ {
+ if (row < 0 || row > (int)m_playDevices.size())
+ return Device::NO_DEVICE;
+ return m_playDevices[row]->getId();
+ }
+
+ DeviceId
+ DeviceManagerDialog::getRecordDeviceIdAt(int row)
+ {
+ if (row < 0 || row > (int)m_recordDevices.size())
+ return Device::NO_DEVICE;
+ return m_recordDevices[row]->getId();
+ }
+
+ void
+ DeviceManagerDialog::slotAddPlayDevice()
+ {
+ QString connection = "";
+ if (m_playConnections.size() > 0)
+ connection = m_playConnections[m_playConnections.size() - 1];
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio,
+ qstrtostr(i18n("New Device")),
+ Device::Midi,
+ MidiDevice::Play,
+ qstrtostr(connection));
+ m_document->getCommandHistory()->addCommand(command);
+ }
+
+ void
+ DeviceManagerDialog::slotAddRecordDevice()
+ {
+ QString connection = "";
+ if (m_recordConnections.size() > 0)
+ connection = m_recordConnections[m_recordConnections.size() - 1];
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio,
+ qstrtostr(i18n("New Device")),
+ Device::Midi,
+ MidiDevice::Record,
+ qstrtostr(connection));
+ m_document->getCommandHistory()->addCommand(command);
+ }
+
+ void
+ DeviceManagerDialog::slotDeletePlayDevice()
+ {
+ // should really grey out if nothing current
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ if (id == Device::NO_DEVICE)
+ return ;
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio, id);
+ m_document->getCommandHistory()->addCommand(command);
+
+ QByteArray data;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (unsigned int)id;
+ rgapp->sequencerSend("removeDevice(unsigned int)", data);
+ }
+
+ void
+ DeviceManagerDialog::slotDeleteRecordDevice()
+ {
+ DeviceId id = getRecordDeviceIdAt(m_recordTable->currentRow());
+ if (id == Device::NO_DEVICE)
+ return ;
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio, id);
+ m_document->getCommandHistory()->addCommand(command);
+ }
+
+ void
+ DeviceManagerDialog::slotPlayValueChanged(int row, int col)
+ {
+ if (!m_document)
+ return ; // closing
+ DeviceId id = getPlayDeviceIdAt(row);
+ if (id == Device::NO_DEVICE)
+ return ;
+
+ Device *device = m_studio->getDevice(id);
+ if (!device) {
+ std::cerr << "WARNING: DeviceManagerDialog::slotPlayValueChanged(): device at row "
+ << row << " (id " << id << ") not found in studio"
+ << std::endl;
+ return ;
+ }
+
+ switch (col) {
+
+ case PLAY_NAME_COL: {
+ std::string name = qstrtostr(m_playTable->text(row, col));
+ if (device->getName() != name) {
+
+ m_document->getCommandHistory()->addCommand
+ (new RenameDeviceCommand(m_studio, id, name));
+ emit deviceNamesChanged();
+
+ QByteArray data;
+ QDataStream arg(data, IO_WriteOnly);
+
+ arg << (unsigned int)id;
+ arg << m_playTable->text(row, col);
+
+ rgapp->sequencerSend("renameDevice(unsigned int, QString)", data);
+ }
+ }
+ break;
+
+ case PLAY_CONNECTION_COL: {
+ std::string connection = qstrtostr(m_playTable->text(row, col));
+ if (connection == qstrtostr(m_noConnectionString))
+ connection = "";
+ if (device->getConnection() != connection) {
+ m_document->getCommandHistory()->addCommand
+ (new ReconnectDeviceCommand(m_studio, id, connection));
+ }
+ }
+ break;
+ }
+ }
+
+ void
+ DeviceManagerDialog::slotRecordValueChanged(int row, int col)
+ {
+ if (!m_document)
+ return ; // closing
+ DeviceId id = getRecordDeviceIdAt(row);
+ if (id == Device::NO_DEVICE)
+ return ;
+
+ Device *device = m_studio->getDevice(id);
+ if (!device) {
+ std::cerr << "WARNING: DeviceManagerDialog::slotRecordValueChanged(): device at row "
+ << row << " (id " << id << ") not found in studio"
+ << std::endl;
+ return ;
+ }
+
+ switch (col) {
+
+ case RECORD_NAME_COL: {
+ std::string name = qstrtostr(m_recordTable->text(row, col));
+ if (device->getName() != name) {
+
+ m_document->getCommandHistory()->addCommand
+ (new RenameDeviceCommand(m_studio, id, name));
+ emit deviceNamesChanged();
+
+ QByteArray data;
+ QDataStream arg(data, IO_WriteOnly);
+
+ arg << (unsigned int)id;
+ arg << m_recordTable->text(row, col);
+
+ rgapp->sequencerSend("renameDevice(unsigned int, QString)", data);
+ }
+ }
+ break;
+
+ case RECORD_CONNECTION_COL: {
+ std::string connection = qstrtostr(m_recordTable->text(row, col));
+ if (device->getConnection() != connection) {
+ m_document->getCommandHistory()->addCommand
+ (new ReconnectDeviceCommand(m_studio, id, connection));
+ }
+ }
+ break;
+
+ case RECORD_CURRENT_COL: {
+ m_recordTable->blockSignals(true);
+
+ QCheckTableItem *check =
+ dynamic_cast<QCheckTableItem *>(m_recordTable->item(row, col));
+ if (!check)
+ return ;
+
+ bool actionConnect = check->isChecked();
+
+ // The following lines are not strictly needed, but give the checkboxes
+ // a smoother behavior while waiting a confirmation from the sequencer.
+ //
+ check->setText(actionConnect ? i18n("Yes") : i18n("No"));
+ MidiDevice *device =
+ dynamic_cast<MidiDevice*>(m_studio->getDevice(id));
+ device->setRecording(actionConnect);
+
+ m_recordTable->setCurrentCell(row, 0);
+
+ m_document->getCommandHistory()->addCommand
+ (new ChangeRecordDeviceCommand(id, actionConnect));
+
+ m_recordTable->blockSignals(false);
+ }
+ break;
+ }
+ }
+
+ void
+ DeviceManagerDialog::slotPlayDeviceSelected(int row, int col)
+ {
+ RG_DEBUG << "slotPlayDeviceSelected(" << row << "," << col << ")" << endl;
+
+ bool enable = (row >= 0 && row < (int)m_playDevices.size());
+ m_deletePlayButton->setEnabled(enable);
+ m_importButton->setEnabled(enable);
+ m_exportButton->setEnabled(enable);
+ m_banksButton->setEnabled(enable);
+ m_controllersButton->setEnabled(enable);
+ }
+
+ void
+ DeviceManagerDialog::slotRecordDeviceSelected(int row, int col)
+ {
+ RG_DEBUG << "slotRecordDeviceSelected(" << row << "," << col << ")" << endl;
+
+ bool enable = (row >= 0 && row < (int)m_recordDevices.size());
+ m_deleteRecordButton->setEnabled(enable);
+ }
+
+ void
+ DeviceManagerDialog::slotImport()
+ {
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ if (id == Device::NO_DEVICE)
+ return ;
+
+ QString deviceDir = KGlobal::dirs()->findResource("appdata", "library/");
+ QDir dir(deviceDir);
+ if (!dir.exists()) {
+ deviceDir = ":ROSEGARDENDEVICE";
+ } else {
+ deviceDir = "file://" + deviceDir;
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (deviceDir,
+ "audio/x-rosegarden-device audio/x-rosegarden audio/x-soundfont",
+ this, i18n("Import from Device in File"));
+
+ if (url.isEmpty())
+ return ;
+
+ ImportDeviceDialog *dialog = new ImportDeviceDialog(this, url);
+ if (dialog->doImport() && dialog->exec() == QDialog::Accepted) {
+
+ ModifyDeviceCommand *command = 0;
+
+ BankList banks(dialog->getBanks());
+ ProgramList programs(dialog->getPrograms());
+ ControlList controls(dialog->getControllers());
+ KeyMappingList keyMappings(dialog->getKeyMappings());
+ MidiDevice::VariationType variation(dialog->getVariationType());
+ std::string librarianName(dialog->getLibrarianName());
+ std::string librarianEmail(dialog->getLibrarianEmail());
+
+ // don't record the librarian when
+ // merging banks -- it's misleading.
+ // (also don't use variation type)
+ if (!dialog->shouldOverwriteBanks()) {
+ librarianName = "";
+ librarianEmail = "";
+ }
+
+ command = new ModifyDeviceCommand(m_studio,
+ id,
+ dialog->getDeviceName(),
+ librarianName,
+ librarianEmail);
+
+ if (dialog->shouldOverwriteBanks()) {
+ command->setVariation(variation);
+ }
+ if (dialog->shouldImportBanks()) {
+ command->setBankList(banks);
+ command->setProgramList(programs);
+ }
+ if (dialog->shouldImportControllers()) {
+ command->setControlList(controls);
+ }
+ if (dialog->shouldImportKeyMappings()) {
+ command->setKeyMappingList(keyMappings);
+ }
+
+ command->setOverwrite(dialog->shouldOverwriteBanks());
+ command->setRename(dialog->shouldRename());
+
+ m_document->getCommandHistory()->addCommand(command);
+
+ if (dialog->shouldRename())
+ emit deviceNamesChanged();
+ }
+
+ delete dialog;
+ }
+
+ void
+ DeviceManagerDialog::slotExport()
+ {
+ QString extension = "rgd";
+
+ QString name =
+ KFileDialog::getSaveFileName(":ROSEGARDEN",
+ (extension.isEmpty() ? QString("*") : ("*." + extension)),
+ this,
+ i18n("Export Device as..."));
+
+ // Check for the existence of the name
+ if (name.isEmpty())
+ return ;
+
+ // Append extension if we don't have one
+ //
+ if (!extension.isEmpty()) {
+ if (!name.endsWith("." + extension)) {
+ name += "." + extension;
+ }
+ }
+
+ QFileInfo info(name);
+
+ if (info.isDir()) {
+ KMessageBox::sorry(this, i18n("You have specified a directory"));
+ return ;
+ }
+
+ if (info.exists()) {
+ int overwrite = KMessageBox::questionYesNo
+ (this, i18n("The specified file exists. Overwrite?"));
+
+ if (overwrite != KMessageBox::Yes)
+ return ;
+
+ }
+
+ std::vector<DeviceId> devices;
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ MidiDevice *md = 0;
+ if (id != Device::NO_DEVICE) {
+ md = dynamic_cast<MidiDevice *>(m_studio->getDevice(id));
+ }
+ if (md) {
+ ExportDeviceDialog ed(this, strtoqstr(md->getName()));
+ if (ed.exec() != QDialog::Accepted)
+ return ;
+ if (ed.getExportType() == ExportDeviceDialog::ExportOne) {
+ devices.push_back(id);
+ }
+ }
+
+ m_document->exportStudio(name, devices);
+ }
+
+ void
+ DeviceManagerDialog::slotSetBanks()
+ {
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ emit editBanks(id);
+ }
+
+ void
+ DeviceManagerDialog::slotSetControllers()
+ {
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ emit editControllers(id);
+ }
+
+ }
+#include "DeviceManagerDialog.moc"
diff --git a/src/gui/studio/DeviceManagerDialog.h b/src/gui/studio/DeviceManagerDialog.h
new file mode 100644
index 0000000..aebc54e
--- /dev/null
+++ b/src/gui/studio/DeviceManagerDialog.h
@@ -0,0 +1,121 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_DEVICEMANAGERDIALOG_H_
+#define _RG_DEVICEMANAGERDIALOG_H_
+
+#include "base/Device.h"
+#include <kmainwindow.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <vector>
+
+
+class QWidget;
+class QTable;
+class QPushButton;
+class QCloseEvent;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class MidiDevice;
+
+
+class DeviceManagerDialog : public KMainWindow
+{
+ Q_OBJECT
+public:
+ DeviceManagerDialog(QWidget *parent, RosegardenGUIDoc *document);
+ ~DeviceManagerDialog();
+
+ void setModified(bool value);
+
+signals:
+ void deviceNamesChanged();
+
+ void editBanks(DeviceId);
+ void editControllers(DeviceId);
+
+ void closing();
+
+protected slots:
+ void slotClose();
+ void slotAddPlayDevice();
+ void slotAddRecordDevice();
+ void slotDeletePlayDevice();
+ void slotDeleteRecordDevice();
+ void slotPlayValueChanged(int row, int col);
+ void slotRecordValueChanged(int row, int col);
+ void slotPlayDeviceSelected(int row, int col);
+ void slotRecordDeviceSelected(int row, int col);
+
+ // for play devices only:
+ void slotImport();
+ void slotExport();
+ void slotSetBanks();
+ void slotSetControllers();
+
+ void slotDevicesResyncd();
+ void populate();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+private:
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+
+ QPushButton *m_deletePlayButton;
+ QPushButton *m_deleteRecordButton;
+ QPushButton *m_importButton;
+ QPushButton *m_exportButton;
+ QPushButton *m_banksButton;
+ QPushButton *m_controllersButton;
+
+ QStringList m_playConnections;
+ QStringList m_recordConnections;
+ void makeConnectionList(unsigned int direction, QStringList &list);
+
+ QTable *m_playTable;
+ QTable *m_recordTable;
+
+ typedef std::vector<MidiDevice *> MidiDeviceList;
+ MidiDeviceList m_playDevices;
+ MidiDeviceList m_recordDevices;
+
+ DeviceId getPlayDeviceIdAt(int row); // NO_DEVICE = not found
+ DeviceId getRecordDeviceIdAt(int row); // NO_DEVICE = not found
+
+ QString m_noConnectionString;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiBankListViewItem.cpp b/src/gui/studio/MidiBankListViewItem.cpp
new file mode 100644
index 0000000..2563ccb
--- /dev/null
+++ b/src/gui/studio/MidiBankListViewItem.cpp
@@ -0,0 +1,98 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MidiBankListViewItem.h"
+
+#include <klocale.h>
+#include "base/Device.h"
+#include "MidiDeviceListViewItem.h"
+#include "MidiKeyMapListViewItem.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MidiBankListViewItem::MidiBankListViewItem(DeviceId deviceId,
+ int bankNb,
+ QListViewItem* parent,
+ QString name,
+ bool percussion, int msb, int lsb)
+ : MidiDeviceListViewItem(deviceId, parent, name, percussion, msb, lsb),
+ m_percussion(percussion),
+ m_bankNb(bankNb)
+{}
+
+void MidiBankListViewItem::setPercussion(bool percussion)
+{
+ m_percussion = percussion;
+ setText(1, QString(percussion ? i18n("Percussion Bank") : i18n("Bank")));
+}
+
+void MidiBankListViewItem::setMSB(int msb)
+{
+ setText(2, QString().setNum(msb));
+}
+
+void MidiBankListViewItem::setLSB(int lsb)
+{
+ setText(3, QString().setNum(lsb));
+}
+
+int MidiBankListViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(i);
+
+ if (!bankItem) {
+ MidiKeyMapListViewItem *keyItem = dynamic_cast<MidiKeyMapListViewItem *>(i);
+ if (keyItem)
+ return -1; // banks before key maps
+ }
+
+ if (!bankItem || (col != 2 && col != 3)) {
+ return MidiDeviceListViewItem::compare(i, col, ascending);
+ }
+
+ int thisVal = text(col).toInt(),
+ otherVal = bankItem->text(col).toInt();
+
+ if (thisVal == otherVal) {
+ if (col == 2) { // if sorting on MSB, suborder with LSB
+ return compare(i, 3, ascending);
+ } else {
+ return 0;
+ }
+ }
+
+ // 'ascending' should be ignored according to Qt docs
+ //
+ return
+ thisVal > otherVal ? 1 :
+ thisVal == otherVal ? 0 :
+ -1;
+
+}
+
+}
diff --git a/src/gui/studio/MidiBankListViewItem.h b/src/gui/studio/MidiBankListViewItem.h
new file mode 100644
index 0000000..87f4b02
--- /dev/null
+++ b/src/gui/studio/MidiBankListViewItem.h
@@ -0,0 +1,70 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIDIBANKLISTVIEWITEM_H_
+#define _RG_MIDIBANKLISTVIEWITEM_H_
+
+#include "base/Device.h"
+#include "MidiDeviceListViewItem.h"
+#include <qstring.h>
+
+
+class QListViewItem;
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiBankListViewItem : public MidiDeviceListViewItem
+{
+public:
+ MidiBankListViewItem(DeviceId deviceId,
+ int bankNb,
+ QListViewItem* parent, QString name,
+ bool percussion,
+ int msb, int lsb);
+
+ int getBank() { return m_bankNb; }
+
+ void setPercussion(bool percussion);
+ bool isPercussion() const { return m_percussion; }
+ void setMSB(int msb);
+ void setLSB(int msb);
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ bool m_percussion;
+ int m_bankNb;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiDeviceListViewItem.cpp b/src/gui/studio/MidiDeviceListViewItem.cpp
new file mode 100644
index 0000000..30a339e
--- /dev/null
+++ b/src/gui/studio/MidiDeviceListViewItem.cpp
@@ -0,0 +1,88 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MidiDeviceListViewItem.h"
+
+#include <klocale.h>
+#include "base/Device.h"
+#include <qlistview.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MidiDeviceListViewItem::MidiDeviceListViewItem(DeviceId deviceId,
+ QListView* parent, QString name)
+ : KListViewItem(parent, name),
+ m_deviceId(deviceId)
+{}
+
+MidiDeviceListViewItem::MidiDeviceListViewItem(DeviceId deviceId,
+ QListViewItem* parent, QString name,
+ bool percussion,
+ int msb, int lsb)
+ : KListViewItem(parent, name,
+ QString(percussion ? i18n("Percussion Bank") : i18n("Bank")),
+ QString().setNum(msb), QString().setNum(lsb)),
+ m_deviceId(deviceId)
+{}
+
+MidiDeviceListViewItem::MidiDeviceListViewItem(DeviceId deviceId,
+ QListViewItem* parent, QString name)
+: KListViewItem(parent, name, i18n("Key Mapping"), "", ""),
+m_deviceId(deviceId)
+{}
+
+int MidiDeviceListViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ MidiDeviceListViewItem* item = dynamic_cast<MidiDeviceListViewItem*>(i);
+ if (!item)
+ return QListViewItem::compare(i, col, ascending);
+ if (col == 0)
+ return
+ getDeviceId() > item->getDeviceId() ? 1 :
+ getDeviceId() == item->getDeviceId() ? 0 :
+ -1;
+
+ int thisVal = text(col).toInt(),
+ otherVal = item->text(col).toInt();
+
+ if (thisVal == otherVal) {
+ if (col == 2) { // if sorting on MSB, suborder with LSB
+ return compare(i, 3, ascending);
+ } else {
+ return 0;
+ }
+ }
+
+ // 'ascending' should be ignored according to Qt docs
+ //
+ return (thisVal > otherVal) ? 1 : -1;
+
+ //!!! how to use percussion here?
+}
+
+}
diff --git a/src/gui/studio/MidiDeviceListViewItem.h b/src/gui/studio/MidiDeviceListViewItem.h
new file mode 100644
index 0000000..53652c6
--- /dev/null
+++ b/src/gui/studio/MidiDeviceListViewItem.h
@@ -0,0 +1,69 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIDIDEVICELISTVIEWITEM_H_
+#define _RG_MIDIDEVICELISTVIEWITEM_H_
+
+#include "base/Device.h"
+#include <klistview.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiDeviceListViewItem : public KListViewItem
+{
+public:
+ // Device
+ MidiDeviceListViewItem(DeviceId id,
+ QListView* parent, QString name);
+
+ // Bank
+ MidiDeviceListViewItem(DeviceId id,
+ QListViewItem* parent, QString name,
+ bool percussion,
+ int msb, int lsb);
+
+ // Key Mapping
+ MidiDeviceListViewItem(DeviceId id,
+ QListViewItem* parent, QString name);
+
+ DeviceId getDeviceId() const { return m_deviceId; }
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ DeviceId m_deviceId;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiKeyMapListViewItem.cpp b/src/gui/studio/MidiKeyMapListViewItem.cpp
new file mode 100644
index 0000000..c070e04
--- /dev/null
+++ b/src/gui/studio/MidiKeyMapListViewItem.cpp
@@ -0,0 +1,56 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MidiKeyMapListViewItem.h"
+
+#include "MidiDeviceListViewItem.h"
+#include "MidiBankListViewItem.h"
+#include "base/Device.h"
+#include <klocale.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MidiKeyMapListViewItem::MidiKeyMapListViewItem(DeviceId deviceId,
+ QListViewItem* parent,
+ QString name)
+ : MidiDeviceListViewItem(deviceId, parent, name),
+ m_name(name)
+{
+ setText(1, i18n("Key Mapping"));
+}
+
+int MidiKeyMapListViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ if (dynamic_cast<MidiBankListViewItem *>(i)) {
+ return 1; // banks before key maps
+ }
+
+ return MidiDeviceListViewItem::compare(i, col, ascending);
+}
+
+}
diff --git a/src/gui/studio/MidiKeyMapListViewItem.h b/src/gui/studio/MidiKeyMapListViewItem.h
new file mode 100644
index 0000000..df53de3
--- /dev/null
+++ b/src/gui/studio/MidiKeyMapListViewItem.h
@@ -0,0 +1,59 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIDIKEYMAPLISTVIEWITEM_H_
+#define _RG_MIDIKEYMAPLISTVIEWITEM_H_
+
+#include "base/Device.h"
+#include "MidiDeviceListViewItem.h"
+#include <qstring.h>
+
+
+class QListViewItem;
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiKeyMapListViewItem : public MidiDeviceListViewItem
+{
+public:
+ MidiKeyMapListViewItem(DeviceId deviceId,
+ QListViewItem* parent, QString name);
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+ QString getName() const { return m_name; }
+
+protected:
+ QString m_name;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiKeyMappingEditor.cpp b/src/gui/studio/MidiKeyMappingEditor.cpp
new file mode 100644
index 0000000..f31d0dc
--- /dev/null
+++ b/src/gui/studio/MidiKeyMappingEditor.cpp
@@ -0,0 +1,197 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MidiKeyMappingEditor.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "BankEditorDialog.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "MidiKeyMapListViewItem.h"
+#include "NameSetEditor.h"
+#include <kcompletion.h>
+#include <klineedit.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qobjectlist.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MidiKeyMappingEditor::MidiKeyMappingEditor(BankEditorDialog* bankEditor,
+ QWidget* parent,
+ const char* name)
+ : NameSetEditor(bankEditor,
+ i18n("Key Mapping details"),
+ parent, name, i18n("Pitches"), false),
+ m_device(0)
+{
+ QWidget *additionalWidget = makeAdditionalWidget(m_mainFrame);
+ if (additionalWidget) {
+ m_mainLayout->addMultiCellWidget(additionalWidget, 0, 2, 0, 2);
+ }
+}
+
+QWidget *
+MidiKeyMappingEditor::makeAdditionalWidget(QWidget *parent)
+{
+ return 0;
+}
+
+void
+MidiKeyMappingEditor::clearAll()
+{
+ blockAllSignals(true);
+
+ for (unsigned int i = 0; i < m_names.size(); ++i)
+ m_names[i]->clear();
+
+ setTitle(i18n("Key Mapping details"));
+
+ setEnabled(false);
+
+ blockAllSignals(false);
+}
+
+void
+MidiKeyMappingEditor::populate(QListViewItem* item)
+{
+ RG_DEBUG << "MidiKeyMappingEditor::populate\n";
+
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem *>(item);
+ if (!keyItem) {
+ RG_DEBUG << "MidiKeyMappingEditor::populate : not a key item - returning\n";
+ return ;
+ }
+
+ MidiDevice* device = m_bankEditor->getCurrentMidiDevice();
+ if (!device)
+ return ;
+
+ m_device = device;
+ m_mappingName = qstrtostr(keyItem->getName());
+
+ setEnabled(true);
+
+ reset();
+}
+
+void
+MidiKeyMappingEditor::reset()
+{
+ if (!m_device)
+ return ;
+
+ setTitle(strtoqstr(m_mappingName));
+
+ const MidiKeyMapping *m = m_device->getKeyMappingByName(m_mappingName);
+
+ if (!m) {
+ RG_DEBUG << "WARNING: MidiKeyMappingEditor::reset: No such mapping as " << m_mappingName << endl;
+ }
+
+ m_mapping = *m;
+
+ blockAllSignals(true);
+
+ // Librarian details
+ //
+ m_librarian->setText(strtoqstr(m_device->getLibrarianName()));
+ m_librarianEmail->setText(strtoqstr(m_device->getLibrarianEmail()));
+
+ for (MidiKeyMapping::KeyNameMap::const_iterator it =
+ m_mapping.getMap().begin();
+ it != m_mapping.getMap().end(); ++it) {
+
+ int i = it->first;
+ if (i < 0 || i > 127) {
+ RG_DEBUG << "WARNING: MidiKeyMappingEditor::reset: Key " << i
+ << " out of range in mapping " << m_mapping.getName()
+ << endl;
+ continue;
+ }
+
+ QString name = strtoqstr(it->second);
+ m_completion.addItem(name);
+ m_names[i]->setText(name);
+ m_names[i]->setCursorPosition(0);
+ }
+
+ blockAllSignals(false);
+}
+
+void
+MidiKeyMappingEditor::slotNameChanged(const QString& name)
+{
+ const KLineEdit* lineEdit = dynamic_cast<const KLineEdit*>(sender());
+ if (!lineEdit) {
+ RG_DEBUG << "MidiKeyMappingEditor::slotNameChanged() : %%% ERROR - signal sender is not a KLineEdit\n";
+ return ;
+ }
+
+ QString senderName = sender()->name();
+
+ // Adjust value back to zero rated
+ //
+ unsigned int pitch = senderName.toUInt() - 1;
+
+ RG_DEBUG << "MidiKeyMappingEditor::slotNameChanged("
+ << name << ") : pitch = " << pitch << endl;
+
+ if (qstrtostr(name) != m_mapping.getMap()[pitch]) {
+ m_mapping.getMap()[pitch] = qstrtostr(name);
+ m_bankEditor->setModified(true);
+ }
+}
+
+void
+MidiKeyMappingEditor::slotEntryButtonPressed()
+{}
+
+void MidiKeyMappingEditor::blockAllSignals(bool block)
+{
+ const QObjectList* allChildren = queryList("KLineEdit", "[0-9]+");
+ QObjectListIt it(*allChildren);
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ obj->blockSignals(block);
+ ++it;
+ }
+}
+
+}
+#include "MidiKeyMappingEditor.moc"
diff --git a/src/gui/studio/MidiKeyMappingEditor.h b/src/gui/studio/MidiKeyMappingEditor.h
new file mode 100644
index 0000000..86959b8
--- /dev/null
+++ b/src/gui/studio/MidiKeyMappingEditor.h
@@ -0,0 +1,78 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIDIKEYMAPPINGEDITOR_H_
+#define _RG_MIDIKEYMAPPINGEDITOR_H_
+
+#include "base/MidiProgram.h"
+#include "NameSetEditor.h"
+#include <string>
+
+
+class QWidget;
+class QString;
+class QListViewItem;
+
+
+namespace Rosegarden
+{
+
+class MidiDevice;
+class BankEditorDialog;
+
+
+class MidiKeyMappingEditor : public NameSetEditor
+{
+ Q_OBJECT
+
+public:
+ MidiKeyMappingEditor(BankEditorDialog *bankEditor,
+ QWidget *parent,
+ const char *name = 0);
+
+ void clearAll();
+ void populate(QListViewItem *);
+ MidiKeyMapping &getMapping() { return m_mapping; }
+ void reset();
+
+public slots:
+ virtual void slotNameChanged(const QString &);
+ virtual void slotEntryButtonPressed();
+
+protected:
+ virtual QWidget *makeAdditionalWidget(QWidget *parent);
+ void blockAllSignals(bool block);
+
+ //--------------- Data members ---------------------------------
+
+ MidiDevice *m_device;
+ std::string m_mappingName;
+ MidiKeyMapping m_mapping;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiMixerVUMeter.cpp b/src/gui/studio/MidiMixerVUMeter.cpp
new file mode 100644
index 0000000..2dd86cc
--- /dev/null
+++ b/src/gui/studio/MidiMixerVUMeter.cpp
@@ -0,0 +1,53 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MidiMixerVUMeter.h"
+
+#include "gui/widgets/VUMeter.h"
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MidiMixerVUMeter::MidiMixerVUMeter(QWidget *parent,
+ VUMeterType type,
+ int width,
+ int height,
+ const char *name):
+ VUMeter(parent, type, false, false, width, height, VUMeter::Vertical, name)
+{
+ setAlignment(AlignCenter);
+}
+
+void
+MidiMixerVUMeter::meterStart()
+{}
+
+void
+MidiMixerVUMeter::meterStop()
+{}
+
+}
diff --git a/src/gui/studio/MidiMixerVUMeter.h b/src/gui/studio/MidiMixerVUMeter.h
new file mode 100644
index 0000000..f67d922
--- /dev/null
+++ b/src/gui/studio/MidiMixerVUMeter.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIDIMIXERVUMETER_H_
+#define _RG_MIDIMIXERVUMETER_H_
+
+#include "gui/widgets/VUMeter.h"
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiMixerVUMeter : public VUMeter
+{
+public:
+ MidiMixerVUMeter(QWidget *parent = 0,
+ VUMeterType type = Plain,
+ int width = 0,
+ int height = 0,
+ const char *name = 0);
+
+protected:
+ virtual void meterStart();
+ virtual void meterStop();
+
+private:
+ int m_textHeight;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiMixerWindow.cpp b/src/gui/studio/MidiMixerWindow.cpp
new file mode 100644
index 0000000..127db7f
--- /dev/null
+++ b/src/gui/studio/MidiMixerWindow.cpp
@@ -0,0 +1,742 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MidiMixerWindow.h"
+#include <qlayout.h>
+
+#include "sound/Midi.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/seqmanager/SequencerMapper.h"
+#include "gui/widgets/Fader.h"
+#include "gui/widgets/Rotary.h"
+#include "gui/widgets/VUMeter.h"
+#include "MidiMixerVUMeter.h"
+#include "MixerWindow.h"
+#include "sound/MappedEvent.h"
+#include "StudioControl.h"
+#include <kaction.h>
+#include <kmainwindow.h>
+#include <kstdaction.h>
+#include <qaccel.h>
+#include <qcolor.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MidiMixerWindow::MidiMixerWindow(QWidget *parent,
+ RosegardenGUIDoc *document):
+ MixerWindow(parent, document),
+ m_tabFrame(0)
+{
+ // Initial setup
+ //
+ setupTabs();
+
+ KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-play")));
+ KAction *play = new KAction(i18n("&Play"), icon, Key_Enter, this,
+ SIGNAL(play()), actionCollection(), "play");
+ // Alternative shortcut for Play
+ KShortcut playShortcut = play->shortcut();
+ playShortcut.append( KKey(Key_Return + CTRL) );
+ play->setShortcut(playShortcut);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-stop")));
+ new KAction(i18n("&Stop"), icon, Key_Insert, this,
+ SIGNAL(stop()), actionCollection(), "stop");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind")));
+ new KAction(i18n("Re&wind"), icon, Key_End, this,
+ SIGNAL(rewindPlayback()), actionCollection(),
+ "playback_pointer_back_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd")));
+ new KAction(i18n("&Fast Forward"), icon, Key_PageDown, this,
+ SIGNAL(fastForwardPlayback()), actionCollection(),
+ "playback_pointer_forward_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind-end")));
+ new KAction(i18n("Rewind to &Beginning"), icon, 0, this,
+ SIGNAL(rewindPlaybackToBeginning()), actionCollection(),
+ "playback_pointer_start");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd-end")));
+ new KAction(i18n("Fast Forward to &End"), icon, 0, this,
+ SIGNAL(fastForwardPlaybackToEnd()), actionCollection(),
+ "playback_pointer_end");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-record")));
+ new KAction(i18n("&Record"), icon, 0, this,
+ SIGNAL(record()), actionCollection(),
+ "record");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-panic")));
+ new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
+ SIGNAL(panic()), actionCollection(),
+ "panic");
+
+ createGUI("midimixer.rc");
+
+}
+
+void
+MidiMixerWindow::setupTabs()
+{
+ DeviceListConstIterator it;
+ MidiDevice *dev = 0;
+ InstrumentList instruments;
+ InstrumentList::const_iterator iIt;
+ int faderCount = 0, deviceCount = 1;
+
+ if (m_tabFrame)
+ delete m_tabFrame;
+
+ // Setup m_tabFrame
+ //
+ m_tabWidget = new QTabWidget(this);
+ setCentralWidget(m_tabWidget);
+ connect(m_tabWidget, SIGNAL(currentChanged(QWidget *)),
+ this, SLOT(slotCurrentTabChanged(QWidget *)));
+ m_tabWidget->setTabPosition(QTabWidget::Bottom);
+ setCaption(i18n("MIDI Mixer"));
+
+ for (it = m_studio->begin(); it != m_studio->end(); ++it) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev) {
+ // Get the control parameters that are on the IPB (and hence can
+ // be shown here too).
+ //
+ ControlList controls = dev->getIPBControlParameters();
+
+ instruments = dev->getPresentationInstruments();
+
+ // Don't add a frame for empty devices
+ //
+ if (!instruments.size())
+ continue;
+
+ m_tabFrame = new QFrame(m_tabWidget);
+ m_tabFrame->setFrameStyle(QFrame::TabWidgetPanel);
+ m_tabFrame->setMargin(10);
+
+ QGridLayout *mainLayout = new QGridLayout
+ (m_tabFrame, instruments.size() + 4, controls.size() + 4, 5);
+
+ // MIDI Mixer label
+ //
+ //QLabel *label = new QLabel(QString("%1 %2").
+ //arg(strtoqstr(dev->getName()))
+ //.arg(i18n("MIDI Mixer")), m_tabFrame);
+
+ QLabel *label = new QLabel("", m_tabFrame);
+ mainLayout->addMultiCellWidget(label, 0, 0, 0, 16, Qt::AlignCenter);
+
+ // control labels
+ for (unsigned int i = 0; i < controls.size(); ++i) {
+ label = new QLabel(strtoqstr(controls[i].getName()), m_tabFrame);
+ mainLayout->addWidget(label, i + 1, 0, Qt::AlignCenter);
+ }
+
+ // meter label
+ //
+ //label = new QLabel(i18n("Meter"), m_tabFrame);
+ //mainLayout->addWidget(label,
+ //controls.size() + 1, 0, Qt::AlignCenter);
+
+ // volume label
+ label = new QLabel(i18n("Volume"), m_tabFrame);
+ mainLayout->addWidget(label, controls.size() + 2, 0,
+ Qt::AlignCenter);
+
+ // instrument label
+ label = new QLabel(i18n("Instrument"), m_tabFrame);
+ mainLayout->addWidget(label, controls.size() + 3, 0,
+ Qt::AlignCenter);
+
+ int posCount = 1;
+ int firstInstrument = -1;
+
+ for (iIt = instruments.begin(); iIt != instruments.end(); ++iIt) {
+
+ // Add new fader struct
+ //
+ m_faders.push_back(new FaderStruct());
+
+ // Store the first ID
+ //
+ if (firstInstrument == -1)
+ firstInstrument = (*iIt)->getId();
+
+
+ // Add the controls
+ //
+ for (unsigned int i = 0; i < controls.size(); ++i) {
+ QColor knobColour = Qt::white;
+
+ if (controls[i].getColourIndex() > 0) {
+ Colour c =
+ m_document->getComposition().getGeneralColourMap().
+ getColourByIndex(controls[i].getColourIndex());
+
+ knobColour = QColor(c.getRed(),
+ c.getGreen(), c.getBlue());
+ }
+
+ Rotary *controller =
+ new Rotary(m_tabFrame,
+ controls[i].getMin(),
+ controls[i].getMax(),
+ 1.0,
+ 5.0,
+ controls[i].getDefault(),
+ 20,
+ Rotary::NoTicks,
+ false,
+ controls[i].getDefault() == 64); //!!! hacky
+
+ controller->setKnobColour(knobColour);
+
+ connect(controller, SIGNAL(valueChanged(float)),
+ this, SLOT(slotControllerChanged(float)));
+
+ mainLayout->addWidget(controller, i + 1, posCount,
+ Qt::AlignCenter);
+
+ // Store the rotary
+ //
+ m_faders[faderCount]->m_controllerRotaries.push_back(
+ std::pair<MidiByte, Rotary*>
+ (controls[i].getControllerValue(), controller));
+ }
+
+ // Pan rotary
+ //
+ MidiMixerVUMeter *meter =
+ new MidiMixerVUMeter(m_tabFrame,
+ VUMeter::FixedHeightVisiblePeakHold, 6, 30);
+ mainLayout->addWidget(meter, controls.size() + 1,
+ posCount, Qt::AlignCenter);
+ m_faders[faderCount]->m_vuMeter = meter;
+
+ // Volume fader
+ //
+ Fader *fader =
+ new Fader(0, 127, 100, 20, 80, m_tabFrame);
+ mainLayout->addWidget(fader, controls.size() + 2,
+ posCount, Qt::AlignCenter);
+ m_faders[faderCount]->m_volumeFader = fader;
+ //fader->setFader(float((*iIt)->getVolume()));
+
+ // Label
+ //
+ QLabel *idLabel = new QLabel(QString("%1").
+ arg((*iIt)->getId() - firstInstrument + 1),
+ m_tabFrame, "idLabel");
+
+ mainLayout->addWidget(idLabel, controls.size() + 3,
+ posCount, Qt::AlignCenter);
+
+ // store id in struct
+ m_faders[faderCount]->m_id = (*iIt)->getId();
+
+ // Connect them up
+ //
+ connect(fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ // Update all the faders and controllers
+ //
+ slotUpdateInstrument((*iIt)->getId());
+
+ // Increment counters
+ //
+ posCount++;
+ faderCount++;
+ }
+
+ QString name = QString("%1 (%2)").arg(strtoqstr(dev->getName()))
+ .arg(deviceCount++);
+
+ addTab(m_tabFrame, name);
+ }
+ }
+}
+
+void
+MidiMixerWindow::addTab(QWidget *tab, const QString &title)
+{
+ m_tabWidget->addTab(tab, title);
+}
+
+void
+MidiMixerWindow::slotFaderLevelChanged(float value)
+{
+ const QObject *s = sender();
+
+ for (FaderVector::const_iterator it = m_faders.begin();
+ it != m_faders.end(); ++it) {
+ if ((*it)->m_volumeFader == s) {
+ Instrument *instr = m_studio->
+ getInstrumentById((*it)->m_id);
+
+ if (instr) {
+
+ instr->setVolume(MidiByte(value));
+
+ MappedEvent mE((*it)->m_id,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ MidiByte(value));
+ StudioControl::sendMappedEvent(mE);
+
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ int tabIndex = m_tabWidget->currentPageIndex();
+ if (tabIndex < 0)
+ tabIndex = 0;
+ int i = 0;
+ for (DeviceList::const_iterator dit = m_studio->begin();
+ dit != m_studio->end(); ++dit) {
+ RG_DEBUG << "slotFaderLevelChanged: i = " << i << ", tabIndex " << tabIndex << endl;
+ if (!dynamic_cast<MidiDevice*>(*dit))
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+ RG_DEBUG << "slotFaderLevelChanged: device id = " << instr->getDevice()->getId() << ", visible device id " << (*dit)->getId() << endl;
+ if (instr->getDevice()->getId() == (*dit)->getId()) {
+ RG_DEBUG << "slotFaderLevelChanged: sending control device mapped event for channel " << instr->getMidiChannel() << endl;
+ mE.setRecordedChannel(instr->getMidiChannel());
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+ break;
+ }
+ }
+
+ emit instrumentParametersChanged((*it)->m_id);
+ return ;
+ }
+ }
+}
+
+void
+MidiMixerWindow::slotControllerChanged(float value)
+{
+ const QObject *s = sender();
+ unsigned int i = 0, j = 0;
+
+ for (i = 0; i < m_faders.size(); ++i) {
+ for (j = 0; j < m_faders[i]->m_controllerRotaries.size(); ++j) {
+ if (m_faders[i]->m_controllerRotaries[j].second == s)
+ break;
+ }
+
+ // break out on match
+ if (j != m_faders[i]->m_controllerRotaries.size())
+ break;
+ }
+
+ // Don't do anything if we've not matched and got solid values
+ // for i and j
+ //
+ if (i == m_faders.size() || j == m_faders[i]->m_controllerRotaries.size())
+ return ;
+
+ //RG_DEBUG << "MidiMixerWindow::slotControllerChanged - found a controller"
+ //<< endl;
+
+ Instrument *instr = m_studio->getInstrumentById(
+ m_faders[i]->m_id);
+
+ if (instr) {
+
+ //RG_DEBUG << "MidiMixerWindow::slotControllerChanged - "
+ //<< "got instrument to change" << endl;
+
+ if (m_faders[i]->m_controllerRotaries[j].first ==
+ MIDI_CONTROLLER_PAN)
+ instr->setPan(MidiByte(value));
+ else {
+ instr->setControllerValue(m_faders[i]->
+ m_controllerRotaries[j].first,
+ MidiByte(value));
+ }
+
+ MappedEvent mE(m_faders[i]->m_id,
+ MappedEvent::MidiController,
+ m_faders[i]->m_controllerRotaries[j].first,
+ MidiByte(value));
+ StudioControl::sendMappedEvent(mE);
+
+ int tabIndex = m_tabWidget->currentPageIndex();
+ if (tabIndex < 0)
+ tabIndex = 0;
+ int i = 0;
+ for (DeviceList::const_iterator dit = m_studio->begin();
+ dit != m_studio->end(); ++dit) {
+ RG_DEBUG << "slotControllerChanged: i = " << i << ", tabIndex " << tabIndex << endl;
+ if (!dynamic_cast<MidiDevice*>(*dit))
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+ RG_DEBUG << "slotControllerChanged: device id = " << instr->getDevice()->getId() << ", visible device id " << (*dit)->getId() << endl;
+ if (instr->getDevice()->getId() == (*dit)->getId()) {
+ RG_DEBUG << "slotControllerChanged: sending control device mapped event for channel " << instr->getMidiChannel() << endl;
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ mE.setRecordedChannel(instr->getMidiChannel());
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+ }
+
+ emit instrumentParametersChanged(m_faders[i]->m_id);
+ }
+}
+
+void
+MidiMixerWindow::slotUpdateInstrument(InstrumentId id)
+{
+ //RG_DEBUG << "MidiMixerWindow::slotUpdateInstrument - id = " << id << endl;
+
+ DeviceListConstIterator it;
+ MidiDevice *dev = 0;
+ InstrumentList instruments;
+ InstrumentList::const_iterator iIt;
+ int count = 0;
+
+ blockSignals(true);
+
+ for (it = m_studio->begin(); it != m_studio->end(); ++it) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev) {
+ instruments = dev->getPresentationInstruments();
+ ControlList controls = dev->getIPBControlParameters();
+
+ for (iIt = instruments.begin(); iIt != instruments.end(); ++iIt) {
+ // Match and set
+ //
+ if ((*iIt)->getId() == id) {
+ // Set Volume fader
+ //
+ m_faders[count]->m_volumeFader->blockSignals(true);
+ m_faders[count]->m_volumeFader->
+ setFader(float((*iIt)->getVolume()));
+ m_faders[count]->m_volumeFader->blockSignals(false);
+
+ /*
+ StaticControllers &staticControls =
+ (*iIt)->getStaticControllers();
+ RG_DEBUG << "STATIC CONTROLS SIZE = "
+ << staticControls.size() << endl;
+ */
+
+ // Set all controllers for this Instrument
+ //
+ for (unsigned int i = 0; i < controls.size(); ++i) {
+ float value = 0.0;
+
+ m_faders[count]->m_controllerRotaries[i].second->blockSignals(true);
+
+ if (controls[i].getControllerValue() ==
+ MIDI_CONTROLLER_PAN) {
+ m_faders[count]->m_controllerRotaries[i].
+ second->setPosition((*iIt)->getPan());
+ } else {
+ // The ControllerValues might not yet be set on
+ // the actual Instrument so don't always expect
+ // to find one. There might be a hole here for
+ // deleted Controllers to hang around on
+ // Instruments..
+ //
+ try {
+ value = float((*iIt)->getControllerValue
+ (controls[i].getControllerValue()));
+ } catch (std::string s) {
+ /*
+ RG_DEBUG <<
+ "MidiMixerWindow::slotUpdateInstrument - "
+ << "can't match controller "
+ << int(controls[i].
+ getControllerValue()) << " - \""
+ << s << "\"" << endl;
+ */
+ continue;
+ }
+
+ /*
+ RG_DEBUG << "MidiMixerWindow::slotUpdateInstrument"
+ << " - MATCHED "
+ << int(controls[i].getControllerValue())
+ << endl;
+ */
+
+ m_faders[count]->m_controllerRotaries[i].
+ second->setPosition(value);
+ }
+
+ m_faders[count]->m_controllerRotaries[i].second->blockSignals(false);
+ }
+ }
+ count++;
+ }
+ }
+ }
+
+ blockSignals(false);
+}
+
+void
+MidiMixerWindow::updateMeters(SequencerMapper *mapper)
+{
+ for (unsigned int i = 0; i != m_faders.size(); ++i) {
+ LevelInfo info;
+ if (!mapper->
+ getInstrumentLevelForMixer(m_faders[i]->m_id, info))
+ continue;
+ m_faders[i]->m_vuMeter->setLevel(double(info.level / 127.0));
+ RG_DEBUG << "MidiMixerWindow::updateMeters - level " << info.level << endl;
+ }
+}
+
+void
+MidiMixerWindow::updateMonitorMeter(SequencerMapper *)
+{
+ // none here
+}
+
+void
+MidiMixerWindow::slotControllerDeviceEventReceived(MappedEvent *e,
+ const void *preferredCustomer)
+{
+ if (preferredCustomer != this)
+ return ;
+ RG_DEBUG << "MidiMixerWindow::slotControllerDeviceEventReceived: this one's for me" << endl;
+ raise();
+
+ // get channel number n from event
+ // get nth instrument on current tab
+
+ if (e->getType() != MappedEvent::MidiController)
+ return ;
+ unsigned int channel = e->getRecordedChannel();
+ MidiByte controller = e->getData1();
+ MidiByte value = e->getData2();
+
+ int tabIndex = m_tabWidget->currentPageIndex();
+
+ int i = 0;
+
+ for (DeviceList::const_iterator it = m_studio->begin();
+ it != m_studio->end(); ++it) {
+
+ MidiDevice *dev =
+ dynamic_cast<MidiDevice*>(*it);
+
+ if (!dev)
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+
+ InstrumentList instruments = dev->getPresentationInstruments();
+
+ for (InstrumentList::const_iterator iIt =
+ instruments.begin(); iIt != instruments.end(); ++iIt) {
+
+ Instrument *instrument = *iIt;
+
+ if (instrument->getMidiChannel() != channel)
+ continue;
+
+ switch (controller) {
+
+ case MIDI_CONTROLLER_VOLUME:
+ RG_DEBUG << "Setting volume for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setVolume(value);
+ break;
+
+ case MIDI_CONTROLLER_PAN:
+ RG_DEBUG << "Setting pan for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setPan(value);
+ break;
+
+ default: {
+ ControlList cl = dev->getIPBControlParameters();
+ for (ControlList::const_iterator i = cl.begin();
+ i != cl.end(); ++i) {
+ if ((*i).getControllerValue() == controller) {
+ RG_DEBUG << "Setting controller " << controller << " for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setControllerValue(controller, value);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MidiByte(controller),
+ MidiByte(value));
+ StudioControl::sendMappedEvent(mE);
+
+ slotUpdateInstrument(instrument->getId());
+ emit instrumentParametersChanged(instrument->getId());
+ }
+
+ break;
+ }
+}
+
+void
+MidiMixerWindow::slotCurrentTabChanged(QWidget *)
+{
+ sendControllerRefresh();
+}
+
+void
+MidiMixerWindow::sendControllerRefresh()
+{
+ //!!! need to know if we have a current external controller device,
+ // as this is expensive
+
+ int tabIndex = m_tabWidget->currentPageIndex();
+ RG_DEBUG << "MidiMixerWindow::slotCurrentTabChanged: current is " << tabIndex << endl;
+
+ if (tabIndex < 0)
+ return ;
+
+ int i = 0;
+
+ for (DeviceList::const_iterator dit = m_studio->begin();
+ dit != m_studio->end(); ++dit) {
+
+ MidiDevice *dev = dynamic_cast<MidiDevice*>(*dit);
+ RG_DEBUG << "device is " << (*dit)->getId() << ", dev " << dev << endl;
+
+ if (!dev)
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+
+ InstrumentList instruments = dev->getPresentationInstruments();
+ ControlList controls = dev->getIPBControlParameters();
+
+ RG_DEBUG << "device has " << instruments.size() << " presentation instruments, " << dev->getAllInstruments().size() << " instruments " << endl;
+
+ for (InstrumentList::const_iterator iIt =
+ instruments.begin(); iIt != instruments.end(); ++iIt) {
+
+ Instrument *instrument = *iIt;
+ int channel = instrument->getMidiChannel();
+
+ RG_DEBUG << "instrument is " << instrument->getId() << endl;
+
+ for (ControlList::const_iterator cIt =
+ controls.begin(); cIt != controls.end(); ++cIt) {
+
+ int controller = (*cIt).getControllerValue();
+ int value;
+ if (controller == MIDI_CONTROLLER_PAN) {
+ value = instrument->getPan();
+ } else {
+ try {
+ value = instrument->getControllerValue(controller);
+ } catch (std::string s) {
+ std::cerr << "Exception in MidiMixerWindow::currentChanged: " << s << " (controller " << controller << ", instrument " << instrument->getId() << ")" << std::endl;
+ value = 0;
+ }
+ }
+
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ controller, value);
+ mE.setRecordedChannel(channel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ instrument->getVolume());
+ mE.setRecordedChannel(channel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ RG_DEBUG << "sending controller mapped event for channel " << channel << ", volume " << instrument->getVolume() << endl;
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ break;
+ }
+}
+
+void
+MidiMixerWindow::slotSynchronise()
+{
+ RG_DEBUG << "MidiMixer::slotSynchronise" << endl;
+ //setupTabs();
+}
+
+}
+#include "MidiMixerWindow.moc"
diff --git a/src/gui/studio/MidiMixerWindow.h b/src/gui/studio/MidiMixerWindow.h
new file mode 100644
index 0000000..d90dc55
--- /dev/null
+++ b/src/gui/studio/MidiMixerWindow.h
@@ -0,0 +1,125 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIDIMIXERWINDOW_H_
+#define _RG_MIDIMIXERWINDOW_H_
+
+#include "base/MidiProgram.h"
+#include "MixerWindow.h"
+#include <vector>
+
+
+class QWidget;
+class QTabWidget;
+class QString;
+class QFrame;
+
+
+namespace Rosegarden
+{
+
+class SequencerMapper;
+class Rotary;
+class RosegardenGUIDoc;
+class MidiMixerVUMeter;
+class MappedEvent;
+class Fader;
+
+
+class MidiMixerWindow : public MixerWindow
+{
+ Q_OBJECT
+
+public:
+ MidiMixerWindow(QWidget *parent, RosegardenGUIDoc *document);
+
+ /**
+ * Setup the tabs on the Mixer according to the Studio
+ */
+ void setupTabs();
+
+ /*
+ * Update the VU meters
+ */
+ void updateMeters(SequencerMapper *mapper);
+ void updateMonitorMeter(SequencerMapper *mapper);
+
+public slots:
+ void slotSynchronise(); // synchronise with updated studio
+
+ void slotControllerDeviceEventReceived(MappedEvent *,
+ const void *);
+
+ void slotCurrentTabChanged(QWidget *);
+
+signals:
+ void play();
+ void stop();
+ void fastForwardPlayback();
+ void rewindPlayback();
+ void fastForwardPlaybackToEnd();
+ void rewindPlaybackToBeginning();
+ void record();
+ void panic();
+
+ // to be redirected to the instrument parameter box if necessary
+ void instrumentParametersChanged(InstrumentId);
+
+protected slots:
+ void slotUpdateInstrument(InstrumentId);
+
+ //void slotPanChanged(float);
+ void slotFaderLevelChanged(float);
+ void slotControllerChanged(float);
+
+protected:
+ void addTab(QWidget *tab, const QString &title);
+
+ virtual void sendControllerRefresh();
+
+ QTabWidget *m_tabWidget;
+
+ struct FaderStruct {
+
+ FaderStruct():m_id(0), m_vuMeter(0), m_volumeFader(0) {}
+
+ InstrumentId m_id;
+ MidiMixerVUMeter *m_vuMeter;
+ Fader *m_volumeFader;
+ std::vector<std::pair<MidiByte, Rotary*> > m_controllerRotaries;
+
+ };
+
+ typedef std::vector<FaderStruct*> FaderVector;
+ FaderVector m_faders;
+
+ QFrame *m_tabFrame;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiProgramsEditor.cpp b/src/gui/studio/MidiProgramsEditor.cpp
new file mode 100644
index 0000000..8f81a04
--- /dev/null
+++ b/src/gui/studio/MidiProgramsEditor.cpp
@@ -0,0 +1,631 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MidiProgramsEditor.h"
+#include "MidiBankListViewItem.h"
+#include "NameSetEditor.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "BankEditorDialog.h"
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "gui/widgets/RosegardenPopupMenu.h"
+#include <kcompletion.h>
+#include <kglobal.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kstddirs.h>
+#include <qcheckbox.h>
+#include <qcursor.h>
+#include <qfile.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qobjectlist.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+#include <algorithm>
+
+namespace Rosegarden
+{
+
+MidiProgramsEditor::MidiProgramsEditor(BankEditorDialog* bankEditor,
+ QWidget* parent,
+ const char* name)
+ : NameSetEditor(bankEditor,
+ i18n("Bank and Program details"),
+ parent, name, i18n("Programs"), true),
+ m_device(0),
+ m_bankList(bankEditor->getBankList()),
+ m_programList(bankEditor->getProgramList()),
+ m_oldBank(false, 0, 0)
+{
+ QWidget *additionalWidget = makeAdditionalWidget(m_mainFrame);
+ if (additionalWidget) {
+ m_mainLayout->addMultiCellWidget(additionalWidget, 0, 2, 0, 2);
+ }
+}
+
+QWidget *
+MidiProgramsEditor::makeAdditionalWidget(QWidget *parent)
+{
+ QFrame *frame = new QFrame(parent);
+
+ m_percussion = new QCheckBox(frame);
+ m_msb = new QSpinBox(frame);
+ m_lsb = new QSpinBox(frame);
+
+ QGridLayout *gridLayout = new QGridLayout(frame,
+ 3, // rows
+ 2, // cols
+ 2); // margin
+
+ gridLayout->addWidget(new QLabel(i18n("Percussion"), frame),
+ 0, 0, AlignLeft);
+ gridLayout->addWidget(m_percussion, 0, 1, AlignLeft);
+ connect(m_percussion, SIGNAL(clicked()),
+ this, SLOT(slotNewPercussion()));
+
+ gridLayout->addWidget(new QLabel(i18n("MSB Value"), frame),
+ 1, 0, AlignLeft);
+ m_msb->setMinValue(0);
+ m_msb->setMaxValue(127);
+ gridLayout->addWidget(m_msb, 1, 1, AlignLeft);
+
+ QToolTip::add
+ (m_msb,
+ i18n("Selects a MSB controller Bank number (MSB/LSB pairs are always unique for any Device)"));
+
+ QToolTip::add
+ (m_lsb,
+ i18n("Selects a LSB controller Bank number (MSB/LSB pairs are always unique for any Device)"));
+
+ connect(m_msb, SIGNAL(valueChanged(int)),
+ this, SLOT(slotNewMSB(int)));
+
+ gridLayout->addWidget(new QLabel(i18n("LSB Value"), frame),
+ 2, 0, AlignLeft);
+ m_lsb->setMinValue(0);
+ m_lsb->setMaxValue(127);
+ gridLayout->addWidget(m_lsb, 2, 1, AlignLeft);
+
+ connect(m_lsb, SIGNAL(valueChanged(int)),
+ this, SLOT(slotNewLSB(int)));
+
+ return frame;
+}
+
+ProgramList
+MidiProgramsEditor::getBankSubset(const MidiBank &bank)
+{
+ ProgramList program;
+ ProgramList::iterator it;
+
+ for (it = m_programList.begin(); it != m_programList.end(); it++) {
+ if (it->getBank() == bank)
+ program.push_back(*it);
+ }
+
+ return program;
+}
+
+MidiBank*
+MidiProgramsEditor::getCurrentBank()
+{
+ return m_currentBank;
+}
+
+void
+MidiProgramsEditor::modifyCurrentPrograms(const MidiBank &oldBank,
+ const MidiBank &newBank)
+{
+ ProgramList::iterator it;
+
+ for (it = m_programList.begin(); it != m_programList.end(); it++) {
+ if (it->getBank() == oldBank) {
+ *it = MidiProgram(newBank, it->getProgram(), it->getName());
+ }
+ }
+}
+
+void
+MidiProgramsEditor::clearAll()
+{
+ blockAllSignals(true);
+
+ for (unsigned int i = 0; i < m_names.size(); ++i)
+ m_names[i]->clear();
+
+ setTitle(i18n("Bank and Program details"));
+
+ m_percussion->setChecked(false);
+ m_msb->setValue(0);
+ m_lsb->setValue(0);
+ m_librarian->clear();
+ m_librarianEmail->clear();
+ m_currentBank = 0;
+ setEnabled(false);
+
+ blockAllSignals(false);
+}
+
+void
+MidiProgramsEditor::populate(QListViewItem* item)
+{
+ RG_DEBUG << "MidiProgramsEditor::populate\n";
+
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(item);
+ if (!bankItem) {
+ RG_DEBUG << "MidiProgramsEditor::populate : not a bank item - returning\n";
+ return ;
+ }
+
+ DeviceId deviceId = bankItem->getDeviceId();
+ m_device = m_bankEditor->getMidiDevice(deviceId);
+ if (!m_device)
+ return ;
+
+ setEnabled(true);
+
+ setBankName(item->text(0));
+
+ RG_DEBUG << "MidiProgramsEditor::populate : bankItem->getBank = "
+ << bankItem->getBank() << endl;
+
+ m_currentBank = &(m_bankList[bankItem->getBank()]); // m_device->getBankByIndex(bankItem->getBank());
+
+ blockAllSignals(true);
+
+ // set the bank values
+ m_percussion->setChecked(m_currentBank->isPercussion());
+ m_msb->setValue(m_currentBank->getMSB());
+ m_lsb->setValue(m_currentBank->getLSB());
+
+ m_oldBank = *m_currentBank;
+
+ // Librarian details
+ //
+ m_librarian->setText(strtoqstr(m_device->getLibrarianName()));
+ m_librarianEmail->setText(strtoqstr(m_device->getLibrarianEmail()));
+
+ ProgramList programSubset = getBankSubset(*m_currentBank);
+ ProgramList::iterator it;
+
+ QPixmap noKeyPixmap, keyPixmap;
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QString file = pixmapDir + "/toolbar/key-white.png";
+ if (QFile(file).exists())
+ noKeyPixmap = QPixmap(file);
+ file = pixmapDir + "/toolbar/key-green.png";
+ if (QFile(file).exists())
+ keyPixmap = QPixmap(file);
+
+ bool haveKeyMappings = m_currentBank->isPercussion()
+ && (m_device->getKeyMappings().size() > 0);
+
+ for (unsigned int i = 0; i < m_names.size(); i++) {
+ m_names[i]->clear();
+ getEntryButton(i)->setEnabled(haveKeyMappings);
+ getEntryButton(i)->setPixmap(noKeyPixmap);
+ QToolTip::remove
+ ( getEntryButton(i) );
+
+ for (it = programSubset.begin(); it != programSubset.end(); it++) {
+ if (it->getProgram() == i) {
+
+ QString programName = strtoqstr(it->getName());
+ m_completion.addItem(programName);
+ m_names[i]->setText(programName);
+
+ if (m_device->getKeyMappingForProgram(*it)) {
+ getEntryButton(i)->setPixmap(keyPixmap);
+ QToolTip::add
+ (getEntryButton(i),
+ i18n("Key Mapping: %1").arg(
+ strtoqstr(m_device->getKeyMappingForProgram(*it)->getName())));
+ }
+
+ break;
+ }
+ }
+
+ // show start of label
+ m_names[i]->setCursorPosition(0);
+ }
+
+ blockAllSignals(false);
+}
+
+void
+MidiProgramsEditor::reset()
+{
+ m_percussion->blockSignals(true);
+ m_msb->blockSignals(true);
+ m_lsb->blockSignals(true);
+
+ m_percussion->setChecked(m_oldBank.isPercussion());
+ m_msb->setValue(m_oldBank.getMSB());
+ m_lsb->setValue(m_oldBank.getLSB());
+
+ if (m_currentBank) {
+ modifyCurrentPrograms(*m_currentBank, m_oldBank);
+ *m_currentBank = m_oldBank;
+ }
+
+ m_percussion->blockSignals(false);
+ m_msb->blockSignals(false);
+ m_lsb->blockSignals(false);
+}
+
+void
+MidiProgramsEditor::slotNewPercussion()
+{
+ RG_DEBUG << "MidiProgramsEditor::slotNewPercussion" << endl;
+ bool percussion = m_percussion->isChecked();
+ m_percussion->blockSignals(true);
+ if (banklistContains(MidiBank(percussion, m_msb->value(), m_lsb->value()))) {
+ RG_DEBUG << "MidiProgramsEditor::slotNewPercussion: calling setChecked(" << !percussion << ")" << endl;
+ m_percussion->setChecked(!percussion);
+ } else {
+ MidiBank newBank(percussion,
+ m_msb->value(),
+ m_lsb->value());
+ modifyCurrentPrograms(*getCurrentBank(), newBank);
+ *getCurrentBank() = newBank;
+ }
+ m_percussion->blockSignals(false);
+ m_bankEditor->setModified(true);
+}
+
+void
+MidiProgramsEditor::slotNewMSB(int value)
+{
+ RG_DEBUG << "MidiProgramsEditor::slotNewMSB(" << value << ")\n";
+
+ m_msb->blockSignals(true);
+
+ int msb;
+
+ try {
+ msb = ensureUniqueMSB(value, value > getCurrentBank()->getMSB());
+ } catch (bool) {
+ msb = getCurrentBank()->getMSB();
+ }
+
+ MidiBank newBank(m_percussion->isChecked(),
+ msb,
+ m_lsb->value());
+
+ modifyCurrentPrograms(*getCurrentBank(), newBank);
+
+ m_msb->setValue(msb);
+ *getCurrentBank() = newBank;
+
+ m_msb->blockSignals(false);
+
+ m_bankEditor->setModified(true);
+}
+
+void
+MidiProgramsEditor::slotNewLSB(int value)
+{
+ RG_DEBUG << "MidiProgramsEditor::slotNewLSB(" << value << ")\n";
+
+ m_lsb->blockSignals(true);
+
+ int lsb;
+
+ try {
+ lsb = ensureUniqueLSB(value, value > getCurrentBank()->getLSB());
+ } catch (bool) {
+ lsb = getCurrentBank()->getLSB();
+ }
+
+ MidiBank newBank(m_percussion->isChecked(),
+ m_msb->value(),
+ lsb);
+
+ modifyCurrentPrograms(*getCurrentBank(), newBank);
+
+ m_lsb->setValue(lsb);
+ *getCurrentBank() = newBank;
+
+ m_lsb->blockSignals(false);
+
+ m_bankEditor->setModified(true);
+}
+
+struct ProgramCmp
+{
+ bool operator()(const Rosegarden::MidiProgram &p1,
+ const Rosegarden::MidiProgram &p2)
+ {
+ if (p1.getProgram() == p2.getProgram()) {
+ const Rosegarden::MidiBank &b1(p1.getBank());
+ const Rosegarden::MidiBank &b2(p2.getBank());
+ if (b1.getMSB() == b2.getMSB())
+ if (b1.getLSB() == b2.getLSB())
+ return ((b1.isPercussion() ? 1 : 0) < (b2.isPercussion() ? 1 : 0));
+ else return (b1.getLSB() < b2.getLSB());
+ else return (b1.getMSB() < b2.getMSB());
+ } else return (p1.getProgram() < p2.getProgram());
+ }
+};
+
+void
+MidiProgramsEditor::slotNameChanged(const QString& programName)
+{
+ const KLineEdit* lineEdit = dynamic_cast<const KLineEdit*>(sender());
+ if (!lineEdit) {
+ RG_DEBUG << "MidiProgramsEditor::slotProgramChanged() : %%% ERROR - signal sender is not a KLineEdit\n";
+ return ;
+ }
+
+ QString senderName = sender()->name();
+
+ // Adjust value back to zero rated
+ //
+ unsigned int id = senderName.toUInt() - 1;
+
+ RG_DEBUG << "MidiProgramsEditor::slotNameChanged("
+ << programName << ") : id = " << id << endl;
+
+ MidiProgram *program = getProgram(*getCurrentBank(), id);
+
+ if (program == 0) {
+ // Do nothing if program name is empty
+ if (programName.isEmpty())
+ return ;
+
+ program = new MidiProgram(*getCurrentBank(), id);
+ m_programList.push_back(*program);
+
+ // Sort the program list by id
+ std::sort(m_programList.begin(), m_programList.end(), ProgramCmp());
+
+ // Now, get with the program
+ //
+ program = getProgram(*getCurrentBank(), id);
+ } else {
+ // If we've found a program and the label is now empty
+ // then remove it from the program list.
+ //
+ if (programName.isEmpty()) {
+ ProgramList::iterator it = m_programList.begin();
+ ProgramList tmpProg;
+
+ for (; it != m_programList.end(); it++) {
+ if (((unsigned int)it->getProgram()) == id) {
+ m_programList.erase(it);
+ m_bankEditor->setModified(true);
+ RG_DEBUG << "deleting empty program (" << id << ")" << endl;
+ return ;
+ }
+ }
+ }
+ }
+
+ if (qstrtostr(programName) != program->getName()) {
+ program->setName(qstrtostr(programName));
+ m_bankEditor->setModified(true);
+ }
+}
+
+void
+MidiProgramsEditor::slotEntryButtonPressed()
+{
+ QPushButton* button = dynamic_cast<QPushButton*>(const_cast<QObject *>(sender()));
+ if (!button) {
+ RG_DEBUG << "MidiProgramsEditor::slotEntryButtonPressed() : %%% ERROR - signal sender is not a QPushButton\n";
+ return ;
+ }
+
+ QString senderName = button->name();
+
+ if (!m_device)
+ return ;
+
+ const KeyMappingList &kml = m_device->getKeyMappings();
+ if (kml.empty())
+ return ;
+
+ // Adjust value back to zero rated
+ //
+ unsigned int id = senderName.toUInt() - 1;
+ MidiProgram *program = getProgram(*getCurrentBank(), id);
+ if (!program)
+ return ;
+ m_currentMenuProgram = id;
+
+ RosegardenPopupMenu *menu = new RosegardenPopupMenu(button);
+
+ const MidiKeyMapping *currentMapping =
+ m_device->getKeyMappingForProgram(*program);
+ int currentEntry = 0;
+
+ menu->insertItem(i18n("<no key mapping>"), this,
+ SLOT(slotEntryMenuItemSelected(int)), 0, 0);
+ menu->setItemParameter(0, 0);
+
+ for (int i = 0; i < kml.size(); ++i) {
+ menu->insertItem(strtoqstr(kml[i].getName()),
+ this, SLOT(slotEntryMenuItemSelected(int)),
+ 0, i + 1);
+ menu->setItemParameter(i + 1, i + 1);
+ if (currentMapping && (kml[i] == *currentMapping))
+ currentEntry = i + 1;
+ }
+
+ int itemHeight = menu->itemHeight(0) + 2;
+ QPoint pos = QCursor::pos();
+
+ pos.rx() -= 10;
+ pos.ry() -= (itemHeight / 2 + currentEntry * itemHeight);
+
+ menu->popup(pos);
+}
+
+void
+MidiProgramsEditor::slotEntryMenuItemSelected(int i)
+{
+ if (!m_device)
+ return ;
+
+ const KeyMappingList &kml = m_device->getKeyMappings();
+ if (kml.empty())
+ return ;
+
+ MidiProgram *program = getProgram(*getCurrentBank(), m_currentMenuProgram);
+ if (!program)
+ return ;
+
+ std::string newMapping;
+
+ if (i == 0) { // no key mapping
+ newMapping = "";
+ } else {
+ --i;
+ if (i < kml.size()) {
+ newMapping = kml[i].getName();
+ }
+ }
+
+ m_device->setKeyMappingForProgram(*program, newMapping);
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ bool haveKeyMappings = (m_device->getKeyMappings().size() > 0);
+ QPushButton *btn = getEntryButton(m_currentMenuProgram);
+
+ if (newMapping.empty()) {
+ QString file = pixmapDir + "/toolbar/key-white.png";
+ if (QFile(file).exists()) {
+ btn->setPixmap(QPixmap(file));
+ }
+ QToolTip::remove
+ (btn);
+ } else {
+ QString file = pixmapDir + "/toolbar/key-green.png";
+ if (QFile(file).exists()) {
+ btn->setPixmap(QPixmap(file));
+ }
+ QToolTip::add
+ (btn, i18n("Key Mapping: %1").arg(strtoqstr(newMapping)));
+ }
+ btn->setEnabled(haveKeyMappings);
+}
+
+int
+MidiProgramsEditor::ensureUniqueMSB(int msb, bool ascending)
+{
+ int newMSB = msb;
+ while (banklistContains(MidiBank(m_percussion->isChecked(),
+ newMSB, m_lsb->value()))
+ && newMSB < 128
+ && newMSB > -1)
+ if (ascending)
+ newMSB++;
+ else
+ newMSB--;
+
+ if (newMSB == -1 || newMSB == 128)
+ throw false;
+
+ return newMSB;
+}
+
+int
+MidiProgramsEditor::ensureUniqueLSB(int lsb, bool ascending)
+{
+ int newLSB = lsb;
+ while (banklistContains(MidiBank(m_percussion->isChecked(),
+ m_msb->value(), newLSB))
+ && newLSB < 128
+ && newLSB > -1)
+ if (ascending)
+ newLSB++;
+ else
+ newLSB--;
+
+ if (newLSB == -1 || newLSB == 128)
+ throw false;
+
+ return newLSB;
+}
+
+bool
+MidiProgramsEditor::banklistContains(const MidiBank &bank)
+{
+ BankList::iterator it;
+
+ for (it = m_bankList.begin(); it != m_bankList.end(); it++)
+ if (*it == bank)
+ return true;
+
+ return false;
+}
+
+MidiProgram*
+MidiProgramsEditor::getProgram(const MidiBank &bank, int programNo)
+{
+ ProgramList::iterator it = m_programList.begin();
+
+ for (; it != m_programList.end(); it++) {
+ if (it->getBank() == bank && it->getProgram() == programNo)
+ return &(*it);
+ }
+
+ return 0;
+}
+
+void
+MidiProgramsEditor::setBankName(const QString& s)
+{
+ setTitle(s);
+}
+
+void MidiProgramsEditor::blockAllSignals(bool block)
+{
+ const QObjectList* allChildren = queryList("KLineEdit", "[0-9]+");
+ QObjectListIt it(*allChildren);
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ obj->blockSignals(block);
+ ++it;
+ }
+
+ m_msb->blockSignals(block);
+ m_lsb->blockSignals(block);
+}
+
+}
+#include "MidiProgramsEditor.moc"
diff --git a/src/gui/studio/MidiProgramsEditor.h b/src/gui/studio/MidiProgramsEditor.h
new file mode 100644
index 0000000..d0ef565
--- /dev/null
+++ b/src/gui/studio/MidiProgramsEditor.h
@@ -0,0 +1,119 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIDIPROGRAMSEDITOR_H_
+#define _RG_MIDIPROGRAMSEDITOR_H_
+
+#include "base/MidiProgram.h"
+#include "NameSetEditor.h"
+
+
+class QWidget;
+class QString;
+class QSpinBox;
+class QListViewItem;
+class QCheckBox;
+class BankList;
+
+
+namespace Rosegarden
+{
+
+class MidiProgram;
+class MidiDevice;
+class BankEditorDialog;
+
+
+class MidiProgramsEditor : public NameSetEditor
+{
+ Q_OBJECT
+public:
+ MidiProgramsEditor(BankEditorDialog *bankEditor,
+ QWidget *parent,
+ const char *name = 0);
+
+ void clearAll();
+ void populate(QListViewItem*);
+ void reset();
+
+public slots:
+
+ // Check that any new MSB/LSB combination is unique for this device
+ //
+ void slotNewMSB(int value);
+ void slotNewLSB(int value);
+ void slotNewPercussion(); // gets value from checkbox
+
+ virtual void slotNameChanged(const QString &);
+ virtual void slotEntryButtonPressed();
+ void slotEntryMenuItemSelected(int);
+
+protected:
+
+ MidiBank* getCurrentBank();
+
+ int ensureUniqueMSB(int msb, bool ascending);
+ int ensureUniqueLSB(int lsb, bool ascending);
+
+ // Does the banklist contain this combination already?
+ //
+ bool banklistContains(const MidiBank &);
+
+ ProgramList getBankSubset(const MidiBank &);
+
+ /// Set the currently loaded programs to new MSB and LSB
+ void modifyCurrentPrograms(const MidiBank &oldBank,
+ const MidiBank &newBank);
+
+ // Get a program (pointer into program list) for modification
+ //
+ MidiProgram* getProgram(const MidiBank &bank, int program);
+
+ void setBankName(const QString& s);
+
+ virtual QWidget *makeAdditionalWidget(QWidget *parent);
+
+ void blockAllSignals(bool block);
+
+ //--------------- Data members ---------------------------------
+ QCheckBox *m_percussion;
+ QSpinBox *m_msb;
+ QSpinBox *m_lsb;
+
+ MidiDevice *m_device;
+
+ MidiBank *m_currentBank;
+ BankList &m_bankList;
+ ProgramList &m_programList;
+
+ MidiBank m_oldBank;
+
+ unsigned int m_currentMenuProgram;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MixerWindow.cpp b/src/gui/studio/MixerWindow.cpp
new file mode 100644
index 0000000..2a65024
--- /dev/null
+++ b/src/gui/studio/MixerWindow.cpp
@@ -0,0 +1,75 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "MixerWindow.h"
+
+#include "misc/Debug.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kmainwindow.h>
+#include <qaccel.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MixerWindow::MixerWindow(QWidget *parent,
+ RosegardenGUIDoc *document) :
+ KMainWindow(parent, "mixerwindow"),
+ m_document(document),
+ m_studio(&document->getStudio()),
+ m_currentId(0)
+{
+ m_accelerators = new QAccel(this);
+}
+
+void
+MixerWindow::closeEvent(QCloseEvent *e)
+{
+ RG_DEBUG << "MixerWindow::closeEvent()\n";
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+MixerWindow::windowActivationChange(bool)
+{
+ if (isActiveWindow()) {
+ emit windowActivated();
+ sendControllerRefresh();
+ }
+}
+
+void
+MixerWindow::slotClose()
+{
+ RG_DEBUG << "MixerWindow::slotClose()\n";
+ close();
+}
+
+}
+#include "MixerWindow.moc"
diff --git a/src/gui/studio/MixerWindow.h b/src/gui/studio/MixerWindow.h
new file mode 100644
index 0000000..9e5f9cf
--- /dev/null
+++ b/src/gui/studio/MixerWindow.h
@@ -0,0 +1,77 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_MIXERWINDOW_H_
+#define _RG_MIXERWINDOW_H_
+
+#include "base/MidiProgram.h"
+#include <kmainwindow.h>
+
+
+class QWidget;
+class QCloseEvent;
+class QAccel;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+
+
+class MixerWindow: public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ MixerWindow(QWidget *parent, RosegardenGUIDoc *document);
+ QAccel* getAccelerators() { return m_accelerators; }
+
+signals:
+ void closing();
+ void windowActivated();
+
+protected slots:
+ void slotClose();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+ virtual void windowActivationChange(bool);
+
+ virtual void sendControllerRefresh() = 0;
+
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+ InstrumentId m_currentId;
+
+ QAccel *m_accelerators;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/NameSetEditor.cpp b/src/gui/studio/NameSetEditor.cpp
new file mode 100644
index 0000000..8dadd0c
--- /dev/null
+++ b/src/gui/studio/NameSetEditor.cpp
@@ -0,0 +1,190 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "NameSetEditor.h"
+#include "BankEditorDialog.h"
+#include <kcompletion.h>
+#include <kglobalsettings.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+#include <iostream>
+
+namespace Rosegarden
+{
+
+NameSetEditor::NameSetEditor(BankEditorDialog* bankEditor,
+ QString title,
+ QWidget* parent,
+ const char* name,
+ QString headingPrefix,
+ bool showEntryButtons)
+ : QVGroupBox(title, parent, name),
+ m_bankEditor(bankEditor),
+ m_mainFrame(new QFrame(this))
+{
+ m_mainLayout = new QGridLayout(m_mainFrame,
+ 4, // rows
+ 6, // cols
+ 2); // margin
+
+ // Librarian
+ //
+ QGroupBox *groupBox = new QGroupBox(2,
+ Qt::Horizontal,
+ i18n("Librarian"),
+ m_mainFrame);
+ m_mainLayout->addMultiCellWidget(groupBox, 0, 2, 3, 5);
+
+ new QLabel(i18n("Name"), groupBox);
+ m_librarian = new QLabel(groupBox);
+
+ new QLabel(i18n("Email"), groupBox);
+ m_librarianEmail = new QLabel(groupBox);
+
+ QToolTip::add
+ (groupBox,
+ i18n("The librarian maintains the Rosegarden device data for this device.\nIf you've made modifications to suit your own device, it might be worth\nliaising with the librarian in order to publish your information for the benefit\nof others."));
+
+ QTabWidget* tabw = new QTabWidget(this);
+
+ tabw->setMargin(10);
+
+ QHBox *h;
+ QVBox *v;
+ QHBox *numBox;
+
+ unsigned int tabs = 4;
+ unsigned int cols = 2;
+ unsigned int labelId = 0;
+
+ for (unsigned int tab = 0; tab < tabs; ++tab) {
+ h = new QHBox(tabw);
+
+ for (unsigned int col = 0; col < cols; ++col) {
+ v = new QVBox(h);
+
+ for (unsigned int row = 0; row < 128 / (tabs*cols); ++row) {
+ numBox = new QHBox(v);
+ QString numberText = QString("%1").arg(labelId + 1);
+
+ if (tab == 0 && col == 0 && row == 0) {
+ // Initial label; button to adjust whether labels start at 0 or 1
+ m_initialLabel = new QPushButton(numberText, numBox);
+ connect(m_initialLabel,
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotToggleInitialLabel()));
+ } else {
+ QLabel *label = new QLabel(numberText, numBox);
+ label->setFixedWidth(40);
+ label->setAlignment(AlignCenter);
+ m_labels.push_back(label);
+ }
+
+
+
+ if (showEntryButtons) {
+ QPushButton *button = new QPushButton("", numBox, numberText);
+ button->setMaximumWidth(40);
+ button->setMaximumHeight(20);
+ button->setFlat(true);
+ connect(button, SIGNAL(clicked()),
+ this, SLOT(slotEntryButtonPressed()));
+ m_entryButtons.push_back(button);
+ }
+
+ KLineEdit* lineEdit = new KLineEdit(numBox, numberText);
+ lineEdit->setMinimumWidth(110);
+ lineEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
+ lineEdit->setCompletionObject(&m_completion);
+ m_names.push_back(lineEdit);
+
+ connect(m_names[labelId],
+ SIGNAL(textChanged(const QString&)),
+ this,
+ SLOT(slotNameChanged(const QString&)));
+
+ ++labelId;
+ }
+ }
+
+ tabw->addTab(h,
+ (tab == 0 ? headingPrefix + QString(" %1 - %2") :
+ QString("%1 - %2")).
+ arg(tab * (128 / tabs) + 1).
+ arg((tab + 1) * (128 / tabs)));
+ }
+
+ m_initialLabel->setMaximumSize(m_labels.front()->size());
+}
+
+void
+NameSetEditor::slotToggleInitialLabel()
+{
+ QString initial = m_initialLabel->text();
+
+ // strip some unrequested nice-ification.. urg!
+ if (initial.startsWith("&")) {
+ initial = initial.right(initial.length() - 1);
+ }
+
+ bool ok;
+ unsigned index = initial.toUInt(&ok);
+
+ if (!ok) {
+ std::cerr << "conversion of '"
+ << initial.ascii()
+ << "' to number failed"
+ << std::endl;
+ return ;
+ }
+
+ if (index == 0)
+ index = 1;
+ else
+ index = 0;
+
+ m_initialLabel->setText(QString("%1").arg(index++));
+ for (std::vector<QLabel*>::iterator it( m_labels.begin() );
+ it != m_labels.end();
+ ++it) {
+ (*it)->setText(QString("%1").arg(index++));
+ }
+}
+
+}
+#include "NameSetEditor.moc"
diff --git a/src/gui/studio/NameSetEditor.h b/src/gui/studio/NameSetEditor.h
new file mode 100644
index 0000000..e1e1476
--- /dev/null
+++ b/src/gui/studio/NameSetEditor.h
@@ -0,0 +1,90 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_NAMESETEDITOR_H_
+#define _RG_NAMESETEDITOR_H_
+
+#include <kcompletion.h>
+#include <qstring.h>
+#include <qvgroupbox.h>
+#include <vector>
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QLabel;
+class QGridLayout;
+class QFrame;
+class KLineEdit;
+
+
+namespace Rosegarden
+{
+
+class BankEditorDialog;
+
+
+class NameSetEditor : public QVGroupBox
+{
+ Q_OBJECT
+public:
+ virtual void clearAll() = 0;
+
+ virtual void populate(QListViewItem *) = 0;
+ virtual void reset() = 0;
+
+public slots:
+ virtual void slotNameChanged(const QString&) = 0;
+ virtual void slotEntryButtonPressed() = 0;
+ void slotToggleInitialLabel();
+
+protected:
+ NameSetEditor(BankEditorDialog *bankEditor,
+ QString title,
+ QWidget *parent,
+ const char *name,
+ QString headingPrefix = "",
+ bool showEntryButtons = false);
+
+ QPushButton *getEntryButton(int n) { return m_entryButtons[n]; }
+ const QPushButton *getEntryButton(int n) const { return m_entryButtons[n]; }
+
+ QGridLayout *m_mainLayout;
+ BankEditorDialog* m_bankEditor;
+ KCompletion m_completion;
+ QPushButton *m_initialLabel;
+ std::vector<QLabel*> m_labels;
+ std::vector<KLineEdit*> m_names;
+ QFrame *m_mainFrame;
+ QLabel *m_librarian;
+ QLabel *m_librarianEmail;
+ std::vector<QPushButton *> m_entryButtons;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/OSCMessage.cpp b/src/gui/studio/OSCMessage.cpp
new file mode 100644
index 0000000..6496732
--- /dev/null
+++ b/src/gui/studio/OSCMessage.cpp
@@ -0,0 +1,87 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+
+#ifdef HAVE_LIBLO
+
+#include "OSCMessage.h"
+
+#include <cstdlib>
+#include <cstring>
+
+namespace Rosegarden
+{
+
+OSCMessage::~OSCMessage()
+{
+ clearArgs();
+}
+
+void
+OSCMessage::clearArgs()
+{
+ while (!m_args.empty()) {
+ free(m_args[0].second);
+ m_args.erase(m_args.begin());
+ }
+}
+
+void
+OSCMessage::addArg(char type, lo_arg *arg)
+{
+ lo_arg *newarg = 0;
+
+ if (type == 's') {
+
+ size_t sz = strlen((char *)arg) + 1;
+ if (sz < sizeof(lo_arg))
+ sz = sizeof(lo_arg);
+ newarg = (lo_arg *)malloc(sz);
+ strcpy((char *)newarg, (char *)arg);
+
+ } else {
+
+ newarg = (lo_arg *)malloc(sizeof(lo_arg));
+ memcpy((char *)newarg, (char *)arg, sizeof(lo_arg));
+ }
+
+ m_args.push_back(OSCArg(type, newarg));
+}
+
+size_t
+OSCMessage::getArgCount() const
+{
+ return m_args.size();
+}
+
+const lo_arg *
+OSCMessage::getArg(size_t i, char &type) const
+{
+ type = m_args[i].first;
+ return m_args[i].second;
+}
+
+}
+
+#endif // HAVE_LIBLO
diff --git a/src/gui/studio/OSCMessage.h b/src/gui/studio/OSCMessage.h
new file mode 100644
index 0000000..05bf63f
--- /dev/null
+++ b/src/gui/studio/OSCMessage.h
@@ -0,0 +1,75 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_OSCMESSAGE_H_
+#define _RG_OSCMESSAGE_H_
+
+#include <lo/lo.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace Rosegarden
+{
+
+
+
+class OSCMessage
+{
+public:
+ OSCMessage() { }
+ ~OSCMessage();
+
+ void setTarget(const int &target) { m_target = target; }
+ int getTarget() const { return m_target; }
+
+ void setTargetData(const int &targetData) { m_targetData = targetData; }
+ int getTargetData() const { return m_targetData; }
+
+ void setMethod(const std::string &method) { m_method = method; }
+ std::string getMethod() const { return m_method; }
+
+ void clearArgs();
+ void addArg(char type, lo_arg *arg);
+
+ size_t getArgCount() const;
+ const lo_arg *getArg(size_t i, char &type) const;
+
+private:
+ int m_target;
+ int m_targetData;
+ std::string m_method;
+ typedef std::pair<char, lo_arg *> OSCArg;
+ std::vector<OSCArg> m_args;
+};
+
+
+class TimerCallbackAssistant;
+
+
+}
+
+#endif
diff --git a/src/gui/studio/RemapInstrumentDialog.cpp b/src/gui/studio/RemapInstrumentDialog.cpp
new file mode 100644
index 0000000..dae43da
--- /dev/null
+++ b/src/gui/studio/RemapInstrumentDialog.cpp
@@ -0,0 +1,184 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "RemapInstrumentDialog.h"
+
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/SoftSynthDevice.h"
+#include "base/Studio.h"
+#include "commands/studio/ModifyDeviceMappingCommand.h"
+#include "commands/studio/ModifyInstrumentMappingCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kcombobox.h>
+#include <kcommand.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <qbuttongroup.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+RemapInstrumentDialog::RemapInstrumentDialog(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KDialogBase(parent, "", true, i18n("Remap Instrument assigments..."),
+ Ok | Apply | Cancel),
+ m_doc(doc)
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ m_buttonGroup = new QButtonGroup(1, Qt::Horizontal,
+ i18n("Device or Instrument"),
+ vBox);
+
+ new QLabel(i18n("Remap Tracks by all Instruments on a Device or by single Instrument"), m_buttonGroup);
+ m_deviceButton = new QRadioButton(i18n("Device"), m_buttonGroup);
+ m_instrumentButton = new QRadioButton(i18n("Instrument"), m_buttonGroup);
+
+
+ connect(m_buttonGroup, SIGNAL(released(int)),
+ this, SLOT(slotRemapReleased(int)));
+
+ QGroupBox *groupBox = new QGroupBox(2, Qt::Horizontal,
+ i18n("Choose Source and Destination"),
+ vBox);
+
+ new QLabel(i18n("From"), groupBox);
+ new QLabel(i18n("To"), groupBox);
+ m_fromCombo = new KComboBox(groupBox);
+ m_toCombo = new KComboBox(groupBox);
+
+ m_buttonGroup->setButton(0);
+ populateCombo(0);
+}
+
+void
+RemapInstrumentDialog::populateCombo(int id)
+{
+ m_fromCombo->clear();
+ m_toCombo->clear();
+ Studio *studio = &m_doc->getStudio();
+
+ if (id == 0) {
+ DeviceList *devices = studio->getDevices();
+ DeviceListIterator it;
+ m_devices.clear();
+
+ for (it = devices->begin(); it != devices->end(); it++) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+
+ if (md) {
+ if (md->getDirection() == MidiDevice::Play) {
+ m_devices.push_back(*it);
+ m_fromCombo->insertItem(strtoqstr((*it)->getName()));
+ m_toCombo->insertItem(strtoqstr((*it)->getName()));
+ }
+ } else {
+ SoftSynthDevice *sd =
+ dynamic_cast<SoftSynthDevice *>(*it);
+ if (sd) {
+ m_devices.push_back(*it);
+ m_fromCombo->insertItem(strtoqstr((*it)->getName()));
+ m_toCombo->insertItem(strtoqstr((*it)->getName()));
+ }
+ }
+ }
+
+ if (m_devices.size() == 0) {
+ m_fromCombo->insertItem(i18n("<no devices>"));
+ m_toCombo->insertItem(i18n("<no devices>"));
+ }
+ } else {
+ m_instruments = studio->getPresentationInstruments();
+ InstrumentList::iterator it = m_instruments.begin();
+
+ for (; it != m_instruments.end(); it++) {
+ m_fromCombo->insertItem(strtoqstr((*it)->getPresentationName()));
+ m_toCombo->insertItem(strtoqstr((*it)->getPresentationName()));
+ }
+ }
+}
+
+void
+RemapInstrumentDialog::slotRemapReleased(int id)
+{
+ populateCombo(id);
+}
+
+void
+RemapInstrumentDialog::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+RemapInstrumentDialog::slotApply()
+{
+ if (m_buttonGroup->id(m_buttonGroup->selected()) == 0) // devices
+ {
+ ModifyDeviceMappingCommand *command =
+ new ModifyDeviceMappingCommand
+ (m_doc,
+ m_devices[m_fromCombo->currentItem()]->getId(),
+ m_devices[m_toCombo->currentItem()]->getId());
+ addCommandToHistory(command);
+ } else // instruments
+ {
+ ModifyInstrumentMappingCommand *command =
+ new ModifyInstrumentMappingCommand
+ (m_doc,
+ m_instruments[m_fromCombo->currentItem()]->getId(),
+ m_instruments[m_toCombo->currentItem()]->getId());
+ addCommandToHistory(command);
+ }
+
+ emit applyClicked();
+}
+
+void
+RemapInstrumentDialog::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+}
+
+MultiViewCommandHistory*
+RemapInstrumentDialog::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+}
+#include "RemapInstrumentDialog.moc"
diff --git a/src/gui/studio/RemapInstrumentDialog.h b/src/gui/studio/RemapInstrumentDialog.h
new file mode 100644
index 0000000..669020e
--- /dev/null
+++ b/src/gui/studio/RemapInstrumentDialog.h
@@ -0,0 +1,84 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_REMAPINSTRUMENTDIALOG_H_
+#define _RG_REMAPINSTRUMENTDIALOG_H_
+
+#include "base/Studio.h"
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QRadioButton;
+class QButtonGroup;
+class KCommand;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class RemapInstrumentDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ RemapInstrumentDialog(QWidget *parent,
+ RosegardenGUIDoc *doc);
+
+ void populateCombo(int id);
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+public slots:
+ void slotRemapReleased(int id);
+
+ void slotOk();
+ void slotApply();
+
+protected:
+
+ RosegardenGUIDoc *m_doc;
+
+ QRadioButton *m_deviceButton;
+ QRadioButton *m_instrumentButton;
+
+ QButtonGroup *m_buttonGroup;
+ KComboBox *m_fromCombo;
+ KComboBox *m_toCombo;
+
+ DeviceList m_devices;
+ InstrumentList m_instruments;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/StudioControl.cpp b/src/gui/studio/StudioControl.cpp
new file mode 100644
index 0000000..e94016a
--- /dev/null
+++ b/src/gui/studio/StudioControl.cpp
@@ -0,0 +1,582 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "StudioControl.h"
+
+#include "sound/Midi.h"
+#include "misc/Debug.h"
+#include "base/MidiProgram.h"
+#include "base/Profiler.h"
+#include "base/RealTime.h"
+#include "gui/application/RosegardenApplication.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedInstrument.h"
+#include "sound/MappedStudio.h"
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MappedObjectId
+StudioControl::createStudioObject(MappedObject::MappedObjectType type)
+{
+Profiler profiler("StudioControl::createStudioObject", true);
+
+int value = -1;
+QByteArray data;
+QCString replyType;
+QByteArray replyData;
+QDataStream streamOut(data, IO_WriteOnly);
+
+streamOut << (int)type;
+
+if (!rgapp->sequencerCall("createMappedObject(int)",
+ replyType, replyData, data))
+{
+ SEQMAN_DEBUG << "createStudioObject - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+} else
+{
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+}
+
+return value;
+}
+
+bool
+StudioControl::destroyStudioObject(MappedObjectId id)
+{
+ Profiler profiler("StudioControl::destroyStudioObject", true);
+
+ int value = 0;
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << int(id);
+
+ if (!rgapp->sequencerCall("destroyMappedObject(int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "destroyStudioObject - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+ }
+
+ if (value == 1)
+ return true;
+ else
+ return false;
+}
+
+MappedObjectPropertyList
+StudioControl::getStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property)
+{
+ Profiler profiler("StudioControl::getStudioObjectProperty", true);
+
+ MappedObjectPropertyList list;
+
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << QString(property);
+
+ if (!rgapp->sequencerCall("getPropertyList(int, QString)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getStudioObjectProperty - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> list;
+ }
+
+ return list;
+}
+
+bool
+StudioControl::setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ MappedObjectValue value)
+{
+ Profiler profiler("StudioControl::setStudioObjectProperty(float)", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (QString)property;
+ streamOut << (float)value;
+
+ rgapp->sequencerSend("setMappedProperty(int, QString, float)", data);
+
+ return true;
+}
+
+bool
+StudioControl::setStudioObjectProperties(const MappedObjectIdList &ids,
+ const MappedObjectPropertyList &properties,
+ const MappedObjectValueList &values)
+{
+ Profiler profiler("StudioControl::setStudioObjectProperties(floats)", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << ids;
+ streamOut << properties;
+ streamOut << values;
+
+ rgapp->sequencerSend("setMappedProperties(MappedObjectIdList, MappedObjectPropertyList, MappedObjectValueList)", data);
+
+ return true;
+}
+
+bool
+StudioControl::setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const QString &value)
+{
+ Profiler profiler("StudioControl::setStudioObjectProperty(string)", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (QString)property;
+ streamOut << (QString)value;
+
+ rgapp->sequencerSend("setMappedProperty(int, QString, QString)", data);
+
+ return true;
+}
+
+bool
+StudioControl::setStudioObjectPropertyList(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const MappedObjectPropertyList &values)
+{
+ Profiler profiler("StudioControl::setStudioObjectPropertyList", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (QString)property;
+ streamOut << values;
+
+ RG_DEBUG << "StudioControl::setStudioObjectPropertyList: " << values.size() << " values for property " << property << endl;
+
+ rgapp->sequencerSend("setMappedPropertyList(int, QString, MappedObjectPropertyList)",
+ data);
+
+ return true;
+}
+
+MappedObjectId
+StudioControl::getStudioObjectByType(MappedObject::MappedObjectType type)
+{
+ Profiler profiler("StudioControl::getStudioObjectByType", true);
+
+ int value = -1;
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)type;
+
+ if (!rgapp->sequencerCall("getMappedObjectId(int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getStudioObjectByType - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+ }
+
+ return value;
+}
+
+void
+StudioControl::setStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId,
+ MappedObjectValue value)
+{
+ Profiler profiler("StudioControl::setStudioPluginPort", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // Use new MappedEvent interface
+ //
+ streamOut << (int)pluginId;
+ streamOut << (unsigned long)portId;
+ streamOut << (float)value;
+
+ rgapp->sequencerSend("setMappedPort(int, unsigned long int, float)", data);
+}
+
+MappedObjectValue
+StudioControl::getStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId)
+{
+ Profiler profiler("StudioControl::getStudioPluginPort", true);
+
+ float value = 0.0;
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)pluginId;
+ streamOut << (unsigned long)portId;
+
+ if (!rgapp->sequencerCall("getMappedPort(int, unsigned long int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getStudioPluginPort - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+ }
+
+ return value;
+}
+
+MappedObjectPropertyList
+StudioControl::getPluginInformation()
+{
+ MappedObjectPropertyList list;
+
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+
+ if (!rgapp->sequencerCall("getPluginInformation()",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getPluginInformation - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> list;
+ }
+
+ return list;
+}
+
+QString
+StudioControl::getPluginProgram(MappedObjectId id, int bank, int program)
+{
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (int)bank;
+ streamOut << (int)program;
+
+ QString programName;
+
+ if (!rgapp->sequencerCall("getPluginProgram(int, int, int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getPluginProgram - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> programName;
+ }
+
+ return programName;
+}
+
+unsigned long
+StudioControl::getPluginProgram(MappedObjectId id, QString name)
+{
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << name;
+
+ unsigned long rv;
+
+ if (!rgapp->sequencerCall("getPluginProgram(int, QString)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getPluginProgram - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> rv;
+ }
+
+ return rv;
+}
+
+void
+StudioControl::connectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2)
+{
+ Profiler profiler("StudioControl::connectStudioObjects", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id1;
+ streamOut << (int)id2;
+
+ if (!rgapp->sequencerSend("connectMappedObjects(int, int)", data)) {
+ SEQMAN_DEBUG << "connectStudioObjects - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ }
+
+ return ;
+}
+
+void
+StudioControl::disconnectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2)
+{
+ Profiler profiler("StudioControl::disconnectStudioObjects", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id1;
+ streamOut << (int)id2;
+
+ if (!rgapp->sequencerSend("disconnectMappedObjects(int, int)", data)) {
+ SEQMAN_DEBUG << "disconnectStudioObjects - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ }
+
+ return ;
+}
+
+void
+StudioControl::disconnectStudioObject(MappedObjectId id)
+{
+ Profiler profiler("StudioControl::disconnectStudioObject", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+
+ if (!rgapp->sequencerSend("disconnectMappedObject(int)", data)) {
+ SEQMAN_DEBUG << "disconnectStudioObject - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ }
+
+ return ;
+}
+
+void
+StudioControl::sendMappedEvent(const MappedEvent &mE)
+{
+ Profiler profiler("StudioControl::sendMappedEvent", true);
+
+ static MappedEvent mEs;
+
+ mEs = mE; // just in case the passed mapped event has dubious
+ // origins and taking its address isn't safe
+
+ mEs.setPersistent(true); // to avoid that MappedComposition dtor try to free it
+
+ MappedComposition mC;
+ mC.insert(&mEs);
+ StudioControl::sendMappedComposition(mC);
+}
+
+void
+StudioControl::sendMappedComposition(const MappedComposition &mC)
+{
+ Profiler profiler("StudioControl::sendMappedComposition", true);
+
+ if (mC.size() == 0)
+ return ;
+
+ QCString replyType;
+ QByteArray replyData;
+
+ MappedComposition::const_iterator it = mC.begin();
+
+ for (; it != mC.end(); it++) {
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (*it);
+ rgapp->sequencerSend("processMappedEvent(MappedEvent)", data);
+ }
+}
+
+void
+StudioControl::sendMappedInstrument(const MappedInstrument &mI)
+{
+ Profiler profiler("StudioControl::sendMappedInstrument", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)mI.getType();
+ streamOut << (unsigned char)mI.getChannel();
+ streamOut << (unsigned int)mI.getId();
+
+ rgapp->sequencerSend("setMappedInstrument(int, unsigned char, unsigned int)", data);
+}
+
+void
+StudioControl::sendQuarterNoteLength(const RealTime &length)
+{
+ Profiler profiler("StudioControl::sendQuarterNoteLength", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (long)length.sec;
+ streamOut << (long)length.nsec;
+
+ rgapp->sequencerSend("setQuarterNoteLength(long int, long int)", data);
+}
+
+void
+StudioControl::sendRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte /* controller */,
+ MidiByte value)
+{
+ Profiler profiler("StudioControl::sendRPN", true);
+
+ MappedComposition mC;
+ MappedEvent *mE =
+ new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_2,
+ paramMSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_1,
+ paramLSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ 6, // data value changed
+ value);
+ mC.insert(mE);
+
+
+ // Null the controller using - this is "best practice"
+ //
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_2,
+ MidiMaxValue); // null
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_1,
+ MidiMaxValue); // null
+ mC.insert(mE);
+
+
+ StudioControl::sendMappedComposition(mC);
+}
+
+void
+StudioControl::sendNRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte /* controller */,
+ MidiByte value)
+{
+ Profiler profiler("StudioControl::sendNRPN", true);
+
+ MappedComposition mC;
+ MappedEvent *mE =
+ new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_NRPN_2,
+ paramMSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_NRPN_1,
+ paramLSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ 6, // data value changed
+ value);
+ mC.insert(mE);
+
+
+ // Null the controller using - this is "best practice"
+ //
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_2,
+ MidiMaxValue); // null
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_1,
+ MidiMaxValue); // null
+ mC.insert(mE);
+}
+
+}
diff --git a/src/gui/studio/StudioControl.h b/src/gui/studio/StudioControl.h
new file mode 100644
index 0000000..cf05d44
--- /dev/null
+++ b/src/gui/studio/StudioControl.h
@@ -0,0 +1,152 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_STUDIOCONTROL_H_
+#define _RG_STUDIOCONTROL_H_
+
+#include "base/MidiProgram.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedStudio.h"
+#include <qstring.h>
+
+
+class MappedObjectValueList;
+class MappedObjectIdList;
+
+
+namespace Rosegarden
+{
+
+class RealTime;
+class MappedInstrument;
+class MappedEvent;
+class MappedComposition;
+
+typedef std::pair<Rosegarden::MidiByte, Rosegarden::MidiByte> MidiControlPair;
+
+class StudioControl
+{
+public:
+
+ // Object management
+ //
+ static MappedObjectId
+ createStudioObject(MappedObject::MappedObjectType type);
+ static MappedObjectId
+ getStudioObjectByType(MappedObject::MappedObjectType type);
+ static bool destroyStudioObject(MappedObjectId id);
+
+ // Properties
+ //
+ static MappedObjectPropertyList
+ getStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property);
+
+ // Set a value to a value
+ //
+ static bool setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ MappedObjectValue value);
+
+ // Set many values to values
+ //
+ static bool setStudioObjectProperties(const MappedObjectIdList &ids,
+ const MappedObjectPropertyList &properties,
+ const MappedObjectValueList &values);
+
+ // Set a value to a string
+ //
+ static bool setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const QString &value);
+
+ // Set a value to a string list
+ //
+ static bool setStudioObjectPropertyList(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const MappedObjectPropertyList &values);
+
+ static void setStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId,
+ MappedObjectValue value);
+
+ static MappedObjectValue getStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId);
+
+ // Get all plugin information
+ //
+ static MappedObjectPropertyList getPluginInformation();
+
+ // Get program name for a given program
+ //
+ static QString getPluginProgram(MappedObjectId, int bank, int program);
+
+ // Get program numbers for a given name (rv is bank << 16 + program)
+ // This is one of the nastiest hacks in the whole application
+ //
+ static unsigned long getPluginProgram(MappedObjectId, QString name);
+
+ // Connection
+ //
+ static void connectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2);
+ static void disconnectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2);
+ static void disconnectStudioObject(MappedObjectId id);
+
+ // Send controllers and other one off MIDI events using these
+ // interfaces.
+ //
+ static void sendMappedEvent(const MappedEvent& mE);
+ static void sendMappedComposition(const MappedComposition &mC);
+
+ // MappedInstrument
+ //
+ static void sendMappedInstrument(const MappedInstrument &mI);
+
+ // Send the Quarter Note Length has changed to the sequencer
+ //
+ static void sendQuarterNoteLength(const RealTime &length);
+
+ // Convenience wrappers for RPNs and NRPNs
+ //
+ static void sendRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte controller,
+ MidiByte value);
+
+ static void sendNRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte controller,
+ MidiByte value);
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/SynthPluginManagerDialog.cpp b/src/gui/studio/SynthPluginManagerDialog.cpp
new file mode 100644
index 0000000..f9a54ea
--- /dev/null
+++ b/src/gui/studio/SynthPluginManagerDialog.cpp
@@ -0,0 +1,360 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "SynthPluginManagerDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "AudioPlugin.h"
+#include "AudioPluginManager.h"
+#include "AudioPluginOSCGUIManager.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include <kaction.h>
+#include <kcombobox.h>
+#include <kmainwindow.h>
+#include <kstdaction.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+SynthPluginManagerDialog::SynthPluginManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *doc
+#ifdef HAVE_LIBLO
+ , AudioPluginOSCGUIManager *guiManager
+#endif
+ ) :
+ KMainWindow(parent, "synthpluginmanagerdialog"),
+ m_document(doc),
+ m_studio(&doc->getStudio()),
+ m_pluginManager(doc->getPluginManager())
+#ifdef HAVE_LIBLO
+ , m_guiManager(guiManager)
+#endif
+ {
+ setCaption(i18n("Manage Synth Plugins"));
+
+ QFrame *mainBox = new QFrame(this);
+ setCentralWidget(mainBox);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout(mainBox, 10, 10);
+
+ QGroupBox *groupBox = new QGroupBox(1, Horizontal, i18n("Synth plugins"), mainBox);
+ mainLayout->addWidget(groupBox);
+
+ QFrame *pluginFrame = new QFrame(groupBox);
+ QGridLayout *pluginLayout = new QGridLayout(pluginFrame, 1, 4, 3, 3);
+
+ m_synthPlugins.clear();
+ m_synthPlugins.push_back( -1);
+
+ int count = 0;
+
+ for (PluginIterator itr = m_pluginManager->begin();
+ itr != m_pluginManager->end(); ++itr) {
+
+ if ((*itr)->isSynth()) {
+ m_synthPlugins.push_back(count);
+ }
+
+ ++count;
+ }
+
+ for (int i = 0; i < SoftSynthInstrumentCount; ++i) {
+
+ InstrumentId id = SoftSynthInstrumentBase + i;
+ Instrument *instrument = m_studio->getInstrumentById(id);
+ if (!instrument)
+ continue;
+
+ // pluginLayout->addWidget(new QLabel(instrument->getPresentationName().c_str(),
+ // pluginFrame), i, 0);
+ pluginLayout->addWidget(new QLabel(QString("%1").arg(i + 1),
+ pluginFrame), i, 0);
+
+ AudioPluginInstance *plugin = instrument->getPlugin
+ (Instrument::SYNTH_PLUGIN_POSITION);
+
+ std::string identifier;
+ if (plugin)
+ identifier = plugin->getIdentifier();
+
+ int currentItem = 0;
+
+ KComboBox *pluginCombo = new KComboBox(pluginFrame);
+ pluginCombo->insertItem(i18n("<none>"));
+
+ for (size_t j = 0; j < m_synthPlugins.size(); ++j) {
+
+ if (m_synthPlugins[j] == -1)
+ continue;
+
+ AudioPlugin *plugin =
+ m_pluginManager->getPlugin(m_synthPlugins[j]);
+
+ pluginCombo->insertItem(plugin->getName());
+
+ if (plugin->getIdentifier() == identifier.c_str()) {
+ pluginCombo->setCurrentItem(pluginCombo->count() - 1);
+ }
+ }
+
+ connect(pluginCombo, SIGNAL(activated(int)),
+ this, SLOT(slotPluginChanged(int)));
+
+ pluginLayout->addWidget(pluginCombo, i, 1);
+
+ m_synthCombos.push_back(pluginCombo);
+
+ QPushButton *controlsButton = new QPushButton(i18n("Controls"), pluginFrame);
+ pluginLayout->addWidget(controlsButton, i, 2);
+ connect(controlsButton, SIGNAL(clicked()), this, SLOT(slotControlsButtonClicked()));
+ m_controlsButtons.push_back(controlsButton);
+
+#ifdef HAVE_LIBLO
+
+ QPushButton *guiButton = new QPushButton(i18n("Editor >>"), pluginFrame);
+ pluginLayout->addWidget(guiButton, i, 3);
+ guiButton->setEnabled(m_guiManager->hasGUI
+ (id, Instrument::SYNTH_PLUGIN_POSITION));
+ connect(guiButton, SIGNAL(clicked()), this, SLOT(slotGUIButtonClicked()));
+ m_guiButtons.push_back(guiButton);
+#endif
+
+ }
+
+ QFrame* btnBox = new QFrame(mainBox);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QPushButton *closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 0, 10);
+ layout->addStretch(10);
+ layout->addWidget(closeButton);
+ layout->addSpacing(5);
+
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ closeButton->setText(close->text());
+ connect(closeButton, SIGNAL(clicked()), this, SLOT(slotClose()));
+
+ mainLayout->addWidget(btnBox);
+
+ createGUI("synthpluginmanager.rc");
+
+ setAutoSaveSettings(SynthPluginManagerConfigGroup, true);
+ }
+
+ SynthPluginManagerDialog::~SynthPluginManagerDialog()
+ {
+ RG_DEBUG << "\n*** SynthPluginManagerDialog::~SynthPluginManagerDialog()"
+ << endl;
+ }
+
+ void
+ SynthPluginManagerDialog::updatePlugin(InstrumentId id, int plugin)
+ {
+ if (id < SoftSynthInstrumentBase)
+ return ;
+ int row = id - SoftSynthInstrumentBase;
+ if (row >= m_synthCombos.size())
+ return ;
+
+ KComboBox *comboBox = m_synthCombos[row];
+
+ for (unsigned int i = 0; i < m_synthPlugins.size(); ++i) {
+ if (m_synthPlugins[i] == plugin) {
+ blockSignals(true);
+ comboBox->setCurrentItem(i);
+ blockSignals(false);
+ return ;
+ }
+ }
+
+ blockSignals(true);
+ comboBox->setCurrentItem(0);
+ blockSignals(false);
+ return ;
+ }
+
+ void
+ SynthPluginManagerDialog::slotClose()
+ {
+ close();
+ }
+
+ void
+ SynthPluginManagerDialog::closeEvent(QCloseEvent *e)
+ {
+ emit closing();
+ KMainWindow::closeEvent(e);
+ }
+
+ void
+ SynthPluginManagerDialog::slotGUIButtonClicked()
+ {
+ const QObject *s = sender();
+
+ int instrumentNo = -1;
+
+ for (unsigned int i = 0; i < m_guiButtons.size(); ++i) {
+ if (s == m_guiButtons[i])
+ instrumentNo = i;
+ }
+
+ if (instrumentNo == -1) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotGUIButtonClicked: unknown sender" << endl;
+ return ;
+ }
+
+ InstrumentId id = SoftSynthInstrumentBase + instrumentNo;
+
+ emit showPluginGUI(id, Instrument::SYNTH_PLUGIN_POSITION);
+ }
+
+ void
+ SynthPluginManagerDialog::slotControlsButtonClicked()
+ {
+ const QObject *s = sender();
+
+ int instrumentNo = -1;
+
+ for (unsigned int i = 0; i < m_controlsButtons.size(); ++i) {
+ if (s == m_controlsButtons[i])
+ instrumentNo = i;
+ }
+
+ if (instrumentNo == -1) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotControlsButtonClicked: unknown sender" << endl;
+ return ;
+ }
+
+ InstrumentId id = SoftSynthInstrumentBase + instrumentNo;
+
+ emit showPluginDialog(this, id, Instrument::SYNTH_PLUGIN_POSITION);
+ }
+
+ void
+ SynthPluginManagerDialog::slotPluginChanged(int index)
+ {
+ const QObject *s = sender();
+
+ RG_DEBUG << "SynthPluginManagerDialog::slotPluginChanged(" << index
+ << ")" << endl;
+
+ int instrumentNo = -1;
+
+ for (unsigned int i = 0; i < m_synthCombos.size(); ++i) {
+ if (s == m_synthCombos[i])
+ instrumentNo = i;
+ }
+
+ if (instrumentNo == -1) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotValueChanged: unknown sender" << endl;
+ return ;
+ }
+
+ InstrumentId id = SoftSynthInstrumentBase + instrumentNo;
+
+ if (index >= int(m_synthPlugins.size())) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotValueChanged: synth "
+ << index << " out of range" << endl;
+ return ;
+ }
+
+ // NB m_synthPlugins[0] is -1 to represent the <none> item
+
+ AudioPlugin *plugin = m_pluginManager->getPlugin(m_synthPlugins[index]);
+ Instrument *instrument = m_studio->getInstrumentById(id);
+
+ if (instrument) {
+
+ AudioPluginInstance *pluginInstance = instrument->getPlugin
+ (Instrument::SYNTH_PLUGIN_POSITION);
+
+ if (pluginInstance) {
+
+ if (plugin) {
+ RG_DEBUG << "plugin is " << plugin->getIdentifier() << endl;
+ pluginInstance->setIdentifier(plugin->getIdentifier().data());
+
+ // set ports to defaults
+
+ AudioPlugin::PortIterator it = plugin->begin();
+ int count = 0;
+
+ for (; it != plugin->end(); ++it) {
+
+ if (((*it)->getType() & PluginPort::Control) &&
+ ((*it)->getType() & PluginPort::Input)) {
+
+ if (pluginInstance->getPort(count) == 0) {
+ pluginInstance->addPort(count, (float)(*it)->getDefaultValue());
+ } else {
+ pluginInstance->getPort(count)->value = (*it)->getDefaultValue();
+ }
+ }
+
+ ++count;
+ }
+
+ } else {
+ pluginInstance->setIdentifier("");
+ }
+ }
+ }
+
+#ifdef HAVE_LIBLO
+ if (instrumentNo < m_guiButtons.size()) {
+ m_guiButtons[instrumentNo]->setEnabled
+ (m_guiManager->hasGUI
+ (id, Instrument::SYNTH_PLUGIN_POSITION));
+ }
+#endif
+
+ emit pluginSelected(id, Instrument::SYNTH_PLUGIN_POSITION,
+ m_synthPlugins[index]);
+ }
+
+ }
+#include "SynthPluginManagerDialog.moc"
diff --git a/src/gui/studio/SynthPluginManagerDialog.h b/src/gui/studio/SynthPluginManagerDialog.h
new file mode 100644
index 0000000..b66a338
--- /dev/null
+++ b/src/gui/studio/SynthPluginManagerDialog.h
@@ -0,0 +1,98 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_SYNTHPLUGINMANAGERDIALOG_H_
+#define _RG_SYNTHPLUGINMANAGERDIALOG_H_
+
+#include "base/MidiProgram.h"
+#include <kmainwindow.h>
+#include <vector>
+
+
+class QWidget;
+class QPushButton;
+class QCloseEvent;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class AudioPluginOSCGUIManager;
+class AudioPluginManager;
+
+
+class SynthPluginManagerDialog : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ SynthPluginManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *doc
+#ifdef HAVE_LIBLO
+ , AudioPluginOSCGUIManager *guiManager
+#endif
+ );
+
+ virtual ~SynthPluginManagerDialog();
+
+ void updatePlugin(InstrumentId id, int plugin);
+
+signals:
+ void closing();
+ void pluginSelected(InstrumentId, int pluginIndex, int plugin);
+ void showPluginDialog(QWidget *, InstrumentId, int pluginIndex);
+ void showPluginGUI(InstrumentId, int pluginIndex);
+
+protected slots:
+ void slotClose();
+ void slotPluginChanged(int index);
+ void slotControlsButtonClicked();
+ void slotGUIButtonClicked();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+protected:
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+ AudioPluginManager *m_pluginManager;
+ std::vector<int> m_synthPlugins;
+ std::vector<KComboBox *> m_synthCombos;
+ std::vector<QPushButton *> m_controlsButtons;
+ std::vector<QPushButton *> m_guiButtons;
+
+#ifdef HAVE_LIBLO
+ AudioPluginOSCGUIManager *m_guiManager;
+#endif
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/TimerCallbackAssistant.cpp b/src/gui/studio/TimerCallbackAssistant.cpp
new file mode 100644
index 0000000..ec8518c
--- /dev/null
+++ b/src/gui/studio/TimerCallbackAssistant.cpp
@@ -0,0 +1,57 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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 "TimerCallbackAssistant.h"
+
+#include <qobject.h>
+#include <qtimer.h>
+
+
+namespace Rosegarden
+{
+
+TimerCallbackAssistant::TimerCallbackAssistant(int ms, void (*callback)(void *data),
+ void *data) :
+ m_callback(callback),
+ m_data(data)
+{
+ QTimer *timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(slotCallback()));
+ timer->start(ms, FALSE);
+}
+
+TimerCallbackAssistant::~TimerCallbackAssistant()
+{
+ // nothing -- the QTimer is deleted automatically by its parent QObject (me)
+}
+
+void
+TimerCallbackAssistant::slotCallback()
+{
+ m_callback(m_data);
+}
+
+}
+#include "TimerCallbackAssistant.moc"
diff --git a/src/gui/studio/TimerCallbackAssistant.h b/src/gui/studio/TimerCallbackAssistant.h
new file mode 100644
index 0000000..2a8e353
--- /dev/null
+++ b/src/gui/studio/TimerCallbackAssistant.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ 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.
+*/
+
+#ifndef _RG_TIMERCALLBACKASSISTANT_H_
+#define _RG_TIMERCALLBACKASSISTANT_H_
+
+#include <qobject.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+/* This assistant class is here simply to work around the fact that
+ AudioPluginOSCGUI cannot be a QObject because it's only
+ conditionally compiled. */
+
+class TimerCallbackAssistant : public QObject
+{
+ Q_OBJECT
+
+public:
+ TimerCallbackAssistant(int ms, void (*callback)(void *data), void *data);
+ virtual ~TimerCallbackAssistant();
+
+protected slots:
+ void slotCallback();
+
+private:
+ void (*m_callback)(void *);
+ void *m_data;
+};
+
+
+}
+
+#endif