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.songpart; 7 8 import std.algorithm : map, joiner; 9 import std.array : array, join; 10 import std.string : format; 11 12 import musicpulator.musicalscale; 13 import musicpulator.musicalprogression; 14 import musicpulator.songtrack; 15 import musicpulator.core; 16 17 /// Enumeration of song part titles. 18 enum SongPartTitle : string 19 { 20 /// An outro. 21 outro = "Outro", 22 /// The first half of an outro. 23 outroFirst = "Outro (First Half)", 24 /// The second half of an outro. 25 outroSecond = "Outro (Second Half)", 26 /// An intro. 27 intro = "Intro", 28 /// The first half of an intro. 29 introFirst = "Intro (First Half)", 30 /// The second half of an intro. 31 introSecond = "Intro (Second Half)", 32 /// A verse. 33 verse = "Verse", 34 /// The first verse. 35 verse1 = "Verse 1", 36 /// The second verse. 37 verse2 = "Verse 2", 38 /// The third verse. 39 verse3 = "Verse 3", 40 /// The fourth verse. 41 verse4 = "Verse 4", 42 /// The first half of the first verse. 43 verse1First = "Verse 1 (First Half)", 44 /// The first half of the second verse. 45 verse2First = "Verse 2 (First Half)", 46 /// The first half of the third verse. 47 verse3First = "Verse 3 (First Half)", 48 /// The first half of the fourth verse. 49 verse4First = "Verse 4 (First Half)", 50 /// The second half of the first verse. 51 verse1Second = "Verse 1 (Second Half)", 52 /// The second half of the second verse. 53 verse2Second = "Verse 2 (Second Half)", 54 /// The second half of the third verse. 55 verse3Second = "Verse 3 (Second Half)", 56 /// The second half of the fouth verse. 57 verse4Second = "Verse 4 (Second Half)", 58 /// A chorus. 59 chorus = "Chorus", 60 /// The first chorus. 61 chorus1 = "Chorus 1", 62 /// The second chorus. 63 chorus2 = "Chorus 2", 64 /// The third chorus. 65 chorus3 = "Chorus 3", 66 /// The fourth chorus. 67 chorus4 = "Chorus 4", 68 /// The first half of the first chorus. 69 chorus1First = "Chorus 1 (First Half)", 70 /// The first half of the second chorus. 71 chorus2First = "Chorus 2 (First Half)", 72 /// The first half of the third chorus. 73 chorus3First = "Chorus 3 (First Half)", 74 /// The first half of the fourth chorus. 75 chorus4First = "Chorus 4 (First Half)", 76 /// The second half of the first chorus. 77 chorus1Second = "Chorus 1 (Second Half)", 78 /// The second half of the second chorus. 79 chorus2Second = "Chorus 2 (Second Half)", 80 /// The second half of third chorus. 81 chorus3Second = "Chorus 3 (Second Half)", 82 /// The second half of the fourth chorus. 83 chorus4Second = "Chorus 4 (Second Half)", 84 /// A bridge. 85 bridge = "Bridge", 86 /// The first bridge. 87 bridge1 = "Bridge 1", 88 /// The second bridge. 89 bridge2 = "Bridge 2", 90 /// The third bridge. 91 bridge3 = "Bridge 3", 92 /// The fourth bridge. 93 bridge4 = "Bridge 4", 94 /// A solo. 95 solo = "Solo", 96 /// The first solo. 97 solo1 = "Solo 1", 98 /// The second solo. 99 solo2 = "Solo 2", 100 /// The third solo. 101 solo3 = "Solo 3", 102 /// The fourth solo. 103 solo4 = "Solo 4", 104 /// The fifth solo. 105 solo5 = "Solo 5", 106 /// The sixth solo. 107 solo6 = "Solo 6", 108 /// The seventh solo. 109 solo7 = "Solo 7", 110 /// The eighth solo. 111 solo8 = "Solo 8" 112 } 113 114 /// Wrapper around a song part. 115 final class SongPart 116 { 117 private: 118 /// The title of the song part. 119 SongPartTitle _title; 120 /// The bar of the song part. 121 size_t _bar; 122 /// The tracks of the song part. 123 InternalCollection!SongTrack _tracks; 124 125 public: 126 final: 127 /** 128 * Creates a new song part. 129 * Params: 130 * title = The title. 131 */ 132 this(SongPartTitle title) 133 { 134 _title = title; 135 } 136 137 @property 138 { 139 /// Gets the title of the song part. 140 SongPartTitle title() { return _title; } 141 142 /// Gets the bar of the song part. 143 size_t bar() { return _bar; } 144 145 /// Sets the bar of the song part. 146 void bar(size_t newBar) 147 { 148 _bar = newBar; 149 } 150 151 /// Gets the tracks of the song part. 152 InternalCollection!SongTrack tracks() { return _tracks; } 153 } 154 155 /** 156 * Adds a track to the song part. 157 * Params: 158 * track = The track to add. 159 */ 160 void addTrack(SongTrack track) 161 { 162 if (!track) 163 { 164 return; 165 } 166 167 track.parentPart = this; 168 169 _tracks.add(track); 170 } 171 172 /// Converts the song part to string. Calls toJson(). 173 override string toString() 174 { 175 return toJson(); 176 } 177 178 /// Converts the song part to json. 179 string toJson() 180 { 181 auto tracksJson = "["; 182 183 if (_tracks.length) 184 { 185 foreach (track; _tracks) 186 { 187 tracksJson ~= track.toJson() ~ ","; 188 } 189 190 tracksJson.length -= 1; 191 } 192 193 tracksJson ~= "]"; 194 195 return `{"tracks":%s,"title": "%s", "bar": %d}` 196 .format(tracksJson, cast(string)_title, _bar); 197 } 198 199 /// Converts the song part to xml. 200 string toXml() 201 { 202 auto tracksXml = ""; 203 204 if (_tracks.length) 205 { 206 tracksXml = _tracks.map!(t => t.toXml()).array.join; 207 } 208 209 return `<SongPart title="%s" bar="%d">%s</SongPart>` 210 .format(cast(string)_title, _bar, tracksXml); 211 } 212 }