1 /**
2 Library for sound file decoding and encoding. See README.md for licence explanations.
3 
4 Copyright: Guillaume Piolats 2020.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module audioformats;
8 
9 
10 // Public API
11 
12 public import audioformats.stream;
13 
14 
15 public import audioformats.internals: AudioFormatsException;
16 import core.stdc.stdlib: free;
17 
18 /// Frees an exception thrown by audio-formats.
19 void destroyAudioFormatException(AudioFormatsException e) nothrow @nogc
20 {
21     import audioformats.internals;
22     destroyFree!AudioFormatsException(e);
23 }
24 
25 
26 /// Encode a slice to a WAV file.
27 ///
28 /// Returns: `true` on success.
29 bool saveAsWAV(const(float)[] data, 
30                const(char)[] filePath,
31                int numChannels = 1,
32                float sampleRate = 44100.0f,
33                EncodingOptions options = EncodingOptions.init) nothrow @nogc
34 {
35     return saveAsWAVImpl!float(data, filePath, numChannels, sampleRate, options);
36 }
37 ///ditto
38 bool saveAsWAV(const(double)[] data, 
39                const(char)[] filePath,
40                int numChannels = 1,
41                float sampleRate = 44100.0f,
42                EncodingOptions options = EncodingOptions.init) nothrow @nogc
43 {
44     return saveAsWAVImpl!double(data, filePath, numChannels, sampleRate, options);
45 }
46 
47 
48 /// Encode a slice to a WAV in memory.
49 /// The returned slice MUST be freed with `freeEncodedAudio`.
50 ///
51 /// Returns: `null` in case of error.
52 const(ubyte)[] toWAV(const(float)[] data, 
53                      int numChannels = 1,
54                      float sampleRate = 44100.0f,
55                      EncodingOptions options = EncodingOptions.init) nothrow @nogc
56 {
57     return toWAVImpl!float(data, numChannels, sampleRate, options);
58 }
59 ///ditto
60 const(ubyte)[] toWAV(const(double)[] data, 
61                      int numChannels = 1,
62                      float sampleRate = 44100.0f,
63                      EncodingOptions options = EncodingOptions.init) nothrow @nogc
64 {
65     return toWAVImpl!double(data, numChannels, sampleRate, options);
66 }
67 
68 
69 /// Disowned audio buffers (with eg. `encodeToWAV`) must be freed with this function.
70 void freeEncodedAudio(const(ubyte)[] encoded)
71 {
72     free(cast(void*)encoded.ptr);
73 }
74 
75 
76 private:
77 
78 
79 const(ubyte)[] toWAVImpl(T)(const(T)[] data, int numChannels, float sampleRate, EncodingOptions options) nothrow @nogc
80 {
81     assert(data !is null);
82     import core.stdc.string: strlen;
83 
84     try
85     {
86         AudioStream encoder;
87         encoder.openToBuffer(AudioFileFormat.wav, sampleRate, numChannels, options);
88         static if (is(T == float))
89             encoder.writeSamplesFloat(data);
90         else
91             encoder.writeSamplesDouble(data);
92         const(ubyte)[] r = encoder.finalizeAndGetEncodedResultDisown();
93         return r;
94     }
95     catch (AudioFormatsException e)
96     {
97         destroyAudioFormatException(e);
98         return null;
99     }
100     catch(Exception e)
101     {
102         return null;
103     }
104 }
105 
106 bool saveAsWAVImpl(T)(const(T)[] data, 
107                       const(char)[] filePath,
108                       int numChannels, 
109                       float sampleRate,
110                       EncodingOptions options) nothrow @nogc
111 {
112     if (data is null)
113         return false;
114     if (filePath is null)
115         return false;
116 
117     import core.stdc.string: strlen;
118 
119     try
120     {
121         AudioStream encoder;
122         encoder.openToFile(filePath, AudioFileFormat.wav, sampleRate, numChannels, options);
123         static if (is(T == float))
124             encoder.writeSamplesFloat(data);
125         else
126             encoder.writeSamplesDouble(data);
127         encoder.flush();
128         encoder.finalizeEncoding();
129     }
130     catch (AudioFormatsException e)
131     {
132         destroyAudioFormatException(e);
133         return false;
134     }
135     catch(Exception e)
136     {
137         return false;
138     }
139     return true;
140 }