1 /// 2 module flod.etc.mad; 3 4 import flod.pipeline; 5 import flod.traits; 6 7 /// 8 auto decodeMp3(Pipeline)(auto ref Pipeline pipeline) 9 { 10 return pipeline.pipe!MadDecoder; 11 } 12 13 @filter!ubyte(Method.peek, Method.alloc) 14 private struct MadDecoder(alias Context, A...) { 15 import std.exception : enforce; 16 import deimos.mad; 17 static assert(mad_decoder.sizeof == 88); 18 19 mixin Context!A; 20 21 const(void)* bufferStart = null; 22 size_t chunkSize = 4096; 23 24 Throwable exception; 25 26 private mad_flow input(mad_stream* stream) 27 { 28 import core.exception : OutOfMemoryError, InvalidMemoryOperationError; 29 try { 30 size_t cons; 31 if (bufferStart !is null) { 32 cons = stream.this_frame - bufferStart; 33 if (cons > 0) { 34 source.consume(cons); 35 if (chunkSize > 4096) 36 chunkSize = 4096; 37 } 38 switch (stream.error) with (mad_error) { 39 case BUFLEN: 40 if (cons == 0) 41 chunkSize *= 2; 42 break; 43 case BUFPTR: 44 throw new InvalidMemoryOperationError("MAD error: invalid buffer pointer"); 45 case NOMEM: 46 throw new OutOfMemoryError("MAD error: out of memory"); 47 default: 48 break; 49 } 50 } 51 auto buf = source.peek(chunkSize); 52 if (cons == 0 && buf.length < chunkSize) 53 return mad_flow.STOP; 54 bufferStart = buf.ptr; 55 mad_stream_buffer(stream, buf.ptr, buf.length); 56 return mad_flow.CONTINUE; 57 } 58 catch (Throwable e) { 59 exception = e; 60 return mad_flow.BREAK; 61 } 62 } 63 64 private extern(C) 65 static mad_flow inputCallback(void* data, mad_stream* stream) 66 { 67 auto self = cast(MadDecoder*) data; 68 return self.input(stream); 69 } 70 71 private static int scale(mad_fixed_t sample) @nogc nothrow pure 72 { 73 /* round */ 74 sample += (1L << (MAD_F_FRACBITS - 16)); 75 76 /* clip */ 77 if (sample >= MAD_F_ONE) 78 sample = MAD_F_ONE - 1; 79 else if (sample < -MAD_F_ONE) 80 sample = -MAD_F_ONE; 81 82 /* quantize */ 83 return sample >> (MAD_F_FRACBITS + 1 - 16); 84 } 85 86 mad_flow output(const(mad_header)* header, mad_pcm* pcm) 87 { 88 try { 89 uint nchannels, nsamples; 90 const(mad_fixed_t)* left_ch; 91 const(mad_fixed_t)* right_ch; 92 93 nchannels = pcm.channels; 94 nsamples = pcm.length; 95 left_ch = pcm.samples[0].ptr; 96 right_ch = pcm.samples[1].ptr; 97 if (nsamples > 0) { 98 size_t size = short.sizeof * nchannels * nsamples; 99 ubyte[] buf; 100 enforce(sink.alloc(buf, size)); 101 size_t i = 0; 102 while (nsamples--) { 103 int sample = scale(*left_ch++); 104 buf[i++] = (sample >> 0) & 0xff; 105 buf[i++] = (sample >> 8) & 0xff; 106 if (nchannels == 2) { 107 sample = scale(*right_ch++); 108 buf[i++] = (sample >> 0) & 0xff; 109 buf[i++] = (sample >> 8) & 0xff; 110 } 111 } 112 sink.commit(size); 113 } 114 } catch (Throwable e) { 115 exception = e; 116 return mad_flow.BREAK; 117 } 118 return mad_flow.CONTINUE; 119 } 120 121 private extern(C) 122 static mad_flow outputCallback(void* data, const(mad_header)* header, mad_pcm* pcm) 123 { 124 auto self = cast(MadDecoder*) data; 125 return self.output(header, pcm); 126 } 127 128 private extern(C) 129 static mad_flow errorCallback(void* data, mad_stream* stream, mad_frame* frame) @nogc nothrow 130 { 131 return mad_flow.CONTINUE; 132 } 133 134 void run() 135 { 136 mad_decoder decoder; 137 mad_decoder_init(&decoder, cast(void*) &this, 138 &inputCallback, null /* header */, null /* filter */, 139 &outputCallback, &errorCallback, null /* message */); 140 assert(&this !is null); 141 142 /* start decoding */ 143 auto result = mad_decoder_run(&decoder, mad_decoder_mode.SYNC); 144 145 /* release the decoder */ 146 mad_decoder_finish(&decoder); 147 if (exception) 148 throw exception; 149 } 150 }