音频数据有问题

Posted

技术标签:

【中文标题】音频数据有问题【英文标题】:something wrong with audio data 【发布时间】:2012-05-16 18:19:19 【问题描述】:

我认为主要问题来自 MicrophoneManager 中的读取方法,但看不到问题出在哪里。我的 bytesread 控制台输出为 0(这是在另一个类 AudioTransmitter 中)。在我看来,它不是流式传输音频数据,因为发送的数据没有变化,就像我说的读取字节数为 0。

    public class MicrophoneManager
    //  private     TargetDataLine   targetDataLine;
      private   float            sampleRate       = 8000.0F;    //8000,11025,16000,22050,44100
      private   int              sampleSizeInBits = 16;         //8,16
      private   int              channels         = 1;          //1,2
      private   boolean          signed           = true;       //true,false
      private   boolean          bigEndian        = false;      //true,false
      private   AudioFormat      audioFormat;
    //  private AudioRecord      audioRecord;
    //  private AudioInputStream ais;
      private static MicrophoneManager singletonMicrophoneManager = null;

      public AudioRecord audioRecord; 
      public int mSamplesRead; //how many samples read 
      public int buffersizebytes; 
      public int buflen; 
      public int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; 
      public int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; 
      public static byte[] buffer; //+-32767 
      public static final int SAMPPERSEC = 8000; //samp per sec 8000, 11025, 22050 44100 or 48000 


      public class MicrophoneManager
//  private     TargetDataLine   targetDataLine;
  private   float            sampleRate       = 8000.0F;    //8000,11025,16000,22050,44100
  private   int              sampleSizeInBits = 16;         //8,16
  private   int              channels         = 1;          //1,2
  private   boolean          signed           = true;       //true,false
  private   boolean          bigEndian        = false;      //true,false
  private   AudioFormat      audioFormat;
//  private AudioRecord      audioRecord;
//  private AudioInputStream ais;
  private static MicrophoneManager singletonMicrophoneManager = null;

  public AudioRecord audioRecord; 
  public int mSamplesRead; //how many samples read 
  public int buffersizebytes; 
  public int buflen; 
  public int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; 
  public int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; 
  public static short[] buffer; //+-32767 
  public static final int SAMPPERSEC = 8000; //samp per sec 8000, 11025, 22050 44100 or 48000 


  public MicrophoneManager()  
      System.out.println("Initializing");   
//      audioFormat = new AudioFormat(sampleRate,sampleSizeInBits,channels,signed,bigEndian);

//      audioRecord = new AudioRecord(MediaRecorder.Audiosource.DEFAULT, (int) sampleRate, channels, AudioFormat.ENCODING_PCM_16BIT, buffersizebytes);

      buffersizebytes = AudioRecord.getMinBufferSize(SAMPPERSEC,channelConfiguration,audioEncoding); //4096 on ion 
      buffer = new short[buffersizebytes]; 
      buflen=buffersizebytes/2; 
      audioRecord = new AudioRecord(android.media.MediaRecorder.AudioSource.MIC,SAMPPERSEC, 
      channelConfiguration,audioEncoding,buffersizebytes); //constructor       
  

  public static MicrophoneManager getMicrophoneManager() throws Exception 
      if (singletonMicrophoneManager == null) 
          singletonMicrophoneManager = new MicrophoneManager();
          singletonMicrophoneManager.initialize();
      
      return singletonMicrophoneManager;
  

  public void initialize() throws Exception       
  

  public void startAudioInput()

      try  
          audioRecord.startRecording(); 
          mSamplesRead = audioRecord.read(buffer, 0, buffer.length); 
          audioRecord.stop(); 
           catch (Throwable t)  
          // Log.e("AudioRecord", "Recording Failed"); 
              System.out.println("Error Starting audio input"+t);
           
  

  public void stopAudioInput()
      audioRecord.stop(); 
      System.out.println("Stopping audio input");         
  

  public void finishAudioInput()
      audioRecord.release(); 
      System.out.println("Finishing audio input");        
  

  public boolean available() throws Exception 
      return true;
  

  public int read(byte[] inBuf) throws Exception 
     return audioRecord.read(inBuf,0,inBuf.length);
      


音频发射器:

public class AudioTransmitter extends Thread

    private MicrophoneManager mm=null;
    private boolean transmittingAudio = false;
    private String host;
    private int port;
    private long id=0;
    boolean run=true;

    public AudioTransmitter(String host, int port, long id) 
        this.host = host;
        this.port = port;
        this.id = id;
        this.start();
    

    public void run() 

        System.out.println("creating audio transmitter host "+host+" port "+port+" id "+id);

        TrustManager[] trustAllCerts = new TrustManager[]
                new X509TrustManager() 
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() 
                        return null;
                    
                    public void checkClientTrusted(
                            java.security.cert.X509Certificate[] certs, String authType) 
                    
                    public void checkServerTrusted(
                            java.security.cert.X509Certificate[] chain, String authType) 
                        for (int j=0; j<chain.length; j++)
                        
                            System.out.println("Client certificate information:");
                            System.out.println("  Subject DN: " + chain[j].getSubjectDN());
                            System.out.println("  Issuer DN: " + chain[j].getIssuerDN());
                            System.out.println("  Serial number: " + chain[j].getSerialNumber());
                            System.out.println("");
                        
                    
                
        ;

        while (run) 
            if(transmittingAudio) 

                try 
                    if(mm==null) 
                        mm = new MicrophoneManager();
//                      mm.initialize();
                    
                    SSLContext sc = SSLContext.getInstance("SSL");
                    sc.init(null, trustAllCerts, new java.security.SecureRandom());
                    SSLSocketFactory sslFact = sc.getSocketFactory();
                    SSLSocket socket = (SSLSocket)sslFact.createSocket(host, port);

                    socket.setSoTimeout(10000);
                    InputStream inputStream = socket.getInputStream();
                    DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream));
                    OutputStream outputStream = socket.getOutputStream();
                    DataOutputStream os = new DataOutputStream(new BufferedOutputStream(outputStream));
                    PrintWriter socketPrinter = new PrintWriter(os);
                    BufferedReader br = new BufferedReader(new InputStreamReader(in));

//                  socketPrinter.println("POST /transmitaudio?patient=1333369798370 HTTP/1.0");
                    socketPrinter.println("POST /transmitaudio?id="+id+" HTTP/1.0");
                    socketPrinter.println("Content-Type: audio/basic");
                    socketPrinter.println("Content-Length: 99999");
                    socketPrinter.println("Connection: Keep-Alive");
                    socketPrinter.println("Cache-Control: no-cache");
                    socketPrinter.println();
                    socketPrinter.flush();

//                  in.read();

                    mm.startAudioInput();

                    int buffersizebytes = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT); //4096 on ion 

                    System.out.println("audio started");
                    byte[] data = new byte[buffersizebytes];
                    while(transmittingAudio) 
                            //              byte[] data = new byte[mm.available()];
                            int bytesRead = mm.read(data);
                            os.write(data,0,bytesRead);
                            os.flush();
                            //              ca.transmitAxisAudioPacket(data);
//                          System.out.println("read "+data);
                            System.out.println("bytesRead "+bytesRead);
                            System.out.println("data "+Arrays.toString(data));
                    
                    os.close();
                    mm.stopAudioInput();
                 catch (Exception e) 
                    System.out.println("excpetion while transmitting audio connection will be closed"+e);
                    transmittingAudio=false;
                
            
            else 
                try 
                Thread.sleep(1000);
                 catch (Exception e)
                    System.out.println("exception while thread sleeping"+e);
            
        

    

    public void setTransmittingAudio(boolean transmittingAudio) 
        this.transmittingAudio = transmittingAudio;
    

    public void finished() 
        this.transmittingAudio = false;
        mm.finishAudioInput();
    



【问题讨论】:

您忘记了详细解释“出错”的部分 "我的 bytesread 控制台输出为 0" 基本上它似乎没有读取任何内容。每当我输出从流中读取的字节时,它们都是静态的(总是相同的值)。我不明白为什么 read 方法将返回值输入第一个参数并返回长度。因此,当我尝试提取数据时,我什么也得不到。 mm.read(data) 将数据读入data,然后mm.getBuffer() 将一些其他数据与其他缓冲区一起返回给您。 mm.read() 读取 0 也可能意味着录制没有开始(你在 start 函数中停止它并将数据读入某个缓冲区)。 好的,我现在明白了。最初我认为是因为 mm.read() 返回未设置数据的长度。那么为什么如果我做 System.out.println("read "+data);我一遍又一遍地得到 [B@4053c125。这是地址而不是值吗?以及 bytesread 在输出时为 0 的事实,似乎没有读取任何内容 data 是一个没有定义 toString 方法的对象。所以它会打印出对象 ID (getClass().getName() + '@' + Integer.toHexString(hashCode()))。 Arrays.toString(data) 应该打印你的实际内容。 【参考方案1】:

你在打电话

mSamplesRead = audioRecord.read(buffer, 0, buffersizebytes); 

这有一些问题。

audioRecord.read() 想要您正在读取的数组的长度,而不是字节大小。您已将编码设置为 16 位。你真的应该做这样的事情:

new short[] buffer = short[1024]; // or whatever length you like
mSampleRead = audioRecord.read(buffer,0,buffer.length);

您正在调用阅读buffersizebytes,但您设置了buffer = new byte[1024];。没有特别的理由认为buffersizebytes 是正确的数字吗?您想要一个具有 16 位编码的 short[] 数组,并且您希望读取的 SAMPLES(不是字节)数小于或等于该 short[] buffer 的长度。

另外,如果你打印出抛出异常时得到的异常,你的状态会更好,改变

System.out.println("Error Starting audio input");

System.out.println("Error Starting audio input" + t);

你至少会知道为什么 android 会把你扔进垃圾箱。

【讨论】:

为什么我想要一个短的[]​​?因为那时当我去流式传输这些字节时,我的 os.write 不喜欢 short[] 它想要一个 byte[] 数组。将更新上面的代码以反映您概述的其他更改。 audioRecord = new AudioRecord(android.media.MediaRecorder.AudioSource.MIC,SAMPPERSEC,channelConfiguration,audioEncoding,buffersizebytes); // 这应该是 buffersizebytes 还是 buffer.length? 我没有检查过,但看起来你可以使用 byte[]。请记住, .read() 您放入 SAMPLES 进行读取,因此您需要 byte[] 数组是您请求的样本数量的 2 倍。对 AudioRecord() 构造函数的调用确实需要以字节为单位的缓冲区大小。因此,您希望它至少是您的读取时间的 2 倍,除非您不介意在读取请求被填充之前阻止每次读取。如果你试图在同一个线程中做其他事情,你应该避免阻塞,如果你把这个读取放在它自己的线程中,你可以阻塞。 阅读 AudioRecord 的文档。您可以在 read() 函数中有 byte[] 。 @mwengler 说的是非常真实的。 developer.android.com/reference/android/media/AudioRecord.html【参考方案2】:

感谢你们的帮助,但是我找到了一种更好的方法来做需要的事情 (here)。这是代码。请注意,这也使用 http://www.devdaily.com/java/jwarehouse/android/core/java/android/speech/srec/UlawEncoderInputStream.java.shtml 将音频转换为 ulaw

public class AudioWorker extends Thread
 
    private boolean stopped = false;

    private String host;
    private int port;
    private long id=0;
    boolean run=true;
    AudioRecord recorder;

    //ulaw encoder stuff
    private final static String TAG = "UlawEncoderInputStream";

    private final static int MAX_ULAW = 8192;
    private final static int SCALE_BITS = 16;

    private InputStream mIn;

    private int mMax = 0;

    private final byte[] mBuf = new byte[1024];
    private int mBufCount = 0; // should be 0 or 1

    private final byte[] mOneByte = new byte[1];
    ////
    /**
     * Give the thread high priority so that it's not canceled unexpectedly, and start it
     */
    public AudioWorker(String host, int port, long id)
     
        this.host = host;
        this.port = port;
        this.id = id;
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
//        start();
    

    @Override
    public void run()
     
        Log.i("AudioWorker", "Running AudioWorker Thread");
        recorder = null;
        AudioTrack track = null;
        short[][]   buffers  = new short[256][160];
        int ix = 0;

        /*
         * Initialize buffer to hold continuously recorded AudioWorker data, start recording, and start
         * playback.
         */
        try
        
            int N = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);
            recorder = new AudioRecord(AudioSource.MIC, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, N*10);
            track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, 
                    AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, N*10, AudioTrack.MODE_STREAM);
            recorder.startRecording();
            track.play();
            /*
             * Loops until something outside of this thread stops it.
             * Reads the data from the recorder and writes it to the AudioWorker track for playback.
             */


            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            SSLSocketFactory sslFact = sc.getSocketFactory();
            SSLSocket socket = (SSLSocket)sslFact.createSocket(host, port);

            socket.setSoTimeout(10000);
            InputStream inputStream = socket.getInputStream();
            DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream));
            OutputStream outputStream = socket.getOutputStream();
            DataOutputStream os = new DataOutputStream(new BufferedOutputStream(outputStream));
            PrintWriter socketPrinter = new PrintWriter(os);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));

//          socketPrinter.println("POST /transmitaudio?patient=1333369798370 HTTP/1.0");
            socketPrinter.println("POST /transmitaudio?id="+id+" HTTP/1.0");
            socketPrinter.println("Content-Type: AudioWorker/basic");
            socketPrinter.println("Content-Length: 99999");
            socketPrinter.println("Connection: Keep-Alive");
            socketPrinter.println("Cache-Control: no-cache");
            socketPrinter.println();
            socketPrinter.flush();


            while(!stopped)
             
                Log.i("Map", "Writing new data to buffer");
                short[] buffer = buffers[ix++ % buffers.length];

                N = recorder.read(buffer,0,buffer.length);
                track.write(buffer, 0, buffer.length);

                byte[] bytes2 = new byte[buffer.length * 2];
                ByteBuffer.wrap(bytes2).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(buffer);

                read(bytes2, 0, bytes2.length);
//              os.write(bytes2,0,bytes2.length);
                os.write(bytes2,0,bytes2.length);

                System.out.println("bytesRead "+buffer.length);
                System.out.println("data "+Arrays.toString(buffer));
            
            os.close();
        
        catch(Throwable x)
         
            Log.w("AudioWorker", "Error reading voice AudioWorker", x);
        
        /*
         * Frees the thread's resources after the loop completes so that it can be run again
         */
        finally
         
            recorder.stop();
            recorder.release();
            track.stop();
            track.release();
        
    

    /**
     * Called from outside of the thread in order to stop the recording/playback loop
     */

/**
 * Called from outside of the thread in order to stop the recording/playback loop
 */
public void close()
 
     stopped = true;

public void resumeThread()
 
     stopped = false;
     run();


    TrustManager[] trustAllCerts = new TrustManager[]
            new X509TrustManager() 
                public java.security.cert.X509Certificate[] getAcceptedIssuers() 
                    return null;
                
                public void checkClientTrusted(
                        java.security.cert.X509Certificate[] certs, String authType) 
                
                public void checkServerTrusted(
                        java.security.cert.X509Certificate[] chain, String authType) 
                    for (int j=0; j<chain.length; j++)
                    
                        System.out.println("Client certificate information:");
                        System.out.println("  Subject DN: " + chain[j].getSubjectDN());
                        System.out.println("  Issuer DN: " + chain[j].getIssuerDN());
                        System.out.println("  Serial number: " + chain[j].getSerialNumber());
                        System.out.println("");
                    
                
            
    ;


    public static void encode(byte[] pcmBuf, int pcmOffset,
            byte[] ulawBuf, int ulawOffset, int length, int max) 

        // from  'ulaw' in wikipedia
        // +8191 to +8159                          0x80
        // +8158 to +4063 in 16 intervals of 256   0x80 + interval number
        // +4062 to +2015 in 16 intervals of 128   0x90 + interval number
        // +2014 to  +991 in 16 intervals of  64   0xA0 + interval number
        //  +990 to  +479 in 16 intervals of  32   0xB0 + interval number
        //  +478 to  +223 in 16 intervals of  16   0xC0 + interval number
        //  +222 to   +95 in 16 intervals of   8   0xD0 + interval number
        //   +94 to   +31 in 16 intervals of   4   0xE0 + interval number
        //   +30 to    +1 in 15 intervals of   2   0xF0 + interval number
        //     0                                   0xFF

        //    -1                                   0x7F
        //   -31 to    -2 in 15 intervals of   2   0x70 + interval number
        //   -95 to   -32 in 16 intervals of   4   0x60 + interval number
        //  -223 to   -96 in 16 intervals of   8   0x50 + interval number
        //  -479 to  -224 in 16 intervals of  16   0x40 + interval number
        //  -991 to  -480 in 16 intervals of  32   0x30 + interval number
        // -2015 to  -992 in 16 intervals of  64   0x20 + interval number
        // -4063 to -2016 in 16 intervals of 128   0x10 + interval number
        // -8159 to -4064 in 16 intervals of 256   0x00 + interval number
        // -8192 to -8160                          0x00

        // set scale factors
        if (max <= 0) max = MAX_ULAW;

        int coef = MAX_ULAW * (1 << SCALE_BITS) / max;

        for (int i = 0; i < length; i++) 
            int pcm = (0xff & pcmBuf[pcmOffset++]) + (pcmBuf[pcmOffset++] << 8);
            pcm = (pcm * coef) >> SCALE_BITS;

            int ulaw;
            if (pcm >= 0) 
                ulaw = pcm <= 0 ? 0xff :
                        pcm <=   30 ? 0xf0 + ((  30 - pcm) >> 1) :
                        pcm <=   94 ? 0xe0 + ((  94 - pcm) >> 2) :
                        pcm <=  222 ? 0xd0 + (( 222 - pcm) >> 3) :
                        pcm <=  478 ? 0xc0 + (( 478 - pcm) >> 4) :
                        pcm <=  990 ? 0xb0 + (( 990 - pcm) >> 5) :
                        pcm <= 2014 ? 0xa0 + ((2014 - pcm) >> 6) :
                        pcm <= 4062 ? 0x90 + ((4062 - pcm) >> 7) :
                        pcm <= 8158 ? 0x80 + ((8158 - pcm) >> 8) :
                        0x80;
             else 
                ulaw = -1 <= pcm ? 0x7f :
                          -31 <= pcm ? 0x70 + ((pcm -   -31) >> 1) :
                          -95 <= pcm ? 0x60 + ((pcm -   -95) >> 2) :
                         -223 <= pcm ? 0x50 + ((pcm -  -223) >> 3) :
                         -479 <= pcm ? 0x40 + ((pcm -  -479) >> 4) :
                         -991 <= pcm ? 0x30 + ((pcm -  -991) >> 5) :
                        -2015 <= pcm ? 0x20 + ((pcm - -2015) >> 6) :
                        -4063 <= pcm ? 0x10 + ((pcm - -4063) >> 7) :
                        -8159 <= pcm ? 0x00 + ((pcm - -8159) >> 8) :
                        0x00;
            
            ulawBuf[ulawOffset++] = (byte)ulaw;
        
    
    public static int maxAbsPcm(byte[] pcmBuf, int offset, int length) 
        int max = 0;
        for (int i = 0; i < length; i++) 
            int pcm = (0xff & pcmBuf[offset++]) + (pcmBuf[offset++] << 8);
            if (pcm < 0) pcm = -pcm;
            if (pcm > max) max = pcm;
        
        return max;
    

    public int read(byte[] buf, int offset, int length) throws IOException 
        if (recorder == null) throw new IllegalStateException("not open");

        // return at least one byte, but try to fill 'length'
        while (mBufCount < 2) 
            int n = recorder.read(mBuf, mBufCount, Math.min(length * 2, mBuf.length - mBufCount));
            if (n == -1) return -1;
            mBufCount += n;
        

        // compand data
        int n = Math.min(mBufCount / 2, length);
        encode(mBuf, 0, buf, offset, n, mMax);

        // move data to bottom of mBuf
        mBufCount -= n * 2;
        for (int i = 0; i < mBufCount; i++) mBuf[i] = mBuf[i + n * 2];

        return n;
    


【讨论】:

流式传输时仍有一些暂停,因此可能存在一些阻塞,需要对其进行调整 谢天谢地,我以某种方式打破了这个,这个帖子是我唯一的备份。不会再犯这个错误

以上是关于音频数据有问题的主要内容,如果未能解决你的问题,请参考以下文章

Java - 将 16 位有符号 pcm 音频数据数组转换为双精度数组

使用 Python 使用音频传输数据

将 32 位有符号整数转换为 24 位有符号音频数据

iOS利用FFmpeg解码音频数据并播放

如何将音频数据数组转换为 wav 文件?

音频文件元数据的最佳库? [关闭]