- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我想在毫秒范围内播放 mp3 声音文件。所以我尝试按照描述这样做 question.
但我收到的结果对我来说几乎很好。声音文件具有不同的参数,例如采样率...
所以,我从 ringdroid 获取代码图书馆。这是两个java文件,解码器和播放器,基于AudioTrack.java
玩家:
public class CustomPlayer {
public interface OnCompletionListener {
public void onCompletion();
}
private ShortBuffer mSamples;
private int mSampleRate;
private int mChannels;
private int mNumSamples; // Number of samples per channel.
private AudioTrack mAudioTrack;
private short[] mBuffer;
private int mPlaybackStart; // Start offset, in samples.
private Thread mPlayThread;
private boolean mKeepPlaying;
private OnCompletionListener mListener;
public CustomPlayer(ShortBuffer samples, int sampleRate, int channels, int numSamples) {
mSamples = samples;
mSampleRate = sampleRate;
mChannels = channels;
mNumSamples = numSamples;
mPlaybackStart = 0;
int bufferSize = AudioTrack.getMinBufferSize(
mSampleRate,
mChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
// make sure minBufferSize can contain at least 1 second of audio (16 bits sample).
if (bufferSize < mChannels * mSampleRate * 2) {
bufferSize = mChannels * mSampleRate * 2;
}
mBuffer = new short[bufferSize / 2]; // bufferSize is in Bytes.
mAudioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
mSampleRate,
mChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
mBuffer.length * 2,
AudioTrack.MODE_STREAM);
// Check when player played all the given data and notify user if mListener is set.
mAudioTrack.setNotificationMarkerPosition(mNumSamples - 1); // Set the marker to the end.
mAudioTrack.setPlaybackPositionUpdateListener(
new AudioTrack.OnPlaybackPositionUpdateListener() {
@Override
public void onPeriodicNotification(AudioTrack track) {
}
@Override
public void onMarkerReached(AudioTrack track) {
stop();
if (mListener != null) {
mListener.onCompletion();
}
}
});
mPlayThread = null;
mKeepPlaying = true;
mListener = null;
}
public CustomPlayer(SoundFile sf) {
this(sf.getSamples(), sf.getSampleRate(), sf.getChannels(), sf.getNumSamples());
}
public void setOnCompletionListener(OnCompletionListener listener) {
mListener = listener;
}
public boolean isPlaying() {
return mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING;
}
public boolean isPaused() {
return mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED;
}
public void start() {
if (isPlaying()) {
return;
}
mKeepPlaying = true;
mAudioTrack.flush();
mAudioTrack.play();
// Setting thread feeding the audio samples to the audio hardware.
// (Assumes mChannels = 1 or 2).
mPlayThread = new Thread() {
public void run() {
int position = mPlaybackStart * mChannels;
mSamples.position(position);
int limit = mNumSamples * mChannels;
while (mSamples.position() < limit && mKeepPlaying) {
int numSamplesLeft = limit - mSamples.position();
if (numSamplesLeft >= mBuffer.length) {
mSamples.get(mBuffer);
} else {
for (int i = numSamplesLeft; i < mBuffer.length; i++) {
mBuffer[i] = 0;
}
mSamples.get(mBuffer, 0, numSamplesLeft);
}
// TODO(nfaralli): use the write method that takes a ByteBuffer as argument.
mAudioTrack.write(mBuffer, 0, mBuffer.length);
}
}
};
mPlayThread.start();
}
public void pause() {
if (isPlaying()) {
mAudioTrack.pause();
// mAudioTrack.write() should block if it cannot write.
}
}
public void stop() {
if (isPlaying() || isPaused()) {
mKeepPlaying = false;
mAudioTrack.pause(); // pause() stops the playback immediately.
mAudioTrack.stop(); // Unblock mAudioTrack.write() to avoid deadlocks.
if (mPlayThread != null) {
try {
mPlayThread.join();
} catch (InterruptedException e) {
}
mPlayThread = null;
}
mAudioTrack.flush(); // just in case...
}
}
public void release() {
stop();
mAudioTrack.release();
}
public void seekTo(int msec) {
boolean wasPlaying = isPlaying();
stop();
mPlaybackStart = (int) (msec * (mSampleRate / 1000.0));
if (mPlaybackStart > mNumSamples) {
mPlaybackStart = mNumSamples; // Nothing to play...
}
mAudioTrack.setNotificationMarkerPosition(mNumSamples - 1 - mPlaybackStart);
if (wasPlaying) {
start();
}
}
public int getCurrentPosition() {
return (int) ((mPlaybackStart + mAudioTrack.getPlaybackHeadPosition()) *
(1000.0 / mSampleRate));
}
}
解码器:
public class SoundFile {
private ProgressListener mProgressListener = null;
private String mFileType;
private int mFileSize;
private int mAvgBitRate; // Average bit rate in kbps.
private int mSampleRate;
private int mChannels;
private int mNumSamples; // total number of samples per channel in audio file
private ShortBuffer mDecodedSamples; // shared buffer with mDecodedBytes.
private int mNumFrames;
private int[] mFrameGains;
// Progress listener interface.
public interface ProgressListener {
boolean reportProgress(double fractionComplete);
}
// Custom exception for invalid inputs.
public class InvalidInputException extends Exception {
private static final long serialVersionUID = -2505698991597837165L;
public InvalidInputException(String message) {
super(message);
}
}
public static String[] getSupportedExtensions() {
return new String[]{"mp3"};
}
public static boolean isFilenameSupported(String filename) {
String[] extensions = getSupportedExtensions();
for (int i = 0; i < extensions.length; i++) {
if (filename.endsWith("." + extensions[i])) {
return true;
}
}
return false;
}
public String getFiletype() {
return mFileType;
}
public int getFileSizeBytes() {
return mFileSize;
}
public int getAvgBitrateKbps() {
return mAvgBitRate;
}
public int getSampleRate() {
return mSampleRate;
}
public int getChannels() {
return mChannels;
}
public int getNumSamples() {
return mNumSamples; // Number of samples per channel.
}
// Should be removed when the app will use directly the samples instead of the frames.
public int getNumFrames() {
return mNumFrames;
}
// Should be removed when the app will use directly the samples instead of the frames.
public int getSamplesPerFrame() {
return 1024; // just a fixed value here...
}
// Should be removed when the app will use directly the samples instead of the frames.
public int[] getFrameGains() {
return mFrameGains;
}
public ShortBuffer getSamples() {
if (mDecodedSamples != null) {
return mDecodedSamples.asReadOnlyBuffer();
} else {
return null;
}
}
private SoundFile() {
}
private void setProgressListener(ProgressListener progressListener) {
mProgressListener = progressListener;
}
public static SoundFile readMp3File(File file, int mSecStart) {
SoundFile soundFile = new SoundFile();
try {
soundFile.readFile(file, mSecStart);
} catch (IOException | InvalidInputException e) {
Log.e(SoundFile.class.getName(), "read File", e);
}
return soundFile;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void readFile(File inputFile, int mSecStart) throws java.io.IOException, InvalidInputException {
MediaExtractor extractor = new MediaExtractor();
MediaFormat format = null;
int i;
File mInputFile = inputFile;
String[] components = mInputFile.getPath().split("\\.");
mFileType = components[components.length - 1];
mFileSize = (int) mInputFile.length();
extractor.setDataSource(mInputFile.getPath());
int numTracks = extractor.getTrackCount();
// find and select the first audio track present in the file.
for (i = 0; i < numTracks; i++) {
format = extractor.getTrackFormat(i);
if (format.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
extractor.selectTrack(i);
break;
}
}
if (i == numTracks) {
throw new InvalidInputException("No audio track found in " + mInputFile);
}
mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
// Expected total number of samples per channel.
int expectedNumSamples =
(int) ((format.getLong(MediaFormat.KEY_DURATION) / 1000000.f) * mSampleRate + 0.5f);
MediaCodec codec = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME));
codec.configure(format, null, null, 0);
codec.start();
int decodedSamplesSize = 0; // size of the output buffer containing decoded samples.
byte[] decodedSamples = null;
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
int sample_size;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
long presentation_time;
int tot_size_read = 0;
boolean done_reading = false;
ByteBuffer mDecodedBytes = ByteBuffer.allocate(1 << 20);
Boolean firstSampleData = true;
while (true) {
// read data from file and feed it to the decoder input buffers.
int inputBufferIndex = codec.dequeueInputBuffer(100);
if (!done_reading && inputBufferIndex >= 0) {
sample_size = extractor.readSampleData(inputBuffers[inputBufferIndex], 0);
if (firstSampleData
&& format.getString(MediaFormat.KEY_MIME).equals("audio/mp4a-latm")
&& sample_size == 2) {
extractor.advance();
tot_size_read += sample_size;
} else if (sample_size < 0) {
// All samples have been read.
codec.queueInputBuffer(
inputBufferIndex, 0, 0, -1, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
done_reading = true;
} else {
presentation_time = extractor.getSampleTime();
codec.queueInputBuffer(inputBufferIndex, 0, sample_size, presentation_time, 0);
extractor.advance();
tot_size_read += sample_size;
if (mProgressListener != null) {
if (!mProgressListener.reportProgress((float) (tot_size_read) / mFileSize)) {
// We are asked to stop reading the file. Returning immediately. The
// SoundFile object is invalid and should NOT be used afterward!
extractor.release();
extractor = null;
codec.stop();
codec.release();
codec = null;
return;
}
}
}
firstSampleData = false;
}
// Get decoded stream from the decoder output buffers.
int outputBufferIndex = codec.dequeueOutputBuffer(info, 100);
if (outputBufferIndex >= 0 && info.size > 0) {
if (decodedSamplesSize < info.size) {
decodedSamplesSize = info.size;
decodedSamples = new byte[decodedSamplesSize];
}
outputBuffers[outputBufferIndex].get(decodedSamples, 0, info.size);
outputBuffers[outputBufferIndex].clear();
if (mDecodedBytes.remaining() < info.size) {
int position = mDecodedBytes.position();
int newSize = (int) ((position * (1.0 * mFileSize / tot_size_read)) * 1.2);
if (newSize - position < info.size + 5 * (1 << 20)) {
newSize = position + info.size + 5 * (1 << 20);
}
ByteBuffer newDecodedBytes = null;
int retry = 10;
while (retry > 0) {
try {
newDecodedBytes = ByteBuffer.allocate(newSize);
break;
} catch (OutOfMemoryError oome) {
retry--;
}
}
if (retry == 0) {
break;
}
mDecodedBytes.rewind();
newDecodedBytes.put(mDecodedBytes);
mDecodedBytes = newDecodedBytes;
mDecodedBytes.position(position);
}
mDecodedBytes.put(decodedSamples, 0, info.size);
codec.releaseOutputBuffer(outputBufferIndex, false);
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0
|| (mDecodedBytes.position() / (2 * mChannels)) >= expectedNumSamples) {
break;
}
}
mNumSamples = mDecodedBytes.position() / (mChannels * 2); // One sample = 2 bytes.
mDecodedBytes.rewind();
mDecodedBytes.order(ByteOrder.LITTLE_ENDIAN);
mDecodedSamples = mDecodedBytes.asShortBuffer();
mAvgBitRate = (int) ((mFileSize * 8) * ((float) mSampleRate / mNumSamples) / 1000);
extractor.release();
extractor = null;
codec.stop();
codec.release();
codec = null;
mDecodedSamples.rewind();
}
public int getOneMillisecondBytes() {
return (int) (1000.0 * getSamplesPerFrame()) / (mSampleRate * 2);
}
}
我使用的这段代码:
SoundFile soundFile = SoundFile.readMp3File(audio, getTimeFromDate(start));
player = new CustomPlayer(soundFile);
player.seekTo(getTimeFromDate(start));
player.start();
final Timer timer = new Timer();
playerWatcher = new TimerTask() {
@Override
public void run() {
if (player == null) {
timer.cancel();
} else if (player.getCurrentPosition() >= finishMS) {
player.stop();
timer.cancel();
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (!doesReplicaHasExercise) {
onNextDialog();
} else {
DialogEditableTextView dialogEditableTextView = (DialogEditableTextView)
dialogMaker.lines.get(dialogMaker.lines.size() - 1);
dialogEditableTextView.startEditingFirstItem();
}
}
});
}
}
};
timer.schedule(playerWatcher, 0, 5);
所以,声音播放就像我预期的那样。声音中的单词按照我想要的方式播放,范围毫秒。但我还有另一个问题,那就是文件解码时间太长。我不知道如何处理音频文件。我如何才能只解码我需要播放的部分???请帮助我。
最佳答案
看来您读取了所有声音数据:
while (true) {
// read data from file and feed it to the decoder input buffers.
int inputBufferIndex = codec.dequeueInputBuffer(100);
if (!done_reading && inputBufferIndex >= 0) {
sample_size = extractor.readSampleData(inputBuffers[inputBufferIndex], 0);
if (firstSampleData
&& format.getString(MediaFormat.KEY_MIME).equals("audio/mp4a-latm")
&& sample_size == 2) {
extractor.advance();
tot_size_read += sample_size;
} else if (sample_size < 0) {
// All samples have been read.
codec.queueInputBuffer(
inputBufferIndex, 0, 0, -1, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
done_reading = true;
} else {
presentation_time = extractor.getSampleTime();
codec.queueInputBuffer(inputBufferIndex, 0, sample_size, presentation_time, 0);
extractor.advance();
tot_size_read += sample_size;
if (mProgressListener != null) {
if (!mProgressListener.reportProgress((float) (tot_size_read) / mFileSize)) {
// We are asked to stop reading the file. Returning immediately. The
// SoundFile object is invalid and should NOT be used afterward!
extractor.release();
extractor = null;
codec.stop();
codec.release();
codec = null;
return;
}
}
}
firstSampleData = false;
}
关于java - android音频文件在毫秒范围内播放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36937169/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!