I've created an app that records audio, saves the sample to the sd card then plays it back, using the record and play buttons. I need to reverse this sample. I can do all this and the the reversed sample is saved on the SD card under a different name. The original sample is test.wav and the same sample reversed is save as revFile.wav. when i try play revFile.wav android says it can't play this format.
I've litterally put the sample in an array then reversed the contents, something is telling me that there could be header info at the start of the sample that needs striping first, any ideas. thanks.
Here's what i have so far.
public class recorder extends Activity {
MediaRecorder myRecorder = null;
DataInputStream dis = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void onClickPlay(View v){
Log.v("onClickplay", "play clicked");
try{
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/test.wav");
mp.prepare();
mp.start();
} catch(Exception e3) {
e3.printStackTrace();
}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("playing");
}
public void onClickRecord(View v){
Log.v("onClickRecord", "record clicked");
File path = Environment.getExternalStorageDirectory();
Log.v("file path", ""+path.getAbsolutePath());
File file = new File(path, "test.wav");
if(file.exists()){
file.delete();
}
path.mkdirs();
Log.v("file path", ""+file.getAbsolutePath());
myRecorder = new MediaRecorder();
myRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
myRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
myRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
myRecorder.setOutputFile(file.getAbsolutePath());
Log.i("myrecorder", "about to prepare recording");
try{
myRecorder.prepare();
} catch(Exception e) {
e.printStackTrace();
}
Log.i("myrecorder", "prepared");
myRecorder.start(); // Recording is now started
Log.i("myrecorder", "recording");
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("recording");
}
public void onClickStop(View v){
Log.v("onClickStop", "stop clicked");
try{
myRecorder.stop();
myRecorder.reset(); // You can reuse the object by going back to setAudioSource() step
myRecorder.release(); // Now the object cannot be reused
}catch(Exception e){}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("recording stopped");
}
public void onClickReverse(View v){
Log.v("onClickReverse", "reverse clicked");
File f = Environment.getExternalStorageDirectory();
String path = f.getAbsolutePath();
path = path + "/test.wav";
Log.v("path = ", ""+path);
Log.v("dir = ", ""+f.getAbsolutePath());
Log.v("test file exists? = ", ""+f.getAbsolutePath()+"/test.wav");
File f2 = new File(path);
Log.v("f2 = ", ""+f2.getAbsolutePath());
try {
InputStream is = new FileInputStream(f2);
BufferedInputStream bis = new BufferedInputStream(is);
dis = new DataInputStream(bis);
} catch (Exception e) {
e.printStackTrace();
}
int fileLength = (int)f2.length();
byte[] buffer = new byte[fileLength];
/*File reversedFile = Environment.getExternalStorageDirectory();
File revFile = new File(reversedFile, "reversedFile.wav");
Log.v("reversedfile path", ""+ revFile.getAbsolutePath());
if(revFile.exists()){
revFile.delete();
}
reversedFile.mkdirs();
*/
byte[] byteArray = new byte[fileLength +1];
Log.v("bytearray size = ", ""+byteArray.length);
try {
while(dis.read(buffer) != -1 ) {
dis.read(buffer);
Log.v("about to read buffer", "buffer");
byteArray = buffer;
}
Log.v(" buffer size = ", ""+ buffer.length);
} catch (IOException e) {
e.printStackTrace();
}
byte[] tempArray = new byte[fileLength];
int j=0;
for (int i=byteArray.length-1; i >=0; i--) {
tempArray[ j++ ] = byteArray[i];
}
File revPath = Environment.getExternalStorageDirectory();
Log.v("revpath path", ""+revPath.getAbsolutePath());
File revFile = new File(revPath, "revFile.wav");
Log.v("revfile path ", ""+revFile.getAbsolutePath());
if(revFile.exists()){
revFile.delete();
}
revPath.mkdirs();
try {
OutputStream os = new FileOutputStream(revFile);
BufferedOutputStream bos = new BufferedOutputStream(os);
DataOutputStream dos = new DataOutputStream(bos);
Log.v("temparray size = ", ""+ tempArray.length);
dos.write(tempArray);
dos.flush();
dos.close();
} catch (Exception e) {
e.printStackTrace();
}
try{
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(Environment.getExternalStorageDirectory().getPath()
+"/revFile.wav");
mp.prepare();
mp.start();
} catch(Exception e3) {
e3.printStackTrace();
}
TextView text = (TextView)findViewById(R.id.TextView01);
text.setText("playing reversed file");
}
}// end of onclickrev
The WAV file format includes a 44-byte header chunk. Most WAV files consist of this 44 byte header, followed by the actual sample data. So, to reverse a WAV file, you should first copy the 44 byte header from the original file, and then copy the inverted sample data from the original after the header. If you just reverse the byte order of the original entire file, it definitely won't work. It also won't work if you copy the header and then reverse the byte order of the remainder of the file (actually it will sort of work, except what you get will be just noise). You actually need to reverse the frames, where the frame size is dependent on the bytes-per-sample and whether the file is stereo or mono (for example, if the file is stereo and 2 bytes per sample, then each frame is 4 bytes).
Note that not all WAV files are "canonical" like this. WAV files are actually a variant of RIFF files, so technically you need much more complicated code to find the various parts of the header and sample data within the original file. However, most WAV files are just the header followed by the samples (and this will certainly be true if you're recording the audio yourself), in which case you can save yourself a lot of work.
Joe Cullity's link is a good description of the WAV file format.
There is a good description of the .WAV header at link text
Also note that all data in the file is stored as Little Endian order (low order byte of 1 multi byte number is stored at the lowest address....) so you can't just reverse the bytes. you need to see how many bytes wide each sample is, (usually 16, but check the header) and reverse them in chunks of that size
I'm pretty sure that you're right and that the problem is that you can't just reverse the bytes in the file to reverse the waveform because you're destroying header information. You should try to see if there's a good library out there to do this, since I have little experience working with .wav files.
Related
I need some help with my Java application. Its purpose is to read a certain website, so I need to play many audio files in a row. The JAR is compiled using Java 8. I tested my application with Windows 11 and Java 16.0.1, everything works fine. Then I used the latest Ubuntu Linux and Java 11.0.13 as well as Java 8: It plays some audio, but not every file.
I wrote a test class and the result was, that - no matter in which order I play the audio - only the first (exactly!) 62 files are played. Every next file (even the ones, that were successfully played at first) produces the exception my code throws at this position:
if (mixerSelected != null) {
audioClip0 = AudioSystem.getClip(mixerSelected);
} else {
throw new IllegalArgumentException("File is not compatible: '" + audioFilePath + "'.");
}
I ensured that every audio file is .WAV with
8k sample rate,
16k Bytes per second in average,
16 Bits, and
pcm_s16le codec.
My application is built as JAR-file including my audio files in the resources directory.
This is my code:
public class PlayAudio {
/**
* plays an audio file
*
* #param audioFilePath String: path to the audio file
* #param speed double: speed applied to the audios
*/
public boolean singleFile(String audioFilePath, double speed) {
//audioFilePath = "audio" + File.separator + audioFilePath;
audioFilePath = "audio" + "/" + audioFilePath;
AudioInputStream audioStream0;
//create new file using path to the audio
try {
//load files from resources folder as stream
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(audioFilePath);
InputStream bufferedInputStream = new BufferedInputStream(inputStream);
if (bufferedInputStream == null) {
throw new IllegalArgumentException("File not found: '" + audioFilePath + "'.");
} else {
//create new AudioStream
audioStream0 = AudioSystem.getAudioInputStream(bufferedInputStream);
}
} catch (IllegalArgumentException e) {
//handle
return false;
} catch (IOException e) {
//handle
return false;
} catch (UnsupportedAudioFileException e) {
//handle
return false;
}
try {
//create new AudioFormat
AudioFormat audioFormat0 = audioStream0.getFormat();
//create new Info
DataLine.Info info0 = new DataLine.Info(Clip.class, audioFormat0);
//initialize new Mixer
Mixer.Info mixerSelected = null;
for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) {
Mixer mixer = AudioSystem.getMixer(mixerInfo);
if (mixer.isLineSupported(info0)) {
mixerSelected = mixerInfo;
break;
}
}
//create new Clip
Clip audioClip0;
if (mixerSelected != null) {
audioClip0 = AudioSystem.getClip(mixerSelected);
} else {
//THIS EXCEPTION GETS THROWN!!!
throw new IllegalArgumentException("File is not compatible: '" + audioFilePath + "'.");
}
//open created Clips via created AudioStream
audioClip0.open(audioStream0);
//start the play of audio file
audioClip0.start();
//wait until play completed
double waitTime = (double)((((double)audioClip0.getMicrosecondLength()/1000.0)/speed + 50.0) * 0.8);
Thread.sleep((long)waitTime);
return true;
//handle exceptions
} catch (LineUnavailableException e) {
//handle
return false;
} catch (IOException e) {
//handle
return false;
} catch (InterruptedException e) {
//handle
return false;
} catch (IllegalArgumentException e) {
//THIS EXCEPTION GETS THROWN!!!
//handle invalid audio clips
System.out.println(e);
e.printStackTrace();
return false;
}
}
/**
* plays multiple audio files in the order they are stored in an ArrayList
*
* #param fileNames ArrayList<String>: list with filenames of audio files to play
* #param speaker String: speaker to use for playing the audios (can be 'm' or 'w')
* #param speed double: speed applied to the audios
* #return boolean: true if playing audios completed successfully, otherwise false
*/
public static boolean multiFiles(ArrayList<String> fileNames, String speaker, double speed) {
PlayAudio player = new PlayAudio();
//play every audio file in the array of file names
for (int i = 0; (i < fileNames.toArray().length); i ++) {
//generate file names
String fullFileName = speaker + "_" + fileNames.toArray()[i];
//play audio
player.singleFile(fullFileName, speed);
}
return true;
}
}
What did I already try?
I tried it on another computer that runs Ubuntu Linux as well.
I created a new instance of PlayAudio() everytime a new audio is played.
I used audioClip0.stop(); after every audio.
I increased the milliseconds of sleep after every audio to length of the audio plus 1 second.
I rebuilt the projects ... nearly 1k times.
How can I reproduce the error?
I simply need to play more than 62 audio files running my JAR-file under Linux Ubuntu.
How can you help me?
I don't know how to handle this issue. What is the problem playing .WAV-files with Linux?
Is there a common way to fix this?
(I am not allowed to use any library except OracleJDK and OpenJDK.)
The #1 suggestion is by Mark Rotteveel. The AudioInputStream class needs closing. This is often a surprise for people, because Java is well known for managing garbage collection. But for AudioInputStream there are resources that need to be released. The API doesn't do an adequate job of pointing this out, imho, but the need for handling can be inferred from the description for the AudioInputStream.close() method:
Closes this audio input stream and releases any system resources
associated with the stream.
The #2 suggestion is from both Andrew Thompson and Hendrik may be more a helpful hint than a direct solution, but it is still a very good idea, and it seems plausible to me that the inefficiency of all the additional, unneeded infrastructure (ClassLoader, InputStream, BufferedInputStream) might be contributing to the issue. But I really don't have a good enough understanding of the underlying code to know how pertinent that is.
However, I think you can do even better. Don't use Clip. You current use of Clip goes against the concept of its design. Clips are meant for short duration sounds that are to be held in memory and played multiple times, not files that are repeatedly reloaded before each playback. The proper class for this sort of use (load and play) is the SourceDataLine.
An example of playback using a SourceDataLine can be found in the javasound wiki. This example also illustrates the use of URL for obtaining the necessary AudioInputStream. I will quote it here verbatim.
public class ExampleSourceDataLine {
public static void main(String[] args) throws Exception {
// Name string uses relative addressing, assumes the resource is
// located in "audio" child folder of folder holding this class.
URL url = ExampleSourceDataLine.class.getResource("audio/371535__robinhood76__06934-distant-ship-horn.wav");
// The wav file named above was obtained from https://freesound.org/people/Robinhood76/sounds/371535/
// and matches the audioFormat.
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
AudioFormat audioFormat = new AudioFormat(
Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
int bytesRead = 0;
byte[] buffer = new byte[1024];
sourceDataLine.start();
while((bytesRead = audioInputStream.read(buffer)) != -1)
{
// It is possible at this point manipulate the data in buffer[].
// The write operation blocks while the system plays the sound.
sourceDataLine.write(buffer, 0, bytesRead);
}
sourceDataLine.drain();
// release resources
sourceDataLine.close();
audioInputStream.close();
}
}
You will have to do some editing, as the example was set up to run via a main method. Also, you'll be using your audio format, and that the names of the audio files with their folder locations will have to match the relative or absolute location specified in the argument you use in getResource() method. Also, a larger size for the buffer array might be preferred. (I often use 8192).
But most importantly, notice that in this example, we close both the SourceDataLine and the AudioInputStream. The alternate suggestion to use try-with-resources is a good one and will also release the resources.
If there are difficulties altering the above to fit into your program, I'm sure if you show us what you try, we can help with making it work.
After applying the answer from #Phil Freihofner this worked for me:
/**
* plays an audio file
*
* #param audioFilePath String: path to the audio file
* #param speed double: speed applied to the audios
*/
public boolean singleFile(String audioFilePath) {
//get class
ClassLoader classLoader = getClass().getClassLoader();
//use try-with-resources
//load files from resources folder as stream
try (
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(
new BufferedInputStream(
Objects.requireNonNull(classLoader.getResourceAsStream(audioFilePath))))
) {
if (audioInputStream == null) {
throw new IllegalArgumentException("File not found: '" + audioFilePath + "'.");
}
//create new AudioFormat
AudioFormat audioFormat = audioInputStream.getFormat();
//create new Info
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
//create new SourceDataLine and open it
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
//start the play of the audio file
int bytesRead;
byte[] buffer = new byte[8192];
sourceDataLine.start();
while ((bytesRead = audioInputStream.read(buffer)) != -1) {
sourceDataLine.write(buffer, 0, bytesRead);
}
sourceDataLine.drain();
sourceDataLine.close();
audioInputStream.close();
//return true, because play finished
return true;
} catch (Exception e) {
//ignore exceptions
return false;
}
}
Thak you all for contributing to my solution.
I'm working on an android application and I want to save a screenshot of the application. I can save a single screenshot but it keeps over writing the previous screenshot.
I followed a tutorial and modified it but it does not take more than a single screenshot
Attached here is the code in the button action
case R.id.btn_save:
View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap bitmap = getScreenShot(rootView);
int i = 0;
File file = new File("ScreenShot"+ i +".PNG");
if(!file.exists()){
store(bitmap, "ScreenShot"+ i +".PNG");
}
else {
store(bitmap, "ScreenShot"+ i++ +".PNG");
}
and the screenshot storing function
public void store(Bitmap bm, String fileName){
String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Screenshots";
File dir = new File(dirPath);
if (!dir.exists()){
dir.mkdirs();
}
File file = new File(dirPath,fileName);
try{
FileOutputStream fos = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 100,fos);
fos.flush();
fos.close();
}catch (Exception e){
e.printStackTrace();
Toast.makeText(this, "Error saving File", Toast.LENGTH_SHORT).show();
}
}
You are declaring i variable inside the button save so you will always start with 0 when button is clicked. To use the way you are trying you should declare that variable outside that scope, but it will restart when you kill and reopen the app.
You can use Shared Preferences to save the following number to use (or the last you used) if you want to use that approach. If not you can use simply
"Screenshot" + System.currentTimeInMillis().toString().
You will also have the time when the screenshot was taken (although in millis). If you want you can format it to be like "user readable" 20191110 for example
Because in that code the file name is always the same- i is always 0. To make it work for one use of the app, i should be a member variable and incremented every screenshot. To make it work more generally, you should generate a random name using File.createTempFile()
case R.id.btn_save:
View rootView getWindow().getDecorView().findViewById(android.R.id.content);
Bitmap bitmap = getScreenShot(rootView);
File dir = new File(Environment.getExternalStorageDirectory(), "Screenshots");
if (!dir.exists())
if ( !dir.mkdirs())
{
Toast ( could not create dir...);
return;
}
int i = 0;
while (++i > 0 )
{
String fileName = "ScreenShot"+ i +".png";
File file = new File(dir, fileName);
if(!file.exists())
{
store(bitmap, file);
break;
}
}
break;
Change the parameter of store(Bitmap bm, String fileName) to store(Bitmap bm, File file)
There you can remove all code before the try block.
I am writing a camera application for the android platform. I am using the CameraKitView Library for producing my camera view. Everything else including accessing the camera is working as expected except for actually capturing and saving the image. minimum sdk is 15 and target sdk and compile sdk is 28. The code for saving the image is as shown below
cameraKitView.captureImage(new CameraKitView.ImageCallback() {
#Override
public void onImage(CameraKitView cameraKitView, byte[] photo) {
File savedPhoto = new File(Environment.getExternalStorageDirectory(), "pchk.jpg");
try{
FileOutputStream outputStream = new FileOutputStream(savedPhoto.getPath());
outputStream.write(photo);
outputStream.close();
}catch (java.io.IOException e){
e.printStackTrace();
}
}
});
Thank you in advance for your assistance
First of all verify that your file or folder where you want to save a file is exists or not, if not create them
capturedFolderPath is a path of the folder where you want to create the file
File folderFile = new File(capturedFolderPath)
if (!folderFile.exists()) {
folderFile.mkdirs();
}
String imageNameToSave = "xyz.jpg";
String imagePath = capturedFolderPath
+ File.separator + imageNameToSave + ".jpg";
File photoFile = new File(imagePath);
if (!photoFile.exists()) {
photoFile.createNewFile();
}
after creating write the bytes on the file like this
FileOutputStream outputStream = new FileOutputStream(photoFile);
outputStream.write(bytes);
outputStream.close();
I'm having a problem for a few days now and I can't find the solution. I've been looking on the internet for hours but I can't find any answer that works for me.
I'm making an app where I have to send pictures to a webservice. I'm using Fishbun to access to the phone gallery and pick some pictures. When the pictures are picked, I use Bitmapfactory.decodeFile() with the returned path from Fishbun to display the pictures on the screen. It works good. The path at this moment is:
/storage/emulated/0/DCIM/Camera/IMG_20160321_044346.jpg
Then, I save the path in a ArrayList which will be send to the webservice.
When I use BitmapFactory.decodeFile() on the path from the ArrayList, it returns null. The path is exactly the same as the first time, but it seems like I can't decode it twice.
If I restart the app and take back the same picture as the first time in fishbun, the decodeFile() also returns null. Here is the code ....
Fishbun activity result:
protected void onActivityResult(int requestCode, int resultCode,
Intent imageData) {
super.onActivityResult(requestCode, resultCode, imageData);
switch (requestCode) {
case Define.ALBUM_REQUEST_CODE:
if (resultCode == RESULT_OK) {
path = imageData.getStringArrayListExtra(Define.INTENT_PATH);
int i = 0;
int error=0;
while(i<path.size()){
Bitmap bmp = BitmapFactory.decodeFile(path.get(i));
if(bmp.getWidth()<bmp.getHeight()) // bmp is not null the first time, null the second time
{
error++;
path.remove(i);
i--;
}
i++;
}
if(error>=1){
Toast.makeText(newStep7.this,R.string.rerrors, Toast.LENGTH_LONG).show();
}
if(path.size()>0) {
mainAdapter.changePath(path); //display the picture on the screen without changing the path, the method name is kinda wrong
}
break;
}
}
}
This is the decodeFile() before sending my request:
String imgPath = rep.getImgList().get(0);
File file = new File(imgPath);
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
Log.d("startedfromthebottom", file.getAbsolutePath()); //show the same path as in the activityresult above
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); //bitmap is always null
bitmap.compress(Bitmap.CompressFormat.JPEG,100,os);
Could you help me find why my bitmap is null when I decode the same file twice ?
EDIT:
I found the image file in my Android Device Monitor. The image size is 0 and can't be used anymore after the Outputstream
UPDATE: Little code change after applying bwt answer:
String imgPath = rep.getImgList().get(0);
File file = new File(imgPath);
AtomicFile atomicFile =
new AtomicFile(file);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = atomicFile.startWrite();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
Log.d("showmethebitmap", bitmap.toString()); //Error: bitmap is null !
oos = new ObjectOutputStream(fos);
bitmap.compress(Bitmap.CompressFormat.JPEG,0,oos);
oos.writeObject(bitmap);
oos.flush();
atomicFile.finishWrite(fos);
...
} catch (IOException e) {
atomicFile.failWrite(fos);
throw e;
} finally {
if (oos != null) oos.close();
}
I know the question has been answered ,however if someone still getting null make sure that the application has permission to read user's files
You should load the image first, then open the output stream, as this erases the existing content.
Log.d("startedfromthebottom", file.getAbsolutePath()); //show the same path as in the activityresult above
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); //bitmap is always null
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG,100,os);
This is a bit dangerous because if something goes wrong the image is lost. It could be a good idea to use an AtomicFile.
Edit (based on your updated code) :
String imgPath = rep.getImgList().get(0);
File file = new File(imgPath);
AtomicFile atomicFile = new AtomicFile(file);
FileOutputStream fos = null;
try {
// read the current image
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
// open the stream (backup the current content)
// from now on (and until finishWrite/failWrite) we cannot read the file directly
fos = atomicFile.startWrite();
Log.d("showmethebitmap", bitmap.toString()); //Error: bitmap is null !
OutputStream oos = new BufferedOutputStream(fos);
bitmap.compress(Bitmap.CompressFormat.JPEG,0,oos);
// flush but do not close the stream (#see AtomicFile doc)
oos.flush();
// close the stream, remove the backup
atomicFile.finishWrite(fos);
...
} catch (IOException e) {
// recover the content from the backup
atomicFile.failWrite(fos);
throw e;
}
I have a ZIP file of 140 MB containing about 40 thousand MP3 files. I use the following code to play a certain file inside the ZIP file without decompressing it:
String fullPath = Environment.getExternalStorageDirectory().getPath() + "_audio_.mp3";
String path = Environment.getExternalStorageDirectory().getPath() + "mySoundFolder";
try {
ZipFile zip = new ZipFile(path + "myFile.zip");
Enumeration zipEntries = zip.entries();
ZipEntry entry = zip.getEntry("myFile" + "/" + currentWord + ".mp3");
if (entry != null) {
Log.i(MAIN_TAG, "entry found: " + entry.getName());
InputStream in = zip.getInputStream(entry);
File f = new File(fullPath);
FileOutputStream out = new FileOutputStream(f);
IOUtils.copy(in, out);
byte buffer[] = new byte[4096];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
if (f.exists())
{
Log.i(MAIN_TAG,"Audio file found!");
final MediaPlayer mp = new MediaPlayer();
mp.setDataSource(fullPath);
mp.prepare();
mp.setOnBufferingUpdateListener(null);
mp.setLooping(false);
mp.setOnPreparedListener(new OnPreparedListener()
{ public void onPrepared(MediaPlayer arg0)
{
mp.start();
Log.i(MAIN_TAG,"Pronunciation finished!");
}});
}
else
{
Log.i(MAIN_TAG,"File doesn't exist!!");
}
}
else {
// no such entry in the zip
Log.i(MAIN_TAG, "no such entry in the zip");
}
}
catch (IOException e) {
e.printStackTrace();
Log.i(MAIN_TAG,"IOException reading zip file");
}
}
There are two strange things with this code:
It works flawlessly in Android 2.2 but fails in Android 4.0.3. In 2.2, it finds and plays the MP3 file as I expect, but in 4.0.3, it keeps saying it cannot find the entry in the ZIP file ("no such entry in the zip").
If I reduce the number of MP3 files down to about 100 files, then in Android 4.0.3, it finds and plays the selected MP3 files as it should do.
Can you guys please help me to figure out what the problem is?
Thanks a lot in advance.
In the end, I have a workaround for this problem. I split my zip file into two files, with each containing about 20k entries. Voila, it works like a charm again.
I've heard of Java's problem with reading entries in zip files of more than 64k entries. What I have no idea why is my file has only about 40k entries but it faces the problem as well.