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: doesn't support BigEndian properly there 205 write_uint_LE(userData, *cast(uint*)(&value)); 206 } 207 208 void write_uint_LE(void* userData, uint value) @nogc 209 { 210 ubyte[4] v; 211 *cast(uint*)(v.ptr) = value; 212 version(BigEndian) 213 { 214 ubyte v0 = v[0]; 215 v[0] = v[3]; 216 v[3] = v0; 217 ubyte v1 = v[1]; 218 v[1] = v[2]; 219 v[2] = v1; 220 } 221 222 if (4 != write(v.ptr, 4, userData)) 223 throw mallocNew!Exception(writeFailureMessage); 224 } 225 226 void write_ushort_LE(void* userData, ushort value) @nogc 227 { 228 ubyte[2] v; 229 *cast(ushort*)(v.ptr) = value; 230 version(BigEndian) 231 { 232 ubyte v0 = v[0]; 233 v[0] = v[1]; 234 v[1] = v0; 235 } 236 237 if (2 != write(v.ptr, 2, userData)) 238 throw mallocNew!Exception(writeFailureMessage); 239 } 240 241 void writeRIFFChunkHeader(void* userData, uint chunkId, uint chunkSize) @nogc 242 { 243 write_uint_BE(userData, chunkId); 244 write_uint_LE(userData, chunkSize); 245 } 246 247 // </writing> 248 } 249 250 251 template RIFFChunkId(string id) 252 { 253 static assert(id.length == 4); 254 uint RIFFChunkId = (cast(ubyte)(id[0]) << 24) 255 | (cast(ubyte)(id[1]) << 16) 256 | (cast(ubyte)(id[2]) << 8) 257 | (cast(ubyte)(id[3])); 258 }