2016-03-24 10 views
16

Ho una funzione su cui voglio passare per utilizzare Androids AudioTrack anziché MediaPlayer, a causa di alcuni bug noti con MediaPlayer, come il piccolo spazio tra le tracce in loop.Riprodurre l'audio in loop utilizzando AudioTrack

Mi è stato consigliato di utilizzare AudioTrack ma non ne ho trovato molti esempi in uso. Ho trovato una domanda su SO per quanto riguarda AudioTrack e usato alcuni di quel codice di hack insieme qualcosa:

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); 
     } 
    } 
} 

Quindi questo vuol sembrano riprodurre l'intera traccia audio (~ 1: 00 min) e poi si ferma. Ora l'obiettivo finale qui è due avere 2 tracce audio separate che suonano e si ripetono allo stesso tempo. Attualmente ho le tracce audio nella directory /res/raw/, ma posso spostarle in una semplice cartella assets se fosse meglio. La mia attuale implementazione di AudioTrack è corretta? Se è così, come faccio a farlo in loop?

In sommario: come è possibile riprodurre l'audio in loop senza uno spazio utilizzando AudioTrack?

Suggerimenti per modi alternativi per ottenere l'audio in loop, ad esempio le librerie di terze parti, sono i benvenuti.

+0

Si desidera riprodurre entrambe le tracce contemporaneamente e anche il ciclo dell'audio una volta terminato correttamente? –

+0

Questo è corretto. – Orbit

+0

Controllare questo: http://developer.android.com/reference/android/media/SoundPool.html –

risposta

5

Non è possibile eseguire il ciclo utilizzando un AudioTrack configurato con AudioTrack.MODE_STREAM. Se si utilizza MODE_STREAM AudioTrack deve essere riempito continuamente con nuovi campioni.

Ma è possibile configurarlo con AudioTrack.MODE_STATIC e passare l'intero buffer da riprodurre (voglio dire: se è necessario mescolare due campioni, è necessario passare i campioni misti).

setLoopPoints: imposta i punti di loop e il conteggio dei cicli. Il ciclo può essere infinito. Analogamente a setPlaybackHeadPosition, la traccia deve essere arrestata o sospesa per la modifica dei punti di loop e deve utilizzare la modalità MODE_STATIC.

Si prega di notare che AudioTrack riproduce campioni PCM grezzi, non c'è supporto per WAV, MP3 o altri contenitori.

+0

Se il file è archiviato nelle risorse non elaborate, è possibile restituire un 'InputStream' quando si recupera la risorsa e la si inserisce nel' AudioTrack'. In loop, intendo qualsiasi modo per eseguire il loop in modo corretto, che si tratti di alimentarlo continuamente o di alimentarlo una volta e di eseguire il ciclo automatico, semplicemente non può avere un gap. – Orbit

+0

Capisco cosa stai dicendo, so che il ciclo automatico con '.setLoopingPoints()' può essere fatto solo in modalità statica. Stavo cercando un esempio di loop senza pause con 'MODE_STATIC' e/o' MODE_STREAM' dato che la maggior parte degli esempi che ho trovato non stavano funzionando. Ho giocato un po 'con esso e ho ottenuto un'implementazione funzionante usando la modalità statica e '.setLoopingPoints', anche se a volte studia durante la riproduzione. Ho anche notato che se non viene riprodotto in una nuova discussione, produrrà un suono di avvio rumoroso all'avvio e quindi si bloccherà. Il thread principale sta giocando un problema? – Orbit

1

Vedere l'esempio this, sembra un problema simile, risolto l'alimentazione continuamente il 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

perché 2 * sampleRate? – Coco

+0

A causa del campionamento a 16 bit, così fai 8 * 2 – loretoparisi

0

Stai passando la lunghezza in byte del flusso. setLoopPoints() si aspetta il numero di blocchi audio nel flusso. Vedi getBufferSizeInFrames() nella documentazione.

Tuttavia. Ti avverto che molti formati audio particolari non sono basati su blocchi; sono basati su campioni. MP3 è l'unica eccezione; è allineato su 1152 limiti di byte. Se il contenuto sorgente è MP3 e non è esplicitamente creato per essere allineato, il ciclo continuo è impossibile. Sembra che tu stia usando "whitenoise.wav" come sorgente audio. A meno che la lunghezza di questo file non sia allineata in modo esplicito alle dimensioni del blocco, è possibile che durante il loop si verifichino artefatti audio.

La soluzione per questo è di dissolvenza incrociata dei blocchi del fotogramma iniziale e finale quando si esegue il ciclo, e gestire tutto il buffer da soli, ma si dovrebbe scrivere codice per farlo da soli.