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 }