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 }