1 /** 2 * Copyright © Underground Rekordz 2019 3 * License: MIT (https://github.com/UndergroundRekordz/Musicpulator/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module musicpulator.songtrack; 7 8 import std.string : format; 9 import std.conv : to; 10 11 import musicpulator.songautomation; 12 import musicpulator.songchord; 13 import musicpulator.songmelody; 14 import musicpulator.songsequence; 15 import musicpulator.songpart; 16 import musicpulator.musicalscale; 17 import musicpulator.musicalprogression; 18 19 /// Alias for a float automation. 20 private alias FloatAutomation = SongAutomation!float; 21 22 /// Wrapper around a song track. 23 final class SongTrack 24 { 25 private: 26 /// The chord of the track. 27 SongChord _chord; 28 /// The melody of the track. 29 SongMelody _melody; 30 /// The sequence of the track. 31 SongSequence _sequence; 32 33 /// The name of the track. 34 string _name; 35 /// The bar of the track. 36 size_t _bar; 37 /// The volume of the track. 38 FloatAutomation _volume; 39 /// The velocity of the track. 40 FloatAutomation _velocity; 41 /// The dryness of the track. 42 FloatAutomation _dry; 43 /// The wetness of the track. 44 FloatAutomation _wet; 45 /// The metadata. 46 string[string] _metaData; 47 /// The meta-automation. 48 FloatAutomation[string] _metaAutomation; 49 /// The parent part. 50 SongPart _parentPart; 51 52 this() 53 { 54 _volume = new FloatAutomation("Volume", 1); 55 _velocity = new FloatAutomation("Velocity", 0.8); 56 _dry = new FloatAutomation("Dry", 0); 57 _wet = new FloatAutomation("Wet", 0); 58 } 59 60 public: 61 final: 62 /** 63 * Creates a new track. 64 * Params: 65 * chord = The chord. 66 */ 67 this(SongChord chord) 68 { 69 this(); 70 71 _chord = chord; 72 } 73 74 /** 75 * Creates a new track. 76 * Params: 77 * melody = The melody. 78 */ 79 this(SongMelody melody) 80 { 81 this(); 82 83 _melody = melody; 84 } 85 86 /** 87 * Creates a new track. 88 * Params: 89 * sequence = The sequence. 90 */ 91 this(SongSequence sequence) 92 { 93 this(); 94 95 _sequence = sequence; 96 } 97 98 @property 99 { 100 /// Gets the chord. 101 SongChord chord() { return _chord; } 102 103 /// Sets the chord. This will nullify the melody and sequence if any. 104 void chord(SongChord newChord) 105 { 106 if (!newChord) 107 { 108 return; 109 } 110 111 _melody = null; 112 _sequence = null; 113 114 _chord = newChord; 115 } 116 117 /// Gets the melody. 118 SongMelody melody() { return _melody; } 119 120 /// Sets the melody. This will nullify the chord and sequence if any. 121 void melody(SongMelody newMelody) 122 { 123 if (!newMelody) 124 { 125 return; 126 } 127 128 _chord = null; 129 _sequence = null; 130 131 _melody = newMelody; 132 } 133 134 /// Gets the sequence. 135 SongSequence sequence() { return _sequence; } 136 137 /// Sets the sequence. This will nullify the chord and melody if any. 138 void sequence(SongSequence newSequence) 139 { 140 if (!newSequence) 141 { 142 return; 143 } 144 145 _chord = null; 146 _melody = null; 147 148 _sequence = newSequence; 149 } 150 151 /// Gets the name of the track. 152 string name() { return _name; } 153 154 /// Sets the name of the track. 155 void name(string newName) 156 { 157 _name = newName; 158 } 159 160 /// Gets the bar of the track. 161 size_t bar() { return _bar; } 162 163 /// Gets the relative bar. 164 size_t relativeBar() 165 { 166 if (_parentPart) 167 { 168 return _parentPart.bar + _bar; 169 } 170 171 return _bar; 172 } 173 174 /// Sets the bar of the track. 175 void bar(size_t newBar) 176 { 177 _bar = newBar; 178 } 179 180 /// Gets the volume of the track. 181 FloatAutomation volume() { return _volume; } 182 183 /// Gets the velocity of the track. 184 FloatAutomation velocity() { return _velocity; } 185 186 /// Gets the dryness of the track. 187 FloatAutomation dry() { return _dry; } 188 189 /// Gets the wetness of the track. 190 FloatAutomation wet() { return _wet; } 191 192 /// Gets the scales of the track. 193 const(MusicalScale[][]) scales() 194 { 195 if (_chord) return _chord.scales; 196 else if (_melody) return [_melody.scales]; 197 else return null; 198 } 199 200 /// Gets the progression of the track. 201 MusicalProgression progression() 202 { 203 if (!_melody) return MusicalProgression.none; 204 205 return _melody.progression; 206 } 207 208 /// Gets the parent part. 209 SongPart parentPart() { return _parentPart; } 210 211 package(musicpulator) 212 { 213 /// Sets the parent part. 214 void parentPart(SongPart newPart) 215 { 216 _parentPart = newPart; 217 } 218 } 219 } 220 221 /** 222 * Sets metadata of the track. 223 * Params: 224 * key = The key of the metadata. 225 * value = The value of the metadata. 226 */ 227 void setMetaData(T)(string key, T value) 228 { 229 _metaData[key] = to!string(value); 230 } 231 232 /** 233 * Gets metadata from the track. 234 * Params: 235 * key = The key of the metadata to retrieve. 236 * Returns: 237 * The metadata if existing, null otherwise. 238 */ 239 T getMetaData(T = string)(string key) 240 { 241 if (key !in _metaData) 242 { 243 return T.init; 244 } 245 246 auto value = _metaData.get(key, T.init); 247 248 return to!T(value); 249 } 250 251 /** 252 * Sets the meta-automation of the track. 253 * Params: 254 * key = The key of the meta-automation. 255 * value = The value of the automation point. 256 * automationPoint = The automation point to the set the value at. 257 */ 258 void setMetaAutomation(string key, float value, size_t automationPoint) 259 { 260 if (automationPoint >= 32) 261 { 262 return; 263 } 264 265 auto automation = _metaAutomation.get(key, null); 266 267 if (!automation) 268 { 269 automation = new FloatAutomation(key, value); 270 _metaAutomation[key] = automation; 271 } 272 else 273 { 274 automation.modifyValue(value, automationPoint); 275 } 276 } 277 278 /** 279 * Gets a meta automation. 280 * Params: 281 * key = The key of the meta-automation to retrieve. 282 * Returns: 283 * The meta-automation if found, null otherwise. 284 */ 285 FloatAutomation getMetaAutomation(string key) 286 { 287 return _metaAutomation.get(key, null); 288 } 289 290 /// Converts the song track to a string. Calls toJson(). 291 override string toString() 292 { 293 return toJson(); 294 } 295 296 /// Converts the song track to json. 297 string toJson() 298 { 299 string metaDataEntry = "null"; 300 301 if (_metaData && _metaData.length) 302 { 303 metaDataEntry = "{"; 304 305 foreach (k,v; _metaData) 306 { 307 metaDataEntry ~= `"%s":"%s",`.format(k,v); 308 } 309 310 metaDataEntry.length -= 1; 311 metaDataEntry ~= "}"; 312 } 313 314 string metaAutomationEntry = "null"; 315 316 if (_metaAutomation && _metaAutomation.length) 317 { 318 metaAutomationEntry = "{"; 319 320 foreach (k,v; _metaAutomation) 321 { 322 metaAutomationEntry ~= `"%s":%s,`.format(k,v.toJson()); 323 } 324 325 metaAutomationEntry.length -= 1; 326 metaAutomationEntry ~= "}"; 327 } 328 329 return `{"chord":%s,"melody":%s,"sequence":%s,"name":%s,"bar":%d,"relativeBar":%d,"volume":%s,"velocity":%s,"dry":%s,"wet":%s,"metaData":%s,"metaAutomation":%s}` 330 .format( 331 _chord ? _chord.toJson() : "null", 332 _melody ? _melody.toJson() : "null", 333 _sequence ? _sequence.toJson() : "null", 334 _name ? ("\"" ~ _name ~ "\"") : "null", _bar, relativeBar, 335 _volume.toJson(), _velocity.toJson(), 336 _dry.toJson(), _wet.toJson(), 337 metaDataEntry, metaAutomationEntry 338 ); 339 } 340 341 /// Converts the song track to xml. 342 string toXml() 343 { 344 string metaDataEntry = ""; 345 346 if (_metaData && _metaData.length) 347 { 348 metaDataEntry = "<SongMetaData>"; 349 350 foreach (k,v; _metaData) 351 { 352 metaDataEntry ~= `<SongMetaDataEntry key="%s" value="%s" />`.format(k,v); 353 } 354 355 metaDataEntry ~= "</SongMetaData>"; 356 } 357 358 string metaAutomationEntry = ""; 359 360 if (_metaAutomation && _metaAutomation.length) 361 { 362 metaAutomationEntry = "<SongMetaAutomation>"; 363 364 foreach (k,v; _metaAutomation) 365 { 366 metaAutomationEntry ~= v.toXml(); 367 } 368 369 metaAutomationEntry ~= "</SongMetaAutomation>"; 370 } 371 372 return `<SongTrack name="%s" bar="%d" relativeBar="%d">%s%s%s %s%s%s%s %s %s</SongTrack>` 373 .format( 374 _name ? _name : "", _bar, relativeBar, 375 _chord ? _chord.toXml() : "", 376 _melody ? _melody.toXml() : "", 377 _sequence ? _sequence.toXml() : "", 378 _volume.toXml(), _velocity.toXml(), 379 _dry.toXml(), _wet.toXml(), 380 metaDataEntry, metaAutomationEntry 381 ); 382 } 383 }