Linuxを中心とした話題を投稿予定。 使用ディストリビューションであるFedoraが中心になると思われます。http://oedipa.wiki.fc2.com/にてTips Wikiを公開してます。
[PC][プログラム][C++]WAVEファイルを扱うクラスを作ってみた
割とよく使う割に、自前でちゃんとしたクラスを用意していなかったということで、ちょっくら自前で書いてみた。

リニアPCM限定、かつ先頭から44byteがヘッダであること前提というかなり手抜きなものですが、まぁさして実用にて困ることもないでしょう。

意外とWAVEファイルを扱うクラスの実装って少ないなーって思ってたんですが、単にWAVEファイルと言えども、その実、中身が結構異っているため、実装は簡単ではないみたいですね^^;

Windowsだったらwinmm.dllでしたっけか、あれのWAVEHEADEREXか忘れましたが、定義済みの構造体を使って、ついでにAPIも使う方が楽だとかなんとか。

とは言え、私はWinAPIが大嫌いなもので、それを使いたくなかったんですよねぇ。それに、扱うのはリニアPCMにほぼ限定されますし(せいぜい、32bit floatのWAVEファイルくらい)。

ってな訳で、これまで都度てけとーに関数やらクラスを書いてたのですが、いい加減まじめに作ってみようということでやってみました。

昨晩と今日の昼で書き上げたのですが、まだ動作チェックしてません…。したらボロが出そうだ。

ちなみに、今回使ってみたIDEはMonoDevelopです。本来はC#のオープンソース実装であるMonoの開発用なんですが、一応C/C++も開発できます。

見た目はVC++っぽく、最初は
「お、いい感じじゃん!」
と思ったのですが、悲しいかなインテリセンスが発生しませんでしたOrz

確かC#の開発のときには出てたと思うので、C/C++では出ないということだと思います。ちょっと残念。まぁC/C++開発はオマケってことでしょう。見た目はかなりいい感じなんだけどなぁ。

C/C++の開発はKDevelopとかEclipse + CDT、最近だったらQtDevelopとかになるんかな? WindowsだったらVC++EE一択なんだけどw

ところで、ofstreamをios::binaryで開いたときって、>>演算子で値の取得ってできないんですかね? コンパイルエラーは出ないんですが、値は取得できてませんでした。
まぁ、readメソッドでいいっちゃいいんですが、アレは第1引数に(char*)を要求するんですよね。binaryで開いてるんだし、(void*)でいいじゃねーかと思わなくもない。きっとなにか理由はあるんでしょうけどね^^;

さて、次は何を作りましょうかね?
せっかくなので晒してみる。
*WaveHeader.h

/** @file WaveHeader.h
    @brief WAVEファイルのヘッダ構造体
    @version 1.0
    @author code_air_edge
    @date 09/03/06
*/

/** @brief WAVEヘッダ構造体
*/
struct WaveHeader
{
    char RIFFTag[4]; //!< RIFFヘッダタグ
    int nFileSize; //!< これ以降のファイルサイズ(ファイルサイズー8)
    char WAVETag[4]; //!< WAVEヘッダタグ
    char fmtTag[4]; //!< fmtチャンクタグ
    int nFmtSize; //!< fmtチャンクのサイズ
    short int shFmtID; //!< フォーマットID
    short int shCh; //!< チャネル数
    int nSampleRate; //!< サンプリングレート
    int nBytePerSec; //!< データ速度
    short int shBlockSize; //!< ブロックサイズ
    short int shBitPerSample; //!< サンプルあたりのビット数
    char dataTag[4]; //!< dataチャンクタグ
    int nBytesData; //!< 波形データサイズ
};



*WaveStream.h

/** @file WaveStream.h
    @brief WaveStreamクラス
    fstreamを継承したWAVEファイルを扱うクラス
    @version 1.0
    @author code_air_edge
    @date 09/03/06
*/

#pragma once
#include <fstream>
#include "WaveHeader.h"

using namespace std;


/** @brief WaveStreamクラス
*/
class WaveStream
{
    protected:
    WaveHeader header; //!< WAVEヘッダ
    char *waveData; //!< 波形データ
    fstream stream; //!< ファイルストリーム
    ios::open_mode flag; //!< RWフラグ
    public:
    WaveStream(); //!< コンストラクタ
    /** @brief 引数付きコンストラクタ
        @param [in] filename WAVEファイル名
        @param [in] flag 入出力フラグ
        @exception 読み込みモード時、WAVEファイルでなければ例外発生
    */
    WaveStream(char *filename, ios::open_mode flag);
    ~WaveStream(); //!< デストラクタ
    /** @brief WAVEファイルオープン
        @param [in] filename WAVEファイル名
        @param [in] flag 入出力フラグ
        @exception 読み込みモード時、WAVEファイルでなければ例外発生
    */
    void Open(char *filename, ios::open_mode flag);
    /** @brief WAVEファイルクローズ
    */
    void Close();
    /** @brief WAVEファイルを書き出し
        @exception 波形データがなければ例外発生
        @exception 書き出しモードでWAVEファイルをオープンしていなければ例外発生
    */
    void Write();
    /** @brief WAVEヘッダ取得
        @return WAVEヘッダ
        @exception 読み込みモードでWAVEファイルをオープンしていなければ例外発生
    */
    WaveHeader GetHeader();
    /** @brief WAVEヘッダセット
        @param[in] header WAVEヘッダ
        @exception 書き出しモードでWAVEファイルをオープンしていなければ例外発生
    */
    void SetHeader(WaveHeader *header);
    /** @brief 波形データ取得
        予め必要な領域は確保しておくこと
        @param [out] waveData 波形データ
        @exception 読み込みモードでWAVEファイルをオープンしていなければ例外発生
    */
    void GetWaveData(char *waveData);
    /** @brief 波形データセット
        予めヘッダをセットしておくこと。
        @param[in] waveData 波形データ
        @exception 書き出しモードでWAVEファイルをオープンしていなければ例外発生
    */
    void SetWaveData(char *waveData);   
};



*WaveStream.cpp

/** @file WaveStream.cpp
*/

#include "WaveStream.h"
#include <cstring>
#include <fstream>

using namespace std;

WaveStream::WaveStream()
{
    memset(&header, 0, sizeof(WaveHeader));
    waveData = NULL;
}

WaveStream::WaveStream(char *filename, ios::open_mode flag)
{
    this->Open(filename, flag);
}

WaveStream::~WaveStream()
{
    if(waveData)
    {
        delete [] waveData;
    }
    if(stream.is_open())
    {
        stream.close();
    }
}

void WaveStream::Open(char *filename, ios::open_mode flag)
{
    if(flag != ios::in && flag != ios::out)
    {
        throw "フラグはin,outのいずれかである必要があります";
    }
    stream.open(filename, ios::binary | (flag == ios::in) ? ios::in : ios::out);
    this->flag = flag;
   
    if(flag == ios::out)
    {
        memset(&header, 0, sizeof(WaveHeader));
        return;
    }
    stream.read((char *)&header, sizeof(WaveHeader));
    if(strncmp(header.RIFFTag, "RIFF", 4))
    {
        stream.close();
        throw "WAVEファイルではありません";
    }
    if(strncmp(header.WAVETag, "WAVE", 4))
    {
        stream.close();
        throw "WAVEファイルではありません";
    }
    if(strncmp(header.fmtTag, "fmt ", 4))
    {
        stream.close();
        throw "リニPCM WAVEファイルではありません";
    }
    if(header.shFmtID != 1)
    {
        stream.close();
        throw "リニアPCM WAVEファイルではありません";
    }
   
    waveData = new char[header.nBytesData];
    stream.read(waveData, header.nBytesData);
}

void WaveStream::Close()
{
    if(stream.is_open())
    {
        stream.close();
    }
    if(waveData)
    {
        delete [] waveData;
    }
}

void WaveStream::Write()
{
    if(!stream.is_open())
    {
        throw "ファイルハンドルが開かれていません";
    }
    if(flag != ios::out)
    {
        stream.close();
        throw "読み込みモードで開かれています";
    }
    if(!waveData)
    {
        stream.close();
        throw "波形データがありません";
    }
    stream.write((char *)&header, sizeof(WaveHeader));
    stream.write(waveData, header.nBytesData);
}

WaveHeader WaveStream::GetHeader()
{
    if(!stream.is_open())
    {
        throw "ファイルハンドルが開かれていません";
    }
    if(flag != ios::in)
    {
        stream.close();
        throw "書き出しモードで開かれています";
    }
    return header;
}

void WaveStream::SetHeader(WaveHeader *header)
{
    if(!stream.is_open())
    {
        throw "ファイルハンドルが開かれていません";
    }
    if(flag != ios::out)
    {
        stream.close();
        throw "読み込みモードで開かれています";
    }
    memcpy(&(this->header), header, sizeof(WaveHeader));
}

void WaveStream::GetWaveData(char *waveData)
{
    if(!stream.is_open())
    {
        throw "ファイルハンドルが開かれていません";
    }
    if(flag != ios::in)
    {
        stream.close();
        throw "書き出しモードで開かれています";
    }
    memcpy(waveData, this->waveData, header.nBytesData);
}

void WaveStream::SetWaveData(char *waveData)
{
    if(!stream.is_open())
    {
        throw "ファイルハンドルが開かれていません";
    }
    if(flag != ios::out)
    {
        stream.close();
        throw "読み込みモードで開かれています";
    }
    memcpy(this->waveData, waveData, header.nBytesData);
}

関連記事
スポンサーサイト



コメント
この記事へのコメント
前回の日記の鍵コメの人です。
鍵コメって書いた本人にも見えないんですね…(笑)
しかも酔っぱらった勢いで書いたので
コメント内容が思い出せない(爆)

当方、C++にあってCにない
new charだとかdelete[]とかいう記述に
なかなか慣れないもので
すぐに calloc やら free に走っちゃいますね。
年を取ると頭がかたくなってどうも良く無いや(笑)
2009/03/07(Sat) 23:43 | URL  | Koga #zo3yjUW6[ 編集]
酔ってらしたのですかw
鍵コメって本人にも見えないんですね^^; まぁログインでもしない限り本人かどうかは判断できないですものねぇ…。それもしゃーないか。

酔った勢いということなら、内容は深く考えないことにしますw また飲みましょう!^^

new・deleteはクラスオブジェクト対象でなければ{m,c}alloc・freeで代用しても全然平気ですよー^^
確か(void *)型はnewでは確保できなかったように思うので、allocもバリバリ現役っすよー。
2009/03/08(Sun) 11:27 | URL  | code_air_edge #-[ 編集]
コメントを投稿
URL:
Comment:
Pass:
秘密: 管理者にだけ表示を許可
 
トラックバック
この記事のトラックバックURL
この記事へのトラックバック