Java Sound のオーディオサンプルデータを使用するにはどうすればいいですか? 質問する

Java Sound のオーディオサンプルデータを使用するにはどうすればいいですか? 質問する

この質問は通常、別の質問の一部として尋ねられますが、回答が長くなることが判明しました。他の場所にリンクできるように、ここで回答することにしました。

現時点ではJavaでオーディオサンプルを生成できる方法を知りませんが、将来的にそれが変更されれば、これがその場所になる可能性があります。JavaFX例えば、このようなものがありますAudioSpectrumListenerただし、サンプルに直接アクセスする方法はまだありません。


javax.sound.sampled再生や録音に使用していますが、オーディオで何かを行いたいと思っています。

おそらく、それを視覚的に表示したり、何らかの方法で処理したりしたいと思うでしょう。

Java Sound でこれを行うには、オーディオ サンプル データにアクセスするにはどうすればよいですか?

参照:

ベストアンサー1

まあ、最も簡単な答えは、現時点では Java はプログラマー向けのサンプル データを生成できないということです。

この引用は公式チュートリアルからのものです:

信号処理を適用するには 2 つの方法があります。

  • オブジェクトを照会し、ユーザーの希望に応じてコントロールを設定することで、ミキサーまたはそのコンポーネント ラインでサポートされている任意の処理を使用できますControl。ミキサーとラインでサポートされている一般的なコントロールには、ゲイン、パン、およびリバーブ コントロールがあります。

  • 必要な種類の処理がミキサーまたはそのラインによって提供されていない場合は、プログラムでオーディオ バイトを直接操作し、必要に応じて操作することができます。

このページでは、最初の手法についてより詳しく説明します。2番目の手法には特別なAPIはない

再生はjavax.sound.sampled主にファイルとオーディオ デバイス間のブリッジとして機能します。バイトはファイルから読み込まれ、送信されます。

バイトが意味のあるオーディオサンプルであると想定しないでください。8ビットAIFFファイルでない限り、意味のあるものではありません。(一方、サンプルが間違いなく8ビット符号付き、できるこれらを使って算術演算を行います。単に遊んでいるだけなら、8 ビットを使用することが、ここで説明した複雑さを回避する 1 つの方法です。

代わりに、私はAudioFormat.Encodingそして、自分で解読する方法を説明してください。この回答はないエンコード方法については説明しませんが、下部の完全なコード例に含まれています。エンコードは、ほとんどの場合、デコード処理を逆に実行するだけです。

長い回答ですが、徹底した概要を説明したいと思います。


デジタルオーディオについて

一般的にデジタルオーディオについて説明するときは、線形パルスコード変調(LPCM)。

連続した音波が一定の間隔でサンプリングされ、振幅が何らかのスケールの整数に量子化されます。

ここに示すのは、4 ビットにサンプリングされ量子化された正弦波です。

lpcm_グラフ

(最も正の値である2の補数表現は、最も負の値より 1 小さくなります。これは、注意すべき小さな詳細です。たとえば、オーディオをクリッピングするときにこれを忘れると、正のクリップがオーバーフローします。

コンピューターにオーディオがある場合、これらのサンプルの配列が存在します。サンプル配列は、配列byteを変換するものです。

PCM サンプルをデコードする場合、サンプル レートやチャンネル数はあまり重要ではないため、ここではそれらについてあまり説明しません。チャンネルは通常インターリーブされるため、チャンネルの配列がある場合は次のように保存されます。

Index 0: Sample 0 (Left Channel)
Index 1: Sample 0 (Right Channel)
Index 2: Sample 1 (Left Channel)
Index 3: Sample 1 (Right Channel)
Index 4: Sample 2 (Left Channel)
Index 5: Sample 2 (Right Channel)
...

つまり、ステレオの場合、配列内のサンプルは左と右で交互に表示されます。


いくつかの仮定

すべてのコード例では、次の宣言を前提としています。

  • byte[] bytes;byteから読み取られた配列AudioInputStream
  • float[] samples;埋める出力サンプル配列。
  • float sample;現在作業中のサンプルです。
  • long temp;一般的な操作に使用される中間値。
  • int i;byte現在のサンプルのデータが始まる配列内の位置。

float[]配列内のすべてのサンプルを の範囲に正規化します-1f <= sample <= 1f。私が見た浮動小数点オーディオはすべてこの方法で提供されており、非常に便利です。

ソース オーディオがまだそのようになっていない場合 (たとえば整数サンプルの場合)、次のようにして自分で正規化できます。

sample = sample / fullScale(bitsPerSample);

ここでfullScale、 2 bitsPerSample - 1、すなわちですMath.pow(2, bitsPerSample-1)


byte配列を意味のあるデータに強制変換するにはどうすればよいですか?

配列byteにはサンプルフレームが分割されて一列に並んでいます。これは実際には非常に単純ですが、エンディアンbyteこれは各サンプル パケット内の の順序です。

ここに図があります。このサンプル (配列にパックされていますbyte) は、10 進数値 9999 を保持します。

  ビッグエンディアンとしての24ビットサンプル:

 バイト[i] バイト[i + 1] バイト[i + 2]
 ┌──────┐ ┌──────┐ ┌──────┐
 00000000 00100111 00001111

 リトルエンディアンとしての24ビットサンプル:

 バイト[i] バイト[i + 1] バイト[i + 2]
 ┌──────┐ ┌──────┐ ┌──────┐
 00001111 00100111 00000000

これらは同じバイナリ値を保持しますが、byte順序は逆になります。

  • ビッグエンディアンでは、より重要なbytes がより重要でないbytes の前に来ます。
  • リトルエンディアンでは、下位のbytes が上位の s の前に来ますbytes

WAVAファイルファイルはリトルエンディアン順で保存され、AIFF ファイルビッグエンディアン順で保存されます。エンディアンは以下から取得できます。AudioFormat.isBigEndian

を連結しbyteて変数に入れるにはlong temp、次のようにします。

  1. byteそれぞれをマスク0xFF( )とビット単位でAND演算して0b1111_1111回避する符号拡張byte自動的に昇格されます。 (charbyteおよび は、それらに対して演算が実行されるとshortに昇格されます。) も参照してください。intvalue & 0xffJava では何を行いますか?
  2. 各ビットbyteを位置までシフトします。
  3. s をビット単位で OR しますbyte

24 ビットの例を次に示します。

long temp;
if (isBigEndian) {
    temp = (
          ((bytes[i    ] & 0xffL) << 16)
        | ((bytes[i + 1] & 0xffL) <<  8)
        |  (bytes[i + 2] & 0xffL)
    );
} else {
    temp = (
           (bytes[i    ] & 0xffL)
        | ((bytes[i + 1] & 0xffL) <<  8)
        | ((bytes[i + 2] & 0xffL) << 16)
    );
}

エンディアンに基づいてシフト順序が逆になることに注意してください。

これはループに一般化することもできます。これは、この回答の下部にある完全なコードで確認できます。(メソッドunpackAnyBitpackAnyBitメソッドを参照してください。)

連結が完了したのでbyte、さらにいくつかの手順を実行してサンプルに変換できます。次の手順は、実際のエンコードによって異なります。

どうすればデコードできますかEncoding.PCM_SIGNED

2 の補数の符号は拡張する必要があります。つまり、最上位ビット (MSB) が 1 に設定されている場合、それより上のすべてのビットを 1 で埋めます。算術右シフト ( >>) は、符号ビットが設定されている場合、自動的に埋めるので、通常は次のようにします。

int bitsToExtend = Long.SIZE - bitsPerSample;
float sample = (temp << bitsToExtend) >> bitsToExtend.

(Long.SIZEは 64 です。temp変数が でない場合はlong、別のものを使用します。int temp代わりに eg を使用する場合は、32 を使用します。)

これがどのように機能するかを理解するために、8 ビットを 16 ビットに符号拡張する図を示します。

11111111 はバイト値 -1 ですが、short の上位ビットは 0 です。
 バイトの MSB を short の MSB 位置にシフトします。

 0000 0000 1111 1111
 << 8
 ────────────────────
 1111 1111 0000 0000

 それを後ろにシフトし、右シフトによって上位ビットがすべて 1 で埋められます。
 これでショート値は -1 になりました。

 1111 1111 0000 0000
 >> 8
 ────────────────────
 1111 1111 1111 1111

正の値 (MSB が 0 である値) は変更されません。これは算術右シフトの優れた特性です。

次に、サンプルを正規化する。いくつかの仮定

コードが単純な場合は、明示的に符号拡張を記述する必要はないかもしれない。

byteJavaは、ある整数型からより大きな型(例えば )に変換するときに自動的に符号拡張を行いますint知る入力形式と出力形式が常に符号付きである場合は、前の手順でバイトを連結するときに自動符号拡張を使用できます。

上のセクションを思い出してください(バイト配列を意味のあるデータに強制変換するにはどうすればよいですか?) を使用して、符号拡張が発生しないようにしました。 を最も高い からb & 0xFF削除するだけで、符号拡張が自動的に行われます。& 0xFFbyte

たとえば、次のコードは、符号付き、ビッグ エンディアンの 16 ビット サンプルをデコードします。

for (int i = 0; i < bytes.length; i++) {
    int sample = (bytes[i] << 8) // high byte is sign-extended
               | (bytes[i + 1] & 0xFF); // low byte is not
    // ...
}

どうすればデコードできますかEncoding.PCM_UNSIGNED

これを符号付き数値に変換します。符号なしサンプルは単純にオフセットされるため、たとえば次のようになります。

  • 符号なし値 0 は、最も負の符号付き値に対応します。
  • 符号なし値 2 bitsPerSample - 1は、符号付き値 0 に対応します。
  • 2 bitsPerSampleの符号なし値は、最も正の符号付き値に対応します。

これは非常に簡単です。オフセットを減算するだけです。

float sample = temp - fullScale(bitsPerSample);

次に、サンプルを正規化する。いくつかの仮定

どうすればデコードできますかEncoding.PCM_FLOAT

これは Java 7 以降の新機能です。

実際には、浮動小数点PCMはIEEE 32ビットまたはIEEE 64ビットのいずれかであり、すでに範囲に正規化されています±1.0。サンプルはユーティリティメソッドを使用して取得できます。Float#intBitsToFloatそしてDouble#longBitsToDouble

// IEEE 32-bit
float sample = Float.intBitsToFloat((int) temp);
// IEEE 64-bit
double sampleAsDouble = Double.longBitsToDouble(temp);
float sample = (float) sampleAsDouble; // or just use double for arithmetic

Encoding.ULAWと をデコードするにはどうすればいいですかEncoding.ALAW?

これらは圧縮電話などでよく使われる圧縮コーデックですjavax.sound.sampledSunのAu形式(ただし、このタイプのコンテナだけに限定されるわけではありません。たとえば、WAV にはこれらのエンコーディングを含めることができます。)

概念化できる法律そしてμ法則浮動小数点形式のようです。これらは PCM 形式ですが、値の範囲は非線形です。

解読には2つの方法があります。ここでは数式を使った方法を紹介します。バイナリを直接操作して解読することもできます。このブログ記事で説明されているしかし、見た目はより難解です。

どちらも、圧縮データは 8 ビットです。標準的には、A-law はデコード時に 13 ビット、μ-law はデコード時に 14 ビットですが、式を適用すると範囲が得られます±1.0

数式を適用する前に、次の 3 つのことを行う必要があります。

  1. データの整合性に関係する理由により、一部のビットは標準的に反転されて保存されます。
  2. これらは、2 の補数ではなく、符号と大きさとして保存されます。
  3. 数式では の範囲も想定されている±1.0ため、8 ビットの値をスケーリングする必要があります。

μ-lawの場合すべてのビット反転すると次のようになります。

temp ^= 0xffL; // 0xff == 0b1111_1111

~( の上位ビットを反転したくないので は使用できないことに注意してくださいlong。)

A-lawの場合、1ビットおきに反転すると次のようになります。

temp ^= 0x55L; // 0x55 == 0b0101_0101

(XORは反転に使用できます。ビットを設定、クリア、切り替えるにはどうすればいいですか?

符号と大きさを 2 の補数に変換するには、次のようにします。

  1. 符号ビットが設定されているかどうかを確認します。
  2. そうであれば、符号ビットをクリアし、数値を反転します。
// 0x80 == 0b1000_0000
if ((temp & 0x80L) != 0) {
    temp ^= 0x80L;
    temp = -temp;
}

次に、エンコードされた数値を、いくつかの仮定:

sample = temp / fullScale(8);

これで拡張を適用できます。

Java に翻訳された μ 法則の式は次のようになります。

sample = (float) (
    signum(sample)
        *
    (1.0 / 255.0)
        *
    (pow(256.0, abs(sample)) - 1.0)
);

A-law 式を Java に翻訳すると次のようになります。

float signum = signum(sample);
sample = abs(sample);

if (sample < (1.0 / (1.0 + log(87.7)))) {
    sample = (float) (
        sample * ((1.0 + log(87.7)) / 87.7)
    );
} else {
    sample = (float) (
        exp((sample * (1.0 + log(87.7))) - 1.0) / 87.7
    );
}

sample = signum * sample;

以下にクラスの完全なサンプルコードを示しますSimpleAudioConversion

package mcve.audio;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;

import static java.lang.Math.*;

/**
 * <p>Performs simple audio format conversion.</p>
 *
 * <p>Example usage:</p>
 *
 * <pre>{@code  AudioInputStream ais = ... ;
 * SourceDataLine  line = ... ;
 * AudioFormat      fmt = ... ;
 *
 * // do setup
 *
 * for (int blen = 0; (blen = ais.read(bytes)) > -1;) {
 *     int slen;
 *     slen = SimpleAudioConversion.decode(bytes, samples, blen, fmt);
 *
 *     // do something with samples
 *
 *     blen = SimpleAudioConversion.encode(samples, bytes, slen, fmt);
 *     line.write(bytes, 0, blen);
 * }}</pre>
 *
 * @author Radiodef
 * @see <a href="http://stackoverflow.com/a/26824664/2891664">Overview on Stack Overflow</a>
 */
public final class SimpleAudioConversion {
    private SimpleAudioConversion() {}

    /**
     * Converts from a byte array to an audio sample float array.
     *
     * @param bytes   the byte array, filled by the AudioInputStream
     * @param samples an array to fill up with audio samples
     * @param blen    the return value of AudioInputStream.read
     * @param fmt     the source AudioFormat
     *
     * @return the number of valid audio samples converted
     *
     * @throws NullPointerException if bytes, samples or fmt is null
     * @throws ArrayIndexOutOfBoundsException
     *         if bytes.length is less than blen or
     *         if samples.length is less than blen / bytesPerSample(fmt.getSampleSizeInBits())
     */
    public static int decode(byte[]      bytes,
                             float[]     samples,
                             int         blen,
                             AudioFormat fmt) {
        int   bitsPerSample = fmt.getSampleSizeInBits();
        int  bytesPerSample = bytesPerSample(bitsPerSample);
        boolean isBigEndian = fmt.isBigEndian();
        Encoding   encoding = fmt.getEncoding();
        double    fullScale = fullScale(bitsPerSample);

        int i = 0;
        int s = 0;
        while (i < blen) {
            long temp = unpackBits(bytes, i, isBigEndian, bytesPerSample);
            float sample = 0f;

            if (encoding == Encoding.PCM_SIGNED) {
                temp = extendSign(temp, bitsPerSample);
                sample = (float) (temp / fullScale);

            } else if (encoding == Encoding.PCM_UNSIGNED) {
                temp = unsignedToSigned(temp, bitsPerSample);
                sample = (float) (temp / fullScale);

            } else if (encoding == Encoding.PCM_FLOAT) {
                if (bitsPerSample == 32) {
                    sample = Float.intBitsToFloat((int) temp);
                } else if (bitsPerSample == 64) {
                    sample = (float) Double.longBitsToDouble(temp);
                }
            } else if (encoding == Encoding.ULAW) {
                sample = bitsToMuLaw(temp);

            } else if (encoding == Encoding.ALAW) {
                sample = bitsToALaw(temp);
            }

            samples[s] = sample;

            i += bytesPerSample;
            s++;
        }

        return s;
    }

    /**
     * Converts from an audio sample float array to a byte array.
     *
     * @param samples an array of audio samples to encode
     * @param bytes   an array to fill up with bytes
     * @param slen    the return value of the decode method
     * @param fmt     the destination AudioFormat
     *
     * @return the number of valid bytes converted
     *
     * @throws NullPointerException if samples, bytes or fmt is null
     * @throws ArrayIndexOutOfBoundsException
     *         if samples.length is less than slen or
     *         if bytes.length is less than slen * bytesPerSample(fmt.getSampleSizeInBits())
     */
    public static int encode(float[]     samples,
                             byte[]      bytes,
                             int         slen,
                             AudioFormat fmt) {
        int   bitsPerSample = fmt.getSampleSizeInBits();
        int  bytesPerSample = bytesPerSample(bitsPerSample);
        boolean isBigEndian = fmt.isBigEndian();
        Encoding   encoding = fmt.getEncoding();
        double    fullScale = fullScale(bitsPerSample);

        int i = 0;
        int s = 0;
        while (s < slen) {
            float sample = samples[s];
            long temp = 0L;

            if (encoding == Encoding.PCM_SIGNED) {
                temp = (long) (sample * fullScale);

            } else if (encoding == Encoding.PCM_UNSIGNED) {
                temp = (long) (sample * fullScale);
                temp = signedToUnsigned(temp, bitsPerSample);

            } else if (encoding == Encoding.PCM_FLOAT) {
                if (bitsPerSample == 32) {
                    temp = Float.floatToRawIntBits(sample);
                } else if (bitsPerSample == 64) {
                    temp = Double.doubleToRawLongBits(sample);
                }
            } else if (encoding == Encoding.ULAW) {
                temp = muLawToBits(sample);

            } else if (encoding == Encoding.ALAW) {
                temp = aLawToBits(sample);
            }

            packBits(bytes, i, temp, isBigEndian, bytesPerSample);

            i += bytesPerSample;
            s++;
        }

        return i;
    }

    /**
     * Computes the block-aligned bytes per sample of the audio format,
     * using Math.ceil(bitsPerSample / 8.0).
     * <p>
     * Round towards the ceiling because formats that allow bit depths
     * in non-integral multiples of 8 typically pad up to the nearest
     * integral multiple of 8. So for example, a 31-bit AIFF file will
     * actually store 32-bit blocks.
     *
     * @param  bitsPerSample the return value of AudioFormat.getSampleSizeInBits
     * @return The block-aligned bytes per sample of the audio format.
     */
    public static int bytesPerSample(int bitsPerSample) {
        return (int) ceil(bitsPerSample / 8.0); // optimization: ((bitsPerSample + 7) >>> 3)
    }

    /**
     * Computes the largest magnitude representable by the audio format,
     * using Math.pow(2.0, bitsPerSample - 1). Note that for two's complement
     * audio, the largest positive value is one less than the return value of
     * this method.
     * <p>
     * The result is returned as a double because in the case that
     * bitsPerSample is 64, a long would overflow.
     *
     * @param bitsPerSample the return value of AudioFormat.getBitsPerSample
     * @return the largest magnitude representable by the audio format
     */
    public static double fullScale(int bitsPerSample) {
        return pow(2.0, bitsPerSample - 1); // optimization: (1L << (bitsPerSample - 1))
    }

    private static long unpackBits(byte[]  bytes,
                                   int     i,
                                   boolean isBigEndian,
                                   int     bytesPerSample) {
        switch (bytesPerSample) {
            case  1: return unpack8Bit(bytes, i);
            case  2: return unpack16Bit(bytes, i, isBigEndian);
            case  3: return unpack24Bit(bytes, i, isBigEndian);
            default: return unpackAnyBit(bytes, i, isBigEndian, bytesPerSample);
        }
    }

    private static long unpack8Bit(byte[] bytes, int i) {
        return bytes[i] & 0xffL;
    }

    private static long unpack16Bit(byte[]  bytes,
                                    int     i,
                                    boolean isBigEndian) {
        if (isBigEndian) {
            return (
                  ((bytes[i    ] & 0xffL) << 8)
                |  (bytes[i + 1] & 0xffL)
            );
        } else {
            return (
                   (bytes[i    ] & 0xffL)
                | ((bytes[i + 1] & 0xffL) << 8)
            );
        }
    }

    private static long unpack24Bit(byte[]  bytes,
                                    int     i,
                                    boolean isBigEndian) {
        if (isBigEndian) {
            return (
                  ((bytes[i    ] & 0xffL) << 16)
                | ((bytes[i + 1] & 0xffL) <<  8)
                |  (bytes[i + 2] & 0xffL)
            );
        } else {
            return (
                   (bytes[i    ] & 0xffL)
                | ((bytes[i + 1] & 0xffL) <<  8)
                | ((bytes[i + 2] & 0xffL) << 16)
            );
        }
    }

    private static long unpackAnyBit(byte[]  bytes,
                                     int     i,
                                     boolean isBigEndian,
                                     int     bytesPerSample) {
        long temp = 0;

        if (isBigEndian) {
            for (int b = 0; b < bytesPerSample; b++) {
                temp |= (bytes[i + b] & 0xffL) << (
                    8 * (bytesPerSample - b - 1)
                );
            }
        } else {
            for (int b = 0; b < bytesPerSample; b++) {
                temp |= (bytes[i + b] & 0xffL) << (8 * b);
            }
        }

        return temp;
    }

    private static void packBits(byte[]  bytes,
                                 int     i,
                                 long    temp,
                                 boolean isBigEndian,
                                 int     bytesPerSample) {
        switch (bytesPerSample) {
            case  1: pack8Bit(bytes, i, temp);
                     break;
            case  2: pack16Bit(bytes, i, temp, isBigEndian);
                     break;
            case  3: pack24Bit(bytes, i, temp, isBigEndian);
                     break;
            default: packAnyBit(bytes, i, temp, isBigEndian, bytesPerSample);
                     break;
        }
    }

    private static void pack8Bit(byte[] bytes, int i, long temp) {
        bytes[i] = (byte) (temp & 0xffL);
    }

    private static void pack16Bit(byte[]  bytes,
                                  int     i,
                                  long    temp,
                                  boolean isBigEndian) {
        if (isBigEndian) {
            bytes[i    ] = (byte) ((temp >>> 8) & 0xffL);
            bytes[i + 1] = (byte) ( temp        & 0xffL);
        } else {
            bytes[i    ] = (byte) ( temp        & 0xffL);
            bytes[i + 1] = (byte) ((temp >>> 8) & 0xffL);
        }
    }

    private static void pack24Bit(byte[]  bytes,
                                  int     i,
                                  long    temp,
                                  boolean isBigEndian) {
        if (isBigEndian) {
            bytes[i    ] = (byte) ((temp >>> 16) & 0xffL);
            bytes[i + 1] = (byte) ((temp >>>  8) & 0xffL);
            bytes[i + 2] = (byte) ( temp         & 0xffL);
        } else {
            bytes[i    ] = (byte) ( temp         & 0xffL);
            bytes[i + 1] = (byte) ((temp >>>  8) & 0xffL);
            bytes[i + 2] = (byte) ((temp >>> 16) & 0xffL);
        }
    }

    private static void packAnyBit(byte[]  bytes,
                                   int     i,
                                   long    temp,
                                   boolean isBigEndian,
                                   int     bytesPerSample) {
        if (isBigEndian) {
            for (int b = 0; b < bytesPerSample; b++) {
                bytes[i + b] = (byte) (
                    (temp >>> (8 * (bytesPerSample - b - 1))) & 0xffL
                );
            }
        } else {
            for (int b = 0; b < bytesPerSample; b++) {
                bytes[i + b] = (byte) ((temp >>> (8 * b)) & 0xffL);
            }
        }
    }

    private static long extendSign(long temp, int bitsPerSample) {
        int bitsToExtend = Long.SIZE - bitsPerSample;
        return (temp << bitsToExtend) >> bitsToExtend;
    }

    private static long unsignedToSigned(long temp, int bitsPerSample) {
        return temp - (long) fullScale(bitsPerSample);
    }

    private static long signedToUnsigned(long temp, int bitsPerSample) {
        return temp + (long) fullScale(bitsPerSample);
    }

    // mu-law constant
    private static final double MU = 255.0;
    // A-law constant
    private static final double A = 87.7;
    // natural logarithm of A
    private static final double LN_A = log(A);

    private static float bitsToMuLaw(long temp) {
        temp ^= 0xffL;
        if ((temp & 0x80L) != 0) {
            temp = -(temp ^ 0x80L);
        }

        float sample = (float) (temp / fullScale(8));

        return (float) (
            signum(sample)
                *
            (1.0 / MU)
                *
            (pow(1.0 + MU, abs(sample)) - 1.0)
        );
    }

    private static long muLawToBits(float sample) {
        double sign = signum(sample);
        sample = abs(sample);

        sample = (float) (
            sign * (log(1.0 + (MU * sample)) / log(1.0 + MU))
        );

        long temp = (long) (sample * fullScale(8));

        if (temp < 0) {
            temp = -temp ^ 0x80L;
        }

        return temp ^ 0xffL;
    }

    private static float bitsToALaw(long temp) {
        temp ^= 0x55L;
        if ((temp & 0x80L) != 0) {
            temp = -(temp ^ 0x80L);
        }

        float sample = (float) (temp / fullScale(8));

        float sign = signum(sample);
        sample = abs(sample);

        if (sample < (1.0 / (1.0 + LN_A))) {
            sample = (float) (sample * ((1.0 + LN_A) / A));
        } else {
            sample = (float) (exp((sample * (1.0 + LN_A)) - 1.0) / A);
        }

        return sign * sample;
    }

    private static long aLawToBits(float sample) {
        double sign = signum(sample);
        sample = abs(sample);

        if (sample < (1.0 / A)) {
            sample = (float) ((A * sample) / (1.0 + LN_A));
        } else {
            sample = (float) ((1.0 + log(A * sample)) / (1.0 + LN_A));
        }

        sample *= sign;

        long temp = (long) (sample * fullScale(8));

        if (temp < 0) {
            temp = -temp ^ 0x80L;
        }

        return temp ^ 0x55L;
    }
}

おすすめ記事