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
Related
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'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
I am writing a utility application using open source java based PDFBox to convert PDF file containing 'Hyperlink to open an mp3 file' to replace it with sound object.
I used PDFBox API since it appears to be mature enough to work with Sound object. I could read the PDF file and find the hyperlink with reference to mp3. But I am not able to replace it with sound object. I created the Sound Object and associate with action but it does not work. I think I am missing some important part how to create Sound object using PDActionSound object. Is it possible to refer to external wav file using PDFBox API?
for (PDPage pdPage : pages) {
List<PDAnnotation> annotations = pdPage.getAnnotations();
for (PDAnnotation pdAnnotation : annotations) {
if (pdAnnotation instanceof PDAnnotationLink) {
PDAnnotationLink link = ((PDAnnotationLink) pdAnnotation);
PDAction action = link.getAction();
if (action instanceof PDActionLaunch) {
PDActionLaunch launch = ((PDActionLaunch) action);
String fileInfo = launch.getFile().getFile();
if (fileInfo.contains(".mp3")) {
/* create Sound object referring to external mp3*/
//something like
PDActionSound actionSound = new PDActionSound(
soundStream);
//set the ActionSound to the link.
link.setAction(actionSound);
}
}
}
}
}
How to create sound object (PDActionSound) and add to link successfully?
Speaking of mature, that part has never been used, and now that I had a closer look at the code, I think some work remains to be done... Please try this, I created this with PDFBox 2.0 after reading the PDF specification:
PDSimpleFileSpecification fileSpec = new PDSimpleFileSpecification(new COSString("/C/dir1/dir2/blah.mp3")); // see "File Specification Strings" in PDF spec
COSStream soundStream = new COSStream();
soundStream.createOutputStream().close();
soundStream.setItem(COSName.F, fileSpec);
soundStream.setInt(COSName.R, 44100); // put actual sample rate here
PDActionSound actionSound = new PDActionSound();
actionSound.getCOSObject().setItem(COSName.getPDFName("Sound"), soundStream));
link.setAction(actionSound); // reassign the new action to the link annotation
edit: as the above didn't work, here's an alternative solution as requested in the comments. The file is embedded. It works only with .WAV files, and you have to know details of them. About 1/2 seconds are lost at the beginning. The sound you should hear is "I am Al Bundy". I tried with MP3 and didn't succeed. While googling, I found some texts saying that only "old" formats (wav, aif etc) are supported. I did find another way to play sounds ("Renditions") that even worked with embedded mp3 in another product, but the generated structure in the PDF is even more complex.
COSStream soundStream = new COSStream();
OutputStream os = soundStream.createOutputStream(COSName.FLATE_DECODE);
URL url = new URL("http://cd.textfiles.com/hackchronii/WAV/ALBUNDY1.WAV");
InputStream is = url.openStream();
// FileInputStream is = new FileInputStream(".....WAV");
IOUtils.copy(is, os);
is.close();
os.close();
// See p. 506 in PDF spec, Table 294
soundStream.setInt(COSName.C, 1); // channels
soundStream.setInt(COSName.R, 22050); // sampling rate
//soundStream.setString(COSName.E, "Signed"); // The encoding format for the sample data
soundStream.setInt(COSName.B, 8); // The number of bits per sample value per channel. Default value: 8
// soundStream.setName(COSName.CO, "MP3"); // doesn't work
PDActionSound actionSound = new PDActionSound();
actionSound.getCOSObject().setItem(COSName.getPDFName("Sound"), soundStream);
link.setAction(actionSound);
Update 9.7.2016:
We discussed this on the PDFBox mailing list, and thanks to Gilad Denneboom we know two more things:
1) in Adobe Acrobat it only lets you select either WAV or AIF files
2) code by Gilad Denneboom with MP3SPI to convert MP3 to raw:
private static InputStream getAudioStream(String filename) throws Exception {
File file = new File(filename);
AudioInputStream in = AudioSystem.getAudioInputStream(file);
AudioFormat baseFormat = in.getFormat();
AudioFormat decodedFormat = new AudioFormat(
AudioFormat.Encoding.PCM_UNSIGNED,
baseFormat.getSampleRate(),
baseFormat.getSampleSizeInBits(),
baseFormat.getChannels(),
baseFormat.getChannels(),
baseFormat.getSampleRate(),
false);
return AudioSystem.getAudioInputStream(decodedFormat, in);
}
I'm trying to implement my own version of streaming. I'm sending byte arrays over a websocket. Once I get the first message I write it to a temporary and using android's MediaPlayer to play the file. For the first message everything works fine, I turn the byte array into an mp3 and audio comes out. However I'm not really sure how to keep writing to the file every time a message comes over.
some example code
File test;
FileOutputStream fos;
MediaPlayer mediaPlayer;
FileInputStream MyFile;
Everytime a message comes through this code gets run.
try {
if (fos == null) {
test = File.createTempFile("TCL", "mp3", getCacheDir());
fos = new FileOutputStream(test);
fos.write(bytearray);
mediaPlayer = new MediaPlayer();
MyFile = new FileInputStream(test);
mediaPlayer.setDataSource(MyFile.getFD());
mediaPlayer.prepare();
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}else{
fos.write(bytearray);
}
} catch (IOException ex) {
ex.printStackTrace();
}
I thought I could just keep writing incoming byte[]'s to the file but that doesn't seem to be working. Any advice would be appreciated.
What you're trying to do (play the audio in a file that keeps growing indefinitely) is not supported by MediaPlayer. Instead, look into decoding the audio yourself and sending the raw PCM data to AudioTrack. It's a lot more work, but AudioTrack is the easiest way to progressively play a stream of audio data.
I currently have a Loop back program for testing Audio on Android devices.
It uses AudioRecord and AudioTrack to record PCM audio from the Mic and play PCM audio out the earpiece.
Here is the code:
public class Record extends Thread
{
static final int bufferSize = 200000;
final short[] buffer = new short[bufferSize];
short[] readBuffer = new short[bufferSize];
public void run() {
isRecording = true;
android.os.Process.setThreadPriority
(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
int buffersize = AudioRecord.getMinBufferSize(11025,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
arec = new AudioRecord(MediaRecorder.AudioSource.MIC,
11025,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
buffersize);
atrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
11025,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
buffersize,
AudioTrack.MODE_STREAM);
atrack.setPlaybackRate(11025);
byte[] buffer = new byte[buffersize];
arec.startRecording();
atrack.play();
while(isRecording) {
arec.read(buffer, 0, buffersize);
atrack.write(buffer, 0, buffer.length);
}
}
}
So as you can see in the creation of the AudioTrack and AudioRecord the Encoding is supplied via the AudioFormat but this only allows 16 bit or 8 bit PCM.
I have my own G711 Codec implementation now and I want to be able to encode the audio from the Mic and decode it going into the EarPiece, So I have encode(short lin[], int offset, byte enc[], int frames) and decode(byte enc[], short lin[], int frames) methods but I'm unsure as to how to use them to encode and the decode the audio from the AudioRecord and AudioTrack.
Can anyone help me or point me in the right direction?
Change your arec.read(buffer, 0, buffersize) call to use the Bytebuffer read() method from AudioRecord.
Once you have your bytes into the ByteBuffer object, then you can just insert your G711 implementation call of encode and use the ByteBuffer.asShortBuffer() method to get your captured PCM data into the encoder.
That would solve your initial question without having to introduce a third party library to do that work for you. (This answer is for future people that come across the question).
My question is why?
In your code above you capture PCM data from the microphone, and write it directly to the buffer for playback.
It doesn't make any sense in your implementation to follow the path of PCM -> G711 (encode) -> G711 (decode) -> PCM. All you are doing is introducing unnecessary processing and latency. Now, if you were going to write encoded data to a file instead of trying to play it through the ear piece that would be a different story but your current code doesn't really seem useful to encode the PCM data.
Introducing your own codec here would only make sense in the context of writing the compressed voice data to a file (recording call data for example in a compressed manner) or sending it over the network or something.
I realize this is a pretty old post. Were you able to get your own G711 working? My own initial thought would be to use a lib compiled for the kernel and use JNI to call it.