http://www.geocities.jp/cn_hibari/

うたうVOCALOID、はなすVOCALOID。

このページは、初音ミクを中心としたVOCALOID関連の情報蓄積場所です。普通に歌わせようとする本来の使い方には全く役に立たない方向を中心としています。

このページに関するお問い合わせは、掲示板までどうぞ。

最終更新 2008/09/27

テスト3:喋りの上手いファイルを参考にしてみる(2007/12/02)

上記の動画はわたしの作ったものではありませんが、かなり上手く喋っています。既存の音声からパラメータを取って作ったものではなく打ち込んだものでしょうが、VSQファイルが公開されていたので、良く聞こえるにはどう作ればよいか参考にしようと調べてみました。

音程を固定せずにピッチベンド変化範囲を抑えること、子音から母音へ変化するタイミングを制御することがポイントのようです。また、OPEを変えることでどのホルマントを強くするかを制御しています。

テスト2:VSQファイルを解析してみる(2007/12/11)

VSQファイルは標準MIDIファイル(SMF)の亜種として作られています。その大きな特徴を以下に示します。

テキストメッセージ部分を抽出するソフトはいくつかありますが、今回はNRPN部分も含めて解析してみました。

/*
    VOCALOID vsq
    to
    Text

    by cn_hibari 2007
    http://www.geocities.jp/cn_hibari/
*/

#include <stdio.h>
#include <alloc.h>

unsigned char mthdhead[8] = { 'M', 'T', 'h', 'd', 0, 0, 0, 6 };
unsigned char mtrkhead[4] = { 'M', 'T', 'r', 'k' };

unsigned char buf[256];
long fsz;

typedef union
{
    unsigned char c[2];
    short s;
} bigend2;

typedef union
{
    unsigned char c[4];
    int i;
} bigend4;

bigend2 thdformat;
bigend2 thdtracks;
bigend2 thddivision;
bigend4 rksize;

typedef struct
{
    int index;
    unsigned char str_m[80];
    unsigned char str_l[80];
} strentry;

strentry nrpnstr[] =
{
    /* VOCALOID2 Note Message */
    { 0x5000, "NOTE Version number", "NOTE device number" },
    { 0x5001, "NOTE Delay in millisec MSB", "NOTE Delay in millisec LSB" },
    { 0x5002, "Note number", "" },
    { 0x5003, "Velocity", "" },
    { 0x5004, "Note Duration in millisec MSB", "Note Duration in millisec LSB" },
    { 0x5005, "Note Location(00=Inside 01=Top 02=End 03=Top&End)", "" },
    { 0x500c, "Index of Vibrato DB MSB", "Index of Vibrato DB LSB" },
    { 0x500d, "Index of Vibrato Type", "Duration & Continuation parameter of vibrato" },
    { 0x500e, "Vibrato Delay", "" },
    { 0x5012, "Number of phonetic symbols in bytes", "" },
    { 0x5013, "Phonetic symbol 1", "Consonant adjustment 1" },
    { 0x5014, "Phonetic symbol 2", "Consonant adjustment 2" },
    { 0x5015, "Phonetic symbol 3", "Consonant adjustment 3" },
    { 0x5016, "Phonetic symbol 4", "Consonant adjustment 4" },
    { 0x5017, "Phonetic symbol 5", "Consonant adjustment 5" },
    { 0x5018, "Phonetic symbol 6", "Consonant adjustment 6" },
    { 0x5019, "Phonetic symbol 7", "Consonant adjustment 7" },
    { 0x501a, "Phonetic symbol 8", "Consonant adjustment 8" },
    { 0x501b, "Phonetic symbol 9", "Consonant adjustment 9" },
    { 0x501c, "Phonetic symbol 10", "Consonant adjustment 10" },
    { 0x501d, "Phonetic symbol 11", "Consonant adjustment 11" },
    { 0x501e, "Phonetic symbol 12", "Consonant adjustment 12" },
    { 0x501f, "Phonetic symbol 13", "Consonant adjustment 13" },
    { 0x5020, "Phonetic symbol 14", "Consonant adjustment 14" },
    { 0x5021, "Phonetic symbol 15", "Consonant adjustment 15" },
    { 0x5022, "Phonetic symbol 16", "Consonant adjustment 16" },
    { 0x5023, "Phonetic symbol 17", "Consonant adjustment 17" },
    { 0x5024, "Phonetic symbol 18", "Consonant adjustment 18" },
    { 0x5025, "Phonetic symbol 19", "Consonant adjustment 19" },
    { 0x5026, "Phonetic symbol 20", "Consonant adjustment 20" },
    { 0x5027, "Phonetic symbol 21", "Consonant adjustment 21" },
    { 0x5028, "Phonetic symbol 22", "Consonant adjustment 22" },
    { 0x5029, "Phonetic symbol 23", "Consonant adjustment 23" },
    { 0x502a, "Phonetic symbol 24", "Consonant adjustment 24" },
    { 0x502b, "Phonetic symbol 25", "Consonant adjustment 25" },
    { 0x502c, "Phonetic symbol 26", "Consonant adjustment 26" },
    { 0x502d, "Phonetic symbol 27", "Consonant adjustment 27" },
    { 0x502e, "Phonetic symbol 28", "Consonant adjustment 28" },
    { 0x502f, "Phonetic symbol 29", "Consonant adjustment 29" },
    { 0x5030, "Phonetic symbol 30", "Consonant adjustment 30" },
    { 0x5031, "Phonetic symbol 31", "Consonant adjustment 31" },
    { 0x5032, "Phonetic symbol 32", "Consonant adjustment 32" },
    { 0x504f, "00=continue 7F=EndofPhoneticSymbols", "" },
    { 0x5050, "v1mean in Cent/5", "" },
    { 0x5051, "d1mean in millisec/5", "" },
    { 0x5052, "d1meanFirstNote in millisec/5", "", },
    { 0x5053, "d2mean in millisec/5", "" },
    { 0x5054, "d4mean in millisec/5", "" },
    { 0x5055, "pMeanOnsetFirstNote in Cent/5", "" },
    { 0x5056, "vMeanNoteTransition in Cent/5", "" },
    { 0x5057, "pMeanEndingNote in Cent/5", "" },
    { 0x5058, "AddScoopToUpIntervals&AddPortamentoToDownIntervals", "" },
    { 0x5059, "changeAfterPeak", "" },
    { 0x505a, "Accent", "" },
    { 0x507f, "00=continue 7F=EndofNoteMessage", "" },
    /* VOCALOID2 Bank Select (MSB/LSB) */
    { 0x6000, "BNK Version number", "BNK device number" },
    { 0x6001, "BNK Delay in millisec MSB", "BNK Delay in millisec LSB" },
    { 0x6002, "Language type MSB", "Language type LSB" },
    /* VOCALOID2 Channel Volume */
    { 0x6100, "VOL Version number", "VOL device number" },
    { 0x6101, "VOL Delay in millisec MSB", "VOL Delay in millisec LSB" },
    { 0x6102, "Volume value", "" },
    /* VOCALOID2 Panpot */
    { 0x6200, "PAN Version number", "PAN device number" },
    { 0x6201, "PAN Delay in millisec MSB", "PAN Delay in millisec LSB" },
    { 0x6302, "Pan value", "" },
    /* VOCALOID2 Expression */
    { 0x6300, "EXP Version number", "EXP device number" },
    { 0x6301, "EXP Delay in millisec MSB", "EXP Delay in millisec LSB" },
    { 0x6302, "Expression value", "" },
    /* VOCALOID2 Vibrato Rate */
    { 0x6400, "VBR Version number", "VBR device number" },
    { 0x6401, "VBR Delay in millisec MSB", "VBR Delay in millisec LSB" },
    { 0x6402, "Vibrato Rate value", "" },
    /* VOCALOID2 Vibrato Depth */
    { 0x6500, "VBD Version number", "VBD device number" },
    { 0x6501, "VBD Delay in millisec MSB", "VBD Delay in millisec LSB" },
    { 0x6502, "Vibrato Depth value", "" },
    /* VOCALOID2 Pitch Bend Sensitivity */
    { 0x6700, "PBS Version number", "PBS device number" },
    { 0x6701, "PBS Delay in millisec MSB", "PBS Delay in millisec LSB" },
    { 0x6702, "Pitch Bend Sensitivity MSB", "Pitch Bend Sensitivity LSB" },
    /* Begin of VOCALOID2 MIDI(BODM) */
    { 0x5100, "BODM Version number", "BODM device number" },
    /* End of VOCALOID2 MIDI(EODM) */
    { 0x5200, "EODM Version number", "EODM device number" },
    /* VOCALOID2 Program Change */
    { 0x5300, "PGM Version number", "PGM device number" },
    { 0x5301, "PGM Delay in millisec MSB", "PGM Delay in millisec LSB" },
    { 0x5302, "Voice type", "" },
    /* VOCALOID2 Pitch Bend */
    { 0x5400, "PIT Version number", "PIT device number" },
    { 0x5401, "PIT Delay in millisec MSB", "PIT Delay in millisec LSB" },
    { 0x5402, "Pitch Bend value MSB", "Pitch Bend value LSB" },
    /* VOCALOID2 Voice Change Parameters */
    { 0x5500, "VCE Version number", "VCE device number" },
    { 0x5501, "VCE Delay in millisec MSB", "VCE Delay in millisec LSB" },
    { 0x5502, "Voice Change Parameter ID", "" },
    { 0x5503, "Voice Change Parameter value", "", },
};
int nrpnstrs = sizeof(nrpnstr)/sizeof(strentry);

int nrpn_msb = 0, nrpn_lsb = 0, data_msb = 0, data_lsb = 0, nrpn = 0;


int main(int argc, char *argv[])
{

    FILE *fpin;
    int i, k, t;
    int f;
    int d;
    int e;
    unsigned char c;

    if ( argc <= 1 )
    {
        return 1;
    }
    fpin = fopen(argv[1], "rb");
    if ( fpin == NULL )
    {
        return 1;
    }
    fseek(fpin, 0, SEEK_END);
    fsz = ftell(fpin);
    fseek(fpin, 0, SEEK_SET);

    /* MThdヘッダ */
    fread(buf, 1, 8, fpin);
    f = 0;
    for ( i = 0 ; i < 8 ; ++i )
    {
        if ( buf[i] != mthdhead[i] )
        {
            f = 1;
        }
    }
    if ( f != 0 )
        {
        fclose(fpin);
        return 1;
        }

    printf("*****MThd*****\n");
    fread(&(thdformat.c[1]), 1, 1, fpin);
    fread(&(thdformat.c[0]), 1, 1, fpin);
    printf("format %d\n", thdformat.s);
    fread(&(thdtracks.c[1]), 1, 1, fpin);
    fread(&(thdtracks.c[0]), 1, 1, fpin);
    printf("tracks %d\n", thdtracks.s);
    fread(&(thddivision.c[1]), 1, 1, fpin);
    fread(&(thddivision.c[0]), 1, 1, fpin);
    printf("division %d\n", thddivision.s);

    /* トラック数だけループ */
    for ( t = 0 ; t < thdtracks.s ; ++t )
    {
        /* MTrkヘッダ */
        fread(buf, 1, 4, fpin);
        f = 0;
        for ( i = 0 ; i < 4 ; ++i )
        {
            if ( buf[i] != mtrkhead[i] )
            {
                f = 1;
            }
        }
        if ( f != 0 )
            {
            fclose(fpin);
            return 1;
            }
        fread(&(rksize.c[3]), 1, 1, fpin);
        fread(&(rksize.c[2]), 1, 1, fpin);
        fread(&(rksize.c[1]), 1, 1, fpin);
        fread(&(rksize.c[0]), 1, 1, fpin);
        printf("*****MTrk %d/%d(size %d)*****\n", t+1, thdtracks.s, rksize.i);

        e = 0;
        while ( e == 0 )
        {
            /* デルタタイム */
            d = 0;
            for ( i = 0 ; i < 4 ; ++i )
            {
                fread(&c, 1, 1, fpin);
                d = d * 128 + (int)(c & 0x7f);
                if ( c < 0x80 )
                {
                    break;
                }
            }
            fread(&c, 1, 1, fpin);
            if ( c < 0x80 )
            {
                /* running status */
                fread(buf, 1, 1, fpin);
                printf("%8d:%02X %02X\n", d, c, buf[0]);
            }
            else if (( c & 0xf0 ) == 0x80 )
            {
                /* note off */
                fread(buf, 1, 2, fpin);
                printf("%8d:%02X %02X %02X\n", d, c, buf[0], buf[1]);
            }
            else if (( c & 0xf0 ) == 0x90 )
            {
                /* note on */
                fread(buf, 1, 2, fpin);
                printf("%8d:%02X %02X %02X\n", d, c, buf[0], buf[1]);
            }
            else if (( c & 0xf0 ) == 0xa0 )
            {
                /* key pressure */
                fread(buf, 1, 2, fpin);
                printf("%8d:%02X %02X %02X\n", d, c, buf[0], buf[1]);
            }
            else if (( c & 0xf0 ) == 0xb0 )
            {
                /* control change */
                fread(buf, 1, 2, fpin);

                /* コントロール番号で振り分け */
                switch(buf[0])
                {
                case 99:
                    /* NRPN MSB */
                    nrpn_msb = buf[1];
                    nrpn = (nrpn_msb << 8) | nrpn_lsb;
                    break;
                case 98:
                    /* NRPN LSB */
                    nrpn_lsb = buf[1];
                    nrpn = (nrpn_msb << 8) | nrpn_lsb;
                    break;
                case 6:
                    /* Data Entry MSB */
                    data_msb = buf[1];
                    printf("%8d:NRPN %04X %02X..", d, nrpn, data_msb);
                    for ( i = 0 ; i < nrpnstrs ; ++i )
                    {
                        if ( nrpnstr[i].index == nrpn )
                        {
                            printf(":%s", nrpnstr[i].str_m);
                            break;
                        }
                    }
                    printf("\n");
                    break;
                case 38:
                    /* Data Entry LSB */
                    data_lsb = buf[1];
                    printf("%8d:NRPN %04X ..%02X", d, nrpn, data_lsb);
                    for ( i = 0 ; i < nrpnstrs ; ++i )
                    {
                        if ( nrpnstr[i].index == nrpn )
                        {
                            printf(":%s", nrpnstr[i].str_l);
                            break;
                        }
                    }
                    printf("\n");
                    break;
                default:
                    /* その他コントロールチェンジ */
                    printf("%8d:%02X %02X %02X\n", d, c, buf[0], buf[1]);
                    break;
                }
            }
            else if (( c & 0xf0 ) == 0xc0 )
            {
                /* program change */
                fread(buf, 1, 1, fpin);
                printf("%8d:%02X %02X\n", d, c, buf[0]);
            }
            else if (( c & 0xf0 ) == 0xd0 )
            {
                /* channel pressure */
                fread(buf, 1, 1, fpin);
                printf("%8d:%02X %02X\n", d, c, buf[0]);
            }
            else if (( c & 0xf0 ) == 0xe0 )
            {
                /* pitch bend */
                fread(buf, 1, 2, fpin);
                printf("%8d:%02X %02X %02X\n", d, c, buf[0], buf[1]);
            }
            else if ( c == 0xf0 )
            {
                /* system exclusive */
                fread(buf, 1, 2, fpin);
                printf("%8d:%02X %02X ...\n", d, c, buf[0]);
                fread(buf+1, 1, buf[1], fpin);
            }
            else if ( c == 0xf7 )
            {
                /* system exclusive */
                fread(buf, 1, 2, fpin);
                printf("%8d:%02X %02X ...\n", d, c, buf[0]);
                fread(buf+1, 1, buf[1], fpin);
            }
            else if ( c == 0xff )
            {
                /* system message */
                fread(buf, 1, 2, fpin);
                fread(buf+2, 1, buf[1], fpin);
                if (( buf[0] >= 0x01 ) && ( buf[0] <= 0x09 ))
                {
                    /* テキスト */
                    if (( buf[2] == 'D' ) && (buf[3] == 'M') && (buf[4] == ':'))
                    {
                        /* VOCALOIDテキスト */
                        for ( i = 8 ; i < buf[1] ; ++i )
                        {
                            printf("%c", buf[i+2] );
                        }
                    }
                    else
                    {
                        /* SMFテキスト */
                        printf("%8d:Text:", d, c, buf[0]);
                        for ( i = 0 ; i < buf[1] ; ++i )
                        {
                            printf("%c", buf[i+2] );
                        }
                        printf("\n");
                    }
                }
                else if ( buf[0] == 0x2f )
                {
                    /* end of track */
                    printf("%8d:Track End\n", d );
                    e = 1;
                }
                else if ( buf[0] == 0x51 )
                {
                    /* tempo */
                    i = (buf[2]<<16) | (buf[3]<<8) | buf[4];
                    printf("%8d:Tempo %02X%02X%02X [crotchet=%dus, %dBPM]\n", d, buf[2], buf[3], buf[4], i, 60*1000000/i );
                }
                else if ( buf[0] == 0x58 )
                {
                    /* time signature */
                    i = 1;
                    for ( k = 0 ; k < buf[3] ; ++k )
                    {
                        i *= 2;
                    }
                    printf("%8d:Time Signature %02X %02X %02X %02X [%d/%d]\n", d, buf[2], buf[3], buf[4], buf[5], buf[2], i );
                }
                else
                {
                    /* その他 */
                    printf("%8d:%02X %02X ...\n", d, c, buf[0]);
                }
            }
        }
    }

    fclose(fpin);
    return 0;
}

VSQファイルを指定してこのプログラムを動かすと、以下のような出力が得られます。VSQにエフェクトをかけて再びVSQに戻すようなプログラムを作る際の参考になるかと思います。

*****MThd*****
format 1
tracks 2
division 480
*****MTrk 1/2(size 35)*****
       0:Text:Master Track
       0:Tempo 07A120 [crotchet=500000us, 120BPM]
       0:Time Signature 04 02 18 08 [4/4]
       0:Track End
*****MTrk 2/2(size 2787)*****
       0:Text:Voice1
[Common]
Version=DSB301
Name=Voice1
Color=181,162,123
DynamicsMode=1
PlayMode=1
[Master]
PreMeasure=4
[Mixer]
MasterFeder=0
MasterPanpot=0
MasterMute=0
OutputMode=0
Tracks=1
Feder0=0
Panpot0=0
Mute0=0
Solo0=0
[EventList]
0=ID#0000
7740=ID#0001
8700=ID#0002
9660=ID#0003
10620=ID#0004
11580=ID#0005
12480=EOS
[ID#0000]
Type=Singer
IconHandle=h#0000
[ID#0001]
Type=Anote
Length=900
Note#=69
Dynamics=64
PMBendDepth=0
PMBendLength=0
PMbPortamentoUse=0
DEMdecGainRate=0
DEMaccent=0
LyricHandle=h#0001
[ID#0002]
Type=Anote
Length=900
Note#=57
Dynamics=64
PMBendDepth=0
PMBendLength=0
PMbPortamentoUse=0
DEMdecGainRate=0
DEMaccent=0
LyricHandle=h#0002
[ID#0003]
Type=Anote
Length=900
Note#=69
Dynamics=64
PMBendDepth=0
PMBendLength=0
PMbPortamentoUse=0
DEMdecGainRate=0
DEMaccent=0
LyricHandle=h#0003
[ID#0004]
Type=Anote
Length=900
Note#=81
Dynamics=64
PMBendDepth=0
PMBendLength=0
PMbPortamentoUse=0
DEMdecGainRate=0
DEMaccent=0
LyricHandle=h#0004
[ID#0005]
Type=Anote
Length=900
Note#=69
Dynamics=64
PMBendDepth=0
PMBendLength=0
PMbPortamentoUse=0
DEMdecGainRate=0
DEMaccent=0
LyricHandle=h#0005
[h#0000]
IconID=$07010000
IDS=Miku
Original=0
Caption=
Length=1
Language=0
Program=0
[h#0001]
L0="ぴ","p' i",0.000000,64,0,0
[h#0002]
L0="ち","tS i",0.000000,64,0,0
[h#0003]
L0="べ","b e",0.000000,64,0,0
[h#0004]
L0="ん","n",0.000000,64,0
[h#0005]
L0="ど","d o",0.000000,64,0,0
[PitchBendBPList]
7680=0
8650=8191
9605=0
10570=-8192
11545=0
[PitchBendSensBPList]
7680=12
       0:NRPN 6000 00..:BNK Version number
       0:NRPN 6000 ..00:BNK device number
       0:NRPN 6001 00..:BNK Delay in millisec MSB
       0:NRPN 6001 ..00:BNK Delay in millisec LSB
       0:NRPN 6002 00..:Language type MSB
       0:NRPN 5302 00..:Voice type
       0:NRPN 5400 00..:PIT Version number
       0:NRPN 5400 ..00:PIT device number
       0:NRPN 5401 0F..:PIT Delay in millisec MSB
       0:NRPN 5401 ..50:PIT Delay in millisec LSB
       0:NRPN 5402 40..:Pitch Bend value MSB
       0:NRPN 5402 ..00:Pitch Bend value LSB
       0:NRPN 6702 0C..:Pitch Bend Sensitivity MSB
       0:NRPN 6702 ..00:Pitch Bend Sensitivity LSB
       0:NRPN 5002 45..:Note number
       0:NRPN 5003 40..:Velocity
       0:NRPN 5004 07..:Note Duration in millisec MSB
       0:NRPN 5004 ..2A:Note Duration in millisec LSB
       0:NRPN 5005 03..:Note Location(00=Inside 01=Top 02=End 03=Top&End)
       0:NRPN 5012 03..:Number of phonetic symbols in bytes
       0:NRPN 5013 70..:Phonetic symbol 1
       0:NRPN 5013 ..40:Consonant adjustment 1
       0:NRPN 5014 27..:Phonetic symbol 2
       0:NRPN 5015 69..:Phonetic symbol 3
       0:NRPN 5015 ..00:Consonant adjustment 3
       0:NRPN 504F 7F..:00=continue 7F=EndofPhoneticSymbols
       0:NRPN 5050 00..:v1mean in Cent/5
       0:NRPN 5051 08..:d1mean in millisec/5
       0:NRPN 5052 14..:d1meanFirstNote in millisec/5
       0:NRPN 5053 1C..:d2mean in millisec/5
       0:NRPN 5054 18..:d4mean in millisec/5
       0:NRPN 5055 0A..:pMeanOnsetFirstNote in Cent/5
       0:NRPN 5056 0C..:vMeanNoteTransition in Cent/5
       0:NRPN 5057 0C..:pMeanEndingNote in Cent/5
       0:NRPN 5058 00..:AddScoopToUpIntervals&AddPortamentoToDownIntervals
       0:NRPN 5059 00..:changeAfterPeak
       0:NRPN 505A 00..:Accent
       0:NRPN 507F 7F..:00=continue 7F=EndofNoteMessage
       0:NRPN 5400 00..:PIT Version number
       0:NRPN 5400 ..00:PIT device number
       0:NRPN 5401 0F..:PIT Delay in millisec MSB
       0:NRPN 5401 ..50:PIT Delay in millisec LSB
       0:NRPN 5402 7F..:Pitch Bend value MSB
       0:NRPN 5402 ..7F:Pitch Bend value LSB
       0:NRPN 5002 39..:Note number
       0:NRPN 5003 40..:Velocity
       0:NRPN 5004 07..:Note Duration in millisec MSB
       0:NRPN 5004 ..2A:Note Duration in millisec LSB
       0:NRPN 5005 03..:Note Location(00=Inside 01=Top 02=End 03=Top&End)
       0:NRPN 5012 03..:Number of phonetic symbols in bytes
       0:NRPN 5013 74..:Phonetic symbol 1
       0:NRPN 5013 ..40:Consonant adjustment 1
       0:NRPN 5014 53..:Phonetic symbol 2
       0:NRPN 5015 69..:Phonetic symbol 3
       0:NRPN 5015 ..00:Consonant adjustment 3
       0:NRPN 504F 7F..:00=continue 7F=EndofPhoneticSymbols
       0:NRPN 5050 00..:v1mean in Cent/5
       0:NRPN 5051 08..:d1mean in millisec/5
       0:NRPN 5052 14..:d1meanFirstNote in millisec/5
       0:NRPN 5053 1C..:d2mean in millisec/5
       0:NRPN 5054 18..:d4mean in millisec/5
       0:NRPN 5055 0A..:pMeanOnsetFirstNote in Cent/5
       0:NRPN 5056 0C..:vMeanNoteTransition in Cent/5
       0:NRPN 5057 0C..:pMeanEndingNote in Cent/5
       0:NRPN 5058 00..:AddScoopToUpIntervals&AddPortamentoToDownIntervals
       0:NRPN 5059 00..:changeAfterPeak
       0:NRPN 505A 00..:Accent
       0:NRPN 507F 7F..:00=continue 7F=EndofNoteMessage
       0:NRPN 5400 00..:PIT Version number
       0:NRPN 5400 ..00:PIT device number
       0:NRPN 5401 0F..:PIT Delay in millisec MSB
       0:NRPN 5401 ..50:PIT Delay in millisec LSB
       0:NRPN 5402 40..:Pitch Bend value MSB
       0:NRPN 5402 ..00:Pitch Bend value LSB
       0:NRPN 5002 45..:Note number
       0:NRPN 5003 40..:Velocity
       0:NRPN 5004 07..:Note Duration in millisec MSB
       0:NRPN 5004 ..2A:Note Duration in millisec LSB
       0:NRPN 5005 03..:Note Location(00=Inside 01=Top 02=End 03=Top&End)
       0:NRPN 5012 02..:Number of phonetic symbols in bytes
       0:NRPN 5013 62..:Phonetic symbol 1
       0:NRPN 5013 ..40:Consonant adjustment 1
       0:NRPN 5014 65..:Phonetic symbol 2
       0:NRPN 5014 ..00:Consonant adjustment 2
       0:NRPN 504F 7F..:00=continue 7F=EndofPhoneticSymbols
       0:NRPN 5050 00..:v1mean in Cent/5
       0:NRPN 5051 08..:d1mean in millisec/5
       0:NRPN 5052 14..:d1meanFirstNote in millisec/5
       0:NRPN 5053 1C..:d2mean in millisec/5
       0:NRPN 5054 18..:d4mean in millisec/5
       0:NRPN 5055 0A..:pMeanOnsetFirstNote in Cent/5
       0:NRPN 5056 0C..:vMeanNoteTransition in Cent/5
       0:NRPN 5057 0C..:pMeanEndingNote in Cent/5
       0:NRPN 5058 00..:AddScoopToUpIntervals&AddPortamentoToDownIntervals
       0:NRPN 5059 00..:changeAfterPeak
       0:NRPN 505A 00..:Accent
       0:NRPN 507F 7F..:00=continue 7F=EndofNoteMessage
       0:NRPN 5400 00..:PIT Version number
       0:NRPN 5400 ..00:PIT device number
       0:NRPN 5401 0F..:PIT Delay in millisec MSB
       0:NRPN 5401 ..50:PIT Delay in millisec LSB
       0:NRPN 5402 00..:Pitch Bend value MSB
       0:NRPN 5402 ..00:Pitch Bend value LSB
       0:NRPN 5002 51..:Note number
       0:NRPN 5003 40..:Velocity
       0:NRPN 5004 07..:Note Duration in millisec MSB
       0:NRPN 5004 ..2A:Note Duration in millisec LSB
       0:NRPN 5005 03..:Note Location(00=Inside 01=Top 02=End 03=Top&End)
       0:NRPN 5012 01..:Number of phonetic symbols in bytes
       0:NRPN 5013 6E..:Phonetic symbol 1
       0:NRPN 5013 ..40:Consonant adjustment 1
       0:NRPN 504F 7F..:00=continue 7F=EndofPhoneticSymbols
       0:NRPN 5050 00..:v1mean in Cent/5
       0:NRPN 5051 08..:d1mean in millisec/5
       0:NRPN 5052 14..:d1meanFirstNote in millisec/5
       0:NRPN 5053 1C..:d2mean in millisec/5
       0:NRPN 5054 18..:d4mean in millisec/5
       0:NRPN 5055 0A..:pMeanOnsetFirstNote in Cent/5
       0:NRPN 5056 0C..:vMeanNoteTransition in Cent/5
       0:NRPN 5057 0C..:pMeanEndingNote in Cent/5
       0:NRPN 5058 00..:AddScoopToUpIntervals&AddPortamentoToDownIntervals
       0:NRPN 5059 00..:changeAfterPeak
       0:NRPN 505A 00..:Accent
       0:NRPN 507F 7F..:00=continue 7F=EndofNoteMessage
       0:NRPN 5400 00..:PIT Version number
       0:NRPN 5400 ..00:PIT device number
       0:NRPN 5401 0F..:PIT Delay in millisec MSB
       0:NRPN 5401 ..50:PIT Delay in millisec LSB
       0:NRPN 5402 40..:Pitch Bend value MSB
       0:NRPN 5402 ..00:Pitch Bend value LSB
       0:NRPN 5002 45..:Note number
       0:NRPN 5003 40..:Velocity
       0:NRPN 5004 07..:Note Duration in millisec MSB
       0:NRPN 5004 ..2A:Note Duration in millisec LSB
       0:NRPN 5005 03..:Note Location(00=Inside 01=Top 02=End 03=Top&End)
       0:NRPN 5012 02..:Number of phonetic symbols in bytes
       0:NRPN 5013 64..:Phonetic symbol 1
       0:NRPN 5013 ..40:Consonant adjustment 1
       0:NRPN 5014 6F..:Phonetic symbol 2
       0:NRPN 5014 ..00:Consonant adjustment 2
       0:NRPN 504F 7F..:00=continue 7F=EndofPhoneticSymbols
       0:NRPN 5050 00..:v1mean in Cent/5
       0:NRPN 5051 08..:d1mean in millisec/5
       0:NRPN 5052 14..:d1meanFirstNote in millisec/5
       0:NRPN 5053 1C..:d2mean in millisec/5
       0:NRPN 5054 18..:d4mean in millisec/5
       0:NRPN 5055 0A..:pMeanOnsetFirstNote in Cent/5
       0:NRPN 5056 0C..:vMeanNoteTransition in Cent/5
       0:NRPN 5057 0C..:pMeanEndingNote in Cent/5
       0:NRPN 5058 00..:AddScoopToUpIntervals&AddPortamentoToDownIntervals
       0:NRPN 5059 00..:changeAfterPeak
       0:NRPN 505A 00..:Accent
       0:NRPN 507F 7F..:00=continue 7F=EndofNoteMessage
    6720:Track End

テスト1:音声の基本周波数からPITを作って喋らせてみる(2007/09/30)

今回はフリーソフトのWaveSurferを使いました。起動し、「File」「Open」で解析対象のWaveファイルを開きます。今回は解析したい音声以外にノイズがほとんど載っていないファイルを使っていますが、ノイズがあると精度が大きく低下します。

Screenshot: WaveSurfer

「Speech analysis」を選択します。

Screenshot: WaveSurfer

上から3番目(Hz単位で表示されている部分、基本周波数がグラフ表示されている)のグラフ上で右クリックし「Propaties」を選択します。

Screenshot: WaveSurfer

「Pitch contour」タブを選び、「Max pitch value」を400Hzから800Hzに変更します。音程が高いはずなのに前後より基本周波数が低くなっている部分が直ります。逆はありませんでしたので「Min pitch value」の変更はしていません。

さきほどと同じところで右クリックし、「Save Data File」を選んで基本周波数の推移をファイルに書き出します。出力ファイルには拡張子.f0が付きます。

このファイルは以下のようなテキストファイルになっており、最も左の数値が基本周波数[Hz]です。今回解析したファイルでは110〜450[Hz]程度の値が得られました。0.01秒毎に1行出力されるため、解析対象のWaveファイルの再生時間が20秒ならば2000行出力されます。基本周波数の得られなかった区間では0.0[Hz]が書かれます。

288.696350098 1.0 7579.52197266 0.770115077496
303.171478271 1.0 8823.96777344 0.894143462181
320.838745117 1.0 8707.46777344 0.946116268635

これを使ってSMF(標準MIDIファイル)を強引に作ってみます。以下のCソースをBorland C++ Compiler 5.5でコンパイルし、「f02vsq test.f0 test.mid」と実行するとf0ファイルからSMFが得られます。基本周波数が220Hz基準のピッチベンドに変換されます。テンポ150としたのは、0.4秒=4分音符となってエディタ上のグリッドから時間の対応をつかみやすいためです。

/*
    WaveSurfer F-zero
    to
    VOCALOID Standard MIDI File

    by cn_hibari 2007
    http://www.geocities.jp/cn_hibari/
*/

#include <stdio.h>
#include <alloc.h>
#include <math.h>
#include <ctype.h>

/* 4分音符を何分割した時間を時間単位とするか */
#define    DELTATIME    (480)
/* ファイル入力用1行バッファの長さ[バイト] */
#define FBUFMAX    (256)
/* 出力するA(ラ)の音高 220Hz音をこの音高に対応させる */
#define ATONE    (57+12)

/* エンディアン変換用の共用体 */
typedef union
{
    unsigned long l;
    unsigned char c[4];
} INT32s;

/* log10(2)の逆数を格納する */
double log2 = 0.0;

/* SMF出力バッファ */
unsigned char *smfbuf = NULL;
unsigned long smfsize = 0;
unsigned long bufsize = 2*1024*1024;    /* 出力バッファのサイズ */

/* F0ファイルの基本周波数, VoiceProbability, RMS, 自己相関係数 */
double f0, prob, rms, corr;

/* ファイル入力用1行バッファ */
unsigned char fbuf[FBUFMAX];


int initbuf(void);
int addbuf(unsigned char c);
int addtime(unsigned long t);
void fileout(FILE *fp);

int freq2pitch(double freq);

int initbuf(void)
{
    smfbuf = malloc(bufsize);
    smfsize = 0;
    if ( NULL == smfbuf )
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

void termbuf(void)
{
    if ( NULL != smfbuf )
    {
        free(smfbuf);
    }
}


int addbuf(unsigned char c)
{
    if ( smfsize >= bufsize )
    {
        return 1;
    }
    else
    {
        smfbuf[smfsize] = c;
        ++smfsize;
        return 0;
    }
}

int addtime(unsigned long t)
{
    int ret = 0;

    if ( t >= 128*128*128 )
    {
        ret |= addbuf((unsigned char)((t >> 21) & 0x7f) | 0x80);
        t &= 128*128*128-1;
    }
    if ( t >= 128*128 )
    {
        ret |= addbuf((unsigned char)((t >> 14) & 0x7f) | 0x80);
        t &= 128*128-1;
    }
    if ( t >= 128 )
    {
        ret |= addbuf((unsigned char)((t >> 7) & 0x7f) | 0x80);
        t &= 128-1;
    }
    ret |= addbuf((unsigned char)t & 0x7f);

    return ret;
}

int f0load(FILE *fp)
{
    int ret = 0;

    f0 = 0.0;

    /* ヘッダを読み飛ばすため先頭が数字で始まる行まで飛ばす */
    /* 終了判定も同時に行う */
    do
    {
        if ( NULL == fgets(fbuf, FBUFMAX-1, fp))
        {
            ret = 1;
        }
    } while (( 0 == ret ) && ( 0 == isdigit(fbuf[0])));

    if ( 0 == ret )
    {
        sscanf(fbuf, "%lf%lf%lf%lf", &f0, &prob, &rms, &corr);
    }

    return ret;
}

void fileout(FILE *fp)
{
    INT32s mtrk_sz;

    /* End of Trackの分を追加 */
    mtrk_sz.l = smfsize + 4;

    /* MThdヘッダ */
    fprintf(fp, "MThd" );
    fprintf(fp, "%c%c%c%c", 0, 0, 0, 6 ); /* ヘッダサイズ6 */
    fprintf(fp, "%c%c", 0, 0);    /* フォーマット0 */
    fprintf(fp, "%c%c", 0, 1);    /* トラック数0 */
    fprintf(fp, "%c%c", DELTATIME/256, DELTATIME%256 );    /* デルタタイム */

    /* MTrkヘッダ */
    fprintf(fp, "MTrk%c%c%c%c", mtrk_sz.c[3], mtrk_sz.c[2], mtrk_sz.c[1], mtrk_sz.c[0] );

    fwrite(smfbuf, 1, smfsize, fp);

    /* End of Track */
    /* 最後のデータの直後(経過時間0)にFF 2F 00を入れる */
    fprintf(fp, "%c%c%c%c", 0, 0xff, 0x2f, 0);
}

int freq2pitch(double freq)
{
    double d;

    if ( log2 == 0.0 )
    {
        log2 = 1.0 / log10(2.0);
    }


    if ( freq < 1.0 )
    {
        return 0;
    }
    else if ( freq > 880.0 )
    {
        return 8191;
    }
    else if ( freq < 55.0 )
    {
        return -8192;
    }
    else
    {
        /* 220.0Hzを基準として2の対数をとる */
        /* 110Hz(0.5)が-1、220Hz(1)が0、440Hz(2)が1になる */
        /* それに4096をかけるとセンシティビティー24のピッチ指定になる */
        d = 4096.0 * log10(freq / 220.0) * log2;
        if ( d > 8191.0 )
        {
            d = 8191.0;
        }
        if ( d < -8192.0 )
        {
            d = -8192.0;
        }

        return ((int)d);
    }
}

int main(int argc, char *argv[])
{
    FILE *fpf;
    FILE *fpm;
    unsigned long nexttime;
    int pitch;
    unsigned long pitch_l, pitch_m;
    int lastflag = 0;

    /* 出力バッファ初期化 */
    if ( initbuf() )
    {
        return 1;
    }

    if ( argc <= 2 )
    {
        return 1;
    }

    /* 入力ファイルを開く */
    fpf = fopen(argv[1], "rt");
    if ( fpf == NULL )
    {
        return 1;
    }

    /* 出力ファイルを開く */
    fpm = fopen(argv[2], "wb");
    if ( fpm == NULL )
    {
        fclose(fpf);
        return 1;
    }

    /* 最初にピッチベンドセンシティビティを設定 */
    /* RPN MSB : Control 101 / Data 0 */
    addtime(0);
    addbuf(0xb0);    addbuf(101);    addbuf(0);
    /* RPN LSB : Control 100 / Data 0 */
    addtime(0);
    addbuf(0xb0);    addbuf(100);    addbuf(0);
    /* DataEntry MSB : Control 6  / Data 24 */
    addtime(0);
    addbuf(0xb0);    addbuf(6);    addbuf(24);
    /* DataEntry LSB : Control 26 は使用しない */

    /* 次にテンポ150を設定 */
    /* 150BPMは4分音符の時間が400000[マイクロ秒]=61A80hで指定 */
    /* Meta Event */
    addtime(0);
    addbuf(0xff);    addbuf(0x51);    addbuf(0x03);
    addbuf(0x06);    addbuf(0x1a);    addbuf(0x80);

    /* 最初は1小節分待つ */
    nexttime = DELTATIME * 4;

    while ( 0 == f0load(fpf) )
    {
        /* DELTATIMEは4分音符(0.4秒)だが1行のデータは0.01秒 */
        nexttime += DELTATIME / 40;
        if ( f0 > 1.0 )
        {
            addtime(nexttime);

            /* Pitch Bend */
            pitch = freq2pitch(f0);
            pitch_m = (unsigned long)(pitch + 8192);
            pitch_l = pitch_m % 0x7f; pitch_m & 0x7f;
            pitch_m = pitch_m >> 7;
            addbuf(0xe0);    addbuf(pitch_l);    addbuf(pitch_m);

            printf("%5d %5d %02X %02X\n", nexttime, pitch, pitch_l, pitch_m);

            if ( 0 == lastflag )
            {
                addtime(0);
                /* Note On */
                addbuf(0x90);    addbuf(ATONE);    addbuf(0x40);
                printf("%5d Note On\n", 0);
            }

            nexttime = 0;
            lastflag = 1;
        }
        else
        {
            if ( 0 != lastflag )
            {
                addtime(nexttime);
                /* Note Off */
                addbuf(0x80);    addbuf(ATONE);    addbuf(0x40);
                printf("%5d Note Off\n", nexttime);

                nexttime = 0;
            }
            lastflag = 0;
        }
    }

    /* 終了処理 */
    fileout(fpm);
    fclose(fpm);
    fclose(fpf);
    termbuf();

    printf("ok");
    return 0;
}

ピッチベンド値の変換処理に誤りがありました。

出力されたSMFをVOCALOID Editorで読み込むことで、ピッチベンドがPITに変換されます。

Screenshot: VOCALOID EDITOR

SMFファイルでテンポ150の指定を行っていますがうまく反映されていませんので、まずテンポ120を150に変更します。

その後、PITをを見ながら発声内容を打ち込みます(発声区間の認識は無声音の部分を中心にうまくいっていません)。音程変化は全てPITで表現したため、全てA3に入れています。また、これ以上音程変化が重ならないようベンドの深さは0%としました。

これをWave出力したのが以下の動画です。出力音声に対するエフェクトは、音量調整しか行っていません。


(YouTube動画をインライン表示しています)

この動画に対していただいたコメントは以下の通りです。

PITを使って音程を変化させると声の変化が激しく、この方式では使い物にならない状態です。ただし、大きく変化させるのがよくないのか発声中に変化させるのがよくないのかは分かりません。基準音をA3に固定するのではなく、何種類か用意して使い分けるなどもう少し試行錯誤が必要なようです。

また、PITの変化が音素単位とならず、常になめらかに変化しているのも直感と異なります。窓が適切な値よりも長く変化をつかみきれていないかもしれません。基本周波数を求める処理を今はWaveSurferに完全に任せていますが、このあたりも調整の余地がありそうです。

補足

サンプルソースは、Borland C++ Compier 5.5.1で以下のようにコンパイルしています。

トップページにもどる