Пример #1
0
Undo partSplitter(unsigned int pos, bool onlySelectedTracks)
{
    Undo operations;
    TrackList* tracks = MusEGlobal::song->tracks();

    for (iTrack it = tracks->begin(); it != tracks->end(); ++it) {
        Track* track = *it;
        if (track == 0 || (onlySelectedTracks && !track->selected()))
              continue;

        PartList* pl = track->parts();
        for (iPart p = pl->begin(); p != pl->end(); ++p) {
              Part* part = p->second;
              unsigned int p1 = part->tick();
              unsigned int l0 = part->lenTick();
              if (pos > p1 && pos < (p1+l0)) {
                    Part* p1;
                    Part* p2;
                    part->splitPart(pos, p1, p2);

                    MusEGlobal::song->informAboutNewParts(part, p1);
                    MusEGlobal::song->informAboutNewParts(part, p2);
                    operations.push_back(UndoOp(UndoOp::DeletePart,part));
                    operations.push_back(UndoOp(UndoOp::AddPart,p1));
                    operations.push_back(UndoOp(UndoOp::AddPart,p2));
                    break;
              }
        }
    }
    return operations;
}
Пример #2
0
void TrackComment::songChanged(int flags)
{
	if ((flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED)) == 0)
		return;

	// check if track still exists:
	TrackList* tl = song->tracks();
	iTrack it;
	for (it = tl->begin(); it != tl->end(); ++it)
	{
		if (m_track == *it)
			break;
	}
	if (it == tl->end())
	{
		close();
		return;
	}
	label2->setText(m_track->name());
	if (m_track->comment() != textentry->toPlainText())
	{
		//disconnect(textentry, SIGNAL(textChanged()), this, SLOT(textChanged()));
		textentry->blockSignals(true);
		textentry->setText(m_track->comment());
		textentry->blockSignals(false);
		textentry->moveCursor(QTextCursor::End);
		//connect(textentry, SIGNAL(textChanged()), this, SLOT(textChanged()));
	}
}
Пример #3
0
void TrackList::Swap(TrackList &that)
{
   ListOfTracks::swap(that);
   for (auto it = begin(), last = end(); it != last; ++it)
      (*it)->SetOwner(this, it);
   for (auto it = that.begin(), last = that.end(); it != last; ++it)
      (*it)->SetOwner(&that, it);
}
Пример #4
0
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);
	}

	addPyPartEventsToOOMidiPart(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;
}
Пример #5
0
void LOS::importPart()
{
    unsigned curPos = song->cpos();
    TrackList* tracks = song->tracks();
    Track* track = 0;
    // Get first selected track:
    for (iTrack i = tracks->begin(); i != tracks->end(); i++)
    {
        Track* t = *i;
        if (t->selected())
        {
            // Changed by T356. Support mixed .mpt files.
            //if (t->isMidiTrack()) {
            if (t->isMidiTrack() || t->type() == Track::WAVE)
            {
                track = t;
                break;
            }
            else
            {
                //QMessageBox::warning(this, QString("LOS"), tr("Import part is only valid for midi tracks!"));
                QMessageBox::warning(this, QString("LOS"), tr("Import part is only valid for midi and wave tracks!"));
                return;
            }
        }
    }

    if (track)
    {
        bool loadAll;
        QString filename = getOpenFileName(QString(""), part_file_pattern, this, tr("LOS: load part"), &loadAll);
        if (!filename.isEmpty())
        {
            // Make a backup of the current clone list, to retain any 'copy' items,
            //  so that pasting works properly after.
            CloneList copyCloneList = cloneList;
            // Clear the clone list to prevent any dangerous associations with
            //  current non-original parts.
            cloneList.clear();

            importPartToTrack(filename, curPos, track);

            // Restore backup of the clone list, to retain any 'copy' items,
            //  so that pasting works properly after.
            cloneList.clear();
            cloneList = copyCloneList;
        }
    }
    else
    {
        QMessageBox::warning(this, QString("LOS"), tr("No track selected for import"));
    }
}
Пример #6
0
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().toLatin1());
	}

	Py_INCREF(Py_None);
	return Py_None;
}
Пример #7
0
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().toLatin1());
		PyList_Append(res, ptrackname);
		Py_DECREF(ptrackname);
	}

	return res;
}
void siftPositionTracksToDrawers(const TrackList<SiftPosition>& feature_tracks,
                                 TrackList<DrawerPointer>& drawer_tracks) {
  drawer_tracks.clear();
  TrackList<SiftPosition>::const_iterator feature_track;
  for (feature_track = feature_tracks.begin();
       feature_track != feature_tracks.end();
       ++feature_track) {
    Track<DrawerPointer> drawer_track;
    siftPositionTrackToDrawers(*feature_track, drawer_track);

    drawer_tracks.push_back(Track<DrawerPointer>());
    drawer_tracks.back().swap(drawer_track);
  }
}
void translationTracksToDrawers(const TrackList<cv::Point2d>& feature_tracks,
                                TrackList<DrawerPointer>& drawer_tracks,
                                int radius) {
  drawer_tracks.clear();
  TrackList<cv::Point2d>::const_iterator feature_track;
  for (feature_track = feature_tracks.begin();
       feature_track != feature_tracks.end();
       ++feature_track) {
    Track<DrawerPointer> drawer_track;
    translationTrackToDrawers(*feature_track, drawer_track, radius);

    drawer_tracks.push_back(Track<DrawerPointer>());
    drawer_tracks.back().swap(drawer_track);
  }
}
Пример #10
0
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;
}
void extractExamplesForView(const TrackList<SiftFeature>& tracks,
                            const cv::Mat& F,
                            const CameraProperties& camera1,
                            const CameraProperties& camera2,
                            const std::string& image_format,
                            const std::string& view,
                            int time,
                            const std::vector<double>& scales,
                            const std::vector<double>& angles) {
  // March along epipolar line.
  DistortedEpipolarRasterizer rasterizer(camera2, F);
  rasterizer.init();

  cv::Mat K1(camera1.matrix());
  cv::Mat K1_inv = K1.inv();

  TrackList<SiftFeature>::const_iterator track;
  for (track = tracks.begin(); track != tracks.end(); ++track) {
    Track<SiftFeature>::const_iterator iter = track->find(time);
    CHECK(iter != track->end()) << "Track does not contain the current frame";

    const SiftFeature& feature = iter->second;
    cv::Point2d y1(feature.position.x, feature.position.y);

    // Undo intrinsics, undistort, and re-apply intrinsics.
    cv::Point2d x1 = affineTransformImagePoint(y1, K1_inv);
    x1 = undistort(x1, camera1.distort_w);
    x1 = affineTransformImagePoint(x1, K1);

    // Extract the pixels of the epipolar line.
    std::vector<cv::Point> line;
    rasterizer.compute(x1, line);

    // Load first image.
    cv::Mat image;
    std::string image_file = makeImageFilename(image_format, view, time);
    bool ok = readGrayImage(image_file, image);
    CHECK(ok) << "Could not load image";

    std::deque<std::deque<SiftFeature> > features;
    extractFeaturesAlongLine(line, image, scales, angles, features);
  }
}
Пример #12
0
Undo movePartsTotheRight(unsigned int startTicks, int moveTicks, bool only_selected, set<Track*>* tracklist)
{
      if (moveTicks<=0)
            return Undo();

      Undo operations;
      TrackList* tracks = MusEGlobal::song->tracks();
      
      for (iTrack it = tracks->begin(); it != tracks->end(); ++it) {
            Track* track = *it;
            if ( (track == 0) ||
                 (only_selected && !track->selected()) ||
                 (tracklist && tracklist->find(track)==tracklist->end()) )
                  continue;
            PartList* pl = track->parts();
            for (riPart p = pl->rbegin(); p != pl->rend(); ++p) {
                  Part* part = p->second;
                  unsigned t = part->tick();
                  int l = part->lenTick();
                  if (t + l <= startTicks)
                        continue;
                  if (startTicks > t && startTicks < (t+l)) {
                        // split part to insert new space
                        Part* p1;
                        Part* p2;
                        part->splitPart(startTicks, p1, p2);
                        p2->setTick(startTicks+moveTicks);

                        MusEGlobal::song->informAboutNewParts(part,p1,p2);
                        operations.push_back(UndoOp(UndoOp::DeletePart, part));
                        operations.push_back(UndoOp(UndoOp::AddPart, p1));
                        operations.push_back(UndoOp(UndoOp::AddPart, p2));
                        }
                  else if (t >= startTicks) {
                        operations.push_back(UndoOp(UndoOp::ModifyPartTick, part, part->tick(), t + moveTicks));
                        }
                  }
            }

      adjustGlobalLists(operations, startTicks, moveTicks);

      return operations;
      }
void findMultiviewTracks(
    const TrackList<cv::Point2d>& tracks,
    const std::vector<Camera>& cameras,
    int selected,
    MultiviewTrackList<cv::Point2d>& multiview_tracks,
    int lambda1,
    int lambda2) {
  int num_views = cameras.size();
  multiview_tracks = MultiviewTrackList<cv::Point2d>(num_views);

  TrackList<cv::Point2d>::const_iterator track;
  for (track = tracks.begin(); track != tracks.end(); ++track) {
    // Find match for this track.
    MultiviewTrack<cv::Point2d> multiview_track;
    findMultiviewTrack(*track, cameras, selected, multiview_track, lambda1,
        lambda2);

    // Swap into end of list.
    multiview_tracks.push_back(MultiviewTrack<cv::Point2d>());
    multiview_tracks.back().swap(multiview_track);
  }
}
Пример #14
0
void AudioPortConfig::routingChanged()
{
    //---------------------------------------------------
    //  populate lists
    //---------------------------------------------------

    routeList->clear();
    newSrcList->clear();
    newDstList->clear();
    tracksList->clear();
    btnConnectOut->setEnabled(false);
    connectButton->setEnabled(false);
    removeButton->setEnabled(false);

    TrackList* tl = song->tracks();
    for (ciTrack i = tl->begin(); i != tl->end(); ++i)
    {
        if ((*i)->isMidiTrack())
            continue;
        AudioTrack* track = (AudioTrack*) (*i);
        if (track->type() == Track::WAVE_OUTPUT_HELPER || track->type() == Track::WAVE_INPUT_HELPER)
        {
            for (int channel = 0; channel < track->channels(); ++channel)
            {
                Route r(track, channel);
                tracksList->addItem(r.name());
            }
        }
        else
            tracksList->addItem(Route(track, -1).name());
    }
    if(selectedIndex < tracksList->count())
        tracksList->setCurrentRow(selectedIndex, QItemSelectionModel::ClearAndSelect);
    //if(_selected)
    //	setSelected(_selected->name());
}
Пример #15
0
void Route::read(Xml& xml)/*{{{*/
{
	QString s;
	int dtype = MidiDevice::ALSA_MIDI;
	int port = -1;
	unsigned char rtype = Route::TRACK_ROUTE;

	for (;;)
	{
		const QString& tag = xml.s1();
		Xml::Token token = xml.parse();
		switch (token)
		{
			case Xml::Error:
			case Xml::End:
				return;
			case Xml::Attribut:
#ifdef ROUTE_DEBUG
				printf("Route::read(): attribute:%s\n", tag.toLatin1().constData());
#endif
				if (tag == "type")
					rtype = xml.s2().toInt();
				else if (tag == "devtype")
				{
					dtype = xml.s2().toInt();
					rtype = Route::MIDI_DEVICE_ROUTE;
				}
				else if (tag == "name")
					s = xml.s2();
				else if(tag == "trackId")
				{
					trackId = xml.s2().toLongLong();
					rtype = Route::TRACK_ROUTE;
				}
				else if (tag == "mport") // p3.3.49
				{
					port = xml.s2().toInt();
					rtype = Route::MIDI_PORT_ROUTE;
				}
				else if(tag == "mportId")
				{
					midiPortId  = xml.s2().toLongLong();
					rtype = Route::MIDI_PORT_ROUTE;
				}
				else
					printf("Route::read(): unknown attribute:%s\n", tag.toLatin1().constData());
				break;
			case Xml::TagEnd:
#ifdef ROUTE_DEBUG
				printf("Route::read(): tag end type:%d channel:%d name:%s\n", rtype, channel, s.toLatin1().constData());
#endif
				if (rtype == MIDI_PORT_ROUTE)
				{
					if(midiPortId > 0)
					{
						//qDebug("Route::read(): MIDI_PORT_ROUTE Finding midiport from id");
						type = rtype;
						MidiPort *mp = oomMidiPorts.value(midiPortId);
						if(mp)
						{
							midiPort = mp->portno();
							//qDebug("Route::read(): Found midiport from id: %d", midiPort);
						}
					}
					else if (port >= 0 && port < MIDI_PORTS)
					{
						type = rtype;
						midiPort = port;
						MidiPort *mp = &midiPorts[midiPort];
						midiPortId = mp->id();
					}
					else
						printf("Route::read(): midi port <%d> out of range\n", port);
				}
				else if (!s.isEmpty())
				{
					if (rtype == TRACK_ROUTE)
					{
						if(trackId > 0)
						{
							track = song->findTrackById(trackId);
							type = rtype;
						}
						else
						{
							TrackList* tl = song->tracks();
							iTrack i = tl->begin();
							for (; i != tl->end(); ++i)
							{
								Track* t = *i;
								if (t->name() == s)
								{
									track = t;
									type = rtype;
									trackId = t->id();
									break;
								}
							}
							if (i == tl->end())
								printf("Route::read(): track <%s> not found\n", s.toLatin1().constData());
						}
					}
					else if (rtype == JACK_ROUTE)
					{
						void* jport = 0;
						if (audioDevice) //Fix from Robert at muse
							jport = audioDevice->findPort(s.toLatin1().constData());

						if (jport == 0)
							printf("Route::read(): jack port <%s> not found audioDevice(%p)\n", s.toLatin1().constData(), audioDevice);
						else
						{
							jackPort = jport;
							type = rtype;
						}
					}
					else if (rtype == MIDI_DEVICE_ROUTE)
					{
						iMidiDevice imd = midiDevices.begin();
						for (; imd != midiDevices.end(); ++imd)
						{
							MidiDevice* md = *imd;
							if (md->name() == s && md->deviceType() == dtype)
							{
								// We found a device, but if it is not in use by the song (port is -1), ignore it.
								// This prevents loading and propagation of bogus routes in the oom file.
								if (md->midiPort() == -1)
									break;

								device = md;
								type = rtype;
								break;
							}
						}
						if (imd == midiDevices.end())
							printf("Route::read(): midi device <%s> not found\n", s.toLatin1().constData());
					}
				}
				return;
			default:
				break;
		}
	}
}/*}}}*/
Пример #16
0
Route name2route(const QString& rn, bool /*dst*/, int rtype)/*{{{*/
{
	// printf("name2route %s\n", rn.toLatin1().constData());
	int channel = -1;
	QString s(rn);
	// Support old route style in oom files. Obsolete.
	if (rn.size() >= 2 && rn[0].isNumber() && rn[1] == ':')
	{
		channel = rn[0].toAscii() - int('1');
		s = rn.mid(2);
	}

	if (rtype == -1)
	{
		if (checkAudioDevice())
		{
			void* p = audioDevice->findPort(s.toLatin1().constData());
			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)
				return Route(*i, channel);
		}

		// p3.3.49
		if (s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX)
		{
			bool ok = false;
			int port = s.mid(ROUTE_MIDIPORT_NAME_PREFIX.length()).toInt(&ok);
			if (ok)
				return Route(port, 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);
				}
			}
		}// TODO Distinguish the device types
		else if (rtype == Route::MIDI_DEVICE_ROUTE)
		{
			for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i)
			{
				if ((*i)->name() == s)
					return Route(*i, channel);
			}
		}
		else if (rtype == Route::JACK_ROUTE)
		{
			if (checkAudioDevice())
			{
				void* p = audioDevice->findPort(s.toLatin1().constData());
				if (p)
					return Route(p, channel);
			}
		}
		else if (rtype == Route::MIDI_PORT_ROUTE) // p3.3.49
		{
			if (s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX)
			{
				bool ok = false;
				int port = s.mid(ROUTE_MIDIPORT_NAME_PREFIX.length()).toInt(&ok);
				if (ok)
					return Route(port, channel);
			}
		}
	}

	printf("  name2route: <%s> not found\n", rn.toLatin1().constData());
	return Route((Track*) 0, channel);
}/*}}}*/
Пример #17
0
void globalCut(bool onlySelectedTracks)
      {
      int lpos = MusEGlobal::song->lpos();
      int rpos = MusEGlobal::song->rpos();
      if ((lpos - rpos) >= 0)
            return;

      Undo operations;
      TrackList* tracks = MusEGlobal::song->tracks();
      
      for (iTrack it = tracks->begin(); it != tracks->end(); ++it) {
            Track* track = *it;
            if (track == 0 || (onlySelectedTracks && !track->selected()))
                  continue;
            PartList* pl = track->parts();
            for (iPart p = pl->begin(); p != pl->end(); ++p) {
                  Part* part = p->second;
                  int t = part->tick();
                  int l = part->lenTick();
                  if (t + l <= lpos)
                        continue;
                  if ((t >= lpos) && ((t+l) <= rpos)) {
                        operations.push_back(UndoOp(UndoOp::DeletePart,part));
                        }
                  else if ((t < lpos) && ((t+l) > lpos) && ((t+l) <= rpos)) {
                      // remove part tail
                      int len = lpos - t;
                      
                      if (part->nextClone()==part) // no clones
                      {
                            // cut Events
                            const EventList& el = part->events();
                            for (ciEvent ie = el.lower_bound(len); ie != el.end(); ++ie)
                                    operations.push_back(UndoOp(UndoOp::DeleteEvent,ie->second, part, false, false));
                      }
                      operations.push_back(UndoOp(UndoOp::ModifyPartLength, part, part->lenTick(), len, true, true));
                  }
                  else if ((t < lpos) && ((t+l) > lpos) && ((t+l) > rpos)) {
                        //----------------------
                        // remove part middle
                        //----------------------
                        Part* p1;
                        Part* p2;
                        Part* p3;
                        part->splitPart(lpos, p1, p2);
                        delete p2;
                        part->splitPart(rpos, p2, p3);
                        delete p2;
                        p3->setTick(lpos);

                        MusEGlobal::song->informAboutNewParts(part,p1,p3);
                        operations.push_back(UndoOp(UndoOp::DeletePart,part));
                        operations.push_back(UndoOp(UndoOp::AddPart,p1));
                        operations.push_back(UndoOp(UndoOp::AddPart,p3));
                        }
                  else if ((t >= lpos) && (t < rpos) && (t+l) > rpos) {
                        // remove part head
                        
                        Part* p1;
                        Part* p2;
                        part->splitPart(rpos, p1, p2);
                        delete p1;
                        p2->setTick(lpos);
                        
                        MusEGlobal::song->informAboutNewParts(part,p2);
                        operations.push_back(UndoOp(UndoOp::DeletePart,part));
                        operations.push_back(UndoOp(UndoOp::AddPart,p2));
                        }
                  else if (t >= rpos) {
                        // move part to the left
                        int nt = part->tick();
                        operations.push_back(UndoOp(UndoOp::ModifyPartTick,part,part->tick(), nt - (rpos -lpos) ));
                        }
                  }
            }
      int diff = lpos - rpos;
      adjustGlobalLists(operations, lpos, diff);

      MusEGlobal::song->applyOperationGroup(operations);
      }
Пример #18
0
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;
}