Java PCM波形文件-立体声到单声道

Java PCM波形文件-立体声到单声道,java,audio,pcm,vorbis,Java,Audio,Pcm,Vorbis,我有一个立体声音频文件。将其转换为mono是否只是每隔一个字节跳过一次(在头之后)?它以16位有符号PCM格式编码。我有javax.sound.sampled可用 以下是我尝试过但不起作用的代码: WaveFileWriter wfw = new WaveFileWriter(); AudioFormat format = new AudioFormat(Encoding.PCM_SIGNED, 44100, 16, 2, 2, 44100, false); AudioFormat monoFo

我有一个立体声音频文件。将其转换为mono是否只是每隔一个字节跳过一次(在头之后)?它以16位有符号PCM格式编码。我有
javax.sound.sampled
可用

以下是我尝试过但不起作用的代码:

WaveFileWriter wfw = new WaveFileWriter();
AudioFormat format = new AudioFormat(Encoding.PCM_SIGNED, 44100, 16, 2, 2, 44100, false);
AudioFormat monoFormat = new AudioFormat(Encoding.PCM_SIGNED, 44100, 16, 1, 2, 44100, false);

byte[] audioData = dataout.toByteArray();
int length = audioData.length;
ByteArrayInputStream bais = new ByteArrayInputStream(audioData);

AudioInputStream stereoStream = new AudioInputStream(bais,format,length);
AudioInputStream monoStream = new AudioInputStream(stereoStream,format,length/2);

wfw.write(monoStream, Type.WAVE, new File(Environment.
                 getExternalStorageDirectory().getAbsolutePath()+"/stegDroid/un-ogged.wav"));

在使用Jorbis将
.ogg
文件转换为PCM数据后,使用此代码。唯一的问题是结果是立体声,我需要它是单声道,所以如果有其他解决方案,我很高兴听到它

看看这段代码。当我需要处理wav文件中的字节时,它帮助了我


package GlobalUtilities;

import java.applet.Applet;
import java.applet.AudioClip;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import javax.sound.sampled.*;

/**
 * This class handles the reading, writing, and playing of wav files. It is
 * also capable of converting the file to its raw byte [] form.
 *
 * based on code by Evan Merz modified by Dan Vargo
 * @author dvargo
 */
public class Wav {
    /*
    WAV File Specification
    FROM http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
    The canonical WAVE format starts with the RIFF header:
    0         4   ChunkID          Contains the letters "RIFF" in ASCII form
    (0x52494646 big-endian form).
    4         4   ChunkSize        36 + SubChunk2Size, or more precisely:
    4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
    This is the size of the rest of the chunk
    following this number.  This is the size of the
    entire file in bytes minus 8 bytes for the
    two fields not included in this count:
    ChunkID and ChunkSize.
    8         4   Format           Contains the letters "WAVE"
    (0x57415645 big-endian form).

    The "WAVE" format consists of two subchunks: "fmt " and "data":
    The "fmt " subchunk describes the sound data's format:
    12        4   Subchunk1ID      Contains the letters "fmt "
    (0x666d7420 big-endian form).
    16        4   Subchunk1Size    16 for PCM.  This is the size of the
    rest of the Subchunk which follows this number.
    20        2   AudioFormat      PCM = 1 (i.e. Linear quantization)
    Values other than 1 indicate some
    form of compression.
    22        2   NumChannels      Mono = 1, Stereo = 2, etc.
    24        4   SampleRate       8000, 44100, etc.
    28        4   ByteRate         == SampleRate * NumChannels * BitsPerSample/8
    32        2   BlockAlign       == NumChannels * BitsPerSample/8
    The number of bytes for one sample including
    all channels. I wonder what happens when
    this number isn't an integer?
    34        2   BitsPerSample    8 bits = 8, 16 bits = 16, etc.

    The "data" subchunk contains the size of the data and the actual sound:
    36        4   Subchunk2ID      Contains the letters "data"
    (0x64617461 big-endian form).
    40        4   Subchunk2Size    == NumSamples * NumChannels * BitsPerSample/8
    This is the number of bytes in the data.
    You can also think of this as the size
    of the read of the subchunk following this
    number.
    44        *   Data             The actual sound data.


    The thing that makes reading wav files tricky is that java has no unsigned types.  This means that the
    binary data can't just be read and cast appropriately.  Also, we have to use larger types
    than are normally necessary.

    In many languages including java, an integer is represented by 4 bytes.  The issue here is
    that in most languages, integers can be signed or unsigned, and in wav files the  integers
    are unsigned.  So, to make sure that we can store the proper values, we have to use longs
    to hold integers, and integers to hold shorts.

    Then, we have to convert back when we want to save our wav data.

    It's complicated, but ultimately, it just results in a few extra functions at the bottom of
    this file.  Once you understand the issue, there is no reason to pay any more attention
    to it.

    ALSO:

    This code won't read ALL wav files.  This does not use to full specification.  It just uses
    a trimmed down version that most wav files adhere to.

     */

    ByteArrayOutputStream byteArrayOutputStream;
    AudioFormat audioFormat;
    TargetDataLine targetDataLine;
    AudioInputStream audioInputStream;
    SourceDataLine sourceDataLine;
    float frequency = 8000.0F;  //8000,11025,16000,22050,44100
    int samplesize = 16;
    private String myPath;
    private long myChunkSize;
    private long mySubChunk1Size;
    private int myFormat;
    private long myChannels;
    private long mySampleRate;
    private long myByteRate;
    private int myBlockAlign;
    private int myBitsPerSample;
    private long myDataSize;
    // I made this public so that you can toss whatever you want in here
    // maybe a recorded buffer, maybe just whatever you want
    public byte[] myData;



    public Wav()
    {
        myPath = "";
    }

    // constructor takes a wav path
    public Wav(String tmpPath) {
        myPath = tmpPath;
    }


    // get/set for the Path property
    public String getPath()
    {
        return myPath;
    }

    public void setPath(String newPath)
    {
        myPath = newPath;
    }

    // read a wav file into this class
    public boolean read() {
        DataInputStream inFile = null;
        myData = null;
        byte[] tmpLong = new byte[4];
        byte[] tmpInt = new byte[2];

        try {
            inFile = new DataInputStream(new FileInputStream(myPath));

            //System.out.println("Reading wav file...\n"); // for debugging only

            String chunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();

            inFile.read(tmpLong); // read the ChunkSize
            myChunkSize = byteArrayToLong(tmpLong);

            String format = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();

            // print what we've read so far
            //System.out.println("chunkID:" + chunkID + " chunk1Size:" + myChunkSize + " format:" + format); // for debugging only



            String subChunk1ID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();

            inFile.read(tmpLong); // read the SubChunk1Size
            mySubChunk1Size = byteArrayToLong(tmpLong);

            inFile.read(tmpInt); // read the audio format.  This should be 1 for PCM
            myFormat = byteArrayToInt(tmpInt);

            inFile.read(tmpInt); // read the # of channels (1 or 2)
            myChannels = byteArrayToInt(tmpInt);

            inFile.read(tmpLong); // read the samplerate
            mySampleRate = byteArrayToLong(tmpLong);

            inFile.read(tmpLong); // read the byterate
            myByteRate = byteArrayToLong(tmpLong);

            inFile.read(tmpInt); // read the blockalign
            myBlockAlign = byteArrayToInt(tmpInt);

            inFile.read(tmpInt); // read the bitspersample
            myBitsPerSample = byteArrayToInt(tmpInt);

            // print what we've read so far
            //System.out.println("SubChunk1ID:" + subChunk1ID + " SubChunk1Size:" + mySubChunk1Size + " AudioFormat:" + myFormat + " Channels:" + myChannels + " SampleRate:" + mySampleRate);


            // read the data chunk header - reading this IS necessary, because not all wav files will have the data chunk here - for now, we're just assuming that the data chunk is here
            String dataChunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();

            inFile.read(tmpLong); // read the size of the data
            myDataSize = byteArrayToLong(tmpLong);


            // read the data chunk
            myData = new byte[(int) myDataSize];
            inFile.read(myData);

            // close the input stream
            inFile.close();
        } catch (Exception e) {
            return false;
        }

        return true; // this should probably be something more descriptive
    }

    // write out the wav file
    public boolean save() {
        try {
            DataOutputStream outFile = new DataOutputStream(new FileOutputStream(myPath + "temp"));

            // write the wav file per the wav file format
            outFile.writeBytes("RIFF");                 // 00 - RIFF
            outFile.write(intToByteArray((int) myChunkSize), 0, 4);     // 04 - how big is the rest of this file?
            outFile.writeBytes("WAVE");                 // 08 - WAVE
            outFile.writeBytes("fmt ");                 // 12 - fmt
            outFile.write(intToByteArray((int) mySubChunk1Size), 0, 4); // 16 - size of this chunk
            outFile.write(shortToByteArray((short) myFormat), 0, 2);        // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation
            outFile.write(shortToByteArray((short) myChannels), 0, 2);  // 22 - mono or stereo? 1 or 2?  (or 5 or ???)
            outFile.write(intToByteArray((int) mySampleRate), 0, 4);        // 24 - samples per second (numbers per second)
            outFile.write(intToByteArray((int) myByteRate), 0, 4);      // 28 - bytes per second
            outFile.write(shortToByteArray((short) myBlockAlign), 0, 2);    // 32 - # of bytes in one sample, for all channels
            outFile.write(shortToByteArray((short) myBitsPerSample), 0, 2); // 34 - how many bits in a sample(number)?  usually 16 or 24
            outFile.writeBytes("data");                 // 36 - data
            outFile.write(intToByteArray((int) myDataSize), 0, 4);      // 40 - how big is this data chunk
            outFile.write(myData);                      // 44 - the actual data itself - just a long string of numbers
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return false;
        }

        return true;
    }

    // return a printable summary of the wav file
    public String getSummary() {
        //String newline = System.getProperty("line.separator");
        String newline = "
"; String summary = "Format: " + myFormat + newline + "Channels: " + myChannels + newline + "SampleRate: " + mySampleRate + newline + "ByteRate: " + myByteRate + newline + "BlockAlign: " + myBlockAlign + newline + "BitsPerSample: " + myBitsPerSample + newline + "DataSize: " + myDataSize + ""; return summary; } public byte[] getBytes() { read(); return myData; } /** * Plays back audio stored in the byte array using an audio format given by * freq, sample rate, ect. * @param data The byte array to play */ public void playAudio(byte[] data) { try { byte audioData[] = data; //Get an input stream on the byte array containing the data InputStream byteArrayInputStream = new ByteArrayInputStream(audioData); AudioFormat audioFormat = getAudioFormat(); audioInputStream = new AudioInputStream(byteArrayInputStream, audioFormat, audioData.length / audioFormat.getFrameSize()); DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat); sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo); sourceDataLine.open(audioFormat); sourceDataLine.start(); //Create a thread to play back the data and start it running. It will run \ //until all the data has been played back. Thread playThread = new Thread(new PlayThread()); playThread.start(); } catch (Exception e) { System.out.println(e); } } /** * This method creates and returns an AudioFormat object for a given set * of format parameters. If these parameters don't work well for * you, try some of the other allowable parameter values, which * are shown in comments following the declarations. * @return */ private AudioFormat getAudioFormat() { float sampleRate = frequency; //8000,11025,16000,22050,44100 int sampleSizeInBits = samplesize; //8,16 int channels = 1; //1,2 boolean signed = true; //true,false boolean bigEndian = false; //true,false //return new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 8000.0f, 8, 1, 1, //8000.0f, false ); return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian); } public void playWav(String filePath) { try { AudioClip clip = (AudioClip) Applet.newAudioClip(new File(filePath).toURI().toURL()); clip.play(); } catch (Exception e) { Logger.getLogger(Wav.class.getName()).log(Level.SEVERE, null, e); } } // =========================== // CONVERT BYTES TO JAVA TYPES // =========================== // these two routines convert a byte array to a unsigned short public static int byteArrayToInt(byte[] b) { int start = 0; int low = b[start] & 0xff; int high = b[start + 1] & 0xff; return (int) (high > 8) & 0x000000FF); b[2] = (byte) ((i >> 16) & 0x000000FF); b[3] = (byte) ((i >> 24) & 0x000000FF); return b; } // convert a short to a byte array public static byte[] shortToByteArray(short data) { return new byte[]{(byte) (data & 0xff), (byte) ((data >>> 8) & 0xff)}; } /** * Inner class to play back the data that was saved */ class PlayThread extends Thread { byte tempBuffer[] = new byte[10000]; public void run() { try { int cnt; //Keep looping until the input // read method returns -1 for // empty stream. while ((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1) { if (cnt > 0) { //Write data to the internal // buffer of the data line // where it will be delivered // to the speaker. sourceDataLine.write(tempBuffer, 0, cnt); } } //Block and wait for internal // buffer of the data line to // empty. sourceDataLine.drain(); sourceDataLine.close(); } catch (Exception e) { System.out.println(e); System.exit(0); } } } }

包装的全球性;
导入java.applet.applet;
导入java.applet.AudioClip;
导入java.net.URISyntaxException;
导入java.util.logging.Level;
导入java.util.logging.Logger;
导入java.io.*;
导入java.io.File;
导入java.net.MalformedURLException;
导入java.net.URL;
导入javax.sound.sampled.*;
/**
*此类处理wav文件的读取、写入和播放。它是
*还能够将文件转换为原始字节[]形式。
*
*基于由Dan Vargo修改的Evan Merz编写的代码
*@author-dvargo
*/
公共类Wav{
/*
WAV文件规范
从…起http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
标准波形格式从RIFF头开始:
0 4 ChunkID包含ASCII格式的字母“RIFF”
(0x52494646大端形式)。
4块大小36+子块大小,或者更准确地说:
4+(8+分块尺寸)+(8+分块尺寸)
这是块的其余部分的大小
下面是这个数字。这是
整个文件的字节数减去
此计数中不包括两个字段:
ChunkID和ChunkSize。
8.4格式包含字母“WAVE”
(0x57415645大端形式)。
“波浪”格式由两个子块组成:“fmt”和“数据”:
“fmt”子单元描述声音数据的格式:
12 4子chunk1id包含字母“fmt”
(0x666d7420大端形式)。
16 4用于PCM的Subchunk1尺寸16。这是
这个数字后面的子块的其余部分。
20 2音频格式PCM=1(即线性量化)
除1以外的值表示某些
压缩的形式。
22 2个数字通道单声道=1,立体声=2,等等。
24 4采样器8000、44100等。
28 4字节==采样器*数字通道*位采样/8
32 2块对齐==NumChannels*位采样/8
一个示例的字节数,包括
所有频道。我想知道什么时候发生
这个数字不是整数?
34 2位采样8位=8,16位=16等。
“数据”子块包含数据大小和实际声音:
36 4 Subchunk2ID包含字母“data”
(0x64617461大端形式)。
40 4子chunk2size==NumSamples*NumChannels*BitsPerSample/8
这是数据中的字节数。
您也可以将其视为尺寸
下面的子块的读取
号码。
44*数据-实际声音数据。
让读取wav文件变得棘手的是java没有未签名的类型
二进制数据不能被正确地读取和转换。而且,我们必须使用更大的类型
而不是通常需要的。
在包括java在内的许多语言中,整数由4个字节表示
在大多数语言中,整数可以是有符号的,也可以是无符号的,在wav文件中是整数
因此,为了确保能够存储正确的值,我们必须使用long
用于保存整数,整数用于保存短路。
然后,当我们想要保存wav数据时,我们必须转换回来。
这很复杂,但最终,它只会在程序底部产生一些额外的函数
一旦你了解了这个问题,就没有理由再去关注它了
去吧。
也:
此代码不会读取所有wav文件。这并不是完全用于规范。它只是使用
大多数wav文件都遵循的精简版本。
*/
ByteArrayOutputStream ByteArrayOutputStream;
音频格式;音频格式;
TargetDataLine TargetDataLine;
音频输入流音频输入流;
SourceDataLine SourceDataLine;
浮动频率=8000.0F;//80001102516000205044100
int samplesize=16;
私有字符串myPath;
私人长码;
私有长mySubChunk1Size;
私有int-myFormat;
私人长频道;
私家长住;
私人长粘虫;
私有int-myBlockAlign;
私有int myBitsPerSample;
私有长myDataSize;
//我把这件事公之于众,这样你就可以把你想要的东西扔进去
//也许是一个记录的缓冲区,也许是你想要的任何东西
公共字节[]myData;
公共Wav()
{
myPath=“”;
}
//构造函数采用wav路径
公共Wav(字符串tmpPath){
myPath=tmpPath;
}
//获取/设置路径属性的值
公共字符串getPath()
{
返回myPath;
}
公共void setPath(字符串newPath)
{
myPath=newPath;
}
//将wav文件读入此类
公共布尔读取(){
DataInputStream infle=null;
myData=null;
字节[]tmpLong=新字节[4];
字节[]tmpInt=新字节[2];
试一试{
infle=newdatainputstream(newfileinputstream(myPath));
//System.out.println(“读取wav文件…\n”);//仅用于调试
字符串chunkID=“+(char)infle.readByte()+(char)infle.readByte()+(char)infle.readByte()+(char)infle.readByte();
infle.read(tmpLong);//读取ChunkSize
myChunkSize=byteArrayToLong(tmpLong);
字符串形式
AudioInputStream monoStream = new AudioInputStream(stereoStream,format,length/2);

wfw.write(monoStream, Type.WAVE, new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/stegDroid/un-ogged.wav"));
    AudioFormat format = new AudioFormat(Encoding.PCM_SIGNED, 44100, 16, 2, 2, 44100, 
false);
    AudioFormat format = new AudioFormat(Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, 
false);
    javax.sound.sampled.spi.FormatConversionProvider