24ビットintを読み込むメモ(C++)

昨日に続きC++でwavファイルを読み込む処理を書く時のメモになります。

読み込んだバイト列をサンプル単位の値に変換するときに、8・16・32ビットのデータであれば値をそのままキャストするだけで済むのですが、
24ビットだった場合の処理で毎回手間取ってしまうためここに書いておくことにしました。

バイト列を読み込んで32ビットintで返す構造体を定義します。

struct int24
{
private:
    unsigned char bits[3];
public:
    static const std::int32_t MaxValue = 0x007fffff;     //  8388607
    static const std::int32_t MinValue = 0xff800000;     // -8388608
    explicit operator std::int32_t() const
    {
        if (bits[2] & 0x80)
            return bits[0] | (bits[1] << 8) | (bits[2] << 16) | (0xff << 24);
        else
            return bits[0] | (bits[1] << 8) | (bits[2] << 16);
    }
};

読み込む際はここからintにキャストして

std::shared_ptr<char[]> buffer;    // 読み込むバイト列
/* 読み込み処理 */
int24 val24 = std::reinterpret_pointer_cast<int24[]>(buffer)[0];

std::int32_t val32 = std::static_cast<std::int32_t>(val24);

とかすればよし。……おそらく。

逆に32ビットintからint24に変換したい場合は符号を見て0x7fffffでマスクしたりすればよいと思います(省略)。

配列のshared_ptr

C++でwavファイル等の読み込みをする際に、先にバッファを確保して中身を読む処理が入るのですが、

auto* buffer = new char[size];

/* 読み込み処理 */
free[] buffer;

みたいな形でバッファを確保しているとエラー処理等の分岐時にどこかで解放漏れが起こりそうで怖くありますね(そしてメモリリークが起こる)。

配列をshared_ptrに出来ればこれが楽になるのになと思っていたら、C++14で可能になっていたということに今更ながら先日気付きました。
C++17でさらに変わったらしい記述がこれ。

#include <memory>
auto buffer = std::shared_ptr<char[]>(size);
/* 読み込み処理 */

こうしておけばうかつな解放忘れを防げるわけですねー。

charをunsigned charにするなど違う型にキャストする場合はreinterpret_pointer_cast

auto hoge = std::reinterpret_pointer_cast<unsigned char[]>(buffer);

この書き方もC++17からとのこと。

便利な仕様ですが、VC2022を使って新規プロジェクトを作ると初期設定の言語がC++14であるため設定を変えないと使えない罠。

便利な新機能は特に縛りが無い限り使った方がお得ですね。新機能と云っても8年前ですが。