chiark / gitweb /
Consistency check for finished tracks.
[disorder] / server / decode-mp3.c
CommitLineData
8d399b30
RK
1/*
2 * This file is part of DisOrder
3 * Copyright (C) 2007-2010 Richard Kettlewell
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18/** @file server/decode-mp3.c
19 * @brief Decode MP3 files.
20 */
21#include "decode.h"
22#include <mad.h>
23
24static struct hreader input[1];
25
26/** @brief Dithering state
27 * Filched from mpg321, which credits it to Robert Leslie */
28struct audio_dither {
29 mad_fixed_t error[3];
30 mad_fixed_t random;
31};
32
33/** @brief 32-bit PRNG
34 * Filched from mpg321, which credits it to Robert Leslie */
35static inline unsigned long prng(unsigned long state)
36{
37 return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
38}
39
40/** @brief Generic linear sample quantize and dither routine
41 * Filched from mpg321, which credits it to Robert Leslie */
42static long audio_linear_dither(mad_fixed_t sample,
43 struct audio_dither *dither) {
44 unsigned int scalebits;
45 mad_fixed_t output, mask, rnd;
46 const int bits = 16;
47
48 enum {
49 MIN = -MAD_F_ONE,
50 MAX = MAD_F_ONE - 1
51 };
52
53 /* noise shape */
54 sample += dither->error[0] - dither->error[1] + dither->error[2];
55
56 dither->error[2] = dither->error[1];
57 dither->error[1] = dither->error[0] / 2;
58
59 /* bias */
60 output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1));
61
62 scalebits = MAD_F_FRACBITS + 1 - bits;
63 mask = (1L << scalebits) - 1;
64
65 /* dither */
66 rnd = prng(dither->random);
67 output += (rnd & mask) - (dither->random & mask);
68
69 dither->random = rnd;
70
71 /* clip */
72 if (output > MAX) {
73 output = MAX;
74
75 if (sample > MAX)
76 sample = MAX;
77 }
78 else if (output < MIN) {
79 output = MIN;
80
81 if (sample < MIN)
82 sample = MIN;
83 }
84
85 /* quantize */
86 output &= ~mask;
87
88 /* error feedback */
89 dither->error[0] = sample - output;
90
91 /* scale */
92 return output >> scalebits;
93}
94
95/** @brief MP3 output callback */
96static enum mad_flow mp3_output(void attribute((unused)) *data,
97 struct mad_header const *header,
98 struct mad_pcm *pcm) {
99 size_t n = pcm->length;
100 const mad_fixed_t *l = pcm->samples[0], *r = pcm->samples[1];
101 static struct audio_dither ld[1], rd[1];
102
103 output_header(header->samplerate,
104 pcm->channels,
105 16,
106 2 * pcm->channels * pcm->length,
107 ENDIAN_BIG);
108 switch(pcm->channels) {
109 case 1:
110 while(n--)
111 output_16(audio_linear_dither(*l++, ld));
112 break;
113 case 2:
114 while(n--) {
115 output_16(audio_linear_dither(*l++, ld));
116 output_16(audio_linear_dither(*r++, rd));
117 }
118 break;
119 }
120 return MAD_FLOW_CONTINUE;
121}
122
123/** @brief MP3 input callback */
124static enum mad_flow mp3_input(void attribute((unused)) *data,
125 struct mad_stream *stream) {
126 int used, remain, n;
127
128 /* libmad requires its caller to do ALL the buffering work, including coping
129 * with partial frames. Given that it appears to be completely undocumented
130 * you could perhaps be forgiven for not discovering this... */
131 if(input_count) {
132 /* Compute total number of bytes consumed */
133 used = (char *)stream->next_frame - input_buffer;
134 /* Compute number of bytes left to consume */
135 remain = input_count - used;
136 memmove(input_buffer, input_buffer + used, remain);
137 } else {
138 remain = 0;
139 }
140 /* Read new data */
141 n = hreader_read(input,
142 input_buffer + remain,
143 (sizeof input_buffer) - remain);
144 if(n < 0)
145 disorder_fatal(errno, "reading from %s", path);
146 /* Compute total number of bytes available */
147 input_count = remain + n;
148 if(input_count)
149 mad_stream_buffer(stream, (unsigned char *)input_buffer, input_count);
150 if(n)
151 return MAD_FLOW_CONTINUE;
152 else
153 return MAD_FLOW_STOP;
154}
155
156/** @brief MP3 error callback */
157static enum mad_flow mp3_error(void attribute((unused)) *data,
158 struct mad_stream *stream,
159 struct mad_frame attribute((unused)) *frame) {
160 if(0)
161 /* Just generates pointless verbosity l-( */
162 disorder_error(0, "decoding %s: %s (%#04x)",
163 path, mad_stream_errorstr(stream), stream->error);
164 return MAD_FLOW_CONTINUE;
165}
166
167/** @brief MP3 decoder */
168void decode_mp3(void) {
169 struct mad_decoder mad[1];
170
171 if(hreader_init(path, input))
172 disorder_fatal(errno, "opening %s", path);
173 mad_decoder_init(mad, 0/*data*/, mp3_input, 0/*header*/, 0/*filter*/,
174 mp3_output, mp3_error, 0/*message*/);
175 if(mad_decoder_run(mad, MAD_DECODER_MODE_SYNC))
176 exit(1);
177 mad_decoder_finish(mad);
178}
179
180/*
181Local Variables:
182c-basic-offset:2
183comment-column:40
184fill-column:79
185indent-tabs-mode:nil
186End:
187*/