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();
}
?>