Open consolegrl opened 1 year ago
The project offers an XSD which describes the schema of valid XML documents in the context of the project.
The XSD in turn can be used with a code generator, e.g. CodeSynthesis XSD, to generate classes from the XSD. As a result the workflow in the code is roughly the following one:
Only serialize to XML in the end
So using something like this would make the code that maps the data between both representations nicer because data would be moved between high level classes. That way one would not have to care that much about the technical aspect of all that XML.
I have just played with CodeSynthesis XSD in a Docker container and unfortunately the generated code seems to introduce a compile time dependency to the library and a runtime dependency to Xerces. 🙁
This might be rather unattractive as the project already uses Qt's XML library.
I have just played with CodeSynthesis XSD in a Docker container and unfortunately the generated code seems to introduce a compile time dependency to the library and a runtime dependency to Xerces. 🙁
This might be rather unattractive as the project already uses Qt's XML library.
We already install libxml2-utils
(Ubuntu package name), which contains xmllint
. I've verified that using the example project in dawproject's README will validate using xmllint --noout --schema Project.xsd test.xml
, where test.xml
is the example in its own file (--noout
just quiets the tree output).
So I spent some time going over the dawproject schema and seeing if I could convert our data/projects/shorties/Crunk(Demo).mmp
to the dawproject format.
So far the only things I really hit were:
I've opened up two PRs and we'll see how it goes.
So with the two issues above, I modified the Project.xsd slightly to work with my hand made XML for Crunk(Demo) (yeah, by hand... I know it was a long task to do it this way but I learned quite a bit so far).
diff --git a/Project.xsd b/Project.xsd
index 12e3696..6cb5350 100644
--- a/Project.xsd
+++ b/Project.xsd
@@ -61,6 +61,8 @@
<xs:element name="Scene" type="scene"/>
+ <xs:element name="StringParameter" type="stringParameter"/>
+
<xs:element name="TimeSignatureParameter" type="timeSignatureParameter"/>
<xs:element name="TimeSignaturePoint" type="timeSignaturePoint"/>
@@ -168,6 +170,15 @@
</xs:complexContent>
</xs:complexType>
+ <xs:complexType name="stringParameter">
+ <xs:complexContent>
+ <xs:extension base="parameter">
+ <xs:sequence/>
+ <xs:attribute name="value" type="xs:string"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
<xs:complexType name="integerParameter">
<xs:complexContent>
<xs:extension base="parameter">
@@ -327,6 +338,7 @@
<xs:element ref="BoolParameter"/>
<xs:element ref="IntegerParameter"/>
<xs:element ref="EnumParameter"/>
+ <xs:element ref="StringParameter"/>
<xs:element ref="TimeSignatureParameter"/>
</xs:choice>
</xs:sequence>
@@ -496,7 +508,7 @@
</xs:sequence>
<xs:attribute name="time" type="xs:string" use="required"/>
<xs:attribute name="duration" type="xs:string" use="required"/>
- <xs:attribute name="channel" type="xs:int" use="required"/>
+ <xs:attribute name="channel" type="xs:int"/>
<xs:attribute name="key" type="xs:int" use="required"/>
<xs:attribute name="vel" type="xs:string"/>
<xs:attribute name="rel" type="xs:string"/>
And then here's the converted (and validated with xmllint with the changes above), please remember this may not be 100% correct or best, but it's following what I understand currently. I'm pretty sure some of the RealParameter
units may not actually be linear
, but I didn't feel like trying to find out. lol
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Project version="1.0">
<Application name="LMMS" version="1.2.0"/>
<Transport>
<Tempo max="999" min="1" unit="bpm" value="130" id="tempo0" name="Tempo"/>
<TimeSignature denominator="4" numerator="4" id="timesig0"/>
</Transport>
<Structure>
<Track contentType="tracks" id="bbtrack">
<Track contentType="audio" loaded="true" id="afp0" name="Kick 01">
<Channel role="regular" audioChannels="2" solo="false" destination="mixer1">
<Devices>
<BuiltinDevice deviceRole="instrument" deviceName="audiofileprocessor" deviceVendor="LMMS">
<Parameters>
<IntegerParameter value="1" min="1" max="60" name="range"/>
<IntegerParameter value="0" name="pitch"/>
<IntegerParameter value="57" name="baseNote"/>
<BoolParameter value="true" name="useMasterPitch"/>
<RealParameter value="0" unit="decibel" min="-96.0" max="6.0" name="amp"/>
<RealParameter value="0" unit="normalized" min="0" max="1" name="startPoint"/>
<RealParameter value="0" unit="normalized" min="0" max="1" name="loopPoint"/>
<RealParameter value="1" unit="normalized" min="0" max="1" name="endPoint"/>
<BoolParameter value="false" name="reverse"/>
<IntegerParameter value="0" name="looped"/>
<BoolParameter value="false" name="stutter"/>
<IntegerParameter value="1" name="interp"/>
</Parameters>
<State external="false" path="drums/kick01.ogg"/>
</BuiltinDevice>
</Devices>
<Mute value="false"/>
<Pan value="0" unit="linear" min="-100" max="100"/>
<Volume value="-2.4" unit="decibel" min="-96.0" max="6.0"/>
</Channel>
</Track>
<Track contentType="audio" loaded="true" id="afp1" name="Clap 04">
<Channel role="regular" audioChannels="2" solo="false" destination="mixer1">
<Devices>
<BuiltinDevice deviceRole="instrument" deviceName="audiofileprocessor" deviceVendor="LMMS">
<Parameters>
<IntegerParameter value="1" min="1" max="60" name="pitchrange"/>
<IntegerParameter value="0" name="pitch"/>
<IntegerParameter value="57" name="baseNote"/>
<BoolParameter value="true" name="useMasterPitch"/>
<RealParameter value="0" unit="decibel" min="-96.0" max="6.0" name="amp"/>
<RealParameter value="0" unit="normalized" min="0" max="1" name="startPoint"/>
<RealParameter value="0" unit="normalized" min="0" max="1" name="loopPoint"/>
<RealParameter value="1" unit="normalized" min="0" max="1" name="endPoint"/>
<BoolParameter value="false" name="reverse"/>
<IntegerParameter value="0" name="looped"/>
<BoolParameter value="false" name="stutter"/>
<IntegerParameter value="1" name="interp"/>
</Parameters>
<State external="false" path="drums/clap04.ogg"/>
</BuiltinDevice>
</Devices>
<Mute value="false"/>
<Pan value="0" unit="linear" min="-100" max="100"/>
<Volume value="-1.1" unit="decibel" min="-96.0" max="6.0"/>
</Channel>
</Track>
</Track>
<Track contentType="tracks" id="song">
<Track contentType="audio" loaded="true" id="track0" name="Drum">
<Channel role="regular" audioChannels="2" solo="false" destination="mixer1">
<Mute value="false"/>
<Pan value="1" unit="linear" comment="This is actually unused for bbtrack"/>
<Volume value="1" unit="linear" comment="This is actually unused for bbtrack"/>
</Channel>
</Track>
<Track contentType="audio" loaded="true" id="track1" name="Bass">
<Channel role="regular" audioChannels="2" solo="false" destination="mixer2">
<Devices>
<BuiltinDevice deviceRole="instrument" deviceName="audiofileprocessor" deviceVendor="LMMS" id="afp2">
<Parameters>
<IntegerParameter value="1" min="1" max="60" name="pitchrange"/>
<IntegerParameter value="0" name="pitch"/>
<IntegerParameter value="57" name="baseNote"/>
<BoolParameter value="true" name="useMasterPitch"/>
<RealParameter value="0" unit="decibel" min="-96.0" max="6.0" name="amp"/>
<RealParameter value="0" unit="normalized" min="0" max="1" name="startPoint"/>
<RealParameter value="0" unit="normalized" min="0" max="1" name="loopPoint"/>
<RealParameter value="1" unit="normalized" min="0" max="1" name="endPoint"/>
<BoolParameter value="false" name="reverse"/>
<IntegerParameter value="0" name="looped"/>
<BoolParameter value="false" name="stutter"/>
<IntegerParameter value="1" name="interp"/>
</Parameters>
<State external="false" path="basses/bass_hard01.ogg"/>
</BuiltinDevice>
</Devices>
<Mute value="false"/>
<Pan value="0" unit="linear" min="-100" max="100"/>
<Volume value="0" unit="decibel" min="-96.0" max="6.0"/>
</Channel>
</Track>
<Track contentType="audio" loaded="true" id="track2" name="Synth">
<Channel role="regular" audioChannels="2" solo="false" destination="mixer3">
<Devices>
<BuiltinDevice deviceRole="instrument" deviceName="FreeBoy" deviceVendor="LMMS" id="freeboy0">
<Parameters>
<IntegerParameter value="1" min="1" max="60" name="pitchrange"/>
<IntegerParameter value="0" name="pitch"/>
<IntegerParameter value="57" name="baseNote"/>
<BoolParameter value="true" name="useMasterPitch"/>
<RealParameter value="4" unit="linear" min="0" max="7" name="st" comment="ch1SweepTime"/>
<BoolParameter value="0" name="sd" comment="ch1SweepDir"/>
<RealParameter value="0" unit="linear" min="0" max="7" name="srs" comment="ch1SweepRtShift"/>
<RealParameter value="2" unit="linear" min="0" max="3" name="ch1wpd" comment="ch1WavePatternDuty"/>
<RealParameter value="15" unit="linear" min="0" max="15" name="ch1vol" comment="ch1Volume"/>
<BoolParameter value="0" name="ch1vsd" comment="ch1VolSweepDir"/>
<RealParameter value="0" unit="linear" min="0" max="7" name="ch1ssl" comment="ch1SweepStepLength"/>
<RealParameter value="2" unit="linear" min="0" max="3" name="ch2wpd" comment="ch2WavePatternDuty"/>
<RealParameter value="15" unit="linear" min="0" max="15" name="ch2vol" comment="ch2Volume"/>
<BoolParameter value="0" name="ch2vsd" comment="ch2VolSweepDir"/>
<RealParameter value="0" unit="linear" min="0" max="7" name="ch2ssl" comment="ch2SweepStepLength"/>
<RealParameter value="3" unit="linear" min="0" max="3" name="ch3vol" comment="ch3Volume"/>
<RealParameter value="15" unit="linear" min="0" max="15" name="ch4vol" comment="ch4Volume"/>
<BoolParameter value="0" name="ch4vsd" comment="ch4VolSweepDir"/>
<RealParameter value="7" unit="linear" min="0" max="7" name="ch4ssl" comment="ch4SweepStepLength"/>
<BoolParameter value="0" name="srw" comment="ch4ShiftRegWidth"/>
<RealParameter value="7" unit="linear" min="0" max="7" name="so1vol" comment="so1Volume"/>
<RealParameter value="7" unit="linear" min="0" max="7" name="so2vol" comment="so2Volume"/>
<BoolParameter value="1" name="ch1so1" comment="ch1So1"/>
<BoolParameter value="1" name="ch2so1" comment="ch2So1"/>
<BoolParameter value="0" name="ch3so1" comment="ch3So1"/>
<BoolParameter value="0" name="ch4so1" comment="ch4So1"/>
<BoolParameter value="1" name="ch1so2" comment="ch1So2"/>
<BoolParameter value="1" name="ch2so2" comment="ch2So2"/>
<BoolParameter value="0" name="ch3so2" comment="ch3So2"/>
<BoolParameter value="0" name="ch4so2" comment="ch4So2"/>
<RealParameter value="461" unit="linear" min="-1" max="600" name="Bass"/>
<RealParameter value="-11" unit="linear" min="-100" max="200" name="Treble"/>
<StringParameter value="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" name="sampleShape"/>
</Parameters>
</BuiltinDevice>
<AuPlugin deviceRole="audioFX" deviceName="Plate2x2" deviceVendor="caps" loaded="true" name="ladspaeffect0">
<Parameters>
<RealParameter value="1" unit="linear" min="-1" max="1" name="W/D"/>
<IntegerParameter value="1" min="1" max="8000" name="Decay"/>
<RealParameter value="0" unit="linear" min="0" max="1" name="Gate"/>
<RealParameter value="0.18389" unit="linear" min="0.005" max="0.999" name="port02" comment="bandwidth"/>
<RealParameter value="0.5767" unit="linear" min="0" max="0.749" name="port03" comment="tail"/>
<RealParameter value="0.999" unit="linear" min="0.0005" max="1" name="port04" comment="damping"/>
<RealParameter value="0.37" unit="linear" min="0" max="1" name="port05" comment="blend"/>
</Parameters>
<Enabled value="true"/>
</AuPlugin>
</Devices>
<Mute value="false"/>
<Pan value="0" unit="linear" min="-100" max="100"/>
<Volume value="-1.1" unit="decibel" min="-96.0" max="6.0"/>
</Channel>
</Track>
</Track>
<Channel role="master" audioChannels="2" solo="false" id="mixer0" name="Master">
<Mute value="false"/>
<Volume value="100.0" unit="linear" min="0" max="200"/>
</Channel>
<Channel role="regular" audioChannels="2" solo="false" id="mixer1" name="Drums">
<Mute value="false"/>
<Sends>
<Send type="post" destination="mixer0">
<Volume value="1" unit="linear" min="0" max="1"/>
</Send>
</Sends>
<Volume value="100.0" unit="linear" min="0" max="200"/>
</Channel>
<Channel role="regular" audioChannels="2" solo="false" id="mixer2" name="Bass">
<Mute value="false"/>
<Sends>
<Send type="post" destination="mixer0">
<Volume value="1" unit="linear" min="0" max="1"/>
</Send>
</Sends>
<Volume value="100.0" unit="linear" min="0" max="200"/>
</Channel>
<Channel role="regular" audioChannels="2" solo="false" id="mixer3" name="Lead">
<Mute value="false"/>
<Sends>
<Send type="post" destination="mixer0">
<Volume value="1" unit="linear" min="0" max="1"/>
</Send>
</Sends>
<Volume value="100.0" unit="linear" min="0" max="200"/>
</Channel>
</Structure>
<Arrangement>
<Lanes timeUnit="beats" id="id20">
<Lanes track="afp0" id="id21">
<Clips>
<Clip time="0" duration="16">
<Notes>
<Note time="0" duration="-1" key="57" vel="1"/>
<Note time="2.5" duration="-1" key="57" vel="1"/>
</Notes>
</Clip>
</Clips>
</Lanes>
<Lanes track="afp1" id="id23">
<Clips>
<Clip time="0" duration="16">
<Notes>
<Note time="1" duration="-1" key="57" vel="1"/>
<Note time="3" duration="-1" key="57" vel="1"/>
</Notes>
</Clip>
</Clips>
</Lanes>
<Clips track="track0" name="Drum">
<Clip time="0" duration="16" comment="bbtrack clip"/>
<Clip time="4" duration="16" comment="bbtrack clip"/>
</Clips>
<Lanes track="track1">
<Clips name="Bass">
<Clip time="0" duration="16">
<Notes>
<Note time="0" duration="0.75" key="52" vel="1"/>
<Note time="1.5" duration="0.75" key="52" vel="1"/>
<Note time="3" duration="0.75" key="52" vel="1"/>
<Note time="4.5" duration="0.5" key="59" vel="1"/>
<Note time="5" duration="0.5" key="59" vel="1"/>
<Note time="5.75" duration="0.5" key="59" vel="1"/>
<Note time="6.5" duration="0.5" key="59" vel="1"/>
<Note time="7.5" duration="0.5" key="59" vel="1"/>
<Note time="8" duration="0.75" key="60" vel="1"/>
<Note time="9.5" duration="0.75" key="60" vel="1"/>
<Note time="11" duration="0.75" key="60" vel="1"/>
<Note time="12.5" duration="0.5" key="55" vel="1"/>
<Note time="13" duration="0.5" key="55" vel="1"/>
<Note time="13.75" duration="0.5" key="55" vel="1"/>
<Note time="14.5" duration="0.5" key="59" vel="1"/>
<Note time="15.5" duration="0.5" key="59" vel="1"/>
</Notes>
</Clip>
<Clip time="4" duration="16">
<Notes>
<Note time="0" duration="0.75" key="52" vel="1"/>
<Note time="1.5" duration="0.75" key="52" vel="1"/>
<Note time="3" duration="0.75" key="52" vel="1"/>
<Note time="4.5" duration="0.5" key="59" vel="1"/>
<Note time="5" duration="0.5" key="59" vel="1"/>
<Note time="5.75" duration="0.5" key="59" vel="1"/>
<Note time="6.5" duration="0.5" key="59" vel="1"/>
<Note time="7.5" duration="0.5" key="59" vel="1"/>
<Note time="8" duration="0.75" key="60" vel="1"/>
<Note time="9.5" duration="0.75" key="60" vel="1"/>
<Note time="11" duration="0.75" key="60" vel="1"/>
<Note time="12.5" duration="0.5" key="55" vel="1"/>
<Note time="13" duration="0.5" key="55" vel="1"/>
<Note time="13.75" duration="0.5" key="55" vel="1"/>
<Note time="14.5" duration="0.5" key="59" vel="1"/>
<Note time="15.5" duration="0.5" key="59" vel="1"/>
</Notes>
</Clip>
</Clips>
</Lanes>
<Lanes track="track2">
<Clips name="Synth">
<Clip time="0" duration="16">
<Notes>
<Note time="0.5" duration="0.25" key="51" vel="1"/>
<Note time="1.25" duration="0.25" key="58" vel="1"/>
<Note time="2" duration="0.25" key="51" vel="1"/>
<Note time="4.5" duration="0.25" key="58" vel="1"/>
<Note time="5.25" duration="0.25" key="50" vel="1"/>
<Note time="6" duration="0.25" key="58" vel="1"/>
<Note time="8.5" duration="0.25" key="51" vel="1"/>
<Note time="9.25" duration="0.25" key="58" vel="1"/>
<Note time="10" duration="0.25" key="51" vel="1"/>
<Note time="12.5" duration="0.25" key="58" vel="1"/>
<Note time="13.25" duration="0.25" key="50" vel="1"/>
<Note time="14" duration="0.25" key="58" vel="1"/>
</Notes>
</Clip>
<Clip time="4" duration="16">
<Notes>
<Note time="0.5" duration="0.25" key="51" vel="1"/>
<Note time="1.25" duration="0.25" key="58" vel="1"/>
<Note time="2" duration="0.25" key="51" vel="1"/>
<Note time="4.5" duration="0.25" key="58" vel="1"/>
<Note time="5.25" duration="0.25" key="50" vel="1"/>
<Note time="6" duration="0.25" key="58" vel="1"/>
<Note time="8.5" duration="0.25" key="51" vel="1"/>
<Note time="9.25" duration="0.25" key="58" vel="1"/>
<Note time="10" duration="0.25" key="51" vel="1"/>
<Note time="12.5" duration="0.25" key="58" vel="1"/>
<Note time="13.25" duration="0.25" key="50" vel="1"/>
<Note time="14" duration="0.25" key="58" vel="1"/>
</Notes>
</Clip>
</Clips>
</Lanes>
</Lanes>
<TempoAutomation unit="bpm" id="tempo1" name="Automation Track">
<Target parameter="tempo0"/>
<IntegerPoint value="131" time="0"/>
</TempoAutomation>
</Arrangement>
<Scenes/>
</Project>
@Veratil, that sounds like a lot of work! 👍
So far the only things I really hit were:
1. There's no way that I can find to save base64 encoded data, so a few of our plugins won't work currently
I have only glanced over the schema but if I understand correctly the internal state of the plugins is stored under State
which is of type fileReference
.
<xs:element name="State" type="fileReference" minOccurs="0"/>
So the internal state is not contained in the XML itself but in one or more files which are then referenced from the XML. It would be really nice to have the option for completely self-contained XML files though.
Assuming that these files are intended to store byte streams I now wonder if this might be one of the limits of the project and the format because in that case one might run into problems of endianess, etc. in case plugins do not always save in the exact binary format on all platforms.
Now that I have written all this I also wonder what the other parameters are then intended to be used for and where's the boundary between parameters and state? 🤔
I have only glanced over the schema but if I understand correctly the internal state of the plugins is stored under
State
which is of typefileReference
.
I guess that could be done instead, yes. Endianness doesn't matter as long as the reader is made to read it properly. MIDI files for instance are written in big endian, and we read those fine. :)
Enhancement Summary
This is a note/action plan for implementing dawproject format. I've been working on the DataFile class a lot lately so this is a natural extension of that work.
Justification
Something similar was brough up in #3981