Working on a testing project, my current objective is to use an int to specify which mp3 file to be played upon the initialization of the app without knowing the name of soundtracks beforehand (Scalability issue as I want to be able to add new mp3 files to the randomization pool without touching the code in future). However, when I run the following code, an error occurred with the following description:
error: no suitable method found for create(MainActivity,String) method MediaPlayer.create(Context,Uri) is not applicable (argument mismatch; String cannot be converted to Uri) method MediaPlayer.create(Context,int) is not applicable (argument mismatch; String cannot be converted to int)
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.media.MediaPlayer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.widget.ArrayAdapter;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
RecyclerView bensonRecycler;
ArrayList<String> arrayList;
ArrayAdapter bensonAdapter;
MediaPlayer bensonPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AssetManager assetManager = getAssets();
String[] audios = assetManager.list("sound");
int i = 1;
final MediaPlayer mp = MediaPlayer.create(this, audios[i]);
mp.start();
}
}
My assets of mp3 files have the following structure:
So my question is what should I do in order to use the int i to specify which mp3 soundtrack to play (without knowing name of mp3 soundtracks beforehand) among the array of mp3 assets? (I must use a variable to specify the soundtrack to play in this testing project)
The "assets" folder is not really a folder at all but a bundled stream of bytes. That is why there is the AssetManager.
AssetManager
Provides access to an application's raw asset files; see Resources for the way most applications will want to retrieve their resource data. This class presents a lower-level API that allows you to open and read raw files that have been bundled with the application as a simple stream of bytes.
The AssetManager lets us build a string array of files easily enough and we can view those files as belonging to a smallish kind of file system. The trick is how to present these files (really a stream of bytes) to MediaPlayer
so the sound can be played based upon an index into the sound assets.
The following code will play an indexed sound. Comments in the code explain how it works. To test, I have just built a simple layout with three buttons that play sound at indices 0, 1 and 2.
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private void playSoundFromAssets(int index) {
try {
AssetManager assetManager = getAssets();
String[] audios = assetManager.list(SOUNDFILE_PATH);
if (audios == null || index >= audios.length) {
return;
}
String soundFilePath = new File(SOUNDFILE_PATH, audios[index]).getPath();
AssetFileDescriptor afd = getAssets().openFd(soundFilePath);
final MediaPlayer mp = new MediaPlayer();
/* For API 24+, we can just use the AssetFileDescriptor to play the sound. However,
for API 23-, we can't use the AssetFileDescriptor directly but can retrieve a
FileDescriptor from it that points to the beginning of our assets. The offset
and length from the AssetFileDescriptor serve for the FileDescriptor as well.
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mp.setDataSource(afd);
} else {
FileDescriptor fd = afd.getFileDescriptor();
Log.d("MainActivity", String.format("<<<< %s %d %d", soundFilePath, afd.getStartOffset(), afd.getLength()));
mp.setDataSource(fd, afd.getStartOffset(), afd.getLength());
// One might think that mp.setDataSource(fd) would play the sound file we want, but
// it actually plays all sound files one after another. It seems that fd is a
// FileDescriptor that points to the beginning of the assets.
}
afd.close();
mp.prepare();
mp.start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void playSound(View view) {
switch (view.getId()) {
case R.id.play0:
playSoundFromAssets(0);
break;
case R.id.play1:
playSoundFromAssets(1);
break;
case R.id.play2:
playSoundFromAssets(2);
break;
}
}
private static final String SOUNDFILE_PATH = "sound";
}