D:/work/code_by/CbmrEngine/SongItem.cpp

查看本檔案說明文件.
00001 /*==============================================================================
00002 TSongItem代表的是一首資料庫中的歌,其欄位包括所有在index檔中的資料,還有db檔中的
00003 資料。
00004 《注意》目前對於複歌的處理還不夠完整,例如複製和寫入時並沒有考慮到複歌的部份
00005 ==============================================================================*/
00006 #include <math.h>
00007 #include <string.h>
00008 #include <stdlib.h>
00009 #include "SongItem.h"
00010 
00011 /*==============================================================================
00012 功能:建構子
00013 ==============================================================================*/
00014 TSongItem::TSongItem()
00015 {
00016   start = 0;
00017   note = NULL;
00018   mid = NULL;
00019   noteIndex = NULL;
00020   refrainIndex = NULL;
00021   midRefrainIndex = NULL;
00022   noteSize = midSize = noteIndexSize = refrainIndexSize = 0;
00023   score = 0.0;
00024   Fields = new TMyStringList();
00025 }
00026 
00027 /*==============================================================================
00028 功能:解構子
00029 ==============================================================================*/
00030 TSongItem::~TSongItem()
00031 {
00032   delete[] note;
00033   FreeMid();
00034   FreeRefrain();
00035   delete Fields;
00036 }
00037 
00048 /*==============================================================================
00049 功能:設定
00050 傳入:line為index檔中的一行字串
00051 ==============================================================================*/
00052 void TSongItem::SetProperty(char *line)
00053 {//Q:注意refStr有五個地方要改!
00054  // char *token, *value, refStr[100];
00055   char *token, *value, *refStr;//第一個
00056   int ref[100], len;
00057 
00058   if (line == NULL)
00059     return;
00060 
00061   //refStr[0] = '\0';
00062 refStr = NULL;//第二個
00063   len = strlen(line);
00064   if (line[len - 1] == '\n')
00065     line[len - 1] = '\0';
00066   //將index檔中的一行字串拆成一個個token
00067   token = strtok(line, "\t");
00068   while (token)
00069   {
00070     if ((value = strchr(token, '=')) != NULL)  //沒有"="則不視為欄位
00071     {
00072       value++;
00073       if (*value != '\0' && *value != ' ')  //若"xxx="後面沒有字串或是等於空白則當做沒有此欄位
00074       {
00075         if (!strncmp(token, "start=", 6))
00076           start = atoi(value);
00077         else if (!strncmp(token, "size=", 5))
00078           noteSize = atoi(value);
00079         else if (!strncmp(token, "refrain=", 8))
00080         {
00081 //          strncpy(refStr, value, 99);
00082                           delete[] refStr;
00083                   refStr = new char[strlen(value) + 1];
00084           strcpy(refStr, value);//以上三行為第三個
00085 
00086                 }
00087         else
00088         //Q:加的是xxx=xxx為一個token
00089           Fields->Add(token);
00090       }
00091     }
00092     token = strtok(NULL, "\t");
00093   }
00094 //  if (refStr[0] != '\0')  //有標示複歌
00095   if (refStr != NULL)  //有標示複歌//第四個
00096   {
00097     token = strtok(refStr, "+");
00098 
00099     while (token)
00100     {
00101     //Q:refrainIndex裡面長這個音符在note的哪裡.
00102     //Q:比方第一個複歌index是10,則refrainIndex[0]=20就是note[10*2]的地方.乘2的理由如下  
00103       ref[refrainIndexSize++] = atoi(token) * 2;  //複歌位置,乘以2是要把第幾個音符換算成note陣列的第幾個元素
00104       token = strtok(NULL, "+");
00105     }
00106     delete[] refStr;
00107     refrainIndex = new int[refrainIndexSize];
00108     memcpy(refrainIndex, ref, refrainIndexSize * sizeof(int));  //複歌位置
00109   }
00110 }
00111 
00122 /*==============================================================================
00123 功能:從db檔中讀取資料,放進note欄位
00124 傳入:DbStream為db檔
00125       note中的時間單位是1/64秒,轉換後的單位是resampleRate/64秒
00126 傳出:是否成功
00127 ==============================================================================*/
00128 //Q:resampleRate=4
00129 bool TSongItem::Read(TInputStream *DbStream, int resampleRate)
00130 {
00131   unsigned char uc;
00132   delete[] note;
00133   note = new unsigned char[noteSize];
00134   DbStream->Reset();
00135 
00136   DbStream->Skip(start);
00137   DbStream->Reads(note, noteSize);
00138   uc = DbStream->Read();
00139   if (uc != 0xFF){
00140     return false;
00141 }
00142 
00143   Ready(resampleRate);
00144   return true;
00145 }
00146 
00154 /*==============================================================================
00155 功能:將一首歌寫進index和db檔
00156 傳入:IndexStream為index檔,DbStream為db檔
00157 ==============================================================================*/
00158 void TSongItem::Write(TOutputStream *IndexStream, TOutputStream *DbStream)
00159 {
00160   char str[100];
00161   int i, j;
00162   unsigned char *encodedNote;
00163 
00164   start = DbStream->Size();
00165   sprintf(str, "start=%d\tsize=%d", start, noteSize);  //寫入index檔
00166   IndexStream->Writes(str, strlen(str));
00167   for (i = 0; i < Fields->count; i++)
00168   {
00169     sprintf(str, "\t%s", Fields->Strings(i));
00170     IndexStream->Writes(str, strlen(str));
00171   }
00172   if (refrainIndexSize > 0)
00173   {
00174     sprintf(str, "\trefrain=%d", refrainIndex[0]);
00175     IndexStream->Writes(str, strlen(str));
00176     for (i = 1; i < refrainIndexSize; i++)
00177     {
00178       sprintf(str, "+%d", refrainIndex[i]);
00179       IndexStream->Writes(str, strlen(str));
00180     }
00181   }
00182   IndexStream->Write('\n');
00183   DbStream->Writes(note, noteSize);  //寫入db檔
00184   DbStream->Write(0xFF);
00185 }
00196 /*==============================================================================
00197 功能:將note轉換成中介格式
00198 傳入:note中的時間單位是1/64秒,轉換後的單位是resampleRate/64秒
00199       scale是放大倍率
00200 //Q: resampleRate = 4,scale=INTEGER_SCALE=10  //因為用整數代替浮點數而乘上的倍數        
00201 ==============================================================================*/
00202 void TSongItem::NoteToMid(int resampleRate, int scale)
00203 {
00204         
00205   int i, j, k, size;
00206 //Q:mid和noteIndex都是這裡才建的.mid是中介格式,noteIndex每個音符起點在mid中的索引,
00207 //Q:它紀錄了note和mid的對應.又原本的note可能有包含休止符.noteIndex是去掉休止符後和mid的對應
00208 
00209   delete[] mid;
00210   mid = new int[midSize];
00211   delete[] noteIndex;
00212 //  printf("noteIndexSize is %d\n",noteIndexSize);      
00213 
00214   noteIndex = new int[noteIndexSize];
00215 //  printf("BBB\n");    
00216 
00217   for (i = 0, j = -1, k = 0, size = 0; i < midSize; i++)
00218   {
00219  //Q:i是mid的index值,j是note的index值,k是noteIndex的index值
00220  //Q:j是音長,j-1是音高.同一個音很長的時候,前一次的size += note[j += 2]就會加很多,
00221  //Q:那種情況下while就不會進入,直到跑完那個音長為止,所以mid可以是mid[3]~mid[30]都是Do這樣.
00222  //Q:所以mid是note的展開,但又經過resample處理.ex:note裡是Do8mi4Do4(音高音長)轉成mid變成DoDomiDo
00223     while (size <= i * resampleRate)
00224     {
00225       size += note[j += 2];
00226       if (k < noteIndexSize && note[j - 1] != 0)
00227         noteIndex[k++] = i;  //每個音符起點在mid中的索引
00228         //Q:有點像原本的note對應到mid的哪一個值.noteIndex[1],noteIndex[2],noteIndex[3]可能都等於2,
00229         //Q:表示note的這三個音符都指向mid[2]=某個音高,不過這三個音符不一定都相等.被縮小了~減成1/4
00230     }
00231     //Q:將note對應的音高值存進mid;resampleRate是4,表示轉成中介格式只取1/4
00232     //Q:轉成中介格式有很多方法,如四點取中位數,這裡看起來是取四點的最後一點(以音長為基準,而不是音高)
00233     mid[i] = note[j - 1] * scale;
00234 //printf("%d=%d,",i,mid[i]);
00235 
00236   }
00237 
00238 }
00243 /*==============================================================================
00244 功能:將中介格式釋放掉,為了節省記憶體
00245 ==============================================================================*/
00246 void TSongItem::FreeMid(void)
00247 {
00248   delete[] mid;
00249   mid = NULL;
00250   delete[] noteIndex;
00251   noteIndex = NULL;
00252 }
00257 /*==============================================================================
00258 功能:將複歌索引釋放掉
00259 ==============================================================================*/
00260 void TSongItem::FreeRefrain(void)
00261 {
00262   refrainIndexSize = 0;
00263   delete[] refrainIndex;
00264   refrainIndex = NULL;
00265   delete[] midRefrainIndex;
00266   midRefrainIndex = NULL;
00267 }
00272 /*==============================================================================
00273 功能:當note遭修改時,必須呼叫此函式,否則無法哼唱搜尋
00274 ==============================================================================*/
00275 void TSongItem::Ready(int resampleRate)
00276 {//Q:resampleRate=4
00277   int i, j, k, size;
00278 
00279   //算出展開成中介格式後的長度
00280   //Q:db檔中每首歌是以音高音長音高音長!
00281   for (i = 1, midSize = 0; i < noteSize; i += 2)
00282     midSize += note[i];
00283   midSize /= resampleRate;
00284   //算出這首歌有幾個音符為起點的片段
00285   //Q:其實是在算有幾個音符片段,但是會扣掉後面一段SEGMENT_SIZE的樣子
00286   //Q:?????這段不懂.音符為起點?"資料庫歌曲的片段長度"SEGMENT_SIZE=128
00287   //Q:noteSize-1是減掉每首歌最後寫入的0xFF嗎?j是音高or音長?音長?
00288   //Q:但是這個for只跑一次,一旦if成立就break?那不是只有找一次128?
00289   //Q:照這樣看來,就是從後面往前,找到即將大於(128+1)*4的那個點
00290   for (j = noteSize - 1, size = 0; j >= 1; j -= 2)
00291     if ((size += note[j]) >= (SEGMENT_SIZE + 1) * resampleRate)
00292       break;
00293   //Q:下面這一行應該縮排?在上面的for之下?
00294   //Q:其實上面那個for只用來刪去最後的128*4???從上面break後size本來就會符合下面這個if
00295 //  j = noteSize - 1;
00296   if (size >= (SEGMENT_SIZE + 1) * resampleRate)
00297     for (i = 0, noteIndexSize = 0; i < j; i += 2)
00298       if (note[i] != 0) //Q:看起來像去掉休止符,因為音高=0表示沒有音高
00299         noteIndexSize++;
00300   //找出這首歌每個複歌起點
00301   if (noteSize > 0)
00302   {
00303     delete[] midRefrainIndex;
00304     midRefrainIndex = new int[refrainIndexSize + 1];
00305 
00306     midRefrainIndex[0] = 0;  //一定從頭比對
00307     for (i = 0, j = -1, k = 0, size = 0; k < refrainIndexSize; i++)
00308       while (size <= i * resampleRate)
00309       {
00310         size += note[j += 2];
00311     //           printf("j is%d and noteSizeis %d\n",j,noteSize);
00312 
00313         if (refrainIndex[k] == j - 1)
00314         {
00315           midRefrainIndex[++k] = i;  //複歌位置在mid中的索引
00316           if (k >= refrainIndexSize)
00317             break;
00318         }
00319       }
00320   }
00321 }
00322 

產生日期:Tue Jul 11 11:52:19 2006, 專案:cbmr, 產生器:  doxygen 1.4.7