るみさんのブログ

【備忘録】JAVAでwavを再生するコードを0から書いてみた

作成日:2024-07-21 19:49:13
最新更新日:2024-07-21 20:15:16

WAVの中身の参考元
使った音源:EnyaのBook Of Days

はじめに


ArduinoでWavファイルを再生/録音する動画を見て、私もやってみたくなった!
しかし私はArduinoを持ってないし、買うお金もないので、、、
先に理解を深めるためにWavファイルをJAVAで再生してみる事に!

あ、あと一応GitHubに上げてバックアップしました、PrivateでやるつもりがPubricになったけど知らね
https://github.com/SINtyanneru/WavTest

完成品はこちら

WAVファイルについて


みんなご存知、マイクロソフトの息がかかったファイルです。
マイクロソフトと聞いて生理的に受け付けない人も居るかもしれませんが、ファイル自体はとても優秀です。

まず、無圧縮です、なのでCD音源とかを取り込んで保管したいときにおすすめ!
MP3は非可逆圧縮なので、色々失われます。まあ人間の耳ではわからないけど、保管するなら高音質がいいでしょ?
他にも可逆圧縮なFLACとかあるけど、圧縮とか理解するのはめんどうくさいので知らんふり。
あとあまり容量減らないのでWAVでいいと思う、まあ保管はFLAC使ってるけど()

WAVファイルは先頭にヘッダーがあります。
最初にASCIIコードでRIFFという文字列があり、これがあるか否かでWAVか判定すれば良さそう、
WAVEっていう文字列も進んだ場所にあるけど、めんどいし
RIFFの次にファイルサイズがあります、でもbyte[]の.lengthでファイルサイズ取得できるので要らない。
/Data/Blog/4a202da021f8bd5ed805cbb24303900e_1721592056.png

そしてWindowsで取り込むとなぜかWAVEの後にLISTとかいう文字列が、これの正体はアーティスト名とかそういう物
本来は最後にあるらしいけど、まあ公式が先頭に置いてるなら先頭が正しいんでしょう。
/Data/Blog/4a202da021f8bd5ed805cbb24303900e_1721592028.png

次にfmtというセクションがあります、なぜかfmtの後にASCIコードでスペースが入ってますが、謎
fmt[スペース]の後にfmtのサイズが書いてますが、まあ要らないでしょう、4バイトぐらいあります。
進むと2バイト専有している「フォーマット形式」があります。
先頭が01ならPCM、02ならMPEGらしい、01を想定するので02は弾きましょう!
そしてそして2バイト専有しているチャンネル数、1バイトでいい気もする。
65535(FFFF)チャンネルもスピーカー付けないでしょう()
次に4バイト分でサンプリングレート、、、ですがどうやら逆さに読むらしい、なぜ???
/Data/Blog/4a202da021f8bd5ed805cbb24303900e_1721592094.png

次にdataセクションです、これが本体
dataという文字列から4バイト分のところに長さが書いてます、これは逆さから読まない。
BookOfDaysの場合はD044DA01の模様です。
/Data/Blog/4a202da021f8bd5ed805cbb24303900e_1721592448.png

早速再生してみる!


byte[] FILE_DATA = Files.readAllBytes(Paths.get("test.wav"));

でファイルを開きます。
ちな此のために10進数を16進数に変換する関数も作りました()
まあそれはどうでもよくて、再生するだけならこれでできます。

public static void PLAY_WAV(byte[] DATA, int POS, long DATA_LENGTH, int CHANEL, int SAMPLING_RATE) throws Exception{ int sampleSizeInBits = 16; boolean signed = true; boolean bigEndian = false; AudioFormat format = new AudioFormat(SAMPLING_RATE, sampleSizeInBits, CHANEL, signed, bigEndian); SourceDataLine line = AudioSystem.getSourceDataLine(format); line.open(format); line.start(); LOG(LOG_TYPE.OK, "スピーカーを初期化しました、再生を開始します"); for(int I = POS + 4; I < DATA_LENGTH; I += 1000){ byte[] BUFFER = new byte[1000]; //1000バイトずつ読み込む for(int I2 = 0; I2 < BUFFER.length; I2++){ BUFFER[I2] = DATA[I + I2]; } //読み込んだ分を入れる line.write(BUFFER, 0, BUFFER.length); //line.drain(); } LOG(LOG_TYPE.OK, "再生完了"); //開放 line.close(); LOG(LOG_TYPE.OK, "開放しました"); }

LOGっていうのは独自ライブラリの関数なので、機にしないでください。
DATAにさっき読み込んだやつを、POSはデータセクションの開始位置、めんどければ0でもいい、プツプツ音がするけど()
DATA_LENGTHにデータセクションのサイズを、これは最後にLISTを置く系のがあるので()

SourceDataLineという標準ライブラリに数値を渡すだけで再生してくれます!
JAVAの標準ライブラリが強すぎる説はあるけど、おそらくデバイスファイルに書き込むときも似た仕組みになるんじゃないかな?

っていうか、私っぽくない命名規則だなと思ったそこのあなた、鋭い。
ChatGPTに「1000Hzの正弦波を再生するコード」を生成したときのやつを使いまわしています()
まあ大幅にコード削ってるけども()

終わりに


次回はFLACかな、MP3かな()
意外と簡単なので、みんなもやってみてね!!!



シェア



コメント欄の復旧は少し待ってね