Created
December 6, 2017 12:43
-
-
Save MensObscura/b6776b68e09713e5df537c5e578b3cb5 to your computer and use it in GitHub Desktop.
Read sound file (mp3 at least)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.content.res.AssetFileDescriptor; | |
import android.media.AudioFormat; | |
import android.media.AudioManager; | |
import android.media.AudioTrack; | |
import android.media.MediaCodec; | |
import android.media.MediaCodec.BufferInfo; | |
import android.media.MediaCodecInfo; | |
import android.media.MediaExtractor; | |
import android.media.MediaFormat; | |
import android.util.Log; | |
import java.io.IOException; | |
import java.nio.ByteBuffer; | |
/** | |
* from https://github.com/taehwandev/MediaCodecExample/blob/master/src/net/thdev/mediacodecexample/decoder/AudioDecoderThread.java | |
*/ | |
public class AudioDecoder { | |
private static final int TIMEOUT_US = 1000; | |
private MediaExtractor mExtractor; | |
private MediaCodec mDecoder; | |
private boolean eosReceived; | |
private int mSampleRate = 0; | |
private String mMime; | |
/** | |
* @param context | |
* @param ressourceId | |
*/ | |
public void startPlay(Context context, int ressourceId) { | |
eosReceived = false; | |
mExtractor = new MediaExtractor(); | |
try { | |
final AssetFileDescriptor afd = context.getResources().openRawResourceFd(ressourceId); | |
mExtractor.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
int channel = 0; | |
for (int i = 0; i < mExtractor.getTrackCount(); i++) { | |
MediaFormat format = mExtractor.getTrackFormat(i); | |
String mime = format.getString(MediaFormat.KEY_MIME); | |
if (mime.startsWith("audio/")) { | |
mExtractor.selectTrack(i); | |
Log.d("AudioDecoder", "format : " + format); | |
mMime = format.getString(MediaFormat.KEY_MIME); | |
mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); | |
channel = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); | |
break; | |
} | |
} | |
MediaFormat format = makeAACCodecSpecificData(MediaCodecInfo.CodecProfileLevel.AACObjectLC, mSampleRate, channel); | |
if (format == null) { | |
return; | |
} | |
try { | |
mDecoder = MediaCodec.createDecoderByType(mMime); | |
mDecoder.configure(format, null, null, 0); | |
if (mDecoder == null) { | |
Log.e("AudioDecoder", "Can't find video info!"); | |
return; | |
} | |
mDecoder.start(); | |
new Thread(AACDecoderAndPlayRunnable).start(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
/** | |
* The code profile, Sample rate, channel Count is used to | |
* produce the AAC Codec SpecificData. | |
* Android 4.4.2/frameworks/av/media/libstagefright/avc_utils.cpp refer | |
* to the portion of the code written. | |
* | |
* MPEG-4 Audio refer : http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config | |
* | |
* @param audioProfile is MPEG-4 Audio Object Types | |
* @return MediaFormat | |
*/ | |
private MediaFormat makeAACCodecSpecificData(int audioProfile, int sampleRate, int channelConfig) { | |
MediaFormat format = new MediaFormat(); | |
format.setString(MediaFormat.KEY_MIME, mMime); | |
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate); | |
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channelConfig); | |
int samplingFreq[] = { | |
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, | |
16000, 12000, 11025, 8000 | |
}; | |
// Search the Sampling Frequencies | |
int sampleIndex = -1; | |
for (int i = 0; i < samplingFreq.length; ++i) { | |
if (samplingFreq[i] == sampleRate) { | |
Log.d("TAG", "kSamplingFreq " + samplingFreq[i] + " i : " + i); | |
sampleIndex = i; | |
} | |
} | |
if (sampleIndex == -1) { | |
return null; | |
} | |
ByteBuffer csd = ByteBuffer.allocate(2); | |
csd.put((byte) ((audioProfile << 3) | (sampleIndex >> 1))); | |
csd.position(1); | |
csd.put((byte) ((byte) ((sampleIndex << 7) & 0x80) | (channelConfig << 3))); | |
csd.flip(); | |
format.setByteBuffer("csd-0", csd); // add csd-0 | |
for (int k = 0; k < csd.capacity(); ++k) { | |
Log.e("TAG", "csd : " + csd.array()[k]); | |
} | |
return format; | |
} | |
Runnable AACDecoderAndPlayRunnable = new Runnable() { | |
@Override | |
public void run() { | |
AACDecoderAndPlay(); | |
} | |
}; | |
/** | |
* After decoding AAC, Play using Audio Track. | |
*/ | |
public void AACDecoderAndPlay() { | |
ByteBuffer[] inputBuffers = mDecoder.getInputBuffers(); | |
ByteBuffer[] outputBuffers = mDecoder.getOutputBuffers(); | |
BufferInfo info = new BufferInfo(); | |
int buffsize = AudioTrack.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT); | |
// create an audiotrack object | |
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate, | |
AudioFormat.CHANNEL_OUT_STEREO, | |
AudioFormat.ENCODING_PCM_16BIT, | |
buffsize, | |
AudioTrack.MODE_STREAM); | |
audioTrack.play(); | |
while (!eosReceived) { | |
int inIndex = mDecoder.dequeueInputBuffer(TIMEOUT_US); | |
if (inIndex >= 0) { | |
ByteBuffer buffer = inputBuffers[inIndex]; | |
int sampleSize = mExtractor.readSampleData(buffer, 0); | |
if (sampleSize < 0) { | |
// We shouldn't stop the playback at this point, just pass the EOS | |
// flag to mDecoder, we will get it again from the | |
// dequeueOutputBuffer | |
Log.d("AudioDecoder", "InputBuffer BUFFER_FLAG_END_OF_STREAM"); | |
mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); | |
} else { | |
mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mExtractor.getSampleTime(), 0); | |
mExtractor.advance(); | |
} | |
int outIndex = mDecoder.dequeueOutputBuffer(info, TIMEOUT_US); | |
switch (outIndex) { | |
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: | |
Log.d("AudioDecoder", "INFO_OUTPUT_BUFFERS_CHANGED"); | |
outputBuffers = mDecoder.getOutputBuffers(); | |
break; | |
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: | |
MediaFormat format = mDecoder.getOutputFormat(); | |
Log.d("AudioDecoder", "New format " + format); | |
audioTrack.setPlaybackRate(format.getInteger(MediaFormat.KEY_SAMPLE_RATE)); | |
break; | |
case MediaCodec.INFO_TRY_AGAIN_LATER: | |
Log.d("AudioDecoder", "dequeueOutputBuffer timed out!"); | |
break; | |
default: | |
ByteBuffer outBuffer = outputBuffers[outIndex]; | |
Log.v("AudioDecoder", "We can't use this buffer but render it due to the API limit, " + outBuffer); | |
final byte[] chunk = new byte[info.size]; | |
outBuffer.get(chunk); // Read the buffer all at once | |
outBuffer.clear(); // ** MUST DO!!! OTHERWISE THE NEXT TIME YOU GET THIS SAME BUFFER BAD THINGS WILL HAPPEN | |
audioTrack.write(chunk, info.offset, info.offset + info.size); // AudioTrack write data | |
mDecoder.releaseOutputBuffer(outIndex, false); | |
break; | |
} | |
// All decoded frames have been rendered, we can stop playing now | |
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { | |
Log.d("AudioDecoder", "OutputBuffer BUFFER_FLAG_END_OF_STREAM"); | |
break; | |
} | |
} | |
} | |
mDecoder.stop(); | |
mDecoder.release(); | |
mDecoder = null; | |
mExtractor.release(); | |
mExtractor = null; | |
audioTrack.stop(); | |
audioTrack.release(); | |
audioTrack = null; | |
} | |
public void stop() { | |
eosReceived = true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyFragment extends Fragment{ | |
/*...*/ | |
private void readEndSound() { | |
AudioDecoder audio = new AudioDecoder(); | |
audio.startPlay(getContext(), R.raw.my_sound); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment