0 0 0 0 0 0 0 0 0 0 EOD; $ret.=<< 0 0 -898 0 0 0 64 0 EOD; for($i=1; $i<4; $i++){ $ret.=<< $i 0 -898 0 0 0 64 0 EOD; } $ret.=<< 0 -898 0 0 0 64 0 0 0 0 -129 480 4 0 4 4 0 $BPM EOD; return $ret; } class Note{ /** * * YuyuSequencerの出力する表記をMIDI note numberに変換する。 * 解釈は文脈(トラック)で変化。 * @param string $note * @param int $part 0: メインボーカルのトラック, 1-3: バックコーラスのトラック1~3。 */ function getNoteNum($note=0, $part=0){ $noteNum=array(); // 音感と基礎知識がイマイチなので未検証。結構怪しい。 $noteNum['0']=array(0, 0, 0, 0); // R $noteNum['1']=array(59, 59, 62, 65); // B:Bm-5 $noteNum['2']=array(60, 60, 64, 67); // C:C $noteNum['3']=array(62, 62, 65, 69); // D:Dm $noteNum['4']=array(64, 64, 67, 71); // E:Em $noteNum['5']=array(65, 65, 69, 72); // F:F $noteNum['6']=array(67, 67, 71, 74); // G:G $noteNum['7']=array(69, 69, 72, 76); // A:Am $noteNum['8']=array(71, 71, 74, 77); // B:Bm-5 $noteNum['9']=array(72, 72, 76, 79); // C:C $noteNum['A']=array(74, 59, 62, 65); // D:Bm $noteNum['B']=array(76, 59, 63, 67); // E:Cm $noteNum['C']=array(77, 62, 66, 69); // F:D $noteNum['D']=array(79, 64, 68, 71); // G:E $noteNum['E']=array(61, 65, 68, 72); // C#:Fm $noteNum['F']=array(63, 67, 70, 74); // D#:Gm $noteNum['G']=array(66, 69, 73, 76); // F#:A $noteNum['H']=array(68, 71, 74, 78); // G#:Bm $noteNum['I']=array(70, 72, 75, 79); // A#:Cm $noteNum['J']=array(73, 59, 63, 66); // C#:B $noteNum['K']=array(75, 71, 75, 78); // D#:B $noteNum['L']=array(78, 73, 76, 80); // F#:C# $noteNum['M']=array(80, 63, 66, 70); // G#:D#m $noteNum['N']=array(66, 66, 70, 73); // -:F# $noteNum['O']=array(68, 68, 72, 75); // -:G# $noteNum['P']=array(70, 70, 73, 77); // -:A#m $noteNum['Q']=array(73, 73, 77, 80); // -:C# $noteNum['R']=array(73, 73, 76, 80); // -:C#m $noteNum['S']=array(75, 75, 79, 82); // -:D# $noteNum['T']=array(66, 66, 69, 73); // -:F#m $noteNum['U']=array(68, 68, 71, 75); // -:G#m $noteNum['V']=array(70, 70, 74, 77); // -:A# $noteNum['W']=array(73, 73, 76, 80); // -:C#m $ret=$noteNum[$note][$part]; $ret=($ret!='')?$ret:58; // 何か聞こえた方がいいかもという。 return $ret; } /** * * MIDI note numberに相当する発音記号(??)を返す。 * どれみふぁに相当する言葉の発音をVocaloid用表記の文字列で返す。 * @param int $noteNum 音程。MIDI note numberに相当。 * @return string 1音節の発音記号文字列。 */ function getPhnms($noteNum){ $phnms="J a"; // そこはかとないエラー対策 switch($noteNum){ case 80: case 79:$phnms='s o'; break; case 78: case 77:$phnms='p\ a'; break; case 76:$phnms="m' i"; break; case 75: case 74:$phnms='4 e'; break; case 73: case 72:$phnms='d o'; break; case 71:$phnms='s i'; break; case 70: case 69:$phnms='4 a'; break; case 68: case 67:$phnms='s o'; break; case 66: case 65:$phnms='p\ a'; break; case 64:$phnms="m' i"; break; case 63: case 62:$phnms='4 e'; break; case 61: case 60:$phnms='d o'; break; case 59:$phnms='s i'; break; } return $phnms; } /** * * MIDIでいうnote numberに相当する歌詞(どれみふぁ)を返す。 * @param int $noteNum 音程。MIDI note numberに相当する。 * @return string どれみふぁで表記した歌詞。Vocaloid Editorでの1音節単位。 */ function getLyric($noteNum){ $lyric='にゃ'; switch($noteNum){ case 80: case 79:$lyric="そ"; break; case 78: case 77:$lyric="ふぁ"; break; case 76:$lyric="み"; break; case 75: case 74:$lyric="れ"; break; case 73: case 72:$lyric="ど"; break; case 71:$lyric="し"; break; case 70: case 69:$lyric="ら"; break; case 68: case 67:$lyric="そ"; break; case 66: case 65:$lyric="ふぁ"; break; case 64:$lyric="み"; break; case 63: case 62:$lyric="れ"; break; case 61: case 60:$lyric="ど"; break; case 59:$lyric="し"; break; } return $lyric; } /** * note要素(「音符一個=MIDI note on一つ」に相当するっぽい)を返す。 * @param int $posTick posTick要素の値。多分演奏開始=0で1/4分音符=480tickが * @param int $noteNum MIDIのnote numberに相当する。…みたいだけどオクターブ違う? * @param String $lyric 歌詞。VOCALOID Editorに入力できるひらがなカタカナに相当。 * @param String $phnms 発音記号。VOCALOID Editorが認識する独特な表記法。 * @return string note要素のテキスト。 */ function getNoteElement($posTick, $noteNum, $lyric='ラ', $phnms='4 a'){ $element=<< $posTick 480 $noteNum 64 50 8 0 50 0 127 0 66 1 22391 64 22391 50 EOD; return $element; } } /** * vsTrack要素の生成。割と決め打ち。 * @param String $codeSet URL中(HTTP GET)のYSCパラメータの中味全部。 * @return string vsqxファイルのvsTrack要素。 */ function YuYuGetVsTrack($codeSet){ function getVsTrackHead($track){ //各タグの内容がわかってないので結構危険。 return << $track 7680 38400 50 8 0 50 0 127 0 0 0 0 EOD; } function getVsTrackFoot(){ return << EOD; } $ret=''; { $ret.=getVsTrackHead(0); for($i=15,$tickPos=0; $i<95; $i++, $tickPos+=480){ $code=substr($codeSet, $i, 1); if($code!='0'){ $ret.=Note::getNoteElement($tickPos, $noteNum=Note::getNoteNum($code), Note::getLyric($noteNum), Note::getPhnms($noteNum) ); } } $ret.=getVsTrackFoot(); } for($track=1; $track<4; $track++){ $ret.=getVsTrackHead($track); for($i=104,$tickPos=0; $i<184; $i++, $tickPos+=480){ $code=substr($codeSet, $i, 1); if($code!='0'){ $ret.=Note::getNoteElement($tickPos, Note::getNoteNum($code, $track)); } } $ret.=getVsTrackFoot(); } return $ret; } /** * 細切れにしたvsqxファイルの末尾部分を返すだけの関数。 * リバーブとかかけるならここに細工する(が、根本的にプログラム構造考え直した方がよさげ)。 */ function YuYuGetFoot(){ $ret=''; $ret.=<< EOD; return $ret; } /** * vsqxファイル(Vocaloid3(Vocaloid 3 Editor3.0.4.0相当)の中味丸ごと返す。 */ function YuYuGetVSQX($codeSet){ $errorMessage=''; if($codeSet==''){ $errorMessage.="YSC param must be set."."
"; } if(strlen($codeSet)!=191){ $errorMessage.="YSC param's length must be 192."."
"; } if(strpos($codeSet, 'YuyuSeqPlusCode')!=0){ $errorMessage.="YuyuSeqPlusCode must be set at offset 0"."
"; } if(strpos($codeSet, 'AngelCode')!=95){ $errorMessage.="AngelCode must be set at offset 95"."
"; } if(strpos($codeSet, 'Tempo')!=184){ $errorMessage.="Tempo must be set at offset 184"."
"; } if($errorMessage!=''){ throw new Exception($errorMessage); }else{ $ret=''; // TODO:実はテンポ読んでない。2バイトテキストを数字に直してYuyuSeq32=120BPMぐらいにすればいいんだと思うけど…。 $ret.=YuYuGetHead(120); $ret.=YuYuGetVsTrack($codeSet); $ret.=YuYuGetFoot(); return $ret; } } try { // 'YuyuSeqPlusCode' // .'BA9ABBB0AAA0BDD0BA9ABBB0AABA9000000000000000000000000000000000000000000000000000' // .'AngelCode' // .'70706060505060607070404030297000000000000000000000000000000000000000000000000000' // .'Tempo32'; $ret=YuYuGetVSQX(trim($_GET['YSC'])); header('content-type:text/plain'); echo $ret; } catch (Exception $e) { echo $e->getMessage(); } ?>