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 }