DirectXで音を鳴らしたくていろいろ調べてるのですが、
みなさんMMIO系の関数を使って読み込んでいましたが、
音を愛する僕がそんな便利なものに頼っていいのかと悩んだ末に
自力で読み込んでみることにしました。
WAVE.h
#include <vector>
#include <string>
class WAVE
{
public:
// コンストラクタ
WAVE();
// デストラクタ
~WAVE();
// 代入
void operator=(const WAVE& wave);
// チャンネル数
int channel;
// サンプリング周波数
int sample;
// 量子化ビット数
int bit;
// 時間
int length;
// 音楽データ・モノラル
std::vector<double>monaural;
// 音楽データ・ステレオ
std::vector <std::vector<double>>stereo;
};
// 名前空間指定
namespace sound {
// 文字確認
int CheckChar(unsigned char* data, int dataSize, const std::string& find);
// モノラル・8ビット
void LoadMonaural8(WAVE& wave, FILE* file);
// モノラル・16ビット
void LoadMonaural16(WAVE& wave, FILE* file);
// ステレオ・8ビット
void LoadStereo8(WAVE& wave, FILE* file);
// ステレオ・16ビット
void LoadStereo16(WAVE& wave, FILE* file);
// WAVEの読み込み
int LoadWave(const std::string& fileName, WAVE& wave);
}
WAVE.cpp
#define _USE_MATH_DEFINES
#include "WAVE.h"
#include <iostream>
#include <math.h>
// チャンクIDサイズの最大
#define ID_MAX 4
// オーバーフローの防止
#define OVERFLLOW 32768.0
// RIFFチャンク
struct RIFF {
//チャンクID
unsigned char chunkID[ID_MAX];
//チャンクサイズ
unsigned long chunkSize;
//フォーマットタイプ
unsigned char chunkFormatType[ID_MAX];
};
// FMTチャンク
struct FMT {
//チャンクID
unsigned char chunkID[ID_MAX];
//チャンクサイズ
unsigned long chunkSize;
//フォーマットタイプ
unsigned short waveFormatType;
//フォーマットチャンネル
unsigned short formatChannel;
//サンプリング周波数
unsigned long samplesPerSec;
//ビットレート
unsigned long bytesPerSec;
//ブロックサイズ
unsigned short blockSize;
//量子化ビット数
unsigned short bitsPerSample;
};
// DATAチャンク
struct DATA {
//チャンクID
std::string chunkID;
//チャンクサイズ
unsigned long chunkSize;
};
// 8ビットステレオデータ
struct Sample8 {
//左
unsigned char left;
//右
unsigned char right;
};
// 16ビットステレオデータ
struct Sample16 {
//左
short left;
//右
short right;
};
// コンストラクタ
WAVE::WAVE()
{
}
// デストラクタ
WAVE::~WAVE()
{
}
// 代入
void WAVE::operator=(const WAVE & wave)
{
channel = wave.channel;
sample = wave.sample;
bit = wave.bit;
length = wave.length;
monaural = wave.monaural;
stereo = wave.stereo;
}
// 文字確認
int sound::CheckChar(unsigned char * data, int dataSize, const std::string & find)
{
for (unsigned int i = 0; i < find.size(); ++i)
{
for (int d = 0; d < dataSize; ++d)
{
if (data[d] == find[i])
{
break;
}
else
{
//文字確認失敗
if (d >= dataSize - 1)
{
return -1;
}
}
}
}
return 0;
}
// モノラル・8ビット
void sound::LoadMonaural8(WAVE & wave, FILE * file)
{
wave.monaural.resize(wave.length);
unsigned char data = 0;
for (int i = 0; i < wave.length; ++i)
{
fread(&data, sizeof(data), 1, file);
wave.monaural[i] = static_cast<double>(data);
}
}
// モノラル・16ビット
void sound::LoadMonaural16(WAVE & wave, FILE * file)
{
wave.monaural.resize(wave.length);
short data = 0;
for (int i = 0; i < wave.length; ++i)
{
fread(&data, sizeof(data), 1, file);
/* 音データを-1以上1未満の範囲に正規化する */
wave.monaural[i] = static_cast<double>(data) / OVERFLLOW;
}
}
// ステレオ・8ビット
void sound::LoadStereo8(WAVE & wave, FILE * file)
{
wave.stereo.resize(wave.channel);
for (auto& w : wave.stereo)
{
w.resize(wave.length);
}
Sample8 data = {};
for (int i = 0; i < wave.length; ++i)
{
fread(&data, sizeof(data), 1, file);
wave.stereo[0][i] = static_cast<double>(data.left);
wave.stereo[1][i] = static_cast<double>(data.right);
}
}
// ステレオ・16ビット
void sound::LoadStereo16(WAVE & wave, FILE * file)
{
wave.stereo.resize(wave.channel);
for (auto& w : wave.stereo)
{
w.resize(wave.length);
}
Sample16 data = {};
for (int i = 0; i < wave.length; ++i)
{
fread(&data, sizeof(data), 1, file);
/* 音データを-1以上1未満の範囲に正規化する */
wave.stereo[0][i] = static_cast<double>(data.left) / OVERFLLOW;
wave.stereo[1][i] = static_cast<double>(data.right) / OVERFLLOW;
}
}
// WAVEの読み込み
int sound::LoadWave(const std::string & fileName, WAVE & wave)
{
FILE* file = nullptr;
//ファイルオープン
if (fopen_s(&file, fileName.c_str(), "rb") != 0)
{
return -1;
}
//チャンク宣言
RIFF riff = {};
FMT fmt = {};
DATA data = {};
//RIFF読み込み
fread(&riff.chunkID[0], sizeof(riff.chunkID), 1, file);
if (CheckChar(riff.chunkID, ID_MAX, "RIFF") != 0)
{
fclose(file);
return -1;
}
fread(&riff.chunkSize, sizeof(riff.chunkSize), 1, file);
fread(&riff.chunkFormatType[0], sizeof(riff.chunkFormatType), 1, file);
if (CheckChar(riff.chunkFormatType, ID_MAX, "WAVE") != 0)
{
fclose(file);
return -1;
}
//FMT読み込み
fread(&fmt.chunkID[0], sizeof(fmt.chunkID), 1, file);
if (CheckChar(fmt.chunkID, ID_MAX, "fmt ") != 0)
{
fclose(file);
return -1;
}
fread(&fmt.chunkSize, sizeof(fmt.chunkSize), 1, file);
fread(&fmt.waveFormatType, sizeof(fmt.waveFormatType), 1, file);
fread(&fmt.formatChannel, sizeof(fmt.formatChannel), 1, file);
fread(&fmt.samplesPerSec, sizeof(fmt.samplesPerSec), 1, file);
fread(&fmt.bytesPerSec, sizeof(fmt.bytesPerSec), 1, file);
fread(&fmt.blockSize, sizeof(fmt.blockSize), 1, file);
fread(&fmt.bitsPerSample, sizeof(fmt.bitsPerSample), 1, file);
//拡張部分
std::vector<unsigned char>extended;
extended.resize(fmt.chunkSize - (sizeof(fmt) - sizeof(fmt.chunkID) - sizeof(fmt.chunkSize)));
if (extended.size() > 0)
{
fread(&extended[0], sizeof(unsigned char) * extended.size(), 1, file);
}
//ダミー宣言
std::string chunkID;
chunkID.resize(sizeof(unsigned char) * ID_MAX);
//ID判別
fread(&chunkID[0], sizeof(unsigned char) * ID_MAX, 1, file);
//DATA以外の場合
while (chunkID != "data")
{
//サイズ
unsigned long size = 0;
fread(&size, sizeof(size), 1, file);
//データ
std::vector<unsigned char>data;
data.resize(size);
fread(&data[0], sizeof(unsigned char) * size, 1, file);
//ID
fread(&chunkID[0], sizeof(unsigned char) * ID_MAX, 1, file);
}
//DATA読み込み
data.chunkID = chunkID;
fread(&data.chunkSize, sizeof(data.chunkSize), 1, file);
wave.channel = fmt.formatChannel;
wave.sample = fmt.samplesPerSec;
wave.bit = fmt.bitsPerSample;
wave.length = data.chunkSize / fmt.blockSize;
//モノラル
if (fmt.formatChannel == 1)
{
//8ビット
if (fmt.bitsPerSample == 8)
{
LoadMonaural8(wave, file);
}
//16ビット
else if (fmt.bitsPerSample == 16)
{
LoadMonaural16(wave, file);
}
}
//ステレオ
else if (fmt.formatChannel == 2)
{
//8ビット
if (fmt.bitsPerSample == 8)
{
LoadStereo8(wave, file);
}
//16ビット
else if (fmt.bitsPerSample == 16)
{
LoadStereo16(wave, file);
}
}
//ファイルクローズ
fclose(file);
return 0;
}
こんな感じで実装しました。
モノラル、ステレオの8bit、16bitに対応してます。
これらからDirectSoundや、XAudio2にデータを渡す方法で悩んでます。
もしよかったら、この読み込みについての改善点や
データの渡す方法などコメントで教えて頂ければありがたいです。