You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2367 lines
77 KiB
2367 lines
77 KiB
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
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 "RoseXmlHandler.h"
|
|
|
|
#include "sound/Midi.h"
|
|
#include <tdelocale.h>
|
|
#include "misc/Debug.h"
|
|
#include "misc/Strings.h"
|
|
#include "base/AudioLevel.h"
|
|
#include "base/AudioPluginInstance.h"
|
|
#include "base/BaseProperties.h"
|
|
#include "base/Colour.h"
|
|
#include "base/ColourMap.h"
|
|
#include "base/Composition.h"
|
|
#include "base/ControlParameter.h"
|
|
#include "base/Device.h"
|
|
#include "base/Instrument.h"
|
|
#include "base/Marker.h"
|
|
#include "base/MidiDevice.h"
|
|
#include "base/MidiProgram.h"
|
|
#include "base/MidiTypes.h"
|
|
#include "base/NotationTypes.h"
|
|
#include "base/RealTime.h"
|
|
#include "base/Segment.h"
|
|
#include "base/Studio.h"
|
|
#include "base/Track.h"
|
|
#include "base/TriggerSegment.h"
|
|
#include "gui/application/RosegardenGUIApp.h"
|
|
#include "gui/application/RosegardenApplication.h"
|
|
#include "gui/dialogs/FileLocateDialog.h"
|
|
#include "gui/general/ProgressReporter.h"
|
|
#include "gui/kdeext/TDEStartupLogo.h"
|
|
#include "gui/studio/AudioPlugin.h"
|
|
#include "gui/studio/AudioPluginManager.h"
|
|
#include "gui/widgets/CurrentProgressDialog.h"
|
|
#include "gui/widgets/ProgressDialog.h"
|
|
#include "RosegardenGUIDoc.h"
|
|
#include "sound/AudioFileManager.h"
|
|
#include <tdefiledialog.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tqcstring.h>
|
|
#include <tqdatastream.h>
|
|
#include <tqdialog.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqstring.h>
|
|
#include <tqstringlist.h>
|
|
#include "XmlStorableEvent.h"
|
|
#include "XmlSubHandler.h"
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
using namespace BaseProperties;
|
|
|
|
class ConfigurationXmlSubHandler : public XmlSubHandler
|
|
{
|
|
public:
|
|
ConfigurationXmlSubHandler(const TQString &elementName,
|
|
Rosegarden::Configuration *configuration);
|
|
|
|
virtual bool startElement(const TQString& namespaceURI,
|
|
const TQString& localName,
|
|
const TQString& qName,
|
|
const TQXmlAttributes& atts);
|
|
|
|
virtual bool endElement(const TQString& namespaceURI,
|
|
const TQString& localName,
|
|
const TQString& qName,
|
|
bool& finished);
|
|
|
|
virtual bool characters(const TQString& ch);
|
|
|
|
//--------------- Data members ---------------------------------
|
|
|
|
Rosegarden::Configuration *m_configuration;
|
|
|
|
TQString m_elementName;
|
|
TQString m_propertyName;
|
|
TQString m_propertyType;
|
|
};
|
|
|
|
ConfigurationXmlSubHandler::ConfigurationXmlSubHandler(const TQString &elementName,
|
|
Rosegarden::Configuration *configuration)
|
|
: m_configuration(configuration),
|
|
m_elementName(elementName)
|
|
{
|
|
}
|
|
|
|
bool ConfigurationXmlSubHandler::startElement(const TQString&, const TQString&,
|
|
const TQString& lcName,
|
|
const TQXmlAttributes& atts)
|
|
{
|
|
m_propertyName = lcName;
|
|
m_propertyType = atts.value("type");
|
|
|
|
if (m_propertyName == "property") {
|
|
// handle alternative encoding for properties with arbitrary names
|
|
m_propertyName = atts.value("name");
|
|
TQString value = atts.value("value");
|
|
if (!value.isNull()) {
|
|
m_propertyType = "String";
|
|
m_configuration->set<String>(qstrtostr(m_propertyName),
|
|
qstrtostr(value));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ConfigurationXmlSubHandler::characters(const TQString& chars)
|
|
{
|
|
TQString ch = chars.stripWhiteSpace();
|
|
// this method is also called on newlines - skip these cases
|
|
if (ch.isEmpty()) return true;
|
|
|
|
|
|
if (m_propertyType == "Int") {
|
|
long i = ch.toInt();
|
|
RG_DEBUG << "\"" << m_propertyName << "\" "
|
|
<< "value = " << i << endl;
|
|
m_configuration->set<Int>(qstrtostr(m_propertyName), i);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (m_propertyType == "RealTime") {
|
|
Rosegarden::RealTime rt;
|
|
int sepIdx = ch.find(',');
|
|
|
|
rt.sec = ch.left(sepIdx).toInt();
|
|
rt.nsec = ch.mid(sepIdx + 1).toInt();
|
|
|
|
RG_DEBUG << "\"" << m_propertyName << "\" "
|
|
<< "sec = " << rt.sec << ", nsec = " << rt.nsec << endl;
|
|
|
|
m_configuration->set<Rosegarden::RealTimeT>(qstrtostr(m_propertyName), rt);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (m_propertyType == "Bool") {
|
|
TQString chLc = ch.lower();
|
|
|
|
bool b = (chLc == "true" ||
|
|
chLc == "1" ||
|
|
chLc == "on");
|
|
|
|
m_configuration->set<Rosegarden::Bool>(qstrtostr(m_propertyName), b);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!m_propertyType ||
|
|
m_propertyType == "String") {
|
|
|
|
m_configuration->set<Rosegarden::String>(qstrtostr(m_propertyName),
|
|
qstrtostr(ch));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ConfigurationXmlSubHandler::endElement(const TQString&,
|
|
const TQString&,
|
|
const TQString& lcName,
|
|
bool& finished)
|
|
{
|
|
m_propertyName = "";
|
|
m_propertyType = "";
|
|
finished = (lcName == m_elementName);
|
|
return true;
|
|
}
|
|
|
|
|
|
//----------------------------------------
|
|
|
|
|
|
|
|
RoseXmlHandler::RoseXmlHandler(RosegardenGUIDoc *doc,
|
|
unsigned int elementCount,
|
|
bool createNewDevicesWhenNeeded)
|
|
: ProgressReporter(0),
|
|
m_doc(doc),
|
|
m_currentSegment(0),
|
|
m_currentEvent(0),
|
|
m_currentTime(0),
|
|
m_chordDuration(0),
|
|
m_segmentEndMarkerTime(0),
|
|
m_inChord(false),
|
|
m_inGroup(false),
|
|
m_inComposition(false),
|
|
m_groupId(0),
|
|
m_foundTempo(false),
|
|
m_section(NoSection),
|
|
m_device(0),
|
|
m_deviceRunningId(Device::NO_DEVICE),
|
|
m_msb(0),
|
|
m_lsb(0),
|
|
m_instrument(0),
|
|
m_plugin(0),
|
|
m_pluginInBuss(false),
|
|
m_colourMap(0),
|
|
m_keyMapping(0),
|
|
m_pluginId(0),
|
|
m_totalElements(elementCount),
|
|
m_elementsSoFar(0),
|
|
m_subHandler(0),
|
|
m_deprecation(false),
|
|
m_createDevices(createNewDevicesWhenNeeded),
|
|
m_haveControls(false),
|
|
m_cancelled(false),
|
|
m_skipAllAudio(false),
|
|
m_hasActiveAudio(false)
|
|
{}
|
|
|
|
RoseXmlHandler::~RoseXmlHandler()
|
|
{
|
|
delete m_subHandler;
|
|
}
|
|
|
|
Composition &
|
|
RoseXmlHandler::getComposition()
|
|
{
|
|
return m_doc->getComposition();
|
|
}
|
|
|
|
Studio &
|
|
RoseXmlHandler::getStudio()
|
|
{
|
|
return m_doc->getStudio();
|
|
}
|
|
|
|
AudioFileManager &
|
|
RoseXmlHandler::getAudioFileManager()
|
|
{
|
|
return m_doc->getAudioFileManager();
|
|
}
|
|
|
|
AudioPluginManager *
|
|
RoseXmlHandler::getAudioPluginManager()
|
|
{
|
|
return m_doc->getPluginManager();
|
|
}
|
|
|
|
bool
|
|
RoseXmlHandler::startDocument()
|
|
{
|
|
// Clear tracks
|
|
//
|
|
getComposition().clearTracks();
|
|
|
|
// And the loop
|
|
//
|
|
getComposition().setLoopStart(0);
|
|
getComposition().setLoopEnd(0);
|
|
|
|
// All plugins
|
|
//
|
|
m_doc->clearAllPlugins();
|
|
|
|
// reset state
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
RoseXmlHandler::startElement(const TQString& namespaceURI,
|
|
const TQString& localName,
|
|
const TQString& qName, const TQXmlAttributes& atts)
|
|
{
|
|
// First check if user pressed cancel button on the progress
|
|
// dialog
|
|
//
|
|
if (isOperationCancelled()) {
|
|
// Ideally, we'd throw here, but at this point TQt is in the stack
|
|
// and TQt is very often compiled without exception support.
|
|
//
|
|
m_cancelled = true;
|
|
return false;
|
|
}
|
|
|
|
TQString lcName = qName.lower();
|
|
|
|
if (getSubHandler()) {
|
|
return getSubHandler()->startElement(namespaceURI, localName, lcName, atts);
|
|
}
|
|
|
|
if (lcName == "event") {
|
|
|
|
// RG_DEBUG << "RoseXmlHandler::startElement: found event, current time is " << m_currentTime << endl;
|
|
|
|
if (m_currentEvent) {
|
|
RG_DEBUG << "RoseXmlHandler::startElement: Warning: new event found at time " << m_currentTime << " before previous event has ended; previous event will be lost" << endl;
|
|
delete m_currentEvent;
|
|
}
|
|
|
|
m_currentEvent = new XmlStorableEvent(atts, m_currentTime);
|
|
|
|
if (m_currentEvent->has(BEAMED_GROUP_ID)) {
|
|
|
|
// remap -- we want to ensure that the segment's nextId
|
|
// is always used (and incremented) in preference to the
|
|
// stored id
|
|
|
|
if (!m_currentSegment) {
|
|
m_errorString = "Got grouped event outside of a segment";
|
|
return false;
|
|
}
|
|
|
|
long storedId = m_currentEvent->get
|
|
<Int>(BEAMED_GROUP_ID);
|
|
|
|
if (m_groupIdMap.find(storedId) == m_groupIdMap.end()) {
|
|
m_groupIdMap[storedId] = m_currentSegment->getNextId();
|
|
}
|
|
|
|
m_currentEvent->set
|
|
<Int>(BEAMED_GROUP_ID, m_groupIdMap[storedId]);
|
|
|
|
} else if (m_inGroup) {
|
|
m_currentEvent->set
|
|
<Int>(BEAMED_GROUP_ID, m_groupId);
|
|
m_currentEvent->set
|
|
<String>(BEAMED_GROUP_TYPE, m_groupType);
|
|
if (m_groupType == GROUP_TYPE_TUPLED) {
|
|
m_currentEvent->set
|
|
<Int>
|
|
(BEAMED_GROUP_TUPLET_BASE, m_groupTupletBase);
|
|
m_currentEvent->set
|
|
<Int>
|
|
(BEAMED_GROUP_TUPLED_COUNT, m_groupTupledCount);
|
|
m_currentEvent->set
|
|
<Int>
|
|
(BEAMED_GROUP_UNTUPLED_COUNT, m_groupUntupledCount);
|
|
}
|
|
}
|
|
|
|
timeT duration = m_currentEvent->getDuration();
|
|
|
|
if (!m_inChord) {
|
|
|
|
m_currentTime = m_currentEvent->getAbsoluteTime() + duration;
|
|
|
|
// RG_DEBUG << "RoseXmlHandler::startElement: (we're not in a chord) " << endl;
|
|
|
|
} else if (duration != 0) {
|
|
|
|
// set chord duration to the duration of the shortest
|
|
// element with a non-null duration (if no such elements,
|
|
// leave it as 0).
|
|
|
|
if (m_chordDuration == 0 || duration < m_chordDuration) {
|
|
m_chordDuration = duration;
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "property") {
|
|
|
|
if (!m_currentEvent) {
|
|
RG_DEBUG << "RoseXmlHandler::startElement: Warning: Found property outside of event at time " << m_currentTime << ", ignoring" << endl;
|
|
} else {
|
|
m_currentEvent->setPropertyFromAttributes(atts, true);
|
|
}
|
|
|
|
} else if (lcName == "nproperty") {
|
|
|
|
if (!m_currentEvent) {
|
|
RG_DEBUG << "RoseXmlHandler::startElement: Warning: Found nproperty outside of event at time " << m_currentTime << ", ignoring" << endl;
|
|
} else {
|
|
m_currentEvent->setPropertyFromAttributes(atts, false);
|
|
}
|
|
|
|
} else if (lcName == "chord") {
|
|
|
|
m_inChord = true;
|
|
|
|
} else if (lcName == "group") {
|
|
|
|
if (!m_currentSegment) {
|
|
m_errorString = "Got group outside of a segment";
|
|
return false;
|
|
}
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"group\". We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
m_inGroup = true;
|
|
m_groupId = m_currentSegment->getNextId();
|
|
m_groupType = qstrtostr(atts.value("type"));
|
|
|
|
if (m_groupType == GROUP_TYPE_TUPLED) {
|
|
m_groupTupletBase = atts.value("base").toInt();
|
|
m_groupTupledCount = atts.value("tupled").toInt();
|
|
m_groupUntupledCount = atts.value("untupled").toInt();
|
|
}
|
|
|
|
} else if (lcName == "rosegarden-data") {
|
|
|
|
// FILE FORMAT VERSIONING -- see comments in
|
|
// rosegardenguidoc.cpp. We only care about major and minor
|
|
// here, not point.
|
|
|
|
TQString version = atts.value("version");
|
|
TQString smajor = atts.value("format-version-major");
|
|
TQString sminor = atts.value("format-version-minor");
|
|
|
|
// std::cerr << "\n\n\nRosegarden file version = \"" << version << "\"\n\n\n" << std::endl;
|
|
|
|
if (!smajor.isNull()) {
|
|
|
|
int major = smajor.toInt();
|
|
int minor = sminor.toInt();
|
|
|
|
if (major > RosegardenGUIDoc::FILE_FORMAT_VERSION_MAJOR) {
|
|
m_errorString = i18n("This file was written by Rosegarden %1, and it uses\na different file format that cannot be read by this version.").arg(version);
|
|
return false;
|
|
}
|
|
|
|
if (major == RosegardenGUIDoc::FILE_FORMAT_VERSION_MAJOR &&
|
|
minor > RosegardenGUIDoc::FILE_FORMAT_VERSION_MINOR) {
|
|
|
|
CurrentProgressDialog::freeze();
|
|
TDEStartupLogo::hideIfStillThere();
|
|
|
|
KMessageBox::information(0, i18n("This file was written by Rosegarden %1, which is more recent than this version.\nThere may be some incompatibilities with the file format.").arg(version));
|
|
|
|
CurrentProgressDialog::thaw();
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "studio") {
|
|
|
|
if (m_section != NoSection) {
|
|
m_errorString = "Found Studio in another section";
|
|
return false;
|
|
}
|
|
|
|
// In the Studio we clear down everything apart from Devices and
|
|
// Instruments before we reload. Instruments are derived from
|
|
// the Sequencer, the bank/program information is loaded from
|
|
// the file we're currently examining.
|
|
//
|
|
getStudio().clearMidiBanksAndPrograms();
|
|
getStudio().clearBusses();
|
|
getStudio().clearRecordIns();
|
|
|
|
m_section = InStudio; // set top level section
|
|
|
|
// Get and set MIDI filters
|
|
//
|
|
TQString thruStr = atts.value("thrufilter");
|
|
|
|
if (!thruStr.isNull())
|
|
getStudio().setMIDIThruFilter(thruStr.toInt());
|
|
|
|
TQString recordStr = atts.value("recordfilter");
|
|
|
|
if (!recordStr.isNull())
|
|
getStudio().setMIDIRecordFilter(recordStr.toInt());
|
|
|
|
TQString inputStr = atts.value("audioinputpairs");
|
|
|
|
if (!inputStr.isNull()) {
|
|
int inputs = inputStr.toInt();
|
|
if (inputs < 1)
|
|
inputs = 1; // we simply don't permit no inputs
|
|
while (int(getStudio().getRecordIns().size()) < inputs) {
|
|
getStudio().addRecordIn(new RecordIn());
|
|
}
|
|
}
|
|
|
|
TQString mixerStr = atts.value("mixerdisplayoptions");
|
|
|
|
if (!mixerStr.isNull()) {
|
|
unsigned int mixer = mixerStr.toUInt();
|
|
getStudio().setMixerDisplayOptions(mixer);
|
|
}
|
|
|
|
TQString metronomeStr = atts.value("metronomedevice");
|
|
|
|
if (!metronomeStr.isNull()) {
|
|
DeviceId metronome = metronomeStr.toUInt();
|
|
getStudio().setMetronomeDevice(metronome);
|
|
}
|
|
|
|
} else if (lcName == "timesignature") {
|
|
|
|
if (m_inComposition == false) {
|
|
m_errorString = "TimeSignature object found outside Composition";
|
|
return false;
|
|
}
|
|
|
|
timeT t = 0;
|
|
TQString timeStr = atts.value("time");
|
|
if (!timeStr.isNull())
|
|
t = timeStr.toInt();
|
|
|
|
int num = 4;
|
|
TQString numStr = atts.value("numerator");
|
|
if (!numStr.isNull())
|
|
num = numStr.toInt();
|
|
|
|
int denom = 4;
|
|
TQString denomStr = atts.value("denominator");
|
|
if (!denomStr.isNull())
|
|
denom = denomStr.toInt();
|
|
|
|
bool common = false;
|
|
TQString commonStr = atts.value("common");
|
|
if (!commonStr.isNull())
|
|
common = (commonStr == "true");
|
|
|
|
bool hidden = false;
|
|
TQString hiddenStr = atts.value("hidden");
|
|
if (!hiddenStr.isNull())
|
|
hidden = (hiddenStr == "true");
|
|
|
|
bool hiddenBars = false;
|
|
TQString hiddenBarsStr = atts.value("hiddenbars");
|
|
if (!hiddenBarsStr.isNull())
|
|
hiddenBars = (hiddenBarsStr == "true");
|
|
|
|
getComposition().addTimeSignature
|
|
(t, TimeSignature(num, denom, common, hidden, hiddenBars));
|
|
|
|
} else if (lcName == "tempo") {
|
|
|
|
timeT t = 0;
|
|
TQString timeStr = atts.value("time");
|
|
if (!timeStr.isNull())
|
|
t = timeStr.toInt();
|
|
|
|
tempoT tempo = Composition::getTempoForQpm(120.0);
|
|
TQString tempoStr = atts.value("tempo");
|
|
TQString targetStr = atts.value("target");
|
|
TQString bphStr = atts.value("bph");
|
|
if (!tempoStr.isNull()) {
|
|
tempo = tempoStr.toInt();
|
|
} else if (!bphStr.isNull()) {
|
|
tempo = Composition::getTempoForQpm
|
|
(double(bphStr.toInt()) / 60.0);
|
|
}
|
|
|
|
if (!targetStr.isNull()) {
|
|
getComposition().addTempoAtTime(t, tempo, targetStr.toInt());
|
|
} else {
|
|
getComposition().addTempoAtTime(t, tempo);
|
|
}
|
|
|
|
} else if (lcName == "composition") {
|
|
|
|
if (m_section != NoSection) {
|
|
m_errorString = "Found Composition in another section";
|
|
return false;
|
|
}
|
|
|
|
// set Segment
|
|
m_section = InComposition;
|
|
|
|
// Get and set the record track
|
|
//
|
|
TQString recordStr = atts.value("recordtrack");
|
|
if (!recordStr.isNull()) {
|
|
getComposition().setTrackRecording(recordStr.toInt(), true);
|
|
}
|
|
|
|
TQString recordPlStr = atts.value("recordtracks");
|
|
if (!recordPlStr.isNull()) {
|
|
RG_DEBUG << "Record tracks: " << recordPlStr << endl;
|
|
TQStringList recordList = TQStringList::split(',', recordPlStr);
|
|
for (TQStringList::iterator i = recordList.begin();
|
|
i != recordList.end(); ++i) {
|
|
RG_DEBUG << "Record track: " << (*i).toInt() << endl;
|
|
getComposition().setTrackRecording((*i).toInt(), true);
|
|
}
|
|
}
|
|
|
|
// Get and set the position pointer
|
|
//
|
|
int position = 0;
|
|
TQString positionStr = atts.value("pointer");
|
|
if (!positionStr.isNull()) {
|
|
position = positionStr.toInt();
|
|
}
|
|
|
|
getComposition().setPosition(position);
|
|
|
|
|
|
// Get and (eventually) set the default tempo.
|
|
// We prefer the new compositionDefaultTempo over the
|
|
// older defaultTempo.
|
|
//
|
|
TQString tempoStr = atts.value("compositionDefaultTempo");
|
|
if (!tempoStr.isNull()) {
|
|
tempoT tempo = tempoT(tempoStr.toInt());
|
|
getComposition().setCompositionDefaultTempo(tempo);
|
|
} else {
|
|
tempoStr = atts.value("defaultTempo");
|
|
if (!tempoStr.isNull()) {
|
|
double tempo = qstrtodouble(tempoStr);
|
|
getComposition().setCompositionDefaultTempo
|
|
(Composition::getTempoForQpm(tempo));
|
|
}
|
|
}
|
|
|
|
// set the composition flag
|
|
m_inComposition = true;
|
|
|
|
|
|
// Set the loop
|
|
//
|
|
TQString loopStartStr = atts.value("loopstart");
|
|
TQString loopEndStr = atts.value("loopend");
|
|
|
|
if (!loopStartStr.isNull() && !loopEndStr.isNull()) {
|
|
int loopStart = loopStartStr.toInt();
|
|
int loopEnd = loopEndStr.toInt();
|
|
|
|
getComposition().setLoopStart(loopStart);
|
|
getComposition().setLoopEnd(loopEnd);
|
|
}
|
|
|
|
TQString selectedTrackStr = atts.value("selected");
|
|
|
|
if (!selectedTrackStr.isNull()) {
|
|
TrackId selectedTrack =
|
|
(TrackId)selectedTrackStr.toInt();
|
|
|
|
getComposition().setSelectedTrack(selectedTrack);
|
|
}
|
|
|
|
TQString soloTrackStr = atts.value("solo");
|
|
if (!soloTrackStr.isNull()) {
|
|
if (soloTrackStr.toInt() == 1)
|
|
getComposition().setSolo(true);
|
|
else
|
|
getComposition().setSolo(false);
|
|
}
|
|
|
|
|
|
TQString playMetStr = atts.value("playmetronome");
|
|
if (!playMetStr.isNull()) {
|
|
if (playMetStr.toInt())
|
|
getComposition().setPlayMetronome(true);
|
|
else
|
|
getComposition().setPlayMetronome(false);
|
|
}
|
|
|
|
TQString recMetStr = atts.value("recordmetronome");
|
|
if (!recMetStr.isNull()) {
|
|
if (recMetStr.toInt())
|
|
getComposition().setRecordMetronome(true);
|
|
else
|
|
getComposition().setRecordMetronome(false);
|
|
}
|
|
|
|
TQString nextTriggerIdStr = atts.value("nexttriggerid");
|
|
if (!nextTriggerIdStr.isNull()) {
|
|
getComposition().setNextTriggerSegmentId(nextTriggerIdStr.toInt());
|
|
}
|
|
|
|
TQString copyrightStr = atts.value("copyright");
|
|
if (!copyrightStr.isNull()) {
|
|
getComposition().setCopyrightNote(qstrtostr(copyrightStr));
|
|
}
|
|
|
|
TQString startMarkerStr = atts.value("startMarker");
|
|
TQString endMarkerStr = atts.value("endMarker");
|
|
|
|
if (!startMarkerStr.isNull()) {
|
|
getComposition().setStartMarker(startMarkerStr.toInt());
|
|
}
|
|
|
|
if (!endMarkerStr.isNull()) {
|
|
getComposition().setEndMarker(endMarkerStr.toInt());
|
|
}
|
|
|
|
} else if (lcName == "track") {
|
|
|
|
if (m_section != InComposition) {
|
|
m_errorString = "Track object found outside Composition";
|
|
return false;
|
|
}
|
|
|
|
int id = -1;
|
|
int position = -1;
|
|
int instrument = -1;
|
|
std::string label;
|
|
bool muted = false;
|
|
|
|
TQString trackNbStr = atts.value("id");
|
|
if (!trackNbStr.isNull()) {
|
|
id = trackNbStr.toInt();
|
|
}
|
|
|
|
TQString labelStr = atts.value("label");
|
|
if (!labelStr.isNull()) {
|
|
label = qstrtostr(labelStr);
|
|
}
|
|
|
|
TQString mutedStr = atts.value("muted");
|
|
if (!mutedStr.isNull()) {
|
|
if (mutedStr == "true")
|
|
muted = true;
|
|
else
|
|
muted = false;
|
|
}
|
|
|
|
TQString positionStr = atts.value("position");
|
|
if (!positionStr.isNull()) {
|
|
position = positionStr.toInt();
|
|
}
|
|
|
|
TQString instrumentStr = atts.value("instrument");
|
|
if (!instrumentStr.isNull()) {
|
|
instrument = instrumentStr.toInt();
|
|
}
|
|
|
|
Track *track = new Track(id,
|
|
instrument,
|
|
position,
|
|
label,
|
|
muted);
|
|
|
|
// track properties affecting newly created segments are initialized
|
|
// to default values in the ctor, so they don't need to be initialized
|
|
// here
|
|
|
|
TQString presetLabelStr = atts.value("defaultLabel");
|
|
if (!labelStr.isNull()) {
|
|
track->setPresetLabel(presetLabelStr.ascii());
|
|
}
|
|
|
|
TQString clefStr = atts.value("defaultClef");
|
|
if (!clefStr.isNull()) {
|
|
track->setClef(clefStr.toInt());
|
|
}
|
|
|
|
TQString transposeStr = atts.value("defaultTranspose");
|
|
if (!transposeStr.isNull()) {
|
|
track->setTranspose(transposeStr.toInt());
|
|
}
|
|
|
|
TQString colorStr = atts.value("defaultColour");
|
|
if (!colorStr.isNull()) {
|
|
track->setColor(colorStr.toInt());
|
|
}
|
|
|
|
TQString highplayStr = atts.value("defaultHighestPlayable");
|
|
if (!highplayStr.isNull()) {
|
|
track->setHighestPlayable(highplayStr.toInt());
|
|
}
|
|
|
|
TQString lowplayStr = atts.value("defaultLowestPlayable");
|
|
if (!lowplayStr.isNull()) {
|
|
track->setLowestPlayable(lowplayStr.toInt());
|
|
}
|
|
|
|
TQString staffSizeStr = atts.value("staffSize");
|
|
if (!staffSizeStr.isNull()) {
|
|
track->setStaffSize(staffSizeStr.toInt());
|
|
}
|
|
|
|
TQString staffBracketStr = atts.value("staffBracket");
|
|
if (!staffBracketStr.isNull()) {
|
|
track->setStaffBracket(staffBracketStr.toInt());
|
|
}
|
|
|
|
getComposition().addTrack(track);
|
|
|
|
|
|
} else if (lcName == "segment") {
|
|
|
|
if (m_section != NoSection) {
|
|
m_errorString = "Found Segment in another section";
|
|
return false;
|
|
}
|
|
|
|
// set Segment
|
|
m_section = InSegment;
|
|
|
|
int track = -1, startTime = 0;
|
|
unsigned int colourindex = 0;
|
|
TQString trackNbStr = atts.value("track");
|
|
if (!trackNbStr.isNull()) {
|
|
track = trackNbStr.toInt();
|
|
}
|
|
|
|
TQString startIdxStr = atts.value("start");
|
|
if (!startIdxStr.isNull()) {
|
|
startTime = startIdxStr.toInt();
|
|
}
|
|
|
|
TQString segmentType = (atts.value("type")).lower();
|
|
if (!segmentType.isNull()) {
|
|
if (segmentType == "audio") {
|
|
int audioFileId = atts.value("file").toInt();
|
|
|
|
// check this file id exists on the AudioFileManager
|
|
|
|
if (getAudioFileManager().fileExists(audioFileId) == false) {
|
|
// We don't report an error as this audio file might've
|
|
// been excluded deliberately as we could't actually
|
|
// find the audio file itself.
|
|
//
|
|
return true;
|
|
}
|
|
|
|
// Create an Audio segment and add its reference
|
|
//
|
|
m_currentSegment = new Segment(Segment::Audio);
|
|
m_currentSegment->setAudioFileId(audioFileId);
|
|
m_currentSegment->setStartTime(startTime);
|
|
} else {
|
|
// Create a (normal) internal Segment
|
|
m_currentSegment = new Segment(Segment::Internal);
|
|
}
|
|
|
|
} else {
|
|
// for the moment we default
|
|
m_currentSegment = new Segment(Segment::Internal);
|
|
}
|
|
|
|
TQString repeatStr = atts.value("repeat");
|
|
if (repeatStr.lower() == "true") {
|
|
m_currentSegment->setRepeating(true);
|
|
}
|
|
|
|
TQString delayStr = atts.value("delay");
|
|
if (!delayStr.isNull()) {
|
|
RG_DEBUG << "Delay string is \"" << delayStr << "\"" << endl;
|
|
long delay = delayStr.toLong();
|
|
RG_DEBUG << "Delay is " << delay << endl;
|
|
m_currentSegment->setDelay(delay);
|
|
}
|
|
|
|
TQString rtDelaynSec = atts.value("rtdelaynsec");
|
|
TQString rtDelayuSec = atts.value("rtdelayusec");
|
|
TQString rtDelaySec = atts.value("rtdelaysec");
|
|
if (!rtDelaySec.isNull() && (!rtDelaynSec.isNull() || !rtDelayuSec.isNull())) {
|
|
if (!rtDelaynSec.isNull()) {
|
|
m_currentSegment->setRealTimeDelay
|
|
(RealTime(rtDelaySec.toInt(),
|
|
rtDelaynSec.toInt()));
|
|
} else {
|
|
m_currentSegment->setRealTimeDelay
|
|
(RealTime(rtDelaySec.toInt(),
|
|
rtDelayuSec.toInt() * 1000));
|
|
}
|
|
}
|
|
|
|
TQString transposeStr = atts.value("transpose");
|
|
if (!transposeStr.isNull())
|
|
m_currentSegment->setTranspose(transposeStr.toInt());
|
|
|
|
// fill in the label
|
|
TQString labelStr = atts.value("label");
|
|
if (!labelStr.isNull())
|
|
m_currentSegment->setLabel(qstrtostr(labelStr));
|
|
|
|
m_currentSegment->setTrack(track);
|
|
//m_currentSegment->setStartTime(startTime);
|
|
|
|
TQString colourIndStr = atts.value("colourindex");
|
|
if (!colourIndStr.isNull()) {
|
|
colourindex = colourIndStr.toInt();
|
|
}
|
|
|
|
m_currentSegment->setColourIndex(colourindex);
|
|
|
|
TQString snapGridSizeStr = atts.value("snapgridsize");
|
|
if (!snapGridSizeStr.isNull()) {
|
|
m_currentSegment->setSnapGridSize(snapGridSizeStr.toInt());
|
|
}
|
|
|
|
TQString viewFeaturesStr = atts.value("viewfeatures");
|
|
if (!viewFeaturesStr.isNull()) {
|
|
m_currentSegment->setViewFeatures(viewFeaturesStr.toInt());
|
|
}
|
|
|
|
m_currentTime = startTime;
|
|
|
|
TQString triggerIdStr = atts.value("triggerid");
|
|
TQString triggerPitchStr = atts.value("triggerbasepitch");
|
|
TQString triggerVelocityStr = atts.value("triggerbasevelocity");
|
|
TQString triggerRetuneStr = atts.value("triggerretune");
|
|
TQString triggerAdjustTimeStr = atts.value("triggeradjusttimes");
|
|
|
|
if (!triggerIdStr.isNull()) {
|
|
int pitch = -1;
|
|
if (!triggerPitchStr.isNull())
|
|
pitch = triggerPitchStr.toInt();
|
|
int velocity = -1;
|
|
if (!triggerVelocityStr.isNull())
|
|
velocity = triggerVelocityStr.toInt();
|
|
TriggerSegmentRec *rec =
|
|
getComposition().addTriggerSegment(m_currentSegment,
|
|
triggerIdStr.toInt(),
|
|
pitch, velocity);
|
|
if (rec) {
|
|
if (!triggerRetuneStr.isNull())
|
|
rec->setDefaultRetune(triggerRetuneStr.lower() == "true");
|
|
if (!triggerAdjustTimeStr.isNull())
|
|
rec->setDefaultTimeAdjust(qstrtostr(triggerAdjustTimeStr));
|
|
}
|
|
m_currentSegment->setStartTimeDataMember(startTime);
|
|
} else {
|
|
getComposition().addSegment(m_currentSegment);
|
|
getComposition().setSegmentStartTime(m_currentSegment, startTime);
|
|
}
|
|
|
|
TQString endMarkerStr = atts.value("endmarker");
|
|
if (!endMarkerStr.isNull()) {
|
|
delete m_segmentEndMarkerTime;
|
|
m_segmentEndMarkerTime = new timeT(endMarkerStr.toInt());
|
|
}
|
|
|
|
m_groupIdMap.clear();
|
|
|
|
} else if (lcName == "gui") {
|
|
|
|
if (m_section != InSegment) {
|
|
m_errorString = "Found GUI element outside Segment";
|
|
return false;
|
|
}
|
|
|
|
} else if (lcName == "controller") {
|
|
|
|
if (m_section != InSegment) {
|
|
m_errorString = "Found Controller element outside Segment";
|
|
return false;
|
|
}
|
|
|
|
TQString type = atts.value("type");
|
|
//RG_DEBUG << "RoseXmlHandler::startElement - controller type = " << type << endl;
|
|
|
|
if (type == strtoqstr(PitchBend::EventType))
|
|
m_currentSegment->addEventRuler(PitchBend::EventType);
|
|
else if (type == strtoqstr(Controller::EventType)) {
|
|
TQString value = atts.value("value");
|
|
|
|
if (value != "")
|
|
m_currentSegment->addEventRuler(Controller::EventType, value.toInt());
|
|
}
|
|
|
|
} else if (lcName == "resync") {
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"resync\". We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
TQString time(atts.value("time"));
|
|
bool isNumeric;
|
|
int numTime = time.toInt(&isNumeric);
|
|
if (isNumeric)
|
|
m_currentTime = numTime;
|
|
|
|
} else if (lcName == "audio") {
|
|
|
|
if (m_section != InAudioFiles) {
|
|
m_errorString = "Audio object found outside Audio section";
|
|
return false;
|
|
}
|
|
|
|
if (m_skipAllAudio) {
|
|
std::cout << "SKIPPING audio file" << std::endl;
|
|
return true;
|
|
}
|
|
|
|
TQString id(atts.value("id"));
|
|
TQString file(atts.value("file"));
|
|
TQString label(atts.value("label"));
|
|
|
|
if (id.isEmpty() || file.isEmpty() || label.isEmpty()) {
|
|
m_errorString = "Audio object has empty parameters";
|
|
return false;
|
|
}
|
|
|
|
m_hasActiveAudio = true;
|
|
|
|
// attempt to insert file into AudioFileManager
|
|
// (this checks the integrity of the file at the
|
|
// same time)
|
|
//
|
|
if (getAudioFileManager().insertFile(qstrtostr(label),
|
|
qstrtostr(file),
|
|
id.toInt()) == false) {
|
|
// Ok, now attempt to use the KFileDialog saved default
|
|
// value for the AudioPath.
|
|
//
|
|
TQString thing;
|
|
KURL url = KFileDialog::getStartURL(TQString(":WAVS"), thing);
|
|
getAudioFileManager().setAudioPath(url.path().latin1());
|
|
|
|
/*
|
|
RG_DEBUG << "ATTEMPTING TO FIND IN PATH = "
|
|
<< url.path() << endl;
|
|
*/
|
|
|
|
if (getAudioFileManager().
|
|
insertFile(qstrtostr(label),
|
|
qstrtostr(file), id.toInt()) == false) {
|
|
|
|
// Freeze the progress dialog
|
|
CurrentProgressDialog::freeze();
|
|
|
|
// Hide splash screen if present on startup
|
|
TDEStartupLogo::hideIfStillThere();
|
|
|
|
// Create a locate file dialog - give it the file name
|
|
// and the AudioFileManager path that we've already
|
|
// tried. If we manually locate the file then we reset
|
|
// the audiofilepath to the new value and see if this
|
|
// helps us locate the rest of the files.
|
|
//
|
|
|
|
TQString newFilename = "";
|
|
TQString newPath = "";
|
|
|
|
do {
|
|
|
|
FileLocateDialog fL((RosegardenGUIApp *)m_doc->parent(),
|
|
file,
|
|
TQString(getAudioFileManager().getAudioPath().c_str()));
|
|
int result = fL.exec();
|
|
|
|
if (result == TQDialog::Accepted) {
|
|
newFilename = fL.getFilename();
|
|
newPath = fL.getDirectory();
|
|
} else if (result == TQDialog::Rejected) {
|
|
// just skip the file
|
|
break;
|
|
} else {
|
|
// don't process any more audio files
|
|
m_skipAllAudio = true;
|
|
CurrentProgressDialog::thaw();
|
|
return true;
|
|
}
|
|
|
|
|
|
} while (getAudioFileManager().insertFile(qstrtostr(label),
|
|
qstrtostr(newFilename),
|
|
id.toInt()) == false);
|
|
|
|
if (newPath != "") {
|
|
getAudioFileManager().setAudioPath(qstrtostr(newPath));
|
|
// Set a document post-modify flag
|
|
//m_doc->setModified(true);
|
|
}
|
|
|
|
getAudioFileManager().print();
|
|
|
|
// Restore progress dialog's normal state
|
|
CurrentProgressDialog::thaw();
|
|
} else {
|
|
// AudioPath is modified so set a document post modify flag
|
|
//
|
|
//m_doc->setModified(true);
|
|
}
|
|
|
|
}
|
|
|
|
} else if (lcName == "audiopath") {
|
|
|
|
if (m_section != InAudioFiles) {
|
|
m_errorString = "Audiopath object found outside AudioFiles section";
|
|
return false;
|
|
}
|
|
|
|
TQString search(atts.value("value"));
|
|
|
|
if (search.isEmpty()) {
|
|
m_errorString = "Audiopath has no value";
|
|
return false;
|
|
}
|
|
|
|
if (!search.startsWith("/") && !search.startsWith("~")) {
|
|
TQString docPath = m_doc->getAbsFilePath();
|
|
TQString dirPath = TQFileInfo(docPath).dirPath();
|
|
if (TQFileInfo(dirPath).exists()) {
|
|
search = dirPath + "/" + search;
|
|
}
|
|
}
|
|
|
|
getAudioFileManager().setAudioPath(qstrtostr(search));
|
|
|
|
} else if (lcName == "begin") {
|
|
|
|
double marker = qstrtodouble(atts.value("index"));
|
|
|
|
if (!m_currentSegment) {
|
|
// Don't fail - as this segment could be defunct if we
|
|
// skipped loading the audio file
|
|
//
|
|
return true;
|
|
}
|
|
|
|
if (m_currentSegment->getType() != Segment::Audio) {
|
|
m_errorString = "Found audio begin index in non audio segment";
|
|
return false;
|
|
}
|
|
|
|
// convert to RealTime from float
|
|
int sec = (int)marker;
|
|
int usec = (int)((marker - ((double)sec)) * 1000000.0);
|
|
m_currentSegment->setAudioStartTime(RealTime(sec, usec * 1000));
|
|
|
|
|
|
} else if (lcName == "end") {
|
|
|
|
double marker = qstrtodouble(atts.value("index"));
|
|
|
|
if (!m_currentSegment) {
|
|
// Don't fail - as this segment could be defunct if we
|
|
// skipped loading the audio file
|
|
//
|
|
return true;
|
|
}
|
|
|
|
if (m_currentSegment->getType() != Segment::Audio) {
|
|
m_errorString = "found audio end index in non audio segment";
|
|
return false;
|
|
}
|
|
|
|
int sec = (int)marker;
|
|
int usec = (int)((marker - ((double)sec)) * 1000000.0);
|
|
RealTime markerTime(sec, usec * 1000);
|
|
|
|
if (markerTime < m_currentSegment->getAudioStartTime()) {
|
|
m_errorString = "Audio end index before audio start marker";
|
|
return false;
|
|
}
|
|
|
|
m_currentSegment->setAudioEndTime(markerTime);
|
|
|
|
// Ensure we set end time according to correct RealTime end of Segment
|
|
//
|
|
RealTime realEndTime = getComposition().
|
|
getElapsedRealTime(m_currentSegment->getStartTime()) +
|
|
m_currentSegment->getAudioEndTime() -
|
|
m_currentSegment->getAudioStartTime();
|
|
|
|
timeT absEnd = getComposition().getElapsedTimeForRealTime(realEndTime);
|
|
m_currentSegment->setEndTime(absEnd);
|
|
|
|
} else if (lcName == "fadein") {
|
|
|
|
if (!m_currentSegment) {
|
|
// Don't fail - as this segment could be defunct if we
|
|
// skipped loading the audio file
|
|
//
|
|
return true;
|
|
}
|
|
|
|
if (m_currentSegment->getType() != Segment::Audio) {
|
|
m_errorString = "found fade in time in non audio segment";
|
|
return false;
|
|
}
|
|
|
|
double marker = qstrtodouble(atts.value("time"));
|
|
int sec = (int)marker;
|
|
int usec = (int)((marker - ((double)sec)) * 1000000.0);
|
|
RealTime markerTime(sec, usec * 1000);
|
|
|
|
m_currentSegment->setFadeInTime(markerTime);
|
|
m_currentSegment->setAutoFade(true);
|
|
|
|
|
|
} else if (lcName == "fadeout") {
|
|
|
|
if (!m_currentSegment) {
|
|
// Don't fail - as this segment could be defunct if we
|
|
// skipped loading the audio file
|
|
//
|
|
return true;
|
|
}
|
|
|
|
if (m_currentSegment->getType() != Segment::Audio) {
|
|
m_errorString = "found fade out time in non audio segment";
|
|
return false;
|
|
}
|
|
|
|
double marker = qstrtodouble(atts.value("time"));
|
|
int sec = (int)marker;
|
|
int usec = (int)((marker - ((double)sec)) * 1000000.0);
|
|
RealTime markerTime(sec, usec * 1000);
|
|
|
|
m_currentSegment->setFadeOutTime(markerTime);
|
|
m_currentSegment->setAutoFade(true);
|
|
|
|
} else if (lcName == "device") {
|
|
|
|
if (m_section != InStudio) {
|
|
m_errorString = "Found Device outside Studio";
|
|
return false;
|
|
}
|
|
|
|
m_haveControls = false;
|
|
|
|
TQString type = (atts.value("type")).lower();
|
|
TQString idString = atts.value("id");
|
|
TQString nameStr = atts.value("name");
|
|
|
|
if (idString.isNull()) {
|
|
m_errorString = "No ID on Device tag";
|
|
return false;
|
|
}
|
|
int id = idString.toInt();
|
|
|
|
if (type == "midi") {
|
|
TQString direction = atts.value("direction").lower();
|
|
|
|
if (direction.isNull() ||
|
|
direction == "" ||
|
|
direction == "play") { // ignore inputs
|
|
|
|
// This will leave m_device set only if there is a
|
|
// valid play midi device to modify:
|
|
skipToNextPlayDevice();
|
|
|
|
if (m_device) {
|
|
if (!nameStr.isNull() && nameStr != "") {
|
|
m_device->setName(qstrtostr(nameStr));
|
|
}
|
|
} else if (!nameStr.isNull() && nameStr != "") {
|
|
addMIDIDevice(nameStr, m_createDevices); // also sets m_device
|
|
}
|
|
}
|
|
|
|
TQString connection = atts.value("connection");
|
|
if (m_createDevices && m_device &&
|
|
!connection.isNull() && connection != "") {
|
|
setMIDIDeviceConnection(connection);
|
|
}
|
|
|
|
setMIDIDeviceName(nameStr);
|
|
|
|
TQString vstr = atts.value("variation").lower();
|
|
MidiDevice::VariationType variation =
|
|
MidiDevice::NoVariations;
|
|
if (!vstr.isNull()) {
|
|
if (vstr == "lsb") {
|
|
variation = MidiDevice::VariationFromLSB;
|
|
} else if (vstr == "msb") {
|
|
variation = MidiDevice::VariationFromMSB;
|
|
} else if (vstr == "") {
|
|
variation = MidiDevice::NoVariations;
|
|
}
|
|
}
|
|
MidiDevice *md = dynamic_cast<MidiDevice *>
|
|
(m_device);
|
|
if (md) {
|
|
md->setVariationType(variation);
|
|
}
|
|
} else if (type == "softsynth") {
|
|
m_device = getStudio().getDevice(id);
|
|
|
|
if (m_device && m_device->getType() == Device::SoftSynth)
|
|
m_device->setName(qstrtostr(nameStr));
|
|
} else if (type == "audio") {
|
|
m_device = getStudio().getDevice(id);
|
|
|
|
if (m_device && m_device->getType() == Device::Audio)
|
|
m_device->setName(qstrtostr(nameStr));
|
|
} else {
|
|
m_errorString = "Found unknown Device type";
|
|
return false;
|
|
}
|
|
|
|
} else if (lcName == "librarian") {
|
|
|
|
// The contact details for the maintainer of the banks/programs
|
|
// information.
|
|
//
|
|
if (m_device && m_device->getType() == Device::Midi) {
|
|
TQString name = atts.value("name");
|
|
TQString email = atts.value("email");
|
|
|
|
dynamic_cast<MidiDevice*>(m_device)->
|
|
setLibrarian(qstrtostr(name), qstrtostr(email));
|
|
}
|
|
|
|
} else if (lcName == "bank") {
|
|
|
|
if (m_device) // only if we have a device
|
|
{
|
|
if (m_section != InStudio && m_section != InInstrument)
|
|
{
|
|
m_errorString = "Found Bank outside Studio or Instrument";
|
|
return false;
|
|
}
|
|
|
|
TQString nameStr = atts.value("name");
|
|
m_percussion = (atts.value("percussion").lower() == "true");
|
|
m_msb = (atts.value("msb")).toInt();
|
|
m_lsb = (atts.value("lsb")).toInt();
|
|
|
|
// To actually create a bank
|
|
//
|
|
if (m_section == InStudio)
|
|
{
|
|
// Create a new bank
|
|
MidiBank bank(m_percussion,
|
|
m_msb,
|
|
m_lsb,
|
|
qstrtostr(nameStr));
|
|
|
|
if (m_device->getType() == Device::Midi) {
|
|
// Insert the bank
|
|
//
|
|
dynamic_cast<MidiDevice*>(m_device)->addBank(bank);
|
|
}
|
|
} else // otherwise we're referencing it in an instrument
|
|
if (m_section == InInstrument)
|
|
{
|
|
if (m_instrument) {
|
|
m_instrument->setPercussion(m_percussion);
|
|
m_instrument->setMSB(m_msb);
|
|
m_instrument->setLSB(m_lsb);
|
|
m_instrument->setSendBankSelect(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "program") {
|
|
|
|
if (m_device) // only if we have a device
|
|
{
|
|
if (m_section == InStudio)
|
|
{
|
|
TQString nameStr = (atts.value("name"));
|
|
MidiByte pc = atts.value("id").toInt();
|
|
TQString keyMappingStr = (atts.value("keymapping"));
|
|
|
|
// Create a new program
|
|
MidiProgram program
|
|
(MidiBank(m_percussion,
|
|
m_msb,
|
|
m_lsb),
|
|
pc,
|
|
qstrtostr(nameStr),
|
|
(!keyMappingStr.isNull()) ? qstrtostr(keyMappingStr) : "");
|
|
|
|
if (m_device->getType() == Device::Midi) {
|
|
// Insert the program
|
|
//
|
|
dynamic_cast<MidiDevice*>(m_device)->
|
|
addProgram(program);
|
|
}
|
|
|
|
} else if (m_section == InInstrument)
|
|
{
|
|
if (m_instrument) {
|
|
MidiByte id = atts.value("id").toInt();
|
|
m_instrument->setProgramChange(id);
|
|
m_instrument->setSendProgramChange(true);
|
|
}
|
|
} else
|
|
{
|
|
m_errorString = "Found Program outside Studio and Instrument";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "keymapping") {
|
|
|
|
if (m_section == InInstrument) {
|
|
RG_DEBUG << "Old-style keymapping in instrument found, ignoring" << endl;
|
|
} else {
|
|
|
|
if (m_section != InStudio) {
|
|
m_errorString = "Found Keymapping outside Studio";
|
|
return false;
|
|
}
|
|
|
|
if (m_device && (m_device->getType() == Device::Midi)) {
|
|
TQString name = atts.value("name");
|
|
m_keyMapping = new MidiKeyMapping(qstrtostr(name));
|
|
m_keyNameMap.clear();
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "key") {
|
|
|
|
if (m_keyMapping) {
|
|
TQString numStr = atts.value("number");
|
|
TQString namStr = atts.value("name");
|
|
if (!numStr.isNull() && !namStr.isNull()) {
|
|
m_keyNameMap[numStr.toInt()] = qstrtostr(namStr);
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "controls") {
|
|
|
|
// Only clear down the controllers list if we have found some controllers in the RG file
|
|
//
|
|
if (m_device) {
|
|
dynamic_cast<MidiDevice*>(m_device)->clearControlList();
|
|
}
|
|
|
|
m_haveControls = true;
|
|
|
|
} else if (lcName == "control") {
|
|
|
|
if (m_section != InStudio) {
|
|
m_errorString = "Found ControlParameter outside Studio";
|
|
return false;
|
|
}
|
|
|
|
if (!m_device) {
|
|
//!!! ach no, we can't give this warning -- we might be in a <device> elt
|
|
// but have no sequencer support, for example. we need a separate m_inDevice
|
|
// flag
|
|
// m_deprecation = true;
|
|
// std::cerr << "WARNING: This Rosegarden file uses a deprecated control parameter structure. We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
|
|
} else if (m_device->getType() == Device::Midi) {
|
|
|
|
if (!m_haveControls) {
|
|
m_errorString = "Found ControlParameter outside Controls block";
|
|
return false;
|
|
}
|
|
|
|
TQString name = atts.value("name");
|
|
TQString type = atts.value("type");
|
|
TQString descr = atts.value("description");
|
|
TQString min = atts.value("min");
|
|
TQString max = atts.value("max");
|
|
TQString def = atts.value("default");
|
|
TQString conVal = atts.value("controllervalue");
|
|
TQString colour = atts.value("colourindex");
|
|
TQString ipbPosition = atts.value("ipbposition");
|
|
|
|
ControlParameter con(qstrtostr(name),
|
|
qstrtostr(type),
|
|
qstrtostr(descr),
|
|
min.toInt(),
|
|
max.toInt(),
|
|
def.toInt(),
|
|
MidiByte(conVal.toInt()),
|
|
colour.toInt(),
|
|
ipbPosition.toInt());
|
|
|
|
dynamic_cast<MidiDevice*>(m_device)->
|
|
addControlParameter(con);
|
|
}
|
|
|
|
} else if (lcName == "reverb") { // deprecated but we still read 'em
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"reverb\" (now replaced by a control parameter). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found Reverb outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument)
|
|
m_instrument->setControllerValue(MIDI_CONTROLLER_REVERB, value);
|
|
|
|
|
|
} else if (lcName == "chorus") { // deprecated but we still read 'em
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"chorus\" (now replaced by a control parameter). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found Chorus outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument)
|
|
m_instrument->setControllerValue(MIDI_CONTROLLER_CHORUS, value);
|
|
|
|
} else if (lcName == "filter") { // deprecated but we still read 'em
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"filter\" (now replaced by a control parameter). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found Filter outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument)
|
|
m_instrument->setControllerValue(MIDI_CONTROLLER_FILTER, value);
|
|
|
|
|
|
} else if (lcName == "resonance") { // deprecated but we still read 'em
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"resonance\" (now replaced by a control parameter). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found Resonance outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument)
|
|
m_instrument->setControllerValue(MIDI_CONTROLLER_RESONANCE, value);
|
|
|
|
|
|
} else if (lcName == "attack") { // deprecated but we still read 'em
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"attack\" (now replaced by a control parameter). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found Attack outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument)
|
|
m_instrument->setControllerValue(MIDI_CONTROLLER_ATTACK, value);
|
|
|
|
} else if (lcName == "release") { // deprecated but we still read 'em
|
|
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"release\" (now replaced by a control parameter). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found Release outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument)
|
|
m_instrument->setControllerValue(MIDI_CONTROLLER_RELEASE, value);
|
|
|
|
} else if (lcName == "pan") {
|
|
|
|
if (m_section != InInstrument && m_section != InBuss) {
|
|
m_errorString = "Found Pan outside Instrument or Buss";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_section == InInstrument) {
|
|
if (m_instrument) {
|
|
m_instrument->setPan(value);
|
|
m_instrument->setSendPan(true);
|
|
}
|
|
} else if (m_section == InBuss) {
|
|
if (m_buss) {
|
|
m_buss->setPan(value);
|
|
}
|
|
}
|
|
|
|
// keep "velocity" so we're backwards compatible
|
|
} else if (lcName == "velocity" || lcName == "volume") {
|
|
|
|
if (lcName == "velocity") {
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"velocity\" for an overall MIDI instrument level (now replaced by \"volume\"). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
}
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found Volume outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument) {
|
|
if (m_instrument->getType() == Instrument::Audio ||
|
|
m_instrument->getType() == Instrument::SoftSynth) {
|
|
// Backward compatibility: "volume" was in a 0-127
|
|
// range and we now store "level" (float dB) instead.
|
|
// Note that we have no such compatibility for
|
|
// "recordLevel", whose range has changed silently.
|
|
if (!m_deprecation)
|
|
std::cerr << "WARNING: This Rosegarden file uses the deprecated element \"volume\" for an audio instrument (now replaced by \"level\"). We recommend re-saving the file from this version of Rosegarden to assure your ability to re-load it in future versions" << std::endl;
|
|
m_deprecation = true;
|
|
m_instrument->setLevel
|
|
(AudioLevel::multiplier_to_dB(float(value) / 100.0));
|
|
} else {
|
|
m_instrument->setVolume(value);
|
|
m_instrument->setSendVolume(true);
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "level") {
|
|
|
|
if (m_section != InBuss &&
|
|
(m_section != InInstrument ||
|
|
(m_instrument &&
|
|
m_instrument->getType() != Instrument::Audio &&
|
|
m_instrument->getType() != Instrument::SoftSynth))) {
|
|
m_errorString = "Found Level outside (audio) Instrument or Buss";
|
|
return false;
|
|
}
|
|
|
|
double value = qstrtodouble(atts.value("value"));
|
|
|
|
if (m_section == InBuss) {
|
|
if (m_buss)
|
|
m_buss->setLevel(value);
|
|
} else {
|
|
if (m_instrument)
|
|
m_instrument->setLevel(value);
|
|
}
|
|
|
|
} else if (lcName == "controlchange") {
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found ControlChange outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
MidiByte type = atts.value("type").toInt();
|
|
MidiByte value = atts.value("value").toInt();
|
|
|
|
if (m_instrument) {
|
|
m_instrument->setControllerValue(type, value);
|
|
}
|
|
|
|
} else if (lcName == "plugin" || lcName == "synth") {
|
|
|
|
PluginContainer *container = 0;
|
|
|
|
if (m_section == InInstrument) {
|
|
// std::cerr << "Found plugin in instrument" << std::endl;
|
|
container = m_instrument;
|
|
m_pluginInBuss = false;
|
|
} else if (m_section == InBuss) {
|
|
// std::cerr << "Found plugin in buss" << std::endl;
|
|
container = m_buss;
|
|
m_pluginInBuss = true;
|
|
} else {
|
|
m_errorString = "Found Plugin outside Instrument or Buss";
|
|
return false;
|
|
}
|
|
|
|
// Despite being InInstrument or InBuss we might not actually
|
|
// have a valid one.
|
|
//
|
|
if (container) {
|
|
|
|
// std::cerr << "Have container" << std::endl;
|
|
|
|
emit setOperationName(i18n("Loading plugins..."));
|
|
ProgressDialog::processEvents();
|
|
|
|
// Get the details
|
|
int position;
|
|
if (lcName == "synth") {
|
|
position = Instrument::SYNTH_PLUGIN_POSITION;
|
|
} else {
|
|
position = atts.value("position").toInt();
|
|
}
|
|
|
|
bool bypassed = false;
|
|
TQString bpStr = atts.value("bypassed");
|
|
if (bpStr.lower() == "true")
|
|
bypassed = true;
|
|
|
|
std::string program = "";
|
|
TQString progStr = atts.value("program");
|
|
if (!progStr.isNull()) {
|
|
program = qstrtostr(progStr);
|
|
}
|
|
|
|
// Plugins are identified by a structured identifier
|
|
// string, but we will accept a LADSPA UniqueId if there's
|
|
// no identifier, for backward compatibility
|
|
|
|
TQString identifier = atts.value("identifier");
|
|
|
|
AudioPlugin *plugin = 0;
|
|
AudioPluginManager *apm = getAudioPluginManager();
|
|
|
|
if (identifier.isNull()) {
|
|
if (!(atts.value("id")).isNull()) {
|
|
unsigned long id = atts.value("id").toULong();
|
|
if (apm)
|
|
plugin = apm->getPluginByUniqueId(id);
|
|
}
|
|
} else {
|
|
if (apm)
|
|
plugin = apm->getPluginByIdentifier(identifier);
|
|
}
|
|
|
|
// std::cerr << "Plugin identifier " << identifier << " -> plugin " << plugin << std::endl;
|
|
|
|
// If we find the plugin all is well and good but if
|
|
// we don't we just skip it.
|
|
//
|
|
if (plugin) {
|
|
m_plugin = container->getPlugin(position);
|
|
if (!m_plugin) {
|
|
RG_DEBUG << "WARNING: RoseXmlHandler: instrument/buss "
|
|
<< container->getId() << " has no plugin position "
|
|
<< position << endl;
|
|
} else {
|
|
m_plugin->setAssigned(true);
|
|
m_plugin->setBypass(bypassed);
|
|
m_plugin->setIdentifier(plugin->getIdentifier().ascii());
|
|
// std::cerr << "set identifier to plugin at position " << position << std::endl;
|
|
if (program != "") {
|
|
m_plugin->setProgram(program);
|
|
}
|
|
}
|
|
} else {
|
|
// we shouldn't be halting import of the RG file just because
|
|
// we can't match a plugin
|
|
//
|
|
if (!identifier.isNull()) {
|
|
RG_DEBUG << "WARNING: RoseXmlHandler: plugin " << identifier << " not found" << endl;
|
|
m_pluginsNotFound.insert(identifier);
|
|
} else if (!(atts.value("id")).isNull()) {
|
|
RG_DEBUG << "WARNING: RoseXmlHandler: plugin uid " << atts.value("id") << " not found" << endl;
|
|
} else {
|
|
m_errorString = "No plugin identifier or uid specified";
|
|
return false;
|
|
}
|
|
}
|
|
} else { // no instrument
|
|
|
|
if (lcName == "synth") {
|
|
TQString identifier = atts.value("identifier");
|
|
if (!identifier.isNull()) {
|
|
RG_DEBUG << "WARNING: RoseXmlHandler: no instrument for plugin " << identifier << endl;
|
|
m_pluginsNotFound.insert(identifier);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_section = InPlugin;
|
|
|
|
} else if (lcName == "port") {
|
|
|
|
if (m_section != InPlugin) {
|
|
m_errorString = "Found Port outside Plugin";
|
|
return false;
|
|
}
|
|
unsigned long portId = atts.value("id").toULong();
|
|
double value = qstrtodouble(atts.value("value"));
|
|
|
|
TQString changed = atts.value("changed");
|
|
bool changedSinceProgram = (changed == "true");
|
|
|
|
if (m_plugin) {
|
|
m_plugin->addPort(portId, value);
|
|
if (changedSinceProgram) {
|
|
PluginPortInstance *ppi = m_plugin->getPort(portId);
|
|
if (ppi)
|
|
ppi->changedSinceProgramChange = true;
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "configure") {
|
|
|
|
if (m_section != InPlugin) {
|
|
m_errorString = "Found Configure outside Plugin";
|
|
return false;
|
|
}
|
|
|
|
TQString key = atts.value("key");
|
|
TQString value = atts.value("value");
|
|
|
|
if (m_plugin) {
|
|
m_plugin->setConfigurationValue(qstrtostr(key), qstrtostr(value));
|
|
}
|
|
|
|
} else if (lcName == "metronome") {
|
|
|
|
if (m_section != InStudio) {
|
|
m_errorString = "Found Metronome outside Studio";
|
|
return false;
|
|
}
|
|
|
|
// Only create if we have a device
|
|
//
|
|
if (m_device && m_device->getType() == Device::Midi) {
|
|
InstrumentId instrument =
|
|
atts.value("instrument").toInt();
|
|
|
|
MidiMetronome metronome(instrument);
|
|
|
|
if (!(atts.value("barpitch")).isNull())
|
|
metronome.setBarPitch(atts.value("barpitch").toInt());
|
|
if (!(atts.value("beatpitch")).isNull())
|
|
metronome.setBeatPitch(atts.value("beatpitch").toInt());
|
|
if (!(atts.value("subbeatpitch")).isNull())
|
|
metronome.setSubBeatPitch(atts.value("subbeatpitch").toInt());
|
|
if (!(atts.value("depth")).isNull())
|
|
metronome.setDepth(atts.value("depth").toInt());
|
|
if (!(atts.value("barvelocity")).isNull())
|
|
metronome.setBarVelocity(atts.value("barvelocity").toInt());
|
|
if (!(atts.value("beatvelocity")).isNull())
|
|
metronome.setBeatVelocity(atts.value("beatvelocity").toInt());
|
|
if (!(atts.value("subbeatvelocity")).isNull())
|
|
metronome.setSubBeatVelocity(atts.value("subbeatvelocity").toInt());
|
|
|
|
dynamic_cast<MidiDevice*>(m_device)->
|
|
setMetronome(metronome);
|
|
}
|
|
|
|
} else if (lcName == "instrument") {
|
|
|
|
if (m_section != InStudio) {
|
|
m_errorString = "Found Instrument outside Studio";
|
|
return false;
|
|
}
|
|
|
|
m_section = InInstrument;
|
|
|
|
InstrumentId id = atts.value("id").toInt();
|
|
std::string stringType = qstrtostr(atts.value("type"));
|
|
Instrument::InstrumentType type;
|
|
|
|
if (stringType == "midi")
|
|
type = Instrument::Midi;
|
|
else if (stringType == "audio")
|
|
type = Instrument::Audio;
|
|
else if (stringType == "softsynth")
|
|
type = Instrument::SoftSynth;
|
|
else {
|
|
m_errorString = "Found unknown Instrument type";
|
|
return false;
|
|
}
|
|
|
|
// Try and match an Instrument in the file with one in
|
|
// our studio
|
|
//
|
|
Instrument *instrument = getStudio().getInstrumentById(id);
|
|
|
|
// If we've got an instrument and the types match then
|
|
// we use it from now on.
|
|
//
|
|
if (instrument && instrument->getType() == type) {
|
|
m_instrument = instrument;
|
|
|
|
// We can also get the channel from this tag
|
|
//
|
|
MidiByte channel =
|
|
(MidiByte)atts.value("channel").toInt();
|
|
m_instrument->setMidiChannel(channel);
|
|
}
|
|
|
|
} else if (lcName == "buss") {
|
|
|
|
if (m_section != InStudio) {
|
|
m_errorString = "Found Buss outside Studio";
|
|
return false;
|
|
}
|
|
|
|
m_section = InBuss;
|
|
|
|
BussId id = atts.value("id").toInt();
|
|
Buss *buss = getStudio().getBussById(id);
|
|
|
|
// If we've got a buss then we use it from now on.
|
|
//
|
|
if (buss) {
|
|
m_buss = buss;
|
|
} else {
|
|
m_buss = new Buss(id);
|
|
getStudio().addBuss(m_buss);
|
|
}
|
|
|
|
} else if (lcName == "audiofiles") {
|
|
|
|
if (m_section != NoSection) {
|
|
m_errorString = "Found AudioFiles inside another section";
|
|
return false;
|
|
}
|
|
|
|
m_section = InAudioFiles;
|
|
|
|
int rate = atts.value("expectedRate").toInt();
|
|
if (rate) {
|
|
getAudioFileManager().setExpectedSampleRate(rate);
|
|
}
|
|
|
|
} else if (lcName == "configuration") {
|
|
|
|
setSubHandler(new ConfigurationXmlSubHandler
|
|
(lcName, &m_doc->getConfiguration()));
|
|
|
|
} else if (lcName == "metadata") {
|
|
|
|
if (m_section != InComposition) {
|
|
m_errorString = "Found Metadata outside Composition";
|
|
return false;
|
|
}
|
|
|
|
setSubHandler(new ConfigurationXmlSubHandler
|
|
(lcName, &getComposition().getMetadata()));
|
|
|
|
} else if (lcName == "recordlevel") {
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found recordLevel outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
double value = qstrtodouble(atts.value("value"));
|
|
|
|
// if the value retrieved is greater than (say) 15 then we
|
|
// must have an old-style 0-127 value instead of a shiny new
|
|
// dB value, so convert it
|
|
if (value > 15.0) {
|
|
value = AudioLevel::multiplier_to_dB(value / 100);
|
|
}
|
|
|
|
if (m_instrument)
|
|
m_instrument->setRecordLevel(value);
|
|
|
|
} else if (lcName == "audioinput") {
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found audioInput outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
int value = atts.value("value").toInt();
|
|
int channel = atts.value("channel").toInt();
|
|
|
|
TQString type = atts.value("type");
|
|
if (!type.isNull()) {
|
|
if (type.lower() == "buss") {
|
|
if (m_instrument)
|
|
m_instrument->setAudioInputToBuss(value, channel);
|
|
} else if (type.lower() == "record") {
|
|
if (m_instrument)
|
|
m_instrument->setAudioInputToRecord(value, channel);
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "audiooutput") {
|
|
|
|
if (m_section != InInstrument) {
|
|
m_errorString = "Found audioOutput outside Instrument";
|
|
return false;
|
|
}
|
|
|
|
int value = atts.value("value").toInt();
|
|
if (m_instrument)
|
|
m_instrument->setAudioOutput(value);
|
|
|
|
} else if (lcName == "appearance") {
|
|
|
|
m_section = InAppearance;
|
|
|
|
} else if (lcName == "colourmap") {
|
|
|
|
if (m_section == InAppearance) {
|
|
TQString mapName = atts.value("name");
|
|
m_inColourMap = true;
|
|
if (mapName == "segmentmap") {
|
|
m_colourMap = &m_doc->getComposition().getSegmentColourMap();
|
|
} else
|
|
if (mapName == "generalmap") {
|
|
m_colourMap = &m_doc->getComposition().getGeneralColourMap();
|
|
} else { // This will change later once we get more of the Appearance code sorted out
|
|
RG_DEBUG << "RoseXmlHandler::startElement : Found colourmap with unknown name\n";
|
|
}
|
|
} else {
|
|
m_errorString = "Found colourmap outside Appearance";
|
|
return false;
|
|
}
|
|
|
|
} else if (lcName == "colourpair") {
|
|
|
|
if (m_inColourMap && m_colourMap) {
|
|
unsigned int id = atts.value("id").toInt();
|
|
TQString name = atts.value("name");
|
|
unsigned int red = atts.value("red").toInt();
|
|
unsigned int blue = atts.value("blue").toInt();
|
|
unsigned int green = atts.value("green").toInt();
|
|
Colour colour(red, green, blue);
|
|
m_colourMap->addItem(colour, qstrtostr(name), id);
|
|
} else {
|
|
m_errorString = "Found colourpair outside ColourMap";
|
|
return false;
|
|
}
|
|
|
|
} else if (lcName == "markers") {
|
|
|
|
if (!m_inComposition) {
|
|
m_errorString = "Found Markers outside Composition";
|
|
return false;
|
|
}
|
|
|
|
// clear down any markers
|
|
getComposition().clearMarkers();
|
|
|
|
} else if (lcName == "marker") {
|
|
if (!m_inComposition) {
|
|
m_errorString = "Found Marker outside Composition";
|
|
return false;
|
|
}
|
|
int time = atts.value("time").toInt();
|
|
TQString name = atts.value("name");
|
|
TQString descr = atts.value("description");
|
|
|
|
Marker *marker =
|
|
new Marker(time,
|
|
qstrtostr(name),
|
|
qstrtostr(descr));
|
|
|
|
getComposition().addMarker(marker);
|
|
} else {
|
|
RG_DEBUG << "RoseXmlHandler::startElement : Don't know how to parse this : " << qName << endl;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
RoseXmlHandler::endElement(const TQString& namespaceURI,
|
|
const TQString& localName,
|
|
const TQString& qName)
|
|
{
|
|
if (getSubHandler()) {
|
|
bool finished;
|
|
bool res = getSubHandler()->endElement(namespaceURI, localName, qName.lower(), finished);
|
|
if (finished)
|
|
setSubHandler(0);
|
|
return res;
|
|
}
|
|
|
|
// Set percentage done
|
|
//
|
|
if ((m_totalElements > m_elementsSoFar) &&
|
|
(++m_elementsSoFar % 300 == 0)) {
|
|
|
|
emit setProgress(int(double(m_elementsSoFar) / double(m_totalElements) * 100.0));
|
|
ProgressDialog::processEvents();
|
|
}
|
|
|
|
TQString lcName = qName.lower();
|
|
|
|
if (lcName == "rosegarden-data") {
|
|
|
|
getComposition().updateTriggerSegmentReferences();
|
|
|
|
} else if (lcName == "event") {
|
|
|
|
if (m_currentSegment && m_currentEvent) {
|
|
m_currentSegment->insert(m_currentEvent);
|
|
m_currentEvent = 0;
|
|
} else if (!m_currentSegment && m_currentEvent) {
|
|
m_errorString = "Got event outside of a Segment";
|
|
return false;
|
|
}
|
|
|
|
} else if (lcName == "chord") {
|
|
|
|
m_currentTime += m_chordDuration;
|
|
m_inChord = false;
|
|
m_chordDuration = 0;
|
|
|
|
} else if (lcName == "group") {
|
|
|
|
m_inGroup = false;
|
|
|
|
} else if (lcName == "segment") {
|
|
|
|
if (m_currentSegment && m_segmentEndMarkerTime) {
|
|
m_currentSegment->setEndMarkerTime(*m_segmentEndMarkerTime);
|
|
delete m_segmentEndMarkerTime;
|
|
m_segmentEndMarkerTime = 0;
|
|
}
|
|
|
|
m_currentSegment = 0;
|
|
m_section = NoSection;
|
|
|
|
} else if (lcName == "bar-segment" || lcName == "tempo-segment") {
|
|
|
|
m_currentSegment = 0;
|
|
|
|
} else if (lcName == "composition") {
|
|
m_inComposition = false;
|
|
m_section = NoSection;
|
|
|
|
} else if (lcName == "studio") {
|
|
|
|
m_section = NoSection;
|
|
|
|
} else if (lcName == "buss") {
|
|
|
|
m_section = InStudio;
|
|
m_buss = 0;
|
|
|
|
} else if (lcName == "instrument") {
|
|
|
|
m_section = InStudio;
|
|
m_instrument = 0;
|
|
|
|
} else if (lcName == "plugin") {
|
|
|
|
if (m_pluginInBuss) {
|
|
m_section = InBuss;
|
|
} else {
|
|
m_section = InInstrument;
|
|
}
|
|
m_plugin = 0;
|
|
m_pluginId = 0;
|
|
|
|
} else if (lcName == "device") {
|
|
|
|
m_device = 0;
|
|
|
|
} else if (lcName == "keymapping") {
|
|
|
|
if (m_section == InStudio) {
|
|
if (m_keyMapping) {
|
|
if (!m_keyNameMap.empty()) {
|
|
MidiDevice *md = dynamic_cast<MidiDevice *>
|
|
(m_device);
|
|
if (md) {
|
|
m_keyMapping->setMap(m_keyNameMap);
|
|
md->addKeyMapping(*m_keyMapping);
|
|
}
|
|
}
|
|
m_keyMapping = 0;
|
|
}
|
|
}
|
|
|
|
} else if (lcName == "audiofiles") {
|
|
|
|
m_section = NoSection;
|
|
|
|
} else if (lcName == "appearance") {
|
|
|
|
m_section = NoSection;
|
|
|
|
} else if (lcName == "colourmap") {
|
|
m_inColourMap = false;
|
|
m_colourMap = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
RoseXmlHandler::characters(const TQString& s)
|
|
{
|
|
if (m_subHandler)
|
|
return m_subHandler->characters(s);
|
|
|
|
return true;
|
|
}
|
|
|
|
TQString
|
|
RoseXmlHandler::errorString()
|
|
{
|
|
return m_errorString;
|
|
}
|
|
|
|
bool
|
|
RoseXmlHandler::error(const TQXmlParseException& exception)
|
|
{
|
|
m_errorString = TQString("%1 at line %2, column %3")
|
|
.arg(exception.message())
|
|
.arg(exception.lineNumber())
|
|
.arg(exception.columnNumber());
|
|
return TQXmlDefaultHandler::error( exception );
|
|
}
|
|
|
|
bool
|
|
RoseXmlHandler::fatalError(const TQXmlParseException& exception)
|
|
{
|
|
m_errorString = TQString("%1 at line %2, column %3")
|
|
.arg(exception.message())
|
|
.arg(exception.lineNumber())
|
|
.arg(exception.columnNumber());
|
|
return TQXmlDefaultHandler::fatalError( exception );
|
|
}
|
|
|
|
bool
|
|
RoseXmlHandler::endDocument()
|
|
{
|
|
if (m_foundTempo == false) {
|
|
getComposition().setCompositionDefaultTempo
|
|
(Composition::getTempoForQpm(120.0));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
RoseXmlHandler::setSubHandler(XmlSubHandler* sh)
|
|
{
|
|
delete m_subHandler;
|
|
m_subHandler = sh;
|
|
}
|
|
|
|
void
|
|
RoseXmlHandler::addMIDIDevice(TQString name, bool createAtSequencer)
|
|
{
|
|
unsigned int deviceId = 0;
|
|
|
|
if (createAtSequencer) {
|
|
|
|
TQByteArray data;
|
|
TQByteArray replyData;
|
|
TQCString replyType;
|
|
TQDataStream arg(data, IO_WriteOnly);
|
|
|
|
arg << (int)Device::Midi;
|
|
arg << (unsigned int)MidiDevice::Play;
|
|
|
|
if (!rgapp->sequencerCall("addDevice(int, unsigned int)", replyType, replyData, data)) {
|
|
SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
|
|
<< "can't call sequencer addDevice" << endl;
|
|
return ;
|
|
}
|
|
|
|
if (replyType == "unsigned int") {
|
|
TQDataStream reply(replyData, IO_ReadOnly);
|
|
reply >> deviceId;
|
|
} else {
|
|
SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
|
|
<< "got unknown returntype from addDevice()" << endl;
|
|
return ;
|
|
}
|
|
|
|
if (deviceId == Device::NO_DEVICE) {
|
|
SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
|
|
<< "sequencer addDevice failed" << endl;
|
|
return ;
|
|
}
|
|
|
|
SEQMAN_DEBUG << "RoseXmlHandler::addMIDIDevice - "
|
|
<< " added device " << deviceId << endl;
|
|
|
|
} else {
|
|
// Generate a new device id at the base Studio side only.
|
|
// This may not correspond to any given device id at the
|
|
// sequencer side. We should _never_ do this in a document
|
|
// that's actually intended to be retained for use, only
|
|
// in temporary documents for device import etc.
|
|
int tempId = -1;
|
|
for (DeviceListIterator i = getStudio().getDevices()->begin();
|
|
i != getStudio().getDevices()->end(); ++i) {
|
|
if (int((*i)->getId()) > tempId)
|
|
tempId = int((*i)->getId());
|
|
}
|
|
deviceId = tempId + 1;
|
|
}
|
|
|
|
// add the device, so we can name it and set our pointer to it --
|
|
// instruments will be sync'd later in the natural course of things
|
|
getStudio().addDevice(qstrtostr(name), deviceId, Device::Midi);
|
|
m_device = getStudio().getDevice(deviceId);
|
|
m_deviceRunningId = deviceId;
|
|
}
|
|
|
|
void
|
|
RoseXmlHandler::skipToNextPlayDevice()
|
|
{
|
|
SEQMAN_DEBUG << "RoseXmlHandler::skipToNextPlayDevice; m_deviceRunningId is " << m_deviceRunningId << endl;
|
|
|
|
for (DeviceList::iterator i = getStudio().getDevices()->begin();
|
|
i != getStudio().getDevices()->end(); ++i) {
|
|
|
|
MidiDevice *md =
|
|
dynamic_cast<MidiDevice *>(*i);
|
|
|
|
if (md && md->getDirection() == MidiDevice::Play) {
|
|
if (m_deviceRunningId == Device::NO_DEVICE ||
|
|
md->getId() > m_deviceRunningId) {
|
|
|
|
SEQMAN_DEBUG << "RoseXmlHandler::skipToNextPlayDevice: found next device: id " << md->getId() << endl;
|
|
|
|
m_device = md;
|
|
m_deviceRunningId = md->getId();
|
|
return ;
|
|
}
|
|
}
|
|
}
|
|
|
|
SEQMAN_DEBUG << "RoseXmlHandler::skipToNextPlayDevice: fresh out of devices" << endl;
|
|
|
|
m_device = 0;
|
|
}
|
|
|
|
void
|
|
RoseXmlHandler::setMIDIDeviceConnection(TQString connection)
|
|
{
|
|
SEQMAN_DEBUG << "RoseXmlHandler::setMIDIDeviceConnection(" << connection << ")" << endl;
|
|
|
|
MidiDevice *md = dynamic_cast<MidiDevice *>(m_device);
|
|
if (!md)
|
|
return ;
|
|
|
|
TQByteArray data;
|
|
TQDataStream arg(data, IO_WriteOnly);
|
|
|
|
arg << (unsigned int)md->getId();
|
|
arg << connection;
|
|
|
|
rgapp->sequencerSend("setPlausibleConnection(unsigned int, TQString)",
|
|
data);
|
|
// connection should be sync'd later in the natural course of things
|
|
}
|
|
|
|
void
|
|
RoseXmlHandler::setMIDIDeviceName(TQString name)
|
|
{
|
|
SEQMAN_DEBUG << "RoseXmlHandler::setMIDIDeviceName(" << name << ")" << endl;
|
|
|
|
MidiDevice *md = dynamic_cast<MidiDevice *>(m_device);
|
|
if (!md)
|
|
return ;
|
|
|
|
TQByteArray data;
|
|
TQDataStream arg(data, IO_WriteOnly);
|
|
|
|
arg << (unsigned int)md->getId();
|
|
arg << name;
|
|
|
|
std::cerr << "Renaming device " << md->getId() << " to " << name.ascii() << std::endl;
|
|
|
|
rgapp->sequencerSend("renameDevice(unsigned int, TQString)",
|
|
data);
|
|
}
|
|
|
|
}
|