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 }