Hi,

Most of my mp3s have BPMs stored in tag, so I made some changes to read and
use them. Additional changes are especially related to minimum and maximum
BPM values in BpmDetect class. When maxBpm is lower than 2*minBpm (passed to
BpmDetect constructor), then BpmDetect::getBpm() will sometimes return 0. So
default values are used in this constructor (defined in bpmdetect.h) and
commented out values passed to it from BpmScheme in BpmDetector. BPM is
corrected with correctBPM to be within the limit specified by BpmScheme (or
little more, rather than 0).
Maybe you'll find this useful.

Cheers,
Martin
Index: lib/soundtouch/STTypes.h
===================================================================
--- lib/soundtouch/STTypes.h	(revision 1965)
+++ lib/soundtouch/STTypes.h	(working copy)
@@ -47,6 +47,11 @@
 
     typedef  int    BOOL;
 
+#if defined(FALSE) || defined(TRUE)
+    #undef FALSE
+    #undef TRUE
+#endif
+
     #define FALSE   0
     #define TRUE    1
 
Index: src/soundsource.cpp
===================================================================
--- src/soundsource.cpp	(revision 1965)
+++ src/soundsource.cpp	(working copy)
@@ -55,3 +55,10 @@
 {
     return m_qFilename;
 }
+
+float SoundSource::str2bpm( QString sBpm ) {
+  float bpm = sBpm.toFloat();
+  if(bpm < 60) bpm = 0;
+  while( bpm > 300 ) bpm = bpm / 10.;
+  return bpm;
+}
Index: src/soundsourcemp3.cpp
===================================================================
--- src/soundsourcemp3.cpp	(revision 1965)
+++ src/soundsourcemp3.cpp	(working copy)
@@ -453,10 +453,10 @@
 {
     QString location = Track->getLocation();
 
-	QFile sizetest( location );
-	if (sizetest.size() == 0) {
-		return ERR;
-	}
+    QFile sizetest( location );
+    if (sizetest.size() == 0) {
+        return ERR;
+    }
 
     Track->setType("mp3");
 
@@ -474,6 +474,10 @@
             getField(tag,"TPE1",&s);
             if (s.length()>2)
                 Track->setArtist(s);
+            s="";
+            getField(tag,"TBPM",&s);
+            if (s.length()>2)
+                Track->setBpm(str2bpm(s));
 
             /*
                // On some tracks this segfaults. TLEN is very seldom used anyway...
Index: src/soundsourceoggvorbis.cpp
===================================================================
--- src/soundsourceoggvorbis.cpp	(revision 1965)
+++ src/soundsourceoggvorbis.cpp	(working copy)
@@ -182,6 +182,9 @@
         Track->setTitle(vorbis_comment_query(comment, "title", 0));
     if (QString(vorbis_comment_query(comment, "artist", 0)).length()!=0)
         Track->setArtist(vorbis_comment_query(comment, "artist", 0));
+    if (QString(vorbis_comment_query(comment, "TBPM", 0)).length()!=0)
+        Track->setBpm(str2bpm(vorbis_comment_query(comment, "TBPM", 0)));
+
     Track->setType("ogg");
     Track->setDuration((int)ov_time_total(&vf, -1));
     Track->setBitrate(ov_bitrate(&vf, -1)/1000);
Index: src/bpmdetector.cpp
===================================================================
--- src/bpmdetector.cpp	(revision 1965)
+++ src/bpmdetector.cpp	(working copy)
@@ -101,8 +101,6 @@
 
 void BpmDetector::run()
 {
-    int i = 0;
-
     while (1)
     {
         TrackInfoObject * pTrackInfoObject = NULL;
@@ -152,16 +150,15 @@
         // Check if BPM has been detected in the meantime
         if (pTrackInfoObject->getBpmConfirm() == false || pTrackInfoObject->getBpm() == 0.)
         {
-            #define CHUNKSIZE 4096
+            const int numSamples = 8192;
 
             SoundSourceProxy * pSoundSource = new SoundSourceProxy(pTrackInfoObject);
-            int16_t data16[ CHUNKSIZE*2];      // for 16 bit samples
-            int8_t data8[ CHUNKSIZE ];            // for 8 bit samples
-            soundtouch::SAMPLETYPE samples[ CHUNKSIZE * 2];
+            int16_t data16[numSamples];      // for 16 bit samples
+            soundtouch::SAMPLETYPE samples[ numSamples ];
             unsigned int length = 0, read = 0, totalsteps = 0, pos = 0, end = 0;
-            int channels = 2, bits = 16;
+            int channels = 2;
             float frequency = 44100;
-            
+
             if(pTrackInfoObject->getSampleRate())
             {
                 frequency = pTrackInfoObject->getSampleRate();
@@ -170,12 +167,7 @@
             {
                 channels = pTrackInfoObject->getChannels();
             }
-            if(pTrackInfoObject->getBitrate())
-            {
-                bits = pTrackInfoObject->getBitrate();
-            }
 
-
             length = pSoundSource->length();
 
             if(!pScheme->getAnalyzeEntireSong())
@@ -184,7 +176,7 @@
                 pos = length / 2;
 
             }
-            
+
             if(pos %2 != 0)
             {
                 //Bug Fix: above formula allows iBeatPosStart
@@ -192,35 +184,24 @@
                 pos--;
             }
 
-            totalsteps = ( length / CHUNKSIZE );
+            totalsteps = ( length / numSamples );
             end = pos + length;
 
-            
-            BpmDetect bpmd( channels, ( int ) frequency, pScheme->getMaxBpm(), pScheme->getMinBpm() );
+            // Use default minBpm and maxBpm values
+            BpmDetect bpmd( channels, ( int ) frequency/*, pScheme->getMinBpm(), pScheme->getMaxBpm()*/ );
 
             int cprogress = 0;
             pSoundSource->seek(pos);
             do {
-                read = pSoundSource->read(CHUNKSIZE, data16);
+                read = pSoundSource->read(numSamples, data16);
 
                 if(read >= 2)
                 {
                     pos += read;
-
-                    //****************************************************
-                    // Replace:
-                    //result = FMOD_Sound_ReadData( sound, data16, CHUNKSIZE, &read );
-
-                    //****************************************************
                     for ( unsigned int i = 0; i < read ; i++ ) {
-                        int16_t test = data16[i];
-                        if(test > 0)
-                        {
-                            test = 0;
-                        }
                         samples[ i ] = ( float ) data16[ i ] / 32768;
                     }
-                    bpmd.inputSamples( samples, read / ( channels ) );
+                    bpmd.inputSamples( samples, read / channels );
 
                     cprogress++;
                     if ( cprogress % 250 == 0 ) {
@@ -233,24 +214,25 @@
                         }
                     }
                 }
-            } while (read == CHUNKSIZE && pos <= length);
+            } while (read == numSamples && pos <= end);
 
             float BPM = bpmd.getBpm();
-            if ( BPM != 0. ) {
-                BPM = Correct_BPM( BPM, pScheme->getMaxBpm(), pScheme->getMinBpm() );
+            if ( BPM != 0 ) {
+                BPM = BpmDetect::correctBPM(BPM, pScheme->getMinBpm(), pScheme->getMaxBpm());
                 pTrackInfoObject->setBpm(BPM);
                 pTrackInfoObject->setBpmConfirm();
                 if(pBpmReceiver){
                     pBpmReceiver->setComplete(pTrackInfoObject, false, BPM);
                 }
                 qDebug() << "BPM detection successful for" << pTrackInfoObject->getFilename();
+                qDebug() << "BPM" << BPM;
                 delete pSoundSource;
                 continue;
             }
             else
-            {            
+            {
                 qDebug() << "BPM detection failed, setting to 0.";
-                
+
 #ifdef __C_METRICS__
 		        cm_writemsg_ascii(1, "BPM detection failed, setting to 0.");
 #endif
Index: src/soundsource.h
===================================================================
--- src/soundsource.h	(revision 1965)
+++ src/soundsource.h	(working copy)
@@ -39,6 +39,7 @@
     virtual long seek(long) = 0;
     virtual unsigned read(unsigned long size, const SAMPLE*) = 0;
     virtual long unsigned length() = 0;
+    static float str2bpm( QString sBpm );
     static int ParseHeader(TrackInfoObject *);
     virtual int getSrate();
     /** Return a list of cue points stored in the file */
Index: src/bpmdetect.cpp
===================================================================
--- src/bpmdetect.cpp	(revision 1965)
+++ src/bpmdetect.cpp	(working copy)
@@ -95,20 +95,17 @@
 /// Normalization coefficient for calculating RMS sliding average approximation.
 const float avgnorm = (1 - avgdecay);
 
-float Correct_BPM( float BPM, int max, int min) {
-    if ( BPM == 0. )
-        return BPM;
+float BpmDetect::correctBPM( float BPM, int min, int max) {
+    if ( BPM == 0 ) return BPM;
 
-    while ( BPM > max )
-        BPM /= 2;
-    while ( BPM < min )
-        BPM *= 2;
+    if( BPM*2 < max ) BPM *= 2;
+    while ( BPM > max ) BPM /= 2;
+    while ( BPM < min ) BPM *= 2;
 
     return BPM;
 }
 
-
-BpmDetect::BpmDetect(int numChannels, int sampleRate, int _maxBpm, int _minBpm)
+BpmDetect::BpmDetect(int numChannels, int sampleRate, int _minBpm, int _maxBpm)
 {
     xcorr = NULL;
 
Index: src/soundsourceproxy.cpp
===================================================================
--- src/soundsourceproxy.cpp	(revision 1965)
+++ src/soundsourceproxy.cpp	(working copy)
@@ -40,9 +40,9 @@
     m_pSoundSource = new SoundSourceFFmpeg(qFilename);
     return;
 #endif
-    if (qFilename.lower().endsWith(".mp3"))
+    if (qFilename.toLower().endsWith(".mp3"))
         m_pSoundSource = new SoundSourceMp3(qFilename);
-    else if (qFilename.lower().endsWith(".ogg"))
+    else if (qFilename.toLower().endsWith(".ogg"))
         m_pSoundSource = new SoundSourceOggVorbis(qFilename);
     else
 #ifdef __SNDFILE__
@@ -62,9 +62,9 @@
     return;
 #endif
 
-    if (qFilename.lower().endsWith(".mp3"))
+    if (qFilename.toLower().endsWith(".mp3"))
 	m_pSoundSource = new SoundSourceMp3(qFilename);
-    else if (qFilename.lower().endsWith(".ogg"))
+    else if (qFilename.toLower().endsWith(".ogg"))
 	m_pSoundSource = new SoundSourceOggVorbis(qFilename);
     else
     {
@@ -76,7 +76,8 @@
 #endif
     }
 
-    pTrack->setDuration(length()/(2*getSrate()));
+    if(getSrate()) pTrack->setDuration(length()/(2*getSrate()));
+    else pTrack->setDuration(0);
 }
 
 SoundSourceProxy::~SoundSourceProxy()
@@ -105,12 +106,12 @@
 #ifdef __FFMPEGFILE__
     return SoundSourceFFmpeg::ParseHeader(p);;
 #endif
-    if (qFilename.lower().endsWith(".mp3"))
+    if (qFilename.toLower().endsWith(".mp3"))
 	return SoundSourceMp3::ParseHeader(p);
-    else if (qFilename.lower().endsWith(".ogg"))
+    else if (qFilename.toLower().endsWith(".ogg"))
 	return SoundSourceOggVorbis::ParseHeader(p);
-    else if (qFilename.lower().endsWith(".wav") || qFilename.lower().endsWith(".aif") ||
-	     qFilename.lower().endsWith(".aiff") || qFilename.lower().endsWith(".flac"))
+    else if (qFilename.toLower().endsWith(".wav") || qFilename.toLower().endsWith(".aif") ||
+	     qFilename.toLower().endsWith(".aiff") || qFilename.toLower().endsWith(".flac"))
 #ifdef __SNDFILE__
 	return SoundSourceSndFile::ParseHeader(p);
 #endif
Index: src/track.cpp
===================================================================
--- src/track.cpp	(revision 1965)
+++ src/track.cpp	(working copy)
@@ -48,6 +48,7 @@
 #include "woverview.h"
 #include "playerinfo.h"
 #include "defs_promo.h"
+#include "soundsourceproxy.h"
 
 #include <q3progressdialog.h>
 
@@ -855,6 +856,7 @@
 {
 
     QString filename = pTrackInfoObject->getLocation();
+    qDebug() << "Load to player1:" << filename;
     if (!filename.isEmpty())
     {
         // Check if filename is valid
@@ -886,6 +888,8 @@
     // Request a new track from the reader:
     m_pBuffer1->getReader()->requestNewTrack(m_pTrackPlayer1, bStartFromEndPos);
 
+    SoundSourceProxy::ParseHeader(m_pTrackPlayer1);
+    if(m_pTrackPlayer1->getBpm()) m_pTrackPlayer1->setBpmConfirm(true);
     // Detect BPM if required
     if (m_pTrackPlayer1->getBpmConfirm()== false || m_pTrackPlayer1->getBpm() == 0.)
         m_pTrackPlayer1->sendToBpmQueue();
@@ -924,6 +928,7 @@
 void Track::slotLoadPlayer2(TrackInfoObject * pTrackInfoObject, bool bStartFromEndPos)
 {
     QString filename = pTrackInfoObject->getLocation();
+    qDebug() << "Load to player2:" << filename;
     if (!filename.isEmpty())
     {
         // Check if filename is valid
@@ -955,6 +960,8 @@
     // Request a new track from the reader:
     m_pBuffer2->getReader()->requestNewTrack(m_pTrackPlayer2, bStartFromEndPos);
 
+    SoundSourceProxy::ParseHeader(m_pTrackPlayer2);
+    if(m_pTrackPlayer2->getBpm()) m_pTrackPlayer2->setBpmConfirm(true);
     // Detect BPM if required
     if (m_pTrackPlayer2->getBpmConfirm()== false || m_pTrackPlayer2->getBpm() == 0.)
         m_pTrackPlayer2->sendToBpmQueue();
Index: src/bpmdetect.h
===================================================================
--- src/bpmdetect.h	(revision 1965)
+++ src/bpmdetect.h	(working copy)
@@ -94,14 +94,6 @@
 /// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
 #define MAX_BPM 170
 
-/**
- * @brief Correct BPM
- * if value is lower than MINIMUM_BPM or higher than MAXIMUM_BPM
- * @param BPM BPM to correct
- * @return corrected BPM
- */
-float Correct_BPM( float BPM, int max, int min );
-
 /// Class for calculating BPM rate for audio data.
 class BpmDetect
 {
@@ -171,15 +163,22 @@
 
 public:
     /// Constructor.
-    BpmDetect(int numChannels,  ///< Number of channels in sample data.
-              int sampleRate,   ///< Sample rate in Hz.
-              int _maxBpm,
-			  int _minBpm
-			  );
+    /// @note _maxBpm should be at least 2 * _minBpm, otherwise BPM won't always be detected
+    BpmDetect(int numChannels,          ///< Number of channels in sample data.
+              int sampleRate,           ///< Sample rate in Hz.
+              int _minBpm = MIN_BPM,    ///< Minimum acceptable BPM
+              int _maxBpm = MAX_BPM     ///< Maximum acceptable BPM
+    );
 
     /// Destructor.
     virtual ~BpmDetect();
 
+    /**
+     * Multiplying or dividing BPM by 2 if value is lower than min or greater than max
+     * @return corrected BPM (can be greater than max)
+     */
+    static float correctBPM( float BPM, int min, int max );
+
     /// Inputs a block of samples for analyzing: Envelopes the samples and then
     /// updates the autocorrelation estimation. When whole song data has been input
     /// in smaller blocks using this function, read the resulting bpm with 'getBpm' 
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
Mixxx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mixxx-devel

Reply via email to