ソフトな忘却力(19) .wavファイルの生成、octaveとcppの両方で

Joseph Halfmoon

WAVファイルを扱おうとしたらいつの間にかWindowsの標準アプリではそのようなフォーマットは捨て去られていたのにようやく気づきました。お間抜け。自分で音声信号のファイルを生成したりするときには楽なのだWAV形式。単なるPCMだし。ヘッダも簡単。今回はOctaveとC++の両方でWAVファイル書き出しやってみます。

WAV形式は、RIFF(Resource Interchage File Format)の一種で実は非圧縮のPCMデータを格納しなければいけない決まりではないみたいです。しかし、私の心の中では、非圧縮の8ビットもしくは16ビットPCM、モノラル/ステレオ、サンプリング周波数44.1/22.05/11.025kHzの音声データファイル、にキメウチです。多分、「かつて」実際そうだったのではないかと。

しかし、何時の間にかWindowsの標準アプリでは生成できなくなってましたね。昔お世話になった「サウンドレコーダー」が廃止?になったのは何時なのかしら。当方、WAVフォーマットで音楽を聴こうなどという野望を持っているわけでもなく、単に信号処理した結果などを読み書きするのに生データに限りなく近いWAVフォーマットがお好みというだけであります。

Octaveでの書き出し

信号処理の開発では、MATLAB/Octave/Scilabなどのソフトウエアを使うことが多いと思います。当然奴ら、サウンドファイルの読み書き機能は持っている、と。まずは、リファレンス用にOctaveで以下の仕様のWAVファイルを書き出してみました。

  • サンプリング周波数 11.025kHz
  • 8ビットPCMモノラル
  • 440Hz正弦波を1秒記録

Octaveのスクリプトが以下に

% generate .wave file sample
samplingHz=11025;
quantization=8;
soundHz=440;
nData=11025;
dt=1.0/samplingHz;
t=(1:nData)*dt-dt;
soundOmega=2*pi*soundHz;
y=sin(soundOmega*t);
audiowrite("test.wav", y, samplingHz, "BitsPerSample", quantization);
info=audioinfo("test.wav");

上記を走らせて得た test.wav ファイルを以下に添付しました。再生ボタンを押せば音が聞こえる筈。

C++での書き出し

自前のソフトに処理を組み込むことを考えると C/C++ で書けた方が安心です。まあ WAVフォーマットは単純なので、自力でバイナリで書き込んでも作れると思います。ホスト機上であれば、世の中には立派なライブラリが存在するので無理に自力で書く必要もなし。今回は以下のライブラリを使わせていただきます。

libsndfile

Windows上のVSCodeから、Windows配下のWSLにインストールしてあるUbuntu 20.04LTS上でソース作成、ビルドして実行確認いたしました。例によって CMake 使用です。CMakeLists.txt(ツールに自動生成してもらったものにtarget_link_librariesのみ自力更生) はこんな感じ。

cmake_minimum_required(VERSION 3.0.0)
project(wavefileSample VERSION 0.1.0)

include(CTest)
enable_testing()

add_executable(wavefileSample main.cpp)

target_link_libraries(
    wavefileSample
    sndfile
)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

ソースのmain.cppはこちら。今回は8ビット符号無PCMなので、「音源の生成のところ」がモッタイナイ感じ。

#include <iostream>
#include <sndfile.h>
#include <cmath>

using namespace std;

int main(int, char**) {
    int samplingHz=11025;
    int quantization=8;
    int soundHz=440;
    int nData=11025;
    double dt = 1.0/samplingHz;
    double soundOmega=2.0*M_PI*soundHz;
    unsigned char buffer[nData];
    for (int idx=0; idx<nData; idx++) {
        buffer[idx] = (sin(soundOmega * dt * idx) + 1.0) * 256.0 / 2.0;
    }
    SF_INFO info;
    info.samplerate = samplingHz;
    info.channels = 1;
    info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_U8;
    SNDFILE *file=sf_open("csample.wav", SFM_WRITE, &info);
    sf_write_raw(file, buffer, nData);
    sf_close(file);
    cout << "generate .wav file sample.\n";
    return 0;
}

なお、しばらくローカル環境のWSL使っておらず update, upgradeした後に

  • cmake
  • ninja-build
  • libsndfile1-dev

のインストールが必要でした。ちょい昔はcmake使ってなかったです。はい。最後のやつが今回のライブラリです。ご本家ホームページみるとライブラリのビルドの仕方など説明されていますが、使うだけであれば上記を apt install するだけで大丈夫でした。

ビルド後のファイルを動かして生成した音声ファイルが以下に。

耳で聞く限り Octave生成のファイルと一緒に聞こえます。耳大丈夫か?ホントか?

まあ、これで信号処理した結果のファイルをWAV出力するのはOKということで。

ソフトな忘却力(18) 偶数個のときのmedian、浮動小数のgtest へ戻る

ソフトな忘却力(20) Node-REDのsmoothノードのフィルタ特性を推定 へ進む