1 module audioformats.io;
2 
3 import dplug.core.nogc;
4 
5 
6 nothrow @nogc
7 {
8     alias ioSeekCallback          = void function(long offset, bool relative, void* userData);
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     ushort read_ushort_LE(void* userData) @nogc
63     {
64         ubyte[2] v;
65         if (2 == read(v.ptr, 2, userData))
66         {
67             version(BigEndian)
68             {
69                 ubyte v0 = v[0];
70                 v[0] = v[1];
71                 v[1] = v0;
72             }
73             return *cast(ushort*)(v.ptr);
74         }
75         else
76             throw mallocNew!Exception("expected ushort");
77     }
78 
79     uint read_uint_BE(void* userData) @nogc
80     {
81         ubyte[4] v;
82         if (4 == read(v.ptr, 4, userData))
83         {
84             version(LittleEndian)
85             {
86                 ubyte v0 = v[0];
87                 v[0] = v[3];
88                 v[3] = v0;
89                 ubyte v1 = v[1];
90                 v[1] = v[2];
91                 v[2] = v1;
92             }
93             return *cast(uint*)(v.ptr);
94         }
95         else
96             throw mallocNew!Exception("expected uint");
97     }
98 
99     uint read_uint_LE(void* userData) @nogc
100     {
101         ubyte[4] v;
102         if (4 == read(v.ptr, 4, userData))
103         {
104             version(BigEndian)
105             {
106                 ubyte v0 = v[0];
107                 v[0] = v[3];
108                 v[3] = v0;
109                 ubyte v1 = v[1];
110                 v[1] = v[2];
111                 v[2] = v1;
112             }
113             return *cast(uint*)(v.ptr);
114         }
115         else
116             throw mallocNew!Exception("expected uint");
117     }
118 
119     uint read_24bits_LE(void* userData) @nogc
120     {
121         ubyte[3] v;
122         if (3 == read(v.ptr, 3, userData))
123         {
124             return v[0] | (v[1] << 8) | (v[2] << 16);
125         }
126         else
127             throw mallocNew!Exception("expected 24-bit int");
128     }
129 
130     float read_float_LE(void* userData) @nogc
131     {
132         uint u = read_uint_LE(userData);
133         return *cast(float*)(&u);
134     }
135 
136     double read_double_LE(void* userData) @nogc
137     {
138         ubyte[8] v;
139         if (8 == read(v.ptr, 8, userData))
140         {
141             version(BigEndian)
142             {
143                 ubyte v0 = v[0];
144                 v[0] = v[7];
145                 v[7] = v0;
146                 ubyte v1 = v[1];
147                 v[1] = v[6];
148                 v[6] = v1;
149                 ubyte v2 = v[2];
150                 v[2] = v[5];
151                 v[5] = v2;
152                 ubyte v3 = v[3];
153                 v[3] = v[4];
154                 v[4] = v3;
155             }
156             return *cast(double*)(v.ptr);
157         }
158         else
159             throw mallocNew!Exception("expected double");
160     }
161 
162     void readRIFFChunkHeader(void* userData, ref uint chunkId, ref uint chunkSize) @nogc
163     {
164         chunkId = read_uint_BE(userData);
165         chunkSize = read_uint_LE(userData);
166     }
167 
168     // </reading>
169 
170     // <writing>
171 
172     string writeFailureMessage = "write failure";
173 
174     void write_uint_BE(void* userData, uint value) @nogc
175     {
176         ubyte[4] v;
177         *cast(uint*)(v.ptr) = value;
178         version(LittleEndian)
179         {
180             ubyte v0 = v[0];
181             v[0] = v[3];
182             v[3] = v0;
183             ubyte v1 = v[1];
184             v[1] = v[2];
185             v[2] = v1;
186         }
187 
188         if (4 != write(v.ptr, 4, userData))
189             throw mallocNew!Exception(writeFailureMessage);
190     }
191 
192     void write_float_LE(void* userData, float value) @nogc
193     {
194         write_uint_LE(userData, *cast(uint*)(&value));
195     }
196 
197     void write_uint_LE(void* userData, uint value) @nogc
198     {
199         ubyte[4] v;
200         *cast(uint*)(v.ptr) = value;
201         version(BigEndian)
202         {
203             ubyte v0 = v[0];
204             v[0] = v[3];
205             v[3] = v0;
206             ubyte v1 = v[1];
207             v[1] = v[2];
208             v[2] = v1;
209         }
210 
211         if (4 != write(v.ptr, 4, userData))
212             throw mallocNew!Exception(writeFailureMessage);
213     }
214 
215     void write_ushort_LE(void* userData, uint value) @nogc
216     {
217         ubyte[2] v;
218         *cast(uint*)(v.ptr) = value;
219         version(BigEndian)
220         {
221             ubyte v0 = v[0];
222             v[0] = v[1];
223             v[1] = v0;
224         }
225 
226         if (2 != write(v.ptr, 2, userData))
227             throw mallocNew!Exception(writeFailureMessage);
228     }
229 
230     void writeRIFFChunkHeader(void* userData, uint chunkId, uint chunkSize) @nogc
231     {
232         write_uint_BE(userData, chunkId);
233         write_uint_LE(userData, chunkSize);
234     }
235 
236     // </writing>
237 }
238 
239 
240 template RIFFChunkId(string id)
241 {
242     static assert(id.length == 4);
243     uint RIFFChunkId = (cast(ubyte)(id[0]) << 24)
244         | (cast(ubyte)(id[1]) << 16)
245         | (cast(ubyte)(id[2]) << 8)
246         | (cast(ubyte)(id[3]));
247 }