Merge lp:~mixxxdevelopers/mixxx/features_flac into lp:~mixxxdevelopers/mixxx/trunk

Proposed by William Good
Status: Merged
Merged at revision: 2548
Proposed branch: lp:~mixxxdevelopers/mixxx/features_flac
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 523 lines (+477/-1)
4 files modified
mixxx/build/depends.py (+13/-1)
mixxx/src/soundsourceflac.cpp (+374/-0)
mixxx/src/soundsourceflac.h (+87/-0)
mixxx/src/soundsourceproxy.cpp (+3/-0)
To merge this branch: bzr merge lp:~mixxxdevelopers/mixxx/features_flac
Reviewer Review Type Date Requested Status
Albert Santoni Needs Fixing
Review via email: mp+31018@code.launchpad.net

Description of the change

Adds a new SoundSource using libFLAC. Written to replace Mixxx's use of libsndfile for FLAC due to the bugs linked to this branch.

To post a comment you must log in.
2416. By William Good

Changed qDebugs to qWarnings as necessary and broke up some way-long lines.

2417. By William Good

Fixed memory allocation bug (was allocating based on number of channels when
    it really uses 2 samples per frame no matter the number of channels).

Revision history for this message
William Good (bkgood) wrote :

Tested and works with all the FLAC files generated by the file format script in src/test.

2418. By William Good

Marking a todo (code cleanup before taking a diff for gsoc)

2419. By William Good

Merging with trunk

2420. By William Good

Merging from lp:mixxx

Revision history for this message
Albert Santoni (gamegod) wrote :

+ setBitrate(m_iSampleRate * 16 * m_iChannels / 1000); // 16 = bps

- Should that be 1024 instead of 1000? Hardcoded bit depth too. Where do you tell FLAC that you want 16-bit samples? Can you make this a constant at least?

+inline int SoundSourceFLAC::getShift() const {
- same hardcoded 16 constant?

- How do you deal with 24-bit FLAC files?

parseHeader():
- This is some undocumented bad news: parseHeader() has to be re-entrant. You can't use the existing file handle, because it can get called after an open() on an existing SoundSource object. This is the most important thing to fix.

review: Needs Fixing
Revision history for this message
William Good (bkgood) wrote :

> + setBitrate(m_iSampleRate * 16 * m_iChannels / 1000); // 16 = bps
>
> - Should that be 1024 instead of 1000? Hardcoded bit depth too. Where do you
> tell FLAC that you want 16-bit samples? Can you make this a constant at least?
>
I believe it should be 1000 as setBitrate is supposed to get its argument in
kbits/s (according to the wiki SoundSource page). Fixed the hardcoded bps
thing, that was left over from when I only did 16-bit. FLAC gives me whatever
size samples are in the file, then I shift them to 16-bit since that's what
Mixxx wants.

SSSndfile uses setBitrate(samplerate*32/1000).

> +inline int SoundSourceFLAC::getShift() const {
> - same hardcoded 16 constant?
>
> - How do you deal with 24-bit FLAC files?
>
The sample value is shifted so that the sample fits in a 16-bit short.
getShift gives the number of places needed to shift the sample in order
to make the sample fit in 16 bits (so 24-bit samples are shifted right
abs(16-bps) times and <16-bit samples are shifted left 16-bps times),
which is the meaning of the constant (doxygen-style comment above the
method kinda explains this). shift() is then called on every sample to
normalize it to 16-bits using a shift amount/direction from getShift.
See ::flacWrite().

>
> parseHeader():
> - This is some undocumented bad news: parseHeader() has to be re-entrant. You
> can't use the existing file handle, because it can get called after an open()
> on an existing SoundSource object. This is the most important thing to fix.
I believe this is now fixed.

2421. By William Good

Made SSFLAC::parseHeader re-entrant. Commented/fixed some magic numbers.

2422. By William Good

Silenced some debugs

2423. By William Good

Merged from lp:mixxx

2424. By William Good

Switched SSFLAC to using TagLib for metadata parsing

2425. By William Good

Copied FLAC filename to QByteArray before passing to TagLib for safety

2426. By William Good

Merged from lp:mixxx

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/build/depends.py'
--- mixxx/build/depends.py 2010-11-02 19:50:15 +0000
+++ mixxx/build/depends.py 2010-11-14 02:21:19 +0000
@@ -100,6 +100,18 @@
100 def sources(self, build):100 def sources(self, build):
101 return ['soundsourcesndfile.cpp']101 return ['soundsourcesndfile.cpp']
102102
103class FLAC(Dependence):
104 def configure(self, build, conf):
105 if not conf.CheckHeader('FLAC/stream_decoder.h'):
106 raise Exception('Did not find libFLAC development headers, exiting!')
107 elif not conf.CheckLib(['libFLAC', 'FLAC']):
108 raise Exception('Did not find libFLAC development libraries, exiting!')
109 return
110
111 def sources(self, build):
112 return ['soundsourceflac.cpp',]
113
114
103class Qt(Dependence):115class Qt(Dependence):
104 DEFAULT_QTDIRS = {'linux': '/usr/share/qt4',116 DEFAULT_QTDIRS = {'linux': '/usr/share/qt4',
105 'bsd': '/usr/local/lib/qt4',117 'bsd': '/usr/local/lib/qt4',
@@ -681,7 +693,7 @@
681693
682 def depends(self, build):694 def depends(self, build):
683 return [SoundTouch, KissFFT, PortAudio, PortMIDI, Qt,695 return [SoundTouch, KissFFT, PortAudio, PortMIDI, Qt,
684 FidLib, Mad, SndFile, OggVorbis, OpenGL, TagLib]696 FidLib, Mad, SndFile, FLAC, OggVorbis, OpenGL, TagLib]
685697
686 def post_dependency_check_configure(self, build, conf):698 def post_dependency_check_configure(self, build, conf):
687 """Sets up additional things in the Environment that must happen699 """Sets up additional things in the Environment that must happen
688700
=== added file 'mixxx/src/soundsourceflac.cpp'
--- mixxx/src/soundsourceflac.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/soundsourceflac.cpp 2010-11-14 02:21:19 +0000
@@ -0,0 +1,374 @@
1/**
2 * \file soundsourceflac.cpp
3 * \author Bill Good <bkgood at gmail dot com>
4 * \date May 22, 2010
5 */
6
7/***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include <cstring> // memcpy
17#include <QtDebug>
18#include <taglib/flacfile.h>
19
20#include "soundsourceflac.h"
21
22SoundSourceFLAC::SoundSourceFLAC(QString filename)
23 : SoundSource(filename)
24 , m_file(filename)
25 , m_decoder(NULL)
26 , m_samples(0)
27 , m_bps(0)
28 , m_flacBuffer(NULL)
29 , m_flacBufferLength(0)
30 , m_leftoverBuffer(NULL)
31 , m_leftoverBufferLength(0) {
32}
33
34SoundSourceFLAC::~SoundSourceFLAC() {
35 if (m_flacBuffer != NULL) {
36 delete [] m_flacBuffer;
37 m_flacBuffer = NULL;
38 }
39 if (m_leftoverBuffer != NULL) {
40 delete [] m_leftoverBuffer;
41 m_leftoverBuffer = NULL;
42 }
43 if (m_decoder) {
44 FLAC__stream_decoder_finish(m_decoder);
45 FLAC__stream_decoder_delete(m_decoder); // frees memory
46 m_decoder = NULL; // probably not necessary
47 }
48}
49
50// soundsource overrides
51int SoundSourceFLAC::open() {
52 m_file.open(QIODevice::ReadOnly);
53 m_decoder = FLAC__stream_decoder_new();
54 if (m_decoder == NULL) {
55 qWarning() << "SSFLAC: decoder allocation failed!";
56 return ERR;
57 }
58 if (!FLAC__stream_decoder_set_metadata_respond(m_decoder,
59 FLAC__METADATA_TYPE_VORBIS_COMMENT)) {
60 qWarning() << "SSFLAC: set metadata respond to vorbis comments failed";
61 goto decoderError;
62 }
63 FLAC__StreamDecoderInitStatus initStatus;
64 initStatus = FLAC__stream_decoder_init_stream(
65 m_decoder, FLAC_read_cb, FLAC_seek_cb, FLAC_tell_cb, FLAC_length_cb,
66 FLAC_eof_cb, FLAC_write_cb, FLAC_metadata_cb, FLAC_error_cb,
67 (void*) this);
68 if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
69 qWarning() << "SSFLAC: decoder init failed!";
70 goto decoderError;
71 }
72 if (!FLAC__stream_decoder_process_until_end_of_metadata(m_decoder)) {
73 qWarning() << "SSFLAC: process to end of meta failed!";
74 qWarning() << "SSFLAC: decoder state: " << FLAC__stream_decoder_get_state(m_decoder);
75 goto decoderError;
76 } // now number of samples etc. should be populated
77 if (m_flacBuffer == NULL) {
78 // we want 2 samples per frame, see ::flacWrite code -- bkgood
79 m_flacBuffer = new FLAC__int16[m_maxBlocksize * 2 /*m_iChannels*/];
80 }
81 if (m_leftoverBuffer == NULL) {
82 m_leftoverBuffer = new FLAC__int16[m_maxBlocksize * 2 /*m_iChannels*/];
83 }
84// qDebug() << "SSFLAC: Total samples: " << m_samples;
85// qDebug() << "SSFLAC: Sampling rate: " << m_iSampleRate << " Hz";
86// qDebug() << "SSFLAC: Channels: " << m_iChannels;
87// qDebug() << "SSFLAC: BPS: " << m_bps;
88 return OK;
89decoderError:
90 FLAC__stream_decoder_finish(m_decoder);
91 FLAC__stream_decoder_delete(m_decoder);
92 m_decoder = NULL;
93 return ERR;
94}
95
96long SoundSourceFLAC::seek(long filepos) {
97 if (!m_decoder) return 0;
98 FLAC__bool seekResult;
99 // important division here, filepos is in audio samples (i.e. shorts)
100 // but libflac expects a number in time samples. I _think_ this should
101 // be hard-coded at two because *2 is the assumption the caller makes
102 // -- bkgood
103 seekResult = FLAC__stream_decoder_seek_absolute(m_decoder, filepos / 2);
104 m_leftoverBufferLength = 0; // clear internal buffer since we moved
105 return filepos;
106}
107
108unsigned int SoundSourceFLAC::read(unsigned long size, const SAMPLE *destination) {
109 if (!m_decoder) return 0;
110 SAMPLE *destBuffer = const_cast<SAMPLE*>(destination);
111 unsigned int samplesWritten = 0;
112 unsigned int i = 0;
113 while (samplesWritten < size) {
114 // if our buffer from libflac is empty (either because we explicitly cleared
115 // it or because we've simply used all the samples), ask for a new buffer
116 if (m_flacBufferLength == 0) {
117 i = 0;
118 if (!FLAC__stream_decoder_process_single(m_decoder)) {
119 qWarning() << "SSFLAC: decoder_process_single returned false";
120 break;
121 } else if (m_flacBufferLength == 0) {
122 // EOF
123 break;
124 }
125 }
126 destBuffer[samplesWritten++] = m_flacBuffer[i++];
127 --m_flacBufferLength;
128 }
129 if (m_flacBufferLength != 0) {
130 memcpy(m_leftoverBuffer, &m_flacBuffer[i],
131 m_flacBufferLength * sizeof(m_flacBuffer[0])); // safe because leftoverBuffer
132 // is as long as flacbuffer
133 memcpy(m_flacBuffer, m_leftoverBuffer,
134 m_flacBufferLength * sizeof(m_leftoverBuffer[0]));
135 // this whole if block could go away if this just used a ring buffer but I'd
136 // rather do that after I've gotten off the inital happiness of getting this right,
137 // if I see SIGSEGV one more time I'll pop -- bkgood
138 }
139 return samplesWritten;
140}
141
142inline unsigned long SoundSourceFLAC::length() {
143 return m_samples * m_iChannels;
144}
145
146int SoundSourceFLAC::parseHeader() {
147 setType("flac");
148 QByteArray fileName(m_file.fileName().toUtf8());
149 TagLib::FLAC::File f(fileName.constData());
150 bool result = processTaglibFile(f);
151 TagLib::ID3v2::Tag *id3v2 = f.ID3v2Tag();
152 TagLib::Ogg::XiphComment *xiph = f.xiphComment();
153 if (id3v2) {
154 processID3v2Tag(id3v2);
155 }
156 if (xiph) {
157 processXiphComment(xiph);
158 }
159 return result ? OK : ERR;
160}
161
162void SoundSourceFLAC::setTag(const QString &tag) {
163 QString key = tag.left(tag.indexOf("=")).toUpper();
164 QString value = tag.right(tag.length() - tag.indexOf("=") - 1);
165 // standard here: http://www.xiph.org/vorbis/doc/v-comment.html
166 if (key == "ARTIST") {
167 m_sArtist = value;
168 } else if (key == "TITLE") {
169 m_sTitle = value;
170 } else if (key == "ALBUM") {
171 m_sAlbum = value;
172 } else if (key == "COMMENT") { // this doesn't exist in standard vorbis comments
173 m_sComment = value;
174 } else if (key == "DATE") {
175 m_sYear = value;
176 } else if (key == "GENRE") {
177 m_sGenre = value;
178 } else if (key == "TRACKNUMBER") {
179 m_sTrackNumber = value;
180 } else if (key == "BPM") { // this doesn't exist in standard vorbis comments
181 m_fBPM = value.toFloat();
182 }
183}
184
185/**
186 * Shift needed to take our FLAC sample size to Mixxx's 16-bit samples.
187 * Shift right on negative, left on positive.
188 */
189inline int SoundSourceFLAC::getShift() const {
190 return 16 - m_bps;
191}
192
193/**
194 * Shift a sample from FLAC as necessary to get a 16-bit value.
195 */
196inline FLAC__int16 SoundSourceFLAC::shift(FLAC__int32 sample) const {
197 // this is how libsndfile does this operation and is wonderfully
198 // straightforward. Just shift the sample left or right so that
199 // it fits in a 16-bit short. -- bkgood
200 int shift = getShift();
201 if (shift == 0) {
202 return sample;
203 } else if (shift < 0) {
204 return sample >> abs(shift);
205 } else {
206 return sample << shift;
207 }
208};
209
210// static
211QList<QString> SoundSourceFLAC::supportedFileExtensions() {
212 QList<QString> list;
213 list.push_back("flac");
214 return list;
215}
216
217
218// flac callback methods
219FLAC__StreamDecoderReadStatus SoundSourceFLAC::flacRead(FLAC__byte buffer[], size_t *bytes) {
220 *bytes = m_file.read((char*) buffer, *bytes);
221 if (*bytes > 0) {
222 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
223 } else if (*bytes == 0) {
224 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
225 } else {
226 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
227 }
228}
229
230FLAC__StreamDecoderSeekStatus SoundSourceFLAC::flacSeek(FLAC__uint64 offset) {
231 if (m_file.seek(offset)) {
232 return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
233 } else {
234 return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
235 }
236}
237
238FLAC__StreamDecoderTellStatus SoundSourceFLAC::flacTell(FLAC__uint64 *offset) {
239 if (m_file.isSequential()) {
240 return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
241 }
242 *offset = m_file.pos();
243 return FLAC__STREAM_DECODER_TELL_STATUS_OK;
244}
245
246FLAC__StreamDecoderLengthStatus SoundSourceFLAC::flacLength(FLAC__uint64 *length) {
247 if (m_file.isSequential()) {
248 return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
249 }
250 *length = m_file.size();
251 return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
252}
253
254FLAC__bool SoundSourceFLAC::flacEOF() {
255 if (m_file.isSequential()) {
256 return false;
257 }
258 return m_file.atEnd();
259}
260
261FLAC__StreamDecoderWriteStatus SoundSourceFLAC::flacWrite(const FLAC__Frame *frame,
262 const FLAC__int32 *const buffer[]) {
263 unsigned int i;
264 m_flacBufferLength = 0;
265 if (frame->header.channels > 1) {
266 // stereo (or greater)
267 for (i = 0; i < frame->header.blocksize; ++i) {
268 m_flacBuffer[m_flacBufferLength++] = shift(buffer[0][i]); // left channel
269 m_flacBuffer[m_flacBufferLength++] = shift(buffer[1][i]); // right channel
270 }
271 } else {
272 // mono
273 for (i = 0; i < frame->header.blocksize; ++i) {
274 m_flacBuffer[m_flacBufferLength++] = shift(buffer[0][i]); // left channel
275 m_flacBuffer[m_flacBufferLength++] = shift(buffer[0][i]); // mono channel
276 }
277 }
278 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; // can't anticipate any errors here
279}
280
281void SoundSourceFLAC::flacMetadata(const FLAC__StreamMetadata *metadata) {
282 switch (metadata->type) {
283 case FLAC__METADATA_TYPE_STREAMINFO:
284 m_samples = metadata->data.stream_info.total_samples;
285 m_iChannels = metadata->data.stream_info.channels;
286 m_iSampleRate = metadata->data.stream_info.sample_rate;
287 m_bps = metadata->data.stream_info.bits_per_sample;
288 m_minBlocksize = metadata->data.stream_info.min_blocksize;
289 m_maxBlocksize = metadata->data.stream_info.max_blocksize;
290 m_minFramesize = metadata->data.stream_info.min_framesize;
291 m_maxFramesize = metadata->data.stream_info.max_framesize;
292// qDebug() << "FLAC file " << m_qFilename;
293// qDebug() << m_iChannels << " @ " << m_iSampleRate << " Hz, " << m_samples
294// << " total, " << m_bps << " bps";
295// qDebug() << "Blocksize in [" << m_minBlocksize << ", " << m_maxBlocksize
296// << "], Framesize in [" << m_minFramesize << ", " << m_maxFramesize << "]";
297 break;
298 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
299 for (unsigned int i = 0; i < metadata->data.vorbis_comment.num_comments; ++i) {
300 m_tags.append(QString::fromUtf8(
301 (const char*) metadata->data.vorbis_comment.comments[i].entry,
302 metadata->data.vorbis_comment.comments[i].length));
303 }
304 break;
305 default:
306 // don't care, and libflac won't send us any others anyway...
307 break;
308 }
309}
310
311void SoundSourceFLAC::flacError(FLAC__StreamDecoderErrorStatus status) {
312 QString error;
313 // not much can be done at this point -- luckly the decoder seems to be
314 // pretty forgiving -- bkgood
315 switch (status) {
316 case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
317 error = "STREAM_DECODER_ERROR_STATUS_LOST_SYNC";
318 break;
319 case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
320 error = "STREAM_DECODER_ERROR_STATUS_BAD_HEADER";
321 break;
322 case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
323 error = "STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH";
324 break;
325 case FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM:
326 error = "STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM";
327 break;
328 }
329 qWarning() << "SSFLAC got error" << error << "from libFLAC for file"
330 << m_file.fileName();
331 // not much else to do here... whatever function that initiated whatever
332 // decoder method resulted in this error will return an error, and the caller
333 // will bail. libFLAC docs say to not close the decoder here -- bkgood
334}
335
336// begin callbacks (have to be regular functions because normal libFLAC isn't C++-aware)
337
338FLAC__StreamDecoderReadStatus FLAC_read_cb(const FLAC__StreamDecoder*, FLAC__byte buffer[],
339 size_t *bytes, void *client_data) {
340 return ((SoundSourceFLAC*) client_data)->flacRead(buffer, bytes);
341}
342
343FLAC__StreamDecoderSeekStatus FLAC_seek_cb(const FLAC__StreamDecoder*,
344 FLAC__uint64 absolute_byte_offset, void *client_data) {
345 return ((SoundSourceFLAC*) client_data)->flacSeek(absolute_byte_offset);
346}
347
348FLAC__StreamDecoderTellStatus FLAC_tell_cb(const FLAC__StreamDecoder*,
349 FLAC__uint64 *absolute_byte_offset, void *client_data) {
350 return ((SoundSourceFLAC*) client_data)->flacTell(absolute_byte_offset);
351}
352
353FLAC__StreamDecoderLengthStatus FLAC_length_cb(const FLAC__StreamDecoder*,
354 FLAC__uint64 *stream_length, void *client_data) {
355 return ((SoundSourceFLAC*) client_data)->flacLength(stream_length);
356}
357
358FLAC__bool FLAC_eof_cb(const FLAC__StreamDecoder*, void *client_data) {
359 return ((SoundSourceFLAC*) client_data)->flacEOF();
360}
361
362FLAC__StreamDecoderWriteStatus FLAC_write_cb(const FLAC__StreamDecoder*, const FLAC__Frame *frame,
363 const FLAC__int32 *const buffer[], void *client_data) {
364 return ((SoundSourceFLAC*) client_data)->flacWrite(frame, buffer);
365}
366
367void FLAC_metadata_cb(const FLAC__StreamDecoder*, const FLAC__StreamMetadata *metadata, void *client_data) {
368 ((SoundSourceFLAC*) client_data)->flacMetadata(metadata);
369}
370
371void FLAC_error_cb(const FLAC__StreamDecoder*, FLAC__StreamDecoderErrorStatus status, void *client_data) {
372 ((SoundSourceFLAC*) client_data)->flacError(status);
373}
374// end callbacks
0375
=== added file 'mixxx/src/soundsourceflac.h'
--- mixxx/src/soundsourceflac.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/soundsourceflac.h 2010-11-14 02:21:19 +0000
@@ -0,0 +1,87 @@
1/**
2 * \file sourdsourceflac.h
3 * \class SoundSourceFLAC
4 * \brief Decodes FLAC files using libFLAC for Mixxx.
5 * \author Bill Good <bkgood at gmail dot com>
6 * \date May 22, 2010
7 */
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#ifndef SOUNDSOURCEFLAC_H
19#define SOUNDSOURCEFLAC_H
20
21#include <QFile>
22#include <QString>
23#include <FLAC/stream_decoder.h>
24
25#include "defs.h"
26#include "soundsource.h"
27
28class TrackInfoObject;
29
30class SoundSourceFLAC : public SoundSource {
31public:
32 SoundSourceFLAC(QString filename);
33 ~SoundSourceFLAC();
34 int open();
35 long seek(long filepos);
36 unsigned read(unsigned long size, const SAMPLE *buffer);
37 inline long unsigned length();
38 int parseHeader();
39 static QList<QString> supportedFileExtensions();
40 // callback methods
41 FLAC__StreamDecoderReadStatus flacRead(FLAC__byte buffer[], size_t *bytes);
42 FLAC__StreamDecoderSeekStatus flacSeek(FLAC__uint64 offset);
43 FLAC__StreamDecoderTellStatus flacTell(FLAC__uint64 *offset);
44 FLAC__StreamDecoderLengthStatus flacLength(FLAC__uint64 *length);
45 FLAC__bool flacEOF();
46 FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__Frame *frame, const FLAC__int32 *const buffer[]);
47 void flacMetadata(const FLAC__StreamMetadata *metadata);
48 void flacError(FLAC__StreamDecoderErrorStatus status);
49private:
50 void setTag(const QString &tag);
51 // these next two are inline but are defined in the cpp file because
52 // they should only be used there -- bkgood
53 inline int getShift() const;
54 inline FLAC__int16 shift(const FLAC__int32 sample) const;
55 QFile m_file;
56 FLAC__StreamDecoder *m_decoder;
57 FLAC__StreamMetadata_StreamInfo *m_streamInfo;
58 unsigned int m_samples; // total number of samples
59 unsigned int m_bps; // bits per sample
60 // misc bits about the flac format:
61 // flac encodes from and decodes to LPCM in blocks, each block is made up of
62 // subblocks (one for each chan)
63 // flac stores in 'frames', each of which has a header and a certain number
64 // of subframes (one for each channel)
65 unsigned int m_minBlocksize; // in time samples (audio samples = time samples * chanCount)
66 unsigned int m_maxBlocksize;
67 unsigned int m_minFramesize;
68 unsigned int m_maxFramesize;
69 FLAC__int16 *m_flacBuffer; // buffer for the write callback to write a single frame's samples
70 unsigned int m_flacBufferLength;
71 FLAC__int16 *m_leftoverBuffer; // buffer to place any samples which haven't been used
72 // at the end of a read call
73 unsigned int m_leftoverBufferLength;
74 QList<QString> m_tags; // stored in vorbis comment format as received, ex. "ARTIST=blah"
75};
76
77// callbacks for libFLAC
78FLAC__StreamDecoderReadStatus FLAC_read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
79FLAC__StreamDecoderSeekStatus FLAC_seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data);
80FLAC__StreamDecoderTellStatus FLAC_tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
81FLAC__StreamDecoderLengthStatus FLAC_length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data);
82FLAC__bool FLAC_eof_cb(const FLAC__StreamDecoder *decoder, void *client_data);
83FLAC__StreamDecoderWriteStatus FLAC_write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data);
84void FLAC_metadata_cb(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
85void FLAC_error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
86
87#endif // ifndef SOUNDSOURCEFLAC_H
088
=== modified file 'mixxx/src/soundsourceproxy.cpp'
--- mixxx/src/soundsourceproxy.cpp 2010-10-14 04:25:11 +0000
+++ mixxx/src/soundsourceproxy.cpp 2010-11-14 02:21:19 +0000
@@ -26,6 +26,7 @@
26#ifdef __FFMPEGFILE__26#ifdef __FFMPEGFILE__
27#include "soundsourceffmpeg.h"27#include "soundsourceffmpeg.h"
28#endif28#endif
29#include "soundsourceflac.h"
2930
30#include <QLibrary>31#include <QLibrary>
31#include <QMutexLocker>32#include <QMutexLocker>
@@ -117,6 +118,8 @@
117 return new SoundSourceMp3(qFilename);118 return new SoundSourceMp3(qFilename);
118 } else if (SoundSourceOggVorbis::supportedFileExtensions().contains(extension)) {119 } else if (SoundSourceOggVorbis::supportedFileExtensions().contains(extension)) {
119 return new SoundSourceOggVorbis(qFilename);120 return new SoundSourceOggVorbis(qFilename);
121 } else if (SoundSourceFLAC::supportedFileExtensions().contains(extension)) {
122 return new SoundSourceFLAC(qFilename);
120 } else if (m_extensionsSupportedByPlugins.contains(extension)) {123 } else if (m_extensionsSupportedByPlugins.contains(extension)) {
121 getSoundSourceFunc getter = m_extensionsSupportedByPlugins.value(extension);124 getSoundSourceFunc getter = m_extensionsSupportedByPlugins.value(extension);
122 if (getter)125 if (getter)