Merge lp:~fenugrec/mixxx/wavpack into lp:~mixxxdevelopers/mixxx/trunk

Proposed by fenugrec
Status: Merged
Merge reported by: Albert Santoni
Merged at revision: not available
Proposed branch: lp:~fenugrec/mixxx/wavpack
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 941 lines (+454/-220)
8 files modified
mixxx/src/SConscript (+20/-0)
mixxx/src/defs_audiofiles.h (+4/-4)
mixxx/src/soundsourceoggvorbis.cpp (+39/-57)
mixxx/src/soundsourceproxy.cpp (+69/-44)
mixxx/src/soundsourcewv.cpp (+194/-0)
mixxx/src/soundsourcewv.h (+33/-0)
mixxx/src/test/soundFileFormats/README.txt (+6/-3)
mixxx/src/test/soundFileFormats/generateFiles.sh (+89/-112)
To merge this branch: bzr merge lp:~fenugrec/mixxx/wavpack
Reviewer Review Type Date Requested Status
Albert Santoni Approve
Sean M. Pappalardo Needs Fixing
Review via email: mp+20183@code.launchpad.net

Commit message

Added Wavpack support

To post a comment you must log in.
Revision history for this message
fenugrec (fenugrec) wrote :

[copied from 1.7 merge proposal]
Wavpack support is pretty much ready to be included. SConscript
options may need to be tweaked to merge properly. Affected files:

src/SConscript
src/defs_audiofiles.h
src/soundsourceproxy.cpp
src/test/soundFileFormats/generateFiles.sh
src/soundsourcewv.{cpp,h} (added)

Hence, it should not affect the functionality of mixxx beyond the
ability to process *.wv files.
Compiling mixxx with wavpack requires libwavpack > 4.0
(not hardcoded, any recent version should work)

Revision history for this message
Sean M. Pappalardo (pegasus-renegadetech) wrote :

I know you've been waiting patiently for this to be merged, but can I possibly ask you to re-merge trunk into this branch again and test? Trunk has diverged since the SoundSource plugins merge which does away with the hard-coded SUPPORTED_FILETYPES #define. (It auto-detects at runtime based on what libs are loaded.) Once you get it working with that, getting it approved for merging to trunk will be much quicker.

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

Last time I talked to fenugrec on IRC, we sort of toyed with the idea
of turning wavpack into another plugin, but we never really decided
what the best course of action was. Merging trunk into the branch and
fixing everything is probably a good start though...

On Thu, Jun 17, 2010 at 1:02 AM, Pegasus <email address hidden> wrote:
> Review: Needs Fixing
> I know you've been waiting patiently for this to be merged, but can I possibly ask you to re-merge trunk into this branch again and test? Trunk has diverged since the SoundSource plugins merge which does away with the hard-coded SUPPORTED_FILETYPES #define. (It auto-detects at runtime based on what libs are loaded.) Once you get it working with that, getting it approved for merging to trunk will be much quicker.
> --
> https://code.launchpad.net/~fenugrec/mixxx/wavpack/+merge/20183
> Your team Mixxx Development Team is subscribed to branch lp:mixxx.
>

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

Hey fenugrec,

If you can merge trunk into your branch (we changed our SCons files around), I'll approve a merge back into trunk and we can finally close this. I looked over your code when I was working on the gme plugin, since I forked your code.

When you merge trunk into your branch, take a look at build/depends.py and build/features.py.

Thanks!
Albert

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

Wait, I'm totally confused... we already merged this!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mixxx/src/SConscript'
2--- mixxx/src/SConscript 2010-02-25 10:14:57 +0000
3+++ mixxx/src/SConscript 2010-02-25 22:20:32 +0000
4@@ -217,6 +217,7 @@
5 vars.Add('midiscript', 'Set to 1 to enable MIDI Scripting support.', 1)
6 vars.Add('tonal', 'Set to 1 to enable tonal analysis', 0)
7 vars.Add('m4a','Set to 1 to enable support for M4A audio (Apple non-drm''d music format)', 0)
8+vars.Add('wv','Set to 1 to enable support for WavPack audio (lossless & hybrid lossy, open codec)',0)
9 vars.Add('qdebug', 'Set to 1 to enable verbose console debug output.', 1)
10 vars.Add('test', 'Set to 1 to build Mixxx test fixtures.', 0)
11 if not 'win' in platform:
12@@ -582,6 +583,9 @@
13 print 'Did not find libfaad or the libfaad development headers, exiting!'
14 Exit(1)
15
16+#hack^2
17+have_wv = conf.CheckLib('wavpack')
18+
19 ## Check for OpenGL (it's messy to do it for all three platforms)
20 ## XXX this should *NOT* have hardcoded paths like this
21 if not conf.CheckLib('GL') and not conf.CheckLib('opengl32') and not conf.CheckCHeader('/System/Library/Frameworks/OpenGL.framework/Versions/A/Headers/gl.h') and not conf.CheckCHeader('GL/gl.h'):
22@@ -1063,6 +1067,22 @@
23 print "Apple M4A audio file support... disabled"
24
25
26+flags_wv = getFlags(env,'wv',0)
27+if int(flags_wv):
28+ print "WavPack audio file support...",
29+ if have_wv:
30+ print "enabled"
31+ env.Append(CPPDEFINES = '__WV__')
32+ build_flags += 'wv '
33+ sources += Split("""soundsourcewv.cpp""");
34+ env.Append(LIBS='libwavpack')
35+ else:
36+ print "not found"
37+else:
38+ print "WavPack audio file support disabled"
39+
40+
41+
42 def build_gtest():
43 gtest_dir = env.Dir("#lib/gtest-1.3.0")
44 gtest_dir.addRepository(env.Dir('#lib/gtest-1.3.0'))
45
46=== modified file 'mixxx/src/defs_audiofiles.h'
47--- mixxx/src/defs_audiofiles.h 2010-02-25 10:14:57 +0000
48+++ mixxx/src/defs_audiofiles.h 2010-02-25 22:20:33 +0000
49@@ -9,14 +9,14 @@
50
51 #ifdef __M4A__
52 /** The types of audio files we support */
53-#define MIXXX_SUPPORTED_AUDIO_FILETYPES "*.wav *.mp3 *.m4a *.mp4 *.ogg *.oga *.aiff *.aif *.flac"
54+#define MIXXX_SUPPORTED_AUDIO_FILETYPES "*.wav *.mp3 *.m4a *.mp4 *.ogg *.oga *.aiff *.aif *.flac *.wv"
55 /** A regex for the types of audio files we support */
56-#define MIXXX_SUPPORTED_AUDIO_FILETYPES_REGEX "\\.(mp3|m4a|mp4|ogg|oga|aiff|aif|wav|flac)$"
57+#define MIXXX_SUPPORTED_AUDIO_FILETYPES_REGEX "\\.(mp3|m4a|mp4|ogg|oga|aiff|aif|wav|flac|wv)$"
58 #else //If the __M4A__ symbol isn't defined, then we can't use .m4a as a supported audio filetype :)
59 /** The types of audio files we support */
60-#define MIXXX_SUPPORTED_AUDIO_FILETYPES "*.wav *.mp3 *.ogg *.aiff *.aif *.flac"
61+#define MIXXX_SUPPORTED_AUDIO_FILETYPES "*.wav *.mp3 *.ogg *.aiff *.aif *.flac *.wv"
62 /** A regex for the types of audio files we support */
63-#define MIXXX_SUPPORTED_AUDIO_FILETYPES_REGEX "\\.(mp3|ogg|oga|aiff|aif|wav|flac)$"
64+#define MIXXX_SUPPORTED_AUDIO_FILETYPES_REGEX "\\.(mp3|ogg|oga|aiff|aif|wav|flac|wv)$"
65 #endif //__M4A__
66
67 #endif
68
69=== modified file 'mixxx/src/soundsourceoggvorbis.cpp'
70--- mixxx/src/soundsourceoggvorbis.cpp 2010-02-25 10:14:57 +0000
71+++ mixxx/src/soundsourceoggvorbis.cpp 2010-02-25 22:20:33 +0000
72@@ -45,18 +45,18 @@
73 Class for reading Ogg Vorbis
74 */
75
76-SoundSourceOggVorbis::SoundSourceOggVorbis(QString qFilename)
77+SoundSourceOggVorbis::SoundSourceOggVorbis(QString qFilename)
78 : SoundSource(qFilename)
79 {
80 QByteArray qBAFilename = qFilename.toUtf8();
81+ filelength = 0;
82
83 #ifdef __WINDOWS__
84 if(ov_fopen(qBAFilename.data(), &vf) < 0) {
85 qDebug() << "oggvorbis: Input does not appear to be an Ogg bitstream.";
86- filelength = 0;
87 return;
88 }
89-#else
90+#else
91 FILE *vorbisfile = fopen(qBAFilename.data(), "r");
92
93 if (!vorbisfile) {
94@@ -66,7 +66,6 @@
95
96 if(ov_open(vorbisfile, &vf, NULL, 0) < 0) {
97 qDebug() << "oggvorbis: Input does not appear to be an Ogg bitstream.";
98- filelength = 0;
99 return;
100 }
101 #endif
102@@ -80,7 +79,6 @@
103 if(channels > 2){
104 qDebug() << "oggvorbis: No support for more than 2 channels!";
105 ov_clear(&vf);
106- filelength = 0;
107 return;
108 }
109
110@@ -90,10 +88,10 @@
111 // hand. a 30 second long 48khz mono ogg and a 48khz stereo ogg both report
112 // 1440000 for ov_pcm_total.
113 ogg_int64_t ret = ov_pcm_total(&vf, -1);
114-
115+
116 if (ret >= 0) {
117 // We pretend that the file is stereo to the rest of the world.
118- filelength = ret * 2;
119+ filelength = ret * 2;
120 }
121 else //error
122 {
123@@ -120,15 +118,15 @@
124 // In our speak, filepos is a sample in the file abstraction (i.e. it's
125 // stereo no matter what). filepos/2 is the frame we want to seek to.
126 Q_ASSERT(filepos%2==0);
127-
128+
129 if (ov_seekable(&vf)){
130 if(ov_pcm_seek(&vf, filepos/2) != 0) {
131 // This is totally common (i.e. you're at EOF). Let's not leave this
132 // qDebug on.
133-
134+
135 // qDebug() << "ogg vorbis: Seek ERR on seekable.";
136 }
137-
138+
139 // Even if an error occured, return them the current position because
140 // that's what we promised. (Double it because ov_pcm_tell returns
141 // frames and we pretend to the world that everything is stereo)
142@@ -149,18 +147,18 @@
143 {
144
145 Q_ASSERT(size%2==0);
146-
147+
148 char *pRead = (char*) destination;
149 SAMPLE *dest = (SAMPLE*) destination;
150-
151-
152+
153+
154
155 // 'needed' is size of buffer in bytes. 'size' is size in SAMPLEs,
156 // which is 2 bytes. If the stream is mono, we read 'size' bytes,
157 // so that half the buffer is full, then below we double each
158 // sample on the left and right channel. If the stream is stereo,
159 // then ov_read interleaves the samples into the full length of
160- // the buffer.
161+ // the buffer.
162
163 // ov_read speaks bytes, we speak words. needed is the bytes to
164 // read, not words to read.
165@@ -172,7 +170,7 @@
166 unsigned int needed = size * channels;
167
168 unsigned int index=0,ret=0;
169-
170+
171 // loop until requested number of samples has been retrieved
172 while (needed > 0) {
173 // read samples into buffer
174@@ -203,11 +201,11 @@
175 dest[i*2] = dest[i];
176 dest[(i*2)+1] = dest[i];
177 }
178-
179+
180 // Pretend we read twice as many bytes as we did, since we just repeated
181 // each pair of bytes.
182 index *= 2;
183- }
184+ }
185
186 // index is the total bytes read, so the words read is index/2
187 return index / 2;
188@@ -225,7 +223,7 @@
189
190 #ifdef __WINDOWS__
191 if (ov_fopen(qBAFilename.data(), &vf) < 0) {
192- qDebug() << "oggvorbis::ParseHeader : Input does not appear to be an Ogg bitstream.";
193+ qDebug() << "oggvorbis::ParseHeader : Input does not appear to be an Ogg bitstream.";
194 return ERR;
195 }
196 #else
197@@ -247,47 +245,31 @@
198 Track->setType("ogg");
199 comment = ov_comment(&vf, -1);
200 if (comment == NULL) {
201- qDebug() << "oggvorbis: fatal error reading file.";
202- ov_clear(&vf);
203- return ERR;
204- }
205-
206- //precache
207- const char* title_p = vorbis_comment_query(comment, (char*)"title", 0); //the char* cast is to shut up the compiler; libvorbis should take `const char*` here but I don't expect us to get them to change that -kousu 2009/02
208- const char* artist_p = vorbis_comment_query(comment, (char*)"artist", 0);
209- const char* bpm_p = vorbis_comment_query(comment, (char*)"TBPM", 0);
210- const char* album_p = vorbis_comment_query(comment, (char*)"album", 0);
211- const char* year_p = vorbis_comment_query(comment, (char*)"date", 0);
212- const char* genre_p = vorbis_comment_query(comment, (char*)"genre", 0);
213- const char* track_p = vorbis_comment_query(comment, (char*)"tracknumber", 0);
214-
215-
216- if(title_p)
217- Track->setTitle(QString::fromUtf8(title_p));
218- if(artist_p)
219- Track->setArtist(QString::fromUtf8(artist_p));
220- if(album_p)
221- Track->setAlbum(QString::fromUtf8(album_p));
222- if(year_p)
223- Track->setYear(QString::fromUtf8(year_p));
224- if(genre_p)
225- Track->setGenre(QString::fromUtf8(genre_p));
226- if(track_p)
227- Track->setTrackNumber(QString::fromUtf8(track_p));
228- if (bpm_p) {
229- float bpm = str2bpm(bpm_p);
230- if(bpm > 0.0f) {
231- Track->setBpm(bpm);
232- Track->setBpmConfirm(true);
233- }
234- }
235- Track->setHeaderParsed(true);
236+ qDebug() << "oggvorbis::ParseHeader : no tags in file";
237+ } else {
238+ //precache
239+ const char* title_p = vorbis_comment_query(comment, (char*)"title", 0); //the char* cast is to shut up the compiler; libvorbis should take `const char*` here but I don't expect us to get them to change that -kousu 2009/02
240+ const char* artist_p = vorbis_comment_query(comment, (char*)"artist", 0);
241+ const char* bpm_p = vorbis_comment_query(comment, (char*)"TBPM", 0);
242+
243+ if(title_p)
244+ Track->setTitle(title_p);
245+ if(artist_p)
246+ Track->setArtist(artist_p);
247+ if (bpm_p) {
248+ float bpm = str2bpm(bpm_p);
249+ if(bpm > 0.0f) {
250+ Track->setBpm(bpm);
251+ Track->setBpmConfirm(true);
252+ }
253+ }
254+ }
255
256 int duration = (int)ov_time_total(&vf, -1);
257 if (duration == OV_EINVAL) {
258 ov_clear(&vf); //close on return !
259- return ERR;
260- }
261+ return ERR;
262+ }
263 Track->setDuration(duration);
264 Track->setBitrate(ov_bitrate(&vf, -1)/1000);
265
266@@ -303,9 +285,9 @@
267 ov_clear(&vf);
268 return ERR;
269 }
270-
271+
272 ov_clear(&vf);
273- Track->setHeaderParsed(true);
274+ Track->setHeaderParsed(true);
275 return OK;
276 }
277
278
279=== modified file 'mixxx/src/soundsourceproxy.cpp'
280--- mixxx/src/soundsourceproxy.cpp 2010-02-25 10:14:57 +0000
281+++ mixxx/src/soundsourceproxy.cpp 2010-02-25 22:20:33 +0000
282@@ -29,52 +29,74 @@
283 #ifdef __FFMPEGFILE__
284 #include "soundsourceffmpeg.h"
285 #endif
286+#ifdef __WV__
287+#include "soundsourcewv.h"
288+#endif
289+
290
291 //Added by qt3to4:
292 #include <Q3ValueList>
293
294
295-SoundSourceProxy::SoundSourceProxy(QString qFilename)
296- : SoundSource(qFilename),
297- m_pSoundSource(NULL) {
298- initialize(qFilename);
299-}
300-
301-SoundSourceProxy::SoundSourceProxy(TrackInfoObject * pTrack)
302- : SoundSource(pTrack->getLocation()),
303- m_pSoundSource(NULL) {
304- initialize(pTrack->getLocation());
305-
306- // Set the track duration in seconds
307- if(getSrate())
308- pTrack->setDuration(length()/(2*getSrate()));
309- else
310- pTrack->setDuration(0);
311-}
312-
313-void SoundSourceProxy::initialize(QString qFilename) {
314-
315-#ifdef __FFMPEGFILE__
316- m_pSoundSource = new SoundSourceFFmpeg(qFilename);
317- return;
318-#endif
319- QString filename = qFilename.toLower();
320- if (filename.endsWith(".mp3"))
321- m_pSoundSource = new SoundSourceMp3(qFilename);
322- else if (filename.endsWith(".ogg") || filename.endsWith(".oga"))
323- m_pSoundSource = new SoundSourceOggVorbis(qFilename);
324-#ifdef __M4A__
325- else if (filename.endsWith(".m4a") ||
326- filename.endsWith(".mp4"))
327- m_pSoundSource = new SoundSourceM4A(qFilename);
328-#endif
329-#ifdef __SNDFILE__
330- else if (filename.endsWith(".wav") ||
331- filename.endsWith(".aif") ||
332- filename.endsWith(".aiff") ||
333- filename.endsWith(".flac"))
334- m_pSoundSource = new SoundSourceSndFile(qFilename);
335-#endif
336+SoundSourceProxy::SoundSourceProxy(QString qFilename) : SoundSource(qFilename)
337+{
338+#ifdef __FFMPEGFILE__
339+ m_pSoundSource = new SoundSourceFFmpeg(qFilename);
340+ return;
341+#endif
342+ QString filename = qFilename.toLower();
343+ if (filename.endsWith(".mp3"))
344+ m_pSoundSource = new SoundSourceMp3(qFilename);
345+ else if (filename.endsWith(".ogg") || filename.endsWith(".oga"))
346+ m_pSoundSource = new SoundSourceOggVorbis(qFilename);
347+#ifdef __M4A__
348+ else if (filename.endsWith(".m4a"))
349+ m_pSoundSource = new SoundSourceM4A(qFilename);
350+#endif
351+#ifdef __WV__
352+ else if (qFilename.toLower().endsWith(".wv"))
353+ m_pSoundSource = new SoundSourceWV(qFilename);
354+#endif
355+#ifdef __SNDFILE__
356+ else if (filename.endsWith(".wav") ||
357+ filename.endsWith(".aif") ||
358+ filename.endsWith(".aiff") ||
359+ filename.endsWith(".flac"))
360+ m_pSoundSource = new SoundSourceSndFile(qFilename);
361+#endif
362+}
363+
364+SoundSourceProxy::SoundSourceProxy(TrackInfoObject * pTrack) : SoundSource(pTrack->getLocation())
365+{
366+ QString qFilename = pTrack->getLocation();
367+
368+#ifdef __FFMPEGFILE__
369+ m_pSoundSource = new SoundSourceFFmpeg(qFilename);
370+ return;
371+#endif
372+ QString filename = qFilename.toLower();
373+ if (filename.endsWith(".mp3"))
374+ m_pSoundSource = new SoundSourceMp3(qFilename);
375+ else if (filename.endsWith(".ogg") || filename.endsWith(".oga"))
376+ m_pSoundSource = new SoundSourceOggVorbis(qFilename);
377+#ifdef __M4A__
378+ else if (filename.endsWith(".m4a"))
379+ m_pSoundSource = new SoundSourceM4A(qFilename);
380+#endif
381+#ifdef __WV__
382+ else if (qFilename.toLower().endsWith(".wv"))
383+ m_pSoundSource = new SoundSourceWV(qFilename);
384+#endif
385+#ifdef __SNDFILE__
386+ else if (filename.endsWith(".wav") ||
387+ filename.endsWith(".aif") ||
388+ filename.endsWith(".aiff") ||
389+ filename.endsWith(".flac"))
390+ m_pSoundSource = new SoundSourceSndFile(qFilename);
391+#endif
392+
393+ if(getSrate()) pTrack->setDuration(length()/(2*getSrate()));
394+ else pTrack->setDuration(0);
395 }
396
397 SoundSourceProxy::~SoundSourceProxy()
398@@ -118,9 +140,12 @@
399 else if (filename.endsWith(".ogg") || filename.endsWith(".oga"))
400 return SoundSourceOggVorbis::ParseHeader(p);
401 #ifdef __M4A__
402- else if (filename.endsWith(".m4a") ||
403- filename.endsWith(".mp4"))
404- return SoundSourceM4A::ParseHeader(p);
405+ else if (filename.endsWith(".m4a"))
406+ return SoundSourceM4A::ParseHeader(p);
407+#endif
408+#ifdef __WV__
409+ else if (qFilename.toLower().endsWith(".wv"))
410+ return SoundSourceWV::ParseHeader(p);
411 #endif
412 #ifdef __SNDFILE__
413 else if (filename.endsWith(".wav") ||
414
415=== added file 'mixxx/src/soundsourcewv.cpp'
416--- mixxx/src/soundsourcewv.cpp 1970-01-01 00:00:00 +0000
417+++ mixxx/src/soundsourcewv.cpp 2010-02-25 22:20:33 +0000
418@@ -0,0 +1,194 @@
419+//soundsourcewv.cpp : sound source proxy for .wv (WavPack files)
420+//created by fenugrec
421+//great help from rryan & others on #mixxx
422+//format_samples adapted from cmus (Peter Lemenkov)
423+
424+#include "trackinfoobject.h"
425+#include "soundsourcewv.h"
426+
427+#include <QtDebug>
428+
429+void format_samples(int bps, char *dst, int32_t *src, uint32_t count);
430+
431+SoundSourceWV::SoundSourceWV(QString qFilename) : SoundSource(qFilename)
432+{
433+ // Initialize variables to invalid values in case loading fails.
434+ filewvc=NULL;
435+ QByteArray qBAFilename = qFilename.toUtf8();
436+ char msg[80]; //hold posible error message
437+
438+ filewvc = WavpackOpenFileInput(qBAFilename.data(),msg,OPEN_2CH_MAX,0);
439+ if (!filewvc) {
440+ qDebug() << "SSWV::constructor: failed to open file : "<<msg;
441+ return;
442+ }
443+ if (WavpackGetMode(filewvc) & MODE_FLOAT) {
444+ qDebug() << "SSWV::constructor: cannot load 32bit float files";
445+ WavpackCloseFile(filewvc);
446+ filewvc=NULL;
447+ return;
448+ }
449+ // wavpack_open succeeded -> populate variables
450+ filelength = WavpackGetNumSamples(filewvc);
451+ SRATE=WavpackGetSampleRate(filewvc);
452+ channels=WavpackGetReducedChannels(filewvc);
453+ Bps=WavpackGetBytesPerSample(filewvc);
454+ qDebug () << "SSWV::constructor: opened filewvc with filelength: "<<filelength<<" SRATE: " << SRATE
455+ << " channels: " << channels << " bytes per samp: "<<Bps;
456+ if (Bps>2) {
457+ qDebug() << "SSWV::constructor: warning: input file has > 2 bytes per sample, will be truncated to 16bits";
458+ }
459+}
460+
461+
462+SoundSourceWV::~SoundSourceWV(){
463+ if (filewvc) {
464+ WavpackCloseFile(filewvc);
465+ filewvc=NULL;
466+ }
467+}
468+
469+
470+long SoundSourceWV::seek(long filepos){
471+ if (WavpackSeekSample(filewvc,filepos>>1) != true) {
472+ qDebug() << "SSWV::seek : could not seek to sample #" << (filepos>>1);
473+ return 0;
474+ }
475+ return filepos;
476+}
477+
478+
479+unsigned SoundSourceWV::read(volatile unsigned long size, const SAMPLE* destination){
480+ //SAMPLE is "short int" => 16bits. [size] is timesamps*2 (because L+R)
481+ SAMPLE * dest = (SAMPLE*) destination;
482+ unsigned long sampsread=0;
483+ unsigned long timesamps, tsdone;
484+
485+ //tempbuffer is fixed size : WV_BUF_LENGTH of uint32
486+ while (sampsread != size) {
487+ timesamps=(size-sampsread)>>1; //timesamps still remaining
488+ if (timesamps > (WV_BUF_LENGTH/channels)) { //if requested size requires more than one buffer filling
489+ timesamps=(WV_BUF_LENGTH/channels); //tempbuffer must hold (timesamps * channels) samples
490+ qDebug() << "SSWV::read : performance warning, size requested > buffer size !";
491+ }
492+
493+ tsdone=WavpackUnpackSamples(filewvc, tempbuffer, timesamps); //fill temp buffer with timesamps*4bytes*channels
494+ //data is right justified, format_samples() fixes that.
495+
496+ format_samples(Bps, (char *) (dest + (sampsread>>1)*channels), tempbuffer, tsdone*channels); //this will unpack the 4byte/sample
497+ //output of wUnpackSamples(), sign-extending or truncating to output 16bit / sample.
498+ //specifying dest+sampsread should resume the conversion where it was left if size requested
499+ //required multiple reads (size req. > fixed buffer size)
500+
501+ sampsread = sampsread + (tsdone<<1);
502+ if (tsdone!=timesamps) {
503+ qDebug () << "SSWV::read : WavpackUnpackSamples read "<<sampsread<<" asamps out of "<<size<<" requested";
504+ break; //exit the while loop : subsequent reads are sure to read less than required.
505+ }
506+
507+ }
508+
509+ if (channels==1) { //if MONO : expand array to double it's size; see ssov.cpp
510+ for(int i=(sampsread/2-1); i>=0; i--) { //algo courtesy of rryan !
511+ dest[i*2] = dest[i]; //go through array backwards, expanding and copying L -> R
512+ dest[(i*2)+1] = dest[i];
513+ }
514+ }
515+
516+ return sampsread;
517+}
518+
519+
520+inline long unsigned SoundSourceWV::length(){
521+ //filelength is # of timesamps.
522+ return filelength<<1;
523+}
524+
525+
526+int SoundSourceWV::ParseHeader( TrackInfoObject * Track){
527+ QString filename = Track->getLocation();
528+ QByteArray qBAFilename = filename.toUtf8();
529+ char msg[80];
530+ *msg='\0';
531+
532+ WavpackContext *twvc = WavpackOpenFileInput(filename, msg, OPEN_TAGS,0);
533+ if (!twvc) {
534+ qDebug() << "SSWV::ParseHeader : WavpackOpenFileInput: " << msg;
535+ return ERR;
536+ }
537+
538+ int wavpackmode = WavpackGetMode(twvc);
539+ if (MODE_FLOAT & wavpackmode) {
540+ qDebug() << "SSWV::ParseHeader: 32 bit float file format will not be loaded";
541+ WavpackCloseFile(twvc);
542+ return ERR;
543+ }
544+
545+ Track->setType("wv");
546+ if (!(MODE_VALID_TAG & wavpackmode)) {
547+ qDebug() << "SSWV::ParseHeader: no valid tags";
548+ } else {
549+ char wvtag[80];
550+ if (WavpackGetTagItem(twvc, "TITLE", wvtag, 80)) //should be case-insensitive.
551+ Track->setTitle(QString(wvtag));
552+ if (WavpackGetTagItem(twvc, "ARTIST", wvtag, 80))
553+ Track->setArtist(QString(wvtag));
554+ if (WavpackGetTagItem(twvc, "TBPM", wvtag, 80)) {
555+ float bpm=str2bpm(QString(wvtag));
556+ if (bpm>0.0f) {
557+ Track->setBpm(bpm);
558+ Track->setBpmConfirm(true);
559+ }
560+ }
561+ }
562+
563+ Track->setDuration(WavpackGetNumSamples(twvc) / WavpackGetSampleRate(twvc));
564+ Track->setBitrate(WavpackGetAverageBitrate(twvc, 0)/1000);
565+ Track->setSampleRate(WavpackGetSampleRate(twvc));
566+ Track->setChannels(WavpackGetReducedChannels(twvc));
567+
568+ WavpackCloseFile(twvc);
569+ Track->setHeaderParsed(true);
570+ return OK;
571+}
572+
573+
574+
575+void format_samples(int Bps, char *dst, int32_t *src, uint32_t count)
576+{
577+ //this handles converting the fixed 32bit per sample produced by UnpackSamples
578+ //to 16 bps, by truncating (24/32) or sign-extending (8)
579+ //could eventually be asm-optimized..
580+ int32_t temp;
581+
582+ switch (Bps) {
583+ case 1:
584+ while (count--) {
585+ *dst++ = (char) 0; //left shift the 8 bit sample
586+ *dst++ = (char) *src++ ;//+ 128; //only works with u8int ?
587+ }
588+ break;
589+ case 2:
590+ while (count--) {
591+ *dst++ = (char) (temp = *src++); //low byte
592+ *dst++ = (char) (temp >> 8); //high byte
593+ }
594+ break;
595+ case 3: //modified to truncate to 16bits
596+ while (count--) {
597+ *dst++ = (char) (temp = (*src++) >> 8);
598+ *dst++ = (char) (temp >> 8);
599+ }
600+ break;
601+ case 4: //also truncates
602+ while (count--) {
603+ *dst++ = (char) (temp = (*src++) >> 16);
604+ *dst++ = (char) (temp >> 8);
605+ //*dst++ = (char) (temp >> 16);
606+ //*dst++ = (char) (temp >> 24);
607+ }
608+ break;
609+ }
610+
611+ return;
612+}
613
614=== added file 'mixxx/src/soundsourcewv.h'
615--- mixxx/src/soundsourcewv.h 1970-01-01 00:00:00 +0000
616+++ mixxx/src/soundsourcewv.h 2010-02-25 22:20:33 +0000
617@@ -0,0 +1,33 @@
618+//soundsourcewv.h
619+// wavpack sound proxy for mixxx.
620+// fenugrec 12/2009
621+
622+
623+#ifndef SOUNDSOURCEWV_H
624+#define SOUNDSOURCEWV_H
625+
626+#include "qstring.h"
627+#include "soundsource.h"
628+
629+#include "wavpack/wavpack.h"
630+
631+#define WV_BUF_LENGTH 65536
632+class TrackInfoObject;
633+
634+class SoundSourceWV : public SoundSource {
635+ public:
636+ SoundSourceWV(QString qFilename);
637+ ~SoundSourceWV();
638+ long seek(long);
639+ unsigned read(unsigned long size, const SAMPLE*);
640+ inline long unsigned length();
641+ static int ParseHeader( TrackInfoObject * );
642+ private:
643+ int channels;
644+ int Bps;
645+ unsigned long filelength;
646+ WavpackContext * filewvc; //works as a file handle to access the wv file.
647+ int32_t tempbuffer[WV_BUF_LENGTH]; //hax ! legacy from cmus. this is 64k*4bytes.
648+
649+};
650+#endif
651\ No newline at end of file
652
653=== modified file 'mixxx/src/test/soundFileFormats/README.txt'
654--- mixxx/src/test/soundFileFormats/README.txt 2009-07-21 18:07:08 +0000
655+++ mixxx/src/test/soundFileFormats/README.txt 2010-02-25 22:20:33 +0000
656@@ -1,8 +1,11 @@
657 The scripts in this directory allow you to generate all the bit depths and sample rates of the file types that Mixxx supports.
658
659-You must have sox, lame and bzip2 installed on your system for them to work.
660+You must have ffmpeg, wavpack and bzip2 installed on your system for them to work.
661+If you do not specify "ff" when calling the script, you will also need vorbis-tools and NeroAAC
662+(freely available at http://www.nero.com/enu/technologies-aac-codec.html )
663
664-Then run ./generateFiles on OSX and Linux, or generateFiles.cmd on Windows. Then load each of the resulting files into Mixxx and verify for each:
665+Then run ./generateFiles.sh on OSX and Linux. Then load each of the resulting files into Mixxx and verify for each:
666+Running " ./generateFiles ff " will use only ffmpeg (some limitations apply, see script for details)
667
668 1) it loads without problems
669 2) the large and summary waveforms eventually display something (just looks like a green bar)
670@@ -13,4 +16,4 @@
671
672 If any of these fails, please report a bug using the instructions on this page: http://mixxx.org/wiki/doku.php/reporting_bugs
673
674-When you're done, you can run ./generateFiles clean to delete all the generated files
675\ No newline at end of file
676+When you're done, you can run "./generateFiles clean" to delete all the generated files - the whole testsuite takes about 200MB !
677
678=== modified file 'mixxx/src/test/soundFileFormats/generateFiles.sh'
679--- mixxx/src/test/soundFileFormats/generateFiles.sh 2009-07-21 20:19:02 +0000
680+++ mixxx/src/test/soundFileFormats/generateFiles.sh 2010-02-25 22:20:33 +0000
681@@ -2,6 +2,7 @@
682 #
683 # Test sound file generator for Mixxx
684 # created by Sean M. Pappalardo on 7/20/2009
685+# modified by fenugrec 01-2009
686 #
687 # This script generates sound files in all of the formats
688 # Mixxx supports with all of the various sample sizes,
689@@ -9,41 +10,34 @@
690 #
691 # Pass it the "clean" argument to delete all the files it makes
692 # Pass it the "table" argument to generate a table of all the file formats & types in Wiki syntax
693-
694-formats=("wav" "mp3" "ogg" "flac") # mp3 must come after wav because it converts the wavs
695+# Pass it the "ff" argument to use only ffmpeg (this will disable:
696+# 22kHz m4a & ogg files
697+# 96kHz ogg files
698+
699+formats=("wav" "mp3" "m4a" "ogg" "flac" "wv") #wav must be generated first
700+
701 channels=(1 2)
702-samplesizes=(16 24 32)
703+channelnames=("Mono" "Stereo") #used as channelnames[channels-1] to get friendlyname.
704+samplesizes=(s16 s24 s32) #sample_fmt for ffmpeg
705 samplerates=(22050 32000 44100 48000 96000)
706
707+
708 if [ "$1" == "clean" ]
709 then
710 for format in ${formats[*]}
711 do
712+ if [ -e invalidfile.${format} ]
713+ then
714+ echo "Removing invalidfile.${format}"
715+ rm invalidfile.${format}
716+ fi
717+
718 for rate in ${samplerates[*]}
719 do
720 friendlyrate=`expr $rate / 1000`
721 for channel in ${channels[*]}
722 do
723- if [ $channel -eq 1 ]
724- then
725- friendlychannel="Mono"
726- fi
727- if [ $channel -eq 2 ]
728- then
729- friendlychannel="Stereo"
730- fi
731-
732- # Remove files from formats for which sample size is irrelevant
733- if ( [ $format == "ogg" ] || [ $format == "mp3" ] ) && [ -e test${friendlyrate}k${friendlychannel}.${format} ]
734- then
735- echo "Removing test${friendlyrate}k${friendlychannel}.${format}"
736- rm test${friendlyrate}k${friendlychannel}.${format}
737- if [ $? -gt 0 ]
738- then
739- echo "Error #$?"
740- exit 1
741- fi
742- fi
743+ friendlychannel=${channelnames[${channel}-1]}
744
745 for ssize in ${samplesizes[*]}
746 do
747@@ -51,10 +45,6 @@
748 then
749 echo "Removing test${ssize}bit${friendlyrate}k${friendlychannel}.${format}"
750 rm test${ssize}bit${friendlyrate}k${friendlychannel}.${format}
751- if [ $? -gt 0 ]
752- then
753- echo "Error #$?"
754- exit 1
755 fi
756 fi
757 done
758@@ -86,111 +76,98 @@
759 do
760 if [ "$1" == "table" ]
761 then
762- if [ $format == "ogg" ] || [ $format == "mp3" ]
763- then
764- friendlyformat="OGG Vorbis"
765- fi
766- if [ $format == "wav" ]
767- then
768- friendlyformat="WAVE/AIFF"
769- fi
770- if [ $format == "mp3" ]
771- then
772- friendlyformat="MP3"
773- fi
774- if [ $format == "flac" ]
775- then
776- friendlyformat="FLAC"
777- fi
778+ case $format in
779+ "ogg") friendlyformat="OGG Vorbis";;
780+ "m4a") friendlyformat="M4A";;
781+ "wav") friendlyformat="WAVE/AIFF";;
782+ "mp3") friendlyformat="MP3";;
783+ "flac") friendlyformat="FLAC";;
784+ "wv") friendlyformat="WavPack";;
785+ esac
786 echo
787 echo "==== $friendlyformat ===="
788 echo "^ Channels ^ Bit depth ^ Sample Rate ^ Does it work? ^"
789+ else #if not generating a table
790+ vorbisflag=""
791+ enccmd="ffmpeg -i test\${ssize}bit\${friendlyrate}k\${friendlychannel}.wav -ab 128k \$vorbisflag test\${ssize}bit\${friendlyrate}k\${friendlychannel}.\${format}"
792+ #loaded default encoding command, which may be replaced by format-specific commands:
793+
794+ if [ $format == "wav" ]
795+ then
796+ enccmd="ffmpeg -i 1kHzR440HzLReference_32i96kStereo.wav -sample_fmt \${ssize} -ar \${rate} -ac \${channel} test\${ssize}bit\${friendlyrate}k\${friendlychannel}.wav"
797+ elif [ $format == "ogg" ]
798+ then
799+ vorbisflag="-acodec libvorbis" #because .ogg is ambiguous to ffmpeg
800+ elif [ $format == "wv" ]
801+ then #use wavpack regardless (ffmpeg can't encode wv)
802+ enccmd="wavpack -b200 test\${ssize}bit\${friendlyrate}k\${friendlychannel}.wav"
803+ fi
804+
805+ if [ "$1" != "ff" ]
806+ then #use specific encoders as well as ffmpeg
807+ if [ $format == "ogg" ]
808+ then
809+ enccmd="oggenc test\${ssize}bit\${friendlyrate}k\${friendlychannel}.wav"
810+ elif [ $format == "m4a" ]
811+ then
812+ enccmd="neroAacEnc -if test\${ssize}bit\${friendlyrate}k\${friendlychannel}.wav -of test\${ssize}bit\${friendlyrate}k\${friendlychannel}.\${format}"
813+ fi
814+ fi
815+
816+ echo "Generating invalid file : invalidfile.${format}"
817+ echo "Invalid ${format} file" > invalidfile.${format}
818 fi
819+
820 for channel in ${channels[*]}
821 do
822- if [ $channel -eq 1 ]
823- then
824- friendlychannel="Mono"
825- lameopt="-m m"
826- fi
827- if [ $channel -eq 2 ]
828- then
829- friendlychannel="Stereo"
830- lameopt=""
831- fi
832+ friendlychannel=${channelnames[${channel}-1]} #not sure if this is faster than ifs...
833+
834 for ssize in ${samplesizes[*]}
835 do
836- # Hack because sox doesn't abort if the parameters are out of spec
837- if [ $ssize -gt 24 ] && [ $format == "flac" ]
838- then
839- if [ "$1" != "table" ]
840- then
841- echo "FLAC doesn't support ${ssize}-bit, skipping"
842- fi
843- break
844- fi
845-
846- # vorbis and MP3 don't use bit depth, so only run sox once per sample rate
847- if [ $ssize != ${samplesizes[0]} ] && ( [ $format == "ogg" ] || [ $format == "mp3" ] )
848- then
849- break
850- fi
851+ #MP3 & m4a don't use bit depth (s16 only), so only run once per sample rate
852+ if [ $ssize != "s16" ] && ( [ $format == "mp3" ] || [ $format == "m4a" ] )
853+ then
854+ continue
855+ fi
856+
857 for rate in ${samplerates[*]}
858 do
859 friendlyrate=`expr $rate / 1000`
860- problem="false"
861-
862- # Hack because lame doesn't abort if the parameters are out of spec
863- if [ $rate -gt 48000 ] && [ $format == "mp3" ]
864- then
865- if [ "$1" != "table" ]
866- then
867- echo "MP3 doesn't support ${rate}Hz, skipping"
868- fi
869- problem="true"
870- fi
871-
872- if [ "$1" == "table" ] && [ "$problem" != "true" ]
873- then
874- if [ $format == "ogg" ] || [ $format == "mp3" ]
875- then # sample size is irrelevant for MP3 and Vorbis
876+ #disable impossible formats.
877+ if [ $rate -gt 48000 ] && [ $format == "mp3" ] #96kHz MP3 unsupported
878+ then
879+ continue #next for-iteration
880+ elif [ $rate -lt 32000 ] && ([ $format == "m4a" ] || [ $format == "ogg" ]) && [ "$1" == "ff" ]
881+ then #disable 22khz m4a & ogg files with ffmpeg(buggy)
882+ continue
883+ elif [ $rate -gt 48000 ] && [ $format == "ogg" ] && [ "$1" == "ff" ]
884+ then #disable 96kHz ogg with ffmpeg
885+ continue
886+ fi
887+
888+ if [ "$1" == "table" ]
889+ then
890+ if [ $format == "mp3" ] || [ $format == "m4a" ]
891+ then # sample size is irrelevant for MP3 and m4a
892 if [ $channel -eq 1 ]
893 then
894 echo "^ Mono ^ ^ $rate Hz | |"
895 else
896- echo "^ $friendlychannel ^ ^ $rate Hz | |"
897+ echo "^ Stereo ^ ^ $rate Hz | |"
898 fi
899 else
900 if [ $channel -eq 1 ]
901 then
902- echo "^ Mono ^ $ssize-bit ^ $rate Hz | |"
903+ echo "^ Mono ^ ${ssize#s}-bit ^ $rate Hz | |"
904 else
905- echo "^ $friendlychannel ^ $ssize-bit ^ $rate Hz | |"
906- fi
907- fi
908- fi
909-
910- if [ "$problem" != "true" ] && ( [ $format == "ogg" ] || [ $format == "mp3" ] )
911- then
912- if [ "$1" != "table" ]
913- then
914- echo "Generating ${rate}Hz ${channel}-channel ${format} file"
915- if [ $format == "mp3" ]
916- then # sox can't make MP3s by default, so we use LAME
917- lame -S $lameopt --tt test${friendlyrate}k${friendlychannel} test32bit${friendlyrate}k${friendlychannel}.wav test${friendlyrate}k${friendlychannel}.mp3
918- else # For formats where sample size is irrelevant
919- sox -V0 1kHzR440HzLReference_32i96kStereo.wav -c ${channel} -r ${rate} test${friendlyrate}k${friendlychannel}.${format}
920- fi
921- problem="true"
922- fi
923- fi
924-
925- # Use sox by default
926- if [ "$1" != "table" ] && [ $problem != "true" ]
927- then
928- echo "Generating ${ssize}-bit ${rate}Hz ${channel}-channel ${format} file"
929- sox -V0 1kHzR440HzLReference_32i96kStereo.wav -b ${ssize} -c ${channel} -r ${rate} test${ssize}bit${friendlyrate}k${friendlychannel}.${format}
930- fi
931+ echo "^ Stereo ^ ${ssize#s}-bit ^ $rate Hz | |"
932+ fi
933+ fi
934+ else #not generating a table
935+ echo "Generating ${ssize}bit ${rate}Hz ${friendlychannel} ${format} file"
936+ exec `eval ${enccmd}` 2>/dev/null #hack : parse enccmd and run it
937+ fi
938+
939 if [ $? -gt 1 ]
940 then
941 echo "Error #$?, aborting"