//========================================================= // MusE // Linux Music Editor // $Id: audio.cpp,v 1.59.2.30 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "app.h" #include "song.h" #include "node.h" #include "audiodev.h" //#include "driver/audiodev.h" // p4.0.2 #include "mididev.h" #include "alsamidi.h" //#include "driver/alsamidi.h" // p4.0.2 #include "synth.h" #include "audioprefetch.h" #include "plugin.h" #include "audio.h" #include "wave.h" #include "midictrl.h" #include "midiseq.h" #include "sync.h" #include "midi.h" #include "event.h" #include "gconfig.h" #include "pos.h" #include "ticksynth.h" extern double curTime(); Audio* audio; AudioDevice* audioDevice; // current audio device in use // p3.3.25 extern unsigned int volatile midiExtSyncTicks; //static const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; //static const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; const char* seqMsgList[] = { "SEQM_ADD_TRACK", "SEQM_REMOVE_TRACK", "SEQM_CHANGE_TRACK", "SEQM_MOVE_TRACK", "SEQM_ADD_PART", "SEQM_REMOVE_PART", "SEQM_CHANGE_PART", "SEQM_ADD_EVENT", "SEQM_REMOVE_EVENT", "SEQM_CHANGE_EVENT", "SEQM_ADD_TEMPO", "SEQM_SET_TEMPO", "SEQM_REMOVE_TEMPO", "SEQM_ADD_SIG", "SEQM_REMOVE_SIG", "SEQM_SET_GLOBAL_TEMPO", "SEQM_UNDO", "SEQM_REDO", "SEQM_RESET_DEVICES", "SEQM_INIT_DEVICES", "SEQM_PANIC", "SEQM_MIDI_LOCAL_OFF", "SEQM_SET_MIDI_DEVICE", "SEQM_PLAY_MIDI_EVENT", "SEQM_SET_HW_CTRL_STATE", "SEQM_SET_HW_CTRL_STATES", "SEQM_SET_TRACK_OUT_PORT", "SEQM_SET_TRACK_OUT_CHAN", "SEQM_REMAP_PORT_DRUM_CTL_EVS", "SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS", "SEQM_SCAN_ALSA_MIDI_PORTS", "SEQM_SET_AUX", "SEQM_UPDATE_SOLO_STATES", "MIDI_SHOW_INSTR_GUI", "MIDI_SHOW_INSTR_NATIVE_GUI", "AUDIO_RECORD", "AUDIO_ROUTEADD", "AUDIO_ROUTEREMOVE", "AUDIO_REMOVEROUTES", //"AUDIO_VOL", "AUDIO_PAN", "AUDIO_ADDPLUGIN", "AUDIO_SET_SEG_SIZE", "AUDIO_SET_PREFADER", "AUDIO_SET_CHANNELS", //"AUDIO_SET_PLUGIN_CTRL_VAL", "AUDIO_SWAP_CONTROLLER_IDX", "AUDIO_CLEAR_CONTROLLER_EVENTS", "AUDIO_SEEK_PREV_AC_EVENT", "AUDIO_SEEK_NEXT_AC_EVENT", "AUDIO_ERASE_AC_EVENT", "AUDIO_ERASE_RANGE_AC_EVENTS", "AUDIO_ADD_AC_EVENT", "AUDIO_CHANGE_AC_EVENT", "AUDIO_SET_SOLO", "AUDIO_SET_SEND_METRONOME", "MS_PROCESS", "MS_STOP", "MS_SET_RTC", "MS_UPDATE_POLL_FD", "SEQM_IDLE", "SEQM_SEEK" }; const char* audioStates[] = { "STOP", "START_PLAY", "PLAY", "LOOP1", "LOOP2", "SYNC", "PRECOUNT" }; //--------------------------------------------------------- // Audio //--------------------------------------------------------- Audio::Audio() { _running = false; recording = false; idle = false; _freewheel = false; _bounce = false; //loopPassed = false; _loopFrame = 0; _loopCount = 0; _pos.setType(Pos::FRAMES); _pos.setFrame(0); curTickPos = 0; midiClick = 0; clickno = 0; clicksMeasure = 0; ticksBeat = 0; syncTime = 0.0; syncFrame = 0; frameOffset = 0; state = STOP; msg = 0; // Changed by Tim. p3.3.8 //startRecordPos.setType(Pos::TICKS); //endRecordPos.setType(Pos::TICKS); startRecordPos.setType(Pos::FRAMES); endRecordPos.setType(Pos::FRAMES); _audioMonitor = 0; _audioMaster = 0; //--------------------------------------------------- // establish pipes/sockets //--------------------------------------------------- int filedes[2]; // 0 - reading 1 - writing if (pipe(filedes) == -1) { perror("creating pipe0"); exit(-1); } fromThreadFdw = filedes[1]; fromThreadFdr = filedes[0]; int rv = fcntl(fromThreadFdw, F_SETFL, O_NONBLOCK); if (rv == -1) perror("set pipe O_NONBLOCK"); if (pipe(filedes) == -1) { perror("creating pipe1"); exit(-1); } sigFd = filedes[1]; QSocketNotifier* ss = new QSocketNotifier(filedes[0], QSocketNotifier::Read); song->connect(ss, SIGNAL(activated(int)), song, SLOT(seqSignal(int))); } //--------------------------------------------------------- // start // start audio processing //--------------------------------------------------------- extern bool initJackAudio(); bool Audio::start() { //process(segmentSize); // warm up caches state = STOP; _loopCount = 0; muse->setHeartBeat(); if (audioDevice) { // Added by Tim. p3.3.6 //_running = true; //audioDevice->start(); } else { if(false == initJackAudio()) { // Added by Tim. p3.3.6 //_running = true; InputList* itl = song->inputs(); for (iAudioInput i = itl->begin(); i != itl->end(); ++i) { //printf("reconnecting input %s\n", (*i)->name().ascii()); for (int x=0; x < (*i)->channels();x++) (*i)->setJackPort(x,0); (*i)->setName((*i)->name()); // restore jack connection } OutputList* otl = song->outputs(); for (iAudioOutput i = otl->begin(); i != otl->end(); ++i) { //printf("reconnecting output %s\n", (*i)->name().ascii()); for (int x=0; x < (*i)->channels();x++) (*i)->setJackPort(x,0); //printf("name=%s\n",(*i)->name().toLatin1()); (*i)->setName((*i)->name()); // restore jack connection } //audioDevice->start(); } else { printf("Failed to init audio!\n"); return false; } } audioDevice->start(realTimePriority); _running = true; // shall we really stop JACK transport and locate to // saved position? audioDevice->stopTransport(); //audioDevice->seekTransport(song->cPos().frame()); audioDevice->seekTransport(song->cPos()); return true; } //--------------------------------------------------------- // stop // stop audio processing //--------------------------------------------------------- void Audio::stop(bool) { if (audioDevice) audioDevice->stop(); _running = false; } //--------------------------------------------------------- // sync // return true if sync is completed //--------------------------------------------------------- bool Audio::sync(int jackState, unsigned frame) { // Changed by Tim. p3.3.24 /* bool done = true; if (state == LOOP1) state = LOOP2; else { if (_pos.frame() != frame) { Pos p(frame, false); seek(p); } state = State(jackState); if (!_freewheel) //done = audioPrefetch->seekDone; done = audioPrefetch->seekDone(); } return done; */ bool done = true; if (state == LOOP1) state = LOOP2; else { State s = State(jackState); // // STOP -> START_PLAY start rolling // STOP -> STOP seek in stop state // PLAY -> START_PLAY seek in play state if (state != START_PLAY) { //Pos p(frame, AL::FRAMES); // seek(p); Pos p(frame, false); seek(p); if (!_freewheel) done = audioPrefetch->seekDone(); if (s == START_PLAY) state = START_PLAY; } else { //if (frame != _seqTime.pos.frame()) { if (frame != _pos.frame()) { // seek during seek //seek(Pos(frame, AL::FRAMES)); seek(Pos(frame, false)); } done = audioPrefetch->seekDone(); } } return done; } //--------------------------------------------------------- // setFreewheel //--------------------------------------------------------- void Audio::setFreewheel(bool val) { // printf("JACK: freewheel callback %d\n", val); _freewheel = val; } //--------------------------------------------------------- // shutdown //--------------------------------------------------------- void Audio::shutdown() { _running = false; printf("Audio::shutdown()\n"); write(sigFd, "S", 1); } //--------------------------------------------------------- // process // process one audio buffer at position "_pos " // of size "frames" //--------------------------------------------------------- void Audio::process(unsigned frames) { // Disabled by Tim. p3.3.22 // extern int watchAudio; // ++watchAudio; // make a simple watchdog happy if (!checkAudioDevice()) return; if (msg) { processMsg(msg); int sn = msg->serialNo; msg = 0; // dont process again int rv = write(fromThreadFdw, &sn, sizeof(int)); if (rv != sizeof(int)) { fprintf(stderr, "audio: write(%d) pipe failed: %s\n", fromThreadFdw, strerror(errno)); } } OutputList* ol = song->outputs(); if (idle) { // deliver no audio for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->silence(frames); return; } int jackState = audioDevice->getState(); //if(debugMsg) // printf("Audio::process Current state:%s jackState:%s\n", audioStates[state], audioStates[jackState]); if (state == START_PLAY && jackState == PLAY) { _loopCount = 0; startRolling(); if (_bounce) write(sigFd, "f", 1); } else if (state == LOOP2 && jackState == PLAY) { ++_loopCount; // Number of times we have looped so far Pos newPos(_loopFrame, false); seek(newPos); startRolling(); } else if (isPlaying() && jackState == STOP) { // p3.3.43 Make sure to stop bounce and freewheel mode, for example if user presses stop // in QJackCtl before right-hand marker is reached (which is handled below). //printf("Audio::process isPlaying() && jackState == STOP\n"); //if (_bounce) //{ //printf(" stopping bounce...\n"); // _bounce = false; // write(sigFd, "F", 1); //} stopRolling(); } else if (state == START_PLAY && jackState == STOP) { state = STOP; if (_bounce) { audioDevice->startTransport(); } else write(sigFd, "3", 1); // abort rolling } else if (state == STOP && jackState == PLAY) { _loopCount = 0; startRolling(); } else if (state == LOOP1 && jackState == PLAY) ; // treat as play else if (state == LOOP2 && jackState == START_PLAY) { ; // sync cycle } else if (state != jackState) printf("JACK: state transition %s -> %s ?\n", audioStates[state], audioStates[jackState]); // printf("p %s %s %d\n", audioStates[jackState], audioStates[state], _pos.frame()); // // clear aux send buffers // AuxList* al = song->auxs(); for (unsigned i = 0; i < al->size(); ++i) { AudioAux* a = (AudioAux*)((*al)[i]); float** dst = a->sendBuffer(); for (int ch = 0; ch < a->channels(); ++ch) memset(dst[ch], 0, sizeof(float) * segmentSize); } for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->processInit(frames); int samplePos = _pos.frame(); int offset = 0; // buffer offset in audio buffers if (isPlaying()) { if (!freewheel()) audioPrefetch->msgTick(); if (_bounce && _pos >= song->rPos()) { _bounce = false; write(sigFd, "F", 1); return; } // // check for end of song // if ((curTickPos >= song->len()) && !(song->record() || _bounce || song->loop())) { //if(debugMsg) // printf("Audio::process curTickPos >= song->len\n"); audioDevice->stopTransport(); return; } // // check for loop end // if (state == PLAY && song->loop() && !_bounce && !extSyncFlag.value()) { const Pos& loop = song->rPos(); unsigned n = loop.frame() - samplePos - (3 * frames); if (n < frames) { // loop end in current cycle unsigned lpos = song->lPos().frame(); // adjust loop start so we get exact loop len if (n > lpos) n = 0; state = LOOP1; _loopFrame = lpos - n; // clear sustain for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &midiPorts[i]; for (int ch = 0; ch < MIDI_CHANNELS; ++ch) { if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { if (mp->device()!=NULL) { //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); // may cause problems, called from audio thread mp->device()->putEvent(ev); } } } } //audioDevice->seekTransport(_loopFrame); Pos lp(_loopFrame, false); audioDevice->seekTransport(lp); // printf(" process: seek to %d, end %d\n", _loopFrame, loop.frame()); } } // p3.3.25 if(extSyncFlag.value()) { nextTickPos = curTickPos + midiExtSyncTicks; // Probably not good - interfere with midi thread. midiExtSyncTicks = 0; } else { Pos ppp(_pos); ppp += frames; nextTickPos = ppp.tick(); } } // // resync with audio interface // syncFrame = audioDevice->framePos(); syncTime = curTime(); frameOffset = syncFrame - samplePos; //printf("Audio::process calling process1:\n"); process1(samplePos, offset, frames); for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->processWrite(); if (isPlaying()) { _pos += frames; curTickPos = nextTickPos; } } //--------------------------------------------------------- // process1 //--------------------------------------------------------- void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) { if (midiSeqRunning) { processMidi(); } //midiSeq->msgProcess(); // // process not connected tracks // to animate meter display // TrackList* tl = song->tracks(); AudioTrack* track; int channels; for(ciTrack it = tl->begin(); it != tl->end(); ++it) { if((*it)->isMidiTrack()) continue; track = (AudioTrack*)(*it); // Added by T356. // For audio track types, synths etc. which need some kind of non-audio // (but possibly audio-affecting) processing always, even if their output path // is ultimately unconnected. // Example: A fluidsynth instance whose output path ultimately led to nowhere // would not allow us to load a font. Since process() was driven by audio output, // in this case there was nothing driving the process() function which responds to // such gui commands. So I separated the events processing from process(), into this. // It should be used for things like midi events, gui events etc. - things which need to // be done BEFORE all the AudioOutput::process() are called below. That does NOT include // audio processing, because THAT is done at the very end of this routine. // This will also reset the track's processed flag. track->preProcessAlways(); // Removed by T356 /* if (track->noOutRoute() && !track->noInRoute() && track->type() != Track::AUDIO_AUX && track->type() != Track::AUDIO_OUTPUT) { channels = track->channels(); float* buffer[channels]; float data[frames * channels]; for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; track->copyData(samplePos, channels, frames, buffer); } */ } // Pre-process the metronome. ((AudioTrack*)metronome)->preProcessAlways(); OutputList* ol = song->outputs(); for (ciAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->process(samplePos, offset, frames); // Removed by T356 /* AuxList* auxl = song->auxs(); for (ciAudioAux ia = auxl->begin(); ia != auxl->end(); ++ia) { track = (AudioTrack*)(*ia); if (track->noOutRoute()) { channels = track->channels(); float* buffer[channels]; float data[frames * channels]; for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; track->copyData(samplePos, channels, frames, buffer); } } */ // Added by T356. // Were ANY tracks unprocessed as a result of processing all the AudioOutputs, above? // Not just unconnected ones, as previously done, but ones whose output path ultimately leads nowhere. // Those tracks were missed, until this fix. // Do them now. This will animate meters, and 'quietly' process some audio which needs to be done - // for example synths really need to be processed, 'quietly' or not, otherwise the next time // processing is 'turned on', if there was a backlog of events while it was off, then they all happen at once. for(ciTrack it = tl->begin(); it != tl->end(); ++it) { if((*it)->isMidiTrack()) continue; track = (AudioTrack*)(*it); // Ignore unprocessed tracks which have an output route, because they will be processed by // whatever track(s) they are routed to. if(!track->processed() && track->noOutRoute() && (track->type() != Track::AUDIO_OUTPUT)) { channels = track->channels(); // Just a dummy buffer. float* buffer[channels]; float data[frames * channels]; for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); // p3.3.38 //track->copyData(samplePos, channels, frames, buffer); track->copyData(samplePos, channels, -1, -1, frames, buffer); } } } //--------------------------------------------------------- // processMsg //--------------------------------------------------------- void Audio::processMsg(AudioMsg* msg) { switch(msg->id) { case AUDIO_RECORD: msg->snode->setRecordFlag2(msg->ival); break; case AUDIO_ROUTEADD: addRoute(msg->sroute, msg->droute); break; case AUDIO_ROUTEREMOVE: removeRoute(msg->sroute, msg->droute); break; case AUDIO_REMOVEROUTES: // p3.3.55 removeAllRoutes(msg->sroute, msg->droute); break; //case AUDIO_VOL: // msg->snode->setVolume(msg->dval); // break; //case AUDIO_PAN: // msg->snode->setPan(msg->dval); // break; case SEQM_SET_AUX: msg->snode->setAuxSend(msg->ival, msg->dval); break; case AUDIO_SET_PREFADER: msg->snode->setPrefader(msg->ival); break; case AUDIO_SET_CHANNELS: msg->snode->setChannels(msg->ival); break; case AUDIO_ADDPLUGIN: msg->snode->addPlugin(msg->plugin, msg->ival); break; //case AUDIO_SET_PLUGIN_CTRL_VAL: //msg->plugin->track()->setPluginCtrlVal(msg->ival, msg->dval); // p3.3.43 // msg->snode->setPluginCtrlVal(msg->ival, msg->dval); // break; case AUDIO_SWAP_CONTROLLER_IDX: msg->snode->swapControllerIDX(msg->a, msg->b); break; case AUDIO_CLEAR_CONTROLLER_EVENTS: msg->snode->clearControllerEvents(msg->ival); break; case AUDIO_SEEK_PREV_AC_EVENT: msg->snode->seekPrevACEvent(msg->ival); break; case AUDIO_SEEK_NEXT_AC_EVENT: msg->snode->seekNextACEvent(msg->ival); break; case AUDIO_ERASE_AC_EVENT: msg->snode->eraseACEvent(msg->ival, msg->a); break; case AUDIO_ERASE_RANGE_AC_EVENTS: msg->snode->eraseRangeACEvents(msg->ival, msg->a, msg->b); break; case AUDIO_ADD_AC_EVENT: msg->snode->addACEvent(msg->ival, msg->a, msg->dval); break; case AUDIO_CHANGE_AC_EVENT: msg->snode->changeACEvent(msg->ival, msg->a, msg->b, msg->dval); break; case AUDIO_SET_SOLO: msg->track->setSolo((bool)msg->ival); break; case AUDIO_SET_SEND_METRONOME: msg->snode->setSendMetronome((bool)msg->ival); break; case AUDIO_SET_SEG_SIZE: segmentSize = msg->ival; sampleRate = msg->iival; #if 0 //TODO audioOutput.segmentSizeChanged(); for (int i = 0; i < mixerGroups; ++i) audioGroups[i].segmentSizeChanged(); for (iSynthI ii = synthiInstances.begin(); ii != synthiInstances.end();++ii) (*ii)->segmentSizeChanged(); #endif break; case SEQM_RESET_DEVICES: //printf("Audio::processMsg SEQM_RESET_DEVICES\n"); for (int i = 0; i < MIDI_PORTS; ++i) { if(!midiPorts[i].device()) continue; // p4.0.15 midiPorts[i].instrument()->reset(i, song->mtype()); } break; case SEQM_INIT_DEVICES: initDevices(); break; case SEQM_MIDI_LOCAL_OFF: sendLocalOff(); break; case SEQM_PANIC: panic(); break; case SEQM_PLAY_MIDI_EVENT: { MidiPlayEvent* ev = (MidiPlayEvent*)(msg->p1); midiPorts[ev->port()].sendEvent(*ev); // Record?? } break; case SEQM_SET_HW_CTRL_STATE: { MidiPort* port = (MidiPort*)(msg->p1); port->setHwCtrlState(msg->a, msg->b, msg->c); } break; case SEQM_SET_HW_CTRL_STATES: { MidiPort* port = (MidiPort*)(msg->p1); port->setHwCtrlStates(msg->a, msg->b, msg->c, msg->ival); } break; case SEQM_SCAN_ALSA_MIDI_PORTS: alsaScanMidiPorts(); break; case MIDI_SHOW_INSTR_GUI: midiSeq->msgUpdatePollFd(); break; case MIDI_SHOW_INSTR_NATIVE_GUI: // p4.0.20 midiSeq->msgUpdatePollFd(); break; case SEQM_ADD_TEMPO: case SEQM_REMOVE_TEMPO: case SEQM_SET_GLOBAL_TEMPO: case SEQM_SET_TEMPO: song->processMsg(msg); if (isPlaying()) { if (!checkAudioDevice()) return; _pos.setTick(curTickPos); int samplePos = _pos.frame(); syncFrame = audioDevice->framePos(); syncTime = curTime(); frameOffset = syncFrame - samplePos; } break; case SEQM_ADD_TRACK: case SEQM_REMOVE_TRACK: case SEQM_CHANGE_TRACK: case SEQM_ADD_PART: case SEQM_REMOVE_PART: case SEQM_CHANGE_PART: case SEQM_SET_TRACK_OUT_CHAN: case SEQM_SET_TRACK_OUT_PORT: case SEQM_REMAP_PORT_DRUM_CTL_EVS: case SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS: midiSeq->sendMsg(msg); break; case SEQM_IDLE: idle = msg->a; midiSeq->sendMsg(msg); break; default: song->processMsg(msg); break; } } //--------------------------------------------------------- // seek // - called before start play // - initiated from gui //--------------------------------------------------------- void Audio::seek(const Pos& p) { if (_pos == p) { if(debugMsg) printf("Audio::seek already there\n"); return; } // p3.3.23 //printf("Audio::seek frame:%d\n", p.frame()); _pos = p; if (!checkAudioDevice()) return; syncFrame = audioDevice->framePos(); frameOffset = syncFrame - _pos.frame(); curTickPos = _pos.tick(); // p4.0.22 // Tell midi thread to tell ALSA devices to handle seek. midiSeq->msgSeek(); // We are in the audio thread. Directly seek Jack midi devices. for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { MidiDevice* md = *i; if(md->deviceType() == MidiDevice::JACK_MIDI) md->handleSeek(); } // Moved into MidiDevice::handleSeek #if 0 // p3.3.31 // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!extSyncFlag.value()) { for(int port = 0; port < MIDI_PORTS; ++port) { MidiPort* mp = &midiPorts[port]; MidiDevice* dev = mp->device(); //if(!dev || !mp->syncInfo().MCOut()) if(!dev || !mp->syncInfo().MRTOut()) continue; // Added by T356: Shall we check for device write open flag to see if it's ok to send?... // This means obey what the user has chosen for read/write in the midi port config dialog, // which already takes into account whether the device is writable or not. //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // continue; //int port = dev->midiPort(); // By checking for no port here (-1), (and out of bounds), it means // the device must be assigned to a port for these MMC commands to be sent. // Without this check, interesting sync things can be done by the user without ever // assigning any devices to ports ! //if(port < 0 || port > MIDI_PORTS) //if(port < -1 || port > MIDI_PORTS) // continue; int beat = (curTickPos * 4) / config.division; bool isPlaying=false; if(state == PLAY) isPlaying = true; mp->sendStop(); mp->sendSongpos(beat); if(isPlaying) mp->sendContinue(); } } #endif /* if(genMCSync) { for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { MidiDevice* dev = (*imd); if(!dev->syncInfo().MCOut()) continue; // Added by T356: Shall we check for device write open flag to see if it's ok to send?... // This means obey what the user has chosen for read/write in the midi port config dialog, // which already takes into account whether the device is writable or not. //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // continue; int port = dev->midiPort(); // By checking for no port here (-1), (and out of bounds), it means // the device must be assigned to a port for these MMC commands to be sent. // Without this check, interesting sync things can be done by the user without ever // assigning any devices to ports ! //if(port < 0 || port > MIDI_PORTS) if(port < -1 || port > MIDI_PORTS) continue; int beat = (curTickPos * 4) / config.division; bool isPlaying=false; if(state == PLAY) isPlaying = true; if(port == -1) // Send straight to the device... Copied from MidiPort. { MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); dev->putEvent(event); event.setType(ME_SONGPOS); event.setA(beat); dev->putEvent(event); if(isPlaying) { event.setType(ME_CONTINUE); event.setA(0); dev->putEvent(event); } } else // Go through the port... { MidiPort* mp = &midiPorts[port]; mp->sendStop(); mp->sendSongpos(beat); if(isPlaying) mp->sendContinue(); } } } */ //loopPassed = true; // for record loop mode if (state != LOOP2 && !freewheel()) { // Changed by T356 08/17/08. We need to force prefetch to update, // to ensure the most recent data. Things can happen to a part // before play is pressed - such as part muting, part moving etc. // Without a force, the wrong data was being played. //audioPrefetch->msgSeek(_pos.frame()); audioPrefetch->msgSeek(_pos.frame(), true); } write(sigFd, "G", 1); // signal seek to gui } //--------------------------------------------------------- // writeTick // called from audio prefetch thread context // write another buffer to soundfile //--------------------------------------------------------- void Audio::writeTick() { AudioOutput* ao = song->bounceOutput; if(ao && song->outputs()->find(ao) != song->outputs()->end()) { if(ao->recordFlag()) ao->record(); } WaveTrackList* tl = song->waves(); for (iWaveTrack t = tl->begin(); t != tl->end(); ++t) { WaveTrack* track = *t; if (track->recordFlag()) track->record(); } } //--------------------------------------------------------- // startRolling //--------------------------------------------------------- void Audio::startRolling() { // Changed by Tim. p3.3.8 //startRecordPos = _pos; if (debugMsg) printf("startRolling - loopCount=%d, _pos=%d\n", _loopCount, _pos.tick()); if(_loopCount == 0) { startRecordPos = _pos; } if (song->record()) { recording = true; TrackList* tracks = song->tracks(); for (iTrack i = tracks->begin(); i != tracks->end(); ++i) { if ((*i)->isMidiTrack()) continue; if ((*i)->type() == Track::WAVE) ((WaveTrack*)(*i))->resetMeter(); } } state = PLAY; write(sigFd, "1", 1); // Play // p3.3.31 // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!extSyncFlag.value()) { // Changed by Tim. p3.3.6 //if (genMMC) // midiPorts[txSyncPort].sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); //if (genMCSync) { // if (curTickPos) // midiPorts[txSyncPort].sendContinue(); // else // midiPorts[txSyncPort].sendStart(); // } for(int port = 0; port < MIDI_PORTS; ++port) { MidiPort* mp = &midiPorts[port]; MidiDevice* dev = mp->device(); if(!dev) continue; // Shall we check open flags? //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // continue; MidiSyncInfo& si = mp->syncInfo(); //if(genMMC && si.MMCOut()) if(si.MMCOut()) //mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); mp->sendMMCDeferredPlay(); //if(genMCSync && si.MCOut()) //if(si.MCOut()) if(si.MRTOut()) { if(curTickPos) mp->sendContinue(); else mp->sendStart(); } } } /* for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { MidiDevice* dev = (*imd); // Shall we check open flags? //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // continue; int port = dev->midiPort(); // Without this -1 check, interesting sync things can be done by the user without ever // assigning any devices to ports ! //if(port < 0 || port > MIDI_PORTS) if(port < -1 || port > MIDI_PORTS) continue; MidiSyncInfo& si = dev->syncInfo(); if(port == -1) // Send straight to the device... Copied from MidiPort. { if(genMMC && si.MMCOut()) { MidiPlayEvent event(0, 0, ME_SYSEX, mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); dev->putEvent(event); } if(genMCSync && si.MCOut()) { if(curTickPos) { MidiPlayEvent event(0, 0, 0, ME_CONTINUE, 0, 0); dev->putEvent(event); } else { MidiPlayEvent event(0, 0, 0, ME_START, 0, 0); dev->putEvent(event); } } } else // Go through the port... { MidiPort* mp = &midiPorts[port]; if(genMMC && si.MMCOut()) mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); if(genMCSync && si.MCOut()) { if(curTickPos) mp->sendContinue(); else mp->sendStart(); } } } */ if (precountEnableFlag && song->click() && !extSyncFlag.value() && song->record()) { #if 0 state = PRECOUNT; int z, n; if (precountFromMastertrackFlag) AL::sigmap.timesig(playTickPos, z, n); else { z = precountSigZ; n = precountSigN; } clickno = z * preMeasures; clicksMeasure = z; ticksBeat = (division * 4)/n; #endif } else { // // compute next midi metronome click position // int bar, beat; unsigned tick; AL::sigmap.tickValues(curTickPos, &bar, &beat, &tick); if (tick) beat += 1; midiClick = AL::sigmap.bar2tick(bar, beat, 0); } // reenable sustain for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &midiPorts[i]; for (int ch = 0; ch < MIDI_CHANNELS; ++ch) { if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { if(mp->device() != NULL) { //printf("send enable sustain!!!!!!!! port %d ch %d\n", i,ch); MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 127); // may cause problems, called from audio thread mp->device()->playEvents()->add(ev); } } } } //tempomap.clearExtTempoList(); } //--------------------------------------------------------- // stopRolling //--------------------------------------------------------- void Audio::stopRolling() { //if(debugMsg) // printf("Audio::stopRolling state %s\n", audioStates[state]); state = STOP; //playStateExt = false; // not playing // Moved here from MidiSeq::processStop() p4.0.22 // p4.0.22 // Tell midi thread to clear ALSA device notes and stop stuck notes. midiSeq->msgStop(); // We are in the audio thread. Directly clear Jack midi device notes and stop stuck notes. for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; if(md->deviceType() == MidiDevice::JACK_MIDI) md->handleStop(); } // Moved into MidiDevice::handleStop() // p4.0.22 #if 0 //TODO //--------------------------------------------------- // reset sustain //--------------------------------------------------- // clear sustain for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &midiPorts[i]; for (int ch = 0; ch < MIDI_CHANNELS; ++ch) { if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { if(mp->device()!=NULL) { //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); // may cause problems, called from audio thread mp->device()->putEvent(ev); } } } } #endif // Moved into MidiDevice::handleStop() // p4.0.22 #if 0 // p3.3.31 // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!extSyncFlag.value()) { // Changed by Tim. p3.3.6 //MidiPort* syncPort = &midiPorts[txSyncPort]; //if (genMMC) { // unsigned char mmcPos[] = { // 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, // 0, 0, 0, 0, 0 // }; // int frame = tempomap.tick2frame(curTickPos); // MTC mtc(double(frame) / double(sampleRate)); // mmcPos[6] = mtc.h() | (mtcType << 5); // mmcPos[7] = mtc.m(); // mmcPos[8] = mtc.s(); // mmcPos[9] = mtc.f(); // mmcPos[10] = mtc.sf(); // syncPort->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); // syncPort->sendSysex(mmcPos, sizeof(mmcPos)); // } //if (genMCSync) { // Midi Clock // send STOP and // "set song position pointer" // syncPort->sendStop(); // syncPort->sendSongpos(curTickPos * 4 / config.division); // } for(int port = 0; port < MIDI_PORTS; ++port) { MidiPort* mp = &midiPorts[port]; MidiDevice* dev = mp->device(); if(!dev) continue; // Shall we check open flags? //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // continue; MidiSyncInfo& si = mp->syncInfo(); //if(genMMC && si.MMCOut()) if(si.MMCOut()) { //unsigned char mmcPos[] = { // 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, // 0, 0, 0, 0, 0 // }; // p3.3.31 /* int frame = tempomap.tick2frame(curTickPos); MTC mtc(double(frame) / double(sampleRate)); */ //mmcPos[6] = mtc.h() | (mtcType << 5); //mmcPos[7] = mtc.m(); //mmcPos[8] = mtc.s(); //mmcPos[9] = mtc.f(); //mmcPos[10] = mtc.sf(); //mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); mp->sendMMCStop(); //mp->sendSysex(mmcPos, sizeof(mmcPos)); // p3.3.31 // Added check of option send continue not start. // Hmm, is this required? Seems to make other devices unhappy. /* if(!si.sendContNotStart()) mp->sendMMCLocate(mtc.h() | (mtcType << 5), mtc.m(), mtc.s(), mtc.f(), mtc.sf()); */ } //if(genMCSync && si.MCOut()) // Midi Clock //if(si.MCOut()) // Midi Clock if(si.MRTOut()) // { // send STOP and // "set song position pointer" mp->sendStop(); // p3.3.31 // Added check of option send continue not start. // Hmm, is this required? Seems to make other devices unhappy. /* if(!si.sendContNotStart()) mp->sendSongpos(curTickPos * 4 / config.division); */ } } } #endif /* for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { MidiDevice* dev = (*imd); // Shall we check open flags? //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // continue; int port = dev->midiPort(); // Without this -1 check, interesting sync things can be done by the user without ever // assigning any devices to ports ! //if(port < 0 || port > MIDI_PORTS) if(port < -1 || port > MIDI_PORTS) continue; MidiSyncInfo& si = dev->syncInfo(); MidiPort* mp = 0; if(port != -1) mp = &midiPorts[port]; if(genMMC && si.MMCOut()) { unsigned char mmcPos[] = { 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, 0, 0, 0, 0, 0 }; int frame = tempomap.tick2frame(curTickPos); MTC mtc(double(frame) / double(sampleRate)); mmcPos[6] = mtc.h() | (mtcType << 5); mmcPos[7] = mtc.m(); mmcPos[8] = mtc.s(); mmcPos[9] = mtc.f(); mmcPos[10] = mtc.sf(); if(mp) // Go through the port... { mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); mp->sendSysex(mmcPos, sizeof(mmcPos)); } else // Send straight to the device... Copied from MidiPort. { MidiPlayEvent event(0, 0, ME_SYSEX, mmcStopMsg, sizeof(mmcStopMsg)); dev->putEvent(event); event.setData(mmcPos, sizeof(mmcPos)); dev->putEvent(event); } } if(genMCSync && si.MCOut()) // Midi Clock { // send STOP and // "set song position pointer" if(mp) // Go through the port... { mp->sendStop(); mp->sendSongpos(curTickPos * 4 / config.division); } else // Send straight to the device... Copied from MidiPort. { MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); dev->putEvent(event); event.setType(ME_SONGPOS); event.setA(curTickPos * 4 / config.division); dev->putEvent(event); } } } */ WaveTrackList* tracks = song->waves(); for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) { WaveTrack* track = *i; track->resetMeter(); } recording = false; endRecordPos = _pos; write(sigFd, "0", 1); // STOP } //--------------------------------------------------------- // recordStop // execution environment: gui thread //--------------------------------------------------------- void Audio::recordStop() { if (debugMsg) printf("recordStop - startRecordPos=%d\n", startRecordPos.tick()); audio->msgIdle(true); // gain access to all data structures song->startUndo(); WaveTrackList* wl = song->waves(); for (iWaveTrack it = wl->begin(); it != wl->end(); ++it) { WaveTrack* track = *it; if (track->recordFlag() || song->bounceTrack == track) { song->cmdAddRecordedWave(track, startRecordPos, endRecordPos); // The track's _recFile pointer may have been kept and turned // into a SndFileR and added to a new part. // Or _recFile may have been discarded (no new recorded part created). // Regardless, we are done with the pointer itself. Set to zero so // song->setRecordFlag knows about it... track->setRecFile(0); // flush out the old file song->setRecordFlag(track, false); // //track->setRecordFlag1(true); // and re-arm the track here //song->setRecordFlag(track, true); // here } } MidiTrackList* ml = song->midis(); for (iMidiTrack it = ml->begin(); it != ml->end(); ++it) { MidiTrack* mt = *it; MPEventList* mpel = mt->mpevents(); EventList* el = mt->events(); //--------------------------------------------------- // resolve NoteOff events, Controller etc. //--------------------------------------------------- //buildMidiEventList(el, mpel, mt, config.division, true); // Do SysexMeta. Do loops. buildMidiEventList(el, mpel, mt, config.division, true, true); song->cmdAddRecordedEvents(mt, el, startRecordPos.tick()); el->clear(); mpel->clear(); } // // bounce to file operates on the only // selected output port // AudioOutput* ao = song->bounceOutput; if(ao && song->outputs()->find(ao) != song->outputs()->end()) { if(ao->recordFlag()) { song->bounceOutput = 0; SndFile* sf = ao->recFile(); if (sf) delete sf; // close ao->setRecFile(0); ao->setRecordFlag1(false); msgSetRecord(ao, false); } } audio->msgIdle(false); song->endUndo(0); song->setRecord(false); } //--------------------------------------------------------- // curFrame // extrapolates current play frame on syncTime/syncFrame //--------------------------------------------------------- unsigned int Audio::curFrame() const { return lrint((curTime() - syncTime) * sampleRate) + syncFrame; } //--------------------------------------------------------- // timestamp //--------------------------------------------------------- int Audio::timestamp() const { int t = curFrame() - frameOffset; return t; } //--------------------------------------------------------- // sendMsgToGui //--------------------------------------------------------- void Audio::sendMsgToGui(char c) { write(sigFd, &c, 1); }