summaryrefslogtreecommitdiff
path: root/muse2/muse/remote/pyapi.cpp
diff options
context:
space:
mode:
authorRobert Jonsson <spamatica@gmail.com>2010-10-13 19:34:22 +0000
committerRobert Jonsson <spamatica@gmail.com>2010-10-13 19:34:22 +0000
commit8a2c2824a59d7644e13bc52c9a0ecbd641f21f95 (patch)
tree064ad3f2bf8daab0ad27b128abd86a9bbdb1e496 /muse2/muse/remote/pyapi.cpp
parenta27706d9629e8b592cca4659f865b70adef24e6d (diff)
new branch muse2, first checkin
Diffstat (limited to 'muse2/muse/remote/pyapi.cpp')
-rw-r--r--muse2/muse/remote/pyapi.cpp1142
1 files changed, 1142 insertions, 0 deletions
diff --git a/muse2/muse/remote/pyapi.cpp b/muse2/muse/remote/pyapi.cpp
new file mode 100644
index 00000000..e71b8a18
--- /dev/null
+++ b/muse2/muse/remote/pyapi.cpp
@@ -0,0 +1,1142 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net)
+//=========================================================
+#include <Python.h>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <pthread.h>
+
+#include <qobject.h>
+#include <qapplication.h>
+#include <qevent.h>
+
+#include "pyapi.h"
+#include "song.h"
+#include "tempo.h"
+#include "track.h"
+#include "audio.h"
+#include "gconfig.h"
+#include "midictrl.h"
+#include "midiport.h"
+#include "plugin.h"
+#include "midi.h"
+#include "app.h"
+
+// Steals ref: PyList_SetItem, PyTuple_SetItem
+using namespace std;
+
+static pthread_t pyapiThread;
+//------------------------------------------------------------
+QPybridgeEvent::QPybridgeEvent(QPybridgeEvent::EventType _type, int _p1, int _p2)
+ :QEvent(QEvent::User),
+ type(_type),
+ p1(_p1),
+ p2(_p2)
+{
+}
+//------------------------------------------------------------
+// Get current position
+//------------------------------------------------------------
+PyObject* getCPos(PyObject*, PyObject*)
+{
+ return Py_BuildValue("i", song->cpos());
+}
+//------------------------------------------------------------
+// Get position of left locator
+//------------------------------------------------------------
+PyObject* getLPos(PyObject*, PyObject*)
+{
+ return Py_BuildValue("i", song->lpos());
+}
+//------------------------------------------------------------
+// Get position of right locator
+//------------------------------------------------------------
+PyObject* getRPos(PyObject*, PyObject*)
+{
+ return Py_BuildValue("i", song->rpos());
+}
+//------------------------------------------------------------
+// Start playing from current position
+//------------------------------------------------------------
+PyObject* startPlay(PyObject*, PyObject*)
+{
+ //song->setPlay(true);
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETPLAY);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// Stop playing
+//------------------------------------------------------------
+PyObject* stopPlay(PyObject*, PyObject*)
+{
+ //song->setStop(true);
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETSTOP);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// Rewind to start
+//------------------------------------------------------------
+PyObject* rewindStart(PyObject*, PyObject*)
+{
+ //song->rewindStart();
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_REWIND);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// Get tempo at specific position
+//------------------------------------------------------------
+PyObject* getTempo(PyObject*, PyObject* args)
+{
+ int tick;
+ if (!PyArg_ParseTuple(args, "i", &tick)) {
+ return Py_BuildValue("i", 1000);
+ }
+
+ int tempovalue = tempomap.tempo(tick);
+ return Py_BuildValue("i", tempovalue);
+}
+//------------------------------------------------------------
+// Get track names
+//------------------------------------------------------------
+PyObject* getTrackNames(PyObject*, PyObject*)
+{
+ TrackList* tracks = song->tracks();
+ PyObject* res = Py_BuildValue("[]");
+ for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) {
+ Track* track = *t;
+ PyObject* ptrackname = Py_BuildValue("s", track->name().latin1());
+ PyList_Append(res, ptrackname);
+ Py_DECREF(ptrackname);
+ }
+
+ return res;
+}
+//------------------------------------------------------------
+// Find part by serial nr
+//------------------------------------------------------------
+Part* findPartBySerial(int sn)
+{
+ TrackList* tracks = song->tracks();
+ for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) {
+ Track* track = *t;
+ PartList* parts = track->parts();
+ for (ciPart p = parts->begin(); p != parts->end(); p++) {
+ Part* part = p->second;
+ if (part->sn() == sn)
+ return part;
+ }
+ }
+
+ return NULL;
+}
+//------------------------------------------------------------
+// Get parts from track
+//------------------------------------------------------------
+PyObject* getParts(PyObject*, PyObject* args)
+{
+ TrackList* tracks = song->tracks();
+ const char* trackname;
+ if (!PyArg_ParseTuple(args, "s", &trackname)) {
+ return NULL;
+ }
+
+ PyObject* pyparts = Py_BuildValue("[]");
+ for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) {
+ Track* track = *t;
+ if (track->name() != trackname)
+ continue;
+
+ PartList* parts = track->parts();
+ for (ciPart p = parts->begin(); p != parts->end(); p++) {
+ Part* part = p->second;
+
+ MidiPart* mpart = (MidiPart*) part;
+ PyObject* pypart = PyDict_New();
+ int tick = mpart->tick();
+ int lentick = mpart->lenTick();
+ int serialnr = mpart->sn();
+ PyObject* pstrtick = Py_BuildValue("s","tick");
+ PyObject* pitick = Py_BuildValue("i", tick);
+ PyObject* pstrid = Py_BuildValue("s","id");
+ PyObject* pstrserial = Py_BuildValue("i", serialnr);
+ PyObject* pstrlen = Py_BuildValue("s","len");
+ PyObject* pstrtick2 = Py_BuildValue("i", lentick);
+
+ PyDict_SetItem(pypart, pstrtick, pitick);
+ PyDict_SetItem(pypart, pstrid, pstrserial);
+ PyDict_SetItem(pypart, pstrlen, pstrtick2);
+
+ Py_DECREF(pstrtick);
+ Py_DECREF(pitick);
+ Py_DECREF(pstrid);
+ Py_DECREF(pstrserial);
+ Py_DECREF(pstrlen);
+ Py_DECREF(pstrtick2);
+
+ // Pack midi events into list before wrapping it all up
+ EventList* events = mpart->events();
+ PyObject* pyevents = Py_BuildValue("[]");
+ for (ciEvent e = events->begin(); e != events->end(); e++) {
+ PyObject* pyevent = PyDict_New(); // The event structure - a dictionary with keys 'type','tick','data'
+
+ const Event& event = e->second;
+ unsigned tick = e->first;
+ PyObject* eventdata = Py_BuildValue("[i,i,i]", event.dataA(), event.dataB(), event.dataC());
+ PyObject* pstrdata = Py_BuildValue("s", "data");
+ pstrtick = Py_BuildValue("s", "tick");
+ PyObject* pitickval = Py_BuildValue("i", tick);
+ PyDict_SetItem(pyevent, pstrdata, eventdata);
+ PyDict_SetItem(pyevent, pstrtick, pitickval);
+ Py_DECREF(eventdata);
+ Py_DECREF(pstrdata);
+ Py_DECREF(pstrtick);
+ Py_DECREF(pitickval);
+
+ switch(event.type()) {
+ case Note: {
+ PyObject* pstrtype = Py_BuildValue("s", "type");
+ PyObject* pstrnote = Py_BuildValue("s", "note");
+ PyObject* pstrlen = Py_BuildValue("s", "len");
+ PyObject* pilentick = Py_BuildValue("i", event.lenTick());
+ PyDict_SetItem(pyevent, pstrtype, pstrnote);
+ PyDict_SetItem(pyevent, pstrlen, pilentick);
+ Py_DECREF(pstrtype);
+ Py_DECREF(pstrnote);
+ Py_DECREF(pstrlen);
+ Py_DECREF(pilentick);
+ break;
+ }
+ case Controller: {
+ PyObject* pstrtype = Py_BuildValue("s", "type");
+ PyObject* pstrctrl = Py_BuildValue("s", "ctrl");
+ PyDict_SetItem(pyevent, pstrtype, pstrctrl);
+ Py_DECREF(pstrtype);
+ Py_DECREF(pstrctrl);
+ break;
+ }
+ default:
+ printf("Event type not supported yet: %d\n", event.type());
+ break;
+ }
+ PyList_Append(pyevents, pyevent);
+ Py_DECREF(pyevent);
+ }
+ Py_DECREF(pyevents);
+ // Add the event list to the pypart dictionary
+ PyObject* pystrevents = Py_BuildValue("s", "events");
+ PyDict_SetItem(pypart, pystrevents, pyevents);
+ Py_DECREF(pystrevents);
+ PyList_Append(pyparts, pypart);
+ Py_DECREF(pypart);
+ }
+
+ return pyparts;
+ }
+
+ return NULL;
+}
+
+//------------------------------------------------------------
+// parsePythonPart
+// get part id/serialno from python part structure
+//------------------------------------------------------------
+int getPythonPartId(PyObject* part)
+{
+ PyObject* pyid = PyDict_GetItemString(part, "id");
+ int id = PyInt_AsLong(pyid);
+ return id;
+}
+
+//------------------------------------------------------------
+// addPyPartEventsToMusePart
+// parse events from python part structure into muse part
+//------------------------------------------------------------
+bool addPyPartEventsToMusePart(MidiPart* npart, PyObject* part)
+{
+ PyObject* events;
+
+ if (PyDict_Check(part) == false) {
+ printf("Not a dict!\n");
+ return false;
+ }
+ PyObject* pstrevents = Py_BuildValue("s","events");
+ if (PyDict_Contains(part, pstrevents) == false) {
+ Py_DECREF(pstrevents);
+ printf("No events in part data...\n");
+ return false;
+ }
+ Py_DECREF(pstrevents);
+
+ events = PyDict_GetItemString(part, "events");
+
+ if (PyList_Check(events) == false) {
+ printf("Events not a list!\n");
+ return false;
+ }
+
+ //
+ // Go through event list, create MusE events of them and add to new part
+ //
+ Py_ssize_t len = PyList_Size(events);
+ for (Py_ssize_t i=0; i<len; i++) {
+ PyObject* pevent = PyList_GetItem(events, i);
+ if (PyDict_Check(pevent) == false) {
+ printf("Event is not a dictionary!\n");
+ return false;
+ }
+ PyObject* p_etick = PyDict_GetItemString(pevent, "tick");
+ PyObject* p_type = PyDict_GetItemString(pevent, "type");
+ PyObject* p_len = PyDict_GetItemString(pevent, "len");
+ PyObject* p_data = PyDict_GetItemString(pevent, "data"); // list
+
+ int etick = PyInt_AsLong(p_etick);
+ int elen = PyInt_AsLong(p_len);
+ string type = string(PyString_AsString(p_type));
+ int data[3];
+
+ // Traverse data list:
+ for (int j=0; j<3; j++) {
+ PyObject* plItem = PyList_GetItem(p_data, j);
+ data[j] = PyInt_AsLong(plItem);
+ }
+ if (type == "note" || type == "ctrl") {
+ Event event(Note);
+ event.setA(data[0]);
+ event.setB(data[1]);
+ event.setC(data[2]);
+ event.setTick(etick);
+ event.setLenTick(elen);
+ npart->events()->add(event);
+ }
+ else
+ printf("Unhandled event type from python: %s\n", type.c_str());
+ }
+
+ return true;
+}
+//------------------------------------------------------------
+// Create a new part at a particular tick and track
+//------------------------------------------------------------
+PyObject* createPart(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ unsigned tick, tickLen;
+ PyObject* part;
+
+ if (!PyArg_ParseTuple(args, "siiO", &trackname, &tick, &tickLen, &part)) {
+ return NULL;
+ }
+
+ QString qtrackname(trackname);
+ MidiTrack* track = (MidiTrack*) song->findTrack(trackname);
+ if (track == NULL)
+ return NULL;
+
+ MidiPart* npart = new MidiPart(track);
+ npart->setTick(tick);
+ npart->setLenTick(tickLen);
+ addPyPartEventsToMusePart(npart, part);
+
+ song->addPart(npart);
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, SC_TRACK_MODIFIED);
+ QApplication::postEvent(song, pyevent);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+//------------------------------------------------------------
+// Modify a particular part:
+// args: new part data, old part data is used from the part with the same id as the one sent here
+// TODO: Lots and lots of refcount stuff
+//------------------------------------------------------------
+PyObject* modifyPart(PyObject*, PyObject* part)
+{
+ int id = getPythonPartId(part);
+
+ Part* opart = NULL;
+ // Verify a part with that id actually exists, then get it
+ TrackList* tracks = song->tracks();
+ for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) {
+ Track* track = *t;
+ for (ciPart p = track->parts()->begin(); p != track->parts()->end(); p++) {
+ if (p->second->sn() == id) {
+ opart = p->second;
+ break;
+ }
+ }
+ }
+
+ if (opart == NULL) {
+ printf("Part doesn't exist!\n");
+ return NULL;
+ }
+
+ // Remove all note and controller events from current part eventlist
+ std::list< std::pair<const unsigned, Event> > elist;
+ MidiPart* npart = new MidiPart((MidiTrack*)opart->track());
+ npart->setTick(opart->tick());
+ npart->setLenTick(opart->lenTick());
+ npart->setSn(opart->sn());
+
+ for (iEvent e = opart->events()->begin(); e != opart->events()->end(); e++) {
+ Event& event = e->second;
+ if (event.type() == Note || event.type() == Controller)
+ continue;
+
+ npart->events()->add(event);
+ }
+
+ addPyPartEventsToMusePart(npart, part);
+
+ //song->startUndo();
+ song->changePart(opart, npart);
+ //song->endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); // Crash! Probably since the call ends up in Qt GUI thread from this thread
+
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED);
+ QApplication::postEvent(song, pyevent);
+
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// deletePart
+// delete part by serial nr
+//------------------------------------------------------------
+PyObject* deletePart(PyObject*, PyObject* args)
+{
+ int id;
+ if (!PyArg_ParseTuple(args, "i", &id)) {
+ return NULL;
+ }
+
+ Part* part = findPartBySerial(id);
+ if (part == NULL)
+ return NULL;
+
+ song->removePart(part);
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, SC_TRACK_MODIFIED | SC_PART_REMOVED);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+//------------------------------------------------------------
+// setPos
+//------------------------------------------------------------
+PyObject* setPos(PyObject*, PyObject* args)
+{
+ int index;
+ int ticks;
+ if (!PyArg_ParseTuple(args, "ii", &index, &ticks)) {
+ return NULL;
+ }
+
+ //song->setPos(index, ticks);
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_POSCHANGE, index, ticks);
+ QApplication::postEvent(song, pyevent);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+//------------------------------------------------------------
+// setLen
+//------------------------------------------------------------
+PyObject* setSongLen(PyObject*, PyObject* args)
+{
+ unsigned len;
+
+ if (!PyArg_ParseTuple(args, "i", &len)) {
+ return NULL;
+ }
+ //song->setLen(len);// Appears to not be ok to call from python thread, we do it with event instead
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONGLEN_CHANGE, len);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// getLen
+//------------------------------------------------------------
+PyObject* getSongLen(PyObject*, PyObject*)
+{
+ PyObject* pylen = Py_BuildValue("i", song->len());
+
+ return pylen;
+}
+//------------------------------------------------------------
+// getDivision
+//------------------------------------------------------------
+PyObject* getDivision(PyObject*, PyObject*)
+{
+ return Py_BuildValue("i", config.division);
+}
+//------------------------------------------------------------
+// setTrackParameter
+//------------------------------------------------------------
+PyObject* setMidiTrackParameter(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ const char* paramname;
+ int value;
+ if(!PyArg_ParseTuple(args, "ssi", &trackname, &paramname, &value))
+ return NULL;
+
+ Track* track = song->findTrack(QString(trackname));
+ if (track == NULL)
+ return NULL;
+
+ MidiTrack* mt = (MidiTrack*) track;
+
+ QString qparamname(paramname);
+ bool changed = false;
+ if (qparamname == "velocity") {
+ changed = true;
+ mt->velocity = value;
+ }
+ else if (qparamname == "compression") {
+ changed = true;
+ mt->compression = value;
+ }
+ else if (qparamname == "transposition") {
+ changed = true;
+ mt->transposition = value;
+ }
+ else if (qparamname == "delay") {
+ changed = true;
+ mt->delay = value;
+ }
+
+ if (changed) {
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, SC_TRACK_MODIFIED);
+ QApplication::postEvent(song, pyevent);
+ }
+
+ return Py_BuildValue("b", changed); // true/false depending on whether anythin was changed
+}
+//------------------------------------------------------------
+// Set loop
+//------------------------------------------------------------
+PyObject* setLoop(PyObject*, PyObject* args)
+{
+ bool loopFlag;
+ if(!PyArg_ParseTuple(args, "b", &loopFlag))
+ return NULL;
+
+ song->setLoop(loopFlag);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// Get loop value
+//------------------------------------------------------------
+PyObject* getLoop(PyObject*, PyObject*)
+{
+ return Py_BuildValue("b", song->getLoop());
+}
+//------------------------------------------------------------
+// getMute trackname
+//------------------------------------------------------------
+PyObject* getMute(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ if (!PyArg_ParseTuple(args, "s", &trackname)) {
+ return NULL;
+ }
+
+ Track* track = song->findTrack(QString(trackname));
+ if (track == NULL)
+ return NULL;
+
+ return Py_BuildValue("b", track->isMute());
+}
+//------------------------------------------------------------
+// setMute (trackname, boolean)
+//------------------------------------------------------------
+PyObject* setMute(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ bool muted;
+
+ if (!PyArg_ParseTuple(args, "sb", &trackname, &muted)) {
+ return NULL;
+ }
+
+ Track* track = song->findTrack(QString(trackname));
+ if (track == NULL)
+ return NULL;
+
+ int mutedint = 1;
+ if (muted == false)
+ mutedint = 0;
+
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETMUTE, mutedint);
+ pyevent->setS1(trackname);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// setController
+//------------------------------------------------------------
+void setController(const char* trackname, int ctrltype, int ctrlval)
+{
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETCTRL, ctrltype, ctrlval);
+ pyevent->setS1(trackname);
+ QApplication::postEvent(song, pyevent);
+}
+
+//------------------------------------------------------------
+// setMidiControllerValue
+//------------------------------------------------------------
+PyObject* setMidiControllerValue(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ int ctrltype;
+ int value;
+
+ if (!PyArg_ParseTuple(args, "sii", &trackname, &ctrltype, &value)) {
+ return NULL;
+ }
+
+ setController(trackname, ctrltype, value);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+//------------------------------------------------------------
+// getMidiControllerValue
+//------------------------------------------------------------
+PyObject* getMidiControllerValue(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ int ctrltype;
+
+ if (!PyArg_ParseTuple(args, "si", &trackname, &ctrltype)) {
+ return NULL;
+ }
+
+ Track* t = song->findTrack(QString(trackname));
+ if (t == NULL)
+ return NULL;
+
+ if (t->isMidiTrack() == false) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ MidiTrack* track = (MidiTrack*) t;
+ int channel = track->outChannel();
+ int outport = track->outPort();
+ MidiPort* mp = &midiPorts[outport];
+ if (mp == NULL)
+ return Py_BuildValue("i", -1);
+
+ int value = mp->hwCtrlState(channel, ctrltype);
+ return Py_BuildValue("i", value);
+}
+//------------------------------------------------------------
+// setAudioTrackVolume
+//------------------------------------------------------------
+PyObject* setAudioTrackVolume(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ double volume = 0.0f;
+
+ if (!PyArg_ParseTuple(args, "sd", &trackname, &volume)) {
+ return NULL;
+ }
+
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETAUDIOVOL);
+ pyevent->setD1(volume);
+ pyevent->setS1(trackname);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// getAudioTrackVolume
+//------------------------------------------------------------
+PyObject* getAudioTrackVolume(PyObject*, PyObject* args)
+{
+ const char* trackname;
+
+ if (!PyArg_ParseTuple(args, "s", &trackname)) {
+ return NULL;
+ }
+
+ Track* t = song->findTrack(QString(trackname));
+ if (t == NULL)
+ return NULL;
+
+ if (t->type() == Track::DRUM || t->type() == Track::MIDI)
+ return NULL;
+
+ AudioTrack* track = (AudioTrack*) t;
+ return Py_BuildValue("d", track->volume());
+}
+
+//------------------------------------------------------------
+// getSelectedTrack
+//------------------------------------------------------------
+PyObject* getSelectedTrack(PyObject*, PyObject*)
+{
+ TrackList* tracks = song->tracks();
+ for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) {
+ Track* track = *t;
+ if (track->selected())
+ return Py_BuildValue("s", track->name().latin1());
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+//------------------------------------------------------------
+// importPart
+//------------------------------------------------------------
+PyObject* importPart(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ const char* filename;
+ int tick;
+
+ if (!PyArg_ParseTuple(args, "ssi", &trackname, &filename, &tick)) {
+ return NULL;
+ }
+
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_IMPORT_PART, tick);
+ pyevent->setS1(trackname);
+ pyevent->setS2(filename);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// getTrackEffects
+//------------------------------------------------------------
+PyObject* getTrackEffects(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ if (!PyArg_ParseTuple(args, "s", &trackname)) {
+ return NULL;
+ }
+
+ Track* t = song->findTrack(QString(trackname));
+ if (t == NULL)
+ return NULL;
+
+ if (t->type() != Track::WAVE)
+ return NULL;
+
+ AudioTrack* track = (AudioTrack*) t;
+ PyObject* pyfxnames = Py_BuildValue("[]");
+ const Pipeline* pipeline = track->efxPipe();
+ for (int i = 0; i < PipelineDepth; i++) {
+ QString name = pipeline->name(i);
+ printf("fx %d name: %s\n", i, name.latin1());
+ PyObject* pyname = Py_BuildValue("s", name.latin1());
+ PyList_Append(pyfxnames, pyname);
+ Py_DECREF(pyname);
+ }
+
+ return pyfxnames;
+}
+//------------------------------------------------------------
+// toggleTrackEffect
+//------------------------------------------------------------
+PyObject* toggleTrackEffect(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ int fxid;
+ bool onoff;
+
+ if (!PyArg_ParseTuple(args, "sib", &trackname, &fxid, &onoff))
+ return NULL;
+
+ Track* t = song->findTrack(QString(trackname));
+ if (t == NULL)
+ return NULL;
+
+ if (t->type() != Track::WAVE)
+ return NULL;
+
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_TOGGLE_EFFECT, fxid, onoff);
+ pyevent->setS1(trackname);
+
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// changeTrackName
+//------------------------------------------------------------
+PyObject* changeTrackName(PyObject*, PyObject* args)
+{
+ const char* trackname;
+ const char* newname;
+
+ if (!PyArg_ParseTuple(args, "ss", &trackname, &newname))
+ return NULL;
+
+ Track* t = song->findTrack(QString(trackname));
+ if (t == NULL)
+ return Py_BuildValue("b", false);
+
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_CHANGE_TRACKNAME);
+ pyevent->setS1(trackname);
+ pyevent->setS2(newname);
+ QApplication::postEvent(song, pyevent);
+ QPybridgeEvent* pyevent2 = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, SC_TRACK_MODIFIED);
+ QApplication::postEvent(song, pyevent2);
+ return Py_BuildValue("b", true);
+}
+//------------------------------------------------------------
+// addMidiTrack
+//------------------------------------------------------------
+PyObject* addMidiTrack(PyObject*, PyObject*)
+{
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::MIDI);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// addWaveTrack
+//------------------------------------------------------------
+PyObject* addWaveTrack(PyObject*, PyObject*)
+{
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::WAVE);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// addInput
+//------------------------------------------------------------
+PyObject* addInput(PyObject*, PyObject*)
+{
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::AUDIO_INPUT);
+ QApplication::postEvent(song, pyevent);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+//------------------------------------------------------------
+// addOutput
+//------------------------------------------------------------
+PyObject* addOutput(PyObject*, PyObject*)
+{
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::AUDIO_OUTPUT);
+ QApplication::postEvent(song, pyevent);
+ return Py_None;
+}
+//------------------------------------------------------------
+// addGroup
+//------------------------------------------------------------
+PyObject* addGroup(PyObject*, PyObject*)
+{
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::AUDIO_GROUP);
+ QApplication::postEvent(song, pyevent);
+ return Py_None;
+}
+//------------------------------------------------------------
+// deleteTrack
+//------------------------------------------------------------
+PyObject* deleteTrack(PyObject*, PyObject* args)
+{
+ const char* trackname;
+
+ if (!PyArg_ParseTuple(args, "s", &trackname))
+ return NULL;
+
+ QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_DELETE_TRACK);
+ pyevent->setS1(trackname);
+ QApplication::postEvent(song, pyevent);
+ return Py_None;
+}
+//------------------------------------------------------------
+// getOutputRoute
+//------------------------------------------------------------
+/*
+PyObject* getOutputRoute(PyObject*, PyObject* args)
+{
+ const char* trackname;
+
+ if (!PyArg_ParseTuple(args, "s", &trackname))
+ return NULL;
+
+ Track* tt = song->findTrack(QString(trackname));
+ if (tt == NULL)
+ return Py_BuildValue("b", false);
+
+ PyObject* routes = Py_BuildValue("[]");
+ if (tt->type() == Track::WAVE && tt->type() == Track::AUDIO_AUX) {
+ AudioTrack* t = (AudioTrack*)tt;
+ RouteList* r = t->outRoutes();
+
+ OutputList* al = song->outputs();
+ for (iAudioOutput i = al->begin(); i != al->end(); ++i) {
+ Track* track = *i;
+ if (t == track)
+ continue;
+
+ QString s(track->name());
+
+ // for (iRoute ir = r->begin(); ir != r->end(); ++ir) {
+ // if (ir->type == 0 && ir->track == track) {
+ // s += "*";
+ // PyList_Append(routes, Py_BuildValue("s", s.latin1()));
+ // break;
+ // }
+ // }
+ //
+ }
+ }
+ else if (tt->type() == Track::AUDIO_OUTPUT) {
+ }
+
+
+
+ return routes;
+}
+*/
+//------------------------------------------------------------
+// Global method definitions for MusE:s Python API
+//
+// This is where global functions in Python is linked to their equivalent C/C++ functions
+//------------------------------------------------------------
+PyMethodDef g_methodDefinitions[] =
+{
+ { "startPlay", startPlay, METH_VARARGS, "Starts playing the song from current position" },
+ { "stopPlay", stopPlay, METH_VARARGS, "Stops playback if currently playing" },
+ { "rewindStart", rewindStart, METH_VARARGS, "Set current position to beginning of song" },
+ { "getCPos", getCPos, METH_NOARGS, "Get current position (in ticks)" },
+ { "getLPos", getLPos, METH_NOARGS, "Get position of left locator (in ticks)" },
+ { "getRPos", getRPos, METH_NOARGS, "Get position of right locator (in ticks)" },
+ { "setPos", setPos, METH_VARARGS, "Set position of locators or current position" },
+ { "getTempo", getTempo, METH_VARARGS, "Get tempo of the song at a particular tick" },
+ { "setLoop", setLoop, METH_VARARGS, "Set loop mode on/off" },
+ { "getLoop", getLoop, METH_NOARGS, "Get loop value" },
+
+ { "getTrackNames", getTrackNames, METH_VARARGS, "Get track names (which are unique)" },
+ { "getParts", getParts, METH_VARARGS, "Get part data from a track" },
+ { "createPart", createPart, METH_VARARGS, "Create a part" },
+ { "modifyPart", modifyPart, METH_O, "Modify a particular part" },
+ { "deletePart", deletePart, METH_VARARGS, "Remove part with a particular serial nr" },
+ { "getSelectedTrack", getSelectedTrack, METH_NOARGS, "Get first selected track" },
+ { "importPart", importPart, METH_VARARGS, "Import part file to a track at a particular position" },
+ { "changeTrackName", changeTrackName, METH_VARARGS, "Change track name" },
+ { "addMidiTrack", addMidiTrack, METH_NOARGS, "Add a midi track" },
+ { "addWaveTrack", addWaveTrack, METH_NOARGS, "Add a wave track" },
+ { "addInput", addInput, METH_NOARGS, "Add audio input" },
+ { "addOutput", addOutput, METH_NOARGS, "Add audio output" },
+ { "addGroup", addGroup, METH_NOARGS, "Add audio group" },
+ { "deleteTrack", deleteTrack, METH_VARARGS, "Delete a track" },
+
+ { "getTrackEffects", getTrackEffects, METH_VARARGS, "Get names of LADSPA effects on a track" },
+ { "toggleTrackEffect", toggleTrackEffect, METH_VARARGS, "Toggle LADSPA effect on/off" },
+ //{ "getOutputRoute", getOutputRoute, METH_VARARGS, "Get route for an audio output" },
+
+ { "setSongLen", setSongLen, METH_VARARGS, "Set length of song (in ticks)" },
+ { "getSongLen", getSongLen, METH_VARARGS, "Get length of song (in ticks)" },
+
+ { "getMute", getMute, METH_VARARGS, "Get track mute property (if track is played or not)" },
+ { "setMute", setMute, METH_VARARGS, "Set track mute property (if track should be played or not)" },
+ { "setMidiControllerValue", setMidiControllerValue, METH_VARARGS, "Set midi controller value for a track" },
+ { "getMidiControllerValue", getMidiControllerValue, METH_VARARGS, "Get midi controller value for a track" },
+ { "setAudioTrackVolume", setAudioTrackVolume, METH_VARARGS, "Set volume on audio track/aux/output/input" },
+ { "getAudioTrackVolume", getAudioTrackVolume, METH_VARARGS, "Get audio track/aux/output/input volume" },
+
+ { "setMidiTrackParameter", setMidiTrackParameter, METH_VARARGS, "Set transposition, velocity, compression or delay on track level" },
+
+ { "getDivision", getDivision, METH_VARARGS, "Number of ticks per 1/4 (?)" },
+
+ {NULL, NULL, NULL, NULL}
+};
+
+/**
+ * This function launches the Pyro name service, which blocks execution
+ * Thus it needs its own thread
+ **/
+static void* pyapithreadfunc(void*)
+{
+ Py_Initialize();
+ PyImport_AddModule("muse");
+ Py_InitModule( "muse", g_methodDefinitions );
+
+ //
+ // Access the "__main__" module and its name-space dictionary.
+ //
+
+ PyObject *pMainModule = PyImport_AddModule( "__main__" );
+ PyObject *pMainDictionary = PyModule_GetDict( pMainModule );
+ string launcherfilename = string(INSTPREFIX) + string("/share/muse/pybridge/museplauncher.py");
+ printf("Initiating MusE Pybridge launcher from %s\n", launcherfilename.c_str());
+ FILE* fp = fopen(launcherfilename.c_str(),"r");
+ PyRun_File(fp, launcherfilename.c_str(), Py_file_input, pMainDictionary, pMainDictionary);
+ fclose(fp);
+
+ return NULL;
+}
+
+/**
+ * This function currently only launches the thread. There should be some kind of check that
+ * things are up and running as they are supposed to
+ */
+bool initPythonBridge()
+{
+ if (pthread_create(&pyapiThread, NULL, ::pyapithreadfunc, 0)) {
+ return false;
+ }
+ return true; // TODO: Verify that things are up and running!
+}
+
+//---------------------------------------------------------
+// event
+//
+// Function in Song class, run in the Qt event thread context.
+// Handles events sent from the Python bridge subsystem
+//
+// This is part of Qt:s event handling and events are fed
+// here via QApplication::postEvent since gui updates should
+// be done by Qt:s GUI thread. QApplication::postEvent is
+// a static method, which is threadsafe. Using the song object
+// from the Python thread is dangerous when it comes to
+// operations that manipulate the gui itself (read is ok)
+//---------------------------------------------------------
+bool Song::event(QEvent* _e)
+{
+ if (_e->type() != QEvent::User)
+ return false; //ignore all events except user events, which are events from Python bridge subsystem
+
+ QPybridgeEvent* e = (QPybridgeEvent*) _e;
+ switch (e->getType()) {
+ case QPybridgeEvent::SONG_UPDATE:
+ this->update(e->getP1());
+ break;
+ case QPybridgeEvent::SONGLEN_CHANGE:
+ this->setLen(e->getP1());
+ break;
+ case QPybridgeEvent::SONG_POSCHANGE:
+ this->setPos(e->getP1(), e->getP2());
+ break;
+ case QPybridgeEvent::SONG_SETPLAY:
+ this->setPlay(true);
+ break;
+ case QPybridgeEvent::SONG_SETSTOP:
+ this->setStop(true);
+ break;
+ case QPybridgeEvent::SONG_REWIND:
+ this->rewindStart();
+ break;
+ case QPybridgeEvent::SONG_SETMUTE: {
+ Track* track = this->findTrack(e->getS1());
+ if (track == NULL)
+ return false;
+
+ bool muted = e->getP1() == 1;
+ track->setMute(muted);
+ this->update(SC_MUTE | SC_TRACK_MODIFIED);
+ break;
+ }
+ case QPybridgeEvent::SONG_SETCTRL: {
+ Track* t = this->findTrack(e->getS1());
+ if (t == NULL)
+ return false;
+
+ if (t->isMidiTrack() == false)
+ return false;
+
+ MidiTrack* track = (MidiTrack*) t;
+ int chan = track->outChannel();
+
+ int num = e->getP1();
+ int val = e->getP2();
+ int tick = song->cpos();
+ MidiPlayEvent ev(tick, track->outPort(), chan, ME_CONTROLLER, num, val);
+ audio->msgPlayMidiEvent(&ev);
+ song->update(SC_MIDI_CONTROLLER);
+ break;
+ }
+ case QPybridgeEvent::SONG_SETAUDIOVOL: {
+ Track* t = this->findTrack(e->getS1());
+ if (t == NULL)
+ return false;
+
+ if (t->type() == Track::DRUM || t->type() == Track::MIDI)
+ return false;
+
+ AudioTrack* track = (AudioTrack*) t;
+ track->setVolume(e->getD1());
+ break;
+ }
+ case QPybridgeEvent::SONG_IMPORT_PART: {
+ Track* track = this->findTrack(e->getS1());
+ QString filename = e->getS2();
+ unsigned int tick = e->getP1();
+ if (track == NULL)
+ return false;
+
+ muse->importPartToTrack(filename, tick, track);
+ break;
+ }
+ case QPybridgeEvent::SONG_TOGGLE_EFFECT: {
+ Track* t = this->findTrack(e->getS1());
+ if (t == NULL)
+ return false;
+
+ if (t->type() != Track::WAVE)
+ return false;
+
+ int fxid = e->getP1();
+
+ if (fxid > PipelineDepth)
+ return false;
+
+ int onoff = (e->getP2() == 1);
+
+ AudioTrack* track = (AudioTrack*) t;
+ Pipeline* pipeline = track->efxPipe();
+ pipeline->setOn(fxid, onoff);
+ break;
+ }
+ case QPybridgeEvent::SONG_ADD_TRACK:
+ song->addTrack(e->getP1());
+ break;
+ case QPybridgeEvent::SONG_CHANGE_TRACKNAME: {
+ Track* t = this->findTrack(e->getS1());
+ if (t == NULL)
+ return false;
+ t->setName(e->getS2());
+ break;
+ }
+ case QPybridgeEvent::SONG_DELETE_TRACK: {
+ Track* t = this->findTrack(e->getS1());
+ if (t == NULL)
+ return false;
+
+ audio->msgRemoveTrack(t);
+ break;
+ }
+ default:
+ printf("Unknown pythonthread event received: %d\n", e->getType());
+ break;
+ }
+
+
+ return true;
+}
+
+