1 module pxtone.pulse.oggv; 2 3 version (pxINCLUDE_OGGVORBIS): 4 import core.stdc.config; 5 import derelict.vorbis.codec; 6 import derelict.vorbis.file; 7 8 import pxtone.descriptor; 9 import pxtone.error; 10 import pxtone.pulse.pcm; 11 12 import std.stdio; 13 14 struct OVMEM { 15 const(ubyte)[] p_buf; // ogg vorbis-data on memory.s 16 int size; // 17 int pos; // reading position. 18 } 19 20 // 4 callbacks below: 21 22 extern (C) size_t _mread(void* p, size_t size, size_t nmemb, void* p_void) nothrow @system { 23 OVMEM* pom = cast(OVMEM*) p_void; 24 25 if (!pom) { 26 return -1; 27 } 28 if (pom.pos >= pom.size) { 29 return 0; 30 } 31 if (pom.pos == -1) { 32 return 0; 33 } 34 35 int left = pom.size - pom.pos; 36 37 if (cast(int)(size * nmemb) >= left) { 38 p[0 .. pom.size - pom.pos] = cast(void[])pom.p_buf[pom.pos .. pom.size]; 39 pom.pos = pom.size; 40 return left / size; 41 } 42 43 p[0 .. nmemb * size] = cast(void[])pom.p_buf[pom.pos .. pom.pos + nmemb * size]; 44 pom.pos += cast(int)(nmemb * size); 45 46 return nmemb; 47 } 48 49 extern (C) int _mseek(void* p_void, long offset, int mode) nothrow @system { 50 int newpos; 51 OVMEM* pom = cast(OVMEM*) p_void; 52 53 if (!pom || pom.pos < 0) { 54 return -1; 55 } 56 if (offset < 0) { 57 pom.pos = -1; 58 return -1; 59 } 60 61 switch (mode) { 62 case SEEK_SET: 63 newpos = cast(int) offset; 64 break; 65 case SEEK_CUR: 66 newpos = pom.pos + cast(int) offset; 67 break; 68 case SEEK_END: 69 newpos = pom.size + cast(int) offset; 70 break; 71 default: 72 return -1; 73 } 74 if (newpos < 0) { 75 return -1; 76 } 77 78 pom.pos = newpos; 79 80 return 0; 81 } 82 83 extern (C) c_long _mtell(void* p_void) nothrow @system { 84 OVMEM* pom = cast(OVMEM*) p_void; 85 if (!pom) { 86 return -1; 87 } 88 return pom.pos; 89 } 90 91 extern (C) int _mclose_dummy(void* p_void) nothrow @system { 92 OVMEM* pom = cast(OVMEM*) p_void; 93 if (!pom) { 94 return -1; 95 } 96 return 0; 97 } 98 99 ///////////////// 100 // global 101 ///////////////// 102 103 struct pxtnPulse_Oggv { 104 private: 105 int _ch; 106 int _sps2; 107 int _smp_num; 108 int _size; 109 ubyte[] _p_data; 110 111 bool _SetInformation() nothrow @system { 112 bool b_ret = false; 113 114 OVMEM ovmem; 115 ovmem.p_buf = _p_data; 116 ovmem.pos = 0; 117 ovmem.size = _size; 118 119 // set callback func. 120 ov_callbacks oc; 121 oc.read_func = &_mread; 122 oc.seek_func = &_mseek; 123 oc.close_func = &_mclose_dummy; 124 oc.tell_func = &_mtell; 125 126 OggVorbis_File vf; 127 128 vorbis_info* vi; 129 130 switch (ov_open_callbacks(&ovmem, &vf, null, 0, oc)) { 131 case OV_EREAD: 132 goto End; //{printf("A read from media returned an error.\n");exit(1);} 133 case OV_ENOTVORBIS: 134 goto End; //{printf("Bitstream is not Vorbis data. \n");exit(1);} 135 case OV_EVERSION: 136 goto End; //{printf("Vorbis version mismatch. \n");exit(1);} 137 case OV_EBADHEADER: 138 goto End; //{printf("Invalid Vorbis bitstream header. \n");exit(1);} 139 case OV_EFAULT: 140 goto End; //{printf("Internal logic fault; indicates a bug or heap/stack corruption. \n");exit(1);} 141 default: 142 break; 143 } 144 145 vi = ov_info(&vf, -1); 146 147 _ch = vi.channels; 148 _sps2 = vi.rate; 149 _smp_num = cast(int) ov_pcm_total(&vf, -1); 150 151 // end. 152 ov_clear(&vf); 153 154 b_ret = true; 155 156 End: 157 return b_ret; 158 159 } 160 161 public: 162 ~this() nothrow @system { 163 Release(); 164 } 165 166 void Decode(pxtnPulse_PCM* p_pcm) const @system { 167 OggVorbis_File vf; 168 vorbis_info* vi; 169 ov_callbacks oc; 170 171 OVMEM ovmem; 172 int current_section; 173 ubyte[4096] pcmout = 0; //take 4k out of the data segment, not the stack 174 175 ovmem.p_buf = _p_data; 176 ovmem.pos = 0; 177 ovmem.size = _size; 178 179 // set callback func. 180 oc.read_func = &_mread; 181 oc.seek_func = &_mseek; 182 oc.close_func = &_mclose_dummy; 183 oc.tell_func = &_mtell; 184 185 switch (ov_open_callbacks(&ovmem, &vf, null, 0, oc)) { 186 case OV_EREAD: 187 throw new PxtoneException("A read from media returned an error"); 188 case OV_ENOTVORBIS: 189 throw new PxtoneException("Bitstream is not Vorbis data"); 190 case OV_EVERSION: 191 throw new PxtoneException("Vorbis version mismatch"); 192 case OV_EBADHEADER: 193 throw new PxtoneException("Invalid Vorbis bitstream header"); 194 case OV_EFAULT: 195 throw new PxtoneException("Internal logic fault; indicates a bug or heap/stack corruption"); 196 default: 197 break; 198 } 199 200 vi = ov_info(&vf, -1); 201 202 { 203 int smp_num = cast(int) ov_pcm_total(&vf, -1); 204 uint bytes; 205 206 bytes = vi.channels * 2 * smp_num; 207 208 p_pcm.Create(vi.channels, vi.rate, 16, smp_num); 209 } 210 // decode.. 211 { 212 int ret = 0; 213 ubyte* p = p_pcm.get_p_buf().ptr; 214 do { 215 ret = cast(int)ov_read(&vf, cast(byte*) pcmout.ptr, 4096, 0, 2, 1, ¤t_section); 216 if (ret > 0) { 217 p[0 .. ret] = pcmout[0 .. ret]; //fwrite( pcmout, 1, ret, of ); 218 } 219 p += ret; 220 } 221 while (ret); 222 } 223 224 // end. 225 ov_clear(&vf); 226 } 227 228 void Release() nothrow @system { 229 _p_data = null; 230 _ch = 0; 231 _sps2 = 0; 232 _smp_num = 0; 233 _size = 0; 234 } 235 236 bool GetInfo(int* p_ch, int* p_sps, int* p_smp_num) nothrow @safe { 237 if (!_p_data) { 238 return false; 239 } 240 241 if (p_ch) { 242 *p_ch = _ch; 243 } 244 if (p_sps) { 245 *p_sps = _sps2; 246 } 247 if (p_smp_num) { 248 *p_smp_num = _smp_num; 249 } 250 251 return true; 252 } 253 254 int GetSize() const nothrow @safe { 255 if (!_p_data) { 256 return 0; 257 } 258 return cast(int)(int.sizeof * 4 + _size); 259 } 260 261 void ogg_write(ref pxtnDescriptor desc) const @system { 262 desc.w_asfile(_p_data); 263 } 264 265 void ogg_read(ref pxtnDescriptor desc) @system { 266 _size = desc.get_size_bytes(); 267 if (!(_size)) { 268 throw new PxtoneException("desc r"); 269 } 270 _p_data = new ubyte[](_size); 271 scope(failure) { 272 _p_data = null; 273 _size = 0; 274 } 275 desc.r(_p_data); 276 if (!_SetInformation()) { 277 throw new PxtoneException("_SetInformation"); 278 } 279 } 280 281 void pxtn_write(ref pxtnDescriptor p_doc) const @system { 282 if (!_p_data) { 283 throw new PxtoneException("No data"); 284 } 285 286 p_doc.w_asfile(_ch); 287 p_doc.w_asfile(_sps2); 288 p_doc.w_asfile(_smp_num); 289 p_doc.w_asfile(_size); 290 p_doc.w_asfile(_p_data); 291 } 292 293 void pxtn_read(ref pxtnDescriptor p_doc) @system { 294 p_doc.r(_ch); 295 p_doc.r(_sps2); 296 p_doc.r(_smp_num); 297 p_doc.r(_size); 298 299 if (!_size) { 300 throw new PxtoneException("Invalid size read"); 301 } 302 303 _p_data = new ubyte[](_size); 304 scope(failure) { 305 _p_data = null; 306 _size = 0; 307 } 308 p_doc.r(_p_data); 309 } 310 311 bool Copy(ref pxtnPulse_Oggv p_dst) const nothrow @system { 312 p_dst.Release(); 313 if (!_p_data) { 314 return true; 315 } 316 317 p_dst._p_data = new ubyte[](_size); 318 if (!(p_dst._p_data)) { 319 return false; 320 } 321 p_dst._p_data[0 .. _size] = _p_data[0 .. _size]; 322 323 p_dst._ch = _ch; 324 p_dst._sps2 = _sps2; 325 p_dst._size = _size; 326 p_dst._smp_num = _smp_num; 327 328 return true; 329 } 330 }