wavをcに変換するツール?

とあるIoTデバイス(M5Stackなど)で、音(wavファイル)を鳴らす際、データをsdカードに入れたり、メモリに書き込んだりする方法があるが、固定の音の場合はデータをテキスト化して、プログラムに書き込んでしまうのが楽ちん。

やってることは、WAVファイルからPCM音声データを抽出し、各バイトを16進数でC言語の配列として出力するだけです。

### wav to c  by T.Matsuoka@Sketlab (c) 2025
###
import wave
import sys
import os

def wav_to_c_array(wav_path, output_c_path, array_name="audio_data"):
    with wave.open(wav_path, "rb") as wav:
        # パラメータ確認
        num_channels = wav.getnchannels()
        sample_width = wav.getsampwidth()
        sample_rate = wav.getframerate()
        num_frames = wav.getnframes()
        pcm_data = wav.readframes(num_frames)

        print(f"Channels: {num_channels}")
        print(f"Sample width: {sample_width * 8} bits")
        print(f"Sample rate: {sample_rate} Hz")
        print(f"Frames: {num_frames}")
        print(f"Output size: {len(pcm_data)} bytes")

    with open(output_c_path, "w") as f:
        f.write(f"// PCM from: {os.path.basename(wav_path)}\n")
        f.write(f"// Sample rate: {sample_rate} Hz\n")
        f.write(f"// Channels: {num_channels}, Bits per sample: {sample_width * 8}\n\n")
        f.write(f"const unsigned char {array_name}[] = {{\n    ")

        for i, byte in enumerate(pcm_data):
            f.write(f"0x{byte:02X}, ")
            if (i + 1) % 12 == 0:
                f.write("\n    ")

        f.write(f"\n}};\n\n")
        f.write(f"const unsigned int {array_name}_len = {len(pcm_data)};\n")

    print(f"\nC array written to {output_c_path}")

# コマンドライン使用例: python wav2c.py input.wav output.c
if __name__ == "__main__":
    if len(sys.argv) != 3:
        #print("使い方: wav2c 入力wavファイル 出力cファイル")
        print("使い方: python wav2c.py 入力wavファイル 出力cファイル")
        sys.exit(1)

    wav_path = sys.argv[1]
    output_c_path = sys.argv[2]

    wav_to_c_array(wav_path, output_c_path)

筆者はPyinstallerを使って、exe化しています。

実行すると、こんな感じのファイルが出来上がります。
まあ~単にPCMをバイト列にして、それを配列に入れているだけですね。
複数の音を使う場合は、audio_data[]の部分の変数名を変えてください。メインプログラムで利用する際にサイズが必要なので、こちらも。

// PCM from: se01.wav
// Sample rate: 44100 Hz
// Channels: 2, Bits per sample: 16

const unsigned char audio_data[] = {
    0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 
    0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
    0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0x04, 0x00, 
    0x01, 0x00, 0xEA, 0xFF, 0xF8, 0xFF, 0xAF, 0xFF, 0xD0, 0xFF, 0x7C, 0xFF, 
    0x9B, 0xFF, 0x70, 0xFF, 0x80, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x9F, 0xFF, 
       :
       :
     途中省略


    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 
};

const unsigned int audio_data_len = 210268;

で、これを、例えばm5Stackなどに入れる

#include <M5Stack.h>
#include <driver/i2s.h>

// 外部の音声データ(se.c などで定義)
extern const unsigned char audio_data[];
extern const unsigned int audio_data_len;

// I2S設定(M5Stack Basic 用)
#define I2S_BCK_IO   26
#define I2S_WS_IO    25
#define I2S_DO_IO    22
#define I2S_NUM_USED I2S_NUM_0
#define CHUNK_SIZE   512

bool InitI2SSpeaker() {
    i2s_driver_uninstall(I2S_NUM_USED);  // 念のため停止

    i2s_config_t i2s_config = {
        .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
        .sample_rate = 44100,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,  // ステレオ
        .communication_format = I2S_COMM_FORMAT_I2S,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
        .dma_buf_count = 6,
        .dma_buf_len = 60,
        .use_apll = false,
        .tx_desc_auto_clear = true
    };

    i2s_pin_config_t pin_config = {
        .bck_io_num = I2S_BCK_IO,
        .ws_io_num = I2S_WS_IO,
        .data_out_num = I2S_DO_IO,
        .data_in_num = I2S_PIN_NO_CHANGE
    };

    esp_err_t err = ESP_OK;
    err |= i2s_driver_install(I2S_NUM_USED, &i2s_config, 0, NULL);
    err |= i2s_set_pin(I2S_NUM_USED, &pin_config);
    err |= i2s_set_clk(I2S_NUM_USED, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
    return err == ESP_OK;
}

void setup() {
    M5.begin();
    M5.Lcd.setTextSize(2);
    M5.Lcd.println("Playing audio...");
    InitI2SSpeaker();
}

void loop() {
    unsigned int offset = 0;

    while (offset < audio_data_len) {
        size_t chunk = min(CHUNK_SIZE, audio_data_len - offset);
        size_t written;
        i2s_write(I2S_NUM_USED, audio_data + offset, chunk, &written, portMAX_DELAY);
        offset += written;
    }

    // 再生完了後にもう一度最初から再生(ループ)
}

色々作っているのですが、パパっと作ったコードでセキュリティリスクもあったりするので、どう公開するか悩み中です。

Windows、Iot関連、コード試作請け負います。
お気軽にご連絡ください。