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 }