1 /* 2 Copyright (c) 2016-2021 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Generic sound interfaces and their implementations 31 * 32 * Copyright: Timur Gafarov 2016-2021. 33 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 34 * Authors: Timur Gafarov 35 */ 36 module dlib.audio.sound; 37 38 import std.math; 39 import dlib.audio.sample; 40 41 /** 42 * Generalized sound stream class. 43 * This can be used to implement any type of sound, 44 * including compressed audio streams. 45 */ 46 abstract class StreamedSound 47 { 48 /// Number of channels per sample 49 @property uint channels(); 50 51 /// Number of samples per second, in Hz 52 @property uint sampleRate(); 53 54 /// Number of bits in each sample channel 55 @property uint bitDepth(); 56 57 /// Number of bytes in each sample (bitDepth / 8 * channels) 58 @property uint sampleSize() 59 { 60 return (bitDepth / 8) * channels; 61 } 62 63 /// Sample format 64 @property SampleFormat sampleFormat(); 65 66 /// Fill in the buffer with next portion of sound data. 67 /// Function returns actual number of bytes written. 68 /// At the end of a stream, zero is returned. 69 ulong stream(ubyte[] buffer); 70 71 /// Return to the start of a stream 72 void reset(); 73 } 74 75 /** 76 * Sound that can be seeked 77 */ 78 abstract class SeekableSound: StreamedSound 79 { 80 /// Number of samples 81 @property ulong size(); 82 83 /// Duration, in seconds 84 @property double duration(); 85 86 float opIndex(uint chan, ulong pos); 87 float opIndexAssign(float sample, uint chan, ulong pos); 88 } 89 90 /** 91 * Sound that is fully kept in memory 92 */ 93 abstract class Sound: SeekableSound 94 { 95 /// Raw byte data 96 @property ref ubyte[] data(); 97 98 /// Make exact copy of the sound 99 @property Sound dup(); 100 101 /// Make empty sound of the same format 102 Sound createSameFormat(uint ch, double dur); 103 } 104 105 /** 106 * Generic Sound implementation 107 */ 108 class GenericSound: Sound 109 { 110 protected: 111 ubyte[] _data; 112 ulong _size; 113 double _duration; 114 uint _channels; 115 uint _sampleRate; 116 uint _bitDepth; 117 SampleFormat _format; 118 119 public: 120 this(Sound ras) 121 { 122 auto rasData = ras.data; 123 allocateData(rasData.length); 124 for(size_t i = 0; i < _data.length; i++) 125 _data[i] = rasData[i]; 126 _size = ras.size; 127 _duration = ras.duration; 128 _channels = ras.channels; 129 _sampleRate = ras.sampleRate; 130 _bitDepth = ras.bitDepth; 131 _format = ras.sampleFormat; 132 } 133 134 this(double dur, 135 uint freq, 136 uint numChannels, 137 SampleFormat f) 138 { 139 _duration = dur; 140 _sampleRate = freq; 141 _channels = numChannels; 142 _size = cast(ulong)ceil(dur * freq); 143 _format = f; 144 if (f == SampleFormat.U8 || f == SampleFormat.S8) 145 _bitDepth = 8; 146 else if (f == SampleFormat.U16 || f == SampleFormat.S16) 147 _bitDepth = 16; 148 size_t dataSize = cast(size_t)(_size * (numChannels * (_bitDepth / 8))); 149 allocateData(dataSize); 150 } 151 152 this(size_t dataSize, 153 ulong numSamples, 154 double dur, 155 uint numChannels, 156 uint freq, 157 uint bitdepth, 158 SampleFormat f) 159 { 160 allocateData(dataSize); 161 _size = numSamples; 162 _duration = dur; 163 _channels = numChannels; 164 _sampleRate = freq; 165 _bitDepth = bitdepth; 166 _format = f; 167 } 168 169 override @property ulong size() 170 { 171 return _size; 172 } 173 174 override @property double duration() 175 { 176 return _duration; 177 } 178 179 override @property uint channels() 180 { 181 return _channels; 182 } 183 184 override @property uint sampleRate() 185 { 186 return _sampleRate; 187 } 188 189 override @property uint bitDepth() 190 { 191 return _bitDepth; 192 } 193 194 override @property SampleFormat sampleFormat() 195 { 196 return _format; 197 } 198 199 override @property ref ubyte[] data() 200 { 201 return _data; 202 } 203 204 protected: 205 size_t position; // sample position 206 207 protected void allocateData(size_t size) 208 { 209 _data = new ubyte[size]; 210 } 211 212 public: 213 override ulong stream(ubyte[] buffer) 214 { 215 size_t ssize = sampleSize(); 216 size_t bytePos = position * ssize; 217 size_t sizeInBytes = cast(size_t)_size * ssize; 218 219 size_t bytesWritten = buffer.length; 220 if (bytePos + buffer.length >= sizeInBytes) 221 bytesWritten = sizeInBytes - bytePos; 222 223 for(size_t i = bytePos; i < bytePos + bytesWritten; i++) 224 { 225 buffer[i] = _data[i]; 226 } 227 228 return bytesWritten; 229 } 230 231 override void reset() 232 { 233 position = 0; 234 } 235 236 override float opIndex(uint chan, ulong pos) 237 { 238 size_t ssize = sampleSize(); 239 uint sampleChannelSize = _bitDepth / 8; 240 ubyte* samplePtr = &_data[cast(size_t)pos * ssize] + chan * sampleChannelSize; 241 return toFloatSample(samplePtr, _format); 242 } 243 244 override float opIndexAssign(float s, uint chan, ulong pos) 245 { 246 size_t ssize = sampleSize(); 247 uint sampleChannelSize = _bitDepth / 8; 248 ubyte* samplePtr = &_data[cast(size_t)pos * ssize] + chan * sampleChannelSize; 249 fromFloatSample(samplePtr, _format, s); 250 return s; 251 } 252 253 override @property Sound dup() 254 { 255 return new GenericSound(this); 256 } 257 258 override Sound createSameFormat(uint ch, double dur) 259 { 260 return new GenericSound(dur, _sampleRate, ch, _format); 261 } 262 } 263 264 /** 265 * GenericSound object factory 266 */ 267 interface GenericSoundFactory 268 { 269 GenericSound createSound( 270 size_t dataSize, 271 ulong numSamples, 272 double dur, 273 uint numChannels, 274 uint freq, 275 uint bitdepth, 276 SampleFormat f); 277 } 278 279 /** 280 * GenericSoundFactory implementation for Sound class 281 */ 282 class DefaultGenericSoundFactory: GenericSoundFactory 283 { 284 GenericSound createSound( 285 size_t dataSize, 286 ulong numSamples, 287 double dur, 288 uint numChannels, 289 uint freq, 290 uint bitdepth, 291 SampleFormat f) 292 { 293 return new GenericSound( 294 dataSize, 295 numSamples, 296 dur, 297 numChannels, 298 freq, 299 bitdepth, 300 f 301 ); 302 } 303 } 304 305 private __gshared DefaultGenericSoundFactory _defaultGenericSoundFactory; 306 307 /** 308 * GenericSoundFactory singleton 309 */ 310 DefaultGenericSoundFactory defaultGenericSoundFactory() 311 { 312 if (!_defaultGenericSoundFactory) 313 _defaultGenericSoundFactory = new DefaultGenericSoundFactory(); 314 return _defaultGenericSoundFactory; 315 }