2016-03-24 20 views
16

Tôi có tính năng tôi muốn chuyển sang sử dụng Android AudioTrack thay vì MediaPlayer do một số lỗi nổi tiếng với MediaPlayer, chẳng hạn như khoảng cách nhỏ xuất hiện giữa các tuyến đường vòng lặp.Phát lặp âm thanh bằng AudioTrack

Tôi đã được đề xuất sử dụng AudioTrack nhưng chưa tìm thấy nhiều ví dụ về việc sử dụng. Tôi đã tìm thấy một câu hỏi về SO về AudioTrack và sử dụng một số đoạn mã đó để hack cùng điều gì đó:

public class TestActivity extends Activity implements Runnable { 

    Button playButton; 
    byte[] byteData = null; 
    int bufSize; 
    AudioTrack myAT = null; 
    Thread playThread = null; 

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

     playButton = (Button) findViewById(R.id.testButton); 

     InputStream inputStream = getResources().openRawResource(R.raw.whitenoise_wav); 
     try { 
      byteData = new byte[ inputStream.available()]; 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

     try { 
      inputStream.read(byteData); 
      inputStream.close(); 
     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

     initialize(); 

     playButton.setOnClickListener(new View.OnClickListener() { 
      public void onClick(View v) { 

       playThread.start(); 
      } 
     }); 
    } 

    void initialize() { 

     bufSize = android.media.AudioTrack.getMinBufferSize(44100, 
       AudioFormat.CHANNEL_CONFIGURATION_STEREO, 
       AudioFormat.ENCODING_PCM_16BIT); 

     myAT = new AudioTrack(AudioManager.STREAM_MUSIC, 
       44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO, 
       AudioFormat.ENCODING_PCM_16BIT, bufSize, 
       AudioTrack.MODE_STREAM); 
     myAT.setVolume(.2f); 

     playThread = new Thread(this); 
    } 

    public void run() { 
     if (myAT != null) { 
      myAT.play(); 
      myAT.setLoopPoints(0, byteData.length, 6); 
      myAT.write(byteData, 0, byteData.length); 
     } 
    } 
} 

Vì vậy, điều này dường như để chơi toàn bộ track âm thanh (~ 1: 00 phút) rồi dừng lại. Bây giờ mục tiêu cuối cùng ở đây là hai có 2 track âm thanh riêng biệt chơi và looping cùng một lúc. Tôi hiện đang có các bản âm thanh trong thư mục /res/raw/, nhưng tôi có thể chuyển chúng vào thư mục assets đơn giản nếu điều đó tốt hơn. Việc triển khai hiện tại của tôi là AudioTrack có đúng không? Nếu vậy, làm thế nào tôi sẽ làm cho nó lặp lại?

Tóm tắt: cách bạn có thể phát âm thanh lặp mà không có khoảng cách sử dụng AudioTrack?

Đề xuất các cách thay thế để nhận âm thanh lặp, chẳng hạn như thư viện của bên thứ ba, được hoan nghênh.

+0

Bạn có muốn phát cả hai bản nhạc cùng lúc và cũng lặp lại âm thanh đó khi hoàn thành đúng không? –

+0

Đúng vậy. – Orbit

+0

Kiểm tra điều này: http://developer.android.com/reference/android/media/SoundPool.html –

Trả lời

5

Bạn không thể lặp lại bằng cách sử dụng AudioTrack được định cấu hình với AudioTrack.MODE_STREAM. Nếu bạn sử dụng MODE_STREAM AudioTrack cần phải được lấp đầy với các mẫu mới liên tục.

Nhưng bạn có thể định cấu hình nó bằng AudioTrack.MODE_STATIC và chuyển toàn bộ bộ đệm được phát (ý tôi là: nếu bạn cần trộn hai mẫu, bạn phải truyền mẫu hỗn hợp).

setLoopPoints: Đặt điểm vòng lặp và số vòng lặp. Vòng lặp có thể là vô hạn. Tương tự như setPlaybackHeadPosition, bản nhạc phải được dừng hoặc tạm dừng để thay đổi các điểm vòng lặp và phải sử dụng chế độ MODE_STATIC.

Xin lưu ý rằng AudioTrack phát các mẫu PCM thô, không hỗ trợ WAV, MP3 hoặc các vùng chứa khác.

+0

Nếu tệp được lưu trữ trong tài nguyên thô của bạn, bạn có thể trả lại 'InputStream' khi nhận tài nguyên và nguồn cấp dữ liệu vào' AudioTrack'. Bằng cách lặp lại, tôi có nghĩa là bất kỳ cách nào để lặp lại chính xác nó, cho dù nó được cho ăn dữ liệu liên tục hoặc ăn nó một lần và tự động lặp lại, nó chỉ đơn giản là không thể có một khoảng cách. – Orbit

+0

Tôi hiểu câu nói của bạn, tôi biết rằng việc lặp tự động với '.setLoopingPoints()' chỉ có thể được thực hiện ở chế độ tĩnh. Tôi đã tìm kiếm một ví dụ về loopless gaping với một trong hai 'MODE_STATIC' và/hoặc' MODE_STREAM' vì hầu hết các ví dụ mà tôi đã gặp đều không thực sự hoạt động. Tôi chơi xung quanh với nó một chút và có một thực hiện làm việc bằng cách sử dụng chế độ tĩnh và '.setLoopingPoints', mặc dù nó đôi khi studders trong khi phát lại. Tôi cũng nhận thấy rằng nếu nó không được phát trong một chủ đề mới, nó sẽ tạo ra âm thanh khởi động lớn khi khởi động, và sau đó rơi vào im lặng. Đang phát trên chủ đề chính có phải là vấn đề không? – Orbit

1

Nhìn vào ví dụ this, có vẻ như một sự cố tương tự, đã giải quyết việc cấp liên tục AudioTrack.

class ToneGenerator { 
    int sampleRate = 8000; 
    double sample[] = null; 
    byte generatedSnd[] = null; 
    int m_ifreq = 400; 
    Thread m_PlayThread = null; 
    boolean m_bStop = false; 
    AudioTrack m_audioTrack = null; 
    int m_play_length = 1000;//in seconds 

    static public void PlayTone(int freq, int play_length) { 
     ToneGenerator player = new ToneGenerator(); 
     player.m_ifreq = freq; 
     player.m_play_length = play_length; 
     player.play(); 
    } 

    synchronized void stop() { 
     m_bStop = true; 
     if (m_PlayThread != null) { 
      try { 
       m_PlayThread.interrupt(); 
       m_PlayThread.join(); 
       m_PlayThread = null; 
      } catch (Exception e) { 

      } 
     } 
     if (m_audioTrack != null) { 
      m_audioTrack.stop(); 
      m_audioTrack.release(); 
      m_audioTrack = null; 
     } 
    } 

    synchronized void play() { 
     m_bStop = false; 
     m_PlayThread = new Thread() { 
      public void run() { 
       try { 
        int iToneStep = 0; 

        m_audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 
          sampleRate, AudioFormat.CHANNEL_OUT_MONO, 
          AudioFormat.ENCODING_PCM_16BIT, 2 * sampleRate, 
          AudioTrack.MODE_STREAM); 

        while (!m_bStop && m_play_length-- > 0) { 
         genTone(iToneStep++); 

         m_audioTrack.write(generatedSnd, 0, generatedSnd.length); 
         if (iToneStep == 1) { 
          m_audioTrack.play(); 
         } 
        } 
       } catch (Exception e) { 
        Log.e("Tone", e.toString()); 
       } catch (OutOfMemoryError e) { 
        Log.e("Tone", e.toString()); 
       } 

      } 
     }; 
     m_PlayThread.start(); 
    } 

    //Generate tone data for 1 seconds 
    synchronized void genTone(int iStep) { 
     sample = new double[sampleRate]; 

     for (int i = 0; i < sampleRate; ++i) { 
      sample[i] = Math.sin(2 * Math.PI * (i + iStep * sampleRate)/(sampleRate/m_ifreq)); 
     } 

     // convert to 16 bit pcm sound array 
     // assumes the sample buffer is normalised. 
     generatedSnd = new byte[2 * sampleRate]; 
     int idx = 0; 
     for (final double dVal : sample) { 
      // scale to maximum amplitude 
      final short val = (short) ((dVal * 32767)); 
      // in 16 bit wav PCM, first byte is the low order byte 
      generatedSnd[idx++] = (byte) (val & 0x00ff); 
      generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); 
     } 
    } 

} 
+0

lý do tại sao 2 * sampleRate? – Coco

+0

Vì lấy mẫu 16bit nên bạn làm 8 * 2 – loretoparisi

0

Bạn đang chuyển sang độ dài byte của luồng. setLoopPoints() dự kiến ​​số khối âm thanh trong luồng. Xem getBufferSizeInFrames() trong tài liệu.

Tuy nhiên. Tôi cảnh báo bạn rằng hầu hết các định dạng âm thanh cụ thể không chặn; chúng dựa trên mẫu. MP3 là ngoại lệ duy nhất; nó được căn chỉnh trên 1152 byte ranh giới. Nếu nội dung nguồn là MP3 và không được tác giả rõ ràng để được căn chỉnh khối, thì lặp liên tục là không thể. Dường như bạn đang sử dụng "whitenoise.wav" làm nguồn âm thanh của bạn. Trừ khi độ dài của tệp này được liên kết rõ ràng với kích thước khối, bạn có thể có các tạo phẩm âm thanh khi lặp.

Giải pháp cho việc này là làm mờ dần các khối khung hình bắt đầu và khung cuối cùng khi bạn lặp lại và xử lý tất cả bộ đệm, nhưng bạn phải viết mã để tự làm điều này.

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