diff options
author | Tim E. Real <termtech@rogers.com> | 2010-03-31 04:29:35 +0000 |
---|---|---|
committer | Tim E. Real <termtech@rogers.com> | 2010-03-31 04:29:35 +0000 |
commit | 877df47f6c8021a4377e76720e7f13943404f20b (patch) | |
tree | ee93dcbcc70178350571249f6df04ee5996b47b6 | |
parent | a278b34a6c2a47d6cf0c9b5ebfc696f5bf2c984d (diff) |
See ChangeLog
45 files changed, 6219 insertions, 1060 deletions
diff --git a/muse/ChangeLog b/muse/ChangeLog index a9505d58..cdf4928f 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,24 @@ +30.03.2010 + * Major reworks: Jack midi, routing system, multichannel synth ins/outs, midi strips and trackinfo pane. (T356) + - WORK IN PROGRESS. Should be usable for current projects. + - ADVISORY: If opening current projects you are advised to "save as" or back up your med files (waves should be OK) + until all is deemed to be working OK. It is possible more changes to .med file structure are needed later. + - Some .med project file structures have CHANGED (like routing). I have tested opening large existing projects, + then re-saving them. OK so far. + However, I have not added importing code for the old midi trackinfo panel 'input port' and 'input channel' boxes (gone now). + If you want to wait, I am planning to do that, but later... Otherwise you must re-enter them with the new midi input routing. + - ** Multichannel synth inputs and outputs: Fairly satisfied with popup menus, hopefully no major reworks... + Routing system fully supports multichannel synth mono/stero paths but is NOT COMPLETE yet. + When changing a connected track from stereo to mono, there is no coding yet to ensure the existing stereo routes are + changed from stereo to mono (just like the audio input and output tracks have always done with Jack routes). + Also coding must be added to avoid simultaneous mono and stereo routes - make them mutually exclusive - although + there's nothing technically wrong with it, it's just not desirable. + - ** Jack midi: You now create your own Jack midi ports. I have not added any means to delete them yet, but I believe + if you 'unselect' them in the midi ports list so that they are not used, then the saved .med file will ignore them. + - Multi-instances of MusE should work OK now - all ports should hopefully have unique names. + - Number of midi ports increased from 32 to 128. It's a bit much for the ports list, was planning "create your own + midi port as needed" (like RG). Obstacles seem to have been cleared now, but it would be a big job (lots of places to change). + - Along the way various tweaks and fixes when I spotted them. 18.02.2010 * Added: Separate Jack midi client ports per MusE port. (T356) - For now, you will have to manually add further connections to those ports if desired, each session. diff --git a/muse/muse/app.cpp b/muse/muse/app.cpp index 6bc52319..219f59bd 100644 --- a/muse/muse/app.cpp +++ b/muse/muse/app.cpp @@ -95,7 +95,8 @@ #include "didyouknow.h" #include <qtextedit.h> -extern void cacheJackRouteNames(); +//extern void cacheJackRouteNames(); + static pthread_t watchdogThread; //ErrorHandler *error; static const char* fileOpenText = @@ -1698,7 +1699,9 @@ bool MusE::save(const QString& name, bool overwriteWarn) // By T356. Cache the jack in/out route names BEFORE saving. // Because jack often shuts down during save, causing the routes to be lost in the file. - cacheJackRouteNames(); + // Not required any more... + //cacheJackRouteNames(); + if (QFile::exists(name)) { backupCommand.sprintf("cp \"%s\" \"%s.backup\"", name.latin1(), name.latin1()); } @@ -2659,7 +2662,9 @@ int main(int argc, char* argv[]) lash_client = lash_init (lash_args, muse_name, lash_flags, LASH_PROTOCOL(2,0)); lash_alsa_client_id (lash_client, snd_seq_client_id (alsaSeq)); if (!noAudio) { - char *jack_name = ((JackAudioDevice*)audioDevice)->getJackName(); + // p3.3.38 + //char *jack_name = ((JackAudioDevice*)audioDevice)->getJackName(); + const char *jack_name = audioDevice->clientName(); lash_jack_client_name (lash_client, jack_name); } } diff --git a/muse/muse/arranger/arranger.h b/muse/muse/arranger/arranger.h index 5a402585..c43320cb 100644 --- a/muse/muse/arranger/arranger.h +++ b/muse/muse/arranger/arranger.h @@ -132,9 +132,9 @@ class Arranger : public QWidget { void trackInfoScroll(int); //void iNameChanged(); - void iInputChannelChanged(const QString&); + ///void iInputChannelChanged(const QString&); void iOutputChannelChanged(int); - void iInputPortChanged(const QString&); + ///void iInputPortChanged(const QString&); void iOutputPortChanged(int); void iProgHBankChanged(); void iProgLBankChanged(); @@ -166,6 +166,8 @@ class Arranger : public QWidget { void panRecClicked(); void recEchoToggled(bool); void verticalScrollSetYpos(unsigned); + void inRoutesPressed(); + void outRoutesPressed(); signals: void redirectWheelEvent(QWheelEvent*); diff --git a/muse/muse/arranger/trackinfo.cpp b/muse/muse/arranger/trackinfo.cpp index 914d98e6..bc1cf939 100644 --- a/muse/muse/arranger/trackinfo.cpp +++ b/muse/muse/arranger/trackinfo.cpp @@ -40,6 +40,7 @@ #include "mixer/astrip.h" #include "icons.h" #include "app.h" +#include "route.h" //--------------------------------------------------------- @@ -59,9 +60,9 @@ void Arranger::midiTrackInfoHeartBeat() int outChannel = track->outChannel(); int outPort = track->outPort(); - int ichMask = track->inChannelMask(); + ///int ichMask = track->inChannelMask(); //int iptMask = track->inPortMask(); - unsigned int iptMask = track->inPortMask(); + ///unsigned int iptMask = track->inPortMask(); MidiPort* mp = &midiPorts[outPort]; @@ -76,19 +77,36 @@ void Arranger::midiTrackInfoHeartBeat() // Check for detection of midi general activity on chosen channels... int mpt = 0; //int mch = 0; - for(; mpt < MIDI_PORTS; ++mpt) + RouteList* rl = track->inRoutes(); + + ciRoute r = rl->begin(); + //for( ; mpt < MIDI_PORTS; ++mpt) + for( ; r != rl->end(); ++r) { + //if(!r->isValid() || ((r->type != Route::ALSA_MIDI_ROUTE) && (r->type != Route::JACK_MIDI_ROUTE))) + if(!r->isValid() || (r->type != Route::MIDI_DEVICE_ROUTE)) + continue; + + // NOTE: TODO: Code for channelless events like sysex, ** IF we end up using the 'special channel 17' method. + if(r->channel == -1) + continue; + + // No port assigned to the device? + mpt = r->device->midiPort(); + if(mpt == -1) + continue; + //for(; mch < MIDI_CHANNELS; ++mch) //{ //if(midiPorts[mpt].syncInfo().actDetect(mch) && (iptMask & (1 << mpt)) && (ichMask & (1 << mch)) ) - if((iptMask & bitShiftLU[mpt]) && (midiPorts[mpt].syncInfo().actDetectBits() & ichMask) ) + //if((iptMask & bitShiftLU[mpt]) && (midiPorts[mpt].syncInfo().actDetectBits() & ichMask) ) + if(midiPorts[mpt].syncInfo().actDetectBits() & bitShiftLU[r->channel]) { //if(midiTrackInfo->iChanTextLabel->paletteBackgroundColor() != green) // midiTrackInfo->iChanTextLabel->setPaletteBackgroundColor(green); //if(midiTrackInfo->iChanDetectLabel->pixmap() != greendotIcon) if(!midiTrackInfo->_midiDetect) { - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting green icon\n"); midiTrackInfo->_midiDetect = true; @@ -101,14 +119,14 @@ void Arranger::midiTrackInfoHeartBeat() } // No activity detected? //if(mch == MIDI_CHANNELS) - if(mpt == MIDI_PORTS) + //if(mpt == MIDI_PORTS) + if(r == rl->end()) { //if(midiTrackInfo->iChanTextLabel->paletteBackgroundColor() != darkGreen) // midiTrackInfo->iChanTextLabel->setPaletteBackgroundColor(darkGreen); //if(midiTrackInfo->iChanDetectLabel->pixmap() != darkgreendotIcon) if(midiTrackInfo->_midiDetect) { - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting darkgreen icon\n"); midiTrackInfo->_midiDetect = false; @@ -122,7 +140,6 @@ void Arranger::midiTrackInfoHeartBeat() { if(program != CTRL_VAL_UNKNOWN) { - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting program to unknown\n"); program = CTRL_VAL_UNKNOWN; @@ -154,7 +171,6 @@ void Arranger::midiTrackInfoHeartBeat() //if(strcmp(midiTrackInfo->iPatch->text().latin1(), n) != 0) if(midiTrackInfo->iPatch->text() != n) { - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting patch <unknown>\n"); midiTrackInfo->iPatch->setText(n); @@ -172,7 +188,6 @@ void Arranger::midiTrackInfoHeartBeat() else if(strcmp(midiTrackInfo->iPatch->text().latin1(), name) != 0) { - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting patch name\n"); midiTrackInfo->iPatch->setText(name); @@ -207,7 +222,6 @@ void Arranger::midiTrackInfoHeartBeat() pr = 0; //} - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting program\n"); if(midiTrackInfo->iHBank->value() != hb) @@ -249,7 +263,6 @@ void Arranger::midiTrackInfoHeartBeat() volume = v; if(midiTrackInfo->iLautst->value() != v) { - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting volume\n"); midiTrackInfo->iLautst->blockSignals(true); @@ -277,7 +290,6 @@ void Arranger::midiTrackInfoHeartBeat() pan = v; if(midiTrackInfo->iPan->value() != v) { - // Added by Tim. p3.3.6 //printf("Arranger::midiTrackInfoHeartBeat setting pan\n"); midiTrackInfo->iPan->blockSignals(true); @@ -405,7 +417,6 @@ void Arranger::switchInfo(int n) //--------------------------------------------------------- // setTrackInfoLabelText //--------------------------------------------------------- -// Added by Tim. p3.3.9 void Arranger::setTrackInfoLabelText() { @@ -419,7 +430,6 @@ void Arranger::setTrackInfoLabelText() //--------------------------------------------------------- // setTrackInfoLabelFont //--------------------------------------------------------- -// Added by Tim. p3.3.9 void Arranger::setTrackInfoLabelFont() { @@ -498,6 +508,7 @@ void Arranger::iOutputChannelChanged(int channel) } } +/* //--------------------------------------------------------- // iKanalChanged //--------------------------------------------------------- @@ -511,6 +522,7 @@ void Arranger::iInputChannelChanged(const QString& s) list->redraw(); } } +*/ //--------------------------------------------------------- // iOutputPortChanged @@ -531,6 +543,7 @@ void Arranger::iOutputPortChanged(int index) list->redraw(); } +/* //--------------------------------------------------------- // iInputPortChanged //--------------------------------------------------------- @@ -547,6 +560,31 @@ void Arranger::iInputPortChanged(const QString& s) track->setInPortMask(val); list->redraw(); } +*/ + +//--------------------------------------------------------- +// inRoutesPressed +//--------------------------------------------------------- + +void Arranger::inRoutesPressed() +{ + if(!selected) + return; + + song->chooseMidiRoutes(midiTrackInfo->iRButton, (MidiTrack*)selected, false); +} + +//--------------------------------------------------------- +// outRoutesPressed +//--------------------------------------------------------- + +void Arranger::outRoutesPressed() +{ + if(!selected) + return; + + song->chooseMidiRoutes(midiTrackInfo->oRButton, (MidiTrack*)selected, true); +} //--------------------------------------------------------- // iProgHBankChanged @@ -1124,7 +1162,7 @@ void Arranger::genMidiTrackInfo() //connect(midiTrackInfo->iName, SIGNAL(returnPressed()), SLOT(iNameChanged())); connect(midiTrackInfo->iOutputChannel, SIGNAL(valueChanged(int)), SLOT(iOutputChannelChanged(int))); - connect(midiTrackInfo->iInputChannel, SIGNAL(textChanged(const QString&)), SLOT(iInputChannelChanged(const QString&))); + ///connect(midiTrackInfo->iInputChannel, SIGNAL(textChanged(const QString&)), SLOT(iInputChannelChanged(const QString&))); connect(midiTrackInfo->iHBank, SIGNAL(valueChanged(int)), SLOT(iProgHBankChanged())); connect(midiTrackInfo->iLBank, SIGNAL(valueChanged(int)), SLOT(iProgLBankChanged())); connect(midiTrackInfo->iProgram, SIGNAL(valueChanged(int)), SLOT(iProgramChanged())); @@ -1141,12 +1179,17 @@ void Arranger::genMidiTrackInfo() connect(midiTrackInfo->iPan, SIGNAL(valueChanged(int)), SLOT(iPanChanged(int))); connect(midiTrackInfo->iPan, SIGNAL(doubleClicked()), SLOT(iPanDoubleClicked())); connect(midiTrackInfo->iOutput, SIGNAL(activated(int)), SLOT(iOutputPortChanged(int))); - connect(midiTrackInfo->iInput, SIGNAL(textChanged(const QString&)), SLOT(iInputPortChanged(const QString&))); + ///connect(midiTrackInfo->iInput, SIGNAL(textChanged(const QString&)), SLOT(iInputPortChanged(const QString&))); connect(midiTrackInfo->recordButton, SIGNAL(clicked()), SLOT(recordClicked())); connect(midiTrackInfo->progRecButton, SIGNAL(clicked()), SLOT(progRecClicked())); connect(midiTrackInfo->volRecButton, SIGNAL(clicked()), SLOT(volRecClicked())); connect(midiTrackInfo->panRecButton, SIGNAL(clicked()), SLOT(panRecClicked())); connect(midiTrackInfo->recEchoButton, SIGNAL(toggled(bool)), SLOT(recEchoToggled(bool))); + connect(midiTrackInfo->iRButton, SIGNAL(pressed()), SLOT(inRoutesPressed())); + + // TODO: Works OK, but disabled for now, until we figure out what to do about multiple out routes and display values... + midiTrackInfo->oRButton->setEnabled(false); + connect(midiTrackInfo->oRButton, SIGNAL(pressed()), SLOT(outRoutesPressed())); connect(heartBeatTimer, SIGNAL(timeout()), SLOT(midiTrackInfoHeartBeat())); } @@ -1169,12 +1212,12 @@ void Arranger::updateMidiTrackInfo(int flags) //{ int outChannel = track->outChannel(); - int inChannel = track->inChannelMask(); + ///int inChannel = track->inChannelMask(); int outPort = track->outPort(); //int inPort = track->inPortMask(); - unsigned int inPort = track->inPortMask(); + ///unsigned int inPort = track->inPortMask(); - midiTrackInfo->iInput->clear(); + //midiTrackInfo->iInput->clear(); midiTrackInfo->iOutput->clear(); for (int i = 0; i < MIDI_PORTS; ++i) { @@ -1185,7 +1228,8 @@ void Arranger::updateMidiTrackInfo(int flags) midiTrackInfo->iOutput->setCurrentItem(i); } //midiTrackInfo->iInput->setText(bitmap2String(inPort)); - midiTrackInfo->iInput->setText(u32bitmap2String(inPort)); + ///midiTrackInfo->iInput->setText(u32bitmap2String(inPort)); + //midiTrackInfo->iInputChannel->setText(bitmap2String(inChannel)); // Removed by Tim. p3.3.9 @@ -1195,7 +1239,7 @@ void Arranger::updateMidiTrackInfo(int flags) // } midiTrackInfo->iOutputChannel->setValue(outChannel+1); - midiTrackInfo->iInputChannel->setText(bitmap2String(inChannel)); + ///midiTrackInfo->iInputChannel->setText(bitmap2String(inChannel)); // Set record echo. if(midiTrackInfo->recEchoButton->isOn() != track->recEcho()) diff --git a/muse/muse/audio.cpp b/muse/muse/audio.cpp index d8c12331..5172447e 100644 --- a/muse/muse/audio.cpp +++ b/muse/muse/audio.cpp @@ -596,7 +596,9 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) // Added by Tim. p3.3.13 //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().latin1()); - track->copyData(samplePos, channels, frames, buffer); + // p3.3.38 + //track->copyData(samplePos, channels, frames, buffer); + track->copyData(samplePos, channels, -1, -1, frames, buffer); } } } diff --git a/muse/muse/audiotrack.cpp b/muse/muse/audiotrack.cpp index ece9bed6..fd6ba76a 100644 --- a/muse/muse/audiotrack.cpp +++ b/muse/muse/audiotrack.cpp @@ -22,6 +22,8 @@ // By T356. For caching jack in/out routing names BEFORE file save. // Jack often shuts down during file save, causing the routes to be lost in the file. // cacheJackRouteNames() is ONLY called from MusE::save() in app.cpp +// Update: Not required any more because the real problem was Jack RT priority, which has been fixed. +/* typedef std::multimap <const int, QString> jackRouteNameMap; std::map <const AudioTrack*, jackRouteNameMap > jackRouteNameCache; typedef std::multimap <const int, QString>::const_iterator ciJackRouteNameMap; @@ -54,13 +56,17 @@ void cacheJackRouteNames() } } } +*/ + //--------------------------------------------------------- // AudioTrack //--------------------------------------------------------- AudioTrack::AudioTrack(TrackType t) +//AudioTrack::AudioTrack(TrackType t, int num_out_bufs) : Track(t) { + //_totalOutChannels = num_out_bufs; // Is either parameter-default MAX_CHANNELS, or custom value passed (used by syntis). _processed = false; _haveData = false; _sendMetronome = false; @@ -79,8 +85,9 @@ AudioTrack::AudioTrack(TrackType t) //outBuffers = new float*[MAX_CHANNELS]; //for (int i = 0; i < MAX_CHANNELS; ++i) // outBuffers[i] = new float[segmentSize]; - for (int i = 0; i < MAX_CHANNELS; ++i) - posix_memalign((void**)(outBuffers + i), 16, sizeof(float) * segmentSize); + //for (int i = 0; i < MAX_CHANNELS; ++i) + // posix_memalign((void**)(outBuffers + i), 16, sizeof(float) * segmentSize); + // Let's allocate it all in one block, and just point the remaining buffer pointers into the block // which allows faster one-shot buffer copying. // Nope. Nice but interferes with possibility we don't know if other buffers are contiguous (jack buffers, local stack buffers etc.). @@ -88,6 +95,18 @@ AudioTrack::AudioTrack(TrackType t) //for (int i = 0; i < MAX_CHANNELS; ++i) // *(outBuffers + i) = sizeof(float) * segmentSize * i; + // p3.3.38 + // Easy way, less desirable... Start out with enough for MAX_CHANNELS. Then multi-channel syntis can re-allocate, + // via a call to (a modified!) setChannels(). + // Hard way, more desirable... Creating a synti instance passes the total channels to this constructor, overriding MAX_CHANNELS. + _totalOutChannels = MAX_CHANNELS; + outBuffers = new float*[_totalOutChannels]; + for (int i = 0; i < _totalOutChannels; ++i) + posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * segmentSize); + + // This is only set by multi-channel syntis... + _totalInChannels = 0; + bufferPos = MAXINT; setVolume(1.0); @@ -98,6 +117,7 @@ AudioTrack::AudioTrack(TrackType t) AudioTrack::AudioTrack(const AudioTrack& t, bool cloneParts) : Track(t, cloneParts) { + _totalOutChannels = t._totalOutChannels; // Is either MAX_CHANNELS, or custom value (used by syntis). _processed = false; _haveData = false; _sendMetronome = t._sendMetronome; @@ -112,8 +132,18 @@ AudioTrack::AudioTrack(const AudioTrack& t, bool cloneParts) //outBuffers = new float*[MAX_CHANNELS]; //for (int i = 0; i < MAX_CHANNELS; ++i) // outBuffers[i] = new float[segmentSize]; - for (int i = 0; i < MAX_CHANNELS; ++i) - posix_memalign((void**)(outBuffers + i), 16, sizeof(float) * segmentSize); + //for (int i = 0; i < MAX_CHANNELS; ++i) + // posix_memalign((void**)(outBuffers + i), 16, sizeof(float) * segmentSize); + + // p3.3.38 + int chans = _totalOutChannels; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(chans < MAX_CHANNELS) + chans = MAX_CHANNELS; + outBuffers = new float*[chans]; + for (int i = 0; i < chans; ++i) + posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * segmentSize); + bufferPos = MAXINT; _recFile = t._recFile; } @@ -124,11 +154,26 @@ AudioTrack::~AudioTrack() //for (int i = 0; i < MAX_CHANNELS; ++i) // delete[] outBuffers[i]; //delete[] outBuffers; - for(int i = 0; i < MAX_CHANNELS; ++i) + + // p3.3.15 + //for(int i = 0; i < MAX_CHANNELS; ++i) + //{ + // if(outBuffers[i]) + // free(outBuffers[i]); + //} + + // p3.3.38 + int chans = _totalOutChannels; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(chans < MAX_CHANNELS) + chans = MAX_CHANNELS; + for(int i = 0; i < chans; ++i) { if(outBuffers[i]) free(outBuffers[i]); } + delete[] outBuffers; + } //--------------------------------------------------------- @@ -1109,12 +1154,13 @@ void AudioTrack::mapRackPluginsToControllers() */ } +/* //--------------------------------------------------------- // writeRouting //--------------------------------------------------------- void AudioTrack::writeRouting(int level, Xml& xml) const - { +{ QString n; if (type() == Track::AUDIO_INPUT) { ciJackRouteNameCache circ = jackRouteNameCache.find(this); @@ -1168,7 +1214,9 @@ void AudioTrack::writeRouting(int level, Xml& xml) const } } } - } +} +*/ + //--------------------------------------------------------- // AudioInput //--------------------------------------------------------- diff --git a/muse/muse/conf.cpp b/muse/muse/conf.cpp index 57d6f4f0..8af9994b 100644 --- a/muse/muse/conf.cpp +++ b/muse/muse/conf.cpp @@ -40,6 +40,7 @@ #include "midiport.h" #include "mididev.h" #include "driver/audiodev.h" +#include "driver/jackmidi.h" #include "xml.h" #include "waveedit.h" #include "midi.h" @@ -216,6 +217,7 @@ static void readConfigMidiPort(Xml& xml) int openFlags = 1; bool thruFlag = false; MidiSyncInfo tmpSi; + int type = MidiDevice::ALSA_MIDI; for (;;) { Xml::Token token = xml.parse(); @@ -226,6 +228,8 @@ static void readConfigMidiPort(Xml& xml) case Xml::TagStart: if (tag == "name") device = xml.parse1(); + else if (tag == "type") + type = xml.parseInt(); else if (tag == "record") { // old bool f = xml.parseInt(); if (f) @@ -262,7 +266,19 @@ static void readConfigMidiPort(Xml& xml) idx, MIDI_PORTS); idx = 0; } + MidiDevice* dev = midiDevices.find(device); + + if(debugMsg && !dev) + fprintf(stderr, "readConfigMidiPort: device not found %s\n", device.latin1()); + + if(!dev && type == MidiDevice::JACK_MIDI) + { + if(debugMsg) + fprintf(stderr, "readConfigMidiPort: creating jack midi device %s\n", device.latin1()); + dev = MidiJackDevice::createJackMidiDevice(device, openFlags); + } + MidiPort* mp = &midiPorts[idx]; mp->syncInfo().copyParams(tmpSi); if (dev) { @@ -942,6 +958,13 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) xml.strTag(level, "instrument", mport->instrument()->iname()); if (dev) { xml.strTag(level, "name", dev->name()); + + // p3.3.38 + //if(dynamic_cast<MidiJackDevice*>(dev)) + if(dev->deviceType() != MidiDevice::ALSA_MIDI) + //xml.intTag(level, "type", MidiDevice::JACK_MIDI); + xml.intTag(level, "type", dev->deviceType()); + // Changed by T356. "record" is old and by mistake written as rwFlags here. // openFlags was read before, but never written here. //xml.intTag(level, "record", dev->rwFlags() & 0x2 ? 1 : 0); diff --git a/muse/muse/confmport.cpp b/muse/muse/confmport.cpp index a3761439..61960910 100644 --- a/muse/muse/confmport.cpp +++ b/muse/muse/confmport.cpp @@ -44,11 +44,16 @@ #include "synth.h" #include "audio.h" #include "midiseq.h" +#include "driver/alsamidi.h" +#include "driver/jackmidi.h" +#include "audiodev.h" +#include "menutitleitem.h" extern std::vector<Synth*> synthis; enum { DEVCOL_NO = 0, DEVCOL_GUI, DEVCOL_REC, DEVCOL_PLAY, DEVCOL_INSTR, DEVCOL_NAME, - DEVCOL_STATE }; + //DEVCOL_STATE }; + DEVCOL_ROUTES, DEVCOL_STATE }; //--------------------------------------------------------- // rbClicked @@ -95,36 +100,343 @@ void MPConfig::rbClicked(QListViewItem* item, const QPoint&, int col) dev->setOpenFlags(openFlags); midiSeq->msgSetMidiDevice(port, dev); // reopen device break; - case DEVCOL_NAME: + case DEVCOL_ROUTES: { - if (popup == 0) - popup = new QPopupMenu(this); - popup->clear(); - popup->insertItem(tr("<none>"), 0); - int id = 1; - for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { - const QString s = (*i)->name(); - popup->insertItem(s, id); - for (int k = 0; k < MIDI_PORTS; ++k) { - MidiDevice* dev = midiPorts[k].device(); - if (dev && s == dev->name()) { - popup->setItemEnabled(id, false); - break; - } - } - ++id; + if(!checkAudioDevice()) + return; + + if(!dev) + return; + + // Only Jack midi devices. + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(dev); + //if(!mjd) + if(dev->deviceType() != MidiDevice::JACK_MIDI) + return; + + if(!dev->rwFlags() & 3) + return; + + RouteList* rl = (dev->rwFlags() & 1) ? dev->outRoutes() : dev->inRoutes(); + + QPopupMenu* pup = 0; + int gid = 0; + std::list<QString> sl; + + _redisplay: + // Jack input ports if device is writable, and jack output ports if device is readable. + sl = (dev->rwFlags() & 1) ? audioDevice->inputPorts(true, _showAliases) : audioDevice->outputPorts(true, _showAliases); + + pup = new QPopupMenu(this); + pup->setCheckable(true); + + gid = 0; + //for (int i = 0; i < channel; ++i) + //{ + //char buffer[128]; + //snprintf(buffer, 128, "%s %d", tr("Channel").latin1(), i+1); + //MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); + //pup->insertItem(titel); + + pup->insertItem(tr("Show first aliases"), gid); + pup->setItemChecked(gid, (_showAliases == 0)); + ++gid; + pup->insertItem(tr("Show second aliases"), gid); + pup->setItemChecked(gid, (_showAliases == 1)); + ++gid; + pup->insertSeparator(); + + for(std::list<QString>::iterator ip = sl.begin(); ip != sl.end(); ++ip) + { + //int id = pup->insertItem(*ip, gid); + pup->insertItem(*ip, gid); + //Route dst(*ip, true, i); + Route rt(*ip, (dev->rwFlags() & 1), -1, Route::JACK_ROUTE); + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if (*ir == rt) + { + //pup->setItemChecked(id, true); + pup->setItemChecked(gid, true); + break; + } } - n = popup->exec(ppt, 0); - if (n == -1) - break; - - QString s = popup->text(n); - MidiDevice* dev = 0; - if (n > 0) - dev = midiDevices.find(s); - midiSeq->msgSetMidiDevice(port, dev); - muse->changeConfig(true); // save configuration file - song->update(); + ++gid; + } + //if (i+1 != channel) + // pup->insertSeparator(); + //} + + n = pup->exec(ppt, 0); + if (n != -1) + { + if(n == 0) // Show first aliases + { + delete pup; + if(_showAliases == 0) + _showAliases = -1; + else + _showAliases = 0; + goto _redisplay; // Go back + } + else + if(n == 1) // Show second aliases + { + delete pup; + if(_showAliases == 1) + _showAliases = -1; + else + _showAliases = 1; + goto _redisplay; // Go back + } + + QString s(pup->text(n)); + + if(dev->rwFlags() & 1) // Writable + { + Route srcRoute(dev, -1); + Route dstRoute(s, true, -1, Route::JACK_ROUTE); + + iRoute iir = rl->begin(); + for(; iir != rl->end(); ++iir) + { + if(*iir == dstRoute) + break; + } + if(iir != rl->end()) + // disconnect + audio->msgRemoveRoute(srcRoute, dstRoute); + else + // connect + audio->msgAddRoute(srcRoute, dstRoute); + } + else + if(dev->rwFlags() & 2) // Readable + { + Route srcRoute(s, false, -1, Route::JACK_ROUTE); + Route dstRoute(dev, -1); + + iRoute iir = rl->begin(); + for(; iir != rl->end(); ++iir) + { + if(*iir == srcRoute) + break; + } + if(iir != rl->end()) + // disconnect + audio->msgRemoveRoute(srcRoute, dstRoute); + else + // connect + audio->msgAddRoute(srcRoute, dstRoute); + } + + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + delete pup; + //iR->setDown(false); // pup->exec() catches mouse release event + + + + } + break; + case DEVCOL_NAME: + { + QPopupMenu* pup = new QPopupMenu(this); + + pup->setCheckable(true); + + pup->insertItem(tr("Create") + QT_TR_NOOP(" Jack") + tr(" input"), 0); + pup->insertItem(tr("Create") + QT_TR_NOOP(" Jack") + tr(" output"), 1); + + typedef std::map<std::string, int > asmap; + typedef std::map<std::string, int >::iterator imap; + + asmap mapALSA; + asmap mapJACK; + asmap mapSYNTH; + + int aix = 2; + int jix = 0x10000000; + int six = 0x20000000; + for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) + { + //devALSA = dynamic_cast<MidiAlsaDevice*>(*i); + //if(devALSA) + if((*i)->deviceType() == MidiDevice::ALSA_MIDI) + { + //mapALSA.insert( std::pair<std::string, int> (std::string(devALSA->name().lower().latin1()), ii) ); + mapALSA.insert( std::pair<std::string, int> (std::string((*i)->name().latin1()), aix) ); + ++aix; + } + else + if((*i)->deviceType() == MidiDevice::JACK_MIDI) + { + //devJACK = dynamic_cast<MidiJackDevice*>(*i); + //if(devJACK) + //mapJACK.insert( std::pair<std::string, int> (std::string(devJACK->name().lower().latin1()), ii) ); + mapJACK.insert( std::pair<std::string, int> (std::string((*i)->name().latin1()), jix) ); + ++jix; + } + else + if((*i)->deviceType() == MidiDevice::SYNTH_MIDI) + { + mapSYNTH.insert( std::pair<std::string, int> (std::string((*i)->name().latin1()), six) ); + ++six; + } + else + printf("MPConfig::rbClicked unknown midi device: %s\n", (*i)->name().latin1()); + } + + //int sz = midiDevices.size(); + if(!mapALSA.empty()) + { + pup->insertSeparator(); + pup->insertItem(new MenuTitleItem(QT_TR_NOOP("ALSA:"))); + + for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) + { + int idx = i->second; + //if(idx > sz) // Sanity check + // continue; + QString s(i->first.c_str()); + MidiDevice* md = midiDevices.find(s, MidiDevice::ALSA_MIDI); + if(md) + { + //if(!dynamic_cast<MidiAlsaDevice*>(md)) + if(md->deviceType() != MidiDevice::ALSA_MIDI) + continue; + + //pup->insertItem(QT_TR_NOOP(md->name()), idx + 3); + pup->insertItem(QT_TR_NOOP(md->name()), idx); + + //for(int k = 0; k < MIDI_PORTS; ++k) + //{ + //MidiDevice* dev = midiPorts[k].device(); + //if(dev && s == dev->name()) + if(md == dev) + { + //pup->setItemEnabled(idx + 3, false); + //pup->setItemChecked(idx + 3, true); + pup->setItemChecked(idx, true); + //break; + } + //} + } + } + } + + if(!mapJACK.empty()) + { + pup->insertSeparator(); + pup->insertItem(new MenuTitleItem(QT_TR_NOOP("JACK:"))); + + for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) + { + int idx = i->second; + //if(idx > sz) + // continue; + QString s(i->first.c_str()); + MidiDevice* md = midiDevices.find(s, MidiDevice::JACK_MIDI); + if(md) + { + //if(!dynamic_cast<MidiJackDevice*>(md)) + if(md->deviceType() != MidiDevice::JACK_MIDI) + continue; + + //pup->insertItem(QT_TR_NOOP(md->name()), idx + 3); + pup->insertItem(QT_TR_NOOP(md->name()), idx); + + //for(int k = 0; k < MIDI_PORTS; ++k) + //{ + //MidiDevice* dev = midiPorts[k].device(); + //if(dev && s == dev->name()) + if(md == dev) + { + //pup->setItemEnabled(idx + 3, false); + //pup->setItemChecked(idx + 3, true); + pup->setItemChecked(idx, true); + //break; + } + //} + } + } + } + + if(!mapSYNTH.empty()) + { + pup->insertSeparator(); + pup->insertItem(new MenuTitleItem(QT_TR_NOOP("SYNTH:"))); + + for(imap i = mapSYNTH.begin(); i != mapSYNTH.end(); ++i) + { + int idx = i->second; + //if(idx > sz) + // continue; + QString s(i->first.c_str()); + MidiDevice* md = midiDevices.find(s, MidiDevice::SYNTH_MIDI); + if(md) + { + //if(!dynamic_cast<MidiJackDevice*>(md)) + if(md->deviceType() != MidiDevice::SYNTH_MIDI) + continue; + + //pup->insertItem(QT_TR_NOOP(md->name()), idx + 3); + pup->insertItem(QT_TR_NOOP(md->name()), idx); + + //for(int k = 0; k < MIDI_PORTS; ++k) + //{ + //MidiDevice* dev = midiPorts[k].device(); + //if(dev && s == dev->name()) + if(md == dev) + { + //pup->setItemEnabled(idx + 3, false); + //pup->setItemChecked(idx + 3, true); + pup->setItemChecked(idx, true); + //break; + } + //} + } + } + } + + n = pup->exec(ppt, 0); + if(n == -1) + { + delete pup; + break; + } + + //printf("MPConfig::rbClicked n:%d\n", n); + + MidiDevice* sdev = 0; + if(n < 2) + { + delete pup; + if(n == 0) + sdev = MidiJackDevice::createJackMidiDevice(QString(), 2); // 2: Readable. + else + if(n == 1) + sdev = MidiJackDevice::createJackMidiDevice(QString(), 1); // 1:Writable. + } + else + { + int typ = MidiDevice::ALSA_MIDI; + if(n >= 0x10000000) + typ = MidiDevice::JACK_MIDI; + if(n >= 0x20000000) + typ = MidiDevice::SYNTH_MIDI; + + sdev = midiDevices.find(pup->text(n), typ); + delete pup; + // Is it the current device? Reset it to <none>. + if(sdev == dev) + sdev = 0; + } + + midiSeq->msgSetMidiDevice(port, sdev); + muse->changeConfig(true); // save configuration file + song->update(); } break; @@ -189,6 +501,8 @@ QString MPWhatsThis::text(const QPoint& pos) " this port number"); case DEVCOL_INSTR: return QHeader::tr("Instrument connected to port"); + case DEVCOL_ROUTES: + return QHeader::tr("Jack midi ports"); case DEVCOL_STATE: return QHeader::tr("State: result of opening the device"); default: @@ -207,7 +521,8 @@ MPConfig::MPConfig(QWidget* parent, char* name) { popup = 0; instrPopup = 0; - + _showAliases = -1; // 0: Show first aliases, if available. Nah, stick with -1: none at first. + mdevView->setSorting(-1); mdevView->setAllColumnsShowFocus(true); mdevView->addColumn(tr("Port")); @@ -216,6 +531,7 @@ MPConfig::MPConfig(QWidget* parent, char* name) mdevView->addColumn(tr("O")); mdevView->addColumn(tr("Instrument"), 120); mdevView->addColumn(tr("Device Name"), 120); + mdevView->addColumn(tr("Routing"), 80); mdevView->addColumn(tr("State")); mdevView->setFocusPolicy(NoFocus); @@ -279,6 +595,11 @@ void MPConfig::songChanged(int flags) item->setText(DEVCOL_INSTR, tr("<unknown>")); if (dev) { item->setText(DEVCOL_NAME, dev->name()); + // Is it a Jack midi device? Allow renaming. + //if(dynamic_cast<MidiJackDevice*>(dev)) + if(dev->deviceType() == MidiDevice::JACK_MIDI) + item->setRenameEnabled(DEVCOL_NAME, true); + if (dev->rwFlags() & 0x2) item->setPixmap(DEVCOL_REC, dev->openFlags() & 2 ? *dotIcon : *dothIcon); else @@ -303,6 +624,14 @@ void MPConfig::songChanged(int flags) if (!(dev && dev->isSynti())) item->setPixmap(DEVCOL_INSTR, *buttondownIcon); item->setPixmap(DEVCOL_NAME, *buttondownIcon); + + //if(dev && dynamic_cast<MidiJackDevice*>(dev)) + if(dev && dev->deviceType() == MidiDevice::JACK_MIDI) + { + item->setPixmap(DEVCOL_ROUTES, *buttondownIcon); + item->setText(DEVCOL_ROUTES, tr("routes")); + } + mdevView->insertItem(item); } diff --git a/muse/muse/confmport.h b/muse/muse/confmport.h index b98a2189..577c0afd 100644 --- a/muse/muse/confmport.h +++ b/muse/muse/confmport.h @@ -45,7 +45,9 @@ class MPWhatsThis : public QWhatsThis { class MPConfig : public SynthConfigBase { QPopupMenu* popup; QPopupMenu* instrPopup; - + + int _showAliases; // -1: None. 0: First aliases. 1: Second aliases etc. + Q_OBJECT private slots: diff --git a/muse/muse/driver/alsamidi.cpp b/muse/muse/driver/alsamidi.cpp index c9e14bbc..bcd1bd75 100644 --- a/muse/muse/driver/alsamidi.cpp +++ b/muse/muse/driver/alsamidi.cpp @@ -18,6 +18,8 @@ #include "mpevent.h" //#include "sync.h" #include "utils.h" +#include "audiodev.h" +#include "xml.h" static int alsaSeqFdi = -1; static int alsaSeqFdo = -1; @@ -169,6 +171,101 @@ void MidiAlsaDevice::close() } //--------------------------------------------------------- +// writeRouting +//--------------------------------------------------------- + +void MidiAlsaDevice::writeRouting(int level, Xml& xml) const +{ + QString s; + /* + //if(rwFlags() & 2) // Readable + { + //RouteList* rl = _inRoutes; + //for (ciRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) + { + // Since an ALSA midi device supports read + write, this is the only way we can tell if this route is using the device as input. + if(r->type == Route::TRACK_ROUTE) + continue; + + if(!r->name().isEmpty()) + { + xml.tag(level++, "Route"); + + //xml.strTag(level, "srcNode", r->name()); + xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + + //xml.strTag(level, "dstNode", name()); + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().latin1()); + + xml.etag(level--, "Route"); + } + } + } + */ + + for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) + { + //if(r->type != Route::TRACK_ROUTE) + //{ + // printf("MidiAlsaDevice::writeRouting Warning out route is not TRACK_ROUTE type\n"); + // continue; + //} + + if(!r->name().isEmpty()) + { + //xml.tag(level++, "Route"); + + s = QT_TR_NOOP("Route"); + if(r->channel != -1) + s += QString(QT_TR_NOOP(" channel=\"%1\"")).arg(r->channel); + xml.tag(level++, s); + + /* + //xml.strTag(level, "srcNode", name()); + if(r->channel != -1) + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, r->channel, name().latin1()); + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().latin1()); + xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, r->channel, name().latin1()); + else + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().latin1()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().latin1()); + */ + xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, name().latin1()); + + /* + //xml.strTag(level, "dstNode", r->name()); + if(r->channel != -1) + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().latin1()); + } + else + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + } + */ + + s = QT_TR_NOOP("dest"); + if(r->type == Route::MIDI_DEVICE_ROUTE) + s += QString(QT_TR_NOOP(" devtype=\"%1\"")).arg(r->device->deviceType()); + else + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + xml.tag(level, s); + + xml.etag(level--, "Route"); + } + } +} + +//--------------------------------------------------------- // putEvent //--------------------------------------------------------- @@ -418,7 +515,11 @@ bool initMidiAlsa() } } - snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); + + // p3.3.38 + //snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); + snd_seq_set_client_name(alsaSeq, audioDevice->clientName()); + int ci = snd_seq_poll_descriptors_count(alsaSeq, POLLIN); int co = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT); @@ -721,28 +822,39 @@ void alsaProcessMidiInput() break; case SND_SEQ_EVENT_CLOCK: - midiSeq->realtimeSystemInput(curPort, 0xf8); + midiSeq->realtimeSystemInput(curPort, ME_CLOCK); //mdev->syncInfo().trigMCSyncDetect(); break; case SND_SEQ_EVENT_START: - midiSeq->realtimeSystemInput(curPort, 0xfa); + midiSeq->realtimeSystemInput(curPort, ME_START); break; case SND_SEQ_EVENT_CONTINUE: - midiSeq->realtimeSystemInput(curPort, 0xfb); + midiSeq->realtimeSystemInput(curPort, ME_CONTINUE); break; case SND_SEQ_EVENT_STOP: - midiSeq->realtimeSystemInput(curPort, 0xfc); + midiSeq->realtimeSystemInput(curPort, ME_STOP); break; case SND_SEQ_EVENT_TICK: - midiSeq->realtimeSystemInput(curPort, 0xf9); + midiSeq->realtimeSystemInput(curPort, ME_TICK); //mdev->syncInfo().trigTickDetect(); break; case SND_SEQ_EVENT_SYSEX: + + // TODO: Deal with large sysex, which are broken up into chunks! + // For now, do not accept if the first byte is not SYSEX or the last byte is not EOX, + // meaning it's a chunk, possibly with more chunks to follow. + if((*((unsigned char*)ev->data.ext.ptr) != ME_SYSEX) || + (*(((unsigned char*)ev->data.ext.ptr) + ev->data.ext.len - 1) != ME_SYSEX_END)) + { + printf("MusE: alsaProcessMidiInput sysex chunks not supported!\n"); + break; + } + event.setTime(0); // mark as used event.setType(ME_SYSEX); event.setData((unsigned char*)(ev->data.ext.ptr)+1, diff --git a/muse/muse/driver/alsamidi.h b/muse/muse/driver/alsamidi.h index 48645706..6c19ff0d 100644 --- a/muse/muse/driver/alsamidi.h +++ b/muse/muse/driver/alsamidi.h @@ -13,6 +13,8 @@ #include "mididev.h" +class Xml; + //--------------------------------------------------------- // MidiAlsaDevice //--------------------------------------------------------- @@ -35,6 +37,9 @@ class MidiAlsaDevice : public MidiDevice { MidiAlsaDevice() {} MidiAlsaDevice(const snd_seq_addr_t&, const QString& name); virtual ~MidiAlsaDevice() {} + virtual void* clientPort() { return (void*)&adr; } + virtual void writeRouting(int, Xml&) const; + virtual inline int deviceType() { return ALSA_MIDI; } }; extern bool initMidiAlsa(); diff --git a/muse/muse/driver/audiodev.h b/muse/muse/driver/audiodev.h index f66627b8..b888f6eb 100644 --- a/muse/muse/driver/audiodev.h +++ b/muse/muse/driver/audiodev.h @@ -33,11 +33,13 @@ class AudioDevice { virtual float* getBuffer(void* port, unsigned long nframes) = 0; - virtual std::list<QString> outputPorts() = 0; - virtual std::list<QString> inputPorts() = 0; + virtual std::list<QString> outputPorts(bool midi = false, int aliases = -1) = 0; + virtual std::list<QString> inputPorts(bool midi = false, int aliases = -1) = 0; virtual void registerClient() = 0; + virtual const char* clientName() = 0; + //virtual void* registerOutPort(const char* name) = 0; //virtual void* registerInPort(const char* name) = 0; virtual void* registerOutPort(const char* /*name*/, bool /*midi*/) = 0; diff --git a/muse/muse/driver/dummyaudio.cpp b/muse/muse/driver/dummyaudio.cpp index 2695f705..0bca9890 100644 --- a/muse/muse/driver/dummyaudio.cpp +++ b/muse/muse/driver/dummyaudio.cpp @@ -88,11 +88,13 @@ class DummyAudioDevice : public AudioDevice { return buffer; } - virtual std::list<QString> outputPorts(); - virtual std::list<QString> inputPorts(); + virtual std::list<QString> outputPorts(bool midi = false, int aliases = -1); + virtual std::list<QString> inputPorts(bool midi = false, int aliases = -1); virtual void registerClient() {} + virtual const char* clientName() { return "MusE"; } + //virtual void* registerOutPort(const char*) { virtual void* registerOutPort(const char*, bool) { return (void*)1; @@ -214,11 +216,14 @@ bool initDummyAudio() // outputPorts //--------------------------------------------------------- -std::list<QString> DummyAudioDevice::outputPorts() +std::list<QString> DummyAudioDevice::outputPorts(bool midi, int /*aliases*/) { std::list<QString> clientList; - clientList.push_back(QString("output1")); - clientList.push_back(QString("output2")); + if(!midi) + { + clientList.push_back(QString("output1")); + clientList.push_back(QString("output2")); + } return clientList; } @@ -226,11 +231,14 @@ std::list<QString> DummyAudioDevice::outputPorts() // inputPorts //--------------------------------------------------------- -std::list<QString> DummyAudioDevice::inputPorts() +std::list<QString> DummyAudioDevice::inputPorts(bool midi, int /*aliases*/) { std::list<QString> clientList; - clientList.push_back(QString("input1")); - clientList.push_back(QString("input2")); + if(!midi) + { + clientList.push_back(QString("input1")); + clientList.push_back(QString("input2")); + } return clientList; } diff --git a/muse/muse/driver/jack.cpp b/muse/muse/driver/jack.cpp index 3329b73f..2926e281 100644 --- a/muse/muse/driver/jack.cpp +++ b/muse/muse/driver/jack.cpp @@ -6,6 +6,8 @@ //========================================================= #include "config.h" +#include <string> +#include <set> #include <stdio.h> #include <stdlib.h> #include <errno.h> @@ -123,6 +125,7 @@ static void jack_thread_init (void* ) // data undoSetuid(); } +/* //--------------------------------------------------------- // processAudio + Midi // JACK callback @@ -137,6 +140,7 @@ print_triplet(unsigned char *data) memcpy(&c, data+2, 1); fprintf(stderr, "%x,%x,%x", a, b, c); } +*/ /* void handle_jack_midi_in_events(jack_nframes_t frames) @@ -430,7 +434,7 @@ static void noJackError(const char* /* s */) // JackAudioDevice //--------------------------------------------------------- -JackAudioDevice::JackAudioDevice(jack_client_t* cl, char * name) +JackAudioDevice::JackAudioDevice(jack_client_t* cl, char* name) : AudioDevice() { _frameCounter = 0; @@ -496,6 +500,7 @@ int JackAudioDevice::realtimePriority() const return param.sched_priority; } +/* //--------------------------------------------------------- // getJackName() //--------------------------------------------------------- @@ -504,6 +509,22 @@ char* JackAudioDevice::getJackName() { return jackRegisteredName; } +*/ + +/* +//--------------------------------------------------------- +// clientName() +//--------------------------------------------------------- + +const char* JackAudioDevice::clientName() +{ + //if(_client) + // return jack_get_client_name(_client); + //else + // return "MusE"; + return jackRegisteredName; +} +*/ //--------------------------------------------------------- // initJackAudio @@ -531,25 +552,41 @@ bool initJackAudio() jack_set_error_function(noJackError); doSetuid(); - jack_client_t* client = 0; - int i = 0; - char jackIdString[8]; - for (i = 0; i < 5; ++i) { - sprintf(jackIdString, "MusE-%d", i+1); + //jack_client_t* client = 0; + //int i = 0; + //char jackIdString[8]; + //for (i = 0; i < 5; ++i) { + // sprintf(jackIdString, "MusE-%d", i+1); //client = jack_client_new(jackIdString); - client = jack_client_open(jackIdString, JackNoStartServer, 0); - if (client) - break; - } - - if (i == 5) + // client = jack_client_open(jackIdString, JackNoStartServer, 0); + // if (client) + // break; + // } + //if (i == 5) + // return true; + jack_status_t status; + jack_client_t* client = jack_client_open("MusE", JackNoStartServer, &status); + if (!client) { + if (status & JackServerStarted) + printf("jack server started...\n"); + if (status & JackServerFailed) + printf("cannot connect to jack server\n"); + if (status & JackServerError) + printf("communication with jack server failed\n"); + if (status & JackShmFailure) + printf("jack cannot access shared memory\n"); + if (status & JackVersionError) + printf("jack server has wrong version\n"); + printf("cannot create jack client\n"); return true; + } if (debugMsg) - fprintf(stderr, "initJackAudio(): client %s opened.\n", jackIdString); + fprintf(stderr, "initJackAudio(): client %s opened.\n", jack_get_client_name(client)); if (client) { jack_set_error_function(jackError); - jackAudio = new JackAudioDevice(client, jackIdString); + //jackAudio = new JackAudioDevice(client, jackIdString); + jackAudio = new JackAudioDevice(client, jack_get_client_name(client)); if (debugMsg) fprintf(stderr, "initJackAudio(): registering client...\n"); jackAudio->registerClient(); @@ -648,8 +685,10 @@ void JackAudioDevice::registrationChanged() if(JACK_DEBUG) printf("JackAudioDevice::registrationChanged()\n"); + // Rescan. + scanMidiPorts(); // Connect the Jack midi client ports to the device ports. - connectJackMidiPorts(); + //connectJackMidiPorts(); } //--------------------------------------------------------- @@ -661,6 +700,32 @@ void JackAudioDevice::connectJackMidiPorts() if(JACK_DEBUG) printf("JackAudioDevice::connectJackMidiPorts()\n"); + for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) + { + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(*i); + //if(!mjd) + MidiDevice* md = *i; + if(md->deviceType() != MidiDevice::JACK_MIDI) + continue; + + void* port = md->clientPort(); + if(md->rwFlags() & 1) + { + RouteList* rl = md->outRoutes(); + for (iRoute r = rl->begin(); r != rl->end(); ++r) + connect(port, r->jackPort); + } + else + if(md->rwFlags() & 2) + { + RouteList* rl = md->inRoutes(); + for (iRoute r = rl->begin(); r != rl->end(); ++r) + connect(r->jackPort, port); + } + } + + + /* const char* type = JACK_DEFAULT_MIDI_TYPE; const char** ports = jack_get_ports(_client, 0, type, 0); for (const char** p = ports; p && *p; ++p) @@ -668,6 +733,13 @@ void JackAudioDevice::connectJackMidiPorts() jack_port_t* port = jack_port_by_name(_client, *p); if(!port) continue; + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf(" ignoring own port: %s\n", *p); + continue; + } int nsz = jack_port_name_size(); char buffer[nsz]; strncpy(buffer, *p, nsz); @@ -675,6 +747,9 @@ void JackAudioDevice::connectJackMidiPorts() //if(strncmp(buffer, "MusE", 4) == 0) // continue; + if(debugMsg) + printf(" found port: %s ", buffer); + // If there are aliases for this port, use the first one - much better for identifying. //char a1[nsz]; char a2[nsz]; @@ -688,6 +763,9 @@ void JackAudioDevice::connectJackMidiPorts() //char* namep = (na >= 1) ? aliases[0] : buffer; char* namep = aliases[0]; + if(debugMsg) + printf("alias: %s\n", aliases[0]); + //int flags = 0; int pf = jack_port_flags(port); // If Jack port can send data to us... @@ -753,6 +831,8 @@ void JackAudioDevice::connectJackMidiPorts() if(ports) free(ports); + + */ } //--------------------------------------------------------- // client_registration_callback @@ -846,7 +926,8 @@ void JackAudioDevice::graphChanged() } if (!found) { audio->msgRemoveRoute1( - Route(portName, false, channel), + //Route(portName, false, channel), + Route(portName, false, channel, Route::JACK_ROUTE), Route(it, channel) ); erased = true; @@ -877,7 +958,8 @@ void JackAudioDevice::graphChanged() } if (!found) { audio->msgAddRoute1( - Route(*pn, false, channel), + //Route(*pn, false, channel), + Route(*pn, false, channel, Route::JACK_ROUTE), Route(it, channel) ); } @@ -929,7 +1011,8 @@ void JackAudioDevice::graphChanged() if (!found) { audio->msgRemoveRoute1( Route(it, channel), - Route(portName, false, channel) + //Route(portName, false, channel) + Route(portName, false, channel, Route::JACK_ROUTE) ); erased = true; break; @@ -960,7 +1043,8 @@ void JackAudioDevice::graphChanged() if (!found) { audio->msgAddRoute1( Route(it, channel), - Route(*pn, false, channel) + //Route(*pn, false, channel) + Route(*pn, false, channel, Route::JACK_ROUTE) ); } ++pn; @@ -975,6 +1059,201 @@ void JackAudioDevice::graphChanged() } } + for (iMidiDevice ii = midiDevices.begin(); ii != midiDevices.end(); ++ii) + { + MidiDevice* md = *ii; + if(md->deviceType() != MidiDevice::JACK_MIDI) + continue; + + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(*ii); + //if(!mjd) + // continue; + //for (int channel = 0; channel < channels; ++channel) + //{ + jack_port_t* port = (jack_port_t*)md->clientPort(); + if (port == 0) + continue; + const char** ports = jack_port_get_all_connections(_client, port); + + //--------------------------------------- + // outputs + //--------------------------------------- + + if(md->rwFlags() & 1) // Writable + { + RouteList* rl = md->outRoutes(); + + //--------------------------------------- + // check for disconnects + //--------------------------------------- + + bool erased; + // limit set to 20 iterations for disconnects, don't know how to make it go + // the "right" amount + for (int i = 0; i < 20 ; i++) + { + erased = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + bool found = false; + const char** pn = ports; + while (pn && *pn) { + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + ++pn; + } + if (!found) { + audio->msgRemoveRoute1( + //Route(it, channel), + //Route(mjd), + Route(md, -1), + //Route(portName, false, channel) + //Route(portName, false, -1) + Route(portName, false, -1, Route::JACK_ROUTE) + ); + erased = true; + break; + } + } + if (!erased) + break; + } + + //--------------------------------------- + // check for connects + //--------------------------------------- + + if (ports) + { + const char** pn = ports; + while (*pn) { + bool found = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + } + if (!found) { + audio->msgAddRoute1( + //Route(it, channel), + //Route(mjd), + Route(md, -1), + //Route(*pn, false, channel) + //Route(*pn, false, -1) + Route(*pn, false, -1, Route::JACK_ROUTE) + ); + } + ++pn; + } + + // p3.3.37 + //delete ports; + //free(ports); + + //ports = NULL; + } + } + + + //------------------------ + // Inputs + //------------------------ + + if(md->rwFlags() & 2) // Readable + { + RouteList* rl = md->inRoutes(); + + //--------------------------------------- + // check for disconnects + //--------------------------------------- + + bool erased; + // limit set to 20 iterations for disconnects, don't know how to make it go + // the "right" amount + for (int i = 0; i < 20 ; i++) + { + erased = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + bool found = false; + const char** pn = ports; + while (pn && *pn) { + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + ++pn; + } + if (!found) { + audio->msgRemoveRoute1( + //Route(portName, false, channel), + //Route(portName, false, -1), + Route(portName, false, -1, Route::JACK_ROUTE), + //Route(it, channel) + //Route(mjd) + Route(md, -1) + ); + erased = true; + break; + } + } + if (!erased) + break; + } + + //--------------------------------------- + // check for connects + //--------------------------------------- + + if (ports) + { + const char** pn = ports; + while (*pn) { + bool found = false; + for (iRoute irl = rl->begin(); irl != rl->end(); ++irl) { + //if (irl->channel != channel) + // continue; + QString name = irl->name(); + const char* portName = name.latin1(); + if (strcmp(*pn, portName) == 0) { + found = true; + break; + } + } + if (!found) { + audio->msgAddRoute1( + //Route(*pn, false, channel), + //Route(*pn, false, -1), + Route(*pn, false, -1, Route::JACK_ROUTE), + //Route(it, channel) + //Route(mjd) + Route(md, -1) + ); + } + ++pn; + } + } + } + if(ports) + // Done with ports. Free them. + //delete ports; + free(ports); + + ports = NULL; + } } //static int xrun_callback(void*) @@ -1279,24 +1558,67 @@ int JackAudioDevice::frameDelay() const // outputPorts //--------------------------------------------------------- -std::list<QString> JackAudioDevice::outputPorts() +std::list<QString> JackAudioDevice::outputPorts(bool midi, int aliases) { if (JACK_DEBUG) printf("JackAudioDevice::outputPorts()\n"); std::list<QString> clientList; if(!checkJackClient(_client)) return clientList; - const char** ports = jack_get_ports(_client, 0, JACK_DEFAULT_AUDIO_TYPE, 0); + QString qname; + const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; + const char** ports = jack_get_ports(_client, 0, type, JackPortIsOutput); for (const char** p = ports; p && *p; ++p) { jack_port_t* port = jack_port_by_name(_client, *p); - int flags = jack_port_flags(port); - if (!(flags & JackPortIsOutput)) - continue; - char buffer[128]; - strncpy(buffer, *p, 128); - if (strncmp(buffer, "MusE", 4) == 0) - continue; - clientList.push_back(QString(buffer)); + //int flags = jack_port_flags(port); + //if (!(flags & JackPortIsOutput)) + // continue; + //char buffer[128]; + + int nsz = jack_port_name_size(); + char buffer[nsz]; + + strncpy(buffer, *p, nsz); + //if (strncmp(buffer, "MusE", 4) == 0) + //{ + // if(debugMsg) + // printf("JackAudioDevice::outputPorts ignoring own MusE port: %s\n", *p); + // continue; + //} + + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf("JackAudioDevice::outputPorts ignoring own port: %s\n", *p); + continue; } + + // p3.3.38 + if((aliases == 0) || (aliases == 1)) + { + //char a1[nsz]; + char a2[nsz]; + char* al[2]; + //aliases[0] = a1; + al[0] = buffer; + al[1] = a2; + int na = jack_port_get_aliases(port, al); + int a = aliases; + if(a >= na) + { + a = na; + if(a > 0) + a--; + } + qname = QString(al[a]); + } + else + qname = QString(buffer); + + //clientList.push_back(QString(buffer)); + clientList.push_back(qname); + } + // p3.3.37 if(ports) free(ports); @@ -1308,24 +1630,67 @@ std::list<QString> JackAudioDevice::outputPorts() // inputPorts //--------------------------------------------------------- -std::list<QString> JackAudioDevice::inputPorts() +std::list<QString> JackAudioDevice::inputPorts(bool midi, int aliases) { if (JACK_DEBUG) printf("JackAudioDevice::inputPorts()\n"); std::list<QString> clientList; if(!checkJackClient(_client)) return clientList; - const char** ports = jack_get_ports(_client, 0, JACK_DEFAULT_AUDIO_TYPE, 0); + QString qname; + const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; + const char** ports = jack_get_ports(_client, 0, type, JackPortIsInput); for (const char** p = ports; p && *p; ++p) { jack_port_t* port = jack_port_by_name(_client, *p); - int flags = jack_port_flags(port); - if (!(flags & JackPortIsInput)) - continue; - char buffer[128]; - strncpy(buffer, *p, 128); - if (strncmp(buffer, "MusE", 4) == 0) - continue; - clientList.push_back(QString(buffer)); + //int flags = jack_port_flags(port); + //if (!(flags & JackPortIsInput)) + // continue; + //char buffer[128]; + + int nsz = jack_port_name_size(); + char buffer[nsz]; + + strncpy(buffer, *p, nsz); + //if (strncmp(buffer, "MusE", 4) == 0) + //{ + // if(debugMsg) + // printf("JackAudioDevice::inputPorts ignoring own MusE port: %s\n", *p); + // continue; + //} + + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf("JackAudioDevice::inputPorts ignoring own port: %s\n", *p); + continue; + } + + // p3.3.38 + if((aliases == 0) || (aliases == 1)) + { + //char a1[nsz]; + char a2[nsz]; + char* al[2]; + //aliases[0] = a1; + al[0] = buffer; + al[1] = a2; + int na = jack_port_get_aliases(port, al); + int a = aliases; + if(a >= na) + { + a = na; + if(a > 0) + a--; + } + qname = QString(al[a]); + } + else + qname = QString(buffer); + + //clientList.push_back(QString(buffer)); + clientList.push_back(qname); } + // p3.3.37 if(ports) free(ports); @@ -1678,22 +2043,34 @@ void JackAudioDevice::scanMidiPorts() { if(debugMsg) printf("JackAudioDevice::scanMidiPorts:\n"); + +/* const char* type = JACK_DEFAULT_MIDI_TYPE; const char** ports = jack_get_ports(_client, 0, type, 0); + + std::set<std::string> names; for (const char** p = ports; p && *p; ++p) { jack_port_t* port = jack_port_by_name(_client, *p); if(!port) continue; + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(debugMsg) + printf(" ignoring own port: %s\n", *p); + continue; + } + int nsz = jack_port_name_size(); char buffer[nsz]; strncpy(buffer, *p, nsz); // Ignore the MusE Jack port. - if(strncmp(buffer, "MusE", 4) == 0) - continue; + //if(strncmp(buffer, "MusE", 4) == 0) + // continue; if(debugMsg) - printf(" found port:%s\n", buffer); + printf(" found port: %s ", buffer); // If there are aliases for this port, use the first one - much better for identifying. //char a1[nsz]; @@ -1706,8 +2083,75 @@ void JackAudioDevice::scanMidiPorts() jack_port_get_aliases(port, aliases); //int na = jack_port_get_aliases(port, aliases); //char* namep = (na >= 1) ? aliases[0] : buffer; - char* namep = aliases[0]; + //char* namep = aliases[0]; + //names.insert(std::string(*p)); + if(debugMsg) + printf("alias: %s\n", aliases[0]); + + names.insert(std::string(aliases[0])); + } + if(ports) + free(ports); + std::list<MidiDevice*> to_del; + for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) + { + // Only Jack midi devices. + if(dynamic_cast<MidiJackDevice*>(*imd) == 0) + continue; + if(names.find(std::string((*imd)->name().latin1())) == names.end()) + to_del.push_back(*imd); + } + + for(std::list<MidiDevice*>::iterator imd = to_del.begin(); imd != to_del.end(); ++imd) + { + if(debugMsg) + printf(" removing port device:%s\n", (*imd)->name().latin1()); + midiDevices.remove(*imd); + // This will close (and unregister) the client port. + delete (*imd); + } + + //for (const char** p = ports; p && *p; ++p) + for(std::set<std::string>::iterator is = names.begin(); is != names.end(); ++is) + { + //jack_port_t* port = jack_port_by_name(_client, *p); + jack_port_t* port = jack_port_by_name(_client, is->c_str()); + if(!port) + continue; +*/ + + /* + int nsz = jack_port_name_size(); + char buffer[nsz]; + //strncpy(buffer, *p, nsz); + strncpy(buffer, is->c_str(), nsz); + // Ignore the MusE Jack port. + //if(strncmp(buffer, "MusE", 4) == 0) + // continue; + + // If there are aliases for this port, use the first one - much better for identifying. + //char a1[nsz]; + char a2[nsz]; + char* aliases[2]; + //aliases[0] = a1; + aliases[0] = buffer; + aliases[1] = a2; + // To disable aliases, just rem this line. + jack_port_get_aliases(port, aliases); + //int na = jack_port_get_aliases(port, aliases); + //char* namep = (na >= 1) ? aliases[0] : buffer; + char* namep = aliases[0]; + QString qname(namep); + */ + +/* + QString qname(is->c_str()); + + // Port already exists? + if(midiDevices.find(qname)) + continue; + int flags = 0; int pf = jack_port_flags(port); // If Jack port can send data to us... @@ -1722,12 +2166,13 @@ void JackAudioDevice::scanMidiPorts() //JackPort jp(0, QString(buffer), flags); //portList.append(jp); - MidiJackDevice* dev = new MidiJackDevice(0, QString(namep)); + if(debugMsg) + printf(" adding port device:%s\n", qname.latin1()); + + MidiJackDevice* dev = new MidiJackDevice(0, qname); dev->setrwFlags(flags); midiDevices.add(dev); } - // p3.3.37 - if(ports) - free(ports); +*/ } diff --git a/muse/muse/driver/jackaudio.h b/muse/muse/driver/jackaudio.h index 086e36db..242e762d 100644 --- a/muse/muse/driver/jackaudio.h +++ b/muse/muse/driver/jackaudio.h @@ -25,7 +25,7 @@ class JackAudioDevice : public AudioDevice { int samplePos; jack_transport_state_t transportState; jack_position_t pos; - char jackRegisteredName[8]; + char jackRegisteredName[16]; int dummyState; int dummyPos; // Free-running frame counter incremented always in process. @@ -52,17 +52,18 @@ class JackAudioDevice : public AudioDevice { return (float*)jack_port_get_buffer((jack_port_t*)port, nframes); } - virtual std::list<QString> outputPorts(); - virtual std::list<QString> inputPorts(); + virtual std::list<QString> outputPorts(bool midi = false, int aliases = -1); + virtual std::list<QString> inputPorts(bool midi = false, int aliases = -1); virtual void registerClient(); + virtual const char* clientName() { return jackRegisteredName; } //virtual void* registerOutPort(const char* name); //virtual void* registerInPort(const char* name); virtual void* registerOutPort(const char* /*name*/, bool /*midi*/); virtual void* registerInPort(const char* /*name*/, bool /*midi*/); - virtual char* getJackName(); + //virtual char* getJackName(); virtual void unregisterPort(void*); virtual void connect(void*, void*); diff --git a/muse/muse/driver/jackmidi.cpp b/muse/muse/driver/jackmidi.cpp index efc70602..d27d83d6 100644 --- a/muse/muse/driver/jackmidi.cpp +++ b/muse/muse/driver/jackmidi.cpp @@ -5,6 +5,7 @@ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) //========================================================= +#include <qt.h> #include <qstring.h> #include <stdio.h> @@ -25,6 +26,9 @@ #include "audiodev.h" #include "../mplugins/midiitransform.h" #include "../mplugins/mitplugin.h" +#include "xml.h" + +extern unsigned int volatile lastExtMidiSyncTick; // Turn on debug messages. //#define JACK_MIDI_DEBUG @@ -42,28 +46,115 @@ ///int* jackSeq; //static snd_seq_addr_t musePort; -int MidiJackDevice::_nextOutIdNum = 0; -int MidiJackDevice::_nextInIdNum = 0; +//int MidiJackDevice::_nextOutIdNum = 0; +//int MidiJackDevice::_nextInIdNum = 0; + +//int JackMidiPortList::_nextOutIdNum = 0; +//int JackMidiPortList::_nextInIdNum = 0; + +//JackMidiPortList jackMidiClientPorts; + +/* //--------------------------------------------------------- -// MidiAlsaDevice +// JackMidiPortList //--------------------------------------------------------- -MidiJackDevice::MidiJackDevice(const int& a, const QString& n) +JackMidiPortList::JackMidiPortList() +{ + +} + +JackMidiPortList::~JackMidiPortList() +{ + +} + +iJackMidiPort JackMidiPortList::createClientPort(int flags) // 1 = writable, 2 = readable - do not mix +{ + if(flags & 1) + { + char buf[80]; + snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); + jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); + if(_client_jackport == NULL) + { + fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-out\n"); + //return QString("Could not register jack-midi-out client port"); + return end(); + } + else + { + JackMidiPort jmp(_client_jackport, QString(buf), flags); + _nextOutIdNum++; + return insert(begin(), std::pair<jack_port_t*, JackMidiPort>(_client_jackport, jmp)); + } + } + else + if(flags & 2) + { + char buf[80]; + snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); + jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); + if(_client_jackport == NULL) + { + fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-in\n"); + return end(); + } + else + { + JackMidiPort jmp(_client_jackport, QString(buf), flags); + _nextInIdNum++; + return insert(begin(), std::pair<jack_port_t*, JackMidiPort>(_client_jackport, jmp)); + } + } + return end(); +} + +// Return true if removed. +bool JackMidiPortList::removeClientPort(jack_port_t* port) +{ + iJackMidiPort ijp = find(port); + if(ijp == end()) + return false; + + // Is output? + if(ijp->second._flags & 1) + _nextOutIdNum--; + // Is input? + if(ijp->second._flags & 2) + _nextInIdNum--; + + erase(ijp); + + audioDevice->unregisterPort(port); + + return true; +} +*/ + +//--------------------------------------------------------- +// MidiJackDevice +//--------------------------------------------------------- + +//MidiJackDevice::MidiJackDevice(const int& a, const QString& n) +MidiJackDevice::MidiJackDevice(jack_port_t* jack_port, const QString& n) : MidiDevice(n) { - _client_jackport = 0; - adr = a; + //_client_jackport = 0; + _client_jackport = jack_port; + //adr = a; init(); } MidiJackDevice::~MidiJackDevice() { - #ifdef JACK_MIDI_USE_MULTIPLE_CLIENT_PORTS + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::~MidiJackDevice()\n"); + #endif if(_client_jackport) - //audioDevice->unregisterPort(_client_jackport); - close(); - #endif + audioDevice->unregisterPort(_client_jackport); + //close(); } /* @@ -83,6 +174,181 @@ int MidiJackDevice::selectWfd() */ //--------------------------------------------------------- +// createJackMidiDevice +// If name parameter is blank, creates a new (locally) unique one. +//--------------------------------------------------------- + +//QString MidiJackDevice::createJackMidiDevice(int rwflags) // 1:Writable 2: Readable. Do not mix. +MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable. Do not mix. +{ +/// _openFlags &= _rwFlags; // restrict to available bits + +/// #ifdef JACK_MIDI_DEBUG +/// printf("MidiJackDevice::open %s\n", name.latin1()); +/// #endif + + //jack_port_t* jp = jack_port_by_name(_client, name().latin1()); +/// jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().latin1()); + +/// if(!jp) +/// { +/// printf("MidiJackDevice::open: Jack midi port %s not found!\n", name().latin1()); +/// _writeEnable = false; +/// _readEnable = false; +/// return QString("Jack midi port not found"); +/// } + +/// int pf = jack_port_flags(jp); + + //if(!name.isEmpty()) + //{ + // Does not work. + // if(audioDevice->findPort(name.latin1())) + // { + // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Given port name %s already exists!\n", name.latin1()); + // return 0; + // } + //} + + jack_port_t* client_jackport = NULL; + //char buf[80]; + + // If Jack port can receive data from us and we actually want to... + //if((pf & JackPortIsInput) && (_openFlags & 1)) + if(rwflags & 1) + { + if(name.isEmpty()) + { + //snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); + for(int i = 0; ; ++i) + { + //snprintf(buf, 80, "midi-out-%d", i); + name.sprintf("midi-out-%d", i); + + // Does not work. + //if(!audioDevice->findPort(buf)) + // break; + //client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); + client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + if(client_jackport) + break; + + if(i == 65535) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); + return 0; + } + } + //name = QString(buf); + } + else + { + client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + if(!client_jackport) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating output port name %s\n", name.latin1()); + return 0; + } + } + /* + else + { + client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + if(!client_jackport) + { + for(int i = 0; ; ++i) + { + snprintf(buf, 80, "midi-out-%d", i); + // Does not work! + //if(!audioDevice->findPort(buf)) + // break; + client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); + if(client_jackport) + break; + + if(i == 65535) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); + return 0; + } + } + name = QString(buf); + } + } + */ + + //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.latin1(), true); + //if(client_jackport == NULL) + //{ + // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client output port %s\n", name.latin1()); + // return 0; + //} + //else + // _nextOutIdNum++; + + } + else // Note docs say it can't be both input and output. + // If Jack port can send data to us and we actually want it... + //if((pf & JackPortIsOutput) && (_openFlags & 2)) + if(rwflags & 2) + { + if(name.isEmpty()) + { + //snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); + for(int i = 0; ; ++i) + { + //snprintf(buf, 80, "midi-in-%d", i); + name.sprintf("midi-in-%d", i); + + // Does not work. + //if(!audioDevice->findPort(buf)) + // break; + //client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); + client_jackport = (jack_port_t*)audioDevice->registerInPort(name.latin1(), true); + if(client_jackport) + break; + + if(i == 65535) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused input port name!\n"); + return 0; + } + } + //name = QString(buf); + } + else + { + client_jackport = (jack_port_t*)audioDevice->registerInPort(name.latin1(), true); + if(!client_jackport) + { + fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating input port name %s\n", name.latin1()); + return 0; + } + } + + //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.latin1(), true); + + //if(client_jackport == NULL) + //{ + // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client input port %s\n", name.latin1()); + //_readEnable = false; + //return QString("Could not register jack-midi-in client port"); + // return 0; + //} + //else + // _nextInIdNum++; + + } + if(client_jackport == NULL) + return 0; + + MidiJackDevice* dev = new MidiJackDevice(client_jackport, name); + dev->setrwFlags(rwflags); + midiDevices.add(dev); + return dev; +} + +//--------------------------------------------------------- // open //--------------------------------------------------------- @@ -91,9 +357,10 @@ QString MidiJackDevice::open() _openFlags &= _rwFlags; // restrict to available bits #ifdef JACK_MIDI_DEBUG - printf("MidiJackDevice::open %s\n", name.latin1()); + printf("MidiJackDevice::open %s\n", name().latin1()); #endif + /* //jack_port_t* jp = jack_port_by_name(_client, name().latin1()); jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().latin1()); @@ -147,6 +414,11 @@ QString MidiJackDevice::open() _readEnable = true; } } + */ + + _writeEnable = bool(_openFlags & 1); + _readEnable = bool(_openFlags & 2); + return QString("OK"); } @@ -157,9 +429,10 @@ QString MidiJackDevice::open() void MidiJackDevice::close() { #ifdef JACK_MIDI_DEBUG - printf("MidiJackDevice::close %s\n", name.latin1()); + printf("MidiJackDevice::close %s\n", name().latin1()); #endif + /* if(_client_jackport) { int pf = jack_port_flags(_client_jackport); @@ -175,7 +448,11 @@ void MidiJackDevice::close() _readEnable = false; return; } + */ + _writeEnable = false; + _readEnable = false; + /* //jack_port_t* jp = jack_port_by_name(_client, name().latin1()); jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().latin1()); @@ -210,6 +487,124 @@ void MidiJackDevice::close() } //--------------------------------------------------------- +// writeRouting +//--------------------------------------------------------- + +void MidiJackDevice::writeRouting(int level, Xml& xml) const +{ + QString s; + if(rwFlags() & 2) // Readable + { + //RouteList* rl = _inRoutes; + //for (ciRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) + { + if(!r->name().isEmpty()) + { + xml.tag(level++, "Route"); + + //xml.strTag(level, "srcNode", r->name()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + s = QT_TR_NOOP("source"); + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + xml.tag(level, s); + + //xml.strTag(level, "dstNode", name()); + //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().latin1()); + //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().latin1()); + xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().latin1()); + + xml.etag(level--, "Route"); + } + } + } + + for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) + { + if(!r->name().isEmpty()) + { + s = QT_TR_NOOP("Route"); + if(r->channel != -1) + s += QString(QT_TR_NOOP(" channel=\"%1\"")).arg(r->channel); + + //xml.tag(level++, "Route"); + xml.tag(level++, s); + + /* + //xml.strTag(level, "srcNode", name()); + if(r->channel != -1) + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, r->channel, name().latin1()); + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().latin1()); + xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, r->channel, name().latin1()); + else + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().latin1()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().latin1()); + */ + xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().latin1()); + + /* + //xml.strTag(level, "dstNode", r->name()); + if(r->channel != -1) + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().latin1()); + } + else + { + if(r->type == Route::MIDI_DEVICE_ROUTE) + xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().latin1()); + else + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + } + */ + + s = QT_TR_NOOP("dest"); + if(r->type == Route::MIDI_DEVICE_ROUTE) + s += QString(QT_TR_NOOP(" devtype=\"%1\"")).arg(r->device->deviceType()); + else + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + xml.tag(level, s); + + + xml.etag(level--, "Route"); + } + } + + /* + else + if(rwFlags() & 1) // Writable + { + //RouteList* rl = _outRoutes; + //for (ciRoute r = rl->begin(); r != rl->end(); ++r) + for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) + { + if(!r->name().isEmpty()) + { + xml.tag(level++, "Route"); + + //xml.strTag(level, "srcNode", name()); + //if(r->channel != -1) + // xml.tag(level, "srcNode type=\"%d\" channel=\"%d\" name=\"%s\"", Route::JACK_MIDI_ROUTE, r->channel, name().latin1()); + //else + xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().latin1()); + + //xml.strTag(level, "dstNode", r->name()); + xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + + xml.etag(level--, "Route"); + } + } + } + */ +} + +//--------------------------------------------------------- // putEvent //--------------------------------------------------------- @@ -310,12 +705,12 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event) event.dump(); } + int typ = event.type(); + if(_port != -1) { int idin = midiPorts[_port].syncInfo().idIn(); - int typ = event.type(); - //--------------------------------------------------- // filter some SYSEX events //--------------------------------------------------- @@ -370,13 +765,24 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event) // transfer noteOn events to gui for step recording and keyboard // remote control // - if (event.type() == ME_NOTEON) { + if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); song->putEvent(pv); } - if(_recordFifo.put(MidiPlayEvent(event))) - printf("MidiJackDevice::recordEvent: fifo overflow\n"); + //if(_recordFifo.put(MidiPlayEvent(event))) + // printf("MidiJackDevice::recordEvent: fifo overflow\n"); + + // p3.3.38 + // Do not bother recording if it is NOT actually being used by a port. + // Because from this point on, process handles things, by selected port. + if(_port == -1) + return; + + // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. + unsigned int ch = (typ == ME_SYSEX)? MIDI_CHANNELS : event.channel(); + if(_recordFifo[ch].put(MidiPlayEvent(event))) + printf("MidiJackDevice::recordEvent: fifo channel %d overflow\n", ch); } //--------------------------------------------------------- @@ -403,7 +809,9 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) // unsigned curFrame = st->lastFrameTime; //int frameOffset = audio->getFrameOffset(); unsigned pos = audio->pos().frame(); - event.setTime(pos + ev->time); + + //event.setTime(pos + ev->time); + event.setTime(extSyncFlag.value() ? lastExtMidiSyncTick : (pos + ev->time)); event.setChannel(*(ev->buffer) & 0xf); int type = *(ev->buffer) & 0xf0; @@ -428,23 +836,56 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) case ME_SYSEX: { - int type = *(ev->buffer) & 0xff; - switch(type) { - case ME_SYSEX: - event.setTime(0); // mark as used - event.setType(ME_SYSEX); - event.setData((unsigned char*)(ev->buffer + 1), - ev->size - 2); - break; - case ME_CLOCK: - case ME_SENSE: - break; - default: - printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type); - return; - } + int type = *(ev->buffer) & 0xff; + switch(type) + { + case ME_SYSEX: + + // TODO: Deal with large sysex, which are broken up into chunks! + // For now, do not accept if the last byte is not EOX, meaning it's a chunk with more chunks to follow. + if(*(((unsigned char*)ev->buffer) + ev->size - 1) != ME_SYSEX_END) + { + printf("MidiJackDevice::eventReceived sysex chunks not supported!\n"); + return; + } + + //event.setTime(0); // mark as used + event.setType(ME_SYSEX); + event.setData((unsigned char*)(ev->buffer + 1), ev->size - 2); + break; + case ME_MTC_QUARTER: + if(_port != -1) + midiSeq->mtcInputQuarter(_port, *(ev->buffer + 1)); + return; + case ME_SONGPOS: + if(_port != -1) + midiSeq->setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) >> 2 )); // LSB then MSB + return; + //case ME_SONGSEL: + //case ME_TUNE_REQ: + //case ME_SENSE: + case ME_CLOCK: + case ME_TICK: + case ME_START: + case ME_CONTINUE: + case ME_STOP: + if(_port != -1) + midiSeq->realtimeSystemInput(_port, type); + return; + //case ME_SYSEX_END: + //break; + // return; + default: + printf("MidiJackDevice::eventReceived unsupported system event 0x%02x\n", type); + return; + } } - return; + //return; + break; + default: + printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type); + //printf("MidiJackDevice::eventReceived unknown event 0x%02x size:%d buf:0x%02x 0x%02x 0x%02x ...0x%02x\n", type, ev->size, *(ev->buffer), *(ev->buffer + 1), *(ev->buffer + 2), *(ev->buffer + (ev->size - 1))); + return; } if (midiInputTrace) { diff --git a/muse/muse/driver/jackmidi.h b/muse/muse/driver/jackmidi.h index d260163c..a143b7ff 100644 --- a/muse/muse/driver/jackmidi.h +++ b/muse/muse/driver/jackmidi.h @@ -10,15 +10,20 @@ //#include <config.h> +#include <map> + #include <jack/jack.h> #include <jack/midiport.h> #include "mididev.h" +#include "route.h" class QString; class MidiFifo; class MidiRecordEvent; class MidiPlayEvent; +//class RouteList; +class Xml; // Turn on to show multiple devices, work in progress, // not working fully yet, can't seem to connect... @@ -44,23 +49,57 @@ typedef struct { } muse_jack_midi_buffer; */ +/* +struct JackMidiPort +{ + jack_port_t* _jackPort; + QString _name; + int _flags; // 1 = writable, 2 = readable - do not mix + JackMidiPort(jack_port_t* jp, const QString& s, int f) + { + _jackPort = jp; + _name = QString(s); + _flags = f; + } +}; + +typedef std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> >::iterator iJackMidiPort; +typedef std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> >::const_iterator ciJackMidiPort; + +class JackMidiPortList : public std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> > +{ + private: + static int _nextOutIdNum; + static int _nextInIdNum; + + public: + JackMidiPortList(); + ~JackMidiPortList(); + iJackMidiPort createClientPort(int flags); + bool removeClientPort(jack_port_t* port); +}; + +extern JackMidiPortList jackMidiClientPorts; +*/ + //--------------------------------------------------------- // MidiJackDevice //--------------------------------------------------------- class MidiJackDevice : public MidiDevice { public: - int adr; + //int adr; private: // fifo for midi events sent from gui // direct to midi port: MidiFifo eventFifo; - static int _nextOutIdNum; - static int _nextInIdNum; + //static int _nextOutIdNum; + //static int _nextInIdNum; jack_port_t* _client_jackport; + //RouteList _routes; virtual QString open(); virtual void close(); @@ -77,8 +116,14 @@ class MidiJackDevice : public MidiDevice { public: MidiJackDevice() {} - MidiJackDevice(const int&, const QString& name); - void processMidi(); + //MidiJackDevice(const int&, const QString& name); + MidiJackDevice(jack_port_t* jack_port, const QString& name); + + static MidiDevice* createJackMidiDevice(QString /*name*/, int /*rwflags*/); // 1:Writable 2: Readable. Do not mix. + + virtual inline int deviceType() { return JACK_MIDI; } + + virtual void processMidi(); virtual ~MidiJackDevice(); //virtual int selectRfd(); //virtual int selectWfd(); @@ -90,7 +135,12 @@ class MidiJackDevice : public MidiDevice { virtual void collectMidiEvents(); //virtual jack_port_t* jackPort() { return _jackport; } - virtual jack_port_t* clientJackPort() { return _client_jackport; } + //virtual jack_port_t* clientJackPort() { return _client_jackport; } + virtual void* clientPort() { return (void*)_client_jackport; } + + //RouteList* routes() { return &_routes; } + //bool noRoute() const { return _routes.empty(); } + virtual void writeRouting(int, Xml&) const; }; extern bool initMidiJack(); diff --git a/muse/muse/dssihost.cpp b/muse/muse/dssihost.cpp index b9825a83..eb07b1d1 100644 --- a/muse/muse/dssihost.cpp +++ b/muse/muse/dssihost.cpp @@ -360,6 +360,28 @@ static void scanDSSILib(const QFileInfo& fi) //DssiSynth* s = new DssiSynth(fi, label); DssiSynth* s = new DssiSynth(fi, label, QString(descr->LADSPA_Plugin->Name), QString(descr->LADSPA_Plugin->Maker), QString()); + if(debugMsg) + { + fprintf(stderr, "scanDSSILib: name:%s listname:%s lib:%s listlib:%s\n", label.ascii(), s->name().ascii(), fi.baseName(true).ascii(), s->baseName().ascii()); + int ai = 0, ao = 0, ci = 0, co = 0; + for(int pt = 0; pt < descr->LADSPA_Plugin->PortCount; ++pt) + { + LADSPA_PortDescriptor pd = descr->LADSPA_Plugin->PortDescriptors[pt]; + if(LADSPA_IS_PORT_INPUT(pd) && LADSPA_IS_PORT_AUDIO(pd)) + ai++; + else + if(LADSPA_IS_PORT_OUTPUT(pd) && LADSPA_IS_PORT_AUDIO(pd)) + ao++; + else + if(LADSPA_IS_PORT_INPUT(pd) && LADSPA_IS_PORT_CONTROL(pd)) + ci++; + else + if(LADSPA_IS_PORT_OUTPUT(pd) && LADSPA_IS_PORT_CONTROL(pd)) + co++; + } + fprintf(stderr, "audio ins:%d outs:%d control ins:%d outs:%d\n", ai, ao, ci, co); + } + synthis.push_back(s); } else @@ -545,7 +567,8 @@ bool DssiSynthIF::init(DssiSynth* s) for(int k = 0; k < inports; ++k) { //audioInBuffers[k] = new LADSPA_Data[segmentSize]; - posix_memalign((void**)(audioInBuffers + k), 16, sizeof(float) * segmentSize); + //posix_memalign((void**)(audioInBuffers + k), 16, sizeof(float) * segmentSize); + posix_memalign((void**)&audioInBuffers[k], 16, sizeof(float) * segmentSize); memset(audioInBuffers[k], 0, sizeof(float) * segmentSize); ld->connect_port(handle, synth->iIdx[k], audioInBuffers[k]); } @@ -558,9 +581,11 @@ bool DssiSynthIF::init(DssiSynth* s) for(int k = 0; k < outports; ++k) { //audioOutBuffers[k] = new LADSPA_Data[segmentSize]; - posix_memalign((void**)(audioOutBuffers + k), 16, sizeof(float) * segmentSize); + //posix_memalign((void**)(audioOutBuffers + k), 16, sizeof(float) * segmentSize); + posix_memalign((void**)&audioOutBuffers[k], 16, sizeof(float) * segmentSize); memset(audioOutBuffers[k], 0, sizeof(float) * segmentSize); ld->connect_port(handle, synth->oIdx[k], audioOutBuffers[k]); + //printf("DssiSynthIF::init output port name: %s\n", ld->PortNames[synth->oIdx[k]]); // out1, out2, out3 etc } } @@ -1995,6 +2020,16 @@ int DssiSynthIF::channels() const return synth->_outports > MAX_CHANNELS ? MAX_CHANNELS : synth->_outports; } +int DssiSynthIF::totalOutChannels() const +{ + return synth->_outports; +} + +int DssiSynthIF::totalInChannels() const +{ + return synth->_inports; +} + #else //DSSI_SUPPORT void initDSSI() {} #endif diff --git a/muse/muse/dssihost.h b/muse/muse/dssihost.h index f2609dca..eb3637f0 100644 --- a/muse/muse/dssihost.h +++ b/muse/muse/dssihost.h @@ -139,6 +139,8 @@ class DssiSynthIF : public SynthIF //virtual int channels() const { return synth->_outports; } virtual int channels() const; + virtual int totalOutChannels() const; + virtual int totalInChannels() const; virtual void deactivate3() {} diff --git a/muse/muse/globaldefs.h b/muse/muse/globaldefs.h index d842a3f0..06661771 100644 --- a/muse/muse/globaldefs.h +++ b/muse/muse/globaldefs.h @@ -23,7 +23,8 @@ enum AutomationType { const int MAX_CHANNELS = 2; // max audio channels const int MAX_PLUGINS = 4; // plugins in mixer rack -const int MIDI_PORTS = 32; // max Number of Midi Ports +//const int MIDI_PORTS = 32; // max Number of Midi Ports +const int MIDI_PORTS = 128; // max Number of Midi Ports #ifndef MIDI_CHANNELS #define MIDI_CHANNELS 16 // Channels per Port diff --git a/muse/muse/midi.cpp b/muse/muse/midi.cpp index 04f802be..5f919a45 100644 --- a/muse/muse/midi.cpp +++ b/muse/muse/midi.cpp @@ -955,10 +955,11 @@ void Audio::processMidi() } } - // Is it a Jack midi device? - MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); - if(mjd) - mjd->collectMidiEvents(); + // Is it a Jack midi device? + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); + //if(mjd) + // mjd->collectMidiEvents(); + md->collectMidiEvents(); // Take snapshots of the current sizes of the recording fifos, // because they may change while here in process, asynchronously. @@ -1004,220 +1005,314 @@ void Audio::processMidi() if (track->recordFlag()) { //int portMask = track->inPortMask(); - unsigned int portMask = track->inPortMask(); - int channelMask = track->inChannelMask(); + // p3.3.38 Removed + //unsigned int portMask = track->inPortMask(); + //int channelMask = track->inChannelMask(); + MPEventList* rl = track->mpevents(); MidiPort* tport = &midiPorts[port]; - for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) + // p3.3.38 + //for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) + //{ + RouteList* irl = track->inRoutes(); + for(ciRoute r = irl->begin(); r != irl->end(); ++r) { - MidiDevice* dev = *id; - + //if(!r->isValid() || (r->type != Route::ALSA_MIDI_ROUTE && r->type != Route::JACK_MIDI_ROUTE)) + if(!r->isValid() || (r->type != Route::MIDI_DEVICE_ROUTE)) + continue; + + //MidiDevice* dev = *id; + MidiDevice* dev = r->device; + int channel = r->channel; + + // NOTE: TODO: Special for input device sysex 'channel' marked as -1, ** IF we end up going with that method **. + // This would mean having a separate 'System' channel listed in the routing popups. + // The other alternative is to accept sysex from a device as long as ANY regular channel is routed from it, + // this does not require a 'System' channel listed in the routing popups. + // But that requires more code below... Not added yet... + if(channel == -1) + //channel = MIDI_CHANNELS; // Special channel '17' + continue; + int devport = dev->midiPort(); // record only from ports marked in portMask: - if (devport == -1 || !(portMask & (1 << devport))) + //if (devport == -1 || !(portMask & (1 << devport))) + if (devport == -1) continue; //MREventList* el = dev->recordEvents(); - MidiFifo& rf = dev->recordEvents(); + //MidiFifo& rf = dev->recordEvents(); + + + if(!dev->sysexFIFOProcessed()) + { + // Set to the sysex fifo at first. + MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); + // Get the frozen snapshot of the size. + int count = dev->tmpRecordCount(MIDI_CHANNELS); + + for(int i = 0; i < count; ++i) + { + MidiPlayEvent event(rf.peek(i)); + + //unsigned time = event.time() + segmentSize*(segmentCount-1); + //unsigned time = event.time() + (extsync ? config.division/24 : segmentSize*(segmentCount-1)); + //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); + //event.setTime(time); + //if(!extsync) + // event.setTime(event.time() + segmentSize*(segmentCount-1)); + + event.setPort(port); + + // dont't echo controller changes back to software + // synthesizer: + if(!dev->isSynti() && md && track->recEcho()) + playEvents->add(event); + + // If syncing externally the event time is already in units of ticks, set above. + if(!extsync) + { + //time = tempomap.frame2tick(event.time()); + //event.setTime(time); // set tick time + event.setTime(tempomap.frame2tick(event.time())); // set tick time + } + + if(recording) + rl->add(event); + } + + dev->setSysexFIFOProcessed(true); + } + + // Set to the sysex fifo at first. + ///MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); // Get the frozen snapshot of the size. - int count = dev->tmpRecordCount(); + ///int count = dev->tmpRecordCount(MIDI_CHANNELS); - //for (iMREvent ie = el->begin(); ie != el->end(); ++ie) - for(int i = 0; i < count; ++i) + // Iterate once for sysex fifo (if needed), once for channel fifos. + ///for(int sei = 0; sei < 2; ++sei) { - MidiPlayEvent event(rf.peek(i)); - - //int channel = ie->channel(); - int channel = event.channel(); - int defaultPort = devport; - if (!(channelMask & (1 << channel))) - { - continue; - } - - //MidiPlayEvent event(*ie); - int drumRecPitch=0; //prevent compiler warning: variable used without initialization - MidiController *mc = 0; - int ctl = 0; - - //Hmmm, hehhh... - // TODO: Clean up a bit around here when it comes to separate events for rec & for playback. - // But not before 0.7 (ml) - - int prePitch = 0, preVelo = 0; - - event.setChannel(track->outChannel()); - - if (event.isNote() || event.isNoteOff()) - { - // - // apply track values - // - - //Apply drum inkey: - if (track->type() == Track::DRUM) - { - int pitch = event.dataA(); - //Map note that is played according to drumInmap - drumRecPitch = drumMap[(unsigned int)drumInmap[pitch]].enote; - devport = drumMap[(unsigned int)drumInmap[pitch]].port; - event.setPort(devport); - channel = drumMap[(unsigned int)drumInmap[pitch]].channel; - event.setA(drumMap[(unsigned int)drumInmap[pitch]].anote); - event.setChannel(channel); - } - else - { //Track transpose if non-drum - prePitch = event.dataA(); - int pitch = prePitch + track->transposition; - if (pitch > 127) - pitch = 127; - if (pitch < 0) - pitch = 0; - event.setA(pitch); - } - - if (!event.isNoteOff()) - { - preVelo = event.dataB(); - int velo = preVelo + track->velocity; - velo = (velo * track->compression) / 100; - if (velo > 127) - velo = 127; - if (velo < 1) - velo = 1; - event.setB(velo); - } - } - // Added by T356. - else - if(event.type() == ME_CONTROLLER) - { - if(track->type() == Track::DRUM) + // If on first pass, do sysex fifo. + /* + if(sei == 0) + { + // Ignore any further channel routes on this device if already done here. + if(dev->sysexFIFOProcessed()) + continue; + // Go ahead and set this now. + dev->setSysexFIFOProcessed(true); + // Allow it to fall through with the sysex fifo and count... + } + else + { + // We're on the second pass, do channel fifos. + rf = dev->recordEvents(channel); + // Get the frozen snapshot of the size. + count = dev->tmpRecordCount(channel); + } + */ + + MidiFifo& rf = dev->recordEvents(channel); + int count = dev->tmpRecordCount(channel); + + //for (iMREvent ie = el->begin(); ie != el->end(); ++ie) + for(int i = 0; i < count; ++i) + { + MidiPlayEvent event(rf.peek(i)); + + //int channel = ie->channel(); + ///int channel = event.channel(); + + int defaultPort = devport; + ///if (!(channelMask & (1 << channel))) + ///{ + /// continue; + ///} + + //MidiPlayEvent event(*ie); + int drumRecPitch=0; //prevent compiler warning: variable used without initialization + MidiController *mc = 0; + int ctl = 0; + + //Hmmm, hehhh... + // TODO: Clean up a bit around here when it comes to separate events for rec & for playback. + // But not before 0.7 (ml) + + int prePitch = 0, preVelo = 0; + + event.setChannel(track->outChannel()); + + if (event.isNote() || event.isNoteOff()) { - ctl = event.dataA(); - // Regardless of what port the event came from, is it a drum controller event - // according to the track port's instrument? - mc = tport->drumController(ctl); - if(mc) - { - int pitch = ctl & 0x7f; - ctl &= ~0xff; - int dmindex = drumInmap[pitch] & 0x7f; - //Map note that is played according to drumInmap - drumRecPitch = drumMap[dmindex].enote; - devport = drumMap[dmindex].port; - event.setPort(devport); - channel = drumMap[dmindex].channel; - event.setA(ctl | drumMap[dmindex].anote); - event.setChannel(channel); - } - } - } - - // p3.3.25 - // MusE uses a fixed clocks per quarternote of 24. - // At standard 384 ticks per quarternote for example, - // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). - // That is what we'll use if syncing externally. - //unsigned time = event.time() + segmentSize*(segmentCount-1); - //unsigned time = event.time() + (extsync ? config.division/24 : segmentSize*(segmentCount-1)); - // p3.3.34 - // Oops, use the current tick. - //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); - //event.setTime(time); - // p3.3.35 - // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). - // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. - // p3.3.36 - //if(!extsync) - // event.setTime(event.time() + segmentSize*(segmentCount-1)); - - // dont't echo controller changes back to software - // synthesizer: - - if (!dev->isSynti()) - { - //Check if we're outputting to another port than default: - if (devport == defaultPort) { - event.setPort(port); - if(md && track->recEcho()) - playEvents->add(event); + // + // apply track values + // + + //Apply drum inkey: + if (track->type() == Track::DRUM) + { + int pitch = event.dataA(); + //Map note that is played according to drumInmap + drumRecPitch = drumMap[(unsigned int)drumInmap[pitch]].enote; + devport = drumMap[(unsigned int)drumInmap[pitch]].port; + event.setPort(devport); + channel = drumMap[(unsigned int)drumInmap[pitch]].channel; + event.setA(drumMap[(unsigned int)drumInmap[pitch]].anote); + event.setChannel(channel); } - else { - // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? - MidiDevice* mdAlt = midiPorts[devport].device(); - if(mdAlt && track->recEcho()) - mdAlt->playEvents()->add(event); + else + { //Track transpose if non-drum + prePitch = event.dataA(); + int pitch = prePitch + track->transposition; + if (pitch > 127) + pitch = 127; + if (pitch < 0) + pitch = 0; + event.setA(pitch); } - // Shall we activate meters even while rec echo is off? Sure, why not... - if(event.isNote() && event.dataB() > track->activity()) - track->setActivity(event.dataB()); - } - - // p3.3.25 - // If syncing externally the event time is already in units of ticks, set above. - if(!extsync) - { - // p3.3.35 - //time = tempomap.frame2tick(event.time()); - //event.setTime(time); // set tick time - event.setTime(tempomap.frame2tick(event.time())); // set tick time - } - - // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) - if (recording) - { - // In these next steps, it is essential to set the recorded event's port - // to the track port so buildMidiEventList will accept it. Even though - // the port may have no device "<none>". - // - if (track->type() == Track::DRUM) + + if (!event.isNoteOff()) + { + preVelo = event.dataB(); + int velo = preVelo + track->velocity; + velo = (velo * track->compression) / 100; + if (velo > 127) + velo = 127; + if (velo < 1) + velo = 1; + event.setB(velo); + } + } + // Added by T356. + else + if(event.type() == ME_CONTROLLER) + { + if(track->type() == Track::DRUM) + { + ctl = event.dataA(); + // Regardless of what port the event came from, is it a drum controller event + // according to the track port's instrument? + mc = tport->drumController(ctl); + if(mc) { - // Is it a drum controller event? - if(mc) - { - MidiPlayEvent drumRecEvent = event; - drumRecEvent.setA(ctl | drumRecPitch); - // In this case, preVelo is simply the controller value. - drumRecEvent.setB(preVelo); - drumRecEvent.setPort(port); //rec-event to current port - drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel - rl->add(drumRecEvent); + int pitch = ctl & 0x7f; + ctl &= ~0xff; + int dmindex = drumInmap[pitch] & 0x7f; + //Map note that is played according to drumInmap + drumRecPitch = drumMap[dmindex].enote; + devport = drumMap[dmindex].port; + event.setPort(devport); + channel = drumMap[dmindex].channel; + event.setA(ctl | drumMap[dmindex].anote); + event.setChannel(channel); + } + } + } + + // p3.3.25 + // MusE uses a fixed clocks per quarternote of 24. + // At standard 384 ticks per quarternote for example, + // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). + // That is what we'll use if syncing externally. + //unsigned time = event.time() + segmentSize*(segmentCount-1); + //unsigned time = event.time() + (extsync ? config.division/24 : segmentSize*(segmentCount-1)); + // p3.3.34 + // Oops, use the current tick. + //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); + //event.setTime(time); + // p3.3.35 + // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). + // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. + // p3.3.36 + //if(!extsync) + // event.setTime(event.time() + segmentSize*(segmentCount-1)); + + // dont't echo controller changes back to software + // synthesizer: + + if (!dev->isSynti()) + { + //Check if we're outputting to another port than default: + if (devport == defaultPort) { + event.setPort(port); + if(md && track->recEcho()) + playEvents->add(event); + } + else { + // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? + MidiDevice* mdAlt = midiPorts[devport].device(); + if(mdAlt && track->recEcho()) + mdAlt->playEvents()->add(event); + } + // Shall we activate meters even while rec echo is off? Sure, why not... + if(event.isNote() && event.dataB() > track->activity()) + track->setActivity(event.dataB()); + } + + // p3.3.25 + // If syncing externally the event time is already in units of ticks, set above. + if(!extsync) + { + // p3.3.35 + //time = tempomap.frame2tick(event.time()); + //event.setTime(time); // set tick time + event.setTime(tempomap.frame2tick(event.time())); // set tick time + } + + // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) + if (recording) + { + // In these next steps, it is essential to set the recorded event's port + // to the track port so buildMidiEventList will accept it. Even though + // the port may have no device "<none>". + // + if (track->type() == Track::DRUM) + { + // Is it a drum controller event? + if(mc) + { + MidiPlayEvent drumRecEvent = event; + drumRecEvent.setA(ctl | drumRecPitch); + // In this case, preVelo is simply the controller value. + drumRecEvent.setB(preVelo); + drumRecEvent.setPort(port); //rec-event to current port + drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel + rl->add(drumRecEvent); + } + else + { + + MidiPlayEvent drumRecEvent = event; + drumRecEvent.setA(drumRecPitch); + drumRecEvent.setB(preVelo); + // Changed by T356. + // Tested: Events were not being recorded for a drum map entry pointing to a + // different port. This must have been wrong - buildMidiEventList would ignore this. + //drumRecEvent.setPort(devport); + drumRecEvent.setPort(port); //rec-event to current port + + drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel + rl->add(drumRecEvent); + } } - else + else { - - MidiPlayEvent drumRecEvent = event; - drumRecEvent.setA(drumRecPitch); - drumRecEvent.setB(preVelo); - // Changed by T356. - // Tested: Events were not being recorded for a drum map entry pointing to a - // different port. This must have been wrong - buildMidiEventList would ignore this. - //drumRecEvent.setPort(devport); - drumRecEvent.setPort(port); //rec-event to current port - - drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel - rl->add(drumRecEvent); - } - } - else - { - // Restore record-pitch to non-transposed value since we don't want the note transposed twice next - MidiPlayEvent recEvent = event; - if (prePitch) - recEvent.setA(prePitch); - if (preVelo) - recEvent.setB(preVelo); - recEvent.setPort(port); - recEvent.setChannel(track->outChannel()); - - rl->add(recEvent); - } - } - } + // Restore record-pitch to non-transposed value since we don't want the note transposed twice next + MidiPlayEvent recEvent = event; + if (prePitch) + recEvent.setA(prePitch); + if (preVelo) + recEvent.setB(preVelo); + recEvent.setPort(port); + recEvent.setChannel(track->outChannel()); + + rl->add(recEvent); + } + } + } + } } } // Added by Tim. p3.3.8 @@ -1382,13 +1477,13 @@ void Audio::processMidi() // for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { - MidiDevice* md = *id; + //MidiDevice* md = *id; // Is it a Jack midi device? - MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); - if(!mjd) - continue; - - mjd->processMidi(); + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); + //if(!mjd) + // continue; + //mjd->processMidi(); + (*id)->processMidi(); /* int port = md->midiPort(); diff --git a/muse/muse/mididev.cpp b/muse/muse/mididev.cpp index 28acd541..7f52fc2e 100644 --- a/muse/muse/mididev.cpp +++ b/muse/muse/mididev.cpp @@ -83,7 +83,14 @@ void MidiDevice::init() MidiDevice::MidiDevice() { ///_recBufFlipped = false; - _tmpRecordCount = 0; + //_tmpRecordCount = 0; + for(unsigned int i = 0; i < MIDI_CHANNELS + 1; ++i) + _tmpRecordCount[i] = 0; + + _sysexFIFOProcessed = false; + //_sysexWritingChunks = false; + _sysexReadingChunks = false; + init(); } @@ -91,7 +98,14 @@ MidiDevice::MidiDevice(const QString& n) : _name(n) { ///_recBufFlipped = false; - _tmpRecordCount = 0; + //_tmpRecordCount = 0; + for(unsigned int i = 0; i < MIDI_CHANNELS + 1; ++i) + _tmpRecordCount[i] = 0; + + _sysexFIFOProcessed = false; + //_sysexWritingChunks = false; + _sysexReadingChunks = false; + init(); } @@ -151,10 +165,16 @@ bool filterEvent(const MEvent& event, int type, bool thru) //--------------------------------------------------------- void MidiDevice::afterProcess() - { - while (_tmpRecordCount--) - _recordFifo.remove(); - } +{ + //while (_tmpRecordCount--) + // _recordFifo.remove(); + + for(unsigned int i = 0; i < MIDI_CHANNELS + 1; ++i) + { + while (_tmpRecordCount[i]--) + _recordFifo[i].remove(); + } +} //--------------------------------------------------------- // beforeProcess @@ -162,11 +182,17 @@ void MidiDevice::afterProcess() //--------------------------------------------------------- void MidiDevice::beforeProcess() - { - //if (!jackPort(0).isZero()) - // audioDriver->collectMidiEvents(this, jackPort(0)); - _tmpRecordCount = _recordFifo.getSize(); - } +{ + //if (!jackPort(0).isZero()) + // audioDriver->collectMidiEvents(this, jackPort(0)); + + //_tmpRecordCount = _recordFifo.getSize(); + for(unsigned int i = 0; i < MIDI_CHANNELS + 1; ++i) + _tmpRecordCount[i] = _recordFifo[i].getSize(); + + // Reset this. + _sysexFIFOProcessed = false; +} /* //--------------------------------------------------------- @@ -230,13 +256,14 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) event.dump(); } + int typ = event.type(); + if(_port != -1) { int idin = midiPorts[_port].syncInfo().idIn(); // p3.3.26 1/23/10 Section was disabled, enabled by Tim. //#if 0 - int typ = event.type(); //--------------------------------------------------- // filter some SYSEX events @@ -296,7 +323,7 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) // transfer noteOn events to gui for step recording and keyboard // remote control // - if (event.type() == ME_NOTEON) { + if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); song->putEvent(pv); } @@ -305,18 +332,30 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) /// _recordEvents2.add(event); // add event to secondary list of recorded events ///else /// _recordEvents.add(event); // add event to primary list of recorded events - if(_recordFifo.put(MidiPlayEvent(event))) - printf("MidiDevice::recordEvent: fifo overflow\n"); + + //if(_recordFifo.put(MidiPlayEvent(event))) + // printf("MidiDevice::recordEvent: fifo overflow\n"); + + // p3.3.38 + // Do not bother recording if it is NOT actually being used by a port. + // Because from this point on, process handles things, by selected port. + if(_port == -1) + return; + + // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. + unsigned int ch = (typ == ME_SYSEX)? MIDI_CHANNELS : event.channel(); + if(_recordFifo[ch].put(MidiPlayEvent(event))) + printf("MidiDevice::recordEvent: fifo channel %d overflow\n", ch); } //--------------------------------------------------------- // find //--------------------------------------------------------- -MidiDevice* MidiDeviceList::find(const QString& s) +MidiDevice* MidiDeviceList::find(const QString& s, int typeHint) { for (iMidiDevice i = begin(); i != end(); ++i) - if ((*i)->name() == s) + if( (typeHint == -1 || typeHint == (*i)->deviceType()) && ((*i)->name() == s) ) return *i; return 0; } diff --git a/muse/muse/mididev.h b/muse/muse/mididev.h index a0744923..36b33c27 100644 --- a/muse/muse/mididev.h +++ b/muse/muse/mididev.h @@ -14,6 +14,11 @@ #include "mpevent.h" //#include "sync.h" +#include "route.h" +#include "globaldefs.h" + +//class RouteList; +class Xml; //--------------------------------------------------------- // MidiDevice @@ -26,8 +31,10 @@ class MidiDevice { ///MREventList _recordEvents; ///MREventList _recordEvents2; - // Used for multiple reads of fifo during process. - int _tmpRecordCount; + // Used for multiple reads of fifos during process. + //int _tmpRecordCount; + int _tmpRecordCount[MIDI_CHANNELS + 1]; + bool _sysexFIFOProcessed; ///bool _recBufFlipped; // Holds sync settings and detection monitors. @@ -40,22 +47,43 @@ class MidiDevice { int _openFlags; // configured open flags bool _readEnable; // set when opened/closed. bool _writeEnable; // + //int _sysexWriteChunk; + //int _sysexReadChunk; + //bool _sysexWritingChunks; + bool _sysexReadingChunks; + // Recording fifo. - MidiFifo _recordFifo; + //MidiFifo _recordFifo; + // Recording fifos. To speed up processing, one per channel plus one special system 'channel' for channel-less events like sysex. + MidiFifo _recordFifo[MIDI_CHANNELS + 1]; + + RouteList _inRoutes, _outRoutes; + void init(); virtual bool putMidiEvent(const MidiPlayEvent&) = 0; public: + enum { ALSA_MIDI=0, JACK_MIDI=1, SYNTH_MIDI=2 }; + MidiDevice(); MidiDevice(const QString& name); virtual ~MidiDevice() {} + virtual int deviceType() = 0; + + virtual void* clientPort() { return 0; } virtual QString open() = 0; virtual void close() = 0; + virtual void writeRouting(int, Xml&) const { }; + RouteList* inRoutes() { return &_inRoutes; } + RouteList* outRoutes() { return &_outRoutes; } + bool noInRoute() const { return _inRoutes.empty(); } + bool noOutRoute() const { return _outRoutes.empty(); } + const QString& name() const { return _name; } void setName(const QString& s) { _name = s; } - + int midiPort() const { return _port; } void setPort(int p) { _port = p; } @@ -76,6 +104,10 @@ class MidiDevice { virtual void recordEvent(MidiRecordEvent&); virtual bool putEvent(const MidiPlayEvent&); + + // For Jack-based devices - called in Jack audio process callback + virtual void collectMidiEvents() {} + virtual void processMidi() {} MPEventList* stuckNotes() { return &_stuckNotes; } MPEventList* playEvents() { return &_playEvents; } @@ -85,8 +117,16 @@ class MidiDevice { ///bool recBufFlipped() { return _recBufFlipped; } void beforeProcess(); void afterProcess(); - int tmpRecordCount() { return _tmpRecordCount; } - MidiFifo& recordEvents() { return _recordFifo; } + //int tmpRecordCount() { return _tmpRecordCount; } + int tmpRecordCount(const unsigned int ch) { return _tmpRecordCount[ch]; } + //MidiFifo& recordEvents() { return _recordFifo; } + MidiFifo& recordEvents(const unsigned int ch) { return _recordFifo[ch]; } + bool sysexFIFOProcessed() { return _sysexFIFOProcessed; } + void setSysexFIFOProcessed(bool v) { _sysexFIFOProcessed = v; } + //bool sysexWritingChunks() { return _sysexWritingChunks; } + //void setSysexWritingChunks(bool v) { _sysexWritingChunks = v; } + bool sysexReadingChunks() { return _sysexReadingChunks; } + void setSysexReadingChunks(bool v) { _sysexReadingChunks = v; } //virtual void getEvents(unsigned /*from*/, unsigned /*to*/, int /*channel*/, MPEventList* /*dst*/); iMPEvent nextPlayEvent() { return _nextPlayEvent; } @@ -100,13 +140,14 @@ class MidiDevice { typedef std::list<MidiDevice*>::iterator iMidiDevice; -class MidiDeviceList : public std::list<MidiDevice*> { +class MidiDeviceList : public std::list<MidiDevice*> +{ public: void add(MidiDevice* dev); void remove(MidiDevice* dev); - MidiDevice* find(const QString& name); + MidiDevice* find(const QString& name, int typeHint = -1); iMidiDevice find(const MidiDevice* dev); - }; +}; extern MidiDeviceList midiDevices; extern void initMidiDevices(); diff --git a/muse/muse/midiseq.cpp b/muse/muse/midiseq.cpp index bd079c5a..e5692a6a 100644 --- a/muse/muse/midiseq.cpp +++ b/muse/muse/midiseq.cpp @@ -685,10 +685,11 @@ void MidiSeq::processTimerTick() for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; // Is it a Jack midi device? p3.3.36 - MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); - if(mjd) + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); + //if(mjd) + if(md->deviceType() == MidiDevice::JACK_MIDI) continue; - if (md->isSynti()) // syntis are handled by audio thread + if(md->isSynti()) // syntis are handled by audio thread continue; int port = md->midiPort(); MidiPort* mp = port != -1 ? &midiPorts[port] : 0; diff --git a/muse/muse/mixer/astrip.cpp b/muse/muse/mixer/astrip.cpp index 6a31bc82..da06ad63 100644 --- a/muse/muse/mixer/astrip.cpp +++ b/muse/muse/mixer/astrip.cpp @@ -21,6 +21,7 @@ #include <qcursor.h> #include <qmenudata.h> #include <qpainter.h> +#include <qstring.h> #include "globals.h" #include "audio.h" @@ -122,9 +123,6 @@ void AudioStrip::heartBeat() void AudioStrip::configChanged() { - // Added by Tim. p3.3.6 - //printf("AudioStrip::configChanged\n"); - songChanged(SC_CONFIG); } @@ -962,137 +960,1399 @@ AudioStrip::AudioStrip(QWidget* parent, AudioTrack* at) } //--------------------------------------------------------- +// addMenuItem +//--------------------------------------------------------- + +static int addMenuItem(QButton* /*parent*/, AudioTrack* track, Track* route_track, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) +{ + // totalInChannels is only used by syntis. + //int channels = (!isOutput || route_track->type() != Track::AUDIO_SOFTSYNTH) ? ((AudioTrack*)route_track)->totalOutChannels() : ((AudioTrack*)route_track)->totalInChannels(); + //int channels = (!isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? ((AudioTrack*)track)->totalOutChannels() : ((AudioTrack*)track)->totalInChannels(); + int toch = ((AudioTrack*)track)->totalOutChannels(); + // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. + if(track->channels() == 1) + toch = 1; + + // totalInChannels is only used by syntis. + int chans = (isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? toch : ((AudioTrack*)track)->totalInChannels(); + + // Don't add the last stray mono route if the track is stereo. + //if(route_track->channels() > 1 && (channel+1 == chans)) + // return id; + + RouteList* rl = isOutput ? track->outRoutes() : track->inRoutes(); + + QString s(route_track->name()); + //int trackchans = track->channels(); + //QString ns; + + //if(track->channels() > 1 && (channel+1 < channels)) + //if(track->channels() > 1) + //if(route_track->type() == Track::AUDIO_SOFTSYNTH && channels > 2 && track->channels() > 1) + ///if(track->type() == Track::AUDIO_SOFTSYNTH && chans > 2 && route_track->channels() > 1) + /// s += QString(" < [%1,%2]").arg(channel+1).arg(channel+2); + + //int it = lb->insertItem(s); + lb->insertItem(s, id); + + int ach = channel; + int bch = -1; + + Route r(route_track, isOutput ? ach : bch, channels); + //Route r(route_track, channel); + + r.remoteChannel = isOutput ? bch : ach; + + mm.insert( pRouteMenuMap(id, r) ); + + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + //if (ir->type == 0 && ir->track == track) { + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channels == channels) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && + // (channel != -1 && ir->channel == channel) && (channels != -1 && ir->channels == channels)) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == channel) + //printf("addMenuItem: ir->type:%d ir->track:%s track:%s ir->channel:%d channel:%d ir->channels:%d channels:%d\n", + // ir->type, ir->track->name().latin1(), track->name().latin1(), ir->channel, channel, ir->channels, channels); + //if(ir->type == Route::TRACK_ROUTE && ir->track == route_track && ir->channel == channel && ir->remoteChannel == r.remoteChannel) + //if(*ir == r) + //if(ir->type == Route::TRACK_ROUTE && ir->track == route_track && ir->channel == channel && ir->channels == channels && ir->remoteChannel == r.remoteChannel) + + if(ir->type == Route::TRACK_ROUTE && ir->track == route_track && ir->remoteChannel == r.remoteChannel) + { + int tcompch = r.channel; + if(tcompch == -1) + tcompch = 0; + int tcompchs = r.channels; + if(tcompchs == -1) + tcompchs = isOutput ? track->channels() : route_track->channels(); + + int compch = ir->channel; + if(compch == -1) + compch = 0; + int compchs = ir->channels; + if(compchs == -1) + compchs = isOutput ? track->channels() : ir->track->channels(); + + //if(ir->type == Route::TRACK_ROUTE && ir->track == route_track && ir->channel == r.channel && ir->channels == r.channels && ir->remoteChannel == r.remoteChannel) + if(compch == tcompch && compchs == tcompchs) + { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); + break; + } + } + } + return ++id; +} + +//--------------------------------------------------------- // addAuxPorts //--------------------------------------------------------- -static void addAuxPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +//static void addAuxPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +static int addAuxPorts(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) { AuxList* al = song->auxs(); for (iAudioAux i = al->begin(); i != al->end(); ++i) { Track* track = *i; if (t == track) continue; + id = addMenuItem(parent, t, track, lb, id, mm, channel, channels, isOutput); + + /* QString s(track->name()); - int it = lb->insertItem(s); + //int it = lb->insertItem(s); + lb->insertItem(s, id); for (iRoute ir = r->begin(); ir != r->end(); ++ir) { - if (ir->type == 0 && ir->track == track) { - lb->setItemChecked(it, true); + //if (ir->type == 0 && ir->track == track) { + if (ir->type == 0 && ir->track == track && ir->channels == channels) { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); break; } } + ++id; + */ + } + return id; } //--------------------------------------------------------- // addInPorts //--------------------------------------------------------- -static void addInPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +//static void addInPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +static int addInPorts(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) { InputList* al = song->inputs(); for (iAudioInput i = al->begin(); i != al->end(); ++i) { Track* track = *i; if (t == track) continue; + id = addMenuItem(parent, t, track, lb, id, mm, channel, channels, isOutput); + + /* QString s(track->name()); - int it = lb->insertItem(s); + //int it = lb->insertItem(s); + lb->insertItem(s, id); for (iRoute ir = r->begin(); ir != r->end(); ++ir) { - if (ir->type == 0 && ir->track == track) { - lb->setItemChecked(it, true); + //if (ir->type == 0 && ir->track == track) { + if (ir->type == 0 && ir->track == track && ir->channels == channels) { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); break; } } + ++id; + */ + } + return id; } //--------------------------------------------------------- // addOutPorts //--------------------------------------------------------- -static void addOutPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +//static void addOutPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +static int addOutPorts(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) { OutputList* al = song->outputs(); for (iAudioOutput i = al->begin(); i != al->end(); ++i) { Track* track = *i; if (t == track) continue; + id = addMenuItem(parent, t, track, lb, id, mm, channel, channels, isOutput); + + /* QString s(track->name()); - int it = lb->insertItem(s); + //int it = lb->insertItem(s); + lb->insertItem(s, id); for (iRoute ir = r->begin(); ir != r->end(); ++ir) { - if (ir->type == 0 && ir->track == track) { - lb->setItemChecked(it, true); + //if (ir->type == 0 && ir->track == track) { + if (ir->type == 0 && ir->track == track && ir->channels == channels) { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); break; } } + ++id; + */ + } + return id; } //--------------------------------------------------------- // addGroupPorts //--------------------------------------------------------- -static void addGroupPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +//static void addGroupPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +static int addGroupPorts(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) { GroupList* al = song->groups(); for (iAudioGroup i = al->begin(); i != al->end(); ++i) { Track* track = *i; if (t == track) continue; + id = addMenuItem(parent, t, track, lb, id, mm, channel, channels, isOutput); + + /* QString s(track->name()); - int it = lb->insertItem(s); + //int it = lb->insertItem(s); + lb->insertItem(s, id); for (iRoute ir = r->begin(); ir != r->end(); ++ir) { - if (ir->type == 0 && ir->track == track) { - lb->setItemChecked(it, true); + //if (ir->type == 0 && ir->track == track) { + if (ir->type == 0 && ir->track == track && ir->channels == channels) { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); break; } } + ++id; + */ + } + return id; } //--------------------------------------------------------- // addWavePorts //--------------------------------------------------------- -static void addWavePorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +//static void addWavePorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +static int addWavePorts(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) { WaveTrackList* al = song->waves(); for (iWaveTrack i = al->begin(); i != al->end(); ++i) { Track* track = *i; if (t == track) continue; + id = addMenuItem(parent, t, track, lb, id, mm, channel, channels, isOutput); + + /* QString s(track->name()); - int it = lb->insertItem(s); + //int it = lb->insertItem(s); + lb->insertItem(s, id); for (iRoute ir = r->begin(); ir != r->end(); ++ir) { - if (ir->type == 0 && ir->track == track) { - lb->setItemChecked(it, true); + //if (ir->type == 0 && ir->track == track) { + if (ir->type == 0 && ir->track == track && ir->channels == channels) { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); break; } } + ++id; + */ + } + return id; } //--------------------------------------------------------- // addSyntiPorts //--------------------------------------------------------- -static void addSyntiPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +//static void addSyntiPorts(AudioTrack* t, QPopupMenu* lb, RouteList* r) +static int addSyntiPorts(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) +{ + RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); + + SynthIList* al = song->syntis(); + for (iSynthI i = al->begin(); i != al->end(); ++i) + { + Track* track = *i; + if (t == track) + continue; + //id = addMenuItem(parent, track, lb, r, id, mm, channel, channels); + + /* + QString s(track->name()); + //int it = lb->insertItem(s); + lb->insertItem(s, id); + for (iRoute ir = r->begin(); ir != r->end(); ++ir) { + //if (ir->type == 0 && ir->track == track) { + if (ir->type == 0 && ir->track == track && ir->channels == channels) { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); + break; + } + } + ++id; + */ + + //SynthI* synti = (SynthI*)track; + + int toch = ((AudioTrack*)track)->totalOutChannels(); + // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. + if(track->channels() == 1) + toch = 1; + + //int chans = synti->totalOutChannels(); + //int chans = (!isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? ((AudioTrack*)track)->totalOutChannels() : ((AudioTrack*)track)->totalInChannels(); + // totalInChannels is only used by syntis. + int chans = (!isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? toch : ((AudioTrack*)track)->totalInChannels(); + + //int schans = synti->channels(); + //if(schans < chans) + // chans = schans; + int tchans = (channels != -1) ? channels: t->channels(); + if(tchans == 2) + { + // Ignore odd numbered left-over mono channel. + //chans = chans & ~1; + //if(chans != 0) + chans -= 1; + } + + if(chans > 0) + { + QPopupMenu* chpup = new QPopupMenu(parent); + chpup->setCheckable(true); + for(int ch = 0; ch < chans; ++ch) + { + char buffer[128]; + if(tchans == 2) + snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").latin1(), ch+1, ch+2); + else + snprintf(buffer, 128, "%s %d", chpup->tr("Channel").latin1(), ch+1); + chpup->insertItem(QString(buffer), id); + + int ach = (channel == -1) ? ch : channel; + int bch = (channel == -1) ? -1 : ch; + + Route rt(track, (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans); + //Route rt(track, ch); + //rt.remoteChannel = -1; + rt.remoteChannel = (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach; + + mm.insert( pRouteMenuMap(id, rt) ); + + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + //if (ir->type == 0 && ir->track == track) { + //if(ir->type == 0 && ir->track == track && ir->channels == channels) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == channel && + // ir->channels == channels && ir->remoteChannel == r.remoteChannel) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == ch && + // ir->remoteChannel == rt.remoteChannel) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == rt.channel && + // ir->channels == rt.channels && ir->remoteChannel == rt.remoteChannel) + + if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) + { + int tcompch = rt.channel; + if(tcompch == -1) + tcompch = 0; + int tcompchs = rt.channels; + if(tcompchs == -1) + tcompchs = isOutput ? t->channels() : track->channels(); + + int compch = ir->channel; + if(compch == -1) + compch = 0; + int compchs = ir->channels; + if(compchs == -1) + compchs = isOutput ? t->channels() : ir->track->channels(); + + if(compch == tcompch && compchs == tcompchs) + { + chpup->setItemChecked(id, true); + break; + } + } + } + ++id; + } + + lb->insertItem(track->name(), chpup); + } + } + return id; +} + +//--------------------------------------------------------- +// addMultiChannelOutPorts +//--------------------------------------------------------- + +static int addMultiChannelPorts(QButton* parent, AudioTrack* t, QPopupMenu* pup, int id, RouteMenuMap& mm, bool isOutput) +{ + int toch = t->totalOutChannels(); + // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. + if(t->channels() == 1) + toch = 1; + + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + //int chans = t->totalOutChannels(); + // totalInChannels is only used by syntis. + //int chans = isOutput ? t->totalOutChannels() : t->totalInChannels(); + //int chans = (isOutput || t->type() != Track::AUDIO_SOFTSYNTH) ? t->totalOutChannels() : t->totalInChannels(); + int chans = (isOutput || t->type() != Track::AUDIO_SOFTSYNTH) ? toch : t->totalInChannels(); + + // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. + //if(t->channels() == 1) + // chans = 1; + + if(chans > 1) + { + pup->insertItem(new MenuTitleItem("<Mono>")); + //pup->insertSeparator(); + } + + // + // If it's more than one channel, create a sub-menu. If it's just one channel, don't bother with a sub-menu... + // + + QPopupMenu* chpup = pup; + + for(int ch = 0; ch < chans; ++ch) + { + // If more than one channel, create the sub-menu. + if(chans > 1) + { + chpup = new QPopupMenu(parent); + chpup->setCheckable(true); + } + + if(isOutput) + { + switch(t->type()) + { + + case Track::AUDIO_INPUT: + id = addWavePorts(parent, t, chpup, id, mm, ch, 1, isOutput); + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + id = addOutPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + break; + case Track::AUDIO_AUX: + id = addOutPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + break; + default: + break; + + /* + case Track::AUDIO_INPUT: + id = addWavePorts(parent, t, chpup, id, mm, ch, isOutput); + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + id = addOutPorts(parent, t, chpup, id, mm, ch, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, isOutput); + break; + case Track::AUDIO_AUX: + id = addOutPorts(parent, t, chpup, id, mm, ch, isOutput); + break; + default: + break; + */ + } + } + else + { + switch(t->type()) + { + + case Track::AUDIO_OUTPUT: + id = addWavePorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addInPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addAuxPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + break; + case Track::WAVE: + id = addInPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + break; + case Track::AUDIO_SOFTSYNTH: + case Track::AUDIO_GROUP: + id = addWavePorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addInPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, 1, isOutput); + break; + default: + break; + + /* + case Track::AUDIO_OUTPUT: + id = addWavePorts(parent, t, chpup, id, mm, ch, isOutput); + id = addInPorts(parent, t, chpup, id, mm, ch, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, isOutput); + id = addAuxPorts(parent, t, chpup, id, mm, ch, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, isOutput); + break; + case Track::WAVE: + id = addInPorts(parent, t, chpup, id, mm, ch, isOutput); + break; + case Track::AUDIO_SOFTSYNTH: + case Track::AUDIO_GROUP: + id = addWavePorts(parent, t, chpup, id, mm, ch, isOutput); + id = addInPorts(parent, t, chpup, id, mm, ch, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, isOutput); + break; + default: + break; + */ + } + } + + // If more than one channel, add the created sub-menu. + if(chans > 1) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d", pup->tr("Channel").latin1(), ch+1); + pup->insertItem(QString(buffer), chpup); + } + } + + // For stereo listing, ignore odd numbered left-over channels. + chans -= 1; + if(chans > 0) + { + // Ignore odd numbered left-over channels. + //int schans = (chans & ~1) - 1; + + pup->insertSeparator(); + pup->insertItem(new MenuTitleItem("<Stereo>")); + //pup->insertSeparator(); + + // + // If it's more than two channels, create a sub-menu. If it's just two channels, don't bother with a sub-menu... + // + + //QPopupMenu* chpup = pup; + chpup = pup; + if(chans <= 2) + // Just do one iteration. + chans = 1; + + //for(int ch = 0; ch < schans; ++ch) + for(int ch = 0; ch < chans; ++ch) + { + // If more than two channels, create the sub-menu. + if(chans > 2) { + chpup = new QPopupMenu(parent); + chpup->setCheckable(true); + } + + if(isOutput) + { + switch(t->type()) + { + case Track::AUDIO_INPUT: + id = addWavePorts(parent, t, chpup, id, mm, ch, 2, isOutput); + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + id = addOutPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + break; + case Track::AUDIO_AUX: + id = addOutPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + break; + default: + break; + } + } + else + { + switch(t->type()) + { + case Track::AUDIO_OUTPUT: + id = addWavePorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addInPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addAuxPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + break; + case Track::WAVE: + id = addInPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + break; + case Track::AUDIO_SOFTSYNTH: + case Track::AUDIO_GROUP: + id = addWavePorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addInPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addGroupPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + id = addSyntiPorts(parent, t, chpup, id, mm, ch, 2, isOutput); + break; + default: + break; + } + } + + // If more than two channels, add the created sub-menu. + if(chans > 2) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d,%d", pup->tr("Channel").latin1(), ch+1, ch+2); + pup->insertItem(QString(buffer), chpup); + } + } + } + + return id; +} + +//--------------------------------------------------------- +// nonSyntiTrackAddSyntis +//--------------------------------------------------------- + +//static int nonSyntiTrackAddSyntis(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, int channel, int channels, bool isOutput) +static int nonSyntiTrackAddSyntis(QButton* parent, AudioTrack* t, QPopupMenu* lb, int id, RouteMenuMap& mm, bool isOutput) +{ + RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); + SynthIList* al = song->syntis(); - for (iSynthI i = al->begin(); i != al->end(); ++i) { + for (iSynthI i = al->begin(); i != al->end(); ++i) + { Track* track = *i; if (t == track) continue; + //id = addMenuItem(parent, track, lb, r, id, mm, channel, channels); + + /* QString s(track->name()); - int it = lb->insertItem(s); + //int it = lb->insertItem(s); + lb->insertItem(s, id); for (iRoute ir = r->begin(); ir != r->end(); ++ir) { - if (ir->type == 0 && ir->track == track) { - lb->setItemChecked(it, true); + //if (ir->type == 0 && ir->track == track) { + if (ir->type == 0 && ir->track == track && ir->channels == channels) { + //lb->setItemChecked(it, true); + lb->setItemChecked(id, true); break; } } + ++id; + */ + + //SynthI* synti = (SynthI*)track; + + int toch = ((AudioTrack*)track)->totalOutChannels(); + // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. + if(track->channels() == 1) + toch = 1; + + //int chans = synti->totalOutChannels(); + //int chans = (!isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? ((AudioTrack*)track)->totalOutChannels() : ((AudioTrack*)track)->totalInChannels(); + // totalInChannels is only used by syntis. + int chans = (!isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? toch : ((AudioTrack*)track)->totalInChannels(); + + //int schans = synti->channels(); + //if(schans < chans) + // chans = schans; +// int tchans = (channels != -1) ? channels: t->channels(); +// if(tchans == 2) +// { + // Ignore odd numbered left-over mono channel. + //chans = chans & ~1; + //if(chans != 0) +// chans -= 1; +// } + //int tchans = (channels != -1) ? channels: t->channels(); + + if(chans > 0) + { + QPopupMenu* chpup = new QPopupMenu(parent); + chpup->setCheckable(true); + + if(chans > 1) + { + chpup->insertItem(new MenuTitleItem("<Mono>")); + //pup->insertSeparator(); + } + + for(int ch = 0; ch < chans; ++ch) + { + char buffer[128]; + //if(tchans == 2) + // snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").latin1(), ch+1, ch+2); + //else + snprintf(buffer, 128, "%s %d", chpup->tr("Channel").latin1(), ch+1); + chpup->insertItem(QString(buffer), id); + + //int ach = (channel == -1) ? ch : channel; + //int bch = (channel == -1) ? -1 : ch; + int ach = ch; + int bch = -1; + + //Route rt(track, (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans); + Route rt(track, isOutput ? bch : ach, 1); + //Route rt(track, ch); + + //rt.remoteChannel = -1; + //rt.remoteChannel = (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach; + rt.remoteChannel = isOutput ? ach : bch; + + mm.insert( pRouteMenuMap(id, rt) ); + + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + //if (ir->type == 0 && ir->track == track) { + //if(ir->type == 0 && ir->track == track && ir->channels == channels) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == channel && + // ir->channels == channels && ir->remoteChannel == r.remoteChannel) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == ch && + // ir->remoteChannel == rt.remoteChannel) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == rt.channel && + // ir->channels == rt.channels && ir->remoteChannel == rt.remoteChannel) + + if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) + { + int tcompch = rt.channel; + if(tcompch == -1) + tcompch = 0; + int tcompchs = rt.channels; + if(tcompchs == -1) + tcompchs = isOutput ? t->channels() : track->channels(); + + int compch = ir->channel; + if(compch == -1) + compch = 0; + int compchs = ir->channels; + if(compchs == -1) + compchs = isOutput ? t->channels() : ir->track->channels(); + + if(compch == tcompch && compchs == tcompchs) + { + chpup->setItemChecked(id, true); + break; + } + } + } + ++id; + } + + chans -= 1; + if(chans > 0) + { + // Ignore odd numbered left-over channels. + //int schans = (chans & ~1) - 1; + + chpup->insertSeparator(); + chpup->insertItem(new MenuTitleItem("<Stereo>")); + //pup->insertSeparator(); + + for(int ch = 0; ch < chans; ++ch) + { + char buffer[128]; + //if(tchans == 2) + snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").latin1(), ch+1, ch+2); + //else + // snprintf(buffer, 128, "%s %d", chpup->tr("Channel").latin1(), ch+1); + chpup->insertItem(QString(buffer), id); + + //int ach = (channel == -1) ? ch : channel; + //int bch = (channel == -1) ? -1 : ch; + int ach = ch; + int bch = -1; + + //Route rt(track, (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans); + Route rt(track, isOutput ? bch : ach, 2); + //Route rt(track, ch); + + //rt.remoteChannel = -1; + //rt.remoteChannel = (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach; + rt.remoteChannel = isOutput ? ach : bch; + + mm.insert( pRouteMenuMap(id, rt) ); + + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + //if (ir->type == 0 && ir->track == track) { + //if(ir->type == 0 && ir->track == track && ir->channels == channels) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == channel && + // ir->channels == channels && ir->remoteChannel == r.remoteChannel) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == ch && + // ir->remoteChannel == rt.remoteChannel) + //if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->channel == rt.channel && + // ir->channels == rt.channels && ir->remoteChannel == rt.remoteChannel) + + + if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) + { + int tcompch = rt.channel; + if(tcompch == -1) + tcompch = 0; + int tcompchs = rt.channels; + if(tcompchs == -1) + tcompchs = isOutput ? t->channels() : track->channels(); + + int compch = ir->channel; + if(compch == -1) + compch = 0; + int compchs = ir->channels; + if(compchs == -1) + compchs = isOutput ? t->channels() : ir->track->channels(); + + if(compch == tcompch && compchs == tcompchs) + { + chpup->setItemChecked(id, true); + break; + } + } + } + ++id; + } + } + + lb->insertItem(track->name(), chpup); + } + } + return id; +} + +//--------------------------------------------------------- +// iRoutePressed +//--------------------------------------------------------- + +void AudioStrip::iRoutePressed() + { + //if(track->isMidiTrack() || (track->type() == Track::AUDIO_AUX) || (track->type() == Track::AUDIO_SOFTSYNTH)) + if(track->isMidiTrack() || (track->type() == Track::AUDIO_AUX)) + return; + + QPopupMenu* pup = new QPopupMenu(iR); + //pup->setCheckable(true); + AudioTrack* t = (AudioTrack*)track; + RouteList* irl = t->inRoutes(); + + int gid = 0; + RouteMenuMap mm; + + switch(track->type()) + { + case Track::AUDIO_INPUT: + { + pup->setCheckable(true); + //int gid = 0; + for(int i = 0; i < channel; ++i) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d", tr("Channel").latin1(), i+1); + MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); + pup->insertItem(titel); + + if(!checkAudioDevice()) + { + delete pup; + return; + } + std::list<QString> ol = audioDevice->outputPorts(); + for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + int id = pup->insertItem(*ip, (gid * 16) + i); + //Route dst(*ip, true, i); + Route dst(*ip, true, i, Route::JACK_ROUTE); + ++gid; + for(iRoute ir = irl->begin(); ir != irl->end(); ++ir) + { + if(*ir == dst) + { + pup->setItemChecked(id, true); + break; + } + } + } + if(i+1 != channel) + pup->insertSeparator(); + } + } + break; + /* + case Track::AUDIO_OUTPUT: + case Track::WAVE: + case Track::AUDIO_GROUP: + */ + + case Track::AUDIO_OUTPUT: + gid = addWavePorts( iR, t, pup, gid, mm, -1, -1, false); + gid = addInPorts( iR, t, pup, gid, mm, -1, -1, false); + gid = addGroupPorts(iR, t, pup, gid, mm, -1, -1, false); + gid = addAuxPorts( iR, t, pup, gid, mm, -1, -1, false); + //gid = addSyntiPorts(iR, t, pup, gid, mm, -1, -1, false); + gid = nonSyntiTrackAddSyntis(iR, t, pup, gid, mm, false); + break; + case Track::WAVE: + gid = addInPorts( iR, t, pup, gid, mm, -1, -1, false); + break; + case Track::AUDIO_GROUP: + gid = addWavePorts( iR, t, pup, gid, mm, -1, -1, false); + gid = addInPorts( iR, t, pup, gid, mm, -1, -1, false); + gid = addGroupPorts(iR, t, pup, gid, mm, -1, -1, false); + //gid = addSyntiPorts(iR, t, pup, gid, mm, -1, -1, false); + gid = nonSyntiTrackAddSyntis(iR, t, pup, gid, mm, false); + break; + + case Track::AUDIO_SOFTSYNTH: + gid = addMultiChannelPorts(iR, t, pup, gid, mm, false); + break; + default: + delete pup; + return; + } + + if(pup->count() == 0) + { + delete pup; + return; + } + + int n = pup->exec(QCursor::pos()); + if(n != -1) + { + QString s(pup->text(n)); + + if(track->type() == Track::AUDIO_INPUT) + { + delete pup; + int chan = n & 0xf; + + Route srcRoute(s, false, -1, Route::JACK_ROUTE); + Route dstRoute(t, chan); + + srcRoute.channel = chan; + + iRoute iir = irl->begin(); + for(; iir != irl->end(); ++iir) + { + if(*iir == srcRoute) + break; + } + if(iir != irl->end()) + // disconnect + audio->msgRemoveRoute(srcRoute, dstRoute); + else + // connect + audio->msgAddRoute(srcRoute, dstRoute); + + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + iR->setDown(false); // pup->exec() catches mouse release event + return; + } + + iRouteMenuMap imm = mm.find(n); + if(imm == mm.end()) + { + delete pup; + iR->setDown(false); // pup->exec() catches mouse release event + return; + } + + //int chan = n >> 16; + //int chans = (chan >> 15) + 1; // Bit 31 MSB: Mono or stereo. + //chan &= 0xffff; + //int chan = imm->second.channel; + //int chans = imm->second.channels; + + //Route srcRoute(s, false, -1); + //Route srcRoute(s, false, -1, Route::TRACK_ROUTE); + Route &srcRoute = imm->second; + + //Route dstRoute(t, -1); + //Route dstRoute(t, chan, chans); + Route dstRoute(t, imm->second.channel, imm->second.channels); + //Route dstRoute(t, imm->second.channel); + dstRoute.remoteChannel = imm->second.remoteChannel; + + iRoute iir = irl->begin(); + for (; iir != irl->end(); ++iir) { + if (*iir == srcRoute) + break; + } + if (iir != irl->end()) { + // disconnect + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + delete pup; + iR->setDown(false); // pup->exec() catches mouse release event + } + +//--------------------------------------------------------- +// oRoutePressed +//--------------------------------------------------------- + +void AudioStrip::oRoutePressed() +{ + if(track->isMidiTrack()) + return; + + QPopupMenu* pup = new QPopupMenu(oR); + //pup->setCheckable(true); + AudioTrack* t = (AudioTrack*)track; + RouteList* orl = t->outRoutes(); + + int gid = 0; + RouteMenuMap mm; + + switch(track->type()) + { + case Track::AUDIO_OUTPUT: + { + pup->setCheckable(true); + //int gid = 0; + for(int i = 0; i < channel; ++i) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d", tr("Channel").latin1(), i+1); + MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); + pup->insertItem(titel); + + if(!checkAudioDevice()) + { + delete pup; + return; + } + std::list<QString> ol = audioDevice->inputPorts(); + for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + int id = pup->insertItem(*ip, (gid * 16) + i); + //Route dst(*ip, true, i); + Route dst(*ip, true, i, Route::JACK_ROUTE); + ++gid; + for(iRoute ir = orl->begin(); ir != orl->end(); ++ir) + { + if(*ir == dst) + { + pup->setItemChecked(id, true); + break; + } + } + } + if(i+1 != channel) + pup->insertSeparator(); + } + } + break; + /* + case Track::AUDIO_INPUT: + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + */ + + case Track::AUDIO_SOFTSYNTH: + //addOutPorts(t, pup, orl); + //addGroupPorts(t, pup, orl); + gid = addMultiChannelPorts(oR, t, pup, gid, mm, true); + break; + + case Track::AUDIO_INPUT: + gid = addWavePorts( oR, t, pup, gid, mm, -1, -1, true); + case Track::WAVE: + case Track::AUDIO_GROUP: + //case Track::AUDIO_SOFTSYNTH: + gid = addOutPorts( oR, t, pup, gid, mm, -1, -1, true); + gid = addGroupPorts( oR, t, pup, gid, mm, -1, -1, true); + //gid = addSyntiPorts( oR, t, pup, gid, mm, -1, -1, true); + gid = nonSyntiTrackAddSyntis(oR, t, pup, gid, mm, true); + break; + case Track::AUDIO_AUX: + gid = addOutPorts( oR, t, pup, gid, mm, -1, -1, true); + break; + + default: + delete pup; + return; + } + + if(pup->count() == 0) + { + delete pup; + return; + } + + int n = pup->exec(QCursor::pos()); + if (n != -1) { + QString s(pup->text(n)); + + if(track->type() == Track::AUDIO_OUTPUT) + { + delete pup; + int chan = n & 0xf; + + //Route srcRoute(t, -1); + //Route srcRoute(t, chan, chans); + //Route srcRoute(t, chan, 1); + Route srcRoute(t, chan); + + //Route dstRoute(s, true, -1); + Route dstRoute(s, true, -1, Route::JACK_ROUTE); + //Route dstRoute(s, true, 0, Route::JACK_ROUTE); + + //srcRoute.channel = dstRoute.channel = chan; + dstRoute.channel = chan; + //dstRoute.channels = 1; + + // check if route src->dst exists: + iRoute iorl = orl->begin(); + for (; iorl != orl->end(); ++iorl) { + if (*iorl == dstRoute) + break; + } + if (iorl != orl->end()) { + // disconnect if route exists + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect if route does not exist + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + oR->setDown(false); // pup->exec() catches mouse release event + return; + } + + iRouteMenuMap imm = mm.find(n); + if(imm == mm.end()) + { + delete pup; + oR->setDown(false); // pup->exec() catches mouse release event + return; + } + + //int chan = n >> 16; + //int chans = (chan >> 15) + 1; // Bit 31 MSB: Mono or stereo. + //chan &= 0xffff; + //int chan = imm->second.channel; + //int chans = imm->second.channels; + + //Route srcRoute(t, -1); + //srcRoute.remoteChannel = chan; + //Route srcRoute(t, chan, chans); + Route srcRoute(t, imm->second.channel, imm->second.channels); + //Route srcRoute(t, imm->second.channel); + srcRoute.remoteChannel = imm->second.remoteChannel; + + //Route dstRoute(s, true, -1); + //Route dstRoute(s, true, -1, Route::TRACK_ROUTE); + Route &dstRoute = imm->second; + + // check if route src->dst exists: + iRoute iorl = orl->begin(); + for (; iorl != orl->end(); ++iorl) { + if (*iorl == dstRoute) + break; + } + if (iorl != orl->end()) { + // disconnect if route exists + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect if route does not exist + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + delete pup; + oR->setDown(false); // pup->exec() catches mouse release event +} + +/* +//--------------------------------------------------------- +// iRoutePressed +//--------------------------------------------------------- + +void AudioStrip::iRoutePressed() + { + if(track->isMidiTrack() || (track->type() == Track::AUDIO_AUX) || (track->type() == Track::AUDIO_SOFTSYNTH)) + return; + + QPopupMenu* pup = new QPopupMenu(iR); + //pup->setCheckable(true); + AudioTrack* t = (AudioTrack*)track; + RouteList* irl = t->inRoutes(); + + RouteMenuMap mm; + + if(track->type() == Track::AUDIO_INPUT) + { + pup->setCheckable(true); + int gid = 0; + for(int i = 0; i < channel; ++i) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d", tr("Channel").latin1(), i+1); + MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); + pup->insertItem(titel); + + if(!checkAudioDevice()) + { + delete pup; + return; + } + std::list<QString> ol = audioDevice->outputPorts(); + for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + int id = pup->insertItem(*ip, (gid * 16) + i); + //Route dst(*ip, true, i); + Route dst(*ip, true, i, Route::JACK_ROUTE); + ++gid; + for(iRoute ir = irl->begin(); ir != irl->end(); ++ir) + { + if(*ir == dst) + { + pup->setItemChecked(id, true); + break; + } } + } + if(i+1 != channel) + pup->insertSeparator(); + } } + else + addMultiChannelOutPorts(iR, t, pup, irl, mm, false); + + int n = pup->exec(QCursor::pos()); + if (n != -1) { + QString s(pup->text(n)); + + //Route srcRoute(s, false, -1); + Route srcRoute(s, false, -1, (track->type() == Track::AUDIO_INPUT) ? Route::JACK_ROUTE : Route::TRACK_ROUTE); + Route dstRoute(t, -1); + + if (track->type() == Track::AUDIO_INPUT) + srcRoute.channel = dstRoute.channel = n & 0xf; + iRoute iir = irl->begin(); + for (; iir != irl->end(); ++iir) { + if (*iir == srcRoute) + break; + } + if (iir != irl->end()) { + // disconnect + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + delete pup; + iR->setDown(false); // pup->exec() catches mouse release event + } +*/ +/* +//--------------------------------------------------------- +// oRoutePressed +//--------------------------------------------------------- + +void AudioStrip::oRoutePressed() +{ + if(track->isMidiTrack()) + return; + + QPopupMenu* pup = new QPopupMenu(oR); + //pup->setCheckable(true); + AudioTrack* t = (AudioTrack*)track; + RouteList* orl = t->outRoutes(); + + RouteMenuMap mm; + + if(track->type() == Track::AUDIO_OUTPUT) + { + pup->setCheckable(true); + int gid = 0; + for(int i = 0; i < channel; ++i) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d", tr("Channel").latin1(), i+1); + MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); + pup->insertItem(titel); + + if(!checkAudioDevice()) + { + delete pup; + return; + } + std::list<QString> ol = audioDevice->inputPorts(); + for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + int id = pup->insertItem(*ip, (gid * 16) + i); + //Route dst(*ip, true, i); + Route dst(*ip, true, i, Route::JACK_ROUTE); + ++gid; + for(iRoute ir = orl->begin(); ir != orl->end(); ++ir) + { + if(*ir == dst) + { + pup->setItemChecked(id, true); + break; + } + } + } + if(i+1 != channel) + pup->insertSeparator(); + } + } + else + addMultiChannelOutPorts(oR, t, pup, orl, mm, true); + + int n = pup->exec(QCursor::pos()); + if (n != -1) { + QString s(pup->text(n)); + + if(track->type() == Track::AUDIO_OUTPUT) + { + delete pup; + int chan = n & 0xf; + + //Route srcRoute(t, -1); + //Route srcRoute(t, chan, chans); + Route srcRoute(t, chan, 1); + + //Route dstRoute(s, true, -1); + Route dstRoute(s, true, -1, Route::JACK_ROUTE); + //Route dstRoute(s, true, 0, Route::JACK_ROUTE); + + //srcRoute.channel = dstRoute.channel = chan; + dstRoute.channel = chan; + dstRoute.channels = 1; + + // check if route src->dst exists: + iRoute iorl = orl->begin(); + for (; iorl != orl->end(); ++iorl) { + if (*iorl == dstRoute) + break; + } + if (iorl != orl->end()) { + // disconnect if route exists + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect if route does not exist + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + oR->setDown(false); // pup->exec() catches mouse release event + return; + } + + iRouteMenuMap imm = mm.find(n); + if(imm == mm.end()) + { + delete pup; + oR->setDown(false); // pup->exec() catches mouse release event + return; + } + + //int chan = n >> 16; + //int chans = (chan >> 15) + 1; // Bit 31 MSB: Mono or stereo. + //chan &= 0xffff; + int chan = imm->second.channel; + int chans = imm->second.channels; + + //Route srcRoute(t, -1); + Route srcRoute(t, chan, chans); + + //Route dstRoute(s, true, -1); + Route dstRoute(s, true, -1, Route::TRACK_ROUTE); + + // check if route src->dst exists: + iRoute iorl = orl->begin(); + for (; iorl != orl->end(); ++iorl) { + if (*iorl == dstRoute) + break; + } + if (iorl != orl->end()) { + // disconnect if route exists + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect if route does not exist + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + delete pup; + oR->setDown(false); // pup->exec() catches mouse release event +} +*/ + +/* //--------------------------------------------------------- // iRoutePressed //--------------------------------------------------------- @@ -1124,7 +2384,8 @@ void AudioStrip::iRoutePressed() std::list<QString> ol = audioDevice->outputPorts(); for (std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) { int id = pup->insertItem(*ip, (gid * 16) + i); - Route dst(*ip, true, i); + //Route dst(*ip, true, i); + Route dst(*ip, true, i, Route::JACK_ROUTE); ++gid; for (iRoute ir = irl->begin(); ir != irl->end(); ++ir) { if (*ir == dst) { @@ -1138,7 +2399,6 @@ void AudioStrip::iRoutePressed() } } break; - break; case Track::AUDIO_OUTPUT: addWavePorts(t, pup, irl); addInPorts(t, pup, irl); @@ -1159,7 +2419,9 @@ void AudioStrip::iRoutePressed() int n = pup->exec(QCursor::pos()); if (n != -1) { QString s(pup->text(n)); - Route srcRoute(s, false, -1); + + //Route srcRoute(s, false, -1); + Route srcRoute(s, false, -1, (track->type() == Track::AUDIO_INPUT) ? Route::JACK_ROUTE : Route::TRACK_ROUTE); Route dstRoute(t, -1); if (track->type() == Track::AUDIO_INPUT) @@ -1183,7 +2445,9 @@ void AudioStrip::iRoutePressed() delete pup; iR->setDown(false); // pup->exec() catches mouse release event } +*/ +/* //--------------------------------------------------------- // oRoutePressed //--------------------------------------------------------- @@ -1213,7 +2477,8 @@ void AudioStrip::oRoutePressed() std::list<QString> ol = audioDevice->inputPorts(); for (std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) { int id = pup->insertItem(*ip, (gid * 16) + i); - Route dst(*ip, true, i); + //Route dst(*ip, true, i); + Route dst(*ip, true, i, Route::JACK_ROUTE); ++gid; for (iRoute ir = orl->begin(); ir != orl->end(); ++ir) { if (*ir == dst) { @@ -1243,7 +2508,8 @@ void AudioStrip::oRoutePressed() if (n != -1) { QString s(pup->text(n)); Route srcRoute(t, -1); - Route dstRoute(s, true, -1); + //Route dstRoute(s, true, -1); + Route dstRoute(s, true, -1, (track->type() == Track::AUDIO_OUTPUT) ? Route::JACK_ROUTE : Route::TRACK_ROUTE); if (track->type() == Track::AUDIO_OUTPUT) srcRoute.channel = dstRoute.channel = n & 0xf; @@ -1268,4 +2534,4 @@ void AudioStrip::oRoutePressed() delete pup; oR->setDown(false); // pup->exec() catches mouse release event } - +*/ diff --git a/muse/muse/mixer/mstrip.cpp b/muse/muse/mixer/mstrip.cpp index cd9cea86..a2cd26dd 100644 --- a/muse/muse/mixer/mstrip.cpp +++ b/muse/muse/mixer/mstrip.cpp @@ -17,11 +17,14 @@ #include <qcombobox.h> #include <qtooltip.h> #include <qtimer.h> +#include <qpopupmenu.h> +#include <qcursor.h> #include <math.h> #include "midi.h" #include "midictrl.h" #include "mstrip.h" +#include "midiport.h" #include "globals.h" #include "audio.h" #include "song.h" @@ -343,7 +346,7 @@ MidiStrip::MidiStrip(QWidget* parent, MidiTrack* t) dev_ch_label->setFont(config.fonts[1]); // Dealing with a horizontally constrained label. Ignore vertical. Use a minimum readable point size. //autoAdjustFontSize(dev_ch_label, dev_ch_label->text(), false, true, config.fonts[6].pointSize(), 5); - QToolTip::add(dev_ch_label, tr("output: device - channel")); + QToolTip::add(dev_ch_label, tr("output port and channel")); smBox1->addWidget(dev_ch_label); smBox1->addWidget(record); @@ -351,15 +354,36 @@ MidiStrip::MidiStrip(QWidget* parent, MidiTrack* t) layout->addLayout(smBox2); //--------------------------------------------------- - // output routing + // routing //--------------------------------------------------- - route = new QToolButton(this); - route->setFont(config.fonts[1]); - route->setFixedWidth(STRIP_WIDTH); - route->setText(tr("Route")); - QToolTip::add(route, tr("set routing")); - layout->addWidget(route); + // p3.3.38 + //route = new QToolButton(this); + //route->setFont(config.fonts[1]); + //route->setFixedWidth(STRIP_WIDTH); + //route->setText(tr("Route")); + //QToolTip::add(route, tr("set routing")); + //layout->addWidget(route); + QHBoxLayout* rBox = new QHBoxLayout(0); + iR = new QToolButton(this); + iR->setFont(config.fonts[1]); + iR->setFixedWidth((STRIP_WIDTH-4)/2); + iR->setText(tr("iR")); + iR->setToggleButton(false); + QToolTip::add(iR, tr("input routing")); + rBox->addWidget(iR); + connect(iR, SIGNAL(pressed()), SLOT(iRoutePressed())); + oR = new QToolButton(this); + oR->setFont(config.fonts[1]); + oR->setFixedWidth((STRIP_WIDTH-4)/2); + oR->setText(tr("oR")); + oR->setToggleButton(false); + // TODO: Works OK, but disabled for now, until we figure out what to do about multiple out routes and display values... + oR->setEnabled(false); + QToolTip::add(oR, tr("output routing")); + rBox->addWidget(oR); + connect(oR, SIGNAL(pressed()), SLOT(oRoutePressed())); + layout->addLayout(rBox); //--------------------------------------------------- // automation mode @@ -518,6 +542,7 @@ void MidiStrip::labelDoubleClicked(int idx) } +/* //--------------------------------------------------------- // routeClicked //--------------------------------------------------------- @@ -525,6 +550,7 @@ void MidiStrip::labelDoubleClicked(int idx) void MidiStrip::routeClicked() { } +*/ //--------------------------------------------------------- // heartBeat @@ -929,3 +955,224 @@ void MidiStrip::updateOffState() // Ripped from AudioStrip, hehh(mg) if (mute) mute->setEnabled(val); } + +//--------------------------------------------------------- +// iRoutePressed +//--------------------------------------------------------- + +void MidiStrip::iRoutePressed() +{ + if(!track->isMidiTrack()) + return; + + song->chooseMidiRoutes(iR, (MidiTrack*)track, false); + + /* + RouteList* irl = track->inRoutes(); + //Route dst(track, -1); + + QPopupMenu* pup = new QPopupMenu(iR); + pup->setCheckable(true); + + int gid = 0; + + //MidiInPortList* tl = song->midiInPorts(); + //for(iMidiInPort i = tl->begin();i != tl->end(); ++i) + for(int i = 0; i < MIDI_PORTS; ++i) + { + //MidiInPort* track = *i; + // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick wth ports. + MidiPort* mp = &midiPorts[i]; + MidiDevice* md = mp->device(); + if(!md) + continue; + + if(!(md->rwFlags() & 2)) + continue; + + //printf("MidiStrip::iRoutePressed adding submenu portnum:%d\n", i); + + //QMenu* m = menu->addMenu(track->name()); + QPopupMenu* subp = new QPopupMenu(iR); + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + //QAction* a = m->addAction(QString("Channel %1").arg(ch+1)); + //subp->insertItem(QT_TR_NOOP(QString("Channel %1").arg(ch+1)), i * MIDI_CHANNELS + ch); + gid = i * MIDI_CHANNELS + ch; + + //printf("MidiStrip::iRoutePressed inserting gid:%d\n", gid); + + subp->insertItem(QString("Channel %1").arg(ch+1), gid); + //a->setCheckable(true); + //Route src(track, ch, RouteNode::TRACK); + //Route src(md, ch); + //Route r = Route(src, dst); + //a->setData(QVariant::fromValue(r)); + //a->setChecked(rl->indexOf(r) != -1); + Route srcRoute(md, ch); + for(iRoute ir = irl->begin(); ir != irl->end(); ++ir) + { + //if(*ir == dst) + if(*ir == srcRoute) + { + subp->setItemChecked(gid, true); + break; + } + } + } + pup->insertItem(QT_TR_NOOP(md->name()), subp); + } + + int n = pup->exec(QCursor::pos()); + delete pup; + if (n != -1) + { + int mdidx = n / MIDI_CHANNELS; + int ch = n % MIDI_CHANNELS; + + //if(debugMsg) + printf("MidiStrip::iRoutePressed mdidx:%d ch:%d\n", mdidx, ch); + + MidiPort* mp = &midiPorts[mdidx]; + MidiDevice* md = mp->device(); + if(!md) + return; + + if(!(md->rwFlags() & 2)) + return; + + + //QString s(pup->text(n)); + //QT_TR_NOOP(md->name()) + + //Route srcRoute(s, false, -1); + Route srcRoute(md, ch); + //Route srcRoute(md, -1); + //Route dstRoute(track, -1); + Route dstRoute(track, ch); + + //if (track->type() == Track::AUDIO_INPUT) + // srcRoute.channel = dstRoute.channel = n & 0xf; + iRoute iir = irl->begin(); + for (; iir != irl->end(); ++iir) { + if (*iir == srcRoute) + break; + } + if (iir != irl->end()) { + // disconnect + printf("MidiStrip::iRoutePressed removing route src device name: %s dst track name: %s\n", md->name().latin1(), track->name().latin1()); + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect + printf("MidiStrip::iRoutePressed adding route src device name: %s dst track name: %s\n", md->name().latin1(), track->name().latin1()); + audio->msgAddRoute(srcRoute, dstRoute); + } + printf("MidiStrip::iRoutePressed calling msgUpdateSoloStates\n"); + audio->msgUpdateSoloStates(); + printf("MidiStrip::iRoutePressed calling song->update\n"); + song->update(SC_ROUTE); + } + //delete pup; + iR->setDown(false); // pup->exec() catches mouse release event + printf("MidiStrip::iRoutePressed end\n"); + */ + +} + +//--------------------------------------------------------- +// oRoutePressed +//--------------------------------------------------------- + +void MidiStrip::oRoutePressed() + { + if(!track->isMidiTrack()) + return; + + song->chooseMidiRoutes(oR, (MidiTrack*)track, true); + + /* + QPopupMenu* pup = new QPopupMenu(oR); + pup->setCheckable(true); + AudioTrack* t = (AudioTrack*)track; + RouteList* orl = t->outRoutes(); + + switch(track->type()) { + case Track::MIDI: + case Track::DRUM: + delete pup; + return; + case Track::AUDIO_OUTPUT: + { + int gid = 0; + for (int i = 0; i < channel; ++i) { + char buffer[128]; + snprintf(buffer, 128, "%s %d", tr("Channel").latin1(), i+1); + MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); + pup->insertItem(titel); + + if (!checkAudioDevice()) return; + std::list<QString> ol = audioDevice->inputPorts(); + for (std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) { + int id = pup->insertItem(*ip, (gid * 16) + i); + Route dst(*ip, true, i); + ++gid; + for (iRoute ir = orl->begin(); ir != orl->end(); ++ir) { + if (*ir == dst) { + pup->setItemChecked(id, true); + break; + } + } + } + if (i+1 != channel) + pup->insertSeparator(); + } + } + break; + case Track::AUDIO_INPUT: + addWavePorts(t, pup, orl); + case Track::WAVE: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + addOutPorts(t, pup, orl); + addGroupPorts(t, pup, orl); + break; + case Track::AUDIO_AUX: + addOutPorts(t, pup, orl); + break; + } + int n = pup->exec(QCursor::pos()); + if (n != -1) { + QString s(pup->text(n)); + Route srcRoute(t, -1); + Route dstRoute(s, true, -1); + + if (track->type() == Track::AUDIO_OUTPUT) + srcRoute.channel = dstRoute.channel = n & 0xf; + + // check if route src->dst exists: + iRoute iorl = orl->begin(); + for (; iorl != orl->end(); ++iorl) { + if (*iorl == dstRoute) + break; + } + if (iorl != orl->end()) { + // disconnect if route exists + audio->msgRemoveRoute(srcRoute, dstRoute); + } + else { + // connect if route does not exist + audio->msgAddRoute(srcRoute, dstRoute); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + delete pup; + oR->setDown(false); // pup->exec() catches mouse release event + */ + + + } + + diff --git a/muse/muse/mixer/mstrip.h b/muse/muse/mixer/mstrip.h index e32b4310..460a7d65 100644 --- a/muse/muse/mixer/mstrip.h +++ b/muse/muse/mixer/mstrip.h @@ -28,7 +28,9 @@ class MidiStrip : public Strip { Slider* slider; DoubleLabel* sl; - QToolButton* route; + //QToolButton* route; + QToolButton* iR; + QToolButton* oR; struct KNOB { Knob* knob; @@ -49,7 +51,9 @@ class MidiStrip : public Strip { void updateOffState(); private slots: - void routeClicked(); + //void routeClicked(); + void iRoutePressed(); + void oRoutePressed(); void setVolume(double); void setPan(double); void setChorusSend(double); diff --git a/muse/muse/mixer/routedialog.cpp b/muse/muse/mixer/routedialog.cpp index 595305c0..e3b1f211 100644 --- a/muse/muse/mixer/routedialog.cpp +++ b/muse/muse/mixer/routedialog.cpp @@ -49,13 +49,16 @@ void RouteDialog::routingChanged() for (ciTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->isMidiTrack()) continue; - WaveTrack* track = (WaveTrack*)(*i); + // p3.3.38 + //WaveTrack* track = (WaveTrack*)(*i); + AudioTrack* track = (AudioTrack*)(*i); if (track->type() == Track::AUDIO_INPUT) { for (int channel = 0; channel < track->channels(); ++channel) newDstList->insertItem(Route(track, channel).name()); const RouteList* rl = track->inRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - Route dst(track->name(), true, r->channel); + //Route dst(track->name(), true, r->channel); + Route dst(track->name(), true, r->channel, Route::TRACK_ROUTE); new QListViewItem(routeList, r->name(), dst.name()); } } diff --git a/muse/muse/node.cpp b/muse/muse/node.cpp index 4928420f..178c87cc 100644 --- a/muse/muse/node.cpp +++ b/muse/muse/node.cpp @@ -177,7 +177,7 @@ void AudioTrack::updateInternalSoloStates() const RouteList* rl = inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - if(ir->type == TRACK_ROUTE) + if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } } @@ -186,7 +186,7 @@ void AudioTrack::updateInternalSoloStates() const RouteList* rl = outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - if(ir->type == TRACK_ROUTE) + if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } } @@ -243,7 +243,7 @@ void AudioTrack::updateSoloStates(bool noDec) const RouteList* rl = inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - if(ir->type == TRACK_ROUTE) + if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } } @@ -252,7 +252,7 @@ void AudioTrack::updateSoloStates(bool noDec) const RouteList* rl = outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - if(ir->type == TRACK_ROUTE) + if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } } @@ -280,7 +280,8 @@ void Track::setOff(bool val) // copyData //--------------------------------------------------------- -void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float** dstBuffer) +//void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float** dstBuffer) +void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer) { //Changed by T356. 12/12/09. // Overhaul and streamline to eliminate multiple processing during one process loop. @@ -288,9 +289,14 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float // Make better use of AudioTrack::outBuffers as a post-effect pre-volume cache system for multiple calls here during processing. // Previously only WaveTrack used them. (Changed WaveTrack as well). - int srcChannels = channels(); + if(srcStartChan == -1) + srcStartChan = 0; + + int srcChans = (srcChannels == -1) ? channels() : srcChannels; + int srcTotalOutChans = totalOutChannels(); + if(channels() == 1) + srcTotalOutChans = 1; - //Added by Tim. p3.3.16 #ifdef NODE_DEBUG printf("MusE: AudioTrack::copyData name:%s processed:%d\n", name().latin1(), processed()); #endif @@ -304,7 +310,11 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float int i; - float* buffer[srcChannels]; + // p3.3.38 + //float* buffer[srcChannels]; + float* buffer[srcTotalOutChans]; + + //float data[nframes * srcChannels]; //for(i = 0; i < srcChannels; ++i) // buffer[i] = data + i * nframes; @@ -315,7 +325,7 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float double _pan = pan(); vol[0] = _volume * (1.0 - _pan); vol[1] = _volume * (1.0 + _pan); - float meter[srcChannels]; + float meter[srcChans]; // Have we been here already during this process cycle? if(processed()) @@ -330,7 +340,10 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float if(_haveData) { // Point the input buffers at our local cached 'pre-volume' buffers. They need processing, so continue on after. - for(i = 0; i < srcChannels; ++i) + //for(i = 0; i < srcChannels; ++i) + // buffer[i] = outBuffers[i]; + // p3.3.38 + for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = outBuffers[i]; } else @@ -354,8 +367,12 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float // First time here during this process cycle. // Point the input buffers at a temporary stack buffer. - float data[nframes * srcChannels]; - for(i = 0; i < srcChannels; ++i) + //float data[nframes * srcChannels]; + //for(i = 0; i < srcChannels; ++i) + // buffer[i] = data + i * nframes; + // p3.3.38 + float data[nframes * srcTotalOutChans]; + for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = data + i * nframes; // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. @@ -363,9 +380,10 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float // p3.3.29 1/27/10 Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! //if(!getData(pos, srcChannels, nframes, buffer) || off() || (isMute() && !_prefader)) - if(off() || !getData(pos, srcChannels, nframes, buffer) || (isMute() && !_prefader)) + //if(off() || !getData(pos, srcChannels, nframes, buffer) || (isMute() && !_prefader)) + // p3.3.38 + if(off() || !getData(pos, srcTotalOutChans, nframes, buffer) || (isMute() && !_prefader)) { - //Added by Tim. p3.3.16 #ifdef NODE_DEBUG printf("MusE: AudioTrack::copyData name:%s dstChannels:%d zeroing buffers\n", name().latin1(), dstChannels); #endif @@ -383,7 +401,7 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float memset(dstBuffer[i], 0, sizeof(float) * nframes); } - for(i = 0; i < srcChannels; ++i) + for(i = 0; i < srcChans; ++i) { //_meter[i] = 0; _meter[i] = 0.0; @@ -411,7 +429,7 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float // apply plugin chain //--------------------------------------------------- - _efxPipe->apply(srcChannels, nframes, buffer); + _efxPipe->apply(srcChans, nframes, buffer); //--------------------------------------------------- // aux sends @@ -429,9 +447,9 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float AudioAux* a = (AudioAux*)((*al)[k]); float** dst = a->sendBuffer(); int auxChannels = a->channels(); - if((srcChannels ==1 && auxChannels==1) || srcChannels == 2) + if((srcChans ==1 && auxChannels==1) || srcChans == 2) { - for(int ch = 0; ch < srcChannels; ++ch) + for(int ch = 0; ch < srcChans; ++ch) { float* db = dst[ch % a->channels()]; // no matter whether there's one or two dst buffers float* sb = buffer[ch]; @@ -439,7 +457,7 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float *db++ += (*sb++ * m * vol[ch]); // add to mix } } - else if(srcChannels==1 && auxChannels==2) // copy mono to both channels + else if(srcChans==1 && auxChannels==2) // copy mono to both channels { for(int ch = 0; ch < auxChannels; ++ch) { @@ -458,7 +476,7 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float if(_prefader) { - for(i = 0; i < srcChannels; ++i) + for(i = 0; i < srcChans; ++i) { float* p = buffer[i]; meter[i] = 0.0; @@ -514,7 +532,10 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float // If we're using local cached 'pre-volume' buffers, copy the input buffers (as they are right now: post-effect pre-volume) back to them. if(!usedirectbuf) { - for(i = 0; i < srcChannels; ++i) + //for(i = 0; i < srcChannels; ++i) + // AL::dsp->cpy(outBuffers[i], buffer[i], nframes); + // p3.3.38 + for(i = 0; i < srcTotalOutChans; ++i) AL::dsp->cpy(outBuffers[i], buffer[i], nframes); } @@ -522,18 +543,43 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float _haveData = true; } + // Sanity check. Is source starting channel out of range? Just zero and return. + if(srcStartChan >= srcTotalOutChans) + { + unsigned int q; + for(i = 0; i < dstChannels; ++i) + { + if(config.useDenormalBias) + { + for(q = 0; q < nframes; q++) + dstBuffer[i][q] = denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } + _processed = true; + return; + } + // Force a source range to fit actual available total out channels. + if((srcStartChan + srcChans) > srcTotalOutChans) + srcChans = srcTotalOutChans - srcStartChan; + //--------------------------------------------------- // apply volume // postfader metering //--------------------------------------------------- - if(srcChannels == dstChannels) + + if(srcChans == dstChannels) { if(_prefader) { for(int c = 0; c < dstChannels; ++c) { - float* sp = buffer[c]; + // p3.3.38 + //float* sp = buffer[c]; + float* sp = buffer[c + srcStartChan]; + float* dp = dstBuffer[c]; for(unsigned k = 0; k < nframes; ++k) *dp++ = (*sp++ * vol[c]); @@ -544,7 +590,11 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float for(int c = 0; c < dstChannels; ++c) { meter[c] = 0.0; - float* sp = buffer[c]; + + // p3.3.38 + //float* sp = buffer[c]; + float* sp = buffer[c + srcStartChan]; + float* dp = dstBuffer[c]; //printf("2 dstBuffer[c]=%d\n",long(dstBuffer[c])); for(unsigned k = 0; k < nframes; ++k) @@ -562,9 +612,12 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float } } } - else if(srcChannels == 1 && dstChannels == 2) + else if(srcChans == 1 && dstChannels == 2) { - float* sp = buffer[0]; + // p3.3.38 + //float* sp = buffer[0]; + float* sp = buffer[srcStartChan]; + if(_prefader) { for(int c = 0; c < dstChannels; ++c) @@ -592,10 +645,14 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float _peak[0] = _meter[0]; } } - else if(srcChannels == 2 && dstChannels == 1) + else if(srcChans == 2 && dstChannels == 1) { - float* sp1 = buffer[0]; - float* sp2 = buffer[1]; + // p3.3.38 + //float* sp1 = buffer[0]; + //float* sp2 = buffer[1]; + float* sp1 = buffer[srcStartChan]; + float* sp2 = buffer[srcStartChan + 1]; + if(_prefader) { float* dp = dstBuffer[0]; @@ -637,7 +694,8 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float // addData //--------------------------------------------------------- -void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float** dstBuffer) +//void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float** dstBuffer) +void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer) { //Changed by T356. 12/12/09. // Overhaul and streamline to eliminate multiple processing during one process loop. @@ -656,7 +714,13 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* return; } - int srcChannels = channels(); + if(srcStartChan == -1) + srcStartChan = 0; + + int srcChans = (srcChannels == -1) ? channels() : srcChannels; + int srcTotalOutChans = totalOutChannels(); + if(channels() == 1) + srcTotalOutChans = 1; // Special consideration for metronome: It is not part of the track list, // and it has no in or out routes, yet multiple output tracks may call addData on it ! @@ -666,7 +730,10 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* int i; - float* buffer[srcChannels]; + // p3.3.38 + //float* buffer[srcChannels]; + float* buffer[srcTotalOutChans]; + //float data[nframes * srcChannels]; //for (i = 0; i < srcChannels; ++i) // buffer[i] = data + i * nframes; @@ -677,7 +744,7 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* double _pan = pan(); vol[0] = _volume * (1.0 - _pan); vol[1] = _volume * (1.0 + _pan); - float meter[srcChannels]; + float meter[srcChans]; // Have we been here already during this process cycle? if(processed()) @@ -692,7 +759,10 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* if(_haveData) { // Point the input buffers at our local cached 'pre-volume' buffers. They need processing, so continue on after. - for(i = 0; i < srcChannels; ++i) + //for(i = 0; i < srcChannels; ++i) + // buffer[i] = outBuffers[i]; + // p3.3.38 + for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = outBuffers[i]; } else @@ -704,16 +774,23 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* // First time here during this process cycle. // Point the input buffers at a temporary stack buffer. - float data[nframes * srcChannels]; - for(i = 0; i < srcChannels; ++i) - buffer[i] = data + i * nframes; + //float data[nframes * srcChannels]; + //for(i = 0; i < srcChannels; ++i) + // buffer[i] = data + i * nframes; + // p3.3.38 + float data[nframes * srcTotalOutChans]; + for(i = 0; i < srcTotalOutChans; ++i) + buffer[i] = data + i * nframes; + // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. // For ex. if this is an audio input, Jack will set the pointers for us. - if(!getData(pos, srcChannels, nframes, buffer)) + //if(!getData(pos, srcChannels, nframes, buffer)) + // p3.3.38 + if(!getData(pos, srcTotalOutChans, nframes, buffer)) { // No data was available. Nothing to add, but zero our local buffers and the meters. - for(i = 0; i < srcChannels; ++i) + for(i = 0; i < srcChans; ++i) { // If we're using local buffers, we must zero them so that the next thing requiring them // during this process cycle will see zeros. @@ -743,7 +820,7 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* // apply plugin chain //--------------------------------------------------- - _efxPipe->apply(srcChannels, nframes, buffer); + _efxPipe->apply(srcChans, nframes, buffer); //--------------------------------------------------- // aux sends @@ -761,9 +838,9 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* AudioAux* a = (AudioAux*)((*al)[k]); float** dst = a->sendBuffer(); int auxChannels = a->channels(); - if((srcChannels ==1 && auxChannels==1) || srcChannels==2) + if((srcChans ==1 && auxChannels==1) || srcChans==2) { - for(int ch = 0; ch < srcChannels; ++ch) + for(int ch = 0; ch < srcChans; ++ch) { float* db = dst[ch % a->channels()]; float* sb = buffer[ch]; @@ -771,7 +848,7 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* *db++ += (*sb++ * m * vol[ch]); // add to mix } } - else if(srcChannels == 1 && auxChannels == 2) + else if(srcChans == 1 && auxChannels == 2) { for(int ch = 0; ch < auxChannels; ++ch) { @@ -790,7 +867,7 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* if(_prefader) { - for(i = 0; i < srcChannels; ++i) + for(i = 0; i < srcChans; ++i) { float* p = buffer[i]; meter[i] = 0.0; @@ -835,7 +912,10 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* // If we're using local cached 'pre-volume' buffers, copy the input buffers (as they are right now: post-effect pre-volume) back to them. if(!usedirectbuf) { - for(i = 0; i < srcChannels; ++i) + //for(i = 0; i < srcChannels; ++i) + // AL::dsp->cpy(outBuffers[i], buffer[i], nframes); + // p3.3.38 + for(i = 0; i < srcTotalOutChans; ++i) AL::dsp->cpy(outBuffers[i], buffer[i], nframes); } @@ -843,18 +923,42 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* _haveData = true; } + // Sanity check. Is source starting channel out of range? Just zero and return. + if(srcStartChan >= srcTotalOutChans) + { + unsigned int q; + for(i = 0; i < dstChannels; ++i) + { + if(config.useDenormalBias) + { + for(q = 0; q < nframes; q++) + dstBuffer[i][q] = denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } + _processed = true; + return; + } + // Force a source range to fit actual available total out channels. + if((srcStartChan + srcChans) > srcTotalOutChans) + srcChans = srcTotalOutChans - srcStartChan; + //--------------------------------------------------- // apply volume // postfader metering //--------------------------------------------------- - if(srcChannels == dstChannels) + if(srcChans == dstChannels) { if(_prefader) { for(int c = 0; c < dstChannels; ++c) { - float* sp = buffer[c]; + // p3.3.38 + //float* sp = buffer[c]; + float* sp = buffer[c + srcStartChan]; + float* dp = dstBuffer[c]; for(unsigned k = 0; k < nframes; ++k) *dp++ += (*sp++ * vol[c]); @@ -865,7 +969,10 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* for(int c = 0; c < dstChannels; ++c) { meter[c] = 0.0; - float* sp = buffer[c]; + // p3.3.38 + //float* sp = buffer[c]; + float* sp = buffer[c + srcStartChan]; + float* dp = dstBuffer[c]; for(unsigned k = 0; k < nframes; ++k) { @@ -882,21 +989,24 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* } } } - else if(srcChannels == 1 && dstChannels == 2) + else if(srcChans == 1 && dstChannels == 2) { + // p3.3.38 + float* sp = buffer[srcStartChan]; + if(_prefader) { for(int c = 0; c < dstChannels; ++c) { float* dp = dstBuffer[c]; - float* sp = buffer[0]; + //float* sp = buffer[0]; for(unsigned k = 0; k < nframes; ++k) *dp++ += (*sp++ * vol[c]); } } else { - float* sp = buffer[0]; + //float* sp = buffer[0]; meter[0] = 0.0; for(unsigned k = 0; k < nframes; ++k) { @@ -913,10 +1023,14 @@ void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float* _peak[0] = _meter[0]; } } - else if(srcChannels == 2 && dstChannels == 1) + else if(srcChans == 2 && dstChannels == 1) { - float* sp1 = buffer[0]; - float* sp2 = buffer[1]; + // p3.3.38 + //float* sp1 = buffer[0]; + //float* sp2 = buffer[1]; + float* sp1 = buffer[srcStartChan]; + float* sp2 = buffer[srcStartChan + 1]; + if(_prefader) { float* dp = dstBuffer[0]; @@ -1096,7 +1210,6 @@ bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** b RouteList* rl = inRoutes(); - // Added by Tim. p3.3.16 #ifdef NODE_DEBUG printf("AudioTrack::getData name:%s inRoutes:%d\n", name().latin1(), rl->size()); #endif @@ -1105,20 +1218,37 @@ bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** b if (ir == rl->end()) return false; - // Added by Tim. p3.3.16 + if(ir->track->isMidiTrack()) + return false; + #ifdef NODE_DEBUG printf(" calling copyData on %s...\n", ir->track->name().latin1()); #endif - ir->track->copyData(pos, channels, nframes, buffer); + // p3.3.38 + //((AudioTrack*)ir->track)->copyData(pos, channels, nframes, buffer); + ((AudioTrack*)ir->track)->copyData(pos, channels, + //(ir->track->type() == Track::AUDIO_SOFTSYNTH && ir->channel != -1) ? ir->channel : 0, + ir->channel, + ir->channels, + nframes, buffer); + ++ir; for (; ir != rl->end(); ++ir) { - // Added by Tim. p3.3.16 #ifdef NODE_DEBUG printf(" calling addData on %s...\n", ir->track->name().latin1()); #endif - ir->track->addData(pos, channels, nframes, buffer); + if(ir->track->isMidiTrack()) + continue; + + // p3.3.38 + //((AudioTrack*)ir->track)->addData(pos, channels, nframes, buffer); + ((AudioTrack*)ir->track)->addData(pos, channels, + //(ir->track->type() == Track::AUDIO_SOFTSYNTH && ir->channel != -1) ? ir->channel : 0, + ir->channel, + ir->channels, + nframes, buffer); } return true; } @@ -1368,7 +1498,7 @@ void AudioOutput::processInit(unsigned nframes) //--------------------------------------------------------- void AudioOutput::process(unsigned pos, unsigned offset, unsigned n) - { +{ //Added by Tim. p3.3.16 #ifdef NODE_DEBUG printf("MusE: AudioOutput::process name:%s processed:%d\n", name().latin1(), processed()); @@ -1377,8 +1507,11 @@ void AudioOutput::process(unsigned pos, unsigned offset, unsigned n) for (int i = 0; i < _channels; ++i) { buffer1[i] = buffer[i] + offset; } - copyData(pos, _channels, n, buffer1); - } + + // p3.3.38 + //copyData(pos, _channels, n, buffer1); + copyData(pos, _channels, -1, -1, n, buffer1); +} //--------------------------------------------------------- // silence @@ -1427,7 +1560,9 @@ void AudioOutput::processWrite() printf("MusE: AudioOutput::processWrite Calling metronome->addData frame:%u channels:%d frames:%lu\n", audio->pos().frame(), _channels, _nframes); #endif - metronome->addData(audio->pos().frame(), _channels, _nframes, buffer); + // p3.3.38 + //metronome->addData(audio->pos().frame(), _channels, _nframes, buffer); + metronome->addData(audio->pos().frame(), _channels, -1, -1, _nframes, buffer); } } //--------------------------------------------------------- @@ -1606,3 +1741,53 @@ void AudioTrack::setChannels(int n) _efxPipe->setChannels(n); } +//--------------------------------------------------------- +// setTotalOutChannels +//--------------------------------------------------------- + +void AudioTrack::setTotalOutChannels(int num) +{ + if(num == _totalOutChannels) + return; + + int chans = _totalOutChannels; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(chans < MAX_CHANNELS) + chans = MAX_CHANNELS; + for(int i = 0; i < chans; ++i) + { + if(outBuffers[i]) + free(outBuffers[i]); + } + delete[] outBuffers; + + _totalOutChannels = num; + chans = num; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(chans < MAX_CHANNELS) + chans = MAX_CHANNELS; + + outBuffers = new float*[chans]; + for (int i = 0; i < chans; ++i) + posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * segmentSize); + + chans = num; + // Limit the actual track (meters, copying etc, all 'normal' operation) to two-channel stereo. + if(chans > MAX_CHANNELS) + chans = MAX_CHANNELS; + + setChannels(chans); +} + +//--------------------------------------------------------- +// setTotalInChannels +//--------------------------------------------------------- + +void AudioTrack::setTotalInChannels(int num) +{ + if(num == _totalInChannels) + return; + + _totalInChannels = num; +} + diff --git a/muse/muse/route.cpp b/muse/muse/route.cpp index ace53252..0b45fdf2 100644 --- a/muse/muse/route.cpp +++ b/muse/muse/route.cpp @@ -16,6 +16,10 @@ #include "synth.h" #include "audiodev.h" #include "xml.h" +#include "driver/jackmidi.h" +#include "driver/alsamidi.h" + +//#define ROUTE_DEBUG //--------------------------------------------------------- // Route @@ -25,34 +29,79 @@ Route::Route(void* t, int ch) { jackPort = t; channel = ch; + channels = -1; + remoteChannel = -1; type = JACK_ROUTE; } -Route::Route(AudioTrack* t, int ch) +//Route::Route(AudioTrack* t, int ch) +Route::Route(Track* t, int ch, int chans) +//Route::Route(Track* t, int ch) { - channel = ch; - track = t; - type = TRACK_ROUTE; + track = t; + channel = ch; + channels = chans; + remoteChannel = -1; + type = TRACK_ROUTE; } -Route::Route(const QString& s, bool dst, int ch) +//Route::Route(MidiJackDevice* d) +Route::Route(MidiDevice* d, int ch) +{ + device = d; + channel = ch; + channels = -1; + remoteChannel = -1; + /* + //if(dynamic_cast<MidiJackDevice*>(d)) + if(d->deviceType() == MidiDevice::JACK_MIDI) + type = JACK_MIDI_ROUTE; + else + //if(dynamic_cast<MidiAlsaDevice*>(d)) + if(d->deviceType() == MidiDevice::ALSA_MIDI) + type = ALSA_MIDI_ROUTE; + */ + type = MIDI_DEVICE_ROUTE; +} + +//Route::Route(const QString& s, bool dst, int ch) +Route::Route(const QString& s, bool dst, int ch, int rtype) { - Route node(name2route(s, dst)); - channel = node.channel; - if (channel == -1) - channel = ch; + //Route node(name2route(s, dst)); + Route node(name2route(s, dst, rtype)); + channel = node.channel; + if(channel == -1) + channel = ch; + //if(channels == -1) + // channels = chans; + channels = node.channels; + remoteChannel = node.remoteChannel; type = node.type; - if (type == TRACK_ROUTE) - track = node.track; + if(type == TRACK_ROUTE) + track = node.track; + else + if(type == JACK_ROUTE) + jackPort = node.jackPort; + /* + else + if (type == JACK_MIDI_ROUTE) + device = node.device; else - jackPort = node.jackPort; + if (type == ALSA_MIDI_ROUTE) + device = node.device; + */ + else + if (type == MIDI_DEVICE_ROUTE) + device = node.device; } Route::Route() { - track = 0; - channel = -1; - type = TRACK_ROUTE; + track = 0; + channel = -1; + channels = -1; + remoteChannel = -1; + type = TRACK_ROUTE; } //--------------------------------------------------------- @@ -60,58 +109,306 @@ Route::Route() //--------------------------------------------------------- void addRoute(Route src, Route dst) - { +{ + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute:\n"); + #endif + if (!src.isValid() || !dst.isValid()) + { + if(!src.isValid()) + fprintf(stderr, "addRoute: invalid src\n"); + if(!dst.isValid()) + fprintf(stderr, "addRoute: invalid dst\n"); return; - + } + // printf("addRoute %d.%d:<%s> %d.%d:<%s>\n", // src.type, src.channel, src.name().latin1(), // dst.type, dst.channel, dst.name().latin1()); - if (src.type == JACK_ROUTE) { - if (dst.type != TRACK_ROUTE) { - fprintf(stderr, "addRoute: bad route 1\n"); + if (src.type == Route::JACK_ROUTE) + { + //if (dst.type != TRACK_ROUTE) + //{ + // fprintf(stderr, "addRoute: bad route 1\n"); // exit(-1); + // return; + //} + + if (dst.type == Route::TRACK_ROUTE) + { + if (dst.track->type() != Track::AUDIO_INPUT) + { + fprintf(stderr, "addRoute: source is jack, dest:%s is track but not audio input\n", dst.track->name().latin1()); + //exit(-1); return; - } - if (dst.track->type() != Track::AUDIO_INPUT) { - fprintf(stderr, "addRoute: bad route 2\n"); - exit(-1); - } - src.channel = dst.channel; - RouteList* inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { + } + //src.channel = src.dstChannel = dst.channel; + src.channel = dst.channel; + //src.channels = dst.channels = 1; + RouteList* inRoutes = dst.track->inRoutes(); + for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) + { + if (*i == src) // route already there + { + //#ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src track route already exists.\n"); + //#endif + return; + } + } + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src Jack dst track name: %s pushing source route\n", dst.track->name().latin1()); + #endif + inRoutes->push_back(src); + } + else + //if (dst.type == Route::JACK_MIDI_ROUTE) + if (dst.type == Route::MIDI_DEVICE_ROUTE) + { + if(dst.device->deviceType() == MidiDevice::JACK_MIDI) + { + src.channel = dst.channel; + //src.channel = -1; + //src.channel = 0; + //src.channel = src.dstChannel = dst.channel; + //src.channels = dst.channels = 1; + //dst.channel = -1; + + RouteList* routes = dst.device->inRoutes(); + for (iRoute i = routes->begin(); i != routes->end(); ++i) + { if (*i == src) // route already there - return; - } - inRoutes->push_back(src); + { + //#ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src Jack midi route already exists.\n"); + //#endif + return; + } + } + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src Jack dst Jack midi name: %s pushing source route\n", dst.device->name().latin1()); + #endif + routes->push_back(src); + } + else + { + fprintf(stderr, "addRoute: source is Jack, but destination is not jack midi - type:%d\n", dst.device->deviceType()); + // exit(-1); + return; + } + } + else + { + fprintf(stderr, "addRoute: source is Jack, but destination is not track or midi - type:%d \n", dst.type); + // exit(-1); + return; } - else if (dst.type == JACK_ROUTE) { - if (src.type != TRACK_ROUTE) { - fprintf(stderr, "addRoute: bad route 3\n"); - // exit(-1); - return; - } - if (src.track->type() != Track::AUDIO_OUTPUT) { - fprintf(stderr, "addRoute: bad route 4\n"); + } + else if (dst.type == Route::JACK_ROUTE) + { + //if (src.type != TRACK_ROUTE) + //{ + // fprintf(stderr, "addRoute: bad route 3\n"); // exit(-1); - return; - } - RouteList* outRoutes = src.track->outRoutes(); - dst.channel = src.channel; - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { + // return; + //} + + if (src.type == Route::TRACK_ROUTE) + { + if (src.track->type() != Track::AUDIO_OUTPUT) + { + fprintf(stderr, "addRoute: destination is jack, source is track but not audio output\n"); + // exit(-1); + return; + } + RouteList* outRoutes = src.track->outRoutes(); + //dst.channel = dst.dstChannel = src.channel; + dst.channel = src.channel; + //dst.channels = src.channels = 1; + + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { + if (*i == dst) // route already there + { + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: dst track route already exists.\n"); + #endif + return; + } + } + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: dst Jack src track name: %s pushing destination route\n", src.track->name().latin1()); + #endif + outRoutes->push_back(dst); + } + else + //if (src.type == Route::JACK_MIDI_ROUTE) + if (src.type == Route::MIDI_DEVICE_ROUTE) + { + if(src.device->deviceType() == MidiDevice::JACK_MIDI) + { + dst.channel = src.channel; + //dst.channel = -1; + //src.channel = -1; + //dst.channel = dst.dstChannel = src.channel; + //dst.channels = src.channels = 1; + + RouteList* routes = src.device->outRoutes(); + for (iRoute i = routes->begin(); i != routes->end(); ++i) + { if (*i == dst) // route already there - return; - } - outRoutes->push_back(dst); + { + //#ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: dst Jack midi route already exists.\n"); + //#endif + return; + } + } + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: dst Jack src Jack midi name: %s pushing destination route\n", src.device->name().latin1()); + #endif + routes->push_back(dst); + } + else + { + fprintf(stderr, "addRoute: destination is Jack, but source is not jack midi - type:%d\n", src.device->deviceType()); + // exit(-1); + return; + } } - else { - RouteList* outRoutes = src.track->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { + else + { + fprintf(stderr, "addRoute: destination is Jack, but source is not track or midi - type:%d \n", src.type); + // exit(-1); + return; + } + } + else + { + //if ((src.type == Route::JACK_MIDI_ROUTE) || (src.type == Route::ALSA_MIDI_ROUTE)) + if(src.type == Route::MIDI_DEVICE_ROUTE) + { + //src.channel = src.dstChannel = dst.dstChannel = dst.channel; + src.channel = dst.channel; + //src.channels = dst.channels = 1; + RouteList* outRoutes = src.device->outRoutes(); + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src name: %s looking for existing dest in out routes...\n", src.device->name().latin1()); + #endif + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { if (*i == dst) // route already there + { + //#ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src Jack or ALSA midi route already exists.\n"); + //#endif return; - } + } + } + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src midi dst name: %s pushing destination and source routes\n", dst.track->name().latin1()); + #endif + outRoutes->push_back(dst); RouteList* inRoutes = dst.track->inRoutes(); + inRoutes->push_back(src); + } + else + { + if(dst.type == Route::MIDI_DEVICE_ROUTE) + //{ + dst.channel = src.channel; + //src.channel = src.dstChannel = dst.dstChannel = dst.channel; + //src.channels = dst.channels = 1; + //} + //else + //{ + //src.channel = src.dstChannel = dst.dstChannel = dst.channel; + //src.channels = dst.channels = 1; + //} + + RouteList* outRoutes = src.track->outRoutes(); + + // + // Must enforce to ensure channel and channels are valid if defaults of -1 passed. + // + if(src.track->type() == Track::AUDIO_SOFTSYNTH) + { + if(src.channel == -1) + src.channel = 0; + if(src.channels == -1) + src.channels = src.track->channels(); + if(dst.type == Route::TRACK_ROUTE) + { + //if(dst.channel == -1) + // dst.channel = 0; + //if(dst.channels == -1) + // Yes, that's correct: dst channels = src track channels. + // dst.channels = src.track->channels(); + dst.channel = src.channel; + dst.channels = src.channels; + dst.remoteChannel = src.remoteChannel; + } + } + /* + if(dst.type == Route::TRACK_ROUTE && dst.track->type() == Track::AUDIO_SOFTSYNTH) + { + if(dst.channel == -1) + dst.channel = 0; + if(dst.channels == -1) + // Yes, that's correct: dst channels = src track channels. + dst.channels = src.track->channels(); + } + */ + + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { + if (*i == dst) // route already there + // TODO: + //if (i->type == dst.type && i->channel == dst.channel) + { + //if(i->type == Route::TRACK_ROUTE) + { + //if(i->track == dst.track) + { + //if(i->channels == dst.channels) + { + //#ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src track route already exists.\n"); + //#endif + return; + } + //else + //{ + + //} + } + } + } + } + outRoutes->push_back(dst); + RouteList* inRoutes; + //if ((dst.type == Route::JACK_MIDI_ROUTE) || (dst.type == Route::ALSA_MIDI_ROUTE)) + if(dst.type == Route::MIDI_DEVICE_ROUTE) + { + #ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src track dst midi name: %s pushing destination and source routes\n", dst.device->name().latin1()); + #endif + inRoutes = dst.device->inRoutes(); + } + else + { + #ifdef ROUTE_DEBUG + //fprintf(stderr, "addRoute: src track ch:%d chs:%d dst track ch:%d chs:%d name: %s pushing destination and source routes\n", src.channel, src.channels, dst.channel, dst.channels, dst.track->name().latin1()); + fprintf(stderr, "addRoute: src track ch:%d chs:%d remch:%d dst track ch:%d chs:%d remch:%d name: %s pushing dest and source routes\n", + src.channel, src.channels, src.remoteChannel, dst.channel, dst.channels, dst.remoteChannel, dst.track->name().latin1()); + //fprintf(stderr, "addRoute: src track ch:%d dst track ch:%d name: %s pushing destination and source routes\n", src.channel, dst.channel, dst.track->name().latin1()); + #endif + inRoutes = dst.track->inRoutes(); + } + + // // make sure AUDIO_AUX is processed last // @@ -119,76 +416,198 @@ void addRoute(Route src, Route dst) inRoutes->push_back(src); else inRoutes->insert(inRoutes->begin(), src); - } + } } +} //--------------------------------------------------------- // removeRoute //--------------------------------------------------------- void removeRoute(Route src, Route dst) - { +{ //printf("removeRoute %d.%d:<%s> %d.%d:<%s>\n", // src.type, src.channel, src.name().latin1(), // dst.type, dst.channel, dst.name().latin1()); - if (src.type == JACK_ROUTE) { - if (dst.type != TRACK_ROUTE) { - fprintf(stderr, "removeRoute: bad route 1\n"); + if (src.type == Route::JACK_ROUTE) + { + //if (dst.type != TRACK_ROUTE) + //{ + // fprintf(stderr, "removeRoute: bad route 1\n"); // exit(-1); - return; - } - if (dst.track->type() != Track::AUDIO_INPUT) { - fprintf(stderr, "removeRoute: bad route 2\n"); + // return; + //} + if(!dst.isValid()) + { + printf("removeRoute: source is jack, invalid destination\n"); + return; + } + + if (dst.type == Route::TRACK_ROUTE) + { + if (dst.track->type() != Track::AUDIO_INPUT) + { + fprintf(stderr, "removeRoute: source is jack, destination is track but not audio input\n"); + // exit(-1); + return; + } + RouteList* inRoutes = dst.track->inRoutes(); + iRoute i; + for (i = inRoutes->begin(); i != inRoutes->end(); ++i) + { + if (*i == src) + { + inRoutes->erase(i); + break; + } + } + } + else + //if (dst.type == Route::JACK_MIDI_ROUTE) + if (dst.type == Route::MIDI_DEVICE_ROUTE) + { + RouteList* routes = dst.device->inRoutes(); + iRoute i; + for (i = routes->begin(); i != routes->end(); ++i) + { + if (*i == src) + { + routes->erase(i); + break; + } + } + } + else + { + fprintf(stderr, "removeRoute: source is jack, destination unknown\n"); // exit(-1); return; - } - RouteList* inRoutes = dst.track->inRoutes(); - iRoute i; - for (i = inRoutes->begin(); i != inRoutes->end(); ++i) { - if (*i == src) { - inRoutes->erase(i); - break; - } - } } - else if (dst.type == JACK_ROUTE) { - if (src.type != TRACK_ROUTE) { - fprintf(stderr, "removeRoute: bad route 3\n"); + } + else if (dst.type == Route::JACK_ROUTE) + { + //if (src.type != TRACK_ROUTE) + //{ + // fprintf(stderr, "removeRoute: bad route 3\n"); // exit(-1); - return; - } - if (src.track->type() != Track::AUDIO_OUTPUT) { - fprintf(stderr, "removeRoute: bad route 4\n"); + // return; + //} + if(!src.isValid()) + { + printf("removeRoute: destination is jack, invalid source\n"); + return; + } + + if (src.type == Route::TRACK_ROUTE) + { + if (src.track->type() != Track::AUDIO_OUTPUT) + { + fprintf(stderr, "removeRoute: destination is jack, source is track but not audio output\n"); + // exit(-1); + return; + } + RouteList* outRoutes = src.track->outRoutes(); + iRoute i; + for (i = outRoutes->begin(); i != outRoutes->end(); ++i) + { + if (*i == dst) { + outRoutes->erase(i); + break; + } + } + } + else + //if (src.type == Route::JACK_MIDI_ROUTE) + if (src.type == Route::MIDI_DEVICE_ROUTE) + { + RouteList* routes = src.device->outRoutes(); + iRoute i; + for (i = routes->begin(); i != routes->end(); ++i) + { + if (*i == dst) { + routes->erase(i); + break; + } + } + } + else + { + fprintf(stderr, "removeRoute: destination is jack, source unknown\n"); // exit(-1); return; - } - RouteList* outRoutes = src.track->outRoutes(); - iRoute i; - for (i = outRoutes->begin(); i != outRoutes->end(); ++i) { - if (*i == dst) { - outRoutes->erase(i); - break; - } - } } - else { - RouteList* outRoutes = src.track->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { - if (*i == dst) { - outRoutes->erase(i); - break; - } - } - RouteList* inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { - if (*i == src) { - inRoutes->erase(i); - break; - } - } + } + else + { + //if((src.type == Route::JACK_MIDI_ROUTE) || (src.type == Route::ALSA_MIDI_ROUTE)) + if(src.type == Route::MIDI_DEVICE_ROUTE) + { + if(src.isValid()) + { + RouteList* outRoutes = src.device->outRoutes(); + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { + if (*i == dst) { + outRoutes->erase(i); + break; + } + } + } + else + printf("removeRoute: source is midi but invalid\n"); + + if(dst.isValid()) + { + RouteList* inRoutes = dst.track->inRoutes(); + for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) + { + if (*i == src) { + inRoutes->erase(i); + break; + } + } + } + else + printf("removeRoute: source is midi but destination invalid\n"); } + else + { + if(src.isValid()) + { + RouteList* outRoutes = src.track->outRoutes(); + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { + if (*i == dst) { + outRoutes->erase(i); + break; + } + } + } + else + printf("removeRoute: source is track but invalid\n"); + + if(dst.isValid()) + { + RouteList* inRoutes; + //if ((dst.type == Route::JACK_MIDI_ROUTE) || (dst.type == Route::ALSA_MIDI_ROUTE)) + if (dst.type == Route::MIDI_DEVICE_ROUTE) + inRoutes = dst.device->inRoutes(); + else + inRoutes = dst.track->inRoutes(); + for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) + { + if (*i == src) { + inRoutes->erase(i); + break; + } + } + } + else + printf("removeRoute: source is track but destination invalid\n"); + } } +} //--------------------------------------------------------- // track2name @@ -208,7 +627,9 @@ static QString track2name(const Track* n) //--------------------------------------------------------- QString Route::name() const - { +{ + // p3.3.38 Removed + /* QString s; if ((type == TRACK_ROUTE) && (channel != -1)) { // if (channel != -1) { @@ -216,58 +637,333 @@ QString Route::name() const c.setNum(channel+1); s = c + ":"; } - if (type == JACK_ROUTE) { - if (!checkAudioDevice()) return ""; - return s + audioDevice->portName(jackPort); + */ + + /* + if (type == ALSA_MIDI_ROUTE) + { + if(device) + // TODO + //snd_seq_addr_t + return device->name(); + else + return QWidget::tr("None"); + } + else + if (type == JACK_MIDI_ROUTE) + { + if(device) + { + return audioDevice->portName(device->clientPort()); + //MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(device); + //if(jmd) + // return audioDevice->portName(jmd->clientJackPort()); + //else + //{ + // fprintf(stderr, "Route::name Route is Jack midi but device is not a MidiJackDevice\n"); + // return QWidget::tr("None"); + //} } + else + return QWidget::tr("None"); + } + */ + + if(type == MIDI_DEVICE_ROUTE) + { + if(device) + { + if(device->deviceType() == MidiDevice::JACK_MIDI) + return audioDevice->portName(device->clientPort()); + else + //if(device->deviceType() == MidiDevice::ALSA_MIDI) + return device->name(); + } + return QWidget::tr("None"); + } else - return s + track2name(track); + if(type == JACK_ROUTE) + { + if (!checkAudioDevice()) return ""; + //return s + audioDevice->portName(jackPort); + return audioDevice->portName(jackPort); } + else + //return s + track2name(track); + return track2name(track); +} //--------------------------------------------------------- // name2route //--------------------------------------------------------- -Route name2route(const QString& rn, bool dst) - { +//Route name2route(const QString& rn, bool dst) +Route name2route(const QString& rn, bool /*dst*/, int rtype) +{ // printf("name2route %s\n", rn.latin1()); - int channel = -1; - QString s(rn); - if (rn[0].isNumber() && rn[1]==':') { - channel = rn[0] - '1'; - s = rn.mid(2); - } - if (dst) { - TrackList* tl = song->tracks(); - for (iTrack i = tl->begin(); i != tl->end(); ++i) { - if ((*i)->isMidiTrack()) - continue; - AudioTrack* track = (AudioTrack*)*i; - if (track->name() == s) - return Route(track, channel); - } - if (!checkAudioDevice()) return Route((AudioTrack*)NULL,0); - void* p = audioDevice->findPort(s.latin1()); - if (p) - return Route(p, channel); - } - else { - if (!checkAudioDevice()) return Route((AudioTrack*)NULL,0); - void* p = audioDevice->findPort(s.latin1()); - if (p) - return Route(p, channel); - TrackList* tl = song->tracks(); - for (iTrack i = tl->begin(); i != tl->end(); ++i) { - if ((*i)->isMidiTrack()) - continue; - AudioTrack* track = (AudioTrack*)*i; - if (track->name() == s) - return Route(track, channel); - } - } - printf(" name2route: <%s> not found\n", rn.latin1()); - return Route((Track*) 0, channel); + int channel = -1; + //int channel = 0; + QString s(rn); + // Support old route style in med files. Obsolete. + if (rn[0].isNumber() && rn[1]==':') + { + channel = rn[0] - '1'; + s = rn.mid(2); + } + + if(rtype == -1) + { + //if(dst) + //{ + if(checkAudioDevice()) + { + void* p = audioDevice->findPort(s.latin1()); + if(p) + return Route(p, channel); + } + + TrackList* tl = song->tracks(); + for(iTrack i = tl->begin(); i != tl->end(); ++i) + { + if((*i)->isMidiTrack()) + { + MidiTrack* track = (MidiTrack*)*i; + if(track->name() == s) + return Route(track, channel); + //return Route(track, channel, 1); + } + else + { + AudioTrack* track = (AudioTrack*)*i; + if(track->name() == s) + return Route(track, channel); + //return Route(track, channel, 1); + //return Route(track, channel, track->channels()); + } } + + for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) + { + if((*i)->name() == s) + //if (jmd->name() == rn) + return Route(*i, channel); + + /* + MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(*i); + if(jmd) + { + if(jmd->name() == s) + //if (jmd->name() == rn) + return Route(jmd); + } + MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(*i); + if(amd) + { + // TODO + if(amd->name() == s) + //if (amd->name() == rn) + return Route(amd); + } + */ + } + + +/* + } + else + { + if(checkAudioDevice()) + { + void* p = audioDevice->findPort(s.latin1()); + if(p) + return Route(p, channel); + } + + TrackList* tl = song->tracks(); + for(iTrack i = tl->begin(); i != tl->end(); ++i) + { + if((*i)->isMidiTrack()) + { + MidiTrack* track = (MidiTrack*)*i; + if(track->name() == s) + return Route(track, channel); + } + else + { + AudioTrack* track = (AudioTrack*)*i; + if(track->name() == s) + return Route(track, channel); + } + } + + for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) + { + if((*i)->name() == s) + //if (jmd->name() == rn) + return Route(*i, channel); +*/ + + /* + MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(*i); + if(jmd) + { + if(jmd->name() == s) + //if (jmd->name() == rn) + return Route(jmd); + } + MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(*i); + if(amd) + { + // TODO + if(amd->name() == s) + //if (amd->name() == rn) + return Route(amd); + } + */ +// } +// } + + } + else + { + //if(dst) + //{ + if(rtype == Route::TRACK_ROUTE) + { + TrackList* tl = song->tracks(); + for(iTrack i = tl->begin(); i != tl->end(); ++i) + { + if((*i)->isMidiTrack()) + { + MidiTrack* track = (MidiTrack*)*i; + if(track->name() == s) + return Route(track, channel); + } + else + { + AudioTrack* track = (AudioTrack*)*i; + if(track->name() == s) + return Route(track, channel); + //return Route(track, channel, 1); + //return Route(track, channel, track->channels()); + } + } + } + else + //if((rtype == Route::JACK_MIDI_ROUTE) || (rtype == Route::ALSA_MIDI_ROUTE)) + // TODO Distinguish the device types + if(rtype == Route::MIDI_DEVICE_ROUTE) + { + for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) + { + if((*i)->name() == s) + //if (jmd->name() == rn) + return Route(*i, channel); + + /* + MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(*i); + if(jmd) + { + if(jmd->name() == s) + //if (jmd->name() == rn) + return Route(jmd); + } + MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(*i); + if(amd) + { + // TODO + if(amd->name() == s) + //if (amd->name() == rn) + return Route(amd); + } + */ + } + } + else + if(rtype == Route::JACK_ROUTE) + { + if(checkAudioDevice()) + { + void* p = audioDevice->findPort(s.latin1()); + if(p) + return Route(p, channel); + } + } + + +/* + } + else + { + if(rtype == Route::TRACK_ROUTE) + { + TrackList* tl = song->tracks(); + for(iTrack i = tl->begin(); i != tl->end(); ++i) + { + if((*i)->isMidiTrack()) + { + MidiTrack* track = (MidiTrack*)*i; + if (track->name() == s) + return Route(track, channel); + } + else + { + AudioTrack* track = (AudioTrack*)*i; + if(track->name() == s) + return Route(track, channel); + } + } + } + else + if((rtype == Route::JACK_MIDI_ROUTE) || (rtype == Route::ALSA_MIDI_ROUTE)) + { + for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) + { + if((*i)->name() == s) + //if (jmd->name() == rn) + return Route(*i, channel); +*/ + + /* + MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(*i); + if(jmd) + { + if(jmd->name() == s) + //if (jmd->name() == rn) + return Route(jmd); + } + MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(*i); + if(amd) + { + // TODO + if(amd->name() == s) + //if (amd->name() == rn) + return Route(amd); + } + */ + +/* + } + } + else + if(rtype == JACK_ROUTE) + { + if(checkAudioDevice()) + { + void* p = audioDevice->findPort(s.latin1()); + if(p) + return Route(p, channel); + } + } + } +*/ + + } + + printf(" name2route: <%s> not found\n", rn.latin1()); + return Route((Track*) 0, channel); + //return Route((Track*) 0, channel, 1); +} //--------------------------------------------------------- // checkRoute @@ -281,44 +977,95 @@ bool checkRoute(const QString& s, const QString& d) if (!(src.isValid() && dst.isValid()) || (src == dst)) return false; - if (src.type == JACK_ROUTE) { - if (dst.type != TRACK_ROUTE) { - return false; - } - if (dst.track->type() != Track::AUDIO_INPUT) { - return false; - } - src.channel = dst.channel; - RouteList* inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { - if (*i == src) { // route already there - return false; - } - } + if (src.type == Route::JACK_ROUTE) + { + //if (dst.type != TRACK_ROUTE) { + // return false; + // } + + if (dst.type == Route::TRACK_ROUTE) + { + if (dst.track->type() != Track::AUDIO_INPUT) { + return false; + } + src.channel = dst.channel; + RouteList* inRoutes = dst.track->inRoutes(); + for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) + { + if (*i == src) { // route already there + return false; + } + } } - else if (dst.type == JACK_ROUTE) { - if (src.type != TRACK_ROUTE) { - return false; - } - if (src.track->type() != Track::AUDIO_OUTPUT) { - return false; - } - RouteList* outRoutes = src.track->outRoutes(); - dst.channel = src.channel; - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { - if (*i == dst) { // route already there - return false; - } - } + else + //if (dst.type == Route::JACK_MIDI_ROUTE) + if (dst.type == Route::MIDI_DEVICE_ROUTE) + { + //src.channel = dst.channel; + src.channel = -1; + //dst.channel = -1; + RouteList* routes = dst.device->inRoutes(); + for (iRoute i = routes->begin(); i != routes->end(); ++i) + { + if (*i == src) { // route already there + return false; + } + } } - else { - RouteList* outRoutes = src.track->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { + else + return false; + } + else if (dst.type == Route::JACK_ROUTE) + { + //if (src.type != TRACK_ROUTE) { + // return false; + // } + + if (src.type == Route::TRACK_ROUTE) + { + if (src.track->type() != Track::AUDIO_OUTPUT) { + return false; + } + RouteList* outRoutes = src.track->outRoutes(); + dst.channel = src.channel; + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { + if (*i == dst) { // route already there + return false; + } + } + } + else + //if (src.type == Route::JACK_MIDI_ROUTE) + if (src.type == Route::MIDI_DEVICE_ROUTE) + { + RouteList* routes = src.device->outRoutes(); + //dst.channel = src.channel; + dst.channel = -1; + //src.channel = -1; + for (iRoute i = routes->begin(); i != routes->end(); ++i) + { + if (*i == dst) { // route already there + return false; + } + } + } + else + return false; + } + else + { + //RouteList* outRoutes = ((src.type == Route::JACK_MIDI_ROUTE) || (src.type == Route::ALSA_MIDI_ROUTE)) ? + // src.device->outRoutes() : src.track->outRoutes(); + RouteList* outRoutes = (src.type == Route::MIDI_DEVICE_ROUTE) ? src.device->outRoutes() : src.track->outRoutes(); + + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { if (*i == dst) { // route already there return false; } - } } + } return true; } @@ -326,47 +1073,201 @@ bool checkRoute(const QString& s, const QString& d) // read //--------------------------------------------------------- -void Song::readRoute(Xml& xml) +void Route::read(Xml& xml) +{ + QString s; + int dtype = MidiDevice::ALSA_MIDI; + + //channel = -1; + //channels = -1; + //remoteChannel = -1; + type = Route::TRACK_ROUTE; + track = 0; + + for (;;) { + const QString& tag = xml.s1(); + Xml::Token token = xml.parse(); + switch (token) + { + case Xml::Error: + case Xml::End: + return; + //case Xml::TagStart: + // xml.unknown("Route"); + // break; + case Xml::Attribut: + #ifdef ROUTE_DEBUG + printf("Route::read(): attribute:%s\n", tag.latin1()); + #endif + if(tag == "type") + type = xml.s2().toInt(); + else + if(tag == "devtype") + { + dtype = xml.s2().toInt(); + type = Route::MIDI_DEVICE_ROUTE; + } + //else + //if(tag == "channel") + // channel = xml.s2().toInt(); + //else + //if(tag == "channels") + // channels = xml.s2().toInt(); + //else + //if(tag == "remch") + // remoteChannel = xml.s2().toInt(); + else + if(tag == "name") + s = xml.s2(); + else + printf("Route::read(): unknown attribute:%s\n", tag.latin1()); + break; + case Xml::TagEnd: + #ifdef ROUTE_DEBUG + printf("Route::read(): tag end type:%d channel:%d name:%s\n", type, channel, s.latin1()); + #endif + if(!s.isEmpty()) + { + if(type == TRACK_ROUTE) + { + track = 0; + TrackList* tl = song->tracks(); + for (iTrack i = tl->begin(); i != tl->end(); ++i) + { + Track* t = *i; + if (t->name() == s) + { + track = t; + break; + } + } + if(track == 0) + printf("Route::read(): track <%s> not found\n", s.latin1()); + } + else + if(type == JACK_ROUTE) + { + jackPort = audioDevice->findPort(s); + if(jackPort == 0) + printf("Route::read(): jack port <%s> not found\n", s.latin1()); + } + else + //if((type == JACK_MIDI_ROUTE) || (type == ALSA_MIDI_ROUTE)) + if(type == MIDI_DEVICE_ROUTE) + { + device = 0; + for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) + { + MidiDevice* md = *imd; + //if(md->name() == s) + if(md->name() == s && md->deviceType() == dtype) + { + device = md; + break; + } + } + if(device == 0) + printf("Route::read(): midi device <%s> not found\n", s.latin1()); + } + } + return; + default: + break; + } + } +} + + +//--------------------------------------------------------- +// read +//--------------------------------------------------------- + +void Song::readRoute(Xml& xml) +{ QString src; QString dst; + int ch = -1; + int chs = -1; + int remch = -1; - for (;;) { + Route sroute, droute; + + for (;;) + { const QString& tag = xml.s1(); Xml::Token token = xml.parse(); - switch (token) { + switch (token) + { case Xml::Error: case Xml::End: return; case Xml::TagStart: + // p3.3.38 2010/02/03 Support old routes in med files. Now obsolete! if (tag == "srcNode") src = xml.parse1(); else if (tag == "dstNode") dst = xml.parse1(); + // Support new routes. + else if (tag == "source") + { + sroute.read(xml); + sroute.channel = ch; + sroute.channels = chs; + sroute.remoteChannel = remch; + } + else if (tag == "dest") + { + droute.read(xml); + droute.channel = ch; + droute.channels = chs; + droute.remoteChannel = remch; + } else xml.unknown("readRoute"); break; + case Xml::Attribut: + #ifdef ROUTE_DEBUG + printf("Song::readRoute(): attribute:%s\n", tag.latin1()); + #endif + if(tag == "channel") + ch = xml.s2().toInt(); + else + if(tag == "channels") + chs = xml.s2().toInt(); + else + if(tag == "remch") + remch = xml.s2().toInt(); + else + printf("Song::readRoute(): unknown attribute:%s\n", tag.latin1()); + break; case Xml::TagEnd: - if (xml.s1() == "Route") { - - if(!src.isEmpty() && !dst.isEmpty()) - { - - Route s = name2route(src, false); - Route d = name2route(dst, true); - addRoute(s, d); - - } - else - printf(" Warning - route name missing. Ignoring route!\n"); - - return; - } + if (xml.s1() == "Route") + { + // Support old routes in med files. Now obsolete! + if(!src.isEmpty() && !dst.isEmpty()) + { + Route s = name2route(src, false); + Route d = name2route(dst, true); + addRoute(s, d); + } + else + // Support new routes. + if(sroute.isValid() && droute.isValid()) + { + //printf("adding new route...\n"); + addRoute(sroute, droute); + } + else + printf(" Warning - route invalid. Ignoring route!\n"); + + return; + } default: break; - } - } + } } +} //--------------------------------------------------------- // removeRoute @@ -388,30 +1289,156 @@ void RouteList::removeRoute(const Route& r) //--------------------------------------------------------- void Route::dump() const +{ + if (type == TRACK_ROUTE) + { + if(track) + printf("Route dump: track <%s> channel %d channels %d\n", track->name().latin1(), channel, channels); + //printf("Route dump: track <%s> channel %d\n", track->name().latin1(), channel); + //else + // printf("Route dump: invalid track, channel %d\n", channel); + } + else + if (type == JACK_ROUTE) + { + if(checkAudioDevice()) + printf("Route dump: jack audio port <%s> channel %d\n", audioDevice->portName(jackPort).latin1(), channel); + } + else + if (type == MIDI_DEVICE_ROUTE) + { + printf("Route dump: "); + if(device) + { + if(device->deviceType() == MidiDevice::JACK_MIDI) + { + if(checkAudioDevice()) + printf("jack midi port device <%s> ", audioDevice->portName(device->clientPort()).latin1()); + } + else + if(device->deviceType() == MidiDevice::ALSA_MIDI) + printf("alsa midi device <%s> ", device->name().latin1()); + else + if(device->deviceType() == MidiDevice::SYNTH_MIDI) + printf("synth midi device <%s> ", device->name().latin1()); + else + printf("is midi but unknown device type:%d, ", device->deviceType()); + } + else + printf("is midi but invalid device, "); + + printf("channel:%d\n", channel); + } + /* + else + if (type == JACK_MIDI_ROUTE) { - if (type == 0) - printf("Route dump: track <%s> channel %d\n", track->name().latin1(), channel); - else { if (!checkAudioDevice()) return; - printf("Route dump: jPort <%s> channel %d\n", - audioDevice->portName(jackPort).latin1(), channel); - } + printf("Route dump: jMidiPort <%s>\n", + audioDevice->portName(device->clientPort()).latin1()); } + else + if (type == ALSA_MIDI_ROUTE) + { + // TODO + //if (!checkAudioDevice()) return; + //printf("Route dump: aMidiPort <%s>\n", + // audioDevice->portName(device->clientJackPort()).latin1()); + printf("Route dump: aMidiPort\n"); + } + */ + else + printf("Route dump: unknown route type:%d\n", type); +} //--------------------------------------------------------- // operator== //--------------------------------------------------------- bool Route::operator==(const Route& a) const +{ + if ((type == a.type) && (channel == a.channel)) + //if (type == a.type) { - if ((type == a.type) && (channel == a.channel)) { - if (type == 0) - return track == a.track; - else { - if (!checkAudioDevice()) return false; - return audioDevice->portName(jackPort) == audioDevice->portName(a.jackPort); - } + if (type == TRACK_ROUTE) + { + //return track == a.track; + //return track == a.track && remoteChannel == a.remoteChannel; + return track == a.track && channels == a.channels && remoteChannel == a.remoteChannel; + + /* + if(a.track == track && a.remoteChannel == remoteChannel) + { + int tcompch = rt.channel; + if(tcompch == -1) + tcompch = 0; + int tcompchs = rt.channels; + if(tcompchs == -1) + tcompchs = isOutput ? t->channels() : track->channels(); + + int compch = ir->channel; + if(compch == -1) + compch = 0; + int compchs = ir->channels; + if(compchs == -1) + compchs = isOutput ? t->channels() : ir->track->channels(); + + if(compch == tcompch && compchs == tcompchs) + { + chpup->setItemChecked(id, true); + break; + } + } + */ + } - return false; + else + if(channel == a.channel) + { + if (type == JACK_ROUTE) + { + if (!checkAudioDevice()) return false; + return audioDevice->portName(jackPort) == audioDevice->portName(a.jackPort); + } + else + if (type == MIDI_DEVICE_ROUTE) + { + //if(device) + if(device && a.device && device->deviceType() == a.device->deviceType()) + { + if(device->deviceType() == MidiDevice::JACK_MIDI) + { + if (!checkAudioDevice()) return false; + return audioDevice->portName(device->clientPort()) == audioDevice->portName(a.device->clientPort()); + } + else + if(device->deviceType() == MidiDevice::ALSA_MIDI) + // TODO: OK ?? + return device->clientPort() == a.device->clientPort() && (channel == a.channel); + else + if(device->deviceType() == MidiDevice::SYNTH_MIDI) + return device->name() == a.device->name(); + } + } + + /* + if (type == JACK_MIDI_ROUTE) + { + if (!checkAudioDevice()) return false; + return audioDevice->portName(device->clientPort()) == audioDevice->portName(a.device->clientPort()); + } + else + if (type == ALSA_MIDI_ROUTE) + { + // TODO + //if (!checkAudioDevice()) return false; + //return audioDevice->portName(device->clientJackPort()) == audioDevice->portName(a.device->clientJackPort()); + //return device->name() == a.device->name(); + return device->clientPort() == a.device->clientPort() && (channel == a.channel); + } + */ + } } + return false; +} diff --git a/muse/muse/route.h b/muse/muse/route.h index ba40bb06..d8217207 100644 --- a/muse/muse/route.h +++ b/muse/muse/route.h @@ -9,34 +9,69 @@ #ifndef __ROUTE_H__ #define __ROUTE_H__ +//#include <alsa/asoundlib.h> #include <vector> +#include <map> class QString; -class AudioTrack; +//class AudioTrack; +class Track; +//class MidiJackDevice; +class MidiDevice; +class Xml; //--------------------------------------------------------- // Route //--------------------------------------------------------- -enum { TRACK_ROUTE=0, JACK_ROUTE=1 }; +//enum { TRACK_ROUTE=0, JACK_ROUTE=1 }; struct Route { + //enum { TRACK_ROUTE=0, JACK_ROUTE=1, JACK_MIDI_ROUTE=2, ALSA_MIDI_ROUTE=3 }; + enum { TRACK_ROUTE=0, JACK_ROUTE=1, MIDI_DEVICE_ROUTE=2 }; + union { - AudioTrack* track; + //AudioTrack* track; + Track* track; + //MidiJackDevice* device; + MidiDevice* device; void* jackPort; }; + + //snd_seq_addr_t alsaAdr; + + // Starting source channel (of the owner of this route). Normally zero for mono or stereo tracks, higher for multi-channel tracks. int channel; - unsigned char type; // 0 - track, 1 - jackPort + // Number of channels being routed. + int channels; + + // Allow for multi-channel syntis to feed to/from regular tracks, and to feed one to another. + // If a synti is feeding to/from a regular track, remoteChannel is the 'starting' channel of this multi-channel synti. + // If a synti is feeding to/from another synti, this is not used and individual channels are routed using channel instead. + int remoteChannel; + + unsigned char type; // 0 - track, 1 - jackPort, 2 - jack midi device, 3 - alsa midi device Route(void* t, int ch=-1); - Route(AudioTrack* t, int ch); - Route(const QString&, bool dst, int ch); + //Route(AudioTrack* t, int ch); + //Route(Track* t, int ch); + Route(Track* t, int ch = -1, int chans = -1); + //Route(Track* t, int ch = -1); + + //Route(MidiJackDevice* d); + Route(MidiDevice* d, int ch); + //Route(const QString&, bool dst, int ch); + Route(const QString&, bool dst, int ch, int rtype = -1); Route(); QString name() const; bool operator==(const Route& a) const; bool isValid() const { - return ((type == 0) && (track != 0)) || ((type == 1) && (jackPort != 0)); + //return ((type == 0) && (track != 0)) || ((type == 1) && (jackPort != 0)); + return ((type == TRACK_ROUTE) && (track != 0)) || ((type == JACK_ROUTE) && (jackPort != 0)) || + //(((type == JACK_MIDI_ROUTE) || (type == ALSA_MIDI_ROUTE)) && (device != 0)); + ((type == MIDI_DEVICE_ROUTE) && (device != 0)); } + void read(Xml& xml); void dump() const; }; @@ -54,8 +89,25 @@ typedef RouteList::const_iterator ciRoute; extern void addRoute(Route, Route); extern void removeRoute(Route, Route); -extern Route name2route(const QString&, bool dst); +//extern Route name2route(const QString&, bool dst); +extern Route name2route(const QString&, bool dst, int rtype = -1); extern bool checkRoute(const QString&, const QString&); +//--------------------------------------------------------- +// RouteMenuMap +//--------------------------------------------------------- + +//struct TRouteMenuMap{ +// Route r; +// }; +//typedef std::map<int, TRouteMenuMap, std::less<int> >::iterator iRouteMenuMap; +//typedef std::map<int, TRouteMenuMap, std::less<int> >::const_iterator ciRouteMenuMap; +//typedef std::map<int, TRouteMenuMap, std::less<int> > RouteMenuMap; +typedef std::map<int, Route, std::less<int> >::iterator iRouteMenuMap; +typedef std::map<int, Route, std::less<int> >::const_iterator ciRouteMenuMap; +typedef std::map<int, Route, std::less<int> > RouteMenuMap; +typedef std::pair<int, Route> pRouteMenuMap; +typedef std::pair<iRouteMenuMap, bool > rpRouteMenuMap; + #endif diff --git a/muse/muse/seqmsg.cpp b/muse/muse/seqmsg.cpp index 42227305..02fbdf01 100644 --- a/muse/muse/seqmsg.cpp +++ b/muse/muse/seqmsg.cpp @@ -21,6 +21,7 @@ #include "alsamidi.h" #include "audio.h" #include "arranger.h" +#include "driver/jackmidi.h" //--------------------------------------------------------- // sendMsg @@ -72,14 +73,58 @@ bool Audio::sendMessage(AudioMsg* m, bool doUndo) //--------------------------------------------------------- void Audio::msgRemoveRoute(Route src, Route dst) - { +{ msgRemoveRoute1(src, dst); - if (!checkAudioDevice()) return; - if (src.type == JACK_ROUTE) + //if (!checkAudioDevice()) return; + if (src.type == Route::JACK_ROUTE) + { + if (!checkAudioDevice()) return; + + //if(dst.type == Route::JACK_MIDI_ROUTE) + if(dst.type == Route::MIDI_DEVICE_ROUTE) + { + //MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(dst.device); + //if(jmd) + if(dst.device) + { + if(dst.device->deviceType() == MidiDevice::JACK_MIDI) + audioDevice->disconnect(src.jackPort, dst.device->clientPort()); + //else + //{ + // TODO... + //MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(dst.device); + //if(amd) + //} + } + } + else audioDevice->disconnect(src.jackPort, ((AudioInput*)dst.track)->jackPort(dst.channel)); - else if (dst.type == JACK_ROUTE) + } + else if (dst.type == Route::JACK_ROUTE) + { + if (!checkAudioDevice()) return; + + //if(src.type == Route::JACK_MIDI_ROUTE) + if(src.type == Route::MIDI_DEVICE_ROUTE) + { + //MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(src.device); + //if(jmd) + if(src.device) + { + if(src.device->deviceType() == MidiDevice::JACK_MIDI) + audioDevice->disconnect(src.device->clientPort(), dst.jackPort); + //else + //{ + // TODO... + //MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(src.device); + //if(amd) + //} + } + } + else audioDevice->disconnect(((AudioOutput*)src.track)->jackPort(src.channel), dst.jackPort); } +} //--------------------------------------------------------- // msgRemoveRoute1 @@ -100,16 +145,58 @@ void Audio::msgRemoveRoute1(Route src, Route dst) void Audio::msgAddRoute(Route src, Route dst) { - if (src.type == JACK_ROUTE) { + if (src.type == Route::JACK_ROUTE) + { if (!checkAudioDevice()) return; if (isRunning()) + { + //if(dst.type == Route::JACK_MIDI_ROUTE) + if(dst.type == Route::MIDI_DEVICE_ROUTE) + { + //MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(dst.device); + //if(jmd) + if(dst.device) + { + if(dst.device->deviceType() == MidiDevice::JACK_MIDI) + audioDevice->connect(src.jackPort, dst.device->clientPort()); + //else + //{ + // TODO... + //MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(dst.device); + //if(amd) + //} + } + } + else audioDevice->connect(src.jackPort, ((AudioInput*)dst.track)->jackPort(dst.channel)); - } - else if (dst.type == JACK_ROUTE) { + } + } + else if (dst.type == Route::JACK_ROUTE) + { if (!checkAudioDevice()) return; if (audio->isRunning()) + { + //if(src.type == Route::JACK_MIDI_ROUTE) + if(src.type == Route::MIDI_DEVICE_ROUTE) + { + //MidiJackDevice* jmd = dynamic_cast<MidiJackDevice*>(src.device); + //if(jmd) + if(src.device) + { + if(src.device->deviceType() == MidiDevice::JACK_MIDI) + audioDevice->connect(src.device->clientPort(), dst.jackPort); + //else + //{ + // TODO... + //MidiAlsaDevice* amd = dynamic_cast<MidiAlsaDevice*>(src.device); + //if(amd) + //} + } + } + else audioDevice->connect(((AudioOutput*)src.track)->jackPort(dst.channel), dst.jackPort); - } + } + } msgAddRoute1(src, dst); } @@ -203,57 +290,117 @@ void Audio::msgSetChannels(AudioTrack* node, int n) QString name = node->name(); int mc = std::max(n, node->channels()); - if (!name.isEmpty()) { - if (node->type() == Track::AUDIO_INPUT) { - if (!checkAudioDevice()) return; - AudioInput* ai = (AudioInput*)node; - for (int i = 0; i < mc; ++i) { - if (i < n && ai->jackPort(i) == 0) { - char buffer[128]; - snprintf(buffer, 128, "%s-%d", name.latin1(), i); - //ai->setJackPort(i, audioDevice->registerInPort(buffer)); - ai->setJackPort(i, audioDevice->registerInPort(buffer, false)); - } - else if ((i >= n) && ai->jackPort(i)) { - RouteList* ir = node->inRoutes(); - for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) { - Route r = *ii; - if ((r.type == JACK_ROUTE) && (r.channel == i)) { - msgRemoveRoute(r, Route(node,i)); - break; - } - } - audioDevice->unregisterPort(ai->jackPort(i)); - ai->setJackPort(i, 0); - } - } + if (!name.isEmpty()) + { + if (node->type() == Track::AUDIO_INPUT) + { + if (!checkAudioDevice()) return; + AudioInput* ai = (AudioInput*)node; + for (int i = 0; i < mc; ++i) + { + if (i < n && ai->jackPort(i) == 0) + { + char buffer[128]; + snprintf(buffer, 128, "%s-%d", name.latin1(), i); + //ai->setJackPort(i, audioDevice->registerInPort(buffer)); + ai->setJackPort(i, audioDevice->registerInPort(buffer, false)); + } + else if ((i >= n) && ai->jackPort(i)) + { + RouteList* ir = node->inRoutes(); + for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + { + Route r = *ii; + if ((r.type == Route::JACK_ROUTE) && (r.channel == i)) + { + msgRemoveRoute(r, Route(node,i)); + break; + } } - else if (node->type() == Track::AUDIO_OUTPUT) { + audioDevice->unregisterPort(ai->jackPort(i)); + ai->setJackPort(i, 0); + } + } + } + else if (node->type() == Track::AUDIO_OUTPUT) + { if (!checkAudioDevice()) return; AudioOutput* ao = (AudioOutput*)node; - for (int i = 0; i < mc; ++i) { + for (int i = 0; i < mc; ++i) + { void* jp = ao->jackPort(i); - if (i < n && jp == 0) { + if (i < n && jp == 0) + { char buffer[128]; snprintf(buffer, 128, "%s-%d", name.latin1(), i); //ao->setJackPort(i, audioDevice->registerOutPort(buffer)); ao->setJackPort(i, audioDevice->registerOutPort(buffer, false)); - } - else if (i >= n && jp) { + } + else if (i >= n && jp) + { RouteList* ir = node->outRoutes(); - for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) { + for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) + { Route r = *ii; - if ((r.type == JACK_ROUTE) && (r.channel == i)) { + if ((r.type == Route::JACK_ROUTE) && (r.channel == i)) + { msgRemoveRoute(Route(node,i), r); break; - } } + } audioDevice->unregisterPort(jp); ao->setJackPort(i, 0); - } } } } + } + + /* TODO TODO: Change all stereo routes to mono. + // If we are going from stereo to mono we need to disconnect any stray synti 'mono last channel'... + if(n == 1 && node->channels() > 1) + { + // This should always happen - syntis are fixed channels, user cannot change them. But to be safe... + if(node->type() != Track::AUDIO_SOFTSYNTH) + { + if(node->type() != Track::AUDIO_INPUT) + { + RouteList* rl = node->inRoutes(); + for(iRoute r = rl->begin(); r != rl->end(); ++r) + { + // Only interested in synth tracks. + if(r->type != Route::TRACK_ROUTE || r->track->type() != Track::AUDIO_SOFTSYNTH) + continue; + // If it's the last channel... + if(r->channel + 1 == ((AudioTrack*)r->track)->totalOutChannels()) + { + msgRemoveRoute(*r, Route(node, r->channel)); + //msgRemoveRoute(r, Route(node, r->remoteChannel)); + break; + } + } + } + + if(node->type() != Track::AUDIO_OUTPUT) + { + RouteList* rl = node->outRoutes(); + for(iRoute r = rl->begin(); r != rl->end(); ++r) + { + // Only interested in synth tracks. + if(r->type != Route::TRACK_ROUTE || r->track->type() != Track::AUDIO_SOFTSYNTH) + continue; + // If it's the last channel... + if(r->channel + 1 == ((AudioTrack*)r->track)->totalOutChannels()) + { + msgRemoveRoute(Route(node, r->channel), *r); + //msgRemoveRoute(Route(node, r->remoteChannel), r); + break; + } + } + } + } + } + */ + AudioMsg msg; msg.id = AUDIO_SET_CHANNELS; msg.snode = node; diff --git a/muse/muse/song.cpp b/muse/muse/song.cpp index 7b1e46bb..fa08f27f 100644 --- a/muse/muse/song.cpp +++ b/muse/muse/song.cpp @@ -14,6 +14,8 @@ #include <qpopupmenu.h> #include <qdir.h> #include <qaction.h> +#include <qcursor.h> +#include <qbutton.h> #include "app.h" #include "song.h" @@ -42,6 +44,26 @@ extern void clearMidiTransforms(); extern void clearMidiInputTransforms(); Song* song; +/* +//--------------------------------------------------------- +// RoutingMenuItem +//--------------------------------------------------------- + +class RoutingMenuItem : public QCustomMenuItem +{ + Route route; + //virtual QSize sizeHint() { return QSize(80, h); } + virtual void paint(QPainter* p, const QColorGroup&, bool, bool, int x, int y, int w, int h) + { + p->fillRect(x, y, w, h, QBrush(lightGray)); + p->drawText(x, y, w, h, AlignCenter, route.name()); + } + + public: + RoutingMenuItem(const Route& r) : route(r) { } +}; +*/ + //--------------------------------------------------------- // Song //--------------------------------------------------------- @@ -235,10 +257,16 @@ Track* Song::addTrack(int t) case Track::AUDIO_GROUP: case Track::AUDIO_AUX: case Track::AUDIO_INPUT: - case Track::AUDIO_SOFTSYNTH: + // p3.3.38 + //case Track::AUDIO_SOFTSYNTH: audio->msgAddRoute(Route((AudioTrack*)track, -1), Route(ao, -1)); updateFlags |= SC_ROUTE; break; + // p3.3.38 It should actually never get here now, but just in case. + case Track::AUDIO_SOFTSYNTH: + audio->msgAddRoute(Route((AudioTrack*)track, 0, ((AudioTrack*)track)->channels()), Route(ao, 0, ((AudioTrack*)track)->channels())); + updateFlags |= SC_ROUTE; + break; } } audio->msgUpdateSoloStates(); @@ -853,7 +881,6 @@ void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, unsigned start } newPart->setLenTick(endTick); // endTick - part->tick() - // Added by Tim. p3.3.6 //printf("Song::cmdAddRecordedEvents before changePart part:%p events:%p refs:%d Arefs:%d newPart:%p events:%p refs:%d Arefs:%d\n", part, part->events(), part->events()->refCount(), part->events()->arefCount(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount()); // Change the part. @@ -866,7 +893,6 @@ void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, unsigned start // Now add all of the new part's port controller values, and do all clone parts. addPortCtrlEvents(newPart, true); - // Added by Tim. p3.3.6 //printf("Song::cmdAddRecordedEvents after changePart part:%p events:%p refs:%d Arefs:%d newPart:%p events:%p refs:%d Arefs:%d\n", part, part->events(), part->events()->refCount(), part->events()->arefCount(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount()); //undoOp(UndoOp::ModifyPart, part, newPart); @@ -877,7 +903,6 @@ void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, unsigned start //part->events()->incARef(-1); updateFlags |= SC_PART_MODIFIED; - // Added by Tim. p3.3.6 //printf("Song::cmdAddRecordedEvents final part:%p events:%p refs:%d Arefs:%d newPart:%p events:%p refs:%d Arefs:%d\n", part, part->events(), part->events()->refCount(), part->events()->arefCount(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount()); */ @@ -1123,7 +1148,7 @@ void Song::setPlay(bool f) { if (extSyncFlag.value()) { if (debugMsg) - printf("not allowed while using external sync"); + printf("not allowed while using external sync"); return; } // only allow the user to set the button "on" @@ -1137,7 +1162,7 @@ void Song::setStop(bool f) { if (extSyncFlag.value()) { if (debugMsg) - printf("not allowed while using external sync"); + printf("not allowed while using external sync"); return; } // only allow the user to set the button "on" @@ -1857,7 +1882,6 @@ void Song::cmdRemovePart(Part* part) //void Song::cmdChangePart(Part* oldPart, Part* newPart) void Song::cmdChangePart(Part* oldPart, Part* newPart, bool doCtrls, bool doClones) { - // Added by Tim. p3.3.6 //printf("Song::cmdChangePart before changePart oldPart:%p events:%p refs:%d Arefs:%d sn:%d newPart:%p events:%p refs:%d Arefs:%d sn:%d\n", oldPart, oldPart->events(), oldPart->events()->refCount(), oldPart->events()->arefCount(), oldPart->sn(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount(), newPart->sn()); if(doCtrls) @@ -1875,7 +1899,6 @@ void Song::cmdChangePart(Part* oldPart, Part* newPart, bool doCtrls, bool doClon //oldPart->replaceClone(newPart); - // Added by Tim. p3.3.6 //printf("Song::cmdChangePart before repl/unchClone oldPart:%p events:%p refs:%d Arefs:%d sn:%d newPart:%p events:%p refs:%d Arefs:%d sn:%d\n", oldPart, oldPart->events(), oldPart->events()->refCount(), oldPart->events()->arefCount(), oldPart->sn(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount(), newPart->sn()); replaceClone(oldPart, newPart); @@ -1883,7 +1906,6 @@ void Song::cmdChangePart(Part* oldPart, Part* newPart, bool doCtrls, bool doClon if(doCtrls) addPortCtrlEvents(newPart, doClones); - // Added by Tim. p3.3.6 //printf("Song::cmdChangePart after repl/unchClone oldPart:%p events:%p refs:%d Arefs:%d sn:%d newPart:%p events:%p refs:%d Arefs:%d sn:%d\n", oldPart, oldPart->events(), oldPart->events()->refCount(), oldPart->events()->arefCount(), oldPart->sn(), newPart, newPart->events(), newPart->events()->refCount(), newPart->events()->arefCount(), newPart->sn()); updateFlags = SC_PART_MODIFIED; @@ -2028,6 +2050,20 @@ void Song::cleanupForQuit() // Remove the controllers and the values. midiPorts[i].controller()->clearDelete(true); + // Can't do this here. Jack isn't running. + /* + if(debugMsg) + printf("deleting midi devices\n"); + for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) + { + // Since Syntis are midi devices, there's no need to delete them below. + if((*imd)->isSynti()) + continue; + delete (*imd); + } + midiDevices.clear(); // midi devices + */ + if(debugMsg) printf("deleting synths\n"); // Delete all synths. @@ -2385,7 +2421,6 @@ int Song::execMidiAutomationCtlPopup(MidiTrack* track, MidiPart* part, const QPo dctl |= drumMap[note].anote; } - // Added by Tim. p3.3.6 //printf("Song::execMidiAutomationCtlPopup ctlnum:%d dctl:%d anote:%d\n", ctlnum, dctl, drumMap[ctlnum & 0x7f].anote); unsigned tick = cpos(); @@ -2428,7 +2463,6 @@ int Song::execMidiAutomationCtlPopup(MidiTrack* track, MidiPart* part, const QPo ev = i->second; if(ev.type() == Controller) { - // Added by Tim. p3.3.6 //printf("Song::execMidiAutomationCtlPopup ev.dataA:%d\n", ev.dataA()); //if(ev.dataA() == dctl) @@ -2651,7 +2685,7 @@ void Song::connectJackRoutes(AudioTrack* track, bool disconnect) for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; - if ((r.type == JACK_ROUTE) && (r.channel == ch)) + if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) { if(disconnect) audioDevice->disconnect(ao->jackPort(ch), r.jackPort); @@ -2684,7 +2718,7 @@ void Song::connectJackRoutes(AudioTrack* track, bool disconnect) for (iRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; - if ((r.type == JACK_ROUTE) && (r.channel == ch)) + if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) { if(disconnect) audioDevice->disconnect(r.jackPort, ai->jackPort(ch)); @@ -2708,6 +2742,199 @@ void Song::connectJackRoutes(AudioTrack* track, bool disconnect) } //--------------------------------------------------------- +// chooseMidiRoutes +//--------------------------------------------------------- + +void Song::chooseMidiRoutes(QButton* parent, MidiTrack* track, bool dst) +{ + if(!track) + return; + + //if(!track->isMidiTrack()) + // return; + + //if(dst) + //{ + // TODO + + //} + //else + //{ + RouteList* rl = dst ? track->outRoutes() : track->inRoutes(); + //Route dst(track, -1); + + QPopupMenu* pup = new QPopupMenu(parent); + pup->setCheckable(true); + + int gid = 0; + + //MidiInPortList* tl = song->midiInPorts(); + //for(iMidiInPort i = tl->begin();i != tl->end(); ++i) + for(int i = 0; i < MIDI_PORTS; ++i) + { + //MidiInPort* track = *i; + // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick wth ports. + MidiPort* mp = &midiPorts[i]; + MidiDevice* md = mp->device(); + if(!md) + continue; + + if(!(md->rwFlags() & (dst ? 1 : 2))) + continue; + + //printf("MidiStrip::iRoutePressed adding submenu portnum:%d\n", i); + + //QMenu* m = menu->addMenu(track->name()); + QPopupMenu* subp = new QPopupMenu(parent); + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + //QAction* a = m->addAction(QString("Channel %1").arg(ch+1)); + //subp->insertItem(QT_TR_NOOP(QString("Channel %1").arg(ch+1)), i * MIDI_CHANNELS + ch); + gid = i * MIDI_CHANNELS + ch; + + //printf("MidiStrip::iRoutePressed inserting gid:%d\n", gid); + + subp->insertItem(QString("Channel %1").arg(ch+1), gid); + //a->setCheckable(true); + //Route src(track, ch, RouteNode::TRACK); + //Route src(md, ch); + //Route r = Route(src, dst); + //a->setData(QVariant::fromValue(r)); + //a->setChecked(rl->indexOf(r) != -1); + Route srcRoute(md, ch); + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + //if(*ir == dst) + if(*ir == srcRoute) + { + subp->setItemChecked(gid, true); + break; + } + } + } + pup->insertItem(QT_TR_NOOP(md->name()), subp); + } + + /* + QPopupMenu* pup = new QPopupMenu(iR); + pup->setCheckable(true); + //MidiTrack* t = (MidiTrack*)track; + RouteList* irl = track->inRoutes(); + + MidiTrack* t = (MidiTrack*)track; + int gid = 0; + for (int i = 0; i < channel; ++i) + { + char buffer[128]; + snprintf(buffer, 128, "%s %d", tr("Channel").latin1(), i+1); + MenuTitleItem* titel = new MenuTitleItem(QString(buffer)); + pup->insertItem(titel); + + if (!checkAudioDevice()) return; + std::list<QString> ol = audioDevice->outputPorts(); + for (std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip) { + int id = pup->insertItem(*ip, (gid * 16) + i); + Route dst(*ip, true, i); + ++gid; + for (iRoute ir = irl->begin(); ir != irl->end(); ++ir) { + if (*ir == dst) { + pup->setItemChecked(id, true); + break; + } + } + } + if (i+1 != channel) + pup->insertSeparator(); + } + */ + + if(pup->count() == 0) + { + delete pup; + return; + } + + int n = pup->exec(QCursor::pos()); + delete pup; + if (n != -1) + { + int mdidx = n / MIDI_CHANNELS; + int ch = n % MIDI_CHANNELS; + + //if(debugMsg) + //printf("Song::chooseMidiRoutes mdidx:%d ch:%d\n", mdidx, ch); + + MidiPort* mp = &midiPorts[mdidx]; + MidiDevice* md = mp->device(); + if(!md) + return; + + //if(!(md->rwFlags() & 2)) + if(!(md->rwFlags() & (dst ? 1 : 2))) + return; + + + //QString s(pup->text(n)); + //QT_TR_NOOP(md->name()) + + //Route srcRoute(s, false, -1); + Route aRoute(md, ch); + //Route srcRoute(md, -1); + //Route dstRoute(track, -1); + Route bRoute(track, ch); + + //if (track->type() == Track::AUDIO_INPUT) + // srcRoute.channel = dstRoute.channel = n & 0xf; + iRoute iir = rl->begin(); + for (; iir != rl->end(); ++iir) + { + //if(*iir == (dst ? bRoute : aRoute)) + if(*iir == aRoute) + break; + } + if (iir != rl->end()) + { + // disconnect + if(dst) + { + //printf("Song::chooseMidiRoutes removing route src track name: %s dst device name: %s\n", track->name().latin1(), md->name().latin1()); + audio->msgRemoveRoute(bRoute, aRoute); + } + else + { + //printf("Song::chooseMidiRoutes removing route src device name: %s dst track name: %s\n", md->name().latin1(), track->name().latin1()); + audio->msgRemoveRoute(aRoute, bRoute); + } + } + else + { + // connect + if(dst) + { + //printf("Song::chooseMidiRoutes adding route src track name: %s dst device name: %s\n", track->name().latin1(), md->name().latin1()); + audio->msgAddRoute(bRoute, aRoute); + } + else + { + //printf("Song::chooseMidiRoutes adding route src device name: %s dst track name: %s\n", md->name().latin1(), track->name().latin1()); + audio->msgAddRoute(aRoute, bRoute); + } + } + + //printf("Song::chooseMidiRoutes calling msgUpdateSoloStates\n"); + audio->msgUpdateSoloStates(); + //printf("Song::chooseMidiRoutes calling song->update\n"); + song->update(SC_ROUTE); + } + //delete pup; + parent->setDown(false); // pup->exec() catches mouse release event + //printf("Song::chooseMidiRoutes end\n"); + + //} +} + +//--------------------------------------------------------- // insertTrack0 //--------------------------------------------------------- @@ -2725,7 +2952,6 @@ void Song::insertTrack0(Track* track, int idx) void Song::insertTrack1(Track* track, int /*idx*/) { - // Added by Tim. p3.3.13 //printf("Song::insertTrack1 track:%lx\n", track); switch(track->type()) { @@ -2742,7 +2968,6 @@ void Song::insertTrack1(Track* track, int /*idx*/) break; } - // Added by Tim. p3.3.13 //printf("Song::insertTrack1 end of function\n"); } @@ -2753,8 +2978,7 @@ void Song::insertTrack1(Track* track, int /*idx*/) //--------------------------------------------------------- void Song::insertTrack2(Track* track, int idx) - { - // Added by Tim. p3.3.13 +{ //printf("Song::insertTrack2 track:%lx\n", track); int n; @@ -2805,11 +3029,9 @@ void Song::insertTrack2(Track* track, int idx) // initialize missing aux send // iTrack i = _tracks.index2iterator(idx); - // Added by Tim. p3.3.13 //printf("Song::insertTrack2 inserting into _tracks...\n"); _tracks.insert(i, track); - // Added by Tim. p3.3.13 //printf("Song::insertTrack2 inserted\n"); n = _auxs.size(); @@ -2822,6 +3044,7 @@ void Song::insertTrack2(Track* track, int idx) } } + /* // // add routes // @@ -2848,11 +3071,51 @@ void Song::insertTrack2(Track* track, int idx) for (ciRoute r = rl->begin(); r != rl->end(); ++r) r->track->inRoutes()->push_back(src); } + */ - // Added by Tim. p3.3.13 - //printf("Song::insertTrack2 end of function\n"); + // p3.3.38 + // + // add routes + // + + if (track->type() == Track::AUDIO_OUTPUT) + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->outRoutes()->push_back(*r); + } + } + else if (track->type() == Track::AUDIO_INPUT) + { + const RouteList* rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->inRoutes()->push_back(*r); + } } + else + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->outRoutes()->push_back(*r); + } + rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->inRoutes()->push_back(*r); + } + } + + //printf("Song::insertTrack2 end of function\n"); + +} //--------------------------------------------------------- // insertTrack3 @@ -2861,7 +3124,6 @@ void Song::insertTrack2(Track* track, int idx) void Song::insertTrack3(Track* /*track*/, int /*idx*/)//prevent compiler warning: unused parameter { - // Added by Tim. p3.3.13 //printf("Song::insertTrack3\n"); /* @@ -2932,7 +3194,7 @@ void Song::removeTrack1(Track* track) //--------------------------------------------------------- void Song::removeTrack2(Track* track) - { +{ switch(track->type()) { case Track::MIDI: case Track::DRUM: @@ -2970,6 +3232,9 @@ void Song::removeTrack2(Track* track) break; } _tracks.erase(track); + + + /* if (track->isMidiTrack()) return; // @@ -2996,7 +3261,49 @@ void Song::removeTrack2(Track* track) for (ciRoute r = rl->begin(); r != rl->end(); ++r) r->track->inRoutes()->removeRoute(src); } + */ + + // p3.3.38 + + // + // remove routes + // + + if (track->type() == Track::AUDIO_OUTPUT) + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->outRoutes()->removeRoute(*r); + } } + else if (track->type() == Track::AUDIO_INPUT) + { + const RouteList* rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->inRoutes()->removeRoute(*r); + } + } + else + { + const RouteList* rl = track->inRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->outRoutes()->removeRoute(*r); + } + rl = track->outRoutes(); + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->track == track) + r->track->inRoutes()->removeRoute(*r); + } + } + +} //--------------------------------------------------------- // removeTrack3 diff --git a/muse/muse/song.h b/muse/muse/song.h index 0e08d8ab..6c76362b 100644 --- a/muse/muse/song.h +++ b/muse/muse/song.h @@ -36,6 +36,7 @@ class MarkerList; class Marker; class SNode; class QPopupMenu; +class QButton; class MidiPort; class MidiDevice; @@ -301,6 +302,7 @@ class Song : public QObject { int execMidiAutomationCtlPopup(MidiTrack*, MidiPart*, const QPoint&, int); void connectJackRoutes(AudioTrack* track, bool disconnect); void updateSoloStates(); + void chooseMidiRoutes(QButton* /*parent*/, MidiTrack* /*track*/, bool /*dst*/); //----------------------------------------- // undo, redo diff --git a/muse/muse/songfile.cpp b/muse/muse/songfile.cpp index 2e4a5395..07bf1e31 100644 --- a/muse/muse/songfile.cpp +++ b/muse/muse/songfile.cpp @@ -29,6 +29,7 @@ #include "midictrl.h" #include "amixer.h" #include "conf.h" +#include "driver/jackmidi.h" //struct ClonePart { //const EventList* el; @@ -1428,12 +1429,25 @@ void Song::write(int level, Xml& xml) const // write routing for (ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) { - if ((*i)->isMidiTrack()) - continue; - WaveTrack* track = (WaveTrack*)(*i); - track->writeRouting(level, xml); + + // p3.3.38 Changed + //if ((*i)->isMidiTrack()) + // continue; + //WaveTrack* track = (WaveTrack*)(*i); + //track->writeRouting(level, xml); + + (*i)->writeRouting(level, xml); } + // Write Jack midi routing. + for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { + //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(*i); + //if (!mjd) + // continue; + //mjd->writeRouting(level, xml); + (*i)->writeRouting(level, xml); + } + tempomap.write(level, xml); sigmap.write(level, xml); _markerList->write(level, xml); diff --git a/muse/muse/sync.cpp b/muse/muse/sync.cpp index 6b8e959c..0fbfa144 100644 --- a/muse/muse/sync.cpp +++ b/muse/muse/sync.cpp @@ -18,6 +18,7 @@ #include "audiodev.h" #include "gconfig.h" #include "xml.h" +#include "midi.h" //int rxSyncPort = -1; // receive from all ports //int txSyncPort = 1; @@ -26,7 +27,7 @@ //MidiSyncPort midiSyncPorts[MIDI_PORTS]; int volatile curMidiSyncInPort = -1; -bool debugSync = false; +bool debugSync = true; int mtcType = 1; MTC mtcOffset; @@ -880,10 +881,10 @@ void MidiSeq::realtimeSystemInput(int port, int c) MidiPort* mp = &midiPorts[port]; // Trigger on any tick, clock, or realtime command. - if(c == 0xf9) // Tick + if(c == ME_TICK) // Tick mp->syncInfo().trigTickDetect(); else - if(c == 0xf8) // Clock + if(c == ME_CLOCK) // Clock mp->syncInfo().trigMCSyncDetect(); else mp->syncInfo().trigMRTDetect(); // Other @@ -891,7 +892,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) // External sync not on? Clock in not turned on? Otherwise realtime in not turned on? if(!extSyncFlag.value()) return; - if(c == 0xf8) + if(c == ME_CLOCK) { if(!mp->syncInfo().MCIn()) return; @@ -902,7 +903,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) switch(c) { - case 0xf8: // midi clock (24 ticks / quarter note) + case ME_CLOCK: // midi clock (24 ticks / quarter note) { // Not for the current in port? Forget it. if(port != curMidiSyncInPort) @@ -1194,7 +1195,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) } break; - case 0xf9: // midi tick (every 10 msec) + case ME_TICK: // midi tick (every 10 msec) // FIXME: Unfinished? mcStartTick is uninitialized and Song::setPos doesn't set it either. Dangerous to allow this. //if (mcStart) { // song->setPos(0, mcStartTick); @@ -1202,7 +1203,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) // return; // } break; - case 0xfa: // start + case ME_START: // start // Re-transmit start to other devices if clock out turned on. for(int p = 0; p < MIDI_PORTS; ++p) //if(p != port && midiPorts[p].syncInfo().MCOut()) @@ -1259,7 +1260,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) playStateExt = true; } break; - case 0xfb: // continue + case ME_CONTINUE: // continue // Re-transmit continue to other devices if clock out turned on. for(int p = 0; p < MIDI_PORTS; ++p) //if(p != port && midiPorts[p].syncInfo().MCOut()) @@ -1286,7 +1287,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) playStateExt = true; } break; - case 0xfc: // stop + case ME_STOP: // stop { // p3.3.35 // Stop the increment right away. @@ -1326,10 +1327,11 @@ void MidiSeq::realtimeSystemInput(int port, int c) } break; - case 0xfd: // unknown - case 0xfe: // active sensing - case 0xff: // system reset - break; + //case 0xfd: // unknown + //case ME_SENSE: // active sensing + //case ME_META: // system reset (reset is 0xff same enumeration as file meta event) + default: + break; } } diff --git a/muse/muse/synth.cpp b/muse/muse/synth.cpp index e3bf1582..24780a52 100644 --- a/muse/muse/synth.cpp +++ b/muse/muse/synth.cpp @@ -126,12 +126,12 @@ static Synth* findSynth(const QString& sclass, const QString& label) } //--------------------------------------------------------- -// createSynthI +// createSynthInstance // create a synthesizer instance of class "label" //--------------------------------------------------------- //static SynthI* createSynthI(const QString& sclass) -static SynthI* createSynthI(const QString& sclass, const QString& label) +static SynthI* createSynthInstance(const QString& sclass, const QString& label) { //Synth* s = findSynth(sclass); Synth* s = findSynth(sclass, label); @@ -149,7 +149,7 @@ static SynthI* createSynthI(const QString& sclass, const QString& label) } } else - printf("synthi class:%s label:%s not found\n", sclass.latin1(), label.latin1()); + printf("createSynthInstance: synthi class:%s label:%s not found\n", sclass.latin1(), label.latin1()); return si; } @@ -300,6 +300,16 @@ int MessSynthIF::channels() const return _mess->channels(); } +int MessSynthIF::totalOutChannels() const + { + return _mess->channels(); + } + +int MessSynthIF::totalInChannels() const + { + return 0; + } + //SynthIF* MessSynth::createSIF() const SynthIF* MessSynth::createSIF(SynthI* si) { @@ -327,8 +337,11 @@ bool SynthI::initInstance(Synth* s, const QString& instanceName) setIName(instanceName); // set instrument name _sif = s->createSIF(this); - AudioTrack::setChannels(_sif->channels()); - + // p3.3.38 + //AudioTrack::setChannels(_sif->channels()); + AudioTrack::setTotalOutChannels(_sif->totalOutChannels()); + AudioTrack::setTotalInChannels(_sif->totalInChannels()); + //--------------------------------------------------- // read available controller from synti //--------------------------------------------------- @@ -531,36 +544,38 @@ void initMidiSynth() //SynthI* Song::createSynthI(const QString& sclass) SynthI* Song::createSynthI(const QString& sclass, const QString& label) { - // Added by Tim. p3.3.13 //printf("Song::createSynthI calling ::createSynthI class:%s\n", sclass.latin1()); //SynthI* si = ::createSynthI(sclass); - SynthI* si = ::createSynthI(sclass, label); + //SynthI* si = ::createSynthI(sclass, label); + SynthI* si = createSynthInstance(sclass, label); if(!si) return 0; - // Added by Tim. p3.3.13 //printf("Song::createSynthI created SynthI. Before insertTrack1...\n"); insertTrack1(si, -1); - // Added by Tim. p3.3.13 //printf("Song::createSynthI after insertTrack1. Before msgInsertTrack...\n"); msgInsertTrack(si, -1, true); // add to instance list - // Added by Tim. p3.3.13 //printf("Song::createSynthI after msgInsertTrack. Before insertTrack3...\n"); insertTrack3(si, -1); - // Added by Tim. p3.3.13 //printf("Song::createSynthI after insertTrack3. Adding default routes...\n"); OutputList* ol = song->outputs(); // add default route to master (first audio output) if (!ol->empty()) { AudioOutput* ao = ol->front(); - audio->msgAddRoute(Route(si, -1), Route(ao, -1)); + // p3.3.38 + //audio->msgAddRoute(Route(si, -1), Route(ao, -1)); + //audio->msgAddRoute(Route((AudioTrack*)si, -1), Route(ao, -1)); + // Make sure the route channel and channels are valid. + audio->msgAddRoute(Route((AudioTrack*)si, 0, ((AudioTrack*)si)->channels()), Route(ao, 0, ((AudioTrack*)si)->channels())); + audio->msgUpdateSoloStates(); } + return si; } diff --git a/muse/muse/synth.h b/muse/muse/synth.h index 7132e227..87584a53 100644 --- a/muse/muse/synth.h +++ b/muse/muse/synth.h @@ -118,6 +118,8 @@ class SynthIF { //virtual bool init(Synth* s) = 0; virtual int channels() const = 0; + virtual int totalOutChannels() const = 0; + virtual int totalInChannels() const = 0; virtual void deactivate3() = 0; virtual const char* getPatchName(int, int, int, bool) const = 0; virtual const char* getPatchName(int, int, MType, bool) = 0; @@ -169,6 +171,8 @@ class SynthI : public AudioTrack, public MidiDevice, //SynthI* clone() const { return new SynthI(*this); } SynthI* clone(bool /*cloneParts*/) const { return new SynthI(*this); } + virtual inline int deviceType() { return SYNTH_MIDI; } + SynthIF* sif() const { return _sif; } bool initInstance(Synth* s, const QString& instanceName); @@ -237,6 +241,8 @@ class MessSynthIF : public SynthIF { bool init(Synth* s, SynthI* si); virtual int channels() const; + virtual int totalOutChannels() const; + virtual int totalInChannels() const; virtual void deactivate3(); virtual const char* getPatchName(int, int, int, bool) const { return ""; } virtual const char* getPatchName(int, int, MType, bool); diff --git a/muse/muse/ticksynth.cpp b/muse/muse/ticksynth.cpp index acbdfb3e..2cd0ae82 100644 --- a/muse/muse/ticksynth.cpp +++ b/muse/muse/ticksynth.cpp @@ -74,6 +74,8 @@ class MetronomeSynthIF : public SynthIF //virtual bool init(Synth*) { return true; } virtual int channels() const { return 1; } + virtual int totalOutChannels() const { return 1; } + virtual int totalInChannels() const { return 0; } virtual void deactivate3() {} virtual const char* getPatchName(int, int, int, bool) const { return ""; } virtual const char* getPatchName(int, int, MType, bool) { return ""; } diff --git a/muse/muse/track.cpp b/muse/muse/track.cpp index 1580f180..6f6f11a8 100644 --- a/muse/muse/track.cpp +++ b/muse/muse/track.cpp @@ -6,6 +6,9 @@ // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) //========================================================= +#include <qt.h> +#include <qstring.h> + #include "track.h" #include "event.h" #include "mididev.h" @@ -367,8 +370,8 @@ MidiTrack::MidiTrack(const MidiTrack& mt, bool cloneParts) { _outPort = mt.outPort(); _outChannel = mt.outChannel(); - _inPortMask = mt.inPortMask(); - _inChannelMask = mt.inChannelMask(); + ///_inPortMask = mt.inPortMask(); + ///_inChannelMask = mt.inChannelMask(); _events = new EventList; _mpevents = new MPEventList; transposition = mt.transposition; @@ -395,9 +398,9 @@ void MidiTrack::init() _outChannel = 0; // Changed by Tim. p3.3.8 //_inPortMask = 0xffff; - _inPortMask = 0xffffffff; + ///_inPortMask = 0xffffffff; - _inChannelMask = 0xffff; // "ALL" + ///_inChannelMask = 0xffff; // "ALL" transposition = 0; velocity = 0; delay = 0; @@ -648,6 +651,123 @@ bool Track::readProperties(Xml& xml, const QString& tag) } //--------------------------------------------------------- +// writeRouting +//--------------------------------------------------------- + +void Track::writeRouting(int level, Xml& xml) const +{ + QString s; + + if (type() == Track::AUDIO_INPUT) + { + const RouteList* rl = &_inRoutes; + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(!r->name().isEmpty()) + { + s = QT_TR_NOOP("Route"); + if(r->channel != -1) + s += QString(QT_TR_NOOP(" channel=\"%1\"")).arg(r->channel); + + ///Route dst(name(), true, r->channel); + //xml.tag(level++, "Route"); + xml.tag(level++, s); + + // p3.3.38 New routing scheme. + ///xml.strTag(level, "srcNode", r->name()); + //xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + s = QT_TR_NOOP("source"); + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + xml.tag(level, s); + + ///xml.strTag(level, "dstNode", dst.name()); + + //if(r->channel != -1) + // xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::TRACK_ROUTE, r->channel, name().latin1()); + //else + // xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::TRACK_ROUTE, name().latin1()); + xml.tag(level, "dest name=\"%s\"/", name().latin1()); + + xml.etag(level--, "Route"); + } + } + } + + const RouteList* rl = &_outRoutes; + for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(!r->name().isEmpty()) + { + ///QString src(name()); + ///if (type() == Track::AUDIO_OUTPUT) + ///{ + ///Route s(src, false, r->channel); + ///src = s.name(); + ///} + + s = QT_TR_NOOP("Route"); + if(r->channel != -1) + s += QString(QT_TR_NOOP(" channel=\"%1\"")).arg(r->channel); + if(r->channels != -1) + s += QString(QT_TR_NOOP(" channels=\"%1\"")).arg(r->channels); + if(r->remoteChannel != -1) + s += QString(QT_TR_NOOP(" remch=\"%1\"")).arg(r->remoteChannel); + + //xml.tag(level++, "Route"); + xml.tag(level++, s); + + ///xml.strTag(level, "srcNode", src); + //if(r->channel != -1) + + // Allow for a regular mono or stereo track to feed a multi-channel synti. + // thisChannel is the 'starting' channel of this source if feeding a regular track. + //if(r->type == Route::TRACK_ROUTE && r->track->isSynti() && r->channel != -1) + //if(isSynti() && r->thisChannel != -1) + //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::TRACK_ROUTE, r->channel, name().latin1()); + // xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::TRACK_ROUTE, r->thisChannel, name().latin1()); + //else + + //if(r->channel != -1) + // xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::TRACK_ROUTE, r->channel, name().latin1()); + //else + // xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::TRACK_ROUTE, name().latin1()); + xml.tag(level, "source name=\"%s\"/", name().latin1()); + + ///xml.strTag(level, "dstNode", r->name()); + //if(r->channel != -1) + // xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().latin1()); + //else + // xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + + // Allow for a regular mono or stereo track to feed a multi-channel synti. + // Channel is the 'starting' channel of the destination. + //if(r->type == Route::TRACK_ROUTE && r->track->isSynti() && r->channel != -1) + + //if(r->type == Route::TRACK_ROUTE && r->track->type() == Track::AUDIO_SOFTSYNTH && r->remoteChannel != -1) + // xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->remoteChannel, r->name().latin1()); + //else + //if(r->type == Route::MIDI_DEVICE_ROUTE) + // xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().latin1()); + //else + // xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().latin1()); + + s = QT_TR_NOOP("dest"); + if(r->type == Route::MIDI_DEVICE_ROUTE) + s += QString(QT_TR_NOOP(" devtype=\"%1\"")).arg(r->device->deviceType()); + else + if(r->type != Route::TRACK_ROUTE) + s += QString(QT_TR_NOOP(" type=\"%1\"")).arg(r->type); + s += QString(QT_TR_NOOP(" name=\"%1\"/")).arg(r->name()); + xml.tag(level, s); + + xml.etag(level--, "Route"); + } + } +} + +//--------------------------------------------------------- // MidiTrack::write //--------------------------------------------------------- @@ -665,8 +785,8 @@ void MidiTrack::write(int level, Xml& xml) const xml.intTag(level, "device", outPort()); xml.intTag(level, "channel", outChannel()); //xml.intTag(level, "inportMap", inPortMask()); - xml.uintTag(level, "inportMap", inPortMask()); - xml.intTag(level, "inchannelMap", inChannelMask()); + ///xml.uintTag(level, "inportMap", inPortMask()); // Obsolete + ///xml.intTag(level, "inchannelMap", inChannelMask()); // Obsolete xml.intTag(level, "locked", _locked); xml.intTag(level, "echo", _recEcho); @@ -721,9 +841,11 @@ void MidiTrack::read(Xml& xml) setOutChannel(xml.parseInt()); else if (tag == "inportMap") //setInPortMask(xml.parseInt()); - setInPortMask(xml.parseUInt()); + ///setInPortMask(xml.parseUInt()); + xml.skip(tag); // Obsolete else if (tag == "inchannelMap") - setInChannelMask(xml.parseInt()); + ///setInChannelMask(xml.parseInt()); + xml.skip(tag); // Obsolete else if (tag == "locked") _locked = xml.parseInt(); else if (tag == "echo") diff --git a/muse/muse/track.h b/muse/muse/track.h index 6f06c923..9449fd1b 100644 --- a/muse/muse/track.h +++ b/muse/muse/track.h @@ -51,6 +51,9 @@ class Track { static bool _tmpSoloChainDoIns; static bool _tmpSoloChainNoDec; + // p3.3.38 + RouteList _inRoutes; + RouteList _outRoutes; QString _name; bool _recordFlag; @@ -115,6 +118,13 @@ class Track { TrackType type() const { return _type; } void setType(TrackType t) { _type = t; } + // routing + RouteList* inRoutes() { return &_inRoutes; } + RouteList* outRoutes() { return &_outRoutes; } + bool noInRoute() const { return _inRoutes.empty(); } + bool noOutRoute() const { return _outRoutes.empty(); } + void writeRouting(int, Xml&) const; + PartList* parts() { return &_parts; } const PartList* cparts() const { return &_parts; } Part* findPart(unsigned tick); @@ -184,8 +194,8 @@ class MidiTrack : public Track { int _outPort; int _outChannel; //int _inPortMask; - unsigned int _inPortMask; // bitmask of accepted record ports - int _inChannelMask; // bitmask of accepted record channels + ///unsigned int _inPortMask; // bitmask of accepted record ports + ///int _inChannelMask; // bitmask of accepted record channels bool _recEcho; // For midi (and audio). Whether to echo incoming record events to output device. EventList* _events; // tmp Events during midi import @@ -227,14 +237,14 @@ class MidiTrack : public Track { void setOutChanAndUpdate(int i); void setOutPortAndUpdate(int i); //void setInPortMask(int i) { _inPortMask = i; } - void setInPortMask(unsigned int i) { _inPortMask = i; } - void setInChannelMask(int i) { _inChannelMask = i; } + ///void setInPortMask(unsigned int i) { _inPortMask = i; } + ///void setInChannelMask(int i) { _inChannelMask = i; } void setRecEcho(bool b) { _recEcho = b; } int outPort() const { return _outPort; } //int inPortMask() const { return _inPortMask; } - unsigned int inPortMask() const { return _inPortMask; } + ///unsigned int inPortMask() const { return _inPortMask; } int outChannel() const { return _outChannel; } - int inChannelMask() const { return _inChannelMask; } + ///int inChannelMask() const { return _inChannelMask; } bool recEcho() const { return _recEcho; } virtual bool isMute() const; @@ -269,8 +279,8 @@ class AudioTrack : public Track { AutomationType _automationType; - RouteList _inRoutes; - RouteList _outRoutes; + //RouteList _inRoutes; + //RouteList _outRoutes; bool _sendMetronome; @@ -278,8 +288,11 @@ class AudioTrack : public Track { void readAuxSend(Xml& xml); protected: - //float** outBuffers; - float* outBuffers[MAX_CHANNELS]; + float** outBuffers; + //float* outBuffers[MAX_CHANNELS]; + int _totalOutChannels; + int _totalInChannels; + unsigned bufferPos; virtual bool getData(unsigned, int, unsigned, float**); SndFile* _recFile; @@ -288,6 +301,8 @@ class AudioTrack : public Track { public: AudioTrack(TrackType t); + //AudioTrack(TrackType t, int num_out_bufs = MAX_CHANNELS); + //AudioTrack(const AudioTrack&); AudioTrack(const AudioTrack&, bool cloneParts); virtual ~AudioTrack(); @@ -317,6 +332,10 @@ class AudioTrack : public Track { CtrlListList* controller() { return &_controller; } virtual void setChannels(int n); + virtual void setTotalOutChannels(int num); + virtual int totalOutChannels() { return _totalOutChannels; } + virtual void setTotalInChannels(int num); + virtual int totalInChannels() { return _totalInChannels; } virtual bool isMute() const; virtual void setSolo(bool val); @@ -355,19 +374,19 @@ class AudioTrack : public Track { void setPluginCtrlVal(int param, double val); void readVolume(Xml& xml); - void writeRouting(int, Xml&) const; + //void writeRouting(int, Xml&) const; // routing - RouteList* inRoutes() { return &_inRoutes; } - RouteList* outRoutes() { return &_outRoutes; } - bool noInRoute() const { return _inRoutes.empty(); } - bool noOutRoute() const { return _outRoutes.empty(); } + //RouteList* inRoutes() { return &_inRoutes; } + //RouteList* outRoutes() { return &_outRoutes; } + //bool noInRoute() const { return _inRoutes.empty(); } + //bool noOutRoute() const { return _outRoutes.empty(); } virtual void preProcessAlways() { _processed = false; } - virtual void addData(unsigned, int, unsigned, float**); - virtual void copyData(unsigned, int, unsigned, float**); + virtual void addData(unsigned /*samplePos*/, int /*channels*/, int /*srcStartChan*/, int /*srcChannels*/, unsigned /*frames*/, float** /*buffer*/); + virtual void copyData(unsigned /*samplePos*/, int /*channels*/, int /*srcStartChan*/, int /*srcChannels*/, unsigned /*frames*/, float** /*buffer*/); virtual bool hasAuxSend() const { return false; } - + // automation virtual AutomationType automationType() const { return _automationType; } virtual void setAutomationType(AutomationType t); diff --git a/muse/muse/vst.cpp b/muse/muse/vst.cpp index 945bc2e9..194e1993 100644 --- a/muse/muse/vst.cpp +++ b/muse/muse/vst.cpp @@ -473,6 +473,18 @@ int VstSynthIF::channels() const return plugin->numOutputs; } +int VstSynthIF::totalOutChannels() const + { + AEffect* plugin = _fst->plugin; + return plugin->numOutputs; + } + +int VstSynthIF::totalInChannels() const + { + AEffect* plugin = _fst->plugin; + return plugin->numInputs; + } + //--------------------------------------------------------- // createSIF //--------------------------------------------------------- diff --git a/muse/muse/vst.h b/muse/muse/vst.h index 1caef0fc..33eaaab3 100644 --- a/muse/muse/vst.h +++ b/muse/muse/vst.h @@ -61,6 +61,8 @@ class VstSynthIF : public SynthIF virtual int eventsPending() const { return 0; } virtual bool init(Synth*); virtual int channels() const; + virtual int totalOutChannels() const; + virtual int totalInChannels() const; virtual void deactivate3(); virtual const char* getPatchName(int, int, int, bool) const { return ""; } virtual const char* getPatchName(int, int, MType, bool) { return ""; } diff --git a/muse/muse/wavetrack.cpp b/muse/muse/wavetrack.cpp index fbf7b965..c4fe7071 100644 --- a/muse/muse/wavetrack.cpp +++ b/muse/muse/wavetrack.cpp @@ -197,10 +197,39 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float* if ((song->bounceTrack != this) && !noInRoute()) { RouteList* irl = inRoutes(); iRoute i = irl->begin(); - i->track->copyData(framePos, channels, nframe, bp); + if(i->track->isMidiTrack()) + { + if(debugMsg) + printf("WaveTrack::getData: Error: First route is a midi track route!\n"); + return false; + } + // p3.3.38 + //((AudioTrack*)i->track)->copyData(framePos, channels, nframe, bp); + ((AudioTrack*)i->track)->copyData(framePos, channels, + //(i->track->type() == Track::AUDIO_SOFTSYNTH && i->channel != -1) ? i->channel : 0, + i->channel, + i->channels, + nframe, bp); + ++i; for (; i != irl->end(); ++i) - i->track->addData(framePos, channels, nframe, bp); + { + if(i->track->isMidiTrack()) + { + if(debugMsg) + printf("WaveTrack::getData: Error: Route is a midi track route!\n"); + //return false; + continue; + } + // p3.3.38 + //((AudioTrack*)i->track)->addData(framePos, channels, nframe, bp); + ((AudioTrack*)i->track)->addData(framePos, channels, + //(i->track->type() == Track::AUDIO_SOFTSYNTH && i->channel != -1) ? i->channel : 0, + i->channel, + i->channels, + nframe, bp); + + } if (recordFlag()) { if (audio->isRecording() && recFile()) { if (audio->freewheel()) { diff --git a/muse/muse/widgets/mtrackinfobase.ui b/muse/muse/widgets/mtrackinfobase.ui index cf753252..3d2b274a 100644 --- a/muse/muse/widgets/mtrackinfobase.ui +++ b/muse/muse/widgets/mtrackinfobase.ui @@ -95,7 +95,7 @@ <set>AlignCenter | WordBreak</set> </property> </widget> - <widget class="SpinBox" row="8" column="0"> + <widget class="SpinBox" row="7" column="0"> <property name="name"> <cstring>iLen</cstring> </property> @@ -136,81 +136,29 @@ <string>output port</string> </property> </widget> - <widget class="QLayoutWidget" row="2" column="1"> + <widget class="QLabel" row="2" column="1"> <property name="name"> - <cstring>oPortsLayout</cstring> + <cstring>TextLabel2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="indent"> + <number>1</number> + </property> + <property name="text"> + <string>Out ch</string> </property> - <hbox> - <widget class="QLabel"> - <property name="name"> - <cstring>TextLabel2</cstring> - </property> - <property name="sizePolicy"> - <sizepolicy> - <hsizetype>5</hsizetype> - <vsizetype>0</vsizetype> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="margin"> - <number>0</number> - </property> - <property name="indent"> - <number>1</number> - </property> - <property name="text"> - <string>OCh</string> - </property> - </widget> - <widget class="QToolButton"> - <property name="name"> - <cstring>recEchoButton</cstring> - </property> - <property name="maximumSize"> - <size> - <width>14</width> - <height>32767</height> - </size> - </property> - <property name="sizePolicy"> - <sizepolicy> - <hsizetype>1</hsizetype> - <vsizetype>1</vsizetype> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="toggleButton"> - <string>true</string> - </property> - <property name="whatsThis" stdset="0"> - <string>Echo recording events to output.</string> - </property> - <property name="toolTip" stdset="0"> - <string>Echo</string> - </property> - </widget> - <spacer> - <property name="name"> - <cstring>recEchoSpacer</cstring> - </property> - <property name="orientation"> - <enum>Horizontal</enum> - </property> - <property name="sizeType"> - <enum>Minimum</enum> - </property> - <property name="sizeHint"> - <size> - <width>2</width> - <height>2</height> - </size> - </property> - </spacer> - </hbox> </widget> - <widget class="SpinBox" row="7" column="0"> + <widget class="SpinBox" row="6" column="0"> <property name="name"> <cstring>iVerz</cstring> </property> @@ -232,7 +180,7 @@ <number>-1000</number> </property> </widget> - <widget class="SpinBox" row="9" column="0"> + <widget class="SpinBox" row="8" column="0"> <property name="name"> <cstring>iAnschl</cstring> </property> @@ -254,7 +202,7 @@ <number>0</number> </property> </widget> - <widget class="SpinBox" row="10" column="0"> + <widget class="SpinBox" row="9" column="0"> <property name="name"> <cstring>iKompr</cstring> </property> @@ -282,7 +230,7 @@ <number>100</number> </property> </widget> - <widget class="SpinBox" row="6" column="0"> + <widget class="SpinBox" row="5" column="0"> <property name="name"> <cstring>iTransp</cstring> </property> @@ -307,7 +255,7 @@ <number>1</number> </property> </widget> - <widget class="QLabel" row="6" column="1"> + <widget class="QLabel" row="5" column="1"> <property name="name"> <cstring>TextLabel9</cstring> </property> @@ -326,7 +274,7 @@ <number>2</number> </property> </widget> - <widget class="QLabel" row="7" column="1"> + <widget class="QLabel" row="6" column="1"> <property name="name"> <cstring>TextLabel10</cstring> </property> @@ -345,7 +293,7 @@ <number>2</number> </property> </widget> - <widget class="QLabel" row="10" column="1"> + <widget class="QLabel" row="9" column="1"> <property name="name"> <cstring>TextLabel13</cstring> </property> @@ -364,7 +312,7 @@ <number>2</number> </property> </widget> - <widget class="QLabel" row="9" column="1"> + <widget class="QLabel" row="8" column="1"> <property name="name"> <cstring>TextLabel12</cstring> </property> @@ -383,7 +331,7 @@ <number>0</number> </property> </widget> - <widget class="QLabel" row="8" column="1"> + <widget class="QLabel" row="7" column="1"> <property name="name"> <cstring>TextLabel11</cstring> </property> @@ -402,135 +350,131 @@ <number>2</number> </property> </widget> - <widget class="QLineEdit" row="3" column="0"> + <widget class="QLayoutWidget" row="3" column="0"> <property name="name"> - <cstring>iInput</cstring> + <cstring>routingLayout</cstring> </property> - <property name="sizePolicy"> - <sizepolicy> - <hsizetype>7</hsizetype> - <vsizetype>0</vsizetype> + <hbox> + <widget class="QToolButton"> + <property name="name"> + <cstring>iRButton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> <horstretch>0</horstretch> <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="whatsThis" stdset="0"> - <string>Events from all configured ports are -recorded to this track. -You can specify more than one port for -recording: - 1 2 3 record from port 1 2 and 3 - 1-3 same - 1-3 5 record from port 1 2 3 and 5</string> - </property> - <property name="toolTip" stdset="0"> - <string>input ports</string> - </property> - </widget> - <widget class="QLabel" row="3" column="1"> - <property name="name"> - <cstring>iPortsTextLabel</cstring> - </property> - <property name="sizePolicy"> - <sizepolicy> - <hsizetype>0</hsizetype> - <vsizetype>0</vsizetype> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="margin"> - <number>0</number> - </property> - <property name="indent"> - <number>2</number> - </property> - <property name="text"> - <string>IPorts</string> - </property> - </widget> - <widget class="QLayoutWidget" row="4" column="1"> - <property name="name"> - <cstring>iChLayout</cstring> - </property> - <hbox> - <widget class="QLabel"> - <property name="name"> - <cstring>iChanTextLabel</cstring> - </property> - <property name="sizePolicy"> - <sizepolicy> - <hsizetype>0</hsizetype> - <vsizetype>0</vsizetype> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="margin"> - <number>0</number> - </property> - <property name="indent"> - <number>2</number> - </property> - <property name="text"> - <string>ICh</string> - </property> + </sizepolicy> + </property> + <property name="text"> + <string>iR</string> + </property> + <property name="toolTip" stdset="0"> + <string>input routing</string> + </property> </widget> - <widget class="QLabel"> + <widget class="QToolButton"> <property name="name"> - <cstring>iChanDetectLabel</cstring> + <cstring>oRButton</cstring> </property> <property name="sizePolicy"> <sizepolicy> - <hsizetype>5</hsizetype> - <vsizetype>0</vsizetype> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> - <string>W</string> - </property> - <property name="alignment"> - <set>AlignCenter</set> + <string>oR</string> </property> <property name="toolTip" stdset="0"> - <string>input detect</string> - </property> - <property name="whatsThis" stdset="0"> - <string>Input detect indicator. Detects all note on-off, controller, aftertouch, - program change, and pitchbend (but not sysex or realtime) events - on the selected channels, on the selected midi ports.</string> + <string>output routing</string> </property> </widget> </hbox> </widget> - <widget class="QLineEdit" row="4" column="0"> - <property name="name"> - <cstring>iInputChannel</cstring> - </property> - <property name="sizePolicy"> + <widget class="QLayoutWidget" row="3" column="1"> + <property name="name"> + <cstring>routingLayout2</cstring> + </property> + <hbox> + <widget class="QLabel"> + <property name="name"> + <cstring>iChanDetectLabel</cstring> + </property> + <property name="sizePolicy"> <sizepolicy> - <hsizetype>5</hsizetype> - <vsizetype>0</vsizetype> - <horstretch>0</horstretch> - <verstretch>0</verstretch> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> </sizepolicy> - </property> - <property name="toolTip" stdset="0"> - <string>input channels</string> - </property> - <property name="whatsThis" stdset="0"> - <string>Events from all configured channels are -recorded to this track. -You can specify more than one channel for -recording: - 1 2 3 record from channel 1 2 and 3 - 1-3 same - 1-3 5 record from channel 1 2 3 and 5</string> - </property> + </property> + <property name="text"> + <string>W</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + <property name="toolTip" stdset="0"> + <string>input detect</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Input detect indicator. Detects all note on-off, controller, aftertouch, + program change, and pitchbend (but not sysex or realtime) events + on the selected channels, on the selected midi ports.</string> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>recEchoButton</cstring> + </property> + <property name="maximumSize"> + <size> + <width>14</width> + <height>32767</height> + </size> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toggleButton"> + <string>true</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Echo recording events to output.</string> + </property> + <property name="toolTip" stdset="0"> + <string>Echo</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>echoSpacer</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Maximum</enum> + </property> + <property name="sizeHint"> + <size> + <width>4</width> + <height>2</height> + </size> + </property> + </spacer> + </hbox> </widget> - <widget class="QLabel" row="11" column="0" rowspan="1" colspan="2"> + <widget class="QLabel" row="10" column="0" rowspan="1" colspan="2"> <property name="name"> <cstring>TextLabel1_2</cstring> </property> @@ -561,7 +505,7 @@ recording: <set>AlignCenter</set> </property> </widget> - <widget class="QPushButton" row="12" column="0" rowspan="1" colspan="2"> + <widget class="QPushButton" row="11" column="0" rowspan="1" colspan="2"> <property name="name"> <cstring>iPatch</cstring> </property> @@ -580,7 +524,7 @@ recording: <string>Select instrument patch</string> </property> </widget> - <widget class="QLabel" row="13" column="0"> + <widget class="QLabel" row="12" column="0"> <property name="name"> <cstring>textLabel1</cstring> </property> @@ -599,7 +543,7 @@ recording: <set>AlignVCenter|AlignRight</set> </property> </widget> - <widget class="QLayoutWidget" row="13" column="1"> + <widget class="QLayoutWidget" row="12" column="1"> <property name="name"> <cstring>recLayout</cstring> </property> @@ -662,7 +606,7 @@ recording: </spacer> </hbox> </widget> - <widget class="SpinBox" row="14" column="0"> + <widget class="SpinBox" row="13" column="0"> <property name="name"> <cstring>iHBank</cstring> </property> @@ -690,7 +634,7 @@ recording: <string>Bank Select MSB. Double-click on/off.</string> </property> </widget> - <widget class="QLabel" row="14" column="1"> + <widget class="QLabel" row="13" column="1"> <property name="name"> <cstring>TextLabel4</cstring> </property> @@ -715,7 +659,7 @@ recording: <number>2</number> </property> </widget> - <widget class="SpinBox" row="15" column="0"> + <widget class="SpinBox" row="14" column="0"> <property name="name"> <cstring>iLBank</cstring> </property> @@ -743,7 +687,7 @@ recording: <string>Bank Select LSB. Double-click on/off.</string> </property> </widget> - <widget class="QLabel" row="15" column="1"> + <widget class="QLabel" row="14" column="1"> <property name="name"> <cstring>TextLabel5</cstring> </property> @@ -768,7 +712,7 @@ recording: <number>2</number> </property> </widget> - <widget class="SpinBox" row="16" column="0"> + <widget class="SpinBox" row="15" column="0"> <property name="name"> <cstring>iProgram</cstring> </property> @@ -796,7 +740,7 @@ recording: <string>Program. Double-click on/off.</string> </property> </widget> - <widget class="QLayoutWidget" row="16" column="1"> + <widget class="QLayoutWidget" row="15" column="1"> <property name="name"> <cstring>progLayout</cstring> </property> @@ -865,7 +809,7 @@ recording: </spacer> </hbox> </widget> - <widget class="SpinBox" row="17" column="0"> + <widget class="SpinBox" row="16" column="0"> <property name="name"> <cstring>iLautst</cstring> </property> @@ -893,7 +837,7 @@ recording: <string>Volume. Double-click on/off.</string> </property> </widget> - <widget class="QLayoutWidget" row="17" column="1"> + <widget class="QLayoutWidget" row="16" column="1"> <property name="name"> <cstring>volLayout</cstring> </property> @@ -962,7 +906,7 @@ recording: </spacer> </hbox> </widget> - <widget class="SpinBox" row="18" column="0"> + <widget class="SpinBox" row="17" column="0"> <property name="name"> <cstring>iPan</cstring> </property> @@ -993,7 +937,7 @@ recording: <string>Change stereo position. Double-click on/off.</string> </property> </widget> - <widget class="QLayoutWidget" row="18" column="1"> + <widget class="QLayoutWidget" row="17" column="1"> <property name="name"> <cstring>panLayout</cstring> </property> @@ -1062,7 +1006,7 @@ recording: </spacer> </hbox> </widget> - <spacer row="19" column="0" rowspan="1" colspan="2"> + <spacer row="18" column="0" rowspan="1" colspan="2"> <property name="name"> <cstring>spacer5</cstring> </property> @@ -1107,8 +1051,6 @@ recording: <tabstops> <tabstop>iOutput</tabstop> <tabstop>iOutputChannel</tabstop> - <tabstop>iInput</tabstop> - <tabstop>iInputChannel</tabstop> <tabstop>iTransp</tabstop> <tabstop>iVerz</tabstop> <tabstop>iLen</tabstop> |