リニア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);
}
SECRET: 0
返信削除PASS: ec515437e275ef6a2e84be18194d8803
前回の日記の鍵コメの人です。
鍵コメって書いた本人にも見えないんですね…(笑)
しかも酔っぱらった勢いで書いたので
コメント内容が思い出せない(爆)
当方、C++にあってCにない
new charだとかdelete[]とかいう記述に
なかなか慣れないもので
すぐに calloc やら free に走っちゃいますね。
年を取ると頭がかたくなってどうも良く無いや(笑)