I'm developing a sound recognition app on android and am using the MediaRecorder class as well as a tensorflow model. I create the audio file where I will be saving the recorder microphone audio in the onCreate method for the class
audioFile = getExternalCacheDir().getAbsolutePath();
audioFile += "/Recording.3gp";
And I set the output file of the mediaRecorder to this file in the startRecording class
mediaRecorder.setOutputFile(audioFile);
The issue im having is that I need to convert the recording into a series of MFCC values for the model to work, and the MFFC.java class im using requires that the recording be converted to a double array. I'm doing that like this
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(audioFile));
int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0)
{
out.write(buff, 0, read);
}
out.flush();
byte[] bytes = out.toByteArray();
int times = Double.SIZE / Byte.SIZE;
double[] doubleArray = new double[bytes.length / times];
for(int i=0;i<doubleArray.length;i++){
doubleArray[i] = ByteBuffer.wrap(bytes, i*times, times).getDouble();
}
Which is how they said to do it on another stack overflow post. The issue with this is that the audio file im sending the recording(s) to just keeps adding the new recordings to previous ones. This is because I am recording the audio then passing it to my classifier method in a loop like so
while(true){
try {
soundRecognition task = new soundRecognition();
task.execute();
sleep(1500);
}
Solutions I have tried
I have tried to move the creation of the audio to the sound recognition class but I cant do that as it produces errors, specifically mediaRecorder start called in invalid state: 4.
I have tried to overwrite the file using a FileWriter and PrintWriter class, but this didnt work, im assuming because the file is and audio file.
Any help would be appreciated
Related
I'm using the library org.gagravarr:vorbis-java-core:0.8 (https://github.com/Gagravarr/VorbisJava).
I want to get the PCM data from an OGG file and use AudioTrack to play it. Using AudioTrack is a requirement for me because I will later need to concatenate multiple PCM data while it's playing to have the smoothest playback.
As you can see bellow, I tried to set up AudioTrack with data matching the file, read the file's content with the library, and write it directly into the AudioTrack, but the result is no audio when played.
I checked the loop and I'm sure the data is correctly being read.
AudioTrack track = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
)
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(44100)
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
.build()
)
.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
.build();
FileInputStream fileInputStream = new FileInputStream(
this.currentSong.getTrackFile("03")
);
OggFile oggFile = new OggFile(fileInputStream);
OggPacketReader oggPacketReader = oggFile.getPacketReader();
int written = 0;
while (true) {
OggPacket oggPacket = oggPacketReader.getNextPacket();
if (oggPacket == null) break;
byte[] data = oggPacket.getData();
track.write(data, written, data.length);
written += data.length;
}
track.play();
And here is some information about the file I'm trying to read:
Am I even using the appropriate library for this? I recently saw something called MediaCodec to use low-level codecs, but I'm not sure where to start...
Currently, AudioTrack doesn't support the Vorbis format. So, you need to decode your source audio track into raw PCM before feeding it into the AudioTrack.
This can be done using MediaExtractor and MediaCodec (decoder).
Links:
https://developer.android.com/reference/android/media/MediaExtractor
https://developer.android.com/reference/android/media/MediaCodec#asynchronous-processing-using-buffers
I am using TarsosDSP library to apply a low pass filter on a wav file. Here is the code.
private void eq2(File file) throws FileNotFoundException {
new AndroidFFMPEGLocator(this);
InputStream inputStream = new FileInputStream(file);
TarsosDSPAudioFormat format = new TarsosDSPAudioFormat(16000,16,2,true,false);
AudioDispatcher adp = new AudioDispatcher(new UniversalAudioInputStream(inputStream,format),2048,0);
adp.addAudioProcessor(new LowPassFS(100,16000));
RandomAccessFile raf = null;
raf = new RandomAccessFile(Environment.getExternalStorageDirectory()+ "/cibiodLogs/audioFiltered1.wav", "rw");
adp.addAudioProcessor(new WriterProcessor(format,raf));
adp.run();
Thread audioThread = new Thread(adp, "Audio Thread");
audioThread.start();
}
It gives output but the output is choppy and not even filtered.
Here take a look at the original wav file and the output wav file.
Original
Filtered
I have tried different buffer sizes from 2 - 4096 but every time either the output is choppy or the audio is not filtered. Can someone point me what might be going wrong here.
This issue is solved now! The TarsosDSP does not support dual-channel audio processing so the algorithm was getting confused with two channels of audio and thus giving the choppy results.
I am trying to read a file into a byte buffer in android. The application crashes whenever I initialize the byte buffer with the size equal to the size of the file. I have checked correctly and the file size is well below the int max value. Because of certain project setup, I have to test the application on the device due to which I don't have the access to the logcat.
File outputdir = new File(localcontext.getFilesDir(), "appData");
if(!outputdir.exists()){
if(outputdir.mkdir()){
Toast.makeText(localcontext, outputdir.getAbsolutePath(), Toast.LENGTH_SHORT).show();
}
}
tempfile = new File(outputdir, "runningfile.mp4");
bytebuffer = new byte[(int)encryptedfile.length()];
OutputStream os = new FileOutputStream(tempfile.getAbsolutePath(), false);
// DataInputStream dataInputStream = new DataInputStream(fis);
// dataInputStream.readFully(bytebuffer);
// dataInputStream.close();
The application runs fine and displays some message when I comment out the byte buffer initialization line but crashes otherwise.
I am unable to figure out what's wrong here. Please help. Thanks.
Use try catch block and display error with toast.
try{
bytebuffer = new byte[(int)encryptedfile.length()];
}catch(Exception e){
Toast.makeText(getActivity(), e.getMessage(),Toast.LENGTH_LONG).show();
}
I have a file movie.mp4 that I wish to create a preview of and play in a VideoView. For the purpose of this question let's say movie.mp4 is 30 megabytes.
I want to extract the first 10 megabytes of video from movie.mp4 and save it in a temporary file. I will feed this file into the VideoView and play it.
This is what I've done so far:
// Get movie file.
File f = getMovieFile("movie.mp4");
// Create temporary file.
File t = File.createTempFile("preview", "mp4");
t.deleteOnExit();
// Create input stream.
InputStream is = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(is, 2048);
// Create output stream to temp file.
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(t));
byte[] buffer = new byte[16384];
int totalRead = 0;
int bytesRead;
int maxBytes = (int) Math.pow(2, 20) * 10; // 10 megabytes
// Write data from input to output.
while ((bytesRead = bis.read(buffer)) != -1 && totalRead < maxBytes) {
bos.write(buffer, 0, bytesRead);
bos.flush();
totalRead += bytesRead;
}
bos.close();
// Play the preview.
mVideoView.setVideoPath(t.getAbsolutePath());
mVideoView.start();
When I run this bit of code Android complains and tells me that the file is unplayable. If I remove the 10 megabyte restriction and let the loop run its course, the VideoView will play the file just fine.
I'm guessing that when the preview is created and only data from one part of the file is copied, some important meta-data from the original mp4 file is missing in the new one. This is just a guess though.
The problem could be in your video,
a partial mp4 must have metadata at the beginning of the file.
While there are tons of articles about encoding MPEG 4 videos with FFmpeg, most of them fail to warn you about something: most video players will not start the playback until the metadata is available (meaning if that data is placed at the back-end of file, as done by some encoders, the whole video must be downloaded first to reach the metadata & playback begins).
This example use ffmpeg for create a mp4 faststart video:
ffmpeg.exe -i INPUT_FILE_NAME_HERE -c:v libx264 -profile:v baseline
-preset slow -b:v 800k -c:a libvo_aacenc -movflags faststart OUTPUT_FILE_NAME.mp4
Yes, this question has been asked a millions times, and I believe I've looked at them all. They are very "sometimesy", slow, or not what I need.
On one project, I use the following code to use the InputStream received from a GET to turn that into a PDF. This works PERFECTLY, every time, on my physical device and my emulator (Genymotion 2.1.1, Emulator API 18 4.3). Note that some things are edited out, and the PDFs are generally small, less than 1 MB.
public abstract class MyPDFFile extends File implements ApiModel{
public MyPDFFile(InputStream inputStream){
super(context.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS), "my_pdf.pdf");
if (externalStorageIsWritable()) {
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
FileOutputStream fileInputStream = new FileOutputStream(this);
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileInputStream));
int readLine;
char[] cbuf = new char[1];
do {
readLine = bufferedReader.read(cbuf);
bufferedWriter.write(cbuf);
} while (readLine != -1);
bufferedWriter.close();
}
catch(IOException e){
// Didn't work
}
}
else{
// Cant write
}
}
I figured on this new project, I could use the same code to download an APK from the internet to the device. Nope, definitely not the case. I eventually tried this for Inputstream to File:
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] buffer = new byte[1];
while ( (read(buffer)) > 0 ) {
fileOutputStream.write(buffer);
}
fileOutputStream.close();
close();
That works on my emulator, and works fine. I moved to testing on my device... not so much, which is weird, because the working PDF code works on both my emulator and device. I've tried adjusting the size of my buffer to various multiples of 512 (which results in the file being EXTREMELY small, like a few KB, to being EXTREMELY large, about double the apk size, which is about 5.6 MB).
Also, another weird thing: I can NEVER get it to successfully save outside of the constructor. When I do the saving there, the InputStream is fine, my file gets created, yadayada, and when I use successful code, I just rename the file afterwards since all I have access to in the constructor is the InputStream. If I decide "No, I want to name it when I have the proper things" and simply save the InputStream to my object, it NEVER works properly. Can never get above 4KB for the downloaded file. I've tried extends InputStream and extends BufferedInputStream to no avail.
I can post more code if needed. All I would have access to is the InputStream from my GET request; I'm using the browep Android HTTP library and that's all I can get without trying to mess with the library itself (or overriding methods in it).
The problem is that you're reading the file byte by byte. This can take ton of time. Instead, read the file in bigger piece of chunks, like 4 or 8 KBs:
int file_chunk_size = 1024 * 4; //4KBs, written like this to easily change it to 8
byte[] buffer = new byte[file_chunk_size];
int bytesRead = 0;
while ( (bytesRead = read(buffer)) > 0 ) {
fileOutputStream.write(buffer, 0, bytesRead);
}