summaryrefslogtreecommitdiff
path: root/muse_qt4_evolution/muse/audio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse_qt4_evolution/muse/audio.cpp')
-rw-r--r--muse_qt4_evolution/muse/audio.cpp655
1 files changed, 655 insertions, 0 deletions
diff --git a/muse_qt4_evolution/muse/audio.cpp b/muse_qt4_evolution/muse/audio.cpp
new file mode 100644
index 00000000..1ad9eefc
--- /dev/null
+++ b/muse_qt4_evolution/muse/audio.cpp
@@ -0,0 +1,655 @@
+//=============================================================================
+// MusE
+// Linux Music Editor
+// $Id:$
+//
+// Copyright (C) 2002-2006 by Werner Schweer and others
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2.
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#include <fcntl.h>
+
+#include "al/al.h"
+#include "muse.h"
+#include "globals.h"
+#include "song.h"
+#include "audiodev.h"
+#include "audioprefetch.h"
+#include "audiowriteback.h"
+#include "audio.h"
+#include "sync.h"
+#include "midi.h"
+#include "gconfig.h"
+#include "al/sig.h"
+#include "al/tempo.h"
+#include "widgets/utils.h"
+#include "synth.h"
+#include "midioutport.h"
+#include "midiinport.h"
+#include "midictrl.h"
+#include "sync.h"
+
+extern bool initJackAudio();
+
+Audio* audio;
+AudioDriver* audioDriver; // current audio device in use
+
+const char* seqMsgList[] = {
+ "SEQM_ADD_TRACK",
+ "SEQM_REMOVE_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",
+/*10*/"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",
+ "AUDIO_ROUTEADD",
+ "AUDIO_ROUTEREMOVE",
+/*20*/"AUDIO_ADDPLUGIN",
+ "AUDIO_ADDMIDIPLUGIN",
+ "AUDIO_SET_SEG_SIZE",
+ "AUDIO_SET_CHANNELS",
+ "MS_PROCESS",
+ "MS_START",
+ "MS_STOP",
+ "MS_SET_RTC",
+ "SEQM_IDLE",
+ "SEQM_ADD_CTRL",
+ "SEQM_REMOVE_CTRL"
+ };
+
+const char* audioStates[] = {
+ "STOP", "START_PLAY", "PLAY", "LOOP1", "LOOP2", "SYNC", "PRECOUNT"
+ };
+
+
+//---------------------------------------------------------
+// Audio
+//---------------------------------------------------------
+
+Audio::Audio()
+ {
+ recording = false;
+ idle = false;
+ _freewheel = false;
+ _bounce = 0;
+ loopPassed = false;
+
+ _seqTime.pos.setType(AL::FRAMES);
+ startRecordPos.setType(AL::TICKS);
+ endRecordPos.setType(AL::TICKS);
+
+ //---------------------------------------------------
+ // establish pipes/sockets
+ //---------------------------------------------------
+
+ int filedes[2]; // 0 - reading 1 - writing
+ if (pipe(filedes) == -1) {
+ perror("creating pipe0");
+ fatalError("cannot create pipe0");
+ }
+ fromThreadFdw = filedes[1]; // blocking file descriptor
+ fromThreadFdr = filedes[0]; // non blocking file descriptor
+
+ int rv = fcntl(fromThreadFdw, F_SETFL, O_NONBLOCK);
+ if (rv == -1)
+ perror("set pipe O_NONBLOCK");
+
+ if (pipe(filedes) == -1) {
+ perror("creating pipe1");
+ fatalError("cannot create pipe1");
+ }
+ 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
+//---------------------------------------------------------
+
+bool Audio::start()
+ {
+ TrackList* tl = song->tracks();
+
+ _seqTime.curTickPos = 0;
+ _seqTime.nextTickPos = 0;
+ _seqTime.pos.setFrame(~0); // make sure seek is not optimized away
+
+ msg = 0;
+
+ //
+ // init marker for synchronous loop processing
+ //
+ lmark = song->lPos().frame();
+ rmark = song->rPos().frame();
+ state = STOP;
+ muse->setHeartBeat();
+
+ if (audioDriver) {
+ //
+ // allocate ports
+ //
+ for (iTrack i = tl->begin(); i != tl->end(); ++i)
+ (*i)->activate1();
+ seek(song->cpos());
+ process(segmentSize, STOP); // warm up caches; audio must be stopped
+ }
+ else {
+
+ // if audio device has disappeared it probably
+ // means jack has performed a shutdown
+ // try to restart and reconnect everything
+
+ if (false == initJackAudio()) {
+ //
+ // allocate ports, first resetting the old connection
+ //
+ InputList* itl = song->inputs();
+ for (iAudioInput i = itl->begin(); i != itl->end(); ++i) {
+ for (int x=0; x < (*i)->channels();x++) {
+ (*i)->setJackPort(Port(), x); // zero out the old connection
+ }
+ (*i)->activate1();
+ }
+
+ OutputList* otl = song->outputs();
+ for (iAudioOutput i = otl->begin(); i != otl->end(); ++i) {
+ for (int x=0; x < (*i)->channels();x++)
+ (*i)->setJackPort(Port(), x); // zero out the old connection
+ (*i)->activate1();
+ }
+ }
+ else {
+ printf("Failed to init audio!\n");
+ return false;
+ }
+ }
+ audioDriver->start(realTimePriority);
+ audioDriver->stopTransport();
+ return true;
+ }
+
+//---------------------------------------------------------
+// stop
+// stop audio processing
+// non realtime
+//---------------------------------------------------------
+
+void Audio::stop()
+ {
+#if 0
+ MidiOutPortList* opl = song->midiOutPorts();
+ for (iMidiOutPort i = opl->begin(); i != opl->end(); ++i)
+ (*i)->deactivate();
+ MidiInPortList* ipl = song->midiInPorts();
+ for (iMidiInPort i = ipl->begin(); i != ipl->end(); ++i)
+ (*i)->deactivate();
+#endif
+ if (audioDriver)
+ audioDriver->stop();
+ }
+
+//---------------------------------------------------------
+// sync
+// return true if sync is completed
+//---------------------------------------------------------
+
+bool Audio::sync(int jackState, unsigned frame)
+ {
+// printf("Audio::sync() state %s jackState %s frame 0x%x\n", audioStates[state], audioStates[jackState], frame);
+ 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);
+ if (!_freewheel)
+ done = audioPrefetch->seekDone();
+ if (s == START_PLAY)
+ state = START_PLAY;
+ }
+ else {
+ if (frame != _seqTime.pos.frame()) {
+ // seek during seek
+ seek(Pos(frame, AL::FRAMES));
+ }
+ done = audioPrefetch->seekDone();
+ }
+ }
+ return done;
+ }
+
+//---------------------------------------------------------
+// setFreewheel
+//---------------------------------------------------------
+
+void Audio::setFreewheel(bool val)
+ {
+// printf("JACK: freewheel callback %d\n", val);
+ _freewheel = val;
+ }
+
+//---------------------------------------------------------
+// shutdown
+//---------------------------------------------------------
+
+void Audio::shutdown()
+ {
+ audioState = AUDIO_STOP;
+printf("JACK: shutdown callback\n");
+ sendMsgToGui(MSG_JACK_SHUTDOWN);
+ }
+
+//---------------------------------------------------------
+// process
+// process one audio buffer at position "_seqTime._pos "
+// of size "frames"
+//---------------------------------------------------------
+
+void Audio::process(unsigned frames, int jackState)
+ {
+ _seqTime.lastFrameTime = audioDriver->frameTime();
+
+ for (iMidiOutPort i = song->midiOutPorts()->begin(); i != song->midiOutPorts()->end(); ++i)
+ audioDriver->startMidiCycle((*i)->jackPort(0));
+
+ //
+ // process messages from gui
+ //
+ if (msg) {
+ processMsg();
+ msg = 0; // dont process again
+ int rv = write(fromThreadFdw, "x", 1);
+ if (rv != 1) {
+ fprintf(stderr, "audio: write(%d) pipe failed: %s\n",
+ fromThreadFdw, strerror(errno));
+ }
+ }
+
+ if (jackState != state) {
+ if (state == START_PLAY && jackState == PLAY) {
+ startRolling();
+ if (_bounce)
+ sendMsgToGui(MSG_START_BOUNCE);
+ }
+ else if (state == LOOP2 && jackState == PLAY) {
+ Pos newPos(loopFrame, AL::FRAMES);
+ seek(newPos);
+ startRolling();
+ }
+ else if (isPlaying() && jackState == STOP)
+ stopRolling();
+ else if (state == START_PLAY && jackState == STOP) {
+ state = STOP;
+ updateController = true;
+ if (_bounce)
+ audioDriver->startTransport();
+ else
+ sendMsgToGui(MSG_STOP);
+ }
+ else if (state == STOP && jackState == PLAY)
+ startRolling();
+ }
+
+ if (idle || (state == START_PLAY)) {
+ // deliver silence
+ OutputList* ol = song->outputs();
+ for (iAudioOutput i = ol->begin(); i != ol->end(); ++i)
+ (*i)->silence(frames);
+ return;
+ }
+
+ if (state == PLAY) {
+ //
+ // clear prefetch FIFO if left/right locators
+ // have changed
+ //
+ unsigned llmark = song->lPos().frame();
+ unsigned rrmark = song->rPos().frame();
+
+ if (lmark != llmark || rmark != rrmark) {
+ //
+ // invalidate audio prefetch buffer
+ //
+ audioPrefetch->getFifo()->clear();
+ audioPrefetch->msgSeek(_seqTime.startFrame());
+ lmark = llmark;
+ rmark = rrmark;
+ }
+ }
+
+ if (isPlaying()) {
+ if (_bounce == 1 && _seqTime.pos >= song->rPos()) {
+ _bounce = 2;
+ sendMsgToGui(MSG_STOP_BOUNCE);
+ }
+ //
+ // check for end of song
+ //
+ if ((_seqTime.curTickPos >= song->len())
+ && !(song->record() || _bounce || song->loop())) {
+ audioDriver->stopTransport();
+ return;
+ }
+
+ //
+ // check for loop end
+ //
+ if (state == PLAY && song->loop() && !_bounce && !extSyncFlag) {
+ unsigned n = rmark - _seqTime.startFrame() - (3 * frames);
+ if (n < frames) {
+ // loop end in current cycle
+ // adjust loop start so we get exact loop len
+ if (n > lmark)
+ n = 0;
+ state = LOOP1;
+ loopFrame = lmark - n;
+ audioDriver->seekTransport(loopFrame);
+ }
+ }
+
+ Pos ppp(_seqTime.pos);
+ ppp += frames;
+ _seqTime.nextTickPos = ppp.tick();
+ }
+
+ //
+ // compute current controller values
+ // (automation)
+ //
+ TrackList* tl = song->tracks();
+ if (state == PLAY || updateController) {
+ updateController = false;
+ for (iTrack it = tl->begin(); it != tl->end(); ++it) {
+ if ((*it)->isMidiTrack())
+ continue;
+ AudioTrack* track = (AudioTrack*)(*it);
+ if (!track->autoRead())
+ continue;
+ CtrlList* cl = track->controller();
+ for (iCtrl i = cl->begin(); i != cl->end(); ++i) {
+ Ctrl* c = i->second;
+ float val = c->value(_seqTime.startFrame()).f;
+ if (val != c->curVal().f) {
+ c->setCurVal(val);
+ c->setChanged(true);
+ }
+ }
+ }
+ }
+
+ //-----------------------------------------
+ // process midi
+ //-----------------------------------------
+
+ SynthIList* sl = song->syntis();
+ {
+ MidiOutPortList* ol = song->midiOutPorts();
+ MidiInPortList* mil = song->midiInPorts();
+ MidiTrackList* mtl = song->midis();
+
+ for (iMidiInPort i = mil->begin(); i != mil->end(); ++i)
+ (*i)->beforeProcess();
+ for (iMidiTrack i = mtl->begin(); i != mtl->end(); ++i)
+ (*i)->processMidi(&_seqTime);
+ for (iMidiOutPort i = ol->begin(); i != ol->end(); ++i)
+ (*i)->processMidi(&_seqTime);
+ for (iSynthI i = sl->begin(); i != sl->end(); ++i)
+ (*i)->processMidi(&_seqTime);
+
+ for (iMidiInPort i = mil->begin(); i != mil->end(); ++i)
+ (*i)->afterProcess();
+ }
+
+ //-----------------------------------------
+ // process audio
+ //-----------------------------------------
+
+ GroupList* gl = song->groups();
+ InputList* il = song->inputs();
+ WaveTrackList* wl = song->waves();
+
+ for (iAudioInput i = il->begin(); i != il->end(); ++i)
+ (*i)->process();
+ for (iSynthI i = sl->begin(); i != sl->end(); ++i)
+ (*i)->process();
+
+ _curReadIndex = -1;
+ if (isPlaying() && !wl->empty()) {
+ Fifo1* fifo = audioPrefetch->getFifo();
+ if (fifo->count() == 0) {
+ printf("MusE::Audio: fifo underflow at 0x%x\n", _seqTime.curTickPos);
+ audioPrefetch->msgTick();
+ }
+ else {
+ bool msg = true;
+ do {
+ unsigned fifoPos = fifo->readPos();
+ if (fifoPos == _seqTime.startFrame()) {
+ _curReadIndex = fifo->readIndex();
+ break;
+ }
+ else {
+ if (msg) {
+ printf("Muse::Audio: wrong prefetch data 0x%x, expected 0x%x\n",
+ fifoPos, _seqTime.startFrame());
+ msg = false;
+ }
+ if (fifoPos > _seqTime.startFrame()) {
+ // discard whole prefetch buffer
+ seek(_seqTime.pos + frames);
+ break;
+ }
+ fifo->pop(); // discard buffer
+ }
+ } while (fifo->count());
+ }
+ }
+ for (iWaveTrack i = wl->begin(); i != wl->end(); ++i) {
+ if (song->bounceTrack != *i)
+ (*i)->process();
+ }
+ for (iAudioGroup i = gl->begin(); i != gl->end(); ++i)
+ (*i)->process();
+
+ OutputList* aol = song->outputs();
+ for (iAudioOutput i = aol->begin(); i != aol->end(); ++i)
+ (*i)->process();
+
+ if (_bounce == 1 && song->bounceTrack && song->bounceTrack->type() == Track::WAVE)
+ song->bounceTrack->process();
+
+ if (isPlaying()) {
+ if (!freewheel()) {
+ //
+ // consume prefetch buffer
+ //
+ if (_curReadIndex != -1) {
+ audioPrefetch->getFifo()->pop();
+ audioPrefetch->msgTick(); // wakeup prefetch thread
+ }
+ }
+ if (recording && (_bounce == 0 || _bounce == 1))
+ audioWriteback->trigger();
+ _seqTime.pos += frames;
+ _seqTime.curTickPos = _seqTime.nextTickPos;
+ }
+ }
+
+//---------------------------------------------------------
+// processMsg
+//---------------------------------------------------------
+
+void Audio::processMsg()
+ {
+// printf("---msg %d\n", msg->id);
+ switch(msg->id) {
+ case AUDIO_ROUTEADD:
+ addRoute(msg->route);
+ break;
+ case AUDIO_ROUTEREMOVE:
+ removeRoute(msg->route);
+ break;
+ case AUDIO_SET_CHANNELS:
+ msg->track->setChannels(msg->ival);
+ break;
+ case AUDIO_ADDPLUGIN:
+ ((AudioTrack*)msg->track)->addPlugin(msg->plugin, msg->ival,
+ msg->iival);
+ break;
+
+ case AUDIO_SET_SEG_SIZE:
+ segmentSize = msg->ival;
+ AL::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_INIT_DEVICES:
+ initMidiDevices();
+ break;
+ case SEQM_RESET_DEVICES:
+ resetMidiDevices();
+ break;
+ case SEQM_ADD_TEMPO:
+ case SEQM_REMOVE_TEMPO:
+ case SEQM_SET_GLOBAL_TEMPO:
+ case SEQM_SET_TEMPO:
+ song->processMsg(msg);
+ if (isPlaying())
+ _seqTime.pos.setTick(_seqTime.curTickPos);
+ break;
+
+ case SEQM_IDLE:
+ idle = msg->a;
+ break;
+
+ case AUDIO_ADDMIDIPLUGIN:
+ ((MidiTrackBase*)msg->track)->addPlugin(msg->mplugin, msg->ival);
+ break;
+ default:
+ song->processMsg(msg);
+ break;
+ }
+ }
+
+//---------------------------------------------------------
+// seek
+// - called before start play
+// - initiated from gui
+//---------------------------------------------------------
+
+void Audio::seek(const Pos& p)
+ {
+ _seqTime.pos.setFrame(p.frame());
+ _seqTime.curTickPos = _seqTime.pos.tick();
+ _seqTime.nextTickPos = _seqTime.curTickPos;
+ updateController = true;
+
+ loopPassed = true; // for record loop mode
+ if (state != LOOP2 && !freewheel())
+ audioPrefetch->msgSeek(_seqTime.pos.frame());
+
+ MidiOutPortList* ol = song->midiOutPorts();
+ for (iMidiOutPort i = ol->begin(); i != ol->end(); ++i)
+ (*i)->seek(_seqTime.curTickPos, _seqTime.pos.frame());
+ SynthIList* sl = song->syntis();
+ for (iSynthI i = sl->begin(); i != sl->end(); ++i)
+ (*i)->seek(_seqTime.curTickPos, _seqTime.pos.frame());
+ sendMsgToGui(MSG_SEEK);
+ }
+
+//---------------------------------------------------------
+// startRolling
+//---------------------------------------------------------
+
+void Audio::startRolling()
+ {
+ if (song->record()) {
+ startRecordPos = _seqTime.pos;
+ recording = true;
+ TrackList* tracks = song->tracks();
+ for (iTrack i = tracks->begin(); i != tracks->end(); ++i) {
+ if ((*i)->isMidiTrack())
+ continue;
+ ((AudioTrack*)(*i))->recEvents()->clear();
+ }
+ }
+ state = PLAY;
+ sendMsgToGui(MSG_PLAY);
+
+ MidiOutPortList* ol = song->midiOutPorts();
+ for (iMidiOutPort i = ol->begin(); i != ol->end(); ++i)
+ (*i)->start();
+ SynthIList* sl = song->syntis();
+ for (iSynthI i = sl->begin(); i != sl->end(); ++i)
+ (*i)->start();
+ }
+
+//---------------------------------------------------------
+// stopRolling
+// execution environment: realtime thread
+//---------------------------------------------------------
+
+void Audio::stopRolling()
+ {
+ state = STOP;
+ MidiOutPortList* ol = song->midiOutPorts();
+ for (iMidiOutPort i = ol->begin(); i != ol->end(); ++i)
+ (*i)->stop();
+ SynthIList* sl = song->syntis();
+ for (iSynthI i = sl->begin(); i != sl->end(); ++i)
+ (*i)->stop();
+
+ recording = false;
+ endRecordPos = _seqTime.pos;
+ _bounce = 0;
+ sendMsgToGui(MSG_STOP);
+ seek(_seqTime.pos);
+ }
+
+//---------------------------------------------------------
+// sendMsgToGui
+//---------------------------------------------------------
+
+void Audio::sendMsgToGui(char c)
+ {
+ write(sigFd, &c, 1);
+ }
+