00001
00002
00003
00004 #include <stdlib.h>
00005 #include <string.h>
00006 #include <math.h>
00007 #include "SongDb.h"
00008 #include "ListTools.h"
00009 #include "cbmrdtw.h"
00010
00011 #define WARP_NUM 17 //歌聲的伸縮次數,改了這個還要改MIN_WARP_RATE
00012 #define MIN_WARP_RATE 70 //表示最小的伸縮倍率,依WARP_NUM調整,單位是%
00013 #define CONSTA 0.9
00014 #define CONSTB 0.5
00015
00022
00023
00024
00025
00026
00027
00028 static void shiftMid(int *mid, int size, int level)
00029 {
00030 int i;
00031
00032 for (i = 0; i < size; i++)
00033 mid[i] += level;
00034 }
00041
00042
00043
00044
00045
00046 static void shiftMidToMeanZero(int *mid, int size)
00047 {
00048 int i, total;
00049
00050 for (i = 0, total = 0; i < size; i++)
00051 total += mid[i];
00052 shiftMid(mid, size, -total / size);
00053 }
00061
00062
00063
00064
00065
00066
00067 static void insertArray(int *array, int size, int value)
00068 {
00069 int i, j;
00070
00071 for (i = size - 1; i >= 0; i--)
00072 if (value >= array[i])
00073 break;
00074 if (i < size - 1)
00075 {
00076 for (j = size - 1; j > i + 1; j--)
00077 array[j] = array[j - 1];
00078 array[i + 1] = value;
00079 }
00080 }
00081
00082 static bool strequal(char *str1, char *str2)
00083 {
00084 char c1, c2;
00085
00086 while (*str1 != '\0' && *str2 != '\0')
00087 {
00088 c1 = (*str1 >= 'a' && *str1 <= 'z')? *str1 - 32 : *str1;
00089 c2 = (*str2 >= 'a' && *str2 <= 'z')? *str2 - 32 : *str2;
00090 if (c1 != c2)
00091 return false;
00092 if (c1 < 0 && c2 < 0)
00093 {
00094 if (*(++str1) != *(++str2))
00095 return false;
00096 }
00097 ++str1;
00098 ++str2;
00099 }
00100 return *str1 == *str2;
00101 }
00102
00103 static bool strnequal(char *str1, char *str2, int n)
00104 {
00105 int i;
00106 char c1, c2;
00107
00108 i = 0;
00109 while (*str1 != '\0' && *str2 != '\0' && i++ < n)
00110 {
00111 c1 = (*str1 >= 'a' && *str1 <= 'z')? *str1 - 32 : *str1;
00112 c2 = (*str2 >= 'a' && *str2 <= 'z')? *str2 - 32 : *str2;
00113 if (c1 != c2)
00114 return false;
00115 if (*str1 < 0 && *str2 < 0)
00116 {
00117 if (*(++str1) != *(++str2))
00118 return false;
00119 ++i;
00120 }
00121 ++str1;
00122 ++str2;
00123 }
00124 return i == n || *str1 == *str2;
00125 }
00126
00127 int min(int a, int b)
00128 {
00129 return a <= b? a : b;
00130 }
00131
00132
00133
00134
00135 TSongDb::TSongDb()
00136 {
00137 resampleRate = 4;
00138 songNumber = 0;
00139 compareProgress1 = compareProgress2 = 0;
00140 Song = NULL;
00141 topSongIndex = NULL;
00142 SetOption(COMPARE_FROM_ANYWHERE);
00143 }
00144
00145
00146
00147
00148 TSongDb::~TSongDb()
00149 {
00150 int i;
00151
00152 for (i = 0; i < songNumber; i++)
00153 delete Song[i];
00154 delete[] Song;
00155 delete[] topSongIndex;
00156 }
00157
00158 void TSongDb::SetOption(int from, char *language, int method)
00159 {
00160 compareFrom = from;
00161
00162 if (language == NULL)
00163 compareLanguage[0] = '\0';
00164 else
00165 strcpy(compareLanguage, language);
00166 compareMethod = method;
00167 }
00178
00179
00180
00181
00182
00183
00184
00185 bool TSongDb::Read(TInputStream *IndexStream, TInputStream *DbStream, bool _expand)
00186 {
00187 TMyList *List;
00188 TDataInputStream *DIStream;
00189 bool success;
00190 char strLine[1000];
00191 TSongItem *SongItem, **SongTemp;
00192 int i;
00193
00194 List = new TMyList();
00195 DIStream = new TDataInputStream(IndexStream);
00196 success = true;
00197 while (DIStream->ReadLine(strLine, 1000) > 0)
00198 {
00199 SongItem = new TSongItem();
00200 SongItem->SetProperty(strLine);
00201 if (DbStream)
00202 if (!SongItem->Read(DbStream, resampleRate))
00203 {
00204 delete SongItem;
00205 success = false;
00206 break;
00207 }
00208 List->Add(SongItem);
00209 }
00210 delete DIStream;
00211
00212 SongTemp = Song;
00213 Song = new TSongItem*[songNumber + List->count];
00214 memcpy(Song, SongTemp, songNumber * sizeof(TSongItem *));
00215 for (i = 0; i < List->count; i++)
00216 Song[songNumber + i] = (TSongItem *)List->Items(i);
00217 songNumber += List->count;
00218 delete List;
00219 delete[] SongTemp;
00220
00221 delete[] topSongIndex;
00222 topSongIndex = new int[songNumber];
00223
00224 expand = _expand;
00225 if (expand)
00226 for (i = 0; i < songNumber; i++)
00227 Song[i]->NoteToMid(resampleRate, INTEGER_SCALE);
00228
00229 return success;
00230 }
00238
00239
00240
00241
00242
00243
00244 bool TSongDb::Write(TOutputStream *IndexStream, TOutputStream *DbStream)
00245 {
00246 return Write(IndexStream, DbStream, 0, songNumber - 1);
00247 }
00257
00258
00259
00260
00261
00262
00263
00264
00265 bool TSongDb::Write(TOutputStream *IndexStream, TOutputStream *DbStream, int fromIndex, int toIndex)
00266 {
00267 int i;
00268
00269 if (fromIndex < 0)
00270 fromIndex = 0;
00271 if (toIndex > songNumber - 1)
00272 toIndex = songNumber - 1;
00273
00274 for (i = fromIndex; i <= toIndex; i++)
00275 Song[i]->Write(IndexStream, DbStream);
00276
00277 return true;
00278 }
00285
00286
00287
00288
00289
00290 int TSongDb::AddSong(TSongItem *SongItem)
00291 {
00292 TSongItem **SongTemp;
00293
00294 SongTemp = Song;
00295 Song = new TSongItem *[songNumber + 1];
00296 memcpy(Song, SongTemp, songNumber * sizeof(TSongItem *));
00297 Song[songNumber] = SongItem;
00298 delete[] SongTemp;
00299
00300 delete[] topSongIndex;
00301 topSongIndex = new int[songNumber + 1];
00302
00303 return songNumber++;
00304 }
00310
00311
00312
00313
00314 void TSongDb::DeleteSong(int index)
00315 {
00316 int i;
00317
00318 if (index >= 0 && index < songNumber)
00319 {
00320 delete Song[index];
00321 for (i = index; i < songNumber - 1; i++)
00322 Song[i] = Song[i + 1];
00323 delete[] topSongIndex;
00324 topSongIndex = new int[--songNumber];
00325 }
00326 }
00332
00333
00334
00335
00336 void TSongDb::DeleteSong(TSongItem *SongItem)
00337 {
00338 int i;
00339
00340 for (i = 0; i < songNumber; i++)
00341 if (Song[i] == SongItem)
00342 {
00343 DeleteSong(i);
00344 break;
00345 }
00346 }
00353
00354
00355
00356
00357
00358 TSongItem *TSongDb::GetSong(int index)
00359 {
00360 return (index >= 0 && index < songNumber)? Song[index] : NULL;
00361 }
00367
00368
00369
00370
00371 int TSongDb::GetSongNumber(void)
00372 {
00373 return songNumber;
00374 }
00381
00382
00383
00384
00385
00386 void TSongDb::Compare(int *waveMid, int waveSize)
00387 {
00388 Compare(waveMid, waveSize, 0, songNumber - 1);
00389 }
00402
00403
00404
00405
00406
00407
00408
00409 void TSongDb::Compare(int *waveMid, int waveSize, int fromIndex, int toIndex)
00410 {
00411 int i, j, k;
00412
00413 int waveCopy[300], *midiCopy;
00414
00415 int waveWarp[WARP_NUM][1280];
00416 int waveWarpSize[WARP_NUM];
00417 int lsDist, lsDistThreshold, lsDistTemp;
00418 float score;
00419 int *segmentIndex, segmentIndexSize;
00420 int maxMidiSize, midiSize;
00421 int dtwDistance_c, dtwDistance_l, dtwDistance_r, minDtwDistance;
00422 int dtwStringLength_c, dtwStringLength_l, dtwStringLength_r, minDtwStringLength;
00423 bool allLanguage, dtwFixLeft;
00424 TSongItem *CurrentSong;
00425 int minNum;
00426 int *minDist;
00427 int *nonzeroMid;
00428 int best, fromSegment, toSegment;
00429 FILE *fTree;
00430
00431 const int topRank = 100;
00432 const float multiple = 1.4f;
00433
00434 if (fromIndex < 0)
00435 fromIndex = 0;
00436 if (toIndex >= songNumber)
00437 toIndex = songNumber - 1;
00438 compareProgress1 = compareProgress2 = 0;
00439 if (compareMethod == METHOD_LINEAR_SCALING || compareMethod == METHOD_LSDTW)
00440 {
00441 compareNum1 = toIndex - fromIndex + 1;
00442
00443 compareNum2 = compareMethod == METHOD_LINEAR_SCALING? 0 : min(compareNum1, topRank);
00444
00445 }
00446 else
00447 {
00448 compareNum1 = 0;
00449 compareNum2 = toIndex - fromIndex + 1;
00450 }
00451 stopCompare = false;
00452
00453 allLanguage = compareLanguage[0] == '\0';
00454
00455 for (i = fromIndex; i <= toIndex; i++)
00456 {
00457
00458
00459
00460 topSongIndex[i - fromIndex] = i;
00461 Song[i]->bestSegment = 0;
00462 Song[i]->score = 0.0f;
00463 }
00464 if (waveSize < 3)
00465 return;
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475 if (compareMethod == METHOD_LINEAR_SCALING || compareMethod == METHOD_LSDTW)
00476 {
00477
00478
00479 waveCopy[0] = waveMid[0];
00480
00481 for (i = 1; i < waveSize; i++)
00482 waveCopy[i] = (waveMid[i] == 0)? waveCopy[i - 1] : waveMid[i];
00483
00484 for (i = 0; i < WARP_NUM; i++)
00485 {
00486
00487 j = MIN_WARP_RATE + i * 5;
00488 waveWarpSize[i] = (waveSize - 1) * j / 100;
00489
00490
00491 for (k = 0; k < waveWarpSize[i]; k++)
00492 {
00493 int _m = 100 * k / j;
00494 float _n = 100.0f * k / j - _m;
00495 waveWarp[i][k] = floor((1.0 - _n) * waveCopy[_m] + _n * waveCopy[_m + 1] + 0.5);
00496 }
00497 shiftMidToMeanZero(waveWarp[i], waveWarpSize[i]);
00498 }
00499
00500 midiCopy = new int[waveWarpSize[WARP_NUM - 1]];
00501
00502
00503
00504 minDist = new int[minNum = (compareMethod == METHOD_LSDTW)? compareNum2 : min(compareNum1, topRank)];
00505 for (i = 0; i < minNum; i++)
00506 minDist[i] = INFINITE_INT;
00507
00508
00509
00510
00511 for (compareProgress1 = fromIndex; compareProgress1 <= toIndex && !stopCompare; compareProgress1++)
00512 {
00513 CurrentSong = Song[compareProgress1];
00514 if (!allLanguage && strcmp(CurrentSong->Fields->Values("language"), compareLanguage))
00515
00516 continue;
00517
00518
00519
00520
00521 if (!expand){
00522 CurrentSong->NoteToMid(resampleRate, INTEGER_SCALE);
00523 }
00524
00525
00526 if (compareFrom == COMPARE_FROM_REFRAIN)
00527 {
00528 segmentIndex = CurrentSong->midRefrainIndex;
00529 segmentIndexSize = CurrentSong->refrainIndexSize + 1;
00530 }
00531 else
00532 {
00533 segmentIndex = CurrentSong->noteIndex;
00534 segmentIndexSize = (compareFrom == COMPARE_FROM_HEAD)? min(1, CurrentSong->noteIndexSize) : CurrentSong->noteIndexSize;
00535 }
00536 if (segmentIndexSize == 0)
00537 continue;
00538
00539
00540
00541
00542 nonzeroMid = new int[midiSize = (compareFrom == COMPARE_FROM_HEAD)? min(CurrentSong->midSize,waveWarpSize[WARP_NUM - 1]) : CurrentSong->midSize - segmentIndex[0]];
00543
00544
00545 nonzeroMid[0] = CurrentSong->mid[j = segmentIndex[0]];
00546
00547
00548
00549 for (i = 1; i < midiSize; i++)
00550 {
00551
00552 nonzeroMid[i] = (CurrentSong->mid[j + i] == 0)? nonzeroMid[i - 1] : CurrentSong->mid[j + i];
00553
00554 }
00555 lsDistThreshold = minDist[minNum - 1];
00556
00557 for (i = 0; i < segmentIndexSize; i++)
00558 {
00559 if (segmentIndex[i] - segmentIndex[0] + waveWarpSize[WARP_NUM - 1] > midiSize)
00560 break;
00561
00562
00563
00564
00565
00566
00567 memcpy(midiCopy, nonzeroMid + segmentIndex[i] - segmentIndex[0], min(midiSize,waveWarpSize[WARP_NUM - 1]) * sizeof(int));
00568
00569 for (j = WARP_NUM - 1; j >= 0; j--)
00570 {
00571
00572 shiftMidToMeanZero(midiCopy, waveWarpSize[j]);
00573
00574
00575
00576 lsDistTemp = lsDistThreshold == INFINITE_INT? INFINITE_INT : lsDistThreshold * waveWarpSize[j];
00577
00578 for (k = 0, lsDist = 0; k < waveWarpSize[j]; k++)
00579 if ((lsDist += abs(waveWarp[j][k] - midiCopy[k])) > lsDistTemp)
00580 break;
00581
00582
00583
00584
00585 if (lsDist < lsDistTemp)
00586 {
00587 lsDistThreshold = lsDist / waveWarpSize[j];
00588 CurrentSong->bestSegment = i;
00589 }
00590
00591
00592
00593
00594 }
00595
00596 }
00597 delete[] nonzeroMid;
00598
00599 if (lsDistThreshold < minDist[minNum - 1])
00600 {
00601
00602
00603
00604 CurrentSong->score = 100.0f - (float)lsDistThreshold / INTEGER_SCALE;
00605 insertArray(minDist, minNum, lsDistThreshold);
00606
00607
00608
00609
00610 }
00611
00612 if (!expand)
00613 CurrentSong->FreeMid();
00614
00615 }
00616 delete[] minDist;
00617 delete[] midiCopy;
00618
00619 if (stopCompare)
00620 return;
00621 SortByScore(compareNum1, minNum);
00622
00623 }
00624
00625
00626
00627
00628
00629 if (compareMethod == METHOD_DTW || compareMethod == METHOD_LSDTW || compareMethod == METHOD_NOTEBEGIN_DTW)
00630 {
00631
00632
00633
00634 for (i = 0, j = 0; i < waveSize; i++)
00635 if (waveMid[i] != 0)
00636 waveCopy[j++] = waveMid[i];
00637 waveSize = j;
00638
00639 shiftMidToMeanZero(waveCopy, waveSize);
00640
00641
00642
00643 dtwFixLeft = (compareMethod == METHOD_LSDTW || compareMethod == METHOD_NOTEBEGIN_DTW || compareFrom == COMPARE_FROM_HEAD);
00644
00645 maxMidiSize = dtwFixLeft? (waveSize * multiple) : INFINITE_INT;
00646 minDist = new int[minNum = min(compareNum2, 100)];
00647 for (i = 0; i < minNum; i++)
00648 minDist[i] = INFINITE_INT;
00649
00650 for (compareProgress2 = 0; compareProgress2 < compareNum2 && !stopCompare; compareProgress2++)
00651 {
00652 CurrentSong = Song[topSongIndex[compareProgress2]];
00653 CurrentSong->score = 0.0f;
00654 if (!allLanguage && strcmp(CurrentSong->Fields->Values("language"), compareLanguage))
00655 continue;
00656
00657 if (!expand)
00658 CurrentSong->NoteToMid(resampleRate, INTEGER_SCALE);
00659
00660 if (compareFrom == COMPARE_FROM_REFRAIN)
00661 {
00662 segmentIndex = CurrentSong->midRefrainIndex;
00663 segmentIndexSize = CurrentSong->refrainIndexSize + 1;
00664 }
00665 else
00666 {
00667 segmentIndex = CurrentSong->noteIndex;
00668 segmentIndexSize = compareFrom == COMPARE_FROM_HEAD? min(1, CurrentSong->noteIndexSize) : CurrentSong->noteIndexSize;
00669 }
00670 if (segmentIndexSize == 0)
00671 continue;
00672
00673 midiCopy = new int[min(CurrentSong->midSize, maxMidiSize)];
00674
00675 if (compareMethod == METHOD_DTW || compareMethod == METHOD_LSDTW)
00676 fromSegment = toSegment = CurrentSong->bestSegment;
00677 else
00678 {
00679 fromSegment = 0;
00680 toSegment = segmentIndexSize - 1;
00681 }
00682
00683
00684 for (best = fromSegment, minDtwDistance = INFINITE_INT; best <= toSegment; best++)
00685 {
00686
00687
00688 for (j = segmentIndex[best], midiSize = 0; j < CurrentSong->midSize && midiSize < maxMidiSize; j++)
00689 if (CurrentSong->mid[j] != 0)
00690 midiCopy[midiSize++] = CurrentSong->mid[j];
00691 if (midiSize == 0)
00692 continue;
00693
00694 shiftMidToMeanZero(midiCopy, midiSize);
00695
00696 dtwDistance_c = dtw(waveCopy, waveSize, midiCopy, midiSize, dtwFixLeft, &dtwStringLength_c, minDist[minNum - 1]);
00697 for (j = INTEGER_SCALE; j >= (INTEGER_SCALE >> 1) && j > 0; j >>= 1)
00698 {
00699
00700
00701 shiftMid(midiCopy, midiSize, -j);
00702 dtwDistance_l = dtw(waveCopy, waveSize, midiCopy, midiSize, dtwFixLeft, &dtwStringLength_l, minDist[minNum - 1]);
00703 shiftMid(midiCopy, midiSize, j * 2);
00704 dtwDistance_r = dtw(waveCopy, waveSize, midiCopy, midiSize, dtwFixLeft, &dtwStringLength_r, minDist[minNum - 1]);
00705 if (dtwDistance_l < dtwDistance_c && dtwDistance_l < dtwDistance_r)
00706 {
00707 dtwDistance_c = dtwDistance_l;
00708 shiftMid(midiCopy, midiSize, -j * 2);
00709 }
00710 else if (dtwDistance_r < dtwDistance_c && dtwDistance_r < dtwDistance_l)
00711 dtwDistance_c = dtwDistance_r;
00712 else
00713 shiftMid(midiCopy, midiSize, -j);
00714 }
00715
00716 insertArray(minDist, minNum, dtwDistance_c);
00717 if (dtwDistance_c < minDtwDistance)
00718 {
00719 minDtwDistance = dtwDistance_c;
00720 minDtwStringLength = dtwStringLength_c;
00721 }
00722 }
00723
00724 if (minDtwDistance < INFINITE_INT)
00725 CurrentSong->score = (CONSTA * pow(CONSTB, (double)minDtwDistance / INTEGER_SCALE / waveSize) + (1.0 - CONSTA) * minDtwStringLength / waveSize) * 100.0;
00726
00727 if (!expand)
00728 CurrentSong->FreeMid();
00729
00730 delete[] midiCopy;
00731 }
00732 delete[] minDist;
00733 if (!stopCompare)
00734 SortByScore(compareNum2, minNum);
00735 }
00736
00737 }
00738
00748
00749
00750
00751
00752
00753
00754
00755
00756 int TSongDb::Compare(char *fieldName, char *value, bool exactMatch, bool *result)
00757 {
00758 int i, size, count;
00759 char *c;
00760
00761 count = 0;
00762 if (exactMatch)
00763 {
00764 for (i = 0; i < songNumber; i++)
00765 if (result[i] = strequal(Song[i]->Fields->Values(fieldName), value))
00766 ++count;
00767 }
00768 else
00769 {
00770 size = strlen(value);
00771 for (i = 0; i < songNumber; i++)
00772 {
00773 result[i] = false;
00774 c = Song[i]->Fields->Values(fieldName);
00775 while (*c != '\0')
00776 {
00777 if (strnequal(c, value, size))
00778 {
00779 result[i] = true;
00780 ++count;
00781 break;
00782 }
00783 c += *c < 0? 2 : 1;
00784 }
00785 }
00786 }
00787
00788 return count;
00789 }
00800
00801
00802
00803
00804
00805
00806
00807
00808 int TSongDb::Compare(char *fieldName, int size, int operate, bool *result)
00809 {
00810 int i, j, count;
00811 char *c;
00812
00813 count = 0;
00814 for (i = 0; i < songNumber; i++)
00815 {
00816 for (c = Song[i]->Fields->Values(fieldName), j = 0; *c != 0;)
00817 {
00818 if (*c < 32 || (*c > 47 && *c < 58) || (*c > 64 && *c < 91) || (*c > 96 && *c < 123) || *c < 0)
00819 j++;
00820 c += *c < 0? 2 : 1;
00821 }
00822 if (result[i] = (operate == 0 && j == size) || (j - size) * operate > 0)
00823 ++count;
00824 }
00825
00826 return count;
00827 }
00834
00835
00836
00837
00838
00839 void TSongDb::SortByScore(int sortNum, int topNum)
00840 {
00841 int i, j, k, tempd;
00842 float tempf;
00843 float *score;
00844
00845 score = new float[sortNum];
00846 for (i = 0; i < sortNum; i++)
00847 score[i] = Song[topSongIndex[i]]->score;
00848 for (i = 0; i < topNum; i++)
00849 {
00850 k = i;
00851 for (j = i + 1; j < sortNum; j++)
00852 if (score[k] < score[j])
00853 k = j;
00854 if (k != i)
00855 {
00856 tempd = topSongIndex[i];
00857 topSongIndex[i] = topSongIndex[k];
00858 topSongIndex[k] = tempd;
00859 tempf = score[i];
00860 score[i] = score[k];
00861 score[k] = tempf;
00862 }
00863 }
00864 delete[] score;
00865 }
00873
00874
00875
00876
00877
00878 TSongItem *TSongDb::GetTopSong(int rank)
00879 {
00880 return Song[topSongIndex[rank]];
00881 }
00888
00889
00890
00891
00892
00893 int TSongDb::GetTopSongIndex(int rank)
00894 {
00895 return topSongIndex[rank];
00896 }
00900
00901
00902
00903 void TSongDb::StopCompare(void)
00904 {
00905 stopCompare = true;
00906 }
00912
00913
00914
00915
00916 int TSongDb::GetCompareProgress(void)
00917 {
00918 if (compareNum1 == 0 && compareNum2 == 0)
00919 return 0;
00920 if (compareNum1 == 0)
00921 return compareProgress2 * 100 / compareNum2;
00922 else if (compareNum2 == 0)
00923 return compareProgress1 * 100 / compareNum1;
00924 else
00925 return compareProgress1 * 95 / compareNum1 +
00926 compareProgress2 * 5 / compareNum2;
00927 }
00928