1 module soundprocessing.wavfile; 2 3 import std.conv; 4 5 import dlib.audio.sound; 6 7 import soundprocessing.utils; 8 9 /++ 10 + The WavFile class 11 +/ 12 public class WavFile 13 { 14 /++ 15 + Constructs a new `WavFile` object by arguments 16 +/ 17 public static WavFile construct(double duration, uint pitch, string path = "") 18 { 19 throw new Exception("Method not implemented yet!"); 20 } 21 22 private { 23 GenericSound _sound; 24 double _duration; 25 uint _pitch; 26 size_t _dataSize; 27 WavFile[] _files; 28 string _filePath; 29 } 30 31 public { 32 /++ 33 + The `GenericSound` object for the file 34 +/ 35 @property GenericSound sound() { return _sound; } 36 37 /++ 38 + The duration of the sound (seconds) 39 +/ 40 @property double duration() { return _duration; } 41 42 /++ 43 + The pitch of the sound (sample rate) 44 +/ 45 @property uint pitch() { return _pitch; } 46 47 /++ 48 + The total data size for the file (including all sub files) 49 +/ 50 @property size_t dataSize() { return _dataSize; } 51 52 /++ 53 + An array of all sub files in the file 54 +/ 55 @property WavFile[] files() { return _files; } 56 57 /++ 58 + The path of the wav file. If the `WavFile(GenericSound)` constructor is used, the default value is "" 59 +/ 60 @property string filePath() { return _filePath; } 61 } 62 63 /++ 64 + Takes in a path to the wav file to be read from 65 +/ 66 this(string path) 67 { 68 this(loadWAVFile(path)); 69 } 70 71 /++ 72 + Takes in a sound object and constructs the file using the sound 73 +/ 74 this(GenericSound sound) 75 { 76 this._sound = sound; 77 this._duration = _sound.duration; 78 this._pitch = _sound.sampleRate; 79 this._dataSize = sound.data.length.to!size_t; 80 this._files = new WavFile[1]; 81 _files[0] = this; 82 83 this._filePath = ""; 84 } 85 86 /++ 87 + Saves the object into a file. `path`'s default value is the objects file path 88 +/ 89 public void save(string path = _filePath) 90 { 91 if (path.length == 0) throw new Exception("Output path may not be empty!"); 92 93 GenericSound newSound = new GenericSound( 94 _dataSize, 95 _sound.sampleSize.to!ulong, 96 _duration, 97 _sound.channels, 98 _pitch, 99 _sound.bitDepth, 100 _sound.sampleFormat 101 ); 102 103 int total = 0; 104 foreach (WavFile file; _files) 105 { 106 GenericSound sound = file.sound(); 107 foreach (ulong i; 0..sound.size) 108 { 109 newSound[0, total] = sound[1, i]; 110 total++; 111 } 112 } 113 114 saveWAVFile(path, newSound); 115 } 116 117 /++ 118 + Combines the file's sound with another file's sound 119 +/ 120 public void combine(WavFile target) 121 { 122 // TODO: We should also include all `target`'s sub files? 123 124 _dataSize += target.dataSize(); 125 126 WavFile[] tempFiles = _files; 127 _files = new WavFile[tempFiles.length + 1]; 128 129 foreach (ulong i; 0..tempFiles.length) 130 { 131 _files[i] = tempFiles[i]; 132 } 133 134 _files[tempFiles.length] = target; 135 } 136 137 /++ 138 + Sets the play duration (seconds) 139 +/ 140 public void setDuration(double duration) 141 { 142 _duration = duration; 143 } 144 145 /++ 146 + Sets the pitch (sample rate) 147 +/ 148 public void setPitch(uint pitch) 149 { 150 _pitch = pitch; 151 } 152 } 153 154 unittest 155 { 156 WavFile test1 = new WavFile("Assets/Sound1.wav"); 157 test1.setPitch(30_000); 158 test1.setDuration(test1.sound().duration + 1); 159 160 WavFile test2 = new WavFile("Assets/Sound2.wav"); 161 test2.setPitch(78_000); 162 test2.setDuration(test2.sound().duration - 1); 163 164 test1.combine(test2); 165 test1.save("Assets/UnittestCombined1.wav"); 166 167 test2.combine(test1); 168 test2.save("Assets/UnittestCombined2.wav"); 169 }