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 }