![]() |
Mixxx
|
00001 /* 00002 * Copyright (c) 2001-2006 MUSIC TECHNOLOGY GROUP (MTG) 00003 * UNIVERSITAT POMPEU FABRA 00004 * 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00019 * 00020 */ 00021 00022 #ifndef ChordSegmentator_hxx 00023 #define ChordSegmentator_hxx 00024 00025 #include <iostream> 00026 #include <fstream> 00027 #include <cmath> 00028 //#include "Array.hxx" 00029 #include "DiscontinuousSegmentation.hxx" 00030 #include "ChordCorrelator.hxx" 00031 //#include "Assert.hxx" 00032 00033 namespace Simac 00034 { 00035 00044 class ChordSegmentator 00045 { 00046 CLAM::DiscontinuousSegmentation _segmentation; 00047 std::vector<unsigned> _chordIndexes; 00048 00049 unsigned _currentSegment; 00050 bool _segmentOpen; 00051 unsigned _lastChord; 00052 00053 unsigned _method; 00054 00055 // Chord similarity method variables 00056 std::vector< std::vector<double> > _chordSimilarity; 00057 std::vector<double> _segmentChordCorrelation; 00058 unsigned _framesInSegment; 00059 public: 00060 ChordSegmentator() 00061 : _segmentation(0) 00062 , _currentSegment(0) 00063 , _segmentOpen(false) 00064 , _lastChord(0) 00065 , _framesInSegment(0) 00066 { 00067 method(0); 00068 }; 00069 ~ChordSegmentator() {}; 00070 00071 void doIt(CLAM::TData & currentTime, const std::vector<double> & correlation, const unsigned firstCandidate, const unsigned secondCandidate) 00072 { 00073 _segmentation.maxPosition(currentTime); 00074 switch(_method) 00075 { 00076 case 2: 00077 doItSimilarity(currentTime, correlation, firstCandidate, secondCandidate); 00078 break; 00079 default: 00080 doItSimple(currentTime, correlation, firstCandidate, secondCandidate); 00081 } 00082 } 00083 00087 void doItSimple(CLAM::TData & currentTime, const std::vector<double> & correlation, const unsigned firstCandidate, const unsigned secondCandidate) 00088 { 00089 CLAM::TData firstCandidateWeight = correlation[firstCandidate]; 00090 CLAM::TData noCandidateWeight = correlation[0]; 00091 00092 unsigned currentChord = firstCandidateWeight*0.6<=noCandidateWeight || noCandidateWeight<0.001 ? 00093 0 : firstCandidate; 00094 00095 if(_segmentOpen) 00096 { 00097 if(!currentChord) 00098 closeSegment(currentTime); 00099 if(currentChord != _lastChord) 00100 closeSegment(currentTime); 00101 } 00102 if(!_segmentOpen) 00103 { 00104 if(currentChord) 00105 openSegment(currentTime, currentChord); 00106 } 00107 00108 _lastChord = currentChord; 00109 00110 if(_segmentOpen) 00111 _segmentation.dragOffset(_currentSegment, currentTime); 00112 } 00113 00117 void doItSimilarity(CLAM::TData & currentTime, const std::vector<double> & correlation, const unsigned firstCandidate, const unsigned secondCandidate) 00118 { 00119 CLAM::TData firstCandidateWeight = correlation[firstCandidate]; 00120 CLAM::TData noCandidateWeight = correlation[0]; 00121 00122 unsigned currentChord = firstCandidateWeight*0.6<=noCandidateWeight || noCandidateWeight<0.001 ? 00123 0 : firstCandidate; 00124 00125 unsigned segmentChord=0; 00126 00127 if(_segmentOpen) 00128 { 00129 for(unsigned i=0; i<correlation.size(); i++) 00130 _segmentChordCorrelation[i] += correlation[i]/correlation[0]; 00131 _framesInSegment++; 00132 estimateChord(_segmentChordCorrelation, segmentChord); 00133 _chordIndexes[_currentSegment] = segmentChord; 00134 00135 double segmentCorrelationDiffNew = (_segmentChordCorrelation[segmentChord] - _segmentChordCorrelation[currentChord]) / _framesInSegment; 00136 00137 double similarity = _chordSimilarity[currentChord][segmentChord]; 00138 00139 double similarityThreshold = 0.67; 00140 double correlationThreshold = 0.3; 00141 00142 if(!currentChord) 00143 { 00144 closeSegment(currentTime); 00145 _framesInSegment = 0; 00146 for(unsigned i=0; i<correlation.size(); i++) 00147 _segmentChordCorrelation[i] = 0; 00148 } 00149 00150 if (similarity < similarityThreshold) 00151 { 00152 if(segmentCorrelationDiffNew > correlationThreshold) 00153 { 00154 closeSegment(currentTime); 00155 _framesInSegment = 0; 00156 for(unsigned i=0; i<correlation.size(); i++) 00157 _segmentChordCorrelation[i] = 0; 00158 } 00159 } 00160 00161 } 00162 if(!_segmentOpen && currentChord) 00163 { 00164 openSegment(currentTime, currentChord); 00165 for(unsigned i=0; i<correlation.size(); i++) 00166 _segmentChordCorrelation[i] = correlation[i]/correlation[0]; 00167 _framesInSegment++; 00168 segmentChord=currentChord; 00169 } 00170 00171 if(_segmentOpen) 00172 _segmentation.dragOffset(_currentSegment, currentTime); 00173 } 00174 00175 void openSegment(CLAM::TData & currentTime, unsigned currentChord) 00176 { 00177 _chordIndexes.push_back(currentChord); 00178 _currentSegment = _segmentation.insert(currentTime); 00179 _segmentOpen = true; 00180 } 00181 void closeSegment(CLAM::TData & currentTime) 00182 { 00183 _segmentation.dragOffset(_currentSegment, currentTime); 00184 _segmentOpen = false; 00185 00186 switch(_method) 00187 { 00188 case 1: 00189 changeChordIfSegmentTooSmall(_currentSegment); 00190 break; 00191 case 2: 00192 changeChordIfSegmentTooSmall(_currentSegment); 00193 break; 00194 } 00195 00196 mergeSegmentIfIdenticalChordInPreviousSegment(_currentSegment); 00197 } 00198 00199 void changeChordIfSegmentTooSmall(unsigned & segment) 00200 { 00201 double minSegmentLength = 0.5; 00202 00203 std::vector<double> onsets = _segmentation.onsets(); 00204 std::vector<double> offsets = _segmentation.offsets(); 00205 unsigned lastSegment = onsets.size(); 00206 00207 if(offsets[segment]-onsets[segment] < minSegmentLength) 00208 { 00209 if(segment<lastSegment) 00210 if(offsets[segment]==onsets[segment+1]) 00211 _chordIndexes[segment] = _chordIndexes[segment+1]; 00212 if(segment>0) 00213 if(onsets[segment]==offsets[segment-1]) 00214 _chordIndexes[segment] = _chordIndexes[segment-1]; 00215 } 00216 } 00217 void mergeSegmentIfIdenticalChordInPreviousSegment(unsigned & segment) 00218 { 00219 CLAM::TData time = _segmentation.offsets()[segment]; 00220 if(segment>0) 00221 { 00222 if(_chordIndexes[segment] == _chordIndexes[segment-1] 00223 && _segmentation.onsets()[segment] == _segmentation.offsets()[segment-1]) 00224 { 00225 _segmentation.remove(segment); 00226 _chordIndexes.erase(_chordIndexes.begin()+segment); 00227 segment--; 00228 _segmentation.dragOffset(segment, time); 00229 } 00230 } 00231 } 00232 00233 void closeLastSegment(CLAM::TData & currentTime ) 00234 { 00235 _segmentation.maxPosition(currentTime); 00236 00237 if (_lastChord != 0) 00238 { 00239 _segmentation.dragOffset(_currentSegment, currentTime); 00240 _segmentOpen = false; 00241 } 00242 00243 switch(_method) 00244 { 00245 case 1: 00246 changeChordsForSmallSegments(); 00247 joinSegmentsWithIdenticalChords(); 00248 break; 00249 } 00250 } 00251 00252 void eraseAllSegments() 00253 { 00254 while( _segmentation.onsets().size() ) 00255 { 00256 _segmentation.remove(_segmentation.onsets().size()-1); 00257 _chordIndexes.pop_back(); 00258 } 00259 _segmentation.maxPosition(0); 00260 } 00261 00262 void estimateChord(const ChordCorrelator::ChordCorrelation & correlation, unsigned & estimatedChord) 00263 { 00264 double maxCorrelation = 0; 00265 double underMaxCorrelation = 0; 00266 unsigned maxIndex = 0; 00267 unsigned underMaxIndex = 0; 00268 for (unsigned i=0; i<correlation.size(); i++) 00269 { 00270 if (correlation[i]<underMaxCorrelation) continue; 00271 if (correlation[i]<maxCorrelation) 00272 { 00273 underMaxIndex=i; 00274 underMaxCorrelation=correlation[i]; 00275 continue; 00276 } 00277 underMaxIndex=maxIndex; 00278 underMaxCorrelation=maxCorrelation; 00279 maxIndex=i; 00280 maxCorrelation=correlation[i]; 00281 } 00282 estimatedChord = maxIndex; 00283 } 00284 00285 // 00286 // Post Processing Functions 00287 // 00288 00294 void changeChordsForSmallSegments() 00295 { 00296 for(unsigned segment=0; segment<_segmentation.onsets().size(); segment++) 00297 changeChordIfSegmentTooSmall(segment); 00298 } 00299 void joinSegmentsWithIdenticalChords() 00300 { 00301 for(unsigned segment=1; segment<_segmentation.onsets().size(); segment++) 00302 mergeSegmentIfIdenticalChordInPreviousSegment(segment); 00303 } 00304 00305 const CLAM::DiscontinuousSegmentation & segmentation() const { return _segmentation; }; 00306 const std::vector<unsigned> & chordIndexes() const { return _chordIndexes; }; 00307 void method(unsigned method) 00308 { 00309 _method=method; 00310 if(method != 0 && method != 1 && method != 2) 00311 _method = 0; 00312 00313 switch(_method) 00314 { 00315 case 2: 00316 ChordCorrelator chordCorrelator; 00317 _chordSimilarity = chordCorrelator.chordPatternsSimilarity(); 00318 for(unsigned i=0; i<101; ++i) 00319 _segmentChordCorrelation.push_back(0); 00320 break; 00321 } 00322 } 00323 }; 00324 } 00325 #endif//ChordSegmentator