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.songsequence;
7 
8 import std.algorithm : map, sum;
9 import std.array : array, join;
10 import std.string : format;
11 
12 import musicpulator.musicalnote;
13 import musicpulator.core;
14 import musicpulator.constants;
15 
16 /// Aliases for the song sequence entry collection.
17 private alias SongSequenceEntryCollection = InternalMaxSizeCollection!(size_t, 32);
18 
19 /// Wrapper around a song sequence.
20 final class SongSequence
21 {
22   private:
23   /// The note of the sequence.
24   MusicalNote _note;
25   /// The entries of the sequence.
26   SongSequenceEntryCollection _entries;
27   /// The bar of the sequence.
28   size_t _bar;
29   /// The octave of the sequence.
30   size_t _octave;
31 
32   public:
33   final:
34   /**
35   * Creates a new song sequence.
36   * Params:
37   *   note =     The note of the sequence.
38   *   bar =      The bar of the sequence.
39   *   octave =   The octave of the sequence.
40   *   sequence = The sequence entries.
41   */
42   this(MusicalNote note, size_t bar, size_t octave, size_t[] sequence)
43   {
44     _note = note;
45     _bar = bar;
46     _octave = octave;
47 
48     foreach (s; sequence)
49     {
50       addEntry(s);
51     }
52   }
53 
54   @property
55   {
56     /// Gets the note of the sequence.
57     MusicalNote note() { return _note; }
58 
59     /// Gets the entries of the sequence.
60     SongSequenceEntryCollection entries() { return _entries; }
61 
62     /// Gets the bar of the sequence.
63     size_t bar() { return _bar; }
64 
65     /// Gets the octave of the sequence.
66     size_t octave() { return _octave; }
67   }
68 
69   /**
70   * Adds an entry to the sequence.
71   * Params:
72   *   length = The length of the entry to add.
73   */
74   void addEntry(size_t length)
75   {
76     size_t maxLength = songBarSize;
77 
78     if (_entries.length)
79     {
80       maxLength = songBarSize - _entries.sum();
81     }
82 
83     if (length > maxLength)
84     {
85       length = maxLength;
86     }
87     else if (length == 0)
88     {
89       length = 1;
90     }
91 
92     _entries.add(length);
93   }
94 
95   /// Converts the sequence to a string. This calls toJson().
96   override string toString()
97   {
98     return toJson();
99   }
100 
101   /// Converts the sequence to json.
102   string toJson()
103   {
104     return `{"note":%s,"bar":%d,"octave":%d,"sequenceEntry":%s}`
105       .format(_note.toJson(), bar, _octave, _entries);
106   }
107 
108   /// Converts the sequence to xml.
109   string toXml()
110   {
111     auto entryXml = "";
112 
113     if (_entries.length)
114     {
115       entryXml = _entries.map!(e => format(`<SequenceEntry length="%d" />`, e)).array.join;
116     }
117 
118     return `<SongSequence note="%s" bar="%d" octave="%d">%s</SongSequence>`
119       .format(_note, bar, _octave, entryXml);
120   }
121 }