LoginSignup
4
7

More than 5 years have passed since last update.

C++によるWAVEの読み込み

Last updated at Posted at 2018-08-13

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にデータを渡す方法で悩んでます。

もしよかったら、この読み込みについての改善点や
データの渡す方法などコメントで教えて頂ければありがたいです。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7