Hi,
I just made some other changes to this. You can try this patch instead of
that I sent before.
On Sun, May 4, 2008 at 6:57 PM, Albert Santoni <[EMAIL PROTECTED]> wrote:
> Hi Martin,
>
> I have a few comments and questions about your patch.
>
> On Tue, 2008-04-29 at 18:49 +0200, Martin Sakmar wrote:
> > 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).
>
> Ok, so the quirk that the initial BPM detection range must be greater
> than {min, min*2} is something that was in Mixxx before your patch,
> correct?
>
I added a checkbox to dlgprefbpm to enable or disable this. Maybe correctBpm
could be modified to return zero if the BPM can't be within the range, but I
prefer returning a BPM if it can be detected.
> We've received many requests for Mixxx read to BPMs from ID3 tags, so I
> think many people are going to appreciate this.
>
> In track.cpp, in slotLoadPlayer1(), your patch has this chunk:
>
> @@ -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();
>
> Why do you call ParseHeader() here? As best as I can tell, ParseHeader()
> gets called when a new TrackInfoObject is created.
>
ParseHeader is required here to read BPM from tag on load. It's called when
TrackInfo is created from filename, but not when you load a song from
library. I added a bool to TrackInfoObject which is set to true, when header
is parsed, and then it won't be called again in slotLoadPlayer if it already
was called.
> Other than that, everything looks pretty good. If you can shed some
> light on my two questions, I'd be happy to slip this into SVN before the
> beta3 release.
>
> Thanks,
> Albert
>
Regards,
Martin
Index: lib/soundtouch/STTypes.h
===================================================================
--- lib/soundtouch/STTypes.h (revision 1979)
+++ 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 1979)
+++ 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/dlgprefbpm.cpp
===================================================================
--- src/dlgprefbpm.cpp (revision 1979)
+++ src/dlgprefbpm.cpp (working copy)
@@ -45,7 +45,8 @@
// Connection
connect(chkDetectOnImport, SIGNAL(stateChanged(int)), this, SLOT(slotSetBpmDetectOnImport(int)));
connect(chkWriteID3, SIGNAL(stateChanged(int)), this, SLOT(slotSetWriteID3Tag(int)));
- connect(chkEnableBpmDetection, SIGNAL(stateChanged(int)), this, SLOT(slotSetBpmEnabled(int)));
+ connect(chkEnableBpmDetection, SIGNAL(stateChanged(int)), this, SLOT(slotSetBpmEnabled(int)));
+ connect(chkAboveRange, SIGNAL(stateChanged(int)), this, SLOT(slotSetAboveRange(int)));
// TODO: Move this over the the scheme dialog
@@ -69,6 +70,12 @@
else
chkEnableBpmDetection->setChecked(false);
+ int iBpmAboveRange = config->getValueString(ConfigKey(CONFIG_KEY,"BPMAboveRangeEnabled")).toInt();
+ if (iBpmAboveRange)
+ chkAboveRange->setChecked(true);
+ else
+ chkAboveRange->setChecked(false);
+
// Set default value for detect BPM on import check box
int iDetectBpmOnImport = config->getValueString(ConfigKey(CONFIG_KEY,"DetectBPMOnImport")).toInt();
if (iDetectBpmOnImport)
@@ -145,6 +152,13 @@
}
+void DlgPrefBpm::slotSetAboveRange(int) {
+ if (chkAboveRange->isChecked())
+ config->set(ConfigKey(CONFIG_KEY,"BPMAboveRangeEnabled"), ConfigValue(1));
+ else
+ config->set(ConfigKey(CONFIG_KEY,"BPMAboveRangeEnabled"), ConfigValue(0));
+}
+
void DlgPrefBpm::slotSetBpmRangeStart(int begin)
{
//config->set(ConfigKey("[BPM]","BPMRangeStart"),ConfigValue(begin));
@@ -260,12 +274,14 @@
{
chkDetectOnImport->setEnabled(true);
chkWriteID3->setEnabled(true);
+ chkAboveRange->setEnabled(true);
grpBpmSchemes->setEnabled(true);
- }
+ }
else
{
chkDetectOnImport->setEnabled(false);
chkWriteID3->setEnabled(false);
+ chkAboveRange->setEnabled(false);
grpBpmSchemes->setEnabled(false);
}
Index: src/soundsourcemp3.cpp
===================================================================
--- src/soundsourcemp3.cpp (revision 1979)
+++ src/soundsourcemp3.cpp (working copy)
@@ -457,10 +457,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");
@@ -478,6 +478,15 @@
getField(tag,"TPE1",&s);
if (s.length()>2)
Track->setArtist(s);
+ s="";
+ getField(tag,"TBPM",&s);
+ float bpm = 0;
+ if (s.length()>1) bpm = str2bpm(s);
+ if(bpm > 0) {
+ Track->setBpm(bpm);
+ Track->setBpmConfirm(true);
+ }
+ Track->setHeaderParsed(true);
/*
// On some tracks this segfaults. TLEN is very seldom used anyway...
Index: src/soundsourceoggvorbis.cpp
===================================================================
--- src/soundsourceoggvorbis.cpp (revision 1979)
+++ src/soundsourceoggvorbis.cpp (working copy)
@@ -182,6 +182,15 @@
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) {
+ float bpm = str2bpm(vorbis_comment_query(comment, "TBPM", 0));
+ if(bpm > 0) {
+ Track->setBpm(bpm);
+ Track->setBpmConfirm(true);
+ }
+ }
+ Track->setHeaderParsed(true);
+
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 1979)
+++ 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,26 @@
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?
+ int defaultrange = m_Config->getValueString(ConfigKey("[BPM]","BPMAboveRangeEnabled")).toInt();
+ BpmDetect bpmd( channels, ( int ) frequency, defaultrange ? MIN_BPM : pScheme->getMinBpm(),
+ defaultrange ? MAX_BPM : 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 +216,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/dlgprefbpm.h
===================================================================
--- src/dlgprefbpm.h (revision 1979)
+++ src/dlgprefbpm.h (working copy)
@@ -32,6 +32,7 @@
void slotSetBpmEnabled(int);
void slotSetBpmRangeStart(int);
void slotSetBpmRangeEnd(int);
+ void slotSetAboveRange(int);
void slotEditBpmScheme();
void slotAddBpmScheme();
Index: src/trackinfoobject.h
===================================================================
--- src/trackinfoobject.h (revision 1979)
+++ src/trackinfoobject.h (working copy)
@@ -87,6 +87,8 @@
bool getBpmConfirm() const;
/** Set BPM confidence */
void setBpmConfirm(bool confirm=true);
+ bool getHeaderParsed() const;
+ void setHeaderParsed(bool parsed = true);
/** Returns the user comment */
QString getComment() const;
/** Sets the user commnet */
@@ -210,6 +212,8 @@
float m_fMaxBpm;
/** True if BPM is confirmed */
bool m_bBpmConfirm;
+ /** True if header was parsed */
+ bool m_bHeaderParsed;
/** Position of first beat in song */
float m_fBeatFirst;
/** Score. Reflects the relative number of times the track has been played */
@@ -239,7 +243,7 @@
ControlObject *m_pControlObjectBpm;
/** Pointer to ControlObject of duration value (only set when the track is loaded in a player) */
ControlObject *m_pControlObjectDuration;
- int iTemp;
+ int iTemp;
/** Bpm Detection Queue */
BpmDetector *m_BpmDetector;
Index: src/soundsource.h
===================================================================
--- src/soundsource.h (revision 1979)
+++ 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/dlgprefbpmdlg.ui
===================================================================
--- src/dlgprefbpmdlg.ui (revision 1979)
+++ src/dlgprefbpmdlg.ui (working copy)
@@ -6,7 +6,7 @@
<x>0</x>
<y>0</y>
<width>427</width>
- <height>411</height>
+ <height>395</height>
</rect>
</property>
<property name="windowTitle" >
@@ -68,6 +68,16 @@
</property>
</widget>
</item>
+ <item row="4" column="0" >
+ <widget class="QCheckBox" name="chkAboveRange" >
+ <property name="toolTip" >
+ <string>If BPM can be detected but not within specified range</string>
+ </property>
+ <property name="text" >
+ <string>Allow BPM above the range</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -140,19 +150,6 @@
</layout>
</widget>
</item>
- <item row="2" column="0" >
- <spacer>
- <property name="orientation" >
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" >
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
Index: src/bpmdetect.cpp
===================================================================
--- src/bpmdetect.cpp (revision 1979)
+++ 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 1979)
+++ 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()
@@ -103,14 +104,14 @@
{
QString qFilename = p->getFilename();
#ifdef __FFMPEGFILE__
- return SoundSourceFFmpeg::ParseHeader(p);;
+ 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 1979)
+++ 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>
@@ -859,6 +860,7 @@
{
QString filename = pTrackInfoObject->getLocation();
+ qDebug() << "Load to player1:" << filename;
if (!filename.isEmpty())
{
// Check if filename is valid
@@ -890,6 +892,9 @@
// Request a new track from the reader:
m_pBuffer1->getReader()->requestNewTrack(m_pTrackPlayer1, bStartFromEndPos);
+ // Read the tags if required
+ if(!m_pTrackPlayer1->getHeaderParsed())
+ SoundSourceProxy::ParseHeader(m_pTrackPlayer1);
// Detect BPM if required
if (m_pTrackPlayer1->getBpmConfirm()== false || m_pTrackPlayer1->getBpm() == 0.)
m_pTrackPlayer1->sendToBpmQueue();
@@ -928,6 +933,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
@@ -959,6 +965,9 @@
// Request a new track from the reader:
m_pBuffer2->getReader()->requestNewTrack(m_pTrackPlayer2, bStartFromEndPos);
+ // Read the tags if required
+ if(!m_pTrackPlayer2->getHeaderParsed())
+ SoundSourceProxy::ParseHeader(m_pTrackPlayer2);
// Detect BPM if required
if (m_pTrackPlayer2->getBpmConfirm()== false || m_pTrackPlayer2->getBpm() == 0.)
m_pTrackPlayer2->sendToBpmQueue();
Index: src/mixxx.cpp
===================================================================
--- src/mixxx.cpp (revision 1979)
+++ src/mixxx.cpp (working copy)
@@ -821,6 +821,7 @@
//Note: Can't #ifdef this because MOC doesn't catch it.
void MixxxApp::slotOptionsVinylControl(bool toggle)
{
+#ifdef __VINYLCONTROL__
//qDebug() << "slotOptionsVinylControl: toggle is " << (int)toggle;
QString device1 = config->getValueString(ConfigKey("[VinylControl]","DeviceInputDeck1"));
@@ -842,6 +843,7 @@
config->set(ConfigKey("[VinylControl]","Enabled"), ConfigValue((int)toggle));
ControlObject::getControl(ConfigKey("[VinylControl]", "Enabled"))->set((int)toggle);
}
+#endif
}
//Also can't ifdef this (MOC again)
Index: src/trackinfoobject.cpp
===================================================================
--- src/trackinfoobject.cpp (revision 1979)
+++ src/trackinfoobject.cpp (working copy)
@@ -54,6 +54,7 @@
m_iTimesPlayed = 0;
m_fBpm = 0.;
m_bBpmConfirm = false;
+ m_bHeaderParsed = false;
m_fBeatFirst = -1.;
m_iScore = 0;
m_iId = -1;
@@ -113,6 +114,7 @@
m_fBpm = XmlParse::selectNodeQString(nodeHeader, "Bpm").toFloat();
m_bBpmConfirm = XmlParse::selectNodeQString(nodeHeader, "BpmConfirm").toInt();
m_fBeatFirst = XmlParse::selectNodeQString(nodeHeader, "BeatFirst").toFloat();
+ m_bHeaderParsed = false;
m_iScore = 0;
m_iId = XmlParse::selectNodeQString(nodeHeader, "Id").toInt();
@@ -517,6 +519,22 @@
m_qMutex.unlock();
}
+bool TrackInfoObject::getHeaderParsed() const
+{
+ m_qMutex.lock();
+ bool bParsed = m_bHeaderParsed;
+ m_qMutex.unlock();
+
+ return bParsed;
+}
+
+void TrackInfoObject::setHeaderParsed(bool parsed)
+{
+ m_qMutex.lock();
+ m_bHeaderParsed = parsed;
+ m_qMutex.unlock();
+}
+
QString TrackInfoObject::getInfo() const
{
m_qMutex.lock();
Index: src/bpmdetect.h
===================================================================
--- src/bpmdetect.h (revision 1979)
+++ 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