/* Rosegarden A MIDI and audio sequencer and musical notation editor. This program is Copyright 2000-2008 Guillaume Laurent , Chris Cannam , Richard Bown The moral rights of Guillaume Laurent, Chris Cannam, and Richard Bown to claim authorship of this work have been asserted. Other copyrights also apply to some parts of this work. Please see the AUTHORS file and individual file headers for details. 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; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "HydrogenXMLHandler.h" #include "base/Event.h" #include "base/BaseProperties.h" #include #include "misc/Debug.h" #include "misc/Strings.h" #include "base/Composition.h" #include "base/Instrument.h" #include "base/MidiProgram.h" #include "base/NotationTypes.h" #include "base/Segment.h" #include "base/Track.h" #include namespace Rosegarden { HydrogenXMLHandler::HydrogenXMLHandler(Composition *composition, InstrumentId drumIns): m_composition(composition), m_drumInstrument(drumIns), m_inNote(false), m_inInstrument(false), m_inPattern(false), m_inSequence(false), m_patternName(""), m_patternSize(0), m_sequenceName(""), m_position(0), m_velocity(0.0), m_panL(0.0), m_panR(0.0), m_pitch(0.0), m_instrument(0), m_id(0), m_muted(false), m_fileName(""), m_bpm(0), m_volume(0.0), m_name(""), m_author(""), m_notes(""), m_songMode(false), m_version(""), m_currentProperty(""), m_segment(0), m_currentTrackNb(0), m_segmentAdded(false), m_currentBar(0), m_newSegment(false) {} bool HydrogenXMLHandler::startDocument() { RG_DEBUG << "HydrogenXMLHandler::startDocument" << endl; m_inNote = false; m_inInstrument = false; m_inPattern = false; m_inSequence = false; // Pattern attributes // m_patternName = ""; m_patternSize = 0; // Sequence attributes // m_sequenceName = ""; // Note attributes // m_position = 0; m_velocity = 0.0; m_panL = 0.0; m_panR = 0.0; m_pitch = 0.0; m_instrument = 0; // Instrument attributes // m_id = 0; m_muted = false; m_instrumentVolumes.clear(); m_fileName = ""; // Global attributes // m_bpm = 0; m_volume = 0.0; m_name = ""; m_author = ""; m_notes = ""; m_songMode = false; m_version = ""; m_currentProperty = ""; m_segment = 0; m_currentTrackNb = 0; m_segmentAdded = 0; m_currentBar = 0; m_newSegment = false; return true; } bool HydrogenXMLHandler::startElement(const TQString& /*namespaceURI*/, const TQString& /*localName*/, const TQString& qName, const TQXmlAttributes& /*atts*/) { TQString lcName = qName.lower(); if (lcName == "note") { if (m_inInstrument) return false; m_inNote = true; } else if (lcName == "instrument") { // Beware instrument attributes inside Notes if (!m_inNote) m_inInstrument = true; } else if (lcName == "pattern") { m_inPattern = true; m_segmentAdded = false; // flag the segments being added } else if (lcName == "sequence") { // Create a new segment and set some flags // m_segment = new Segment(); m_newSegment = true; m_inSequence = true; } m_currentProperty = lcName; return true; } bool HydrogenXMLHandler::endElement(const TQString& /*namespaceURI*/, const TQString& /*localName*/, const TQString& qName) { TQString lcName = qName.lower(); if (lcName == "note") { RG_DEBUG << "HydrogenXMLHandler::endElement - Hydrogen Note : position = " << m_position << ", velocity = " << m_velocity << ", panL = " << m_panL << ", panR = " << m_panR << ", pitch = " << m_pitch << ", instrument = " << m_instrument << endl; timeT barLength = m_composition->getBarEnd(m_currentBar) - m_composition->getBarStart(m_currentBar); timeT pos = m_composition->getBarStart(m_currentBar) + timeT( double(m_position) / double(m_patternSize) * double(barLength)); // Insert a rest if we've got a new segment // if (m_newSegment) { Event *restEvent = new Event(Note::EventRestType, m_composition->getBarStart(m_currentBar), pos - m_composition->getBarStart(m_currentBar), Note::EventRestSubOrdering); m_segment->insert(restEvent); m_newSegment = false; } // Create and insert this event // Event *noteEvent = new Event(Note::EventType, pos, Note(Note::Semiquaver).getDuration()); // get drum mapping from instrument and calculate velocity noteEvent->set ( BaseProperties::PITCH, 36 + m_instrument); noteEvent->set (BaseProperties::VELOCITY, int(127.0 * m_velocity * m_volume * m_instrumentVolumes[m_instrument])); m_segment->insert(noteEvent); m_inNote = false; } else if (lcName == "instrument" && m_inInstrument) { RG_DEBUG << "HydrogenXMLHandler::endElement - Hydrogen Instrument : id = " << m_id << ", muted = " << m_muted << ", volume = " << m_instrumentVolumes[m_instrument] << ", filename = \"" << m_fileName << "\"" << endl; m_inInstrument = false; } else if (lcName == "pattern") { m_inPattern = false; if (m_segmentAdded) { // Add a blank track to demarcate patterns // Track *track = new Track (m_currentTrackNb, m_drumInstrument, m_currentTrackNb, "", false); m_currentTrackNb++; m_composition->addTrack(track); m_segmentAdded = false; // Each pattern has it's own bar so that the imported // song shows off each pattern a bar at a time. // m_currentBar++; } } else if (lcName == "sequence") { // If we're closing out a sequencer tab and we have a m_segment then // we should close up and add that segment. Only create if we have // some Events in it // if (m_segment->size() > 0) { m_segment->setTrack(m_currentTrackNb); Track *track = new Track (m_currentTrackNb, m_drumInstrument, m_currentTrackNb, m_patternName, false); m_currentTrackNb++; // Enforce start and end markers for this bar so that we have a // whole bar unit segment. // m_segment->setEndMarkerTime(m_composition->getBarEnd(m_currentBar)); TQString label = TQString("%1 - %2 %3 %4").arg(strtoqstr(m_patternName)) .arg(strtoqstr(m_sequenceName)) .arg(i18n(" imported from Hydrogen ")).arg(strtoqstr(m_version)); m_segment->setLabel(qstrtostr(label)); m_composition->addTrack(track); m_composition->addSegment(m_segment); m_segment = 0; m_segmentAdded = true; } m_inSequence = false; } return true; } bool HydrogenXMLHandler::characters(const TQString& chars) { TQString ch = chars.stripWhiteSpace(); if (ch == "") return true; if (m_inNote) { if (m_currentProperty == "position") { m_position = ch.toInt(); } else if (m_currentProperty == "velocity") { m_velocity = qstrtodouble(ch); } else if (m_currentProperty == "pan_L") { m_panL = qstrtodouble(ch); } else if (m_currentProperty == "pan_R") { m_panR = qstrtodouble(ch); } else if (m_currentProperty == "pitch") { m_pitch = qstrtodouble(ch); } else if (m_currentProperty == "instrument") { m_instrument = ch.toInt(); // Standard kit conversion - hardcoded conversion for Hyrdogen's default // drum kit. The m_instrument mapping for low values maps well onto the // kick drum GM kit starting point (MIDI pitch = 36). // switch (m_instrument) { case 11: // Cowbell m_instrument = 20; break; case 12: // Ride Jazz m_instrument = 15; break; case 14: // Ride Rock m_instrument = 17; break; case 15: // Crash Jazz m_instrument = 16; break; default: break; } } } else if (m_inInstrument) { if (m_currentProperty == "id") { m_id = ch.toInt(); } else if (m_currentProperty == "ismuted") { if (ch.lower() == "true") m_muted = true; else m_muted = false; } else if (m_currentProperty == "filename") { m_fileName = qstrtostr(chars); // don't strip whitespace from the filename } else if (m_currentProperty == "volume") { m_instrumentVolumes.push_back(qstrtodouble(ch)); } } else if (m_inPattern) { // Pattern attributes if (m_currentProperty == "name") { if (m_inSequence) m_sequenceName = qstrtostr(chars); else m_patternName = qstrtostr(chars); } else if (m_currentProperty == "size") { m_patternSize = ch.toInt(); } } else { // Global attributes if (m_currentProperty == "version") { m_version = qstrtostr(chars); } else if (m_currentProperty == "bpm") { m_bpm = qstrtodouble(ch); m_composition->addTempoAtTime (0, Composition::getTempoForQpm(m_bpm)); } else if (m_currentProperty == "volume") { m_volume = qstrtodouble(ch); } else if (m_currentProperty == "name") { m_name = qstrtostr(chars); } else if (m_currentProperty == "author") { m_author = qstrtostr(chars); } else if (m_currentProperty == "notes") { m_notes = qstrtostr(chars); } else if (m_currentProperty == "mode") { if (ch.lower() == "song") m_songMode = true; else m_songMode = false; } } return true; } bool HydrogenXMLHandler::endDocument() { RG_DEBUG << "HydrogenXMLHandler::endDocument" << endl; return true; } }