/ / Gapless-Audio-Looping konnte bisher nicht auf Android-Android- und Android-Audiomanager erzielt werden

Gapless audio looping bisher nicht auf Android - Android, Android-Audiomanager erreichen

Ich habe fast jede Methode ausprobiert, aber es ist mir nicht gelungen, eine lückenlose Audiowiedergabe zwischen einer einzelnen Spur mit einer Dauer von 10-15 Sekunden zu erreichen.

Schritte, die ich ausprobiert habe

  1. Verschiedene Audiodateiformate .mp3 .wav .ogg verwenden setLooping(true):

    MediaPlayer mp1 = MediaPlayer.create(MainActivity.this, R.raw.track1);
    mp1.setLooping(true);
    mp1.start();
    
  2. Erstellen von zwei Mediaplayern und Wiederholen einer nach der anderen mit setOnCompletionListenerdie gleiche Schleife konnte nicht ohne Lücken ausgeführt werden.

  3. Verwenden setNextMediaPlayer(nextmp) So funktioniert es, aber nur zwei Loops sind möglich. Nach Abschluss der letzten beiden Schleifen müssen wir uns vorbereiten und erneut beginnen.

    mp1.start();
    mp1.setNextMediaPlayer(mp2);
    
  4. Aktualisieren: Ergebnis der Antwort von @Jeff Mixon: Mediaplayer-Loop wird mit einem Android-Fehler beendet. Jeff Mixon funktioniert gut, aber nur für 10 oder 20Schleifen danach, aufgrund eines Müllsammelproblems stoppt der Mediaplayers sofort und hinterlässt die Protokolle wie unten angegeben. Ich bin wirklich seit 2 Jahren hier festgeblieben. Vielen Dank im Voraus.

    E/MediaPlayer(24311): error (1, -38)
    E/MediaPlayer(23256): Error(1,-1007)
    E/MediaPlayer(23546): Error (1,-2147483648)
    

Antworten:

28 für die Antwort № 1

Nach dem von mir durchgeführten Test funktioniert diese Lösung gut, über 150 Loops mit einer 13 Sekunden langen, 160 kbps MP3 ohne Probleme:

public class LoopMediaPlayer {

public static final String TAG = LoopMediaPlayer.class.getSimpleName();

private Context mContext = null;
private int mResId = 0;
private int mCounter = 1;

private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer = null;

public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}

private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId = resId;

mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});

createNextMediaPlayer();
}

private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}

private MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;

createNextMediaPlayer();

Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
}

Benutzen LoopMediaPlayer Sie können einfach anrufen:

LoopMediaPlayer.create(context, R.raw.sample);

12 für die Antwort № 2

Hässlicher Proof-of-Concept-Code, aber Sie bekommen die Idee:

// Will need this in the callbacks
final AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.sample);

// Build and start first player
final MediaPlayer player1 = MediaPlayer.create(this, R.raw.sample);
player1.start();

// Ready second player
final MediaPlayer player2 = MediaPlayer.create(this, R.raw.sample);
player1.setNextMediaPlayer(player2);

player1.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {

// When player1 completes, we reset it, and set up player2 to go back to player1 when it"s done
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}

player2.setNextMediaPlayer(player1);
}
});
player2.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
// Likewise, when player2 completes, we reset it and tell it player1 to user player2 after it"s finished again
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}

player1.setNextMediaPlayer(player2);
}
});

// This loop repeats itself endlessly in this fashion without gaps

Dies funktionierte für mich auf einem API 19-Gerät und einem 5-Sekunden-128-Kbps-MP3. Keine Lücken in der Schleife.


3 für die Antwort № 3

Zumindest ab KitKat, Mattia Maestrinis Antwort (zu dieser Frage) ist das Als einzige Lösung habe ich herausgefunden, dass ein lückenloses Looping eines großen (> 1 MB unkomprimierten) Audio-Samples möglich ist. Ich habe es versucht:

Indem Sie einfach Maestrinis s einschließen LoopMediaPlayer Klasse in meinem Projekt und dann meine ersetzen MediaPlayer.create() Anrufe mit LoopMediaPlayer.create() Bei Anrufen kann ich sicherstellen, dass mein .OGG-Beispiel nahtlos durchlaufen wird. LoopMediaPlayer ist daher eine empfehlenswert praktische und transparente Lösung.

Aber diese Transparenz wirft die Frage auf: Einmal tausche ich meine MediaPlayer fordert auf LoopMediaPlayer ruft an, wie ruft meine Instanz an MediaPlayer Methoden wie.isPlaying, .pause oder .setVolume? Unten ist meine Lösung für dieses Problem. Möglicherweise kann es von jemandem verbessert werden, der Java-versierter ist als ich (und ich freue mich über deren Input), aber bisher habe ich dies als eine zuverlässige Lösung gefunden.

Die einzigen Änderungen, die ich an Maestrinis Klasse vornehme(abgesehen von einigen von Lint empfohlenen Verbesserungen) sind am Ende des Codes unten angegeben den Rest füge ich für den Kontext ein. Mein Zusatz ist die Implementierung mehrerer Methoden von MediaPlayer innerhalb LoopMediaPlayer indem Sie sie anrufen mCurrentPlayer.

Vorbehalt: während ich mehrere nützliche Methoden von implementiere MediaPlayer unten, Ich setze nicht alle um. Also, wenn Sie zum Beispiel erwarten, anzurufen .attachAuxEffect Sie müssen dies selbst als Methode hinzufügen LoopMediaPlayer nach dem, was ich hinzugefügt habe. Stellen Sie sicher, dass Sie die ursprünglichen Schnittstellen dieser Methoden (d. H. Parameter, Throws und Returns) replizieren:

public class LoopMediaPlayer {

private static final String TAG = LoopMediaPlayer.class.getSimpleName();

private Context mContext = null;
private int mResId   = 0;
private int mCounter = 1;

private MediaPlayer mCurrentPlayer = null;
private MediaPlayer mNextPlayer    = null;

public static LoopMediaPlayer create(Context context, int resId) {
return new LoopMediaPlayer(context, resId);
}

private LoopMediaPlayer(Context context, int resId) {
mContext = context;
mResId   = resId;

mCurrentPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mCurrentPlayer.start();
}
});
createNextMediaPlayer();
}

private void createNextMediaPlayer() {
mNextPlayer = MediaPlayer.create(mContext, mResId);
mCurrentPlayer.setNextMediaPlayer(mNextPlayer);
mCurrentPlayer.setOnCompletionListener(onCompletionListener);
}

private final MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
mCurrentPlayer = mNextPlayer;
createNextMediaPlayer();
Log.d(TAG, String.format("Loop #%d", ++mCounter));
}
};
// code-read additions:
public boolean isPlaying() throws IllegalStateException {
return mCurrentPlayer.isPlaying();
}

public void setVolume(float leftVolume, float rightVolume) {
mCurrentPlayer.setVolume(leftVolume, rightVolume);
}

public void start() throws IllegalStateException {
mCurrentPlayer.start();
}

public void stop() throws IllegalStateException {
mCurrentPlayer.stop();
}

public void pause() throws IllegalStateException {
mCurrentPlayer.pause();
}

public void release() {
mCurrentPlayer.release();
mNextPlayer.release();
}

public void reset() {
mCurrentPlayer.reset();
}
}

2 für die Antwort № 4

So etwas sollte funktionieren. Behalten Sie zwei Kopien derselben Datei im Verzeichnis res.raw. Bitte beachten Sie, dass dies nur ein POC und kein optimierter Code ist. Ich habe das gerade getestet und es funktioniert wie vorgesehen. Lass mich wissen was du denkst.

public class MainActivity extends Activity {
MediaPlayer mp1;
MediaPlayer mp2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp1 = MediaPlayer.create(MainActivity.this, R.raw.demo);
mp2 = MediaPlayer.create(MainActivity.this, R.raw.demo2);

mp1.start();

Thread thread = new Thread(new Runnable() {

@Override
public void run() {
int duration = mp1.getDuration();
while (mp1.isPlaying() || mp2.isPlaying()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
duration = duration - 100;
if (duration < 1000) {
if (mp1.isPlaying()) {
mp2.start();
mp1.reset();
mp1 = MediaPlayer.create(MainActivity.this,
R.raw.demo);
duration = mp2.getDuration();

} else {
mp1.start();
mp2.reset();
mp2 = MediaPlayer.create(MainActivity.this,
R.raw.demo2);
duration = mp1.getDuration();
}
}
}
}

});

thread.start();
}
}

2 für die Antwort № 5

Ich schlage vor, Sie zu benutzen SoundPool API statt MediaPlayer.

Aus der offiziellen Dokumentation:

Die SoundPool-Klasse verwaltet und spielt Audio-Ressourcen für Anwendungen.

...

Sounds können durch Einstellen einer Nicht-Null-Schleife wiederholt werdenSchleife Wert. Der Wert -1 bewirkt, dass der Sound für immer in einer Schleife abgespielt wird. In diesem Fall, Die Anwendung muss die Funktion stop () explizit aufrufen, um die Funktion anzuhalten klingen. Jeder andere Wert ungleich Null führt dazu, dass der Ton das Signal wiederholt eine bestimmte Anzahl von Malen, z.B. Bei einem Wert von 3 wird der Sound abgespielt insgesamt 4 mal.

...

Schau mal Hier für ein praktisches Beispiel für die Verwendung SoundPool.


1 für die Antwort № 6

Aus irgendeinem Grund fand ich das mein "Bei Fertigstellung" Das Ereignis schoss immer im Bruchteil einer Sekunde spät wenn Sie versuchen, eine 8-Sekunden-OGG-Datei zu wiederholen. Versuchen Sie Folgendes, wenn diese Verzögerung auftritt.

Es ist möglich zu gewaltsam Schlange ein "nextMediaPlayer" wie in bisherigen Lösungen empfohlen, indem Sie einfach eine verzögert lauffähig zu einem Handler für Ihre MediaPlayers und Vermeiden Sie das Einschleifen von onCompletion Veranstaltung insgesamt.

Dies funktioniert einwandfrei für mich mit meinem 8-Sekunden-OGG mit 160 KBit / s, mindestens API 16.

Erstellen Sie an einer beliebigen Stelle in Ihrer Aktivität / Dienstleistung eine HandlerThread & Handler...

private HandlerThread SongLooperThread = new HandlerThread("SongLooperThread");
private Handler SongLooperHandler;

public void startSongLooperThread(){
SongLooperThread.start();
Looper looper = SongLooperThread.getLooper();
SongLooperHandler = new Handler(looper){
@Override
public void handleMessage(Message msg){
//do whatever...
}
}
}

public void stopSongLooperThread(){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){
SongLooperThread.quit();
} else {
SongLooperThread.quitSafely();
}
}`

...den Thread startenerkläre und Richten Sie Ihre MediaPlayers ein...

@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();

startSongLooperThread();

activeSongResID = R.raw.some_loop;
activeMP = MediaPlayer.create(getApplicationContext(), activeSongResID);
activeSongMilliseconds = activeMP.getDuration();

queuedMP = MediaPlayer.create(getApplicationContext(),activeSongResID);
}

@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
stopSongLooperThread();

activeMP.release();
queuedMP.release();
activeMP = null;
queuedMP = null;
}

...ein ... kreieren Methode zum Tauschen Ihre MediaPlayers ...

private void swapActivePlayers(){
Log.v("SongLooperService","MediaPlayer swap started....");
queuedMP.start();

//Immediately get the Duration of the current track, then queue the next swap.
activeSongMilliseconds = queuedMP.getDuration();
SongLooperHandler.postDelayed(timedQueue,activeSongMilliseconds);
Log.v("SongLooperService","Next call queued...");

activeMP.release();

//Swap your active and queued MPs...
Log.v("SongLooperService","MediaPlayers swapping....");
MediaPlayer temp = activeMP;
activeMP = queuedMP;
queuedMP = temp;

//Prepare your now invalid queuedMP...
queuedMP = MediaPlayer.create(getApplicationContext(),activeSongResID);
Log.v("SongLooperService","MediaPlayer swapped.");
}

...erstellen Runnables in deinem Thread posten ...

private Runnable startMP = new Runnable(){
public void run(){
activeMP.start();
SongLooperHandler.postDelayed(timedQueue,activeSongMilliseconds);
}
};

private Runnable timedQueue = new Runnable(){
public void run(){
swapActivePlayers();
}
};

Starten Sie in den Diensten von StartCommand () oder irgendwo in Ihrer Aktivität den MediaPlayer ...

...
SongLooperHandler.post(startMP);
...