三流プログラマの戯言

プログラミング初心者が気になったことを書き綴るだけ。主にc#

響ちゃんスイッチ

皆さん、艦これは好きですか。
皆さん、駆逐艦は好きですか。
皆さん、六駆は好きですか。
皆さん、銀髪幼女は好きですか。
皆さん、響ちゃんは好きですか。

私は好きです!(響ちゃんが)
響ちゃんが好きで、ボタンを押すと響ちゃんのボイスが流れる装置を作るぐらいには響きちゃんが好きです。
と言って、この装置のアイディアは自分で出したわけではなく、とある年のNT京都で雪風スイッチというものを見てつくろうと思った次第であります。

画像とかその他もろもろは私のTwitterとかに上がってたりするのでブログの方では省略します。 (単純にめんどいだけ)
この記事は殆ど自分の防備録なので許してね。

細かい機能はさておき、ボタンを押すと音声が流れるという機能が主なわけですが、
数種類のボイスの中から、ランダムに選択されたボイスを再生する為、
元々は、
・ボイスの数、ボイスが存在するディレクトリパスを記したtxtファイル
・連番が与えられたwavファイル郡
を用いて下のコードで呼び出してました。
ちなみにArduinoを使用しています。

void VoiceRandomPlayMenu(char* dirName, char* viewFileName, byte numMax)
{
    LcdUpdateByFile(viewFileName);
    char* fileNamePtr = _dataReadStr3;
    while (*dirName != NULL)*fileNamePtr++ = *dirName++;
    *fileNamePtr++ = '/';
    while (1)
    {
        byte state = GetSwitchState();
        if (state & SwitchState::Center)
        {
            randomSeed(millis());
            byte num = random(20000) % numMax;
            sprintf(fileNamePtr, "%02d.wav", num);
            PlayMusic(_dataReadStr3);
        }
        else if (state)
        {
            return;
        }
    }
}

Arduinoはほぼc++ですがc++に慣れてないので汚いコードに…
やってることは、ファイル数未満の数をランダムに生成し、[数字].wavのファイルを再生する というだけです。
でも、先述のとおり、これだとファイル数が多くなります、また、いろいろなキャラを入れようと思うと、キャラごとにディレクトリを造らない時計ないというめんどくささ…
だから、単一ファイルで音声データを管理するために以下のような読み込みおプログラムを作成

boolean VoiceRandomPlayMenuBySingleFile(char* fileName)
{
    {//ファイルタイプの確認
        char type[5] = "----";

        if (!(_dataFile = SD.open(fileName, FILE_READ)))
        {
            ErrorLog::WriteToLogFile("file Open Error <", false);
            ErrorLog::WriteToLogFile(fileName, false);
            ErrorLog::WriteToLogFile(">");

            LedBlink(Leds::LeftLed, 200);
            return false;
        }  // error opening file

        _dataFile.read((byte*)type, 4);

        if (!IsSame(type, "vlst"))
        {
            _dataFile.close();
            ErrorLog::WriteToLogFile("file type Is Not vlst <", false);
            ErrorLog::WriteToLogFile(fileName, false);
            ErrorLog::WriteToLogFile(">");
            LedBlink(Leds::LeftLed, 200);
            return false;
        }


        _dataFile.read((byte*)_dataReadTempBuf, 2);//Read offset
        _dataFile.seek(*((int*)_dataReadTempBuf));
    }
    
    //重要データの抽出
    _dataFile.read((byte*)_dataReadTempBuf, 4 + 4);//Read ImgPtr And SoundPtr
   
    auto imagePtr = *(uint32_t*)_dataReadTempBuf;
    auto soundNum = *(uint32_t*)(_dataReadTempBuf + 4);
    auto soundPosesPtr = _dataFile.position();
    

    {//サムネ画像の出力
        if (imagePtr == 0)
        {
            LcdClear();
            UpdateText("        No Image.", 4);
        }
        else
        {
            _dataFile.seek(imagePtr);
            _dataFile.read(_imgBuffer[1], 52 * 120);
            SendBufferDataBy52Length(_imgBuffer[1], 0, 120);
            _dataFile.read(_imgBuffer[1], 52 * 120);
            SendBufferDataBy52Length(_imgBuffer[1], 120, 120);
        }

    }

    //音声出力
    uint32_t turgetPos[2];
    while (1)
    {
        byte state = GetSwitchState();
        if (state & SwitchState::Center)
        {
            randomSeed(millis());
            byte num = random(20000) % soundNum;
            _dataFile.seek(soundPosesPtr + (4 + 4) * num);
            _dataFile.read(turgetPos, 4 * 2);
            PlayMusicByFilePlace(&_dataFile, turgetPos[0], (int32_t)turgetPos[1]);
        }
        else if (state)
        {
            return true;
        }
    }
}

ざっくり言うと、各ボイスの開始のデータ位置と各データの長さをどこかに記録しておいて、その都度その場所のデータを再生するという感じ。

また、ファイル形式は下のようにしてる。
フィールド名:byteサイズ (値)

Type : 4 ("vlst")
Offset :2 (6)
imagePtr: 4
Sound num:4
Sound ptrs and length: (4+4)*n
Image data :x
Sound datas :y

とりあえず画像データは抜きで動作することは確認済。
実は、このファイル生成プログラム(旧形式からの変換プログラム)は書いてないので、今回用いたテストファイルは自力でバイナリエディタで書いた。何してるんだ自分は。