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 }