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

Proposed by Phillip Whelan
Status: Superseded
Proposed branch: lp:~mixxxdevelopers/mixxx/features_trackbeats
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 793 lines (+451/-23)
12 files modified
mixxx/build/depends.py (+1/-0)
mixxx/res/schema.xml (+8/-0)
mixxx/src/analyserbpm.cpp (+25/-0)
mixxx/src/analyserbpm.h (+1/-0)
mixxx/src/library/dao/trackdao.cpp (+53/-9)
mixxx/src/library/trackcollection.cpp (+1/-1)
mixxx/src/trackbeats.cpp (+235/-0)
mixxx/src/trackbeats.h (+53/-0)
mixxx/src/trackinfoobject.cpp (+16/-0)
mixxx/src/trackinfoobject.h (+12/-0)
mixxx/src/waveform/waveformrenderbeat.cpp (+41/-13)
mixxx/src/waveform/waveformrenderbeat.h (+5/-0)
To merge this branch: bzr merge lp:~mixxxdevelopers/mixxx/features_trackbeats
Reviewer Review Type Date Requested Status
Mixxx Development Team Pending
Review via email: mp+47143@code.launchpad.net

This proposal has been superseded by a proposal from 2011-02-04.

Description of the change

This branch adds the TrackBeats data type for tracking beats as sample offsets. This version uses SoundTouch to derive the beats. A future version will work with aubio, possibly only in the Analyse pane.

To post a comment you must log in.
2527. By Phillip Whelan

Adding a method to TrackBeats for ascertaining if a sample range has a beat. Wishlist #699971.

2528. By Phillip Whelan

Minor fixes to TrackBeats: pass samples/seconds QLists by reference and fix the parameter names for hasBeatsSamples.

2529. By Phillip Whelan

FIX: get waveformrenderbeat to use the samples QList from TrackBeats by reference.

2530. By Phillip Whelan

FIX: get TrackBeatsPointer to correctly work and have it be freed automatically, just as it should.

2531. By Phillip Whelan

FIX: change setTrackBeats call in AnalyserBPM.

2532. By Phillip Whelan

Cleaning up the order of TrackBeats and creating the few remaining Seconds equivalent functions.

2533. By Phillip Whelan

Commenting of functions and addition of the last seconds equivalent functions. Also adding removeBeatSample function and it's seconds equivalent.

2534. By Phillip Whelan

Removing unecessary white space.

2535. By Phillip Whelan

Tweaks to adding and removing samples.

2536. By RJ Skerry-Ryan

Add Beats abstract interface with a BeatGrid implementation.

2537. By RJ Skerry-Ryan

Tighten up the Beats/BeatGrid interface a little bit. Add a reimplementation of TrackBeats as BeatMatrix

2538. By RJ Skerry-Ryan

Remove TrackBeats. Change TIO, Analyser, and Waveform to use Beats generic interface instead. Add versioned serialization to TrackDAO. Move creation/destruction of the Beats variants into a BeatFactory class. Most awesome class name, ever.

2539. By RJ Skerry-Ryan

Fix lack of deletion of RenderObjects in WaveformRenderer

2540. By RJ Skerry-Ryan

Add destructor traces for TIO. Delete the WaveformRenderer.

2541. By RJ Skerry-Ryan

Tracks are no longer being deleted. Turning on deletion print in TrackDAO. Clear the track cache when TrackDAO is destroyed.

2542. By RJ Skerry-Ryan

Fix SQL statement that got munged by a merge.

2543. By RJ Skerry-Ryan

Rephrase :wavesummaryhex binding to match :beats

2544. By RJ Skerry-Ryan

Doh. Forgot to add BeatFactory files

2545. By Phillip Whelan

FIX: error in ternary syntax.

2546. By Phillip Whelan

FIX: generate BeatGrid if we do not have a beatgrid but we have detected the BPM of the song already.

2547. By Phillip Whelan

Various changes will bring the new Beats classes into feature parity with the
old trackbeats code.

  * beatfactory.cpp: include beatgrid.h and beatmatrix.h because forward
                     declaration proved to not be enough.
  * beats.h, beatgrid.h, beatmatrix.h: adding findNthBeat method.
  * beatgrid.cpp: many changes and fixes, including adding findNthBeat.
      - implement findNthBeat. calculate the beat using m_dFirstBeat.
      - reimplement findPrevBeat and findNextBeat using findNthBeat.
      - replace all other beat calculations with calls to findNextBeat.
  * beatmatrix.cpp:
      - implement findNthBeat.
      - reimplement findPrevBeat and findNextBeat using findNthBeat.

2548. By RJ Skerry-Ryan

Add language to beats.h that makes clear the behavior of findNextBeat and findPrevBeat when dSamples refers to the location of a beat.

2549. By RJ Skerry-Ryan

BeatMatrix::findNthBeat fixes for going in the negative direction. Changed variable from offset to n and updated comments to reflect that.

2550. By RJ Skerry-Ryan

Fixup BeatGrid::findNextBeat a little bit. It wasn't doing the previous direction correctly.

2551. By RJ Skerry-Ryan

Add some parameter checks to BeatGrid.

2552. By RJ Skerry-Ryan

Move BeatGrid legacy check from AnalyserBpm to TrackDAO so that any track loaded from DB will get a BeatGrid if it does not have one already and already has a BPM (or if the file metadata has a stored BPM)

2553. By Phillip Whelan

Merging from trunk.

2554. By RJ Skerry-Ryan

Move beat-related classes to track/

2555. By Phillip Whelan

Merging from Repository.

2556. By Phillip Whelan

Use a struct for (de)serializing the beat, for beatgrid, from the database.

2557. By Phillip Whelan

Undo application of SSE3 patch (not the time nor the place for it).

2558. By Phillip Whelan

Make the QByteArray argument to readByteArray and both Beat classes a const.

2559. By Albert Santoni

Rename struct to BeatGridData and fixed QByteArray pointer leak in TrackDAO.

2560. By Albert Santoni

Merge from trunk

2561. By Albert Santoni

Merge from trunk

2562. By Phillip Whelan

FIX: change track beats code to use sample offsets instead of frame offsets. this way the code fits in with the rest of the codebase.

2563. By Phillip Whelan

Comments for beatgrid units, contributed by Albert Santoni.

2564. By Phillip Whelan

Change beatgrid to serialize in frame offsets but use sample offsets at runtime.

2565. By Phillip Whelan

Cleaning up some cruft left over from earlier shift to using sample offsets at runtime.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/build/depends.py'
--- mixxx/build/depends.py 2011-01-07 05:29:17 +0000
+++ mixxx/build/depends.py 2011-01-22 04:23:54 +0000
@@ -520,6 +520,7 @@
520520
521 "sampleutil.cpp",521 "sampleutil.cpp",
522 "trackinfoobject.cpp",522 "trackinfoobject.cpp",
523 "trackbeats.cpp",
523 "baseplayer.cpp",524 "baseplayer.cpp",
524 "basetrackplayer.cpp",525 "basetrackplayer.cpp",
525 "deck.cpp",526 "deck.cpp",
526527
=== modified file 'mixxx/res/schema.xml'
--- mixxx/res/schema.xml 2011-01-06 13:53:48 +0000
+++ mixxx/res/schema.xml 2011-01-22 04:23:54 +0000
@@ -228,4 +228,12 @@
228 );228 );
229 </sql>229 </sql>
230 </revision>230 </revision>
231 <revision version="10" min_compatible="3">
232 <description>
233 Add beats column to library table.
234 </description>
235 <sql>
236 ALTER TABLE Library ADD COLUMN beats BLOB;
237 </sql>
238 </revision>
231</schema>239</schema>
232240
=== modified file 'mixxx/src/analyserbpm.cpp'
--- mixxx/src/analyserbpm.cpp 2010-10-19 01:35:26 +0000
+++ mixxx/src/analyserbpm.cpp 2011-01-22 04:23:54 +0000
@@ -3,6 +3,7 @@
33
4#include "BPMDetect.h"4#include "BPMDetect.h"
5#include "trackinfoobject.h"5#include "trackinfoobject.h"
6#include "trackbeats.h"
6#include "analyserbpm.h"7#include "analyserbpm.h"
78
89
@@ -29,6 +30,13 @@
29 // defaultrange ? MIN_BPM : m_iMinBpm,30 // defaultrange ? MIN_BPM : m_iMinBpm,
30 // defaultrange ? MAX_BPM : m_iMaxBpm);31 // defaultrange ? MAX_BPM : m_iMaxBpm);
31 }32 }
33 else if ( tio->getBpm() != 0. )
34 {
35 // Make sure we have the track beats loaded or generate them.
36 TrackBeatsPointer pTrackBeats = tio->getTrackBeats();
37 if ( pTrackBeats == NULL )
38 doBeats(tio);
39 }
32}40}
3341
3442
@@ -58,6 +66,21 @@
58 return BPM;66 return BPM;
59}67}
6068
69void AnalyserBPM::doBeats(TrackPointer tio)
70{
71 TrackBeats *pTrackBeats = new TrackBeats(tio);
72 float seconds;
73 float duration = (float)tio->getDuration();
74 float inc = 60. / tio->getBpm();
75
76 for (seconds = 0; seconds < duration; seconds += inc)
77 {
78 pTrackBeats->addBeatSeconds(seconds);
79 }
80
81 tio->setTrackBeats(pTrackBeats, TRUE);
82}
83
61void AnalyserBPM::finalise(TrackPointer tio) {84void AnalyserBPM::finalise(TrackPointer tio) {
62 // Check if BPM detection is enabled85 // Check if BPM detection is enabled
63 if(m_pDetector == NULL) {86 if(m_pDetector == NULL) {
@@ -71,6 +94,8 @@
7194
72 tio->setBpm(newbpm);95 tio->setBpm(newbpm);
73 tio->setBpmConfirm();96 tio->setBpmConfirm();
97
98 doBeats(tio);
74 //if(pBpmReceiver) {99 //if(pBpmReceiver) {
75 //pBpmReceiver->setComplete(tio, false, bpm);100 //pBpmReceiver->setComplete(tio, false, bpm);
76 //}101 //}
77102
=== modified file 'mixxx/src/analyserbpm.h'
--- mixxx/src/analyserbpm.h 2010-07-15 19:07:16 +0000
+++ mixxx/src/analyserbpm.h 2011-01-22 04:23:54 +0000
@@ -17,6 +17,7 @@
1717
18 private:18 private:
19 float correctBPM(float BPM, int min, int max, int aboveRange);19 float correctBPM(float BPM, int min, int max, int aboveRange);
20 void doBeats(TrackPointer tio);
2021
21 ConfigObject<ConfigValue> *m_pConfig;22 ConfigObject<ConfigValue> *m_pConfig;
22 soundtouch::BPMDetect *m_pDetector;23 soundtouch::BPMDetect *m_pDetector;
2324
=== modified file 'mixxx/src/library/dao/trackdao.cpp'
--- mixxx/src/library/dao/trackdao.cpp 2010-12-02 22:17:33 +0000
+++ mixxx/src/library/dao/trackdao.cpp 2011-01-22 04:23:54 +0000
@@ -3,6 +3,7 @@
3#include <QtCore>3#include <QtCore>
4#include <QtSql>4#include <QtSql>
5#include "trackinfoobject.h"5#include "trackinfoobject.h"
6#include "trackbeats.h"
6#include "library/dao/trackdao.h"7#include "library/dao/trackdao.h"
7#include "audiotagger.h"8#include "audiotagger.h"
89
@@ -215,16 +216,16 @@
215216
216void TrackDAO::prepareLibraryInsert(QSqlQuery& query) {217void TrackDAO::prepareLibraryInsert(QSqlQuery& query) {
217 query.prepare("INSERT INTO library (artist, title, album, year, genre, tracknumber, "218 query.prepare("INSERT INTO library (artist, title, album, year, genre, tracknumber, "
218 "filetype, location, comment, url, duration, rating, key, "219 "filetype, location, comment, url, duration, "
219 "bitrate, samplerate, cuepoint, bpm, replaygain, wavesummaryhex, "220 "bitrate, samplerate, cuepoint, bpm, replaygain, wavesummaryhex, "
220 "timesplayed, "221 "timesplayed, "
221 "channels, mixxx_deleted, header_parsed) "222 "channels, mixxx_deleted, header_parsed, beats) "
222 "VALUES (:artist, "223 "VALUES (:artist, "
223 ":title, :album, :year, :genre, :tracknumber, "224 ":title, :album, :year, :genre, :tracknumber, "
224 ":filetype, :location, :comment, :url, :duration, :rating, :key,"225 ":filetype, :location, :comment, :url, :duration, "
225 ":bitrate, :samplerate, :cuepoint, :bpm, :replaygain, :wavesummaryhex, "226 ":bitrate, :samplerate, :cuepoint, :bpm, :replaygain, :wavesummaryhex, "
226 ":timesplayed, "227 ":timesplayed, "
227 ":channels, :mixxx_deleted, :header_parsed)");228 ":channels, :mixxx_deleted, :header_parsed, :beats)");
228}229}
229230
230void TrackDAO::bindTrackToLibraryInsert(QSqlQuery& query, TrackInfoObject* pTrack, int trackLocationId) {231void TrackDAO::bindTrackToLibraryInsert(QSqlQuery& query, TrackInfoObject* pTrack, int trackLocationId) {
@@ -249,11 +250,23 @@
249 const QByteArray* pWaveSummary = pTrack->getWaveSummary();250 const QByteArray* pWaveSummary = pTrack->getWaveSummary();
250 if (pWaveSummary) //Avoid null pointer deref251 if (pWaveSummary) //Avoid null pointer deref
251 query.bindValue(":wavesummaryhex", *pWaveSummary);252 query.bindValue(":wavesummaryhex", *pWaveSummary);
253 else
254 query.bindValue(":wavesummaryhex", QVariant(QVariant::ByteArray));
252 query.bindValue(":timesplayed", pTrack->getTimesPlayed());255 query.bindValue(":timesplayed", pTrack->getTimesPlayed());
253 //query.bindValue(":datetime_added", pTrack->getDateAdded());256 //query.bindValue(":datetime_added", pTrack->getDateAdded());
254 query.bindValue(":channels", pTrack->getChannels());257 query.bindValue(":channels", pTrack->getChannels());
255 query.bindValue(":mixxx_deleted", 0);258 query.bindValue(":mixxx_deleted", 0);
256 query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0);259 query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0);
260 const QByteArray *pBeatsBlob = NULL;
261 TrackBeatsPointer pBeats = pTrack->getTrackBeats();
262 if ( pBeats )
263 pBeatsBlob = pBeats->serializeToBlob();
264
265 if ( pBeatsBlob )
266 query.bindValue(":beats", *pBeatsBlob);
267 else
268 query.bindValue(":beats", QVariant(QVariant::ByteArray));
269
257}270}
258271
259void TrackDAO::addTracks(QList<TrackInfoObject*> tracksToAdd) {272void TrackDAO::addTracks(QList<TrackInfoObject*> tracksToAdd) {
@@ -306,7 +319,8 @@
306 bindTrackToLibraryInsert(query, pTrack, pTrack->getId());319 bindTrackToLibraryInsert(query, pTrack, pTrack->getId());
307320
308 if (!query.exec()) {321 if (!query.exec()) {
309 qDebug() << "Failed to INSERT new track into library"322 qDebug() << "Failed to INSERT new track into library:"
323 << pTrack->getFilename()
310 << __FILE__ << __LINE__ << query.lastError();324 << __FILE__ << __LINE__ << query.lastError();
311 m_database.rollback();325 m_database.rollback();
312 return;326 return;
@@ -550,6 +564,7 @@
550 bool header_parsed = query.value(query.record().indexOf("header_parsed")).toBool();564 bool header_parsed = query.value(query.record().indexOf("header_parsed")).toBool();
551565
552 TrackInfoObject* track = new TrackInfoObject(location, false);566 TrackInfoObject* track = new TrackInfoObject(location, false);
567 TrackPointer pTrack = TrackPointer(track, this->deleteTrack);
553568
554 // TIO already stats the file to see if it exists, what its length is,569 // TIO already stats the file to see if it exists, what its length is,
555 // etc. So don't bother setting it.570 // etc. So don't bother setting it.
@@ -575,6 +590,19 @@
575 track->setReplayGain(replaygain.toFloat());590 track->setReplayGain(replaygain.toFloat());
576 track->setWaveSummary(wavesummaryhex, false);591 track->setWaveSummary(wavesummaryhex, false);
577 delete wavesummaryhex;592 delete wavesummaryhex;
593
594 // TrackBeats needs the samplerate for it's constructor
595 // so we do it all right here.
596 QByteArray* beatsblob = new QByteArray(
597 query.value(query.record().indexOf("beats")).toByteArray());
598 if ( beatsblob->size() > 1 )
599 {
600 TrackBeats* pTrackBeats = new TrackBeats(pTrack);
601 pTrackBeats->unserializeFromBlob(beatsblob);
602 track->setTrackBeats(pTrackBeats, false);
603 }
604 delete beatsblob;
605
578 track->setTimesPlayed(timesplayed);606 track->setTimesPlayed(timesplayed);
579 track->setPlayed(played);607 track->setPlayed(played);
580 track->setChannels(channels);608 track->setChannels(channels);
@@ -593,8 +621,6 @@
593 connect(track, SIGNAL(save()),621 connect(track, SIGNAL(save()),
594 this, SLOT(slotTrackSave()));622 this, SLOT(slotTrackSave()));
595623
596 TrackPointer pTrack = TrackPointer(track, this->deleteTrack);
597
598 // Automatic conversion to a weak pointer624 // Automatic conversion to a weak pointer
599 m_tracks[trackId] = pTrack;625 m_tracks[trackId] = pTrack;
600 m_trackCache.insert(trackId, new TrackPointer(pTrack));626 m_trackCache.insert(trackId, new TrackPointer(pTrack));
@@ -647,7 +673,17 @@
647 time.start();673 time.start();
648 QSqlQuery query(m_database);674 QSqlQuery query(m_database);
649675
650 query.prepare("SELECT library.id, artist, title, album, year, genre, tracknumber, filetype, rating, key, track_locations.location as location, track_locations.filesize as filesize, comment, url, duration, bitrate, samplerate, cuepoint, bpm, replaygain, wavesummaryhex, channels, header_parsed, timesplayed, played FROM Library INNER JOIN track_locations ON library.location = track_locations.id WHERE library.id=" + QString("%1").arg(id));676 query.prepare(
677 "SELECT library.id, artist, title, album, year, genre, tracknumber, "
678 "filetype, rating, key, track_locations.location as location, "
679 "track_locations.filesize as filesize, comment, url, duration, bitrate, "
680 "samplerate, cuepoint, bpm, replaygain, wavesummaryhex, channels, "
681 "header_parsed, timesplayed, played, beats "
682 "FROM Library "
683 "INNER JOIN track_locations "
684 "ON library.location = track_locations.id "
685 "WHERE library.id=" + QString("%1").arg(id)
686 );
651687
652 TrackPointer pTrack;688 TrackPointer pTrack;
653689
@@ -686,7 +722,8 @@
686 "bitrate=:bitrate, samplerate=:samplerate, cuepoint=:cuepoint, "722 "bitrate=:bitrate, samplerate=:samplerate, cuepoint=:cuepoint, "
687 "bpm=:bpm, replaygain=:replaygain, wavesummaryhex=:wavesummaryhex, "723 "bpm=:bpm, replaygain=:replaygain, wavesummaryhex=:wavesummaryhex, "
688 "timesplayed=:timesplayed, played=:played, "724 "timesplayed=:timesplayed, played=:played, "
689 "channels=:channels, header_parsed=:header_parsed "725 "channels=:channels, header_parsed=:header_parsed, "
726 "beats=:beats "
690 "WHERE id="+QString("%1").arg(trackId));727 "WHERE id="+QString("%1").arg(trackId));
691 query.bindValue(":artist", pTrack->getArtist());728 query.bindValue(":artist", pTrack->getArtist());
692 query.bindValue(":title", pTrack->getTitle());729 query.bindValue(":title", pTrack->getTitle());
@@ -713,6 +750,13 @@
713 query.bindValue(":channels", pTrack->getChannels());750 query.bindValue(":channels", pTrack->getChannels());
714 query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0);751 query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0);
715 //query.bindValue(":location", pTrack->getLocation());752 //query.bindValue(":location", pTrack->getLocation());
753 TrackBeatsPointer pBeats = pTrack->getTrackBeats();
754 if ( pBeats )
755 {
756 const QByteArray *pBeatsBlob = pBeats->serializeToBlob();
757 if ( pBeatsBlob )
758 query.bindValue(":beats", *pBeatsBlob);
759 }
716760
717 if (!query.exec()) {761 if (!query.exec()) {
718 qDebug() << query.lastError();762 qDebug() << query.lastError();
719763
=== modified file 'mixxx/src/library/trackcollection.cpp'
--- mixxx/src/library/trackcollection.cpp 2010-12-22 21:23:09 +0000
+++ mixxx/src/library/trackcollection.cpp 2011-01-22 04:23:54 +0000
@@ -68,7 +68,7 @@
68 return false;68 return false;
69 }69 }
7070
71 int requiredSchemaVersion = 9;71 int requiredSchemaVersion = 10;
72 if (!SchemaManager::upgradeToSchemaVersion(m_pConfig, m_db,72 if (!SchemaManager::upgradeToSchemaVersion(m_pConfig, m_db,
73 requiredSchemaVersion)) {73 requiredSchemaVersion)) {
74 QMessageBox::warning(0, tr("Cannot upgrade database schema"),74 QMessageBox::warning(0, tr("Cannot upgrade database schema"),
7575
=== added file 'mixxx/src/trackbeats.cpp'
--- mixxx/src/trackbeats.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/trackbeats.cpp 2011-01-22 04:23:54 +0000
@@ -0,0 +1,235 @@
1#include <QtDebug>
2
3#include "defs.h"
4#include "trackbeats.h"
5#include "trackinfoobject.h"
6
7
8TrackBeats::TrackBeats(TrackPointer tio) : m_qMutex(QMutex::Recursive)
9{
10 m_iSampleRate = tio->getSampleRate();
11}
12
13TrackBeats::~TrackBeats()
14{
15}
16
17void TrackBeats::addBeatSeconds(double beat)
18{
19 QMutexLocker lock(&m_qMutex);
20 addBeatSample((int)round(beat * m_iSampleRate));
21}
22
23double TrackBeats::findNextBeatSeconds(double beat) const
24{
25 QMutexLocker lock(&m_qMutex);
26 int sample = (int) round(beat * m_iSampleRate);
27 return findNextBeatSample(sample) / m_iSampleRate;
28}
29
30double TrackBeats::findPrevBeatSeconds(double beat) const
31{
32 QMutexLocker lock(&m_qMutex);
33 int sample = (int) round(beat * m_iSampleRate);
34 return findPrevBeatSample(sample) / m_iSampleRate;
35}
36
37QList<double> TrackBeats::findBeatsSeconds(double start, double stop) const
38{
39 QMutexLocker lock(&m_qMutex);
40 QList<double> ret;
41 QList<int> samples;
42 int begin = round(start * m_iSampleRate);
43 int end = round(stop * m_iSampleRate);
44 int i;
45
46
47 samples = findBeatsSamples(begin, end);
48 for (i = 0; i < samples.size(); i++)
49 {
50 //qDebug() << "Sample:" << samples.at(i) << "SampleRate:" << m_iSampleRate
51 // << "Seconds:" << ((double)samples.at(i) / (double)m_iSampleRate);
52
53 ret.append((double)samples.at(i) / (double)m_iSampleRate);
54 }
55
56 return ret;
57}
58
59int TrackBeats::getBeatCount() const
60{
61 QMutexLocker lock(&m_qMutex);
62 return m_beats.size();
63}
64
65void TrackBeats::dumpBeats()
66{
67 QMutexLocker lock(&m_qMutex);
68 QMapIterator<int, int> iter(m_beats);
69
70 do {
71 iter.next();
72 qDebug() << "TrackBeat Sample:" << iter.value();
73
74 } while(iter.hasNext());
75}
76
77int TrackBeats::sampleIndex(int sample) const
78{
79 QMutexLocker lock(&m_qMutex);
80 return (int) round(sample / (m_iSampleRate * 10));
81}
82
83void TrackBeats::addBeatSample(int sample)
84{
85 QMutexLocker lock(&m_qMutex);
86 int index = sampleIndex(sample);
87
88
89 if ((m_beatIndex.size()-1) < index )
90 {
91 int i;
92
93
94 for (i = m_beatIndex.size() - 1; i < index; i++ )
95 m_beatIndex.append(sample);
96
97 m_beatIndex.append(sample);
98 }
99
100 m_beats[sample] = sample;
101}
102
103int TrackBeats::findNextBeatSample(int sample) const
104{
105 QMutexLocker lock(&m_qMutex);
106 QMapIterator<int, int> iter(m_beats);
107 int index = sampleIndex(sample);
108
109
110 if (m_beatIndex.size() > index)
111 {
112 // make sure we dont trip on unindex'ed areas (-1)..
113 iter.findNext(m_beatIndex.value(index));
114 do {
115 iter.next();
116 } while((iter.hasNext()) && (iter.value() <= sample));
117
118 return iter.value();
119 }
120
121 return -1;
122}
123
124int TrackBeats::findBeatOffsetSamples(int sample, int offset) const
125{
126 QMutexLocker lock(&m_qMutex);
127 QMapIterator<int, int> iter(m_beats);
128 int index = sampleIndex(sample);
129 int i;
130
131
132 if (m_beatIndex.size() < index)
133 return -1;
134
135
136 iter.findNext(m_beatIndex.value(index));
137 do {
138 iter.next();
139 } while((iter.hasNext()) && (iter.value() <= sample));
140
141 // Backup one just to be before the marker
142 if ((iter.hasPrevious()) && (iter.value() > sample))
143 iter.previous();
144
145 // Find the offset from the current beat
146 if ( offset > 0 )
147 {
148 for (i = 0; i < offset && iter.hasNext(); i++)
149 iter.next();
150 }
151 else if ( offset < 0 )
152 {
153 for (i = offset * -1; i > 0 && iter.hasPrevious(); i--)
154 iter.previous();
155 }
156
157 return iter.value();
158}
159
160int TrackBeats::findPrevBeatSample(int sample) const
161{
162 QMutexLocker lock(&m_qMutex);
163 QMapIterator<int, int> iter(m_beats);
164 int index = sampleIndex(sample);
165
166 if (m_beatIndex.size() > index)
167 {
168 iter.findNext(m_beatIndex.value(index));
169 do {
170 iter.previous();
171 } while((iter.hasPrevious()) && (iter.value() >= sample));
172
173 return iter.value();
174 }
175
176 return -1;
177}
178
179QList<int> TrackBeats::findBeatsSamples(int start, int stop) const
180{
181 QMutexLocker lock(&m_qMutex);
182 QList<int> ret;
183 QMapIterator<int, int> iter(m_beats);
184 int index = sampleIndex(start);
185
186
187 if (iter.findNext(m_beatIndex.value(index)))
188 {
189 do {
190 if ((iter.value() >= start) && (iter.value() <= stop))
191 ret.append(iter.value());
192
193 iter.next();
194 } while((iter.hasNext()) && (iter.value() <= stop));
195 }
196
197 return ret;
198}
199
200QByteArray *TrackBeats::serializeToBlob()
201{
202 QMutexLocker lock(&m_qMutex);
203 QByteArray *blob;
204 int *buffer = new int[getBeatCount()];
205 int *ptr = buffer;
206 QMapIterator<int, int> iter(m_beats);
207
208
209 iter.next();
210
211 while ( iter.hasNext())
212 {
213 *ptr++ = iter.value();
214 iter.next();
215 }
216
217 blob = new QByteArray((char *)buffer, getBeatCount() * sizeof(int));
218 delete []buffer;
219
220 return blob;
221}
222
223void TrackBeats::unserializeFromBlob(QByteArray *blob)
224{
225 QMutexLocker lock(&m_qMutex);
226 int *ptr = (int *)blob->constData();
227 int i;
228
229
230 for (i = blob->size() / sizeof(int); --i; ptr++)
231 {
232 addBeatSample(*ptr);
233 }
234}
235
0236
=== added file 'mixxx/src/trackbeats.h'
--- mixxx/src/trackbeats.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/trackbeats.h 2011-01-22 04:23:54 +0000
@@ -0,0 +1,53 @@
1#include <QtDebug>
2#include <QList>
3#include <QSharedPointer>
4#include "trackinfoobject.h"
5
6
7#ifndef __TRACK_BEATS_H__
8#define __TRACK_BEATS_H__
9
10class TrackBeats;
11typedef QSharedPointer<TrackBeats> TrackBeatsPointer;
12
13class TrackBeats {
14public:
15 TrackBeats(TrackPointer);
16 virtual ~TrackBeats();
17
18 void addBeatSeconds(double);
19
20 double findNextBeatSeconds(double) const;
21 double findPrevBeatSeconds(double) const;
22 QList<double> findBeatsSeconds(double, double) const;
23
24 int getBeatCount() const;
25 void dumpBeats();
26
27 void addBeatSample(int);
28 int findPrevBeatSample(int) const;
29 int findNextBeatSample(int) const;
30 int findBeatOffsetSamples(int sample, int offset) const;
31 QList<int> findBeatsSamples(int, int) const;
32 QByteArray *serializeToBlob();
33 void unserializeFromBlob(QByteArray *blob);
34
35private:
36 /** Find the Samples Index */
37 int sampleIndex(int) const;
38
39 /** Sample Rate for this song */
40 int m_iSampleRate;
41 /** Duration of the entire song in seconds */
42 double m_dDuration;
43 /** 10 second index (in samples) */
44 QList<int> m_beatIndex;
45 /** Map of all the beats in samples */
46 QMap<int, int> m_beats;
47 /** Pointer to the related Track */
48 TrackPointer m_track;
49 /** Mutex protecting access to object */
50 mutable QMutex m_qMutex;
51};
52
53#endif
054
=== modified file 'mixxx/src/trackinfoobject.cpp'
--- mixxx/src/trackinfoobject.cpp 2010-11-24 15:20:09 +0000
+++ mixxx/src/trackinfoobject.cpp 2011-01-22 04:23:54 +0000
@@ -341,6 +341,22 @@
341 m_bBpmConfirm = confirm;341 m_bBpmConfirm = confirm;
342}342}
343343
344void TrackInfoObject::setTrackBeats(TrackBeats *beats, bool isDirty)
345{
346 QMutexLocker lock(&m_qMutex);
347 m_pTrackBeats = TrackBeatsPointer(beats);
348 if ( isDirty )
349 setDirty(true);
350
351 emit(trackBeatsUpdated(1));
352}
353
354TrackBeatsPointer TrackInfoObject::getTrackBeats() const
355{
356 QMutexLocker lock(&m_qMutex);
357 return TrackBeatsPointer(m_pTrackBeats);
358}
359
344bool TrackInfoObject::getHeaderParsed() const360bool TrackInfoObject::getHeaderParsed() const
345{361{
346 QMutexLocker lock(&m_qMutex);362 QMutexLocker lock(&m_qMutex);
347363
=== modified file 'mixxx/src/trackinfoobject.h'
--- mixxx/src/trackinfoobject.h 2010-11-17 21:02:24 +0000
+++ mixxx/src/trackinfoobject.h 2011-01-22 04:23:54 +0000
@@ -40,9 +40,11 @@
40class Cue;40class Cue;
4141
42class TrackInfoObject;42class TrackInfoObject;
43class TrackBeats;
4344
44typedef QSharedPointer<TrackInfoObject> TrackPointer;45typedef QSharedPointer<TrackInfoObject> TrackPointer;
45typedef QWeakPointer<TrackInfoObject> TrackWeakPointer;46typedef QWeakPointer<TrackInfoObject> TrackWeakPointer;
47typedef QSharedPointer<TrackBeats> TrackBeatsPointer;
4648
47#include "segmentation.h"49#include "segmentation.h"
4850
@@ -242,6 +244,12 @@
242 /** Set the track's full file path */244 /** Set the track's full file path */
243 void setLocation(QString location);245 void setLocation(QString location);
244246
247 /** Get the Track's Beats list */
248 TrackBeatsPointer getTrackBeats() const;
249
250 /** Set the Track's Beats */
251 void setTrackBeats(TrackBeats* beats, bool isDirty);
252
245 const Segmentation<QString>* getChordData();253 const Segmentation<QString>* getChordData();
246 void setChordData(Segmentation<QString> cd);254 void setChordData(Segmentation<QString> cd);
247255
@@ -251,6 +259,7 @@
251 signals:259 signals:
252 void wavesummaryUpdated(TrackInfoObject*);260 void wavesummaryUpdated(TrackInfoObject*);
253 void bpmUpdated(double bpm);261 void bpmUpdated(double bpm);
262 void trackBeatsUpdated(int);
254 void ReplayGainUpdated(double replaygain);263 void ReplayGainUpdated(double replaygain);
255 void cuesUpdated();264 void cuesUpdated();
256 void changed();265 void changed();
@@ -370,6 +379,9 @@
370 double m_dVisualResampleRate;379 double m_dVisualResampleRate;
371 Segmentation<QString> m_chordData;380 Segmentation<QString> m_chordData;
372381
382 // Storage for the Track's detected beats
383 TrackBeatsPointer m_pTrackBeats;
384
373 friend class TrackDAO;385 friend class TrackDAO;
374};386};
375387
376388
=== modified file 'mixxx/src/waveform/waveformrenderbeat.cpp'
--- mixxx/src/waveform/waveformrenderbeat.cpp 2010-11-25 01:06:46 +0000
+++ mixxx/src/waveform/waveformrenderbeat.cpp 2011-01-22 04:23:54 +0000
@@ -14,6 +14,8 @@
14#include "widget/wskincolor.h"14#include "widget/wskincolor.h"
15#include "widget/wwidget.h"15#include "widget/wwidget.h"
16#include "trackinfoobject.h"16#include "trackinfoobject.h"
17#include "trackbeats.h"
18
1719
18WaveformRenderBeat::WaveformRenderBeat(const char* group, WaveformRenderer *parent)20WaveformRenderBeat::WaveformRenderBeat(const char* group, WaveformRenderer *parent)
19 : m_pParent(parent),21 : m_pParent(parent),
@@ -21,6 +23,7 @@
21 m_pBeatFirst(NULL),23 m_pBeatFirst(NULL),
22 m_pTrackSamples(NULL),24 m_pTrackSamples(NULL),
23 m_pTrack(),25 m_pTrack(),
26 m_pTrackBeats(NULL),
24 m_iWidth(0),27 m_iWidth(0),
25 m_iHeight(0),28 m_iHeight(0),
26 m_dBpm(-1),29 m_dBpm(-1),
@@ -38,6 +41,7 @@
3841
39 m_pTrackSamples = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(group,"track_samples")));42 m_pTrackSamples = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(group,"track_samples")));
40 slotUpdateTrackSamples(m_pTrackSamples->get());43 slotUpdateTrackSamples(m_pTrackSamples->get());
44
41 connect(m_pTrackSamples, SIGNAL(valueChanged(double)),45 connect(m_pTrackSamples, SIGNAL(valueChanged(double)),
42 this, SLOT(slotUpdateTrackSamples(double)));46 this, SLOT(slotUpdateTrackSamples(double)));
43}47}
@@ -58,6 +62,15 @@
58 m_iNumSamples = (int)samples;62 m_iNumSamples = (int)samples;
59}63}
6064
65void WaveformRenderBeat::slotUpdateTrackBeats(int)
66{
67 m_pTrackBeats = m_pTrack->getTrackBeats();
68 if ( m_pTrackBeats )
69 qDebug() << "WaveformRenderBeat :: beats = " << m_pTrackBeats->getBeatCount();
70 else
71 qDebug() << "No WaveformRenderBeat beats list";
72}
73
61void WaveformRenderBeat::resize(int w, int h) {74void WaveformRenderBeat::resize(int w, int h) {
62 m_iWidth = w;75 m_iWidth = w;
63 m_iHeight = h;76 m_iHeight = h;
@@ -91,7 +104,10 @@
91 m_dSamplesPerDownsample = n;104 m_dSamplesPerDownsample = n;
92 m_dSamplesPerPixel = double(f)/z;105 m_dSamplesPerPixel = double(f)/z;
93106
94 //qDebug() << "WaveformRenderBeat sampleRate " << sampleRate << " samplesPerPixel " << m_dSamplesPerPixel;107 // Reset the tracker beats for each new song
108 m_pTrackBeats = pTrack->getTrackBeats();
109 connect(pTrack.data(), SIGNAL(trackBeatsUpdated(int)), this,
110 SLOT(slotUpdateTrackBeats(int)));
95111
96}112}
97113
@@ -108,9 +124,18 @@
108 colorHighlight = WSkinColor::getCorrectColor(colorHighlight);124 colorHighlight = WSkinColor::getCorrectColor(colorHighlight);
109}125}
110126
111127void WaveformRenderBeat::draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double dPlayPos, double rateAdjust)
112void WaveformRenderBeat::draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double dPlayPos, double rateAdjust) {128{
113 if(m_dBpm == -1 || m_dBpm == 0)129 drawTrackBeat(pPainter, event, buffer, dPlayPos, rateAdjust);
130}
131
132void WaveformRenderBeat::drawTrackBeat(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double dPlayPos, double rateAdjust)
133{
134 QList<int> beatSamples;
135 int i;
136
137
138 if ( m_pTrackBeats == NULL )
114 return;139 return;
115140
116 slotUpdateTrackSamples(m_pTrackSamples->get());141 slotUpdateTrackSamples(m_pTrackSamples->get());
@@ -137,11 +162,6 @@
137 // Therefore, sample s is a beat if it satisfies s % 60f/b == 0.162 // Therefore, sample s is a beat if it satisfies s % 60f/b == 0.
138 // where s is a /mono/ sample163 // where s is a /mono/ sample
139164
140 // beat length in samples
141 if(m_dBeatLength <= 0) {
142 m_dBeatLength = 60.0 * m_iSampleRate / m_dBpm;
143 }
144
145 double subpixelsPerPixel = m_pParent->getSubpixelsPerPixel()*(1.0+rateAdjust);165 double subpixelsPerPixel = m_pParent->getSubpixelsPerPixel()*(1.0+rateAdjust);
146 const int oversample = (int)subpixelsPerPixel;166 const int oversample = (int)subpixelsPerPixel;
147167
@@ -163,10 +183,18 @@
163183
164 // snap to the first beat184 // snap to the first beat
165 double curPos = ceilf(basePos/m_dBeatLength)*m_dBeatLength;185 double curPos = ceilf(basePos/m_dBeatLength)*m_dBeatLength;
166
167 bool reset = false;186 bool reset = false;
168 for(;curPos <= endPos; curPos+=m_dBeatLength) {187 int curSample = (int)round(curPos);
169 if(curPos < 0)188 int endSample = (int)round(endPos);
189
190
191 beatSamples = m_pTrackBeats->findBeatsSamples(curSample, endSample);
192
193 for(i = 0; i < beatSamples.size(); i++) {
194 curPos = beatSamples.at(i);
195
196
197 if (curPos < 0)
170 continue;198 continue;
171199
172 // i relative to the current play position in subpixels200 // i relative to the current play position in subpixels
@@ -174,7 +202,7 @@
174202
175 // If i is less than 20 subpixels from center, highlight it.203 // If i is less than 20 subpixels from center, highlight it.
176 if(abs(i) < 20) {204 if(abs(i) < 20) {
177 pPainter->setPen(colorHighlight);205 pPainter->setPen(QColor(255,255,255));
178 reset = true;206 reset = true;
179 }207 }
180208
181209
=== modified file 'mixxx/src/waveform/waveformrenderbeat.h'
--- mixxx/src/waveform/waveformrenderbeat.h 2010-07-15 19:07:16 +0000
+++ mixxx/src/waveform/waveformrenderbeat.h 2011-01-22 04:23:54 +0000
@@ -16,6 +16,7 @@
16class ControlObjectThreadMain;16class ControlObjectThreadMain;
17class WaveformRenderer;17class WaveformRenderer;
18class SoundSourceProxy;18class SoundSourceProxy;
19class TrackBeats;
1920
20class WaveformRenderBeat : public RenderObject {21class WaveformRenderBeat : public RenderObject {
21 Q_OBJECT22 Q_OBJECT
@@ -23,18 +24,21 @@
23 WaveformRenderBeat(const char *group, WaveformRenderer *parent);24 WaveformRenderBeat(const char *group, WaveformRenderer *parent);
24 void resize(int w, int h);25 void resize(int w, int h);
25 void setup(QDomNode node);26 void setup(QDomNode node);
27 void drawTrackBeat(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust);
26 void draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust);28 void draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust);
27 void newTrack(TrackPointer pTrack);29 void newTrack(TrackPointer pTrack);
2830
29public slots:31public slots:
30 void slotUpdateBpm(double bpm);32 void slotUpdateBpm(double bpm);
31 void slotUpdateBeatFirst(double beatfirst);33 void slotUpdateBeatFirst(double beatfirst);
34 void slotUpdateTrackBeats(int);
32 void slotUpdateTrackSamples(double samples);35 void slotUpdateTrackSamples(double samples);
33private:36private:
34 WaveformRenderer *m_pParent;37 WaveformRenderer *m_pParent;
35 ControlObjectThreadMain *m_pBpm;38 ControlObjectThreadMain *m_pBpm;
36 ControlObjectThreadMain *m_pBeatFirst;39 ControlObjectThreadMain *m_pBeatFirst;
37 ControlObjectThreadMain *m_pTrackSamples;40 ControlObjectThreadMain *m_pTrackSamples;
41 TrackBeatsPointer m_pTrackBeats;
38 TrackPointer m_pTrack;42 TrackPointer m_pTrack;
39 int m_iWidth, m_iHeight;43 int m_iWidth, m_iHeight;
40 double m_dBpm;44 double m_dBpm;
@@ -46,6 +50,7 @@
46 double m_dBeatLength;50 double m_dBeatLength;
47 int m_iNumSamples;51 int m_iNumSamples;
48 int m_iSampleRate;52 int m_iSampleRate;
53
49};54};
5055
51#endif56#endif

Subscribers

People subscribed via source and target branches