using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO; //ファイル操作に必要
namespace WAVE_file
{
/*********************************************
* waveファイルを扱うためのクラス
* 開発者:Katsuhiro Morishita(2011.2) Kumamoto-University
*
* 対応フォーマット:8bit/16bit ステレオ/モノラル リニアPCM
*
* 更新情報:2011/2/18 開発開始
* 参考文献: waveフォーマット:http://www.kk.iij4u.or.jp/~kondo/wave/
* リニアPCM :http://e-words.jp/w/E383AAE3838BE382A2PCM.html
*
* 更新情報:2011/2/21 読み込みファイルのチャンネル(モノラル/ステレオ)を外部から参照できるように解放
*********************************************/
///
/// waveファイルを扱うためのクラス
///
/// 使用する際には、プロジェクトにファイルを追加した後に、ソースヘッダに以下のコードを追加して下さい。
/// 「
/// using WAVE_file;
/// 」
///
class wave
{
// 定数宣言
public const short MONAURAL = 1;
public const short STEREO = 2;
// 構造体宣言
private struct WaveFileHeader // waveファイルのヘッダ構造
{
public Int16 ID; // データ形式(リニアPCMなら1)
public Int16 Cannel; // チャネル数(モノラル/ステレオ)
public Int32 Sampling_Rate; // サンプリングレート[sampling/sec]
public Int32 Data_Rate; // データレート[byte/sec](チャネル数×ブロックサイズ)
public Int16 Block_Size; // 1サンプル当たりのサイズ[byte/sample](分解能で決まる1データサイズ×チャネル)
public Int16 Resolution_Bit; // 1サンプル当たりのビット数[bit/sample](分解能)
public UInt32 Wave_Data_Size; // 波形データサイズ[byte]
}
public struct MusicDatas {
public Int16 Left;
public Int16 Right;
}
// グローバル変数の宣言
private WaveFileHeader file_info;
private Boolean IsOpenStatus = false;
private FileStream fs;
private BinaryReader reader;
private UInt32 ReadWaveDataSize = 0; // 読み込み済みの波形データサイズ
/**************************** プロパティ ************************************/
/*-----------------------------------
* プロパティ ファイルを開けたかどうかの状態を示す。
*
* 本クラスを利用する外部側から、ファイルの状態をチェックする
* ---------------------------------*/
///
/// ファイルを開けたか状態をチェック
///
public Boolean IsOpen {
get {
return this.IsOpenStatus;
}
}
/*-----------------------------------
* プロパティ 読み込んだファイルがステレオかどうかを示す。
*
* 読み込みを確認した後でアクセスしないと、値がどうなるやら不明?
* ---------------------------------*/
///
/// ファイルを開けたか状態をチェック
///
public Int16 Channel
{
get
{
return this.file_info.Cannel;
}
}
/*-----------------------------------
* プロパティ データを読み切ったかどうかを確認する
*
* 本クラスを利用する外部側から、読み込み状況をチェックする
* tureであれば、読み込めるデータが無くなったことを示す。
* ---------------------------------*/
///
/// 最後まで読み切るとtrue
///
public Boolean ReadLimit
{
get
{
Boolean ans;
if (this.ReadWaveDataSize < this.file_info.Wave_Data_Size){
ans = false;
}
else {
ans = true;
}
return ans;
}
}
/*-----------------------------------
* プロパティ 読み出し位置の時刻[s]を返す
*
* 本クラスを利用する外部側から、読み出し位置の時間を計算する
* ---------------------------------*/
///
/// 経過時間[s]
///
public double NowTime {
get { return (double)ReadWaveDataSize/(double)this.file_info.Block_Size/(double)this.file_info.Sampling_Rate; }
}
/*-----------------------------------
* プロパティ サンプリング周波数を返す
*
* 本クラスを利用する外部側から、読み出し位置の時間を計算する
* ---------------------------------*/
///
/// サンプリング周波数
///
public int SamplingRate
{
get { return (int)this.file_info.Sampling_Rate; }
}
/*-----------------------------------
* プロパティ 量子化分解能を返す
*
* 本クラスを利用する外部側から、読み出し位置の時間を計算する
* ---------------------------------*/
///
/// 量子化分解能
///
public int Resolution
{
get { return (int)this.file_info.Resolution_Bit; }
}
/**************************** メソッド ************************************/
/*-----------------------------------
* ファイルを閉じる
* ---------------------------------*/
///
/// ファイルを閉じる
///
public void Close(){
if (this.reader != null)
{
this.reader.Close(); // こちらを先に閉じる
this.fs.Close(); // これが後。リソースを解放しているのか少し怪しい。
this.fs.Dispose();
}
this.IsOpenStatus = false;
return;
}
/*-----------------------------------
* wave音楽ファイルであるかをチェックする
* リニアPCM形式のwave音楽ファイルでなければ、falseを返す
*
* もっとスマートなチェック方法が必ず存在するはず・・・。
* ---------------------------------*/
private Boolean File_check(){
byte[] buf;
Boolean ans = true;
int fmt_chunk_size = 0, fmt_read_size = 0;
buf = reader.ReadBytes(4);
if (ans && (buf[0] != 'R' || buf[1] != 'I' || buf[2] != 'F' || buf[3] != 'F'))ans = false;
if (ans)buf = reader.ReadBytes(4); // 読み飛ばし
if (ans) buf = reader.ReadBytes(4);
if (ans && (buf[0] != 'W' || buf[1] != 'A' || buf[2] != 'V' || buf[3] != 'E')) ans = false;
if (ans) buf = reader.ReadBytes(4); //
if (ans && (buf[0] != 'f' || buf[1] != 'm' || buf[2] != 't' || buf[3] != ' ')) ans = false;
if (ans) fmt_chunk_size = reader.ReadInt32(); // fmtチャンクのサイズを取得.リニアPCMであれば16.
if (ans) this.file_info.ID = reader.ReadInt16();
if (ans && this.file_info.ID != 1) ans = false; // リニアPCMであるかをチェック
if (ans) this.file_info.Cannel = reader.ReadInt16();
if (ans) this.file_info.Sampling_Rate = reader.ReadInt32();
if (ans) this.file_info.Data_Rate = reader.ReadInt32();
if (ans) this.file_info.Block_Size = reader.ReadInt16();
if (ans) this.file_info.Resolution_Bit = reader.ReadInt16();
if (ans && this.file_info.Resolution_Bit != 8 && this.file_info.Resolution_Bit != 16) ans = false;
while (ans && (fmt_chunk_size - 16 - fmt_read_size) > 0) { // fmtチャンクの拡張部分を読み飛ばす
buf = reader.ReadBytes(1); // ID == 1なら、存在しないので必要ないが、拡張に備えて設置
fmt_read_size++;
}
if (ans) buf = reader.ReadBytes(4); //
if (ans && (buf[0] != 'd' || buf[1] != 'a' || buf[2] != 't' || buf[3] != 'a')) ans = false;
if (ans) this.file_info.Wave_Data_Size = reader.ReadUInt32(); // 波形データサイズを取得
return ans;
}
/*-----------------------------------
* ファイルを開く
*
* 引数:ファイルダイアログ
* 返値:成功するとtrueを返す
* ---------------------------------*/
///
/// ファイルを開く
///
public void Open(OpenFileDialog dialog){
Boolean format_match;
if (this.IsOpenStatus == true) this.Close(); // もしファイルを既に開いているなら、一度閉じる
fs = new FileStream(dialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
reader = new BinaryReader(fs);
format_match = File_check();
if (format_match == false) this.Close();
if (format_match) this.IsOpenStatus = true;
this.ReadWaveDataSize = 0;
return;
}
/*-----------------------------------
* 指定ポイント数のデータを読み出す
*
* 引数:ポイント数
* 返値:読めるデータが無くなると、0を書き込んで返す
*
* 完読したことを知らせるプロパティが必要
* ---------------------------------*/
///
/// 指定ポイント数のデータを読み込む
/// モノラルなら、Leftに格納される
///
public MusicDatas[] Read(int size)
{
MusicDatas[] music = new MusicDatas[size]; // これにデータを格納して返す
int i;
if (this.IsOpenStatus) {
for (i = 0; i < size && this.ReadWaveDataSize < this.file_info.Wave_Data_Size; i++){
if (this.ReadWaveDataSize < this.file_info.Wave_Data_Size)
{
if (this.file_info.Resolution_Bit == 8) music[i].Left = (Int16)reader.ReadSByte();
if (this.file_info.Resolution_Bit == 16) music[i].Left = (Int16)reader.ReadInt16();
switch (this.file_info.Cannel)
{
case STEREO:
if (this.file_info.Resolution_Bit == 8) music[i].Right = (Int16)reader.ReadSByte();
if (this.file_info.Resolution_Bit == 16) music[i].Right = (Int16)reader.ReadInt16();
break;
case MONAURAL:
music[i].Right = 0;
break;
}
this.ReadWaveDataSize += (UInt32)this.file_info.Block_Size; // 読み込んだデータ量を更新
}
else {
music[i].Left = 0; // 読めるデータが無くなると、0を書き込んで返す
music[i].Right = 0;
}
}
}
return music;
}
/*-----------------------------------
* 指定時間分のデータが収まる配列サイズを返す
* 引数:時間[s]
* ---------------------------------*/
///
/// 指定時間分のデータを読み込むのに必要となる配列サイズを返す
///
public int GetArrSize(double time) {
return (int)((double)this.file_info .Sampling_Rate * time);
}
/*-----------------------------------
* 時間幅指定でデータを読み出す
* オーバーロードを利用して同じ名前でメソッドを定義した
* 引数:時間[s]
* ---------------------------------*/
///
/// 指定時間分の音声データを返す
///
public MusicDatas[] Read(double time) {
return this.Read (GetArrSize(time));
}
/**************************** 初期化/削除 ************************************/
/*****************
* このクラスのデストラクタ
*
* 初期化時に使用します。
* ***************/
///
/// デストラクタ.
///
~wave(){
this.Close();
}
}
}