~ubuntu-branches/ubuntu/utopic/mpd/utopic-proposed

« back to all changes in this revision

Viewing changes to src/encoder/FlacEncoderPlugin.cxx

  • Committer: Package Import Robot
  • Author(s): Steve Kowalik
  • Date: 2013-11-12 18:17:40 UTC
  • mfrom: (2.2.36 sid)
  • Revision ID: package-import@ubuntu.com-20131112181740-72aa4zihehoobedp
Tags: 0.18.3-1ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - Add libmp3lame-dev to Build-Depends, and enable LAME.
  - Read the user for the daemon from the config file in the init script.
  - Move avahi-daemon from Suggests to Recommends.
  - Added apport hook to include user configuration file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2003-2013 The Music Player Daemon Project
 
3
 * http://www.musicpd.org
 
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 2 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 along
 
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
 
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
18
 */
 
19
 
 
20
#include "config.h"
 
21
#include "FlacEncoderPlugin.hxx"
 
22
#include "EncoderAPI.hxx"
 
23
#include "AudioFormat.hxx"
 
24
#include "pcm/PcmBuffer.hxx"
 
25
#include "ConfigError.hxx"
 
26
#include "util/Error.hxx"
 
27
#include "util/Domain.hxx"
 
28
#include "util/fifo_buffer.h"
 
29
 
 
30
extern "C" {
 
31
#include "util/growing_fifo.h"
 
32
}
 
33
 
 
34
#include <assert.h>
 
35
#include <string.h>
 
36
 
 
37
#include <FLAC/stream_encoder.h>
 
38
 
 
39
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
 
40
#error libFLAC is too old
 
41
#endif
 
42
 
 
43
struct flac_encoder {
 
44
        Encoder encoder;
 
45
 
 
46
        AudioFormat audio_format;
 
47
        unsigned compression;
 
48
 
 
49
        FLAC__StreamEncoder *fse;
 
50
 
 
51
        PcmBuffer expand_buffer;
 
52
 
 
53
        /**
 
54
         * This buffer will hold encoded data from libFLAC until it is
 
55
         * picked up with flac_encoder_read().
 
56
         */
 
57
        struct fifo_buffer *output_buffer;
 
58
 
 
59
        flac_encoder():encoder(flac_encoder_plugin) {}
 
60
};
 
61
 
 
62
static constexpr Domain flac_encoder_domain("vorbis_encoder");
 
63
 
 
64
static bool
 
65
flac_encoder_configure(struct flac_encoder *encoder, const config_param &param,
 
66
                       gcc_unused Error &error)
 
67
{
 
68
        encoder->compression = param.GetBlockValue("compression", 5u);
 
69
 
 
70
        return true;
 
71
}
 
72
 
 
73
static Encoder *
 
74
flac_encoder_init(const config_param &param, Error &error)
 
75
{
 
76
        flac_encoder *encoder = new flac_encoder();
 
77
 
 
78
        /* load configuration from "param" */
 
79
        if (!flac_encoder_configure(encoder, param, error)) {
 
80
                /* configuration has failed, roll back and return error */
 
81
                delete encoder;
 
82
                return nullptr;
 
83
        }
 
84
 
 
85
        return &encoder->encoder;
 
86
}
 
87
 
 
88
static void
 
89
flac_encoder_finish(Encoder *_encoder)
 
90
{
 
91
        struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
 
92
 
 
93
        /* the real libFLAC cleanup was already performed by
 
94
           flac_encoder_close(), so no real work here */
 
95
        delete encoder;
 
96
}
 
97
 
 
98
static bool
 
99
flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
 
100
                   Error &error)
 
101
{
 
102
        if ( !FLAC__stream_encoder_set_compression_level(encoder->fse,
 
103
                                        encoder->compression)) {
 
104
                error.Format(config_domain,
 
105
                             "error setting flac compression to %d",
 
106
                             encoder->compression);
 
107
                return false;
 
108
        }
 
109
 
 
110
        if ( !FLAC__stream_encoder_set_channels(encoder->fse,
 
111
                                        encoder->audio_format.channels)) {
 
112
                error.Format(config_domain,
 
113
                             "error setting flac channels num to %d",
 
114
                             encoder->audio_format.channels);
 
115
                return false;
 
116
        }
 
117
        if ( !FLAC__stream_encoder_set_bits_per_sample(encoder->fse,
 
118
                                                        bits_per_sample)) {
 
119
                error.Format(config_domain,
 
120
                             "error setting flac bit format to %d",
 
121
                             bits_per_sample);
 
122
                return false;
 
123
        }
 
124
        if ( !FLAC__stream_encoder_set_sample_rate(encoder->fse,
 
125
                                        encoder->audio_format.sample_rate)) {
 
126
                error.Format(config_domain,
 
127
                             "error setting flac sample rate to %d",
 
128
                             encoder->audio_format.sample_rate);
 
129
                return false;
 
130
        }
 
131
        return true;
 
132
}
 
133
 
 
134
static FLAC__StreamEncoderWriteStatus
 
135
flac_write_callback(gcc_unused const FLAC__StreamEncoder *fse,
 
136
                    const FLAC__byte data[],
 
137
                    size_t bytes,
 
138
                    gcc_unused unsigned samples,
 
139
                    gcc_unused unsigned current_frame, void *client_data)
 
140
{
 
141
        struct flac_encoder *encoder = (struct flac_encoder *) client_data;
 
142
 
 
143
        //transfer data to buffer
 
144
        growing_fifo_append(&encoder->output_buffer, data, bytes);
 
145
 
 
146
        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
 
147
}
 
148
 
 
149
static void
 
150
flac_encoder_close(Encoder *_encoder)
 
151
{
 
152
        struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
 
153
 
 
154
        FLAC__stream_encoder_delete(encoder->fse);
 
155
 
 
156
        encoder->expand_buffer.Clear();
 
157
        fifo_buffer_free(encoder->output_buffer);
 
158
}
 
159
 
 
160
static bool
 
161
flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
 
162
{
 
163
        struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
 
164
        unsigned bits_per_sample;
 
165
 
 
166
        encoder->audio_format = audio_format;
 
167
 
 
168
        /* FIXME: flac should support 32bit as well */
 
169
        switch (audio_format.format) {
 
170
        case SampleFormat::S8:
 
171
                bits_per_sample = 8;
 
172
                break;
 
173
 
 
174
        case SampleFormat::S16:
 
175
                bits_per_sample = 16;
 
176
                break;
 
177
 
 
178
        case SampleFormat::S24_P32:
 
179
                bits_per_sample = 24;
 
180
                break;
 
181
 
 
182
        default:
 
183
                bits_per_sample = 24;
 
184
                audio_format.format = SampleFormat::S24_P32;
 
185
        }
 
186
 
 
187
        /* allocate the encoder */
 
188
        encoder->fse = FLAC__stream_encoder_new();
 
189
        if (encoder->fse == nullptr) {
 
190
                error.Set(flac_encoder_domain, "flac_new() failed");
 
191
                return false;
 
192
        }
 
193
 
 
194
        if (!flac_encoder_setup(encoder, bits_per_sample, error)) {
 
195
                FLAC__stream_encoder_delete(encoder->fse);
 
196
                return false;
 
197
        }
 
198
 
 
199
        encoder->output_buffer = growing_fifo_new();
 
200
 
 
201
        /* this immediately outputs data through callback */
 
202
 
 
203
        {
 
204
                FLAC__StreamEncoderInitStatus init_status;
 
205
 
 
206
                init_status = FLAC__stream_encoder_init_stream(encoder->fse,
 
207
                            flac_write_callback,
 
208
                            nullptr, nullptr, nullptr, encoder);
 
209
 
 
210
                if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
 
211
                        error.Format(flac_encoder_domain,
 
212
                                     "failed to initialize encoder: %s\n",
 
213
                                     FLAC__StreamEncoderInitStatusString[init_status]);
 
214
                        flac_encoder_close(_encoder);
 
215
                        return false;
 
216
                }
 
217
        }
 
218
 
 
219
        return true;
 
220
}
 
221
 
 
222
 
 
223
static bool
 
224
flac_encoder_flush(Encoder *_encoder, gcc_unused Error &error)
 
225
{
 
226
        struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
 
227
 
 
228
        (void) FLAC__stream_encoder_finish(encoder->fse);
 
229
        return true;
 
230
}
 
231
 
 
232
static inline void
 
233
pcm8_to_flac(int32_t *out, const int8_t *in, unsigned num_samples)
 
234
{
 
235
        while (num_samples > 0) {
 
236
                *out++ = *in++;
 
237
                --num_samples;
 
238
        }
 
239
}
 
240
 
 
241
static inline void
 
242
pcm16_to_flac(int32_t *out, const int16_t *in, unsigned num_samples)
 
243
{
 
244
        while (num_samples > 0) {
 
245
                *out++ = *in++;
 
246
                --num_samples;
 
247
        }
 
248
}
 
249
 
 
250
static bool
 
251
flac_encoder_write(Encoder *_encoder,
 
252
                   const void *data, size_t length,
 
253
                   gcc_unused Error &error)
 
254
{
 
255
        struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
 
256
        unsigned num_frames, num_samples;
 
257
        void *exbuffer;
 
258
        const void *buffer = nullptr;
 
259
 
 
260
        /* format conversion */
 
261
 
 
262
        num_frames = length / encoder->audio_format.GetFrameSize();
 
263
        num_samples = num_frames * encoder->audio_format.channels;
 
264
 
 
265
        switch (encoder->audio_format.format) {
 
266
        case SampleFormat::S8:
 
267
                exbuffer = encoder->expand_buffer.Get(length * 4);
 
268
                pcm8_to_flac((int32_t *)exbuffer, (const int8_t *)data,
 
269
                             num_samples);
 
270
                buffer = exbuffer;
 
271
                break;
 
272
 
 
273
        case SampleFormat::S16:
 
274
                exbuffer = encoder->expand_buffer.Get(length * 2);
 
275
                pcm16_to_flac((int32_t *)exbuffer, (const int16_t *)data,
 
276
                              num_samples);
 
277
                buffer = exbuffer;
 
278
                break;
 
279
 
 
280
        case SampleFormat::S24_P32:
 
281
        case SampleFormat::S32:
 
282
                /* nothing need to be done; format is the same for
 
283
                   both mpd and libFLAC */
 
284
                buffer = data;
 
285
                break;
 
286
 
 
287
        default:
 
288
                gcc_unreachable();
 
289
        }
 
290
 
 
291
        /* feed samples to encoder */
 
292
 
 
293
        if (!FLAC__stream_encoder_process_interleaved(encoder->fse,
 
294
                                                      (const FLAC__int32 *)buffer,
 
295
                                                      num_frames)) {
 
296
                error.Set(flac_encoder_domain, "flac encoder process failed");
 
297
                return false;
 
298
        }
 
299
 
 
300
        return true;
 
301
}
 
302
 
 
303
static size_t
 
304
flac_encoder_read(Encoder *_encoder, void *dest, size_t length)
 
305
{
 
306
        struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
 
307
 
 
308
        size_t max_length;
 
309
        const char *src = (const char *)
 
310
                fifo_buffer_read(encoder->output_buffer, &max_length);
 
311
        if (src == nullptr)
 
312
                return 0;
 
313
 
 
314
        if (length > max_length)
 
315
                length = max_length;
 
316
 
 
317
        memcpy(dest, src, length);
 
318
        fifo_buffer_consume(encoder->output_buffer, length);
 
319
        return length;
 
320
}
 
321
 
 
322
static const char *
 
323
flac_encoder_get_mime_type(gcc_unused Encoder *_encoder)
 
324
{
 
325
        return "audio/flac";
 
326
}
 
327
 
 
328
const EncoderPlugin flac_encoder_plugin = {
 
329
        "flac",
 
330
        flac_encoder_init,
 
331
        flac_encoder_finish,
 
332
        flac_encoder_open,
 
333
        flac_encoder_close,
 
334
        flac_encoder_flush,
 
335
        flac_encoder_flush,
 
336
        nullptr,
 
337
        nullptr,
 
338
        flac_encoder_write,
 
339
        flac_encoder_read,
 
340
        flac_encoder_get_mime_type,
 
341
};
 
342