2013-04-11 23 views
12

Tôi đang cố gắng tạo một ứng dụng Android đơn giản phát trực tiếp âm thanh micrô trực tiếp đến máy chủ để phát lại. Phát lại kết quả có vẻ kỳ lạ, với khoảng trống lớn trong âm thanh. Có ai biết tôi đang làm gì sai không?Android AudioRecord tới Server qua các vấn đề phát lại UDP

CHỈNH SỬA: Đã giải quyết. Hóa ra tôi đã giả định rằng mỗi bộ đệm đến sẽ hoàn toàn đầy đủ, một giả định bị lỗi về phía tôi.

Đây là hoạt động của tôi:

public class MainActivity extends Activity { 
    private static String TAG = "AudioClient"; 

    // the server information 
    private static final String SERVER = "xx.xx.xx.xx"; 
    private static final int PORT = 50005; 

    // the audio recording options 
    private static final int RECORDING_RATE = 44100; 
    private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO; 
    private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT; 

    // the button the user presses to send the audio stream to the server 
    private Button sendAudioButton; 

    // the audio recorder 
    private AudioRecord recorder; 

    // the minimum buffer size needed for audio recording 
    private static int BUFFER_SIZE = AudioRecord.getMinBufferSize(
      RECORDING_RATE, CHANNEL, FORMAT); 

    // are we currently sending audio data 
    private boolean currentlySendingAudio = false; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     Log.i(TAG, "Creating the Audio Client with minimum buffer of " 
       + BUFFER_SIZE + " bytes"); 

     // set up the button 
     sendAudioButton = (Button) findViewById(R.id.start_button); 
     sendAudioButton.setOnTouchListener(new OnTouchListener() { 

      @Override 
      public boolean onTouch(View v, MotionEvent event) { 

       switch (event.getAction()) { 

       case MotionEvent.ACTION_DOWN: 
        startStreamingAudio(); 
        break; 

       case MotionEvent.ACTION_UP: 
        stopStreamingAudio(); 
        break; 
       } 

       return false; 
      } 
     }); 
    } 

    private void startStreamingAudio() { 

     Log.i(TAG, "Starting the audio stream"); 
     currentlySendingAudio = true; 
     startStreaming(); 
    } 

    private void stopStreamingAudio() { 

     Log.i(TAG, "Stopping the audio stream"); 
     currentlySendingAudio = false; 
     recorder.release(); 
    } 

    private void startStreaming() { 

     Log.i(TAG, "Starting the background thread to stream the audio data"); 

     Thread streamThread = new Thread(new Runnable() { 

      @Override 
      public void run() { 
       try { 

        Log.d(TAG, "Creating the datagram socket"); 
        DatagramSocket socket = new DatagramSocket(); 

        Log.d(TAG, "Creating the buffer of size " + BUFFER_SIZE); 
        byte[] buffer = new byte[BUFFER_SIZE]; 

        Log.d(TAG, "Connecting to " + SERVER + ":" + PORT); 
        final InetAddress serverAddress = InetAddress 
          .getByName(SERVER); 
        Log.d(TAG, "Connected to " + SERVER + ":" + PORT); 

        Log.d(TAG, "Creating the reuseable DatagramPacket"); 
        DatagramPacket packet; 

        Log.d(TAG, "Creating the AudioRecord"); 
        recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 
          RECORDING_RATE, CHANNEL, FORMAT, BUFFER_SIZE * 10); 

        Log.d(TAG, "AudioRecord recording..."); 
        recorder.startRecording(); 

        while (currentlySendingAudio == true) { 

         // read the data into the buffer 
         int read = recorder.read(buffer, 0, buffer.length); 

         // place contents of buffer into the packet 
         packet = new DatagramPacket(buffer, read, 
          serverAddress, PORT); 

         // send the packet 
         socket.send(packet); 
        } 

        Log.d(TAG, "AudioRecord finished recording"); 

       } catch (Exception e) { 
        Log.e(TAG, "Exception: " + e); 
       } 
      } 
     }); 

     // start the thread 
     streamThread.start(); 
    } 
} 

Đây là mã server-side của tôi:

class Server { 

    AudioInputStream audioInputStream; 
    static AudioInputStream ais; 
    static AudioFormat format; 
    static boolean status = true; 
    static int port = 50005; 
    static int sampleRate = 11025; 
    static int bufferSize = 9728; 

    static Long lastTime; 
    static long totalBytesReceived = 0L; 

    private static final int audioStreamBufferSize = bufferSize * 20; 
    static byte[] audioStreamBuffer = new byte[audioStreamBufferSize]; 
    private static int audioStreamBufferIndex = 0; 

    public static void main(String args[]) throws Exception { 

     Log("Starting the AudioServer..."); 

     Log("Creating the datagram socket on port " + port + "..."); 
     DatagramSocket serverSocket = new DatagramSocket(null); 
     serverSocket.setReuseAddress(true); 
     serverSocket.bind(new InetSocketAddress(port)); 

     Log("Creating the buffer to hold the received data of size " 
       + bufferSize + "..."); 
     byte[] receiveData = new byte[bufferSize]; 

     Log("Setting the audio rate to " + sampleRate + "hz..."); 
     format = new AudioFormat(sampleRate, 16, 1, true, false); 

     Log("Ready to receive audio data"); 
     while (status == true) { 

      DatagramPacket receivePacket = new DatagramPacket(receiveData, 
        receiveData.length); 
      serverSocket.receive(receivePacket); 
      bufferAudioForPlayback(receivePacket.getData(), 
        receivePacket.getOffset(), receivePacket.getLength()); 
     } 

     serverSocket.close(); 
    } 

    private static void bufferAudioForPlayback(byte[] buffer, int offset, 
      int length) { 

     byte[] actualBytes = new byte[length]; 

     for (int i = 0; i < length; i++) { 
      actualBytes[i] = buffer[i]; 
     } 

     for (byte sample : actualBytes) { 

      int percentage = (int) (((double) audioStreamBufferIndex/(double) audioStreamBuffer.length) * 100.0); 
      Log("buffer is " + percentage + "% full"); 

      audioStreamBuffer[audioStreamBufferIndex] = sample; 
      audioStreamBufferIndex++; 
      Log("Buffer " + audioStreamBufferIndex + "/" 
        + audioStreamBuffer.length + " " + percentage); 

      if (audioStreamBufferIndex == audioStreamBuffer.length - 1) { 
       toSpeaker(audioStreamBuffer); 
       audioStreamBufferIndex = 0; 
       System.exit(0); 
      } 
     } 
    } 

    private static void Log(String log) { 
     System.out.println(log); 
    } 

    public static void toSpeaker(byte soundbytes[]) { 
     try { 

      DataLine.Info dataLineInfo = new DataLine.Info(
        SourceDataLine.class, format); 
      SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem 
        .getLine(dataLineInfo); 

      sourceDataLine.open(format); 

      FloatControl volumeControl = (FloatControl) sourceDataLine 
        .getControl(FloatControl.Type.MASTER_GAIN); 
      volumeControl.setValue(100.0f); 

      sourceDataLine.start(); 
      sourceDataLine.open(format); 
      sourceDataLine.start(); 
      sourceDataLine.write(soundbytes, 0, soundbytes.length); 
      sourceDataLine.drain(); 
      sourceDataLine.close(); 
     } catch (Exception e) { 
      System.out.println("Error with audio playback: " + e); 
      e.printStackTrace(); 
     } 
    } 
} 

Cuối cùng, đây là file xml nguồn lực cho các hoạt động chính:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:padding="20dip"> 

    <ImageView 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:src="@drawable/ic_launcher" 
     android:scaleType="fitCenter"/> 

     <TextView 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:text="@string/app_info" 
     android:layout_weight="1.0" 
     android:textSize="20dip"/> 

    <LinearLayout 
     android:orientation="horizontal" 
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content"> 

     <Button 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:id="@+id/btnStart" 
       android:text="@string/start_recording" 
       android:layout_weight="1.0"/> 

       <Button 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:id="@+id/btnStop" 
       android:text="@string/stop_recording" 
       android:layout_weight="1.0"/> 
    </LinearLayout> 
</LinearLayout> 

EDIT: phát lại âm thanh suh-suh-suh-suh-o-ou-ou-ou-nds-nds-ds như thế này.

+0

Sử dụng phương pháp này, tôi có thể có nhiều hơn một thiết bị dòng vào cùng một cổng? Nếu vậy, làm cách nào để tách luồng nào ở phía máy chủ? –

Trả lời

7

Dưới đây là một cái gì đó bạn có thể thử, thay vì:

// read the data into the buffer 
recorder.read(buffer, 0, buffer.length); 

// place contents of buffer into the packet 
packet = new DatagramPacket(buffer, buffer.length, serverAddress, PORT); 

Đừng nghĩ rằng bạn đã nhận được bộ đệm đọc đầy đủ từ recorder nhưng sử dụng giá trị đọc thực tế thay vì

// read the data into the buffer 
int read = recorder.read(buffer, 0, buffer.length); 

// place contents of buffer into the packet 
packet = new DatagramPacket(buffer, read, serverAddress, PORT); 

Hoặc một cái gì đó giống nhau.

+0

Tôi đã thực hiện các thay đổi bạn đã đề xuất nhưng đầu ra âm thanh vẫn giống như một khẩu súng máy. Bạn có thể nói rằng đó là âm thanh chính xác, nó chỉ là có những khoảng trống. Cảm ơn bạn đã phản hồi! –

+2

Ngoài ra, bạn cũng có thể muốn thay đổi các gói dữ liệu đọc. Vào một cái gì đó giống nhau: bufferAudioForPlayback (receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength()). – harism

+1

Cảm ơn bạn! Điều đó, kết hợp với việc thay đổi tỷ lệ lấy mẫu thành 11025 đã cho tôi phát lại không rõ ràng, không rõ ràng. Bạn thật tuyệt vời, cảm ơn! –

0

Cám ơn bài Joshua .... giúp đỡ rất nhiều cho những con ong mới :)

volumeControl.setValue(volumeControl.getMaximum()); 

removes illegalStateException at Server 

và sự cho phép trong android client

<uses-permission android:name="android.permission.RECORD_AUDIO"/> 
+2

Làm thế nào để các đoạn mã liên quan đến vấn đề phát lại nói lắp? – MarsAtomic

Các vấn đề liên quan