web-dev-qa-db-ja.com

Java-WAVファイルの読み取り、操作、書き込み

Javaプログラムで、オーディオファイルを読み取る最良の方法は何ですか( [〜#〜] wav [〜#〜] ファイル)を数値の配列(float[]short[]、...)、および数値の配列からWAVファイルを書き込むには?

19
yonatan

AudioInputStreamを介してWAVファイルを読み取ります。 Java Sound Tutorials の次のスニペットはうまく機能します。

_int totalFramesRead = 0;
File fileIn = new File(somePathName);
// somePathName is a pre-existing string whose value was
// based on a user selection.
try {
  AudioInputStream audioInputStream = 
    AudioSystem.getAudioInputStream(fileIn);
  int bytesPerFrame = 
    audioInputStream.getFormat().getFrameSize();
    if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) {
    // some audio formats may have unspecified frame size
    // in that case we may read any amount of bytes
    bytesPerFrame = 1;
  } 
  // Set an arbitrary buffer size of 1024 frames.
  int numBytes = 1024 * bytesPerFrame; 
  byte[] audioBytes = new byte[numBytes];
  try {
    int numBytesRead = 0;
    int numFramesRead = 0;
    // Try to read numBytes bytes from the file.
    while ((numBytesRead = 
      audioInputStream.read(audioBytes)) != -1) {
      // Calculate the number of frames actually read.
      numFramesRead = numBytesRead / bytesPerFrame;
      totalFramesRead += numFramesRead;
      // Here, do something useful with the audio data that's 
      // now in the audioBytes array...
    }
  } catch (Exception ex) { 
    // Handle the error...
  }
} catch (Exception e) {
  // Handle the error...
}
_

WAVを作成するには、非常に注意が必要です。表面的には、循環問題のように見えます。書き込むコマンドは、パラメータとしてAudioInputStreamに依存しています。

しかし、どのようにAudioInputStreamにバイトを書き込むのですか? AudioOutputStreamがあるべきではありませんか?

私が見つけたのは、生のオーディオバイトデータにアクセスできるオブジェクトを定義して、TargetDataLineを実装できることです。

これには多くのメソッドを実装する必要がありますが、ほとんどのメソッドはデータをファイルに書き込むために必要ないため、ダミー形式のままにすることができます。実装する主要なメソッドはread(byte[] buffer, int bufferoffset, int numberofbytestoread)です。

このメソッドはおそらく複数回呼び出されるため、データの進行状況を示すインスタンス変数も必要であり、上記のreadメソッドの一部として更新します。

このメソッドを実装したら、オブジェクトを使用して新しいAudioInputStreamを作成できます。

_AudioSystem.write(yourAudioInputStream, AudioFileFormat.WAV, yourFileDestination)
_

念のため、AudioInputStreamをソースとして使用してTargetDataLineを作成できます。

データを直接操作することに関して、上記のスニペットの例の最も内側のループaudioBytesのバッファー内のデータに作用することに成功しました。

その内部ループにいる間に、バイトを整数または浮動小数点数に変換し、volume値(_0.0_から_1.0_の範囲)を乗算してから、リトルエンディアンバイトに変換し直すことができます。

そのバッファ内の一連のサンプルにアクセスできるため、その段階でさまざまな形式のDSPフィルタリングアルゴリズムを使用することもできます。私の経験では、このバッファー内のデータでボリュームを直接変更する方がよいことがわかりました。サンプルごとに1つのデルタを追加して、ボリュームに起因する不連続によるクリックの可能性を最小限に抑えることができるためです。

Javaによって提供されるボリュームの「制御線」を見つけます。ボリュームのジャンプがクリックを引き起こす状況になりがちです。これは、デルタが単一の粒度でのみ実装されているためだと思います変更を小さな断片に分割してサンプルごとに1つずつ追加するのではなく、バッファー読み取り(多くの場合、1024サンプルごとに1つの変更の範囲内)ですが、ボリュームコントロールの実装方法についてはよくわかりません。塩の粒。

結局のところ、Java.Soundは把握するのが本当に頭痛の種でした。バイトから直接ファイルを書き込む明示的な例を含めていないため、チュートリアルに誤りがあります。 「変換方法...」セクションの「Play a File」コーディングの最良の例を埋めるためのチュートリアルに誤りがあります。ただし、そのチュートリアルには貴重な無料情報がたくさんあります。


編集:12/13/17

それ以来、次のコードを使用して、自分のプロジェクトでPCMファイルからオーディオを書き込みました。 TargetDataLineを実装する代わりに、InputStreamを拡張し、_AudioInputStream.write_メソッドへのパラメーターとして使用できます。

_public class StereoPcmInputStream extends InputStream
{
    private float[] dataFrames;
    private int framesCounter;
    private int cursor;
    private int[] pcmOut = new int[2];
    private int[] frameBytes = new int[4];
    private int idx;

    private int framesToRead;

    public void setDataFrames(float[] dataFrames)
    {
        this.dataFrames = dataFrames;
        framesToRead = dataFrames.length / 2;
    }

    @Override
    public int read() throws IOException
    {
        while(available() > 0)
        {
            idx &= 3; 
            if (idx == 0) // set up next frame's worth of data
            {
                framesCounter++; // count elapsing frames

                // scale to 16 bits
                pcmOut[0] = (int)(dataFrames[cursor++] * Short.MAX_VALUE);
                pcmOut[1] = (int)(dataFrames[cursor++] * Short.MAX_VALUE);

                // output as unsigned bytes, in range [0..255]
                frameBytes[0] = (char)pcmOut[0];
                frameBytes[1] = (char)(pcmOut[0] >> 8);
                frameBytes[2] = (char)pcmOut[1];
                frameBytes[3] = (char)(pcmOut[1] >> 8);

            }
            return frameBytes[idx++]; 
        }
        return -1;
    }

    @Override 
    public int available()
    {
        // NOTE: not concurrency safe.
        // 1st half of sum: there are 4 reads available per frame to be read
        // 2nd half of sum: the # of bytes of the current frame that remain to be read
        return 4 * ((framesToRead - 1) - framesCounter) 
                + (4 - (idx % 4));
    }    

    @Override
    public void reset()
    {
        cursor = 0;
        framesCounter = 0;
        idx = 0;
    }

    @Override
    public void close()
    {
        System.out.println(
            "StereoPcmInputStream stopped after reading frames:" 
                + framesCounter);
    }
}
_

ここでエクスポートされるソースデータは、-1〜1の範囲のステレオフロートの形式です。結果のストリームの形式は、16ビット、ステレオ、リトルエンディアンです。

特定のアプリケーションではskipおよびmarkSupportedメソッドを省略しました。ただし、必要に応じて追加することは難しくありません。

10
Phil Freihofner

あなたが達成したいことについてのいくつかの詳細が役立つでしょう。生のWAVデータで問題ない場合は、FileInputStreamとおそらくScannerを使用して、それを数値に変換します。しかし、始めるためのいくつかの意味のあるサンプルコードを提供してみましょう。

この目的のためにcom.Sun.media.sound.WaveFileWriterと呼ばれるクラスがあります。

InputStream in = ...;
OutputStream out = ...;

AudioInputStream in = AudioSystem.getAudioInputStream(in);

WaveFileWriter writer = new WaveFileWriter();
writer.write(in, AudioFileFormat.Type.WAVE, outStream);

数列を音声データに変換するブードゥーを行う独自のAudioInputStreamを実装できます。

writer.write(new VoodooAudioInputStream(numbers), AudioFileFormat.Type.WAVE, outStream);

@ stacker で述べたように、もちろんAPIに慣れる必要があります。

8
sfussenegger

Javax.sound.sampleパッケージは、実際のサンプル値にアクセスする必要がある場合、WAVファイルの処理には適していません。このパッケージを使用すると、ボリューム、サンプルレートなどを変更できますが、他の効果(エコーの追加など)が必要な場合は、自分で作成できます。 (Javaチュートリアルでは、サンプル値を直接処理することが可能であるべきであると示唆されていますが、技術ライターは過度に約束しました。)

このサイトには、WAVファイルを処理するための単純なクラスがあります。 http://www.labbookpages.co.uk/audio/javaWavFiles.html

6
cayhorstmann

WAVファイル仕様 https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

あなたの目的のためのAPIがあります http://code.google.com/p/musicg/

5
c'quet

これは、wavファイルに直接書き込むためのソースコードです。数学とサウンドエンジニアリングを知っていれば、必要なサウンドを生成できます。この例では、方程式はバイノーラルビートを計算します。

import Java.io.ByteArrayInputStream;
import Java.io.File;
import Java.io.IOException;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

public class Example 
{
    public static void main(String[] args) throws  IOException {

    double sampleRate = 44100.0;
    double frequency = 440;
    double frequency2 = 90;
    double amplitude = 1.0;
    double seconds = 2.0;
    double twoPiF = 2 * Math.PI * frequency;
    double piF = Math.PI * frequency2;
    float[] buffer = new float[(int) (seconds * sampleRate)];
    for (int sample = 0; sample < buffer.length; sample++) 
    {
        double time = sample / sampleRate;
        buffer[sample] = (float) (amplitude * Math.cos((double)piF *time)* Math.sin(twoPiF * time));
    }
    final byte[] byteBuffer = new byte[buffer.length * 2];
    int bufferIndex = 0;
    for (int i = 0; i < byteBuffer.length; i++) {
    final int x = (int) (buffer[bufferIndex++] * 32767.0);
    byteBuffer[i] = (byte) x;
    i++;
    byteBuffer[i] = (byte) (x >>> 8);
    }
    File out = new File("out10.wav");
    boolean bigEndian = false;
    boolean signed = true;
    int bits = 16;
    int channels = 1;
    AudioFormat format;
    format = new AudioFormat((float)sampleRate, bits, channels, signed, bigEndian);
    ByteArrayInputStream bais = new ByteArrayInputStream(byteBuffer);
    AudioInputStream audioInputStream;
    audioInputStream = new AudioInputStream(bais, format,buffer.length);
    AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, out);
    audioInputStream.close();
    }

}

これを修正して、クールなヒップホップサブベースを作成できたら、それが現在このプログラムを修正しようとしているからです。

4
Joshua Beckford

Waveファイルは javax.sound.sampleパッケージ でサポートされています

些細なAPIではないので、次のようなAPIを紹介する記事/チュートリアルを読む必要があります

Javaサウンド、紹介

2
stacker

まず第一に、WAVE構造体のヘッダーとデータ位置を知る必要があるかもしれません。スペック here を見つけることができます。データはリトルエンディアンであることに注意してください。

[〜#〜] api [〜#〜] があり、これは目標を達成するのに役立ちます。

2
user1039358