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.songchord;
7 
8 import std.algorithm : map, joiner;
9 import std.array : array, join;
10 import std..string : format;
11 
12 import musicpulator.tools;
13 import musicpulator.core;
14 import musicpulator.constants;
15 import musicpulator.songchordentry;
16 import musicpulator.musicalscale;
17 
18 /// Alias for the chord entry collection.
19 private alias ChordEntryCollection = InternalMaxSizeCollection!(SongChordEntry, songBarSize);
20 
21 /// Wrapper around a song chord.
22 final class SongChord
23 {
24   private:
25   /// The entries.
26   ChordEntryCollection _entries;
27   /// The bar of the chord.
28   size_t _bar;
29   /// The positive harmonics.
30   size_t _positiveHarmonics;
31   /// The negative harmonics.
32   size_t _negativeHarmonics;
33 
34   public:
35   final:
36   /**
37   * Creates a new song chord.
38   * Params:
39   *   entries =           The entries of the song chord.
40   *   bar =               The bar of the chord.
41   *   positiveHarmonics = The positive harmonics. 0 by default.
42   *   negativeHarmonics = The negative harmonics. 0 by default.
43   */
44   this(size_t bar, size_t positiveHarmonics = 0, size_t negativeHarmonics = 0)
45   {
46     _bar = bar;
47     _positiveHarmonics = positiveHarmonics;
48     _negativeHarmonics = negativeHarmonics;
49   }
50 
51   /**
52   * Creates a new song chord.
53   * Params:
54   *   entries =           The entries of the song chord.
55   *   bar =               The bar of the chord.
56   *   positiveHarmonics = The positive harmonics. 0 by default.
57   *   negativeHarmonics = The negative harmonics. 0 by default.
58   */
59   this(SongChordEntry[] entries, size_t bar, size_t positiveHarmonics = 0, size_t negativeHarmonics = 0)
60   {
61     if (entries && entries.length)
62     {
63       foreach (entry; entries.map!(e => new SongChordEntry(e.length, e.bar, e.notes.array)))
64       {
65         _entries.add(entry);
66       }
67     }
68 
69     _bar = bar;
70     _positiveHarmonics = positiveHarmonics;
71     _negativeHarmonics = negativeHarmonics;
72   }
73 
74   @property
75   {
76     /// Gets the entries of the chord.
77     ChordEntryCollection entries() { return _entries; }
78 
79     /// Gets the scales of the chord.
80     const(MusicalScale[][]) scales()
81     {
82       MusicalScale[][] chordScales;
83 
84       foreach (entry; _entries)
85       {
86         auto entryScales = entry.scalesInternal;
87 
88         if (entryScales)
89         {
90           chordScales ~= entryScales;
91         }
92       }
93 
94       return chordScales;
95     }
96 
97     /// Gets the positive harmonics of the chord.
98     size_t positiveHarmonics() { return _positiveHarmonics; }
99 
100     /// Sets the positive harmonics of the chord.
101     void positiveHarmonics(size_t harmonics)
102     {
103       _positiveHarmonics = harmonics;
104     }
105 
106     /// Gets the negative harmonics of the chord.
107     size_t negativeHarmonics() { return _negativeHarmonics; }
108 
109     /// Sets the negative harmonics of the chord.
110     void negativeHarmonics(size_t harmonics)
111     {
112       _negativeHarmonics = harmonics;
113     }
114 
115     /// Gets the bar of the chord.
116     size_t bar() { return _bar; }
117   }
118 
119   /**
120   * Adds a entries to the chord entry.
121   * Params:
122   *   entries = The entries to add.
123   */
124   void addChordEntry(SongChordEntry entry)
125   {
126     if (!entry)
127     {
128       return;
129     }
130 
131     entry.parentChord = this;
132 
133     _entries.add(entry);
134   }
135 
136   /// Converts the chord to a string. This will call toJson().
137   override string toString()
138   {
139     return toJson();
140   }
141 
142   /// Converts the chord to json.
143   string toJson()
144   {
145     return `{"bar":%d,"positiveHarmonics":%d,"negativeHarmonics":%d,"chordEntries":%s}`
146       .format(_bar, _positiveHarmonics, _negativeHarmonics, _entries.map!(e => e.toJson()).joiner(",").array);
147   }
148 
149   /// Converts the chord to xml.
150   string toXml()
151   {
152     auto entryXml = "";
153 
154     if (_entries.length)
155     {
156       entryXml = _entries.map!(e => e.toXml()).array.join;
157     }
158 
159     return `<SongChord bar="%d" positiveHarmonics="%d" negativeHarmonics="%d">%s</SongChord>`
160       .format(_bar, _positiveHarmonics, _negativeHarmonics, entryXml);
161   }
162 }