1 module audioformats.io;
2 
3 import audioformats.internals;
4 
5 nothrow @nogc
6 {
7     alias ioSeekCallback          = bool function(long offset, bool relative, void* userData); // return true on success
8     alias ioTellCallback          = long function(                            void* userData);  
9     alias ioGetFileLengthCallback = long function(                            void* userData);
10     alias ioReadCallback          = int  function(void* outData, int bytes,   void* userData); // returns number of read bytes
11     alias ioWriteCallback         = int  function(void* inData, int bytes,    void* userData); // returns number of written bytes
12     alias ioSkipCallback          = bool function(int bytes,                  void* userData);
13     alias ioFlushCallback         = bool function(                            void* userData);
14 }
15 
16 struct IOCallbacks
17 {
18     ioSeekCallback seek;
19     ioTellCallback tell;
20     ioGetFileLengthCallback getFileLength;
21     ioReadCallback read;
22     ioWriteCallback write;
23     ioSkipCallback skip;
24     ioFlushCallback flush;
25 
26 
27     // Now, some helpers for binary parsing based on these callbacks
28 
29     // <reading>
30 
31     bool nothingToReadAnymore(void* userData) @nogc
32     {
33         return remainingBytesToRead(userData) <= 0;
34     }
35 
36     long remainingBytesToRead(void* userData) @nogc
37     {
38         long cursor = tell(userData);
39         long fileLength = getFileLength(userData);
40         assert(cursor <= fileLength);
41         return fileLength - cursor;
42     }
43   
44     ubyte peek_ubyte(void* userData) @nogc
45     {
46         ubyte b = read_ubyte(userData);
47         seek(tell(userData) - 1, false, userData);
48         return b;
49     }
50 
51     ubyte read_ubyte(void* userData) @nogc
52     {
53         ubyte b;
54         if (1 == read(&b, 1, userData))
55         {
56             return b;
57         }
58         throw mallocNew!AudioFormatsException("expected ubyte");
59     }
60 
61     ubyte[16] read_guid(void* userData) @nogc
62     {
63         ubyte[16] b;
64         if (16 == read(&b, 16, userData))
65         {
66             return b;
67         }
68         throw mallocNew!AudioFormatsException("expected a GUID");
69     }
70 
71     ushort read_ushort_LE(void* userData) @nogc
72     {
73         ubyte[2] v;
74         if (2 == read(v.ptr, 2, userData))
75         {
76             version(BigEndian)
77             {
78                 ubyte v0 = v[0];
79                 v[0] = v[1];
80                 v[1] = v0;
81             }
82             return *cast(ushort*)(v.ptr);
83         }
84         else
85             throw mallocNew!AudioFormatsException("expected ushort");
86     }
87 
88     uint read_uint_BE(void* userData) @nogc
89     {
90         ubyte[4] v;
91         if (4 == read(v.ptr, 4, userData))
92         {
93             version(LittleEndian)
94             {
95                 ubyte v0 = v[0];
96                 v[0] = v[3];
97                 v[3] = v0;
98                 ubyte v1 = v[1];
99                 v[1] = v[2];
100                 v[2] = v1;
101             }
102             return *cast(uint*)(v.ptr);
103         }
104         else
105             throw mallocNew!AudioFormatsException("expected uint");
106     }
107 
108     uint read_uint_LE(void* userData) @nogc
109     {
110         ubyte[4] v;
111         if (4 == read(v.ptr, 4, userData))
112         {
113             version(BigEndian)
114             {
115                 ubyte v0 = v[0];
116                 v[0] = v[3];
117                 v[3] = v0;
118                 ubyte v1 = v[1];
119                 v[1] = v[2];
120                 v[2] = v1;
121             }
122             return *cast(uint*)(v.ptr);
123         }
124         else
125             throw mallocNew!AudioFormatsException("expected uint");
126     }
127 
128     uint read_24bits_LE(void* userData) @nogc
129     {
130         ubyte[3] v;
131         if (3 == read(v.ptr, 3, userData))
132         {
133             return v[0] | (v[1] << 8) | (v[2] << 16);
134         }
135         else
136             throw mallocNew!AudioFormatsException("expected 24-bit int");
137     }
138 
139     float read_float_LE(void* userData) @nogc
140     {
141         uint u = read_uint_LE(userData);
142         return *cast(float*)(&u);
143     }
144 
145     double read_double_LE(void* userData) @nogc
146     {
147         ubyte[8] v;
148         if (8 == read(v.ptr, 8, userData))
149         {
150             version(BigEndian)
151             {
152                 ubyte v0 = v[0];
153                 v[0] = v[7];
154                 v[7] = v0;
155                 ubyte v1 = v[1];
156                 v[1] = v[6];
157                 v[6] = v1;
158                 ubyte v2 = v[2];
159                 v[2] = v[5];
160                 v[5] = v2;
161                 ubyte v3 = v[3];
162                 v[3] = v[4];
163                 v[4] = v3;
164             }
165             return *cast(double*)(v.ptr);
166         }
167         else
168             throw mallocNew!AudioFormatsException("expected double");
169     }
170 
171     void readRIFFChunkHeader(void* userData, ref uint chunkId, ref uint chunkSize) @nogc
172     {
173         chunkId = read_uint_BE(userData);
174         chunkSize = read_uint_LE(userData);
175     }
176 
177     // </reading>
178 
179     // <writing>
180 
181     string writeFailureMessage = "write failure";
182 
183     void write_uint_BE(void* userData, uint value) @nogc
184     {
185         ubyte[4] v;
186         *cast(uint*)(v.ptr) = value;
187         version(LittleEndian)
188         {
189             ubyte v0 = v[0];
190             v[0] = v[3];
191             v[3] = v0;
192             ubyte v1 = v[1];
193             v[1] = v[2];
194             v[2] = v1;
195         }
196 
197         if (4 != write(v.ptr, 4, userData))
198             throw mallocNew!AudioFormatsException(writeFailureMessage);
199     }
200 
201     void write_byte(void* userData, byte value) @nogc
202     {
203         if (1 != write(&value, 1, userData))
204             throw mallocNew!AudioFormatsException(writeFailureMessage);
205     }
206 
207     void write_short_LE(void* userData, short value) @nogc
208     {
209         ubyte[2] v;
210         *cast(ushort*)(v.ptr) = value;
211         version(BigEndian)
212         {
213             ubyte v0 = v[0];
214             v[0] = v[1];
215             v[1] = v0;
216         }
217         if (2 != write(v.ptr, 2, userData))
218             throw mallocNew!AudioFormatsException(writeFailureMessage);
219     }
220 
221     void write_24bits_LE(void* userData, int value) @nogc
222     {
223         ubyte[4] v;
224         *cast(int*)(v.ptr) = value;
225         version(BigEndian)
226         {
227             ubyte v0 = v[0];
228             v[0] = v[3];
229             v[3] = v0;
230             ubyte v1 = v[1];
231             v[1] = v[2];
232             v[2] = v1;
233         }
234         if (3 != write(v.ptr, 3, userData))
235             throw mallocNew!AudioFormatsException(writeFailureMessage);
236     }
237 
238     void write_float_LE(void* userData, float value) @nogc
239     {
240         write_uint_LE(userData, *cast(uint*)(&value));
241     }
242 
243     void write_double_LE(void* userData, float value) @nogc
244     {
245         write_ulong_LE(userData, *cast(ulong*)(&value));
246     }
247 
248     void write_ulong_LE(void* userData, ulong value) @nogc
249     {
250         ubyte[8] v;
251         *cast(ulong*)(v.ptr) = value;
252         version(BigEndian)
253         {
254             ubyte v0 = v[0];
255             v[0] = v[7];
256             v[7] = v0;
257             ubyte v1 = v[1];
258             v[1] = v[6];
259             v[6] = v1;
260             ubyte v2 = v[2];
261             v[2] = v[5];
262             v[5] = v2;
263             ubyte v3 = v[3];
264             v[3] = v[4];
265             v[4] = v3;
266         }
267 
268         if (8 != write(v.ptr, 8, userData))
269             throw mallocNew!AudioFormatsException(writeFailureMessage);
270     }
271 
272     void write_uint_LE(void* userData, uint value) @nogc
273     {
274         ubyte[4] v;
275         *cast(uint*)(v.ptr) = value;
276         version(BigEndian)
277         {
278             ubyte v0 = v[0];
279             v[0] = v[3];
280             v[3] = v0;
281             ubyte v1 = v[1];
282             v[1] = v[2];
283             v[2] = v1;
284         }
285 
286         if (4 != write(v.ptr, 4, userData))
287             throw mallocNew!AudioFormatsException(writeFailureMessage);
288     }
289 
290     void write_ushort_LE(void* userData, ushort value) @nogc
291     {
292         ubyte[2] v;
293         *cast(ushort*)(v.ptr) = value;
294         version(BigEndian)
295         {
296             ubyte v0 = v[0];
297             v[0] = v[1];
298             v[1] = v0;
299         }
300 
301         if (2 != write(v.ptr, 2, userData))
302             throw mallocNew!AudioFormatsException(writeFailureMessage);
303     }
304 
305     void writeRIFFChunkHeader(void* userData, uint chunkId, uint chunkSize) @nogc
306     {
307         write_uint_BE(userData, chunkId);
308         write_uint_LE(userData, chunkSize);
309     }
310 
311     // </writing>
312 }
313 
314 
315 template RIFFChunkId(string id)
316 {
317     static assert(id.length == 4);
318     __gshared uint RIFFChunkId = (cast(ubyte)(id[0]) << 24)
319         | (cast(ubyte)(id[1]) << 16)
320         | (cast(ubyte)(id[2]) << 8)
321         | (cast(ubyte)(id[3]));
322 }