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_float_LE(void* userData, float value) @nogc
203     {
204         // BUG: does it support BigEndian properly here?
205         write_uint_LE(userData, *cast(uint*)(&value));
206     }
207 
208     void write_double_LE(void* userData, float value) @nogc
209     {
210         // BUG: does it support BigEndian properly here?
211         write_ulong_LE(userData, *cast(ulong*)(&value));
212     }
213 
214     void write_ulong_LE(void* userData, ulong value) @nogc
215     {
216         ubyte[8] v;
217         *cast(ulong*)(v.ptr) = value;
218         version(BigEndian)
219         {
220             ubyte v0 = v[0];
221             v[0] = v[7];
222             v[7] = v0;
223             ubyte v1 = v[1];
224             v[1] = v[6];
225             v[6] = v1;
226             ubyte v2 = v[2];
227             v[2] = v[5];
228             v[5] = v2;
229             ubyte v3 = v[3];
230             v[3] = v[4];
231             v[4] = v3;
232         }
233 
234         if (8 != write(v.ptr, 8, userData))
235             throw mallocNew!Exception(writeFailureMessage);
236     }
237 
238     void write_uint_LE(void* userData, uint value) @nogc
239     {
240         ubyte[4] v;
241         *cast(uint*)(v.ptr) = value;
242         version(BigEndian)
243         {
244             ubyte v0 = v[0];
245             v[0] = v[3];
246             v[3] = v0;
247             ubyte v1 = v[1];
248             v[1] = v[2];
249             v[2] = v1;
250         }
251 
252         if (4 != write(v.ptr, 4, userData))
253             throw mallocNew!Exception(writeFailureMessage);
254     }
255 
256     void write_ushort_LE(void* userData, ushort value) @nogc
257     {
258         ubyte[2] v;
259         *cast(ushort*)(v.ptr) = value;
260         version(BigEndian)
261         {
262             ubyte v0 = v[0];
263             v[0] = v[1];
264             v[1] = v0;
265         }
266 
267         if (2 != write(v.ptr, 2, userData))
268             throw mallocNew!Exception(writeFailureMessage);
269     }
270 
271     void writeRIFFChunkHeader(void* userData, uint chunkId, uint chunkSize) @nogc
272     {
273         write_uint_BE(userData, chunkId);
274         write_uint_LE(userData, chunkSize);
275     }
276 
277     // </writing>
278 }
279 
280 
281 template RIFFChunkId(string id)
282 {
283     static assert(id.length == 4);
284     uint RIFFChunkId = (cast(ubyte)(id[0]) << 24)
285         | (cast(ubyte)(id[1]) << 16)
286         | (cast(ubyte)(id[2]) << 8)
287         | (cast(ubyte)(id[3]));
288 }