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