Merge lp:~mixxxdevelopers/mixxx/features_trackbeats into lp:~mixxxdevelopers/mixxx/trunk
- features_trackbeats
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~mixxxdevelopers/mixxx/features_trackbeats |
Merge into: | lp:~mixxxdevelopers/mixxx/trunk |
Diff against target: |
929 lines (+573/-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 (+349/-0) mixxx/src/trackbeats.h (+59/-0) mixxx/src/trackinfoobject.cpp (+16/-0) mixxx/src/trackinfoobject.h (+12/-0) mixxx/src/waveform/waveformrenderbeat.cpp (+42/-13) mixxx/src/waveform/waveformrenderbeat.h (+6/-0) |
To merge this branch: | bzr merge lp:~mixxxdevelopers/mixxx/features_trackbeats |
Related bugs: | |
Related blueprints: |
TrackBeats
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mixxx Development Team | Pending | ||
Review via email: mp+48583@code.launchpad.net |
This proposal supersedes a proposal from 2011-02-04.
This proposal has been superseded by a proposal from 2011-02-11.
Commit message
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.
- 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
1 | === modified file 'mixxx/build/depends.py' | |||
2 | --- mixxx/build/depends.py 2011-02-09 18:34:37 +0000 | |||
3 | +++ mixxx/build/depends.py 2011-02-11 04:42:13 +0000 | |||
4 | @@ -529,6 +529,7 @@ | |||
5 | 529 | 529 | ||
6 | 530 | "sampleutil.cpp", | 530 | "sampleutil.cpp", |
7 | 531 | "trackinfoobject.cpp", | 531 | "trackinfoobject.cpp", |
8 | 532 | "trackbeats.cpp", | ||
9 | 532 | "baseplayer.cpp", | 533 | "baseplayer.cpp", |
10 | 533 | "basetrackplayer.cpp", | 534 | "basetrackplayer.cpp", |
11 | 534 | "deck.cpp", | 535 | "deck.cpp", |
12 | 535 | 536 | ||
13 | === modified file 'mixxx/res/schema.xml' | |||
14 | --- mixxx/res/schema.xml 2011-01-06 13:53:48 +0000 | |||
15 | +++ mixxx/res/schema.xml 2011-02-11 04:42:13 +0000 | |||
16 | @@ -228,4 +228,12 @@ | |||
17 | 228 | ); | 228 | ); |
18 | 229 | </sql> | 229 | </sql> |
19 | 230 | </revision> | 230 | </revision> |
20 | 231 | <revision version="10" min_compatible="3"> | ||
21 | 232 | <description> | ||
22 | 233 | Add beats column to library table. | ||
23 | 234 | </description> | ||
24 | 235 | <sql> | ||
25 | 236 | ALTER TABLE Library ADD COLUMN beats BLOB; | ||
26 | 237 | </sql> | ||
27 | 238 | </revision> | ||
28 | 231 | </schema> | 239 | </schema> |
29 | 232 | 240 | ||
30 | === modified file 'mixxx/src/analyserbpm.cpp' | |||
31 | --- mixxx/src/analyserbpm.cpp 2010-10-19 01:35:26 +0000 | |||
32 | +++ mixxx/src/analyserbpm.cpp 2011-02-11 04:42:13 +0000 | |||
33 | @@ -3,6 +3,7 @@ | |||
34 | 3 | 3 | ||
35 | 4 | #include "BPMDetect.h" | 4 | #include "BPMDetect.h" |
36 | 5 | #include "trackinfoobject.h" | 5 | #include "trackinfoobject.h" |
37 | 6 | #include "trackbeats.h" | ||
38 | 6 | #include "analyserbpm.h" | 7 | #include "analyserbpm.h" |
39 | 7 | 8 | ||
40 | 8 | 9 | ||
41 | @@ -29,6 +30,13 @@ | |||
42 | 29 | // defaultrange ? MIN_BPM : m_iMinBpm, | 30 | // defaultrange ? MIN_BPM : m_iMinBpm, |
43 | 30 | // defaultrange ? MAX_BPM : m_iMaxBpm); | 31 | // defaultrange ? MAX_BPM : m_iMaxBpm); |
44 | 31 | } | 32 | } |
45 | 33 | else if ( tio->getBpm() != 0. ) | ||
46 | 34 | { | ||
47 | 35 | // Make sure we have the track beats loaded or generate them. | ||
48 | 36 | TrackBeatsPointer pTrackBeats = tio->getTrackBeats(); | ||
49 | 37 | if ( pTrackBeats == NULL ) | ||
50 | 38 | doBeats(tio); | ||
51 | 39 | } | ||
52 | 32 | } | 40 | } |
53 | 33 | 41 | ||
54 | 34 | 42 | ||
55 | @@ -58,6 +66,21 @@ | |||
56 | 58 | return BPM; | 66 | return BPM; |
57 | 59 | } | 67 | } |
58 | 60 | 68 | ||
59 | 69 | void AnalyserBPM::doBeats(TrackPointer tio) | ||
60 | 70 | { | ||
61 | 71 | TrackBeats *pTrackBeats = new TrackBeats(tio); | ||
62 | 72 | float seconds; | ||
63 | 73 | float duration = (float)tio->getDuration(); | ||
64 | 74 | float inc = 60. / tio->getBpm(); | ||
65 | 75 | |||
66 | 76 | for (seconds = 0; seconds < duration; seconds += inc) | ||
67 | 77 | { | ||
68 | 78 | pTrackBeats->addBeatSeconds(seconds); | ||
69 | 79 | } | ||
70 | 80 | |||
71 | 81 | tio->setTrackBeats(TrackBeatsPointer(pTrackBeats, &QObject::deleteLater), TRUE); | ||
72 | 82 | } | ||
73 | 83 | |||
74 | 61 | void AnalyserBPM::finalise(TrackPointer tio) { | 84 | void AnalyserBPM::finalise(TrackPointer tio) { |
75 | 62 | // Check if BPM detection is enabled | 85 | // Check if BPM detection is enabled |
76 | 63 | if(m_pDetector == NULL) { | 86 | if(m_pDetector == NULL) { |
77 | @@ -71,6 +94,8 @@ | |||
78 | 71 | 94 | ||
79 | 72 | tio->setBpm(newbpm); | 95 | tio->setBpm(newbpm); |
80 | 73 | tio->setBpmConfirm(); | 96 | tio->setBpmConfirm(); |
81 | 97 | |||
82 | 98 | doBeats(tio); | ||
83 | 74 | //if(pBpmReceiver) { | 99 | //if(pBpmReceiver) { |
84 | 75 | //pBpmReceiver->setComplete(tio, false, bpm); | 100 | //pBpmReceiver->setComplete(tio, false, bpm); |
85 | 76 | //} | 101 | //} |
86 | 77 | 102 | ||
87 | === modified file 'mixxx/src/analyserbpm.h' | |||
88 | --- mixxx/src/analyserbpm.h 2010-07-15 19:07:16 +0000 | |||
89 | +++ mixxx/src/analyserbpm.h 2011-02-11 04:42:13 +0000 | |||
90 | @@ -17,6 +17,7 @@ | |||
91 | 17 | 17 | ||
92 | 18 | private: | 18 | private: |
93 | 19 | float correctBPM(float BPM, int min, int max, int aboveRange); | 19 | float correctBPM(float BPM, int min, int max, int aboveRange); |
94 | 20 | void doBeats(TrackPointer tio); | ||
95 | 20 | 21 | ||
96 | 21 | ConfigObject<ConfigValue> *m_pConfig; | 22 | ConfigObject<ConfigValue> *m_pConfig; |
97 | 22 | soundtouch::BPMDetect *m_pDetector; | 23 | soundtouch::BPMDetect *m_pDetector; |
98 | 23 | 24 | ||
99 | === modified file 'mixxx/src/library/dao/trackdao.cpp' | |||
100 | --- mixxx/src/library/dao/trackdao.cpp 2010-12-02 22:17:33 +0000 | |||
101 | +++ mixxx/src/library/dao/trackdao.cpp 2011-02-11 04:42:13 +0000 | |||
102 | @@ -3,6 +3,7 @@ | |||
103 | 3 | #include <QtCore> | 3 | #include <QtCore> |
104 | 4 | #include <QtSql> | 4 | #include <QtSql> |
105 | 5 | #include "trackinfoobject.h" | 5 | #include "trackinfoobject.h" |
106 | 6 | #include "trackbeats.h" | ||
107 | 6 | #include "library/dao/trackdao.h" | 7 | #include "library/dao/trackdao.h" |
108 | 7 | #include "audiotagger.h" | 8 | #include "audiotagger.h" |
109 | 8 | 9 | ||
110 | @@ -215,16 +216,16 @@ | |||
111 | 215 | 216 | ||
112 | 216 | void TrackDAO::prepareLibraryInsert(QSqlQuery& query) { | 217 | void TrackDAO::prepareLibraryInsert(QSqlQuery& query) { |
113 | 217 | query.prepare("INSERT INTO library (artist, title, album, year, genre, tracknumber, " | 218 | query.prepare("INSERT INTO library (artist, title, album, year, genre, tracknumber, " |
115 | 218 | "filetype, location, comment, url, duration, rating, key, " | 219 | "filetype, location, comment, url, duration, " |
116 | 219 | "bitrate, samplerate, cuepoint, bpm, replaygain, wavesummaryhex, " | 220 | "bitrate, samplerate, cuepoint, bpm, replaygain, wavesummaryhex, " |
117 | 220 | "timesplayed, " | 221 | "timesplayed, " |
119 | 221 | "channels, mixxx_deleted, header_parsed) " | 222 | "channels, mixxx_deleted, header_parsed, beats) " |
120 | 222 | "VALUES (:artist, " | 223 | "VALUES (:artist, " |
121 | 223 | ":title, :album, :year, :genre, :tracknumber, " | 224 | ":title, :album, :year, :genre, :tracknumber, " |
123 | 224 | ":filetype, :location, :comment, :url, :duration, :rating, :key," | 225 | ":filetype, :location, :comment, :url, :duration, " |
124 | 225 | ":bitrate, :samplerate, :cuepoint, :bpm, :replaygain, :wavesummaryhex, " | 226 | ":bitrate, :samplerate, :cuepoint, :bpm, :replaygain, :wavesummaryhex, " |
125 | 226 | ":timesplayed, " | 227 | ":timesplayed, " |
127 | 227 | ":channels, :mixxx_deleted, :header_parsed)"); | 228 | ":channels, :mixxx_deleted, :header_parsed, :beats)"); |
128 | 228 | } | 229 | } |
129 | 229 | 230 | ||
130 | 230 | void TrackDAO::bindTrackToLibraryInsert(QSqlQuery& query, TrackInfoObject* pTrack, int trackLocationId) { | 231 | void TrackDAO::bindTrackToLibraryInsert(QSqlQuery& query, TrackInfoObject* pTrack, int trackLocationId) { |
131 | @@ -249,11 +250,23 @@ | |||
132 | 249 | const QByteArray* pWaveSummary = pTrack->getWaveSummary(); | 250 | const QByteArray* pWaveSummary = pTrack->getWaveSummary(); |
133 | 250 | if (pWaveSummary) //Avoid null pointer deref | 251 | if (pWaveSummary) //Avoid null pointer deref |
134 | 251 | query.bindValue(":wavesummaryhex", *pWaveSummary); | 252 | query.bindValue(":wavesummaryhex", *pWaveSummary); |
135 | 253 | else | ||
136 | 254 | query.bindValue(":wavesummaryhex", QVariant(QVariant::ByteArray)); | ||
137 | 252 | query.bindValue(":timesplayed", pTrack->getTimesPlayed()); | 255 | query.bindValue(":timesplayed", pTrack->getTimesPlayed()); |
138 | 253 | //query.bindValue(":datetime_added", pTrack->getDateAdded()); | 256 | //query.bindValue(":datetime_added", pTrack->getDateAdded()); |
139 | 254 | query.bindValue(":channels", pTrack->getChannels()); | 257 | query.bindValue(":channels", pTrack->getChannels()); |
140 | 255 | query.bindValue(":mixxx_deleted", 0); | 258 | query.bindValue(":mixxx_deleted", 0); |
141 | 256 | query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0); | 259 | query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0); |
142 | 260 | const QByteArray *pBeatsBlob = NULL; | ||
143 | 261 | TrackBeatsPointer pBeats = pTrack->getTrackBeats(); | ||
144 | 262 | if ( pBeats ) | ||
145 | 263 | pBeatsBlob = pBeats->serializeToBlob(); | ||
146 | 264 | |||
147 | 265 | if ( pBeatsBlob ) | ||
148 | 266 | query.bindValue(":beats", *pBeatsBlob); | ||
149 | 267 | else | ||
150 | 268 | query.bindValue(":beats", QVariant(QVariant::ByteArray)); | ||
151 | 269 | |||
152 | 257 | } | 270 | } |
153 | 258 | 271 | ||
154 | 259 | void TrackDAO::addTracks(QList<TrackInfoObject*> tracksToAdd) { | 272 | void TrackDAO::addTracks(QList<TrackInfoObject*> tracksToAdd) { |
155 | @@ -306,7 +319,8 @@ | |||
156 | 306 | bindTrackToLibraryInsert(query, pTrack, pTrack->getId()); | 319 | bindTrackToLibraryInsert(query, pTrack, pTrack->getId()); |
157 | 307 | 320 | ||
158 | 308 | if (!query.exec()) { | 321 | if (!query.exec()) { |
160 | 309 | qDebug() << "Failed to INSERT new track into library" | 322 | qDebug() << "Failed to INSERT new track into library:" |
161 | 323 | << pTrack->getFilename() | ||
162 | 310 | << __FILE__ << __LINE__ << query.lastError(); | 324 | << __FILE__ << __LINE__ << query.lastError(); |
163 | 311 | m_database.rollback(); | 325 | m_database.rollback(); |
164 | 312 | return; | 326 | return; |
165 | @@ -550,6 +564,7 @@ | |||
166 | 550 | bool header_parsed = query.value(query.record().indexOf("header_parsed")).toBool(); | 564 | bool header_parsed = query.value(query.record().indexOf("header_parsed")).toBool(); |
167 | 551 | 565 | ||
168 | 552 | TrackInfoObject* track = new TrackInfoObject(location, false); | 566 | TrackInfoObject* track = new TrackInfoObject(location, false); |
169 | 567 | TrackPointer pTrack = TrackPointer(track, this->deleteTrack); | ||
170 | 553 | 568 | ||
171 | 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, |
172 | 555 | // etc. So don't bother setting it. | 570 | // etc. So don't bother setting it. |
173 | @@ -575,6 +590,19 @@ | |||
174 | 575 | track->setReplayGain(replaygain.toFloat()); | 590 | track->setReplayGain(replaygain.toFloat()); |
175 | 576 | track->setWaveSummary(wavesummaryhex, false); | 591 | track->setWaveSummary(wavesummaryhex, false); |
176 | 577 | delete wavesummaryhex; | 592 | delete wavesummaryhex; |
177 | 593 | |||
178 | 594 | // TrackBeats needs the samplerate for it's constructor | ||
179 | 595 | // so we do it all right here. | ||
180 | 596 | QByteArray* beatsblob = new QByteArray( | ||
181 | 597 | query.value(query.record().indexOf("beats")).toByteArray()); | ||
182 | 598 | if ( beatsblob->size() > 1 ) | ||
183 | 599 | { | ||
184 | 600 | TrackBeats* pTrackBeats = new TrackBeats(pTrack); | ||
185 | 601 | pTrackBeats->unserializeFromBlob(beatsblob); | ||
186 | 602 | track->setTrackBeats(TrackBeatsPointer(pTrackBeats, &QObject::deleteLater), false); | ||
187 | 603 | } | ||
188 | 604 | delete beatsblob; | ||
189 | 605 | |||
190 | 578 | track->setTimesPlayed(timesplayed); | 606 | track->setTimesPlayed(timesplayed); |
191 | 579 | track->setPlayed(played); | 607 | track->setPlayed(played); |
192 | 580 | track->setChannels(channels); | 608 | track->setChannels(channels); |
193 | @@ -593,8 +621,6 @@ | |||
194 | 593 | connect(track, SIGNAL(save()), | 621 | connect(track, SIGNAL(save()), |
195 | 594 | this, SLOT(slotTrackSave())); | 622 | this, SLOT(slotTrackSave())); |
196 | 595 | 623 | ||
197 | 596 | TrackPointer pTrack = TrackPointer(track, this->deleteTrack); | ||
198 | 597 | |||
199 | 598 | // Automatic conversion to a weak pointer | 624 | // Automatic conversion to a weak pointer |
200 | 599 | m_tracks[trackId] = pTrack; | 625 | m_tracks[trackId] = pTrack; |
201 | 600 | m_trackCache.insert(trackId, new TrackPointer(pTrack)); | 626 | m_trackCache.insert(trackId, new TrackPointer(pTrack)); |
202 | @@ -647,7 +673,17 @@ | |||
203 | 647 | time.start(); | 673 | time.start(); |
204 | 648 | QSqlQuery query(m_database); | 674 | QSqlQuery query(m_database); |
205 | 649 | 675 | ||
207 | 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( |
208 | 677 | "SELECT library.id, artist, title, album, year, genre, tracknumber, " | ||
209 | 678 | "filetype, rating, key, track_locations.location as location, " | ||
210 | 679 | "track_locations.filesize as filesize, comment, url, duration, bitrate, " | ||
211 | 680 | "samplerate, cuepoint, bpm, replaygain, wavesummaryhex, channels, " | ||
212 | 681 | "header_parsed, timesplayed, played, beats " | ||
213 | 682 | "FROM Library " | ||
214 | 683 | "INNER JOIN track_locations " | ||
215 | 684 | "ON library.location = track_locations.id " | ||
216 | 685 | "WHERE library.id=" + QString("%1").arg(id) | ||
217 | 686 | ); | ||
218 | 651 | 687 | ||
219 | 652 | TrackPointer pTrack; | 688 | TrackPointer pTrack; |
220 | 653 | 689 | ||
221 | @@ -686,7 +722,8 @@ | |||
222 | 686 | "bitrate=:bitrate, samplerate=:samplerate, cuepoint=:cuepoint, " | 722 | "bitrate=:bitrate, samplerate=:samplerate, cuepoint=:cuepoint, " |
223 | 687 | "bpm=:bpm, replaygain=:replaygain, wavesummaryhex=:wavesummaryhex, " | 723 | "bpm=:bpm, replaygain=:replaygain, wavesummaryhex=:wavesummaryhex, " |
224 | 688 | "timesplayed=:timesplayed, played=:played, " | 724 | "timesplayed=:timesplayed, played=:played, " |
226 | 689 | "channels=:channels, header_parsed=:header_parsed " | 725 | "channels=:channels, header_parsed=:header_parsed, " |
227 | 726 | "beats=:beats " | ||
228 | 690 | "WHERE id="+QString("%1").arg(trackId)); | 727 | "WHERE id="+QString("%1").arg(trackId)); |
229 | 691 | query.bindValue(":artist", pTrack->getArtist()); | 728 | query.bindValue(":artist", pTrack->getArtist()); |
230 | 692 | query.bindValue(":title", pTrack->getTitle()); | 729 | query.bindValue(":title", pTrack->getTitle()); |
231 | @@ -713,6 +750,13 @@ | |||
232 | 713 | query.bindValue(":channels", pTrack->getChannels()); | 750 | query.bindValue(":channels", pTrack->getChannels()); |
233 | 714 | query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0); | 751 | query.bindValue(":header_parsed", pTrack->getHeaderParsed() ? 1 : 0); |
234 | 715 | //query.bindValue(":location", pTrack->getLocation()); | 752 | //query.bindValue(":location", pTrack->getLocation()); |
235 | 753 | TrackBeatsPointer pBeats = pTrack->getTrackBeats(); | ||
236 | 754 | if ( pBeats ) | ||
237 | 755 | { | ||
238 | 756 | const QByteArray *pBeatsBlob = pBeats->serializeToBlob(); | ||
239 | 757 | if ( pBeatsBlob ) | ||
240 | 758 | query.bindValue(":beats", *pBeatsBlob); | ||
241 | 759 | } | ||
242 | 716 | 760 | ||
243 | 717 | if (!query.exec()) { | 761 | if (!query.exec()) { |
244 | 718 | qDebug() << query.lastError(); | 762 | qDebug() << query.lastError(); |
245 | 719 | 763 | ||
246 | === modified file 'mixxx/src/library/trackcollection.cpp' | |||
247 | --- mixxx/src/library/trackcollection.cpp 2010-12-22 21:23:09 +0000 | |||
248 | +++ mixxx/src/library/trackcollection.cpp 2011-02-11 04:42:13 +0000 | |||
249 | @@ -68,7 +68,7 @@ | |||
250 | 68 | return false; | 68 | return false; |
251 | 69 | } | 69 | } |
252 | 70 | 70 | ||
254 | 71 | int requiredSchemaVersion = 9; | 71 | int requiredSchemaVersion = 10; |
255 | 72 | if (!SchemaManager::upgradeToSchemaVersion(m_pConfig, m_db, | 72 | if (!SchemaManager::upgradeToSchemaVersion(m_pConfig, m_db, |
256 | 73 | requiredSchemaVersion)) { | 73 | requiredSchemaVersion)) { |
257 | 74 | QMessageBox::warning(0, tr("Cannot upgrade database schema"), | 74 | QMessageBox::warning(0, tr("Cannot upgrade database schema"), |
258 | 75 | 75 | ||
259 | === added file 'mixxx/src/trackbeats.cpp' | |||
260 | --- mixxx/src/trackbeats.cpp 1970-01-01 00:00:00 +0000 | |||
261 | +++ mixxx/src/trackbeats.cpp 2011-02-11 04:42:13 +0000 | |||
262 | @@ -0,0 +1,349 @@ | |||
263 | 1 | #include <QtDebug> | ||
264 | 2 | |||
265 | 3 | #include "defs.h" | ||
266 | 4 | #include "trackinfoobject.h" | ||
267 | 5 | #include "trackbeats.h" | ||
268 | 6 | |||
269 | 7 | #define TRACKBEATS_INDEX_RANGE 20 | ||
270 | 8 | |||
271 | 9 | |||
272 | 10 | TrackBeats::TrackBeats(TrackPointer tio) : m_qMutex(QMutex::Recursive) | ||
273 | 11 | { | ||
274 | 12 | m_iSampleRate = tio->getSampleRate(); | ||
275 | 13 | } | ||
276 | 14 | |||
277 | 15 | TrackBeats::~TrackBeats() | ||
278 | 16 | { | ||
279 | 17 | } | ||
280 | 18 | |||
281 | 19 | /** | ||
282 | 20 | * Private function that returns the sample index. This is an index into | ||
283 | 21 | * m_beatIndex which holds the first sample offset that holds a beat for a | ||
284 | 22 | * certain range of sample offsets. | ||
285 | 23 | */ | ||
286 | 24 | int TrackBeats::sampleIndex(int sample) const | ||
287 | 25 | { | ||
288 | 26 | QMutexLocker lock(&m_qMutex); | ||
289 | 27 | return (int) round(sample / (m_iSampleRate * TRACKBEATS_INDEX_RANGE)); | ||
290 | 28 | } | ||
291 | 29 | |||
292 | 30 | /** | ||
293 | 31 | * Get the count of beats stored. | ||
294 | 32 | */ | ||
295 | 33 | int TrackBeats::getBeatCount() const | ||
296 | 34 | { | ||
297 | 35 | QMutexLocker lock(&m_qMutex); | ||
298 | 36 | return m_beats.size(); | ||
299 | 37 | } | ||
300 | 38 | |||
301 | 39 | /** | ||
302 | 40 | * Add a beat at 'sample' sample. | ||
303 | 41 | */ | ||
304 | 42 | void TrackBeats::addBeatSample(int sample) | ||
305 | 43 | { | ||
306 | 44 | QMutexLocker lock(&m_qMutex); | ||
307 | 45 | int index = sampleIndex(sample); | ||
308 | 46 | int i; | ||
309 | 47 | |||
310 | 48 | |||
311 | 49 | if ((m_beatIndex.size()-1) < index ) | ||
312 | 50 | { | ||
313 | 51 | for (i = m_beatIndex.size() - 1; i <= index; i++ ) | ||
314 | 52 | m_beatIndex.append(sample); | ||
315 | 53 | } | ||
316 | 54 | else | ||
317 | 55 | { | ||
318 | 56 | for (i = index; m_beatIndex.at(i) >= sample && i >= 0; i-- ) { | ||
319 | 57 | m_beatIndex[index] = sample; | ||
320 | 58 | } | ||
321 | 59 | } | ||
322 | 60 | |||
323 | 61 | m_beats[sample] = sample; | ||
324 | 62 | } | ||
325 | 63 | |||
326 | 64 | /** | ||
327 | 65 | * Remove the beat with the sample offset specified. | ||
328 | 66 | */ | ||
329 | 67 | void TrackBeats::removeBeatSample(int sample) | ||
330 | 68 | { | ||
331 | 69 | QMutexLocker lock(&m_qMutex); | ||
332 | 70 | int index = sampleIndex(sample); | ||
333 | 71 | int i; | ||
334 | 72 | int newSample = -1; | ||
335 | 73 | |||
336 | 74 | |||
337 | 75 | m_beats.remove(sample); | ||
338 | 76 | |||
339 | 77 | if ( ! m_beatIndex.contains(sample)) | ||
340 | 78 | return; | ||
341 | 79 | |||
342 | 80 | if ((index+1) < m_beatIndex.size()) { | ||
343 | 81 | newSample = m_beatIndex.at(index+1); | ||
344 | 82 | |||
345 | 83 | for (i = index; i >= 0 && m_beatIndex.at(i) >= sample; i--) { | ||
346 | 84 | if ( m_beatIndex.at(i) == sample ) | ||
347 | 85 | m_beatIndex[i] = newSample; | ||
348 | 86 | } | ||
349 | 87 | } | ||
350 | 88 | else { | ||
351 | 89 | for (i = index; i >= 0 && m_beatIndex.at(i) >= sample; i--) { | ||
352 | 90 | if ( m_beatIndex.at(i) == sample ) | ||
353 | 91 | m_beatIndex.removeAt(i); | ||
354 | 92 | } | ||
355 | 93 | } | ||
356 | 94 | |||
357 | 95 | if ( m_beatIndex.contains(sample) ) | ||
358 | 96 | qDebug() << "Dangling Indexes:" << m_beatIndex.count(sample); | ||
359 | 97 | } | ||
360 | 98 | |||
361 | 99 | /** | ||
362 | 100 | * Add a beat at 'beat' seconds. | ||
363 | 101 | */ | ||
364 | 102 | void TrackBeats::addBeatSeconds(double beat) | ||
365 | 103 | { | ||
366 | 104 | QMutexLocker lock(&m_qMutex); | ||
367 | 105 | addBeatSample((int)round(beat * m_iSampleRate)); | ||
368 | 106 | } | ||
369 | 107 | |||
370 | 108 | |||
371 | 109 | /** | ||
372 | 110 | * Remove a beat at 'beat' seconds. | ||
373 | 111 | */ | ||
374 | 112 | void TrackBeats::removeBeatSeconds(double beat) | ||
375 | 113 | { | ||
376 | 114 | QMutexLocker lock(&m_qMutex); | ||
377 | 115 | removeBeatSample((int)round(beat * m_iSampleRate)); | ||
378 | 116 | } | ||
379 | 117 | |||
380 | 118 | /** | ||
381 | 119 | * Find the Next beat starting from offset sample. | ||
382 | 120 | */ | ||
383 | 121 | int TrackBeats::findNextBeatSample(int sample) const | ||
384 | 122 | { | ||
385 | 123 | QMutexLocker lock(&m_qMutex); | ||
386 | 124 | QMapIterator<int, int> iter(m_beats); | ||
387 | 125 | int index = sampleIndex(sample); | ||
388 | 126 | |||
389 | 127 | |||
390 | 128 | if (m_beatIndex.size() > index) | ||
391 | 129 | { | ||
392 | 130 | iter.findNext(m_beatIndex.value(index)); | ||
393 | 131 | do { | ||
394 | 132 | iter.next(); | ||
395 | 133 | } while((iter.hasNext()) && (iter.value() <= sample)); | ||
396 | 134 | |||
397 | 135 | return iter.value(); | ||
398 | 136 | } | ||
399 | 137 | |||
400 | 138 | return -1; | ||
401 | 139 | } | ||
402 | 140 | |||
403 | 141 | /** | ||
404 | 142 | * Find the Previous beat starting from offset sample. | ||
405 | 143 | */ | ||
406 | 144 | int TrackBeats::findPrevBeatSample(int sample) const | ||
407 | 145 | { | ||
408 | 146 | QMutexLocker lock(&m_qMutex); | ||
409 | 147 | QMapIterator<int, int> iter(m_beats); | ||
410 | 148 | int index = sampleIndex(sample); | ||
411 | 149 | |||
412 | 150 | if (m_beatIndex.size() > index) | ||
413 | 151 | { | ||
414 | 152 | iter.findNext(m_beatIndex.value(index)); | ||
415 | 153 | do { | ||
416 | 154 | iter.previous(); | ||
417 | 155 | } while((iter.hasPrevious()) && (iter.value() >= sample)); | ||
418 | 156 | |||
419 | 157 | return iter.value(); | ||
420 | 158 | } | ||
421 | 159 | |||
422 | 160 | return -1; | ||
423 | 161 | } | ||
424 | 162 | |||
425 | 163 | /** | ||
426 | 164 | * Find the Nth beat (offset) from sample . | ||
427 | 165 | */ | ||
428 | 166 | int TrackBeats::findBeatOffsetSamples(int sample, int offset) const | ||
429 | 167 | { | ||
430 | 168 | QMutexLocker lock(&m_qMutex); | ||
431 | 169 | QMapIterator<int, int> iter(m_beats); | ||
432 | 170 | int index = sampleIndex(sample); | ||
433 | 171 | int i; | ||
434 | 172 | |||
435 | 173 | |||
436 | 174 | if (m_beatIndex.size() < index) | ||
437 | 175 | return -1; | ||
438 | 176 | |||
439 | 177 | |||
440 | 178 | iter.findNext(m_beatIndex.value(index)); | ||
441 | 179 | do { | ||
442 | 180 | iter.next(); | ||
443 | 181 | } while((iter.hasNext()) && (iter.value() <= sample)); | ||
444 | 182 | |||
445 | 183 | // Backup one just to be before the marker | ||
446 | 184 | if ((iter.hasPrevious()) && (iter.value() > sample)) | ||
447 | 185 | iter.previous(); | ||
448 | 186 | |||
449 | 187 | // Find the offset from the current beat | ||
450 | 188 | if ( offset > 0 ) | ||
451 | 189 | { | ||
452 | 190 | for (i = 0; i < offset && iter.hasNext(); i++) | ||
453 | 191 | iter.next(); | ||
454 | 192 | } | ||
455 | 193 | else if ( offset < 0 ) | ||
456 | 194 | { | ||
457 | 195 | for (i = offset * -1; i > 0 && iter.hasPrevious(); i--) | ||
458 | 196 | iter.previous(); | ||
459 | 197 | } | ||
460 | 198 | |||
461 | 199 | return iter.value(); | ||
462 | 200 | } | ||
463 | 201 | |||
464 | 202 | /** | ||
465 | 203 | * Return TRUE if there is any beats between start and stop (in samples) | ||
466 | 204 | */ | ||
467 | 205 | bool TrackBeats::hasBeatsSamples(double start, double stop) const | ||
468 | 206 | { | ||
469 | 207 | QMutexLocker lock(&m_qMutex); | ||
470 | 208 | QMapIterator<int, int> iter(m_beats); | ||
471 | 209 | int index = sampleIndex(start); | ||
472 | 210 | |||
473 | 211 | |||
474 | 212 | if (iter.findNext(m_beatIndex.value(index))) | ||
475 | 213 | { | ||
476 | 214 | do { | ||
477 | 215 | if ((iter.value() >= start) && (iter.value() <= stop)) | ||
478 | 216 | return true; | ||
479 | 217 | |||
480 | 218 | iter.next(); | ||
481 | 219 | } while((iter.hasNext()) && (iter.value() <= stop)); | ||
482 | 220 | } | ||
483 | 221 | |||
484 | 222 | return false; | ||
485 | 223 | } | ||
486 | 224 | |||
487 | 225 | /** | ||
488 | 226 | * Find and return a list of all beats between sample offset start and stop. | ||
489 | 227 | */ | ||
490 | 228 | QList<int>* TrackBeats::findBeatsSamples(int start, int stop) const | ||
491 | 229 | { | ||
492 | 230 | QMutexLocker lock(&m_qMutex); | ||
493 | 231 | QList<int> *ret = new QList<int>; | ||
494 | 232 | QMapIterator<int, int> iter(m_beats); | ||
495 | 233 | int index = sampleIndex(start); | ||
496 | 234 | |||
497 | 235 | |||
498 | 236 | if (iter.findNext(m_beatIndex.value(index))) | ||
499 | 237 | { | ||
500 | 238 | do { | ||
501 | 239 | if ((iter.value() >= start) && (iter.value() <= stop)) | ||
502 | 240 | ret->append(iter.value()); | ||
503 | 241 | |||
504 | 242 | iter.next(); | ||
505 | 243 | } while((iter.hasNext()) && (iter.value() <= stop)); | ||
506 | 244 | } | ||
507 | 245 | |||
508 | 246 | return ret; | ||
509 | 247 | } | ||
510 | 248 | |||
511 | 249 | /** | ||
512 | 250 | * Find the Next beat starting from an offset in seconds. | ||
513 | 251 | */ | ||
514 | 252 | double TrackBeats::findNextBeatSeconds(double beat) const | ||
515 | 253 | { | ||
516 | 254 | QMutexLocker lock(&m_qMutex); | ||
517 | 255 | int sample = (int) round(beat * m_iSampleRate); | ||
518 | 256 | return findNextBeatSample(sample) / m_iSampleRate; | ||
519 | 257 | } | ||
520 | 258 | |||
521 | 259 | /** | ||
522 | 260 | * Find the Previous beat starting from an offset in seconds. | ||
523 | 261 | */ | ||
524 | 262 | double TrackBeats::findPrevBeatSeconds(double beat) const | ||
525 | 263 | { | ||
526 | 264 | QMutexLocker lock(&m_qMutex); | ||
527 | 265 | int sample = (int) round(beat * m_iSampleRate); | ||
528 | 266 | return findPrevBeatSample(sample) / m_iSampleRate; | ||
529 | 267 | } | ||
530 | 268 | |||
531 | 269 | /** | ||
532 | 270 | * Find the Nth beat (offset) from offset seconds in seconds. | ||
533 | 271 | */ | ||
534 | 272 | double TrackBeats::findBeatOffsetSeconds(double seconds, int offset) const | ||
535 | 273 | { | ||
536 | 274 | QMutexLocker lock(&m_qMutex); | ||
537 | 275 | int bgn = round(seconds * m_iSampleRate); | ||
538 | 276 | return ((double)findBeatOffsetSamples(bgn, offset) / m_iSampleRate); | ||
539 | 277 | } | ||
540 | 278 | |||
541 | 279 | /** | ||
542 | 280 | * Return TRUE if there is any beats between start and stop (in seconds) | ||
543 | 281 | */ | ||
544 | 282 | bool TrackBeats::hasBeatsSeconds(double start, double stop) const | ||
545 | 283 | { | ||
546 | 284 | QMutexLocker lock(&m_qMutex); | ||
547 | 285 | int bgn = round(start * m_iSampleRate); | ||
548 | 286 | int end = round(stop * m_iSampleRate); | ||
549 | 287 | |||
550 | 288 | return hasBeatsSamples(bgn, end); | ||
551 | 289 | } | ||
552 | 290 | |||
553 | 291 | /** | ||
554 | 292 | * Find and return a list of all beats between seconds start and stop. | ||
555 | 293 | */ | ||
556 | 294 | QList<double>* TrackBeats::findBeatsSeconds(double start, double stop) const | ||
557 | 295 | { | ||
558 | 296 | QMutexLocker lock(&m_qMutex); | ||
559 | 297 | QList<double> *ret = new QList<double>; | ||
560 | 298 | QList<int>* samples; | ||
561 | 299 | int begin = round(start * m_iSampleRate); | ||
562 | 300 | int end = round(stop * m_iSampleRate); | ||
563 | 301 | int i; | ||
564 | 302 | |||
565 | 303 | |||
566 | 304 | samples = findBeatsSamples(begin, end); | ||
567 | 305 | for (i = 0; i < samples->size(); i++) | ||
568 | 306 | { | ||
569 | 307 | ret->append((double)samples->at(i) / (double)m_iSampleRate); | ||
570 | 308 | } | ||
571 | 309 | |||
572 | 310 | delete samples; | ||
573 | 311 | return ret; | ||
574 | 312 | } | ||
575 | 313 | |||
576 | 314 | QByteArray *TrackBeats::serializeToBlob() | ||
577 | 315 | { | ||
578 | 316 | QMutexLocker lock(&m_qMutex); | ||
579 | 317 | QByteArray *blob; | ||
580 | 318 | int *buffer = new int[getBeatCount()]; | ||
581 | 319 | int *ptr = buffer; | ||
582 | 320 | QMapIterator<int, int> iter(m_beats); | ||
583 | 321 | |||
584 | 322 | |||
585 | 323 | iter.next(); | ||
586 | 324 | |||
587 | 325 | while ( iter.hasNext()) | ||
588 | 326 | { | ||
589 | 327 | *ptr++ = iter.value(); | ||
590 | 328 | iter.next(); | ||
591 | 329 | } | ||
592 | 330 | |||
593 | 331 | blob = new QByteArray((char *)buffer, getBeatCount() * sizeof(int)); | ||
594 | 332 | delete []buffer; | ||
595 | 333 | |||
596 | 334 | return blob; | ||
597 | 335 | } | ||
598 | 336 | |||
599 | 337 | void TrackBeats::unserializeFromBlob(QByteArray *blob) | ||
600 | 338 | { | ||
601 | 339 | QMutexLocker lock(&m_qMutex); | ||
602 | 340 | int *ptr = (int *)blob->constData(); | ||
603 | 341 | int i; | ||
604 | 342 | |||
605 | 343 | |||
606 | 344 | for (i = blob->size() / sizeof(int); --i; ptr++) | ||
607 | 345 | { | ||
608 | 346 | addBeatSample(*ptr); | ||
609 | 347 | } | ||
610 | 348 | } | ||
611 | 349 | |||
612 | 0 | 350 | ||
613 | === added file 'mixxx/src/trackbeats.h' | |||
614 | --- mixxx/src/trackbeats.h 1970-01-01 00:00:00 +0000 | |||
615 | +++ mixxx/src/trackbeats.h 2011-02-11 04:42:13 +0000 | |||
616 | @@ -0,0 +1,59 @@ | |||
617 | 1 | #include <QtDebug> | ||
618 | 2 | #include <QList> | ||
619 | 3 | #include <QSharedPointer> | ||
620 | 4 | #include "trackinfoobject.h" | ||
621 | 5 | |||
622 | 6 | |||
623 | 7 | #ifndef __TRACK_BEATS_H__ | ||
624 | 8 | #define __TRACK_BEATS_H__ | ||
625 | 9 | |||
626 | 10 | class TrackBeats; | ||
627 | 11 | typedef QSharedPointer<TrackBeats> TrackBeatsPointer; | ||
628 | 12 | |||
629 | 13 | class TrackBeats : public QObject | ||
630 | 14 | { | ||
631 | 15 | Q_OBJECT; | ||
632 | 16 | public: | ||
633 | 17 | TrackBeats(TrackPointer); | ||
634 | 18 | virtual ~TrackBeats(); | ||
635 | 19 | |||
636 | 20 | |||
637 | 21 | int getBeatCount() const; | ||
638 | 22 | |||
639 | 23 | void addBeatSample(int); | ||
640 | 24 | void addBeatSeconds(double); | ||
641 | 25 | void removeBeatSample(int); | ||
642 | 26 | void removeBeatSeconds(double); | ||
643 | 27 | void nudgeSamples(int); | ||
644 | 28 | |||
645 | 29 | int findPrevBeatSample(int) const; | ||
646 | 30 | int findNextBeatSample(int) const; | ||
647 | 31 | int findBeatOffsetSamples(int, int) const; | ||
648 | 32 | bool hasBeatsSamples(double, double) const; | ||
649 | 33 | bool hasBeatsSeconds(double, double) const; | ||
650 | 34 | double findNextBeatSeconds(double) const; | ||
651 | 35 | double findPrevBeatSeconds(double) const; | ||
652 | 36 | QList<int>* findBeatsSamples(int, int) const; | ||
653 | 37 | double findBeatOffsetSeconds(double, int) const; | ||
654 | 38 | QList<double>* findBeatsSeconds(double, double) const; | ||
655 | 39 | QByteArray *serializeToBlob(); | ||
656 | 40 | void unserializeFromBlob(QByteArray *blob); | ||
657 | 41 | |||
658 | 42 | private: | ||
659 | 43 | /** Find the Samples Index */ | ||
660 | 44 | int sampleIndex(int) const; | ||
661 | 45 | /** Sample Rate for this song */ | ||
662 | 46 | int m_iSampleRate; | ||
663 | 47 | /** Duration of the entire song in seconds */ | ||
664 | 48 | double m_dDuration; | ||
665 | 49 | /** 10 second index (in samples) */ | ||
666 | 50 | QList<int> m_beatIndex; | ||
667 | 51 | /** Map of all the beats in samples */ | ||
668 | 52 | QMap<int, int> m_beats; | ||
669 | 53 | /** Pointer to the related Track */ | ||
670 | 54 | TrackPointer m_track; | ||
671 | 55 | /** Mutex protecting access to object */ | ||
672 | 56 | mutable QMutex m_qMutex; | ||
673 | 57 | }; | ||
674 | 58 | |||
675 | 59 | #endif | ||
676 | 0 | 60 | ||
677 | === modified file 'mixxx/src/trackinfoobject.cpp' | |||
678 | --- mixxx/src/trackinfoobject.cpp 2010-11-24 15:20:09 +0000 | |||
679 | +++ mixxx/src/trackinfoobject.cpp 2011-02-11 04:42:13 +0000 | |||
680 | @@ -341,6 +341,22 @@ | |||
681 | 341 | m_bBpmConfirm = confirm; | 341 | m_bBpmConfirm = confirm; |
682 | 342 | } | 342 | } |
683 | 343 | 343 | ||
684 | 344 | void TrackInfoObject::setTrackBeats(TrackBeatsPointer beats, bool isDirty) | ||
685 | 345 | { | ||
686 | 346 | QMutexLocker lock(&m_qMutex); | ||
687 | 347 | m_pTrackBeatsPointer = beats; | ||
688 | 348 | if ( isDirty ) | ||
689 | 349 | setDirty(true); | ||
690 | 350 | |||
691 | 351 | emit(trackBeatsUpdated(1)); | ||
692 | 352 | } | ||
693 | 353 | |||
694 | 354 | TrackBeatsPointer TrackInfoObject::getTrackBeats() const | ||
695 | 355 | { | ||
696 | 356 | QMutexLocker lock(&m_qMutex); | ||
697 | 357 | return m_pTrackBeatsPointer; | ||
698 | 358 | } | ||
699 | 359 | |||
700 | 344 | bool TrackInfoObject::getHeaderParsed() const | 360 | bool TrackInfoObject::getHeaderParsed() const |
701 | 345 | { | 361 | { |
702 | 346 | QMutexLocker lock(&m_qMutex); | 362 | QMutexLocker lock(&m_qMutex); |
703 | 347 | 363 | ||
704 | === modified file 'mixxx/src/trackinfoobject.h' | |||
705 | --- mixxx/src/trackinfoobject.h 2010-11-17 21:02:24 +0000 | |||
706 | +++ mixxx/src/trackinfoobject.h 2011-02-11 04:42:13 +0000 | |||
707 | @@ -40,9 +40,11 @@ | |||
708 | 40 | class Cue; | 40 | class Cue; |
709 | 41 | 41 | ||
710 | 42 | class TrackInfoObject; | 42 | class TrackInfoObject; |
711 | 43 | class TrackBeats; | ||
712 | 43 | 44 | ||
713 | 44 | typedef QSharedPointer<TrackInfoObject> TrackPointer; | 45 | typedef QSharedPointer<TrackInfoObject> TrackPointer; |
714 | 45 | typedef QWeakPointer<TrackInfoObject> TrackWeakPointer; | 46 | typedef QWeakPointer<TrackInfoObject> TrackWeakPointer; |
715 | 47 | typedef QSharedPointer<TrackBeats> TrackBeatsPointer; | ||
716 | 46 | 48 | ||
717 | 47 | #include "segmentation.h" | 49 | #include "segmentation.h" |
718 | 48 | 50 | ||
719 | @@ -242,6 +244,12 @@ | |||
720 | 242 | /** Set the track's full file path */ | 244 | /** Set the track's full file path */ |
721 | 243 | void setLocation(QString location); | 245 | void setLocation(QString location); |
722 | 244 | 246 | ||
723 | 247 | /** Get the Track's Beats list */ | ||
724 | 248 | TrackBeatsPointer getTrackBeats() const; | ||
725 | 249 | |||
726 | 250 | /** Set the Track's Beats */ | ||
727 | 251 | void setTrackBeats(TrackBeatsPointer beats, bool isDirty); | ||
728 | 252 | |||
729 | 245 | const Segmentation<QString>* getChordData(); | 253 | const Segmentation<QString>* getChordData(); |
730 | 246 | void setChordData(Segmentation<QString> cd); | 254 | void setChordData(Segmentation<QString> cd); |
731 | 247 | 255 | ||
732 | @@ -251,6 +259,7 @@ | |||
733 | 251 | signals: | 259 | signals: |
734 | 252 | void wavesummaryUpdated(TrackInfoObject*); | 260 | void wavesummaryUpdated(TrackInfoObject*); |
735 | 253 | void bpmUpdated(double bpm); | 261 | void bpmUpdated(double bpm); |
736 | 262 | void trackBeatsUpdated(int); | ||
737 | 254 | void ReplayGainUpdated(double replaygain); | 263 | void ReplayGainUpdated(double replaygain); |
738 | 255 | void cuesUpdated(); | 264 | void cuesUpdated(); |
739 | 256 | void changed(); | 265 | void changed(); |
740 | @@ -370,6 +379,9 @@ | |||
741 | 370 | double m_dVisualResampleRate; | 379 | double m_dVisualResampleRate; |
742 | 371 | Segmentation<QString> m_chordData; | 380 | Segmentation<QString> m_chordData; |
743 | 372 | 381 | ||
744 | 382 | // Storage for the Track's detected beats | ||
745 | 383 | TrackBeatsPointer m_pTrackBeatsPointer; | ||
746 | 384 | |||
747 | 373 | friend class TrackDAO; | 385 | friend class TrackDAO; |
748 | 374 | }; | 386 | }; |
749 | 375 | 387 | ||
750 | 376 | 388 | ||
751 | === modified file 'mixxx/src/waveform/waveformrenderbeat.cpp' | |||
752 | --- mixxx/src/waveform/waveformrenderbeat.cpp 2010-11-25 01:06:46 +0000 | |||
753 | +++ mixxx/src/waveform/waveformrenderbeat.cpp 2011-02-11 04:42:13 +0000 | |||
754 | @@ -14,6 +14,8 @@ | |||
755 | 14 | #include "widget/wskincolor.h" | 14 | #include "widget/wskincolor.h" |
756 | 15 | #include "widget/wwidget.h" | 15 | #include "widget/wwidget.h" |
757 | 16 | #include "trackinfoobject.h" | 16 | #include "trackinfoobject.h" |
758 | 17 | #include "trackbeats.h" | ||
759 | 18 | |||
760 | 17 | 19 | ||
761 | 18 | WaveformRenderBeat::WaveformRenderBeat(const char* group, WaveformRenderer *parent) | 20 | WaveformRenderBeat::WaveformRenderBeat(const char* group, WaveformRenderer *parent) |
762 | 19 | : m_pParent(parent), | 21 | : m_pParent(parent), |
763 | @@ -21,6 +23,7 @@ | |||
764 | 21 | m_pBeatFirst(NULL), | 23 | m_pBeatFirst(NULL), |
765 | 22 | m_pTrackSamples(NULL), | 24 | m_pTrackSamples(NULL), |
766 | 23 | m_pTrack(), | 25 | m_pTrack(), |
767 | 26 | m_pTrackBeats(NULL), | ||
768 | 24 | m_iWidth(0), | 27 | m_iWidth(0), |
769 | 25 | m_iHeight(0), | 28 | m_iHeight(0), |
770 | 26 | m_dBpm(-1), | 29 | m_dBpm(-1), |
771 | @@ -38,6 +41,7 @@ | |||
772 | 38 | 41 | ||
773 | 39 | m_pTrackSamples = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(group,"track_samples"))); | 42 | m_pTrackSamples = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(group,"track_samples"))); |
774 | 40 | slotUpdateTrackSamples(m_pTrackSamples->get()); | 43 | slotUpdateTrackSamples(m_pTrackSamples->get()); |
775 | 44 | |||
776 | 41 | connect(m_pTrackSamples, SIGNAL(valueChanged(double)), | 45 | connect(m_pTrackSamples, SIGNAL(valueChanged(double)), |
777 | 42 | this, SLOT(slotUpdateTrackSamples(double))); | 46 | this, SLOT(slotUpdateTrackSamples(double))); |
778 | 43 | } | 47 | } |
779 | @@ -58,6 +62,15 @@ | |||
780 | 58 | m_iNumSamples = (int)samples; | 62 | m_iNumSamples = (int)samples; |
781 | 59 | } | 63 | } |
782 | 60 | 64 | ||
783 | 65 | void WaveformRenderBeat::slotUpdateTrackBeats(int) | ||
784 | 66 | { | ||
785 | 67 | m_pTrackBeats = m_pTrack->getTrackBeats(); | ||
786 | 68 | if ( m_pTrackBeats ) | ||
787 | 69 | qDebug() << "WaveformRenderBeat :: beats = " << m_pTrackBeats->getBeatCount(); | ||
788 | 70 | else | ||
789 | 71 | qDebug() << "No WaveformRenderBeat beats list"; | ||
790 | 72 | } | ||
791 | 73 | |||
792 | 61 | void WaveformRenderBeat::resize(int w, int h) { | 74 | void WaveformRenderBeat::resize(int w, int h) { |
793 | 62 | m_iWidth = w; | 75 | m_iWidth = w; |
794 | 63 | m_iHeight = h; | 76 | m_iHeight = h; |
795 | @@ -91,7 +104,10 @@ | |||
796 | 91 | m_dSamplesPerDownsample = n; | 104 | m_dSamplesPerDownsample = n; |
797 | 92 | m_dSamplesPerPixel = double(f)/z; | 105 | m_dSamplesPerPixel = double(f)/z; |
798 | 93 | 106 | ||
800 | 94 | //qDebug() << "WaveformRenderBeat sampleRate " << sampleRate << " samplesPerPixel " << m_dSamplesPerPixel; | 107 | // Reset the tracker beats for each new song |
801 | 108 | m_pTrackBeats = pTrack->getTrackBeats(); | ||
802 | 109 | connect(pTrack.data(), SIGNAL(trackBeatsUpdated(int)), this, | ||
803 | 110 | SLOT(slotUpdateTrackBeats(int))); | ||
804 | 95 | 111 | ||
805 | 96 | } | 112 | } |
806 | 97 | 113 | ||
807 | @@ -108,9 +124,18 @@ | |||
808 | 108 | colorHighlight = WSkinColor::getCorrectColor(colorHighlight); | 124 | colorHighlight = WSkinColor::getCorrectColor(colorHighlight); |
809 | 109 | } | 125 | } |
810 | 110 | 126 | ||
814 | 111 | 127 | void WaveformRenderBeat::draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double dPlayPos, double rateAdjust) | |
815 | 112 | void WaveformRenderBeat::draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double dPlayPos, double rateAdjust) { | 128 | { |
816 | 113 | if(m_dBpm == -1 || m_dBpm == 0) | 129 | drawTrackBeat(pPainter, event, buffer, dPlayPos, rateAdjust); |
817 | 130 | } | ||
818 | 131 | |||
819 | 132 | void WaveformRenderBeat::drawTrackBeat(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double dPlayPos, double rateAdjust) | ||
820 | 133 | { | ||
821 | 134 | QList<int>* beatSamples; | ||
822 | 135 | int i; | ||
823 | 136 | |||
824 | 137 | |||
825 | 138 | if ( m_pTrackBeats == NULL ) | ||
826 | 114 | return; | 139 | return; |
827 | 115 | 140 | ||
828 | 116 | slotUpdateTrackSamples(m_pTrackSamples->get()); | 141 | slotUpdateTrackSamples(m_pTrackSamples->get()); |
829 | @@ -137,11 +162,6 @@ | |||
830 | 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. |
831 | 138 | // where s is a /mono/ sample | 163 | // where s is a /mono/ sample |
832 | 139 | 164 | ||
833 | 140 | // beat length in samples | ||
834 | 141 | if(m_dBeatLength <= 0) { | ||
835 | 142 | m_dBeatLength = 60.0 * m_iSampleRate / m_dBpm; | ||
836 | 143 | } | ||
837 | 144 | |||
838 | 145 | double subpixelsPerPixel = m_pParent->getSubpixelsPerPixel()*(1.0+rateAdjust); | 165 | double subpixelsPerPixel = m_pParent->getSubpixelsPerPixel()*(1.0+rateAdjust); |
839 | 146 | const int oversample = (int)subpixelsPerPixel; | 166 | const int oversample = (int)subpixelsPerPixel; |
840 | 147 | 167 | ||
841 | @@ -163,10 +183,18 @@ | |||
842 | 163 | 183 | ||
843 | 164 | // snap to the first beat | 184 | // snap to the first beat |
844 | 165 | double curPos = ceilf(basePos/m_dBeatLength)*m_dBeatLength; | 185 | double curPos = ceilf(basePos/m_dBeatLength)*m_dBeatLength; |
845 | 166 | |||
846 | 167 | bool reset = false; | 186 | bool reset = false; |
849 | 168 | for(;curPos <= endPos; curPos+=m_dBeatLength) { | 187 | int curSample = (int)round(curPos); |
850 | 169 | if(curPos < 0) | 188 | int endSample = (int)round(endPos); |
851 | 189 | |||
852 | 190 | |||
853 | 191 | beatSamples = m_pTrackBeats->findBeatsSamples(curSample, endSample); | ||
854 | 192 | |||
855 | 193 | for(i = 0; i < beatSamples->size(); i++) { | ||
856 | 194 | curPos = beatSamples->at(i); | ||
857 | 195 | |||
858 | 196 | |||
859 | 197 | if (curPos < 0) | ||
860 | 170 | continue; | 198 | continue; |
861 | 171 | 199 | ||
862 | 172 | // i relative to the current play position in subpixels | 200 | // i relative to the current play position in subpixels |
863 | @@ -174,7 +202,7 @@ | |||
864 | 174 | 202 | ||
865 | 175 | // If i is less than 20 subpixels from center, highlight it. | 203 | // If i is less than 20 subpixels from center, highlight it. |
866 | 176 | if(abs(i) < 20) { | 204 | if(abs(i) < 20) { |
868 | 177 | pPainter->setPen(colorHighlight); | 205 | pPainter->setPen(QColor(255,255,255)); |
869 | 178 | reset = true; | 206 | reset = true; |
870 | 179 | } | 207 | } |
871 | 180 | 208 | ||
872 | @@ -189,6 +217,7 @@ | |||
873 | 189 | } | 217 | } |
874 | 190 | } | 218 | } |
875 | 191 | 219 | ||
876 | 220 | delete beatSamples; | ||
877 | 192 | pPainter->restore(); | 221 | pPainter->restore(); |
878 | 193 | 222 | ||
879 | 194 | } | 223 | } |
880 | 195 | 224 | ||
881 | === modified file 'mixxx/src/waveform/waveformrenderbeat.h' | |||
882 | --- mixxx/src/waveform/waveformrenderbeat.h 2010-07-15 19:07:16 +0000 | |||
883 | +++ mixxx/src/waveform/waveformrenderbeat.h 2011-02-11 04:42:13 +0000 | |||
884 | @@ -7,6 +7,7 @@ | |||
885 | 7 | #include <QVector> | 7 | #include <QVector> |
886 | 8 | 8 | ||
887 | 9 | #include "renderobject.h" | 9 | #include "renderobject.h" |
888 | 10 | #include "trackbeats.h" | ||
889 | 10 | 11 | ||
890 | 11 | class QDomNode; | 12 | class QDomNode; |
891 | 12 | class QPainter; | 13 | class QPainter; |
892 | @@ -17,18 +18,21 @@ | |||
893 | 17 | class WaveformRenderer; | 18 | class WaveformRenderer; |
894 | 18 | class SoundSourceProxy; | 19 | class SoundSourceProxy; |
895 | 19 | 20 | ||
896 | 21 | |||
897 | 20 | class WaveformRenderBeat : public RenderObject { | 22 | class WaveformRenderBeat : public RenderObject { |
898 | 21 | Q_OBJECT | 23 | Q_OBJECT |
899 | 22 | public: | 24 | public: |
900 | 23 | WaveformRenderBeat(const char *group, WaveformRenderer *parent); | 25 | WaveformRenderBeat(const char *group, WaveformRenderer *parent); |
901 | 24 | void resize(int w, int h); | 26 | void resize(int w, int h); |
902 | 25 | void setup(QDomNode node); | 27 | void setup(QDomNode node); |
903 | 28 | void drawTrackBeat(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust); | ||
904 | 26 | void draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust); | 29 | void draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust); |
905 | 27 | void newTrack(TrackPointer pTrack); | 30 | void newTrack(TrackPointer pTrack); |
906 | 28 | 31 | ||
907 | 29 | public slots: | 32 | public slots: |
908 | 30 | void slotUpdateBpm(double bpm); | 33 | void slotUpdateBpm(double bpm); |
909 | 31 | void slotUpdateBeatFirst(double beatfirst); | 34 | void slotUpdateBeatFirst(double beatfirst); |
910 | 35 | void slotUpdateTrackBeats(int); | ||
911 | 32 | void slotUpdateTrackSamples(double samples); | 36 | void slotUpdateTrackSamples(double samples); |
912 | 33 | private: | 37 | private: |
913 | 34 | WaveformRenderer *m_pParent; | 38 | WaveformRenderer *m_pParent; |
914 | @@ -36,6 +40,7 @@ | |||
915 | 36 | ControlObjectThreadMain *m_pBeatFirst; | 40 | ControlObjectThreadMain *m_pBeatFirst; |
916 | 37 | ControlObjectThreadMain *m_pTrackSamples; | 41 | ControlObjectThreadMain *m_pTrackSamples; |
917 | 38 | TrackPointer m_pTrack; | 42 | TrackPointer m_pTrack; |
918 | 43 | TrackBeatsPointer m_pTrackBeats; | ||
919 | 39 | int m_iWidth, m_iHeight; | 44 | int m_iWidth, m_iHeight; |
920 | 40 | double m_dBpm; | 45 | double m_dBpm; |
921 | 41 | double m_dBeatFirst; | 46 | double m_dBeatFirst; |
922 | @@ -46,6 +51,7 @@ | |||
923 | 46 | double m_dBeatLength; | 51 | double m_dBeatLength; |
924 | 47 | int m_iNumSamples; | 52 | int m_iNumSamples; |
925 | 48 | int m_iSampleRate; | 53 | int m_iSampleRate; |
926 | 54 | |||
927 | 49 | }; | 55 | }; |
928 | 50 | 56 | ||
929 | 51 | #endif | 57 | #endif |