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