/* Rosegarden A MIDI and audio sequencer and musical notation editor. This program is Copyright 2000-2008 Guillaume Laurent , Chris Cannam , Richard Bown 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 #include "sound/Midi.h" #include #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 #include #include #include #include #include #include #include #include #include #include #include namespace Rosegarden { MidiMixerWindow::MidiMixerWindow(TQWidget *parent, RosegardenGUIDoc *document): MixerWindow(parent, document), m_tabFrame(0) { // Initial setup // setupTabs(); KStdAction::close(this, TQT_SLOT(slotClose()), actionCollection()); TQIconSet icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-play"))); TDEAction *play = new TDEAction(i18n("&Play"), icon, Key_Enter, this, TQT_SIGNAL(play()), actionCollection(), "play"); // Alternative shortcut for Play TDEShortcut playShortcut = play->shortcut(); playShortcut.append( KKey(Key_Return + CTRL) ); play->setShortcut(playShortcut); icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-stop"))); new TDEAction(i18n("&Stop"), icon, Key_Insert, this, TQT_SIGNAL(stop()), actionCollection(), "stop"); icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-rewind"))); new TDEAction(i18n("Re&wind"), icon, Key_End, this, TQT_SIGNAL(rewindPlayback()), actionCollection(), "playback_pointer_back_bar"); icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-ffwd"))); new TDEAction(i18n("&Fast Forward"), icon, Key_PageDown, this, TQT_SIGNAL(fastForwardPlayback()), actionCollection(), "playback_pointer_forward_bar"); icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-rewind-end"))); new TDEAction(i18n("Rewind to &Beginning"), icon, 0, this, TQT_SIGNAL(rewindPlaybackToBeginning()), actionCollection(), "playback_pointer_start"); icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-ffwd-end"))); new TDEAction(i18n("Fast Forward to &End"), icon, 0, this, TQT_SIGNAL(fastForwardPlaybackToEnd()), actionCollection(), "playback_pointer_end"); icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-record"))); new TDEAction(i18n("&Record"), icon, 0, this, TQT_SIGNAL(record()), actionCollection(), "record"); icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap ("transport-panic"))); new TDEAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this, TQT_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 TQTabWidget(this); setCentralWidget(m_tabWidget); connect(m_tabWidget, TQT_SIGNAL(currentChanged(TQWidget *)), this, TQT_SLOT(slotCurrentTabChanged(TQWidget *))); m_tabWidget->setTabPosition(TQTabWidget::Bottom); setCaption(i18n("MIDI Mixer")); for (it = m_studio->begin(); it != m_studio->end(); ++it) { dev = dynamic_cast(*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 TQFrame(m_tabWidget); m_tabFrame->setFrameStyle(TQFrame::TabWidgetPanel); m_tabFrame->setMargin(10); TQGridLayout *mainLayout = new TQGridLayout (m_tabFrame, instruments.size() + 4, controls.size() + 4, 5); // MIDI Mixer label // //TQLabel *label = new TQLabel(TQString("%1 %2"). //arg(strtoqstr(dev->getName())) //.arg(i18n("MIDI Mixer")), m_tabFrame); TQLabel *label = new TQLabel("", m_tabFrame); mainLayout->addMultiCellWidget(label, 0, 0, 0, 16, TQt::AlignCenter); // control labels for (unsigned int i = 0; i < controls.size(); ++i) { label = new TQLabel(strtoqstr(controls[i].getName()), m_tabFrame); mainLayout->addWidget(label, i + 1, 0, TQt::AlignCenter); } // meter label // //label = new TQLabel(i18n("Meter"), m_tabFrame); //mainLayout->addWidget(label, //controls.size() + 1, 0, TQt::AlignCenter); // volume label label = new TQLabel(i18n("Volume"), m_tabFrame); mainLayout->addWidget(label, controls.size() + 2, 0, TQt::AlignCenter); // instrument label label = new TQLabel(i18n("Instrument"), m_tabFrame); mainLayout->addWidget(label, controls.size() + 3, 0, TQt::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) { TQColor knobColour = TQt::white; if (controls[i].getColourIndex() > 0) { Colour c = m_document->getComposition().getGeneralColourMap(). getColourByIndex(controls[i].getColourIndex()); knobColour = TQColor(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, TQT_SIGNAL(valueChanged(float)), this, TQT_SLOT(slotControllerChanged(float))); mainLayout->addWidget(controller, i + 1, posCount, TQt::AlignCenter); // Store the rotary // m_faders[faderCount]->m_controllerRotaries.push_back( std::pair (controls[i].getControllerValue(), controller)); } // Pan rotary // MidiMixerVUMeter *meter = new MidiMixerVUMeter(m_tabFrame, VUMeter::FixedHeightVisiblePeakHold, 6, 30); mainLayout->addWidget(meter, controls.size() + 1, posCount, TQt::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, TQt::AlignCenter); m_faders[faderCount]->m_volumeFader = fader; //fader->setFader(float((*iIt)->getVolume())); // Label // TQLabel *idLabel = new TQLabel(TQString("%1"). arg((*iIt)->getId() - firstInstrument + 1), m_tabFrame, "idLabel"); mainLayout->addWidget(idLabel, controls.size() + 3, posCount, TQt::AlignCenter); // store id in struct m_faders[faderCount]->m_id = (*iIt)->getId(); // Connect them up // connect(fader, TQT_SIGNAL(faderChanged(float)), this, TQT_SLOT(slotFaderLevelChanged(float))); // Update all the faders and controllers // slotUpdateInstrument((*iIt)->getId()); // Increment counters // posCount++; faderCount++; } TQString name = TQString("%1 (%2)").arg(strtoqstr(dev->getName())) .arg(deviceCount++); addTab(m_tabFrame, name); } } } void MidiMixerWindow::addTab(TQWidget *tab, const TQString &title) { m_tabWidget->addTab(tab, title); } void MidiMixerWindow::slotFaderLevelChanged(float value) { const TQObject *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(*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 TQObject *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(*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(*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(*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(TQWidget *) { 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(*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"