簡體   English   中英

android中的PCM到WAV轉換

[英]PCM to WAV conversion in android

我已經使用 AudioRecord 類錄制了聲音。 但它不能通過音樂播放器播放。 所以我在音頻文件中添加了一些標題信息。 雖然它現在可以通過音樂播放器播放,但它的滯后和嘈雜,它不能播放准確的聲音。 任何幫助,將不勝感激 。 我的代碼在下面

private void rawToWave(final File rawFile, final File waveFile) throws IOException {

    byte[] rawData = new byte[(int) rawFile.length()];
    DataInputStream input = null;
    try {
        input = new DataInputStream(new FileInputStream(rawFile));
        input.read(rawData);
    } finally {
        if (input != null) {
            input.close();
        }
    }

    DataOutputStream output = null;
    try {
        int myBitsPerSample= 2;
        int myFormat = 1;
        long myChannels = 1;
        long mySampleRate = 8000 ;
        long myByteRate = mySampleRate * myChannels * myBitsPerSample/8;
        int myBlockAlign = (int) (myChannels * myBitsPerSample/8);

        output = new DataOutputStream(new FileOutputStream(waveFile));
        // WAVE header
        // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
        writeString(output, "RIFF"); // chunk id
        writeInt(output, 36 + rawData.length); // chunk size
        writeString(output, "WAVE"); // format
        writeString(output, "fmt "); // subchunk 1 id
        writeInt(output, 16); // subchunk 1 size
        writeShort(output, (short) 1); // audio format (1 = PCM)
        writeShort(output, (short) 1); // number of channels
        writeInt(output, (int)mySampleRate ); // sample ratemySampleRate
        writeInt(output, (int) (myByteRate)); // byte rate
        writeShort(output, (short) myBlockAlign); // block align
        writeShort(output, (short) 16); // bits per sample
        writeString(output, "data"); // subchunk 2 id
        writeInt(output, rawData.length); // subchunk 2 size
        // Audio data (conversion big endian -> little endian)
        short[] shorts = new short[rawData.length /2];
        ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
        ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2);



        for (short s : shorts) {
           bytes.putShort(s);
        }



        output.write(bytes.array());
    } finally {
        if (output != null) {
            output.close();
        }
    }
}

public  void writeShortLE(DataOutputStream out, short value) throws IOException {
    out.writeByte(value & 0xFF);
    out.writeByte((value >> 8) & 0xFF);
}



private void writeInt(final DataOutputStream output, final int value) throws IOException {
    output.write(value >> 0);
    output.write(value >> 8);
    output.write(value >> 16);
    output.write(value >> 24);
}

private void writeShort(final DataOutputStream output, final short value) throws IOException {
    output.write(value >> 0);
    output.write(value >> 8);
}

private void writeString(final DataOutputStream output, final String value) throws IOException {
    for (int i = 0; i < value.length(); i++) {
        output.write(value.charAt(i));
    }
}

這是一個完整的工作示例:

/**
 * @param input         raw PCM data
 *                      limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
 * @param output        file to encode to in wav format
 * @param channelCount  number of channels: 1 for mono, 2 for stereo, etc.
 * @param sampleRate    sample rate of PCM audio
 * @param bitsPerSample bits per sample, i.e. 16 for PCM16
 * @throws IOException in event of an error between input/output files
 * @see <a href="http://soundfile.sapp.org/doc/WaveFormat/">soundfile.sapp.org/doc/WaveFormat</a>
 */
static public void PCMToWAV(File input, File output, int channelCount, int sampleRate, int bitsPerSample) throws IOException {
    final int inputSize = (int) input.length();

    try (OutputStream encoded = new FileOutputStream(output)) {
        // WAVE RIFF header
        writeToOutput(encoded, "RIFF"); // chunk id
        writeToOutput(encoded, 36 + inputSize); // chunk size
        writeToOutput(encoded, "WAVE"); // format

        // SUB CHUNK 1 (FORMAT)
        writeToOutput(encoded, "fmt "); // subchunk 1 id
        writeToOutput(encoded, 16); // subchunk 1 size
        writeToOutput(encoded, (short) 1); // audio format (1 = PCM)
        writeToOutput(encoded, (short) channelCount); // number of channelCount
        writeToOutput(encoded, sampleRate); // sample rate
        writeToOutput(encoded, sampleRate * channelCount * bitsPerSample / 8); // byte rate
        writeToOutput(encoded, (short) (channelCount * bitsPerSample / 8)); // block align
        writeToOutput(encoded, (short) bitsPerSample); // bits per sample

        // SUB CHUNK 2 (AUDIO DATA)
        writeToOutput(encoded, "data"); // subchunk 2 id
        writeToOutput(encoded, inputSize); // subchunk 2 size
        copy(new FileInputStream(input), encoded);
    }
}


/**
 * Size of buffer used for transfer, by default
 */
private static final int TRANSFER_BUFFER_SIZE = 10 * 1024;

/**
 * Writes string in big endian form to an output stream
 *
 * @param output stream
 * @param data   string
 * @throws IOException
 */
public static void writeToOutput(OutputStream output, String data) throws IOException {
    for (int i = 0; i < data.length(); i++)
        output.write(data.charAt(i));
}

public static void writeToOutput(OutputStream output, int data) throws IOException {
    output.write(data >> 0);
    output.write(data >> 8);
    output.write(data >> 16);
    output.write(data >> 24);
}

public static void writeToOutput(OutputStream output, short data) throws IOException {
    output.write(data >> 0);
    output.write(data >> 8);
}

public static long copy(InputStream source, OutputStream output)
        throws IOException {
    return copy(source, output, TRANSFER_BUFFER_SIZE);
}

public static long copy(InputStream source, OutputStream output, int bufferSize) throws IOException {
    long read = 0L;
    byte[] buffer = new byte[bufferSize];
    for (int n; (n = source.read(buffer)) != -1; read += n) {
        output.write(buffer, 0, n);
    }
    return read;
}

這是對 Kotlin 中 Tobiq 答案的修改,允許您從 8000Hz 上采樣到 48000Hz。

用法

PCMToWAV(文件(音頻文件)、文件(上采樣音頻文件)、1、8000、48000、16)

PCMToWAV

/**
 * @param input         raw PCM data
 * limit of file size for wave file: < 2^(2*4) - 36 bytes (~4GB)
 * @param output        file to encode to in wav format
 * @param channelCount  number of channels: 1 for mono, 2 for stereo, etc.
 * @param sampleRate    sample rate of PCM audio
 * @param bitsPerSample bits per sample, i.e. 16 for PCM16
 * @throws IOException in event of an error between input/output files
 * @see [soundfile.sapp.org/doc/WaveFormat](http://soundfile.sapp.org/doc/WaveFormat/)
 */
@Throws(IOException::class)
fun PCMToWAV(input: File, output: File?, channelCount: Int, originSampleRate: Int, sampleRate: Int, bitsPerSample: Int) {

    val inputSize = input.length().toInt()
    val sampleFactor : Float = sampleRate.toFloat() / originSampleRate.toFloat()

    FileOutputStream(output).use { encoded ->

        // WAVE RIFF header

        writeToOutput(encoded, "RIFF") // chunk id
        writeToOutput(encoded, 36 + (inputSize.toFloat() * sampleFactor).toInt()) // chunk size
        writeToOutput(encoded, "WAVE") // format

        // SUB CHUNK 1 (FORMAT)

        writeToOutput(encoded, "fmt ") // subchunk 1 id
        writeToOutput(encoded, 16) // subchunk 1 size
        writeToOutput(encoded, 1.toShort()) // audio format (1 = PCM)
        writeToOutput(encoded, channelCount.toShort()) // number of channelCount
        writeToOutput(encoded, sampleRate) // sample rate
        writeToOutput(encoded, sampleRate * channelCount * bitsPerSample / 8) // byte rate
        writeToOutput(encoded, (channelCount * bitsPerSample / 8).toShort()) // block align
        writeToOutput(encoded, bitsPerSample.toShort()) // bits per sample

        // SUB CHUNK 2 (AUDIO DATA)

        if (originSampleRate == sampleRate) {

            writeToOutput(encoded, "data") // subchunk 2 id
            writeToOutput(encoded, inputSize) // subchunk 2 size
            copy(FileInputStream(input), encoded)

        } else {

            // SUB CHUNK 2 (AUDIO DATA) - UPSAMPLE

            writeToOutput(encoded, "data") // subchunk 2 id
            writeToOutput(encoded, (inputSize.toFloat() * sampleFactor).toInt()) // subchunk 2 size

            val inputStream = FileInputStream(input)
            val buffer = ByteArray(2)
            var len: Int = inputStream.read(buffer)

            var overFlow = 0

            while (len != -1) {

                for (i in 0 until (sampleFactor * 1000F).toInt()) {

                    overFlow += 1

                    if (overFlow >= 1000) {

                        encoded.write(buffer, 0, len)
                        overFlow = 0

                    }

                }

                len = inputStream.read(buffer)

            }
            
            inputStream.close()

        }

    }

}


/**
 * Size of buffer used for transfer, by default
 */
private const val TRANSFER_BUFFER_SIZE = 10 * 1024

/**
 * Writes string in big endian form to an output stream
 *
 * @param output stream
 * @param data   string
 * @throws IOException
 */

@Throws(IOException::class)
fun writeToOutput(output: OutputStream, data: String) {

    for (i in 0 until data.length) {
        output.write(data[i].toInt())
    }

}

@Throws(IOException::class)
fun writeToOutput(output: OutputStream, data: Int) {
    output.write(data shr 0)
    output.write(data shr 8)
    output.write(data shr 16)
    output.write(data shr 24)
}

@Throws(IOException::class)
fun writeToOutput(output: OutputStream, data: Short) {
    output.write(data.toInt() shr 0)
    output.write(data.toInt() shr 8)
}

@Throws(IOException::class)
fun copy(source: InputStream, output: OutputStream): Long {
    return copy(source, output, TRANSFER_BUFFER_SIZE)
}

@Throws(IOException::class)
fun copy(source: InputStream, output: OutputStream, bufferSize: Int): Long {
    var read = 0L
    val buffer = ByteArray(bufferSize)
    var n: Int
    while (source.read(buffer).also { n = it } != -1) {
        output.write(buffer, 0, n)
        read += n.toLong()
    }
    return read
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM