parent
709b994c7d
commit
17b18af513
@ -0,0 +1,154 @@ |
||||
//
|
||||
// Created by frank on 2018/2/1.
|
||||
//
|
||||
#include <jni.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
//封装格式
|
||||
#include "libavformat/avformat.h" |
||||
//解码
|
||||
#include "libavcodec/avcodec.h" |
||||
//缩放
|
||||
#include "libswscale/swscale.h" |
||||
//重采样
|
||||
#include "libswresample/swresample.h" |
||||
#include <android/log.h> |
||||
|
||||
#define TAG "MediaPlayer" |
||||
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO, TAG, FORMAT, ##__VA_ARGS__); |
||||
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR, TAG, FORMAT, ##__VA_ARGS__); |
||||
|
||||
#define MAX_AUDIO_FRAME_SIZE 48000 * 4 |
||||
|
||||
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_AudioPlayer_play |
||||
(JNIEnv *env, jobject jthiz, jstring input_jstr){ |
||||
const char* input_cstr = (*env)->GetStringUTFChars(env,input_jstr,NULL); |
||||
LOGI("input_cstr=%s", input_cstr); |
||||
//注册组件
|
||||
av_register_all(); |
||||
AVFormatContext *pFormatCtx = avformat_alloc_context(); |
||||
//打开音频文件
|
||||
if(avformat_open_input(&pFormatCtx,input_cstr,NULL,NULL) != 0){ |
||||
LOGI("%s","无法打开音频文件"); |
||||
return; |
||||
} |
||||
//获取输入文件信息
|
||||
if(avformat_find_stream_info(pFormatCtx,NULL) < 0){ |
||||
LOGI("%s","无法获取输入文件信息"); |
||||
return; |
||||
} |
||||
//获取音频流索引位置
|
||||
int i = 0, audio_stream_idx = -1; |
||||
for(; i < pFormatCtx->nb_streams;i++){ |
||||
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){ |
||||
audio_stream_idx = i; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
//获取音频解码器
|
||||
AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec; |
||||
AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id); |
||||
if(codec == NULL){ |
||||
LOGI("%s","无法获取解码器"); |
||||
return; |
||||
} |
||||
//打开解码器
|
||||
if(avcodec_open2(codecCtx,codec,NULL) < 0){ |
||||
LOGI("%s","无法打开解码器"); |
||||
return; |
||||
} |
||||
//压缩数据
|
||||
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); |
||||
//解压缩数据
|
||||
AVFrame *frame = av_frame_alloc(); |
||||
//frame->16bit 44100 PCM 统一音频采样格式与采样率
|
||||
SwrContext *swrCtx = swr_alloc(); |
||||
|
||||
//输入的采样格式
|
||||
enum AVSampleFormat in_sample_fmt = codecCtx->sample_fmt; |
||||
//输出采样格式16bit PCM
|
||||
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; |
||||
//输入采样率
|
||||
int in_sample_rate = codecCtx->sample_rate; |
||||
//输出采样率
|
||||
int out_sample_rate = in_sample_rate; |
||||
//声道布局(2个声道,默认立体声stereo)
|
||||
uint64_t in_ch_layout = codecCtx->channel_layout; |
||||
//输出的声道布局(立体声)
|
||||
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO; |
||||
|
||||
swr_alloc_set_opts(swrCtx, |
||||
out_ch_layout,out_sample_fmt,out_sample_rate, |
||||
in_ch_layout,in_sample_fmt,in_sample_rate, |
||||
0, NULL); |
||||
swr_init(swrCtx); |
||||
|
||||
//输出的声道个数
|
||||
int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout); |
||||
|
||||
jclass player_class = (*env)->GetObjectClass(env,jthiz); |
||||
if(!player_class){ |
||||
LOGE("player_class not found..."); |
||||
} |
||||
//AudioTrack对象
|
||||
jmethodID audio_track_method = (*env)->GetMethodID(env,player_class,"createAudioTrack","(II)Landroid/media/AudioTrack;"); |
||||
if(!audio_track_method){ |
||||
LOGE("audio_track_method not found..."); |
||||
} |
||||
jobject audio_track = (*env)->CallObjectMethod(env,jthiz,audio_track_method,out_sample_rate,out_channel_nb); |
||||
|
||||
//调用play方法
|
||||
jclass audio_track_class = (*env)->GetObjectClass(env,audio_track); |
||||
jmethodID audio_track_play_mid = (*env)->GetMethodID(env,audio_track_class,"play","()V"); |
||||
(*env)->CallVoidMethod(env,audio_track,audio_track_play_mid); |
||||
|
||||
//获取write()方法
|
||||
jmethodID audio_track_write_mid = (*env)->GetMethodID(env,audio_track_class,"write","([BII)I"); |
||||
|
||||
//16bit 44100 PCM 数据
|
||||
uint8_t *out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE); |
||||
|
||||
int got_frame = 0,index = 0, ret; |
||||
//不断读取编码数据
|
||||
while(av_read_frame(pFormatCtx,packet) >= 0){ |
||||
//解码音频类型的Packet
|
||||
if(packet->stream_index == audio_stream_idx){ |
||||
//解码
|
||||
ret = avcodec_decode_audio4(codecCtx,frame,&got_frame,packet); |
||||
if(ret < 0){ |
||||
break; |
||||
} |
||||
//解码一帧成功
|
||||
if(got_frame > 0){ |
||||
LOGI("decode frame count=%d",index++); |
||||
//音频格式转换
|
||||
swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)frame->data,frame->nb_samples); |
||||
int out_buffer_size = av_samples_get_buffer_size(NULL, out_channel_nb, |
||||
frame->nb_samples, out_sample_fmt, 1); |
||||
|
||||
jbyteArray audio_sample_array = (*env)->NewByteArray(env,out_buffer_size); |
||||
jbyte* sample_byte_array = (*env)->GetByteArrayElements(env,audio_sample_array,NULL); |
||||
//拷贝缓冲数据
|
||||
memcpy(sample_byte_array, out_buffer, (size_t) out_buffer_size); |
||||
//释放数组
|
||||
(*env)->ReleaseByteArrayElements(env,audio_sample_array,sample_byte_array,0); |
||||
//调用AudioTrack的write方法进行播放
|
||||
(*env)->CallIntMethod(env,audio_track,audio_track_write_mid, |
||||
audio_sample_array,0,out_buffer_size); |
||||
//释放局部引用
|
||||
(*env)->DeleteLocalRef(env,audio_sample_array); |
||||
usleep(1000 * 16); |
||||
} |
||||
} |
||||
av_free_packet(packet); |
||||
} |
||||
LOGI("decode audio finish"); |
||||
av_frame_free(&frame); |
||||
av_free(out_buffer); |
||||
swr_free(&swrCtx); |
||||
avcodec_close(codecCtx); |
||||
avformat_close_input(&pFormatCtx); |
||||
(*env)->ReleaseStringUTFChars(env,input_jstr,input_cstr); |
||||
|
||||
} |
@ -0,0 +1,319 @@ |
||||
//
|
||||
// Created by frank on 2018/2/1.
|
||||
//
|
||||
|
||||
#include <jni.h> |
||||
#include <string.h> |
||||
#include "libavcodec/avcodec.h" |
||||
#include "libavformat/avformat.h" |
||||
#include "libswscale/swscale.h" |
||||
#include "libswresample/swresample.h" |
||||
#include "libavutil/samplefmt.h" |
||||
#include <SLES/OpenSLES.h> |
||||
#include <SLES/OpenSLES_Android.h> |
||||
#include <android/log.h> |
||||
#include <libavutil/opt.h> |
||||
|
||||
#define TAG "OpenSLPlayer" |
||||
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO, TAG, FORMAT,##__VA_ARGS__); |
||||
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR, TAG, FORMAT,##__VA_ARGS__); |
||||
|
||||
//引擎接口
|
||||
SLObjectItf engineObject = NULL; |
||||
SLEngineItf engineEngine; |
||||
|
||||
//输出混音器接口
|
||||
SLObjectItf outputMixObject = NULL; |
||||
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL; |
||||
|
||||
//缓冲播放器接口
|
||||
SLObjectItf bqPlayerObject = NULL; |
||||
SLPlayItf bqPlayerPlay; |
||||
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; |
||||
SLEffectSendItf bqPlayerEffectSend; |
||||
SLVolumeItf bqPlayerVolume; |
||||
|
||||
//音效设置
|
||||
const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; |
||||
void *buffer; |
||||
size_t bufferSize; |
||||
uint8_t *outputBuffer; |
||||
size_t outputBufferSize; |
||||
|
||||
//FFmpeg相关
|
||||
AVPacket packet; |
||||
int audioStream; |
||||
AVFrame *aFrame; |
||||
SwrContext *swr; |
||||
AVFormatContext *aFormatCtx; |
||||
AVCodecContext *aCodecCtx; |
||||
int frame_count = 0; |
||||
|
||||
int createAudioPlayer(int *rate, int *channel, const char *file_name) ; |
||||
|
||||
// 释放相关资源
|
||||
int releaseAudioPlayer(); |
||||
|
||||
// 获取PCM数据, 自动回调获取
|
||||
int getPCM(void **pcm, size_t *pcmSize) ; |
||||
|
||||
//播放回调方法
|
||||
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bufferQueueItf, void *context) { |
||||
bufferSize = 0; |
||||
getPCM(&buffer, &bufferSize); |
||||
//如果buffer不为空,入待播放队列
|
||||
if (NULL != buffer && 0 != bufferSize) { |
||||
SLresult result; |
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, bufferSize); |
||||
if(result < 0){ |
||||
LOGE("Enqueue error..."); |
||||
} else{ |
||||
LOGI("decode frame count=%d", frame_count++); |
||||
} |
||||
} |
||||
} |
||||
|
||||
//创建OpenSLES引擎
|
||||
void createEngine() { |
||||
SLresult result; |
||||
//创建引擎
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); |
||||
LOGI("slCreateEngine=%d", result); |
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); |
||||
LOGI("engineObject->Realize=%d", result); |
||||
//获取引擎接口
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); |
||||
LOGI("engineObject->GetInterface=%d", result); |
||||
//创建输出混音器
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0); |
||||
LOGI("CreateOutputMix=%d", result); |
||||
//关联输出混音器
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); |
||||
LOGI("outputMixObject->Realize=%d", result); |
||||
//获取reverb接口
|
||||
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, |
||||
&outputMixEnvironmentalReverb); |
||||
LOGI("outputMixObject->GetInterface=%d", result); |
||||
if (SL_RESULT_SUCCESS == result) { |
||||
result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties( |
||||
outputMixEnvironmentalReverb, &reverbSettings); |
||||
} |
||||
LOGI("SetEnvironmentalReverbProperties=%d", result); |
||||
} |
||||
|
||||
|
||||
//创建带有缓冲队列的音频播放器
|
||||
void createBufferQueueAudioPlayer(int rate, int channel, int bitsPerSample) { |
||||
SLresult result; |
||||
|
||||
//配置音频源
|
||||
SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; |
||||
SLDataFormat_PCM format_pcm; |
||||
format_pcm.formatType = SL_DATAFORMAT_PCM; |
||||
format_pcm.numChannels = (SLuint32) channel; |
||||
format_pcm.bitsPerSample = (SLuint32) bitsPerSample; |
||||
format_pcm.samplesPerSec = (SLuint32) (rate * 1000); |
||||
format_pcm.containerSize = 16; |
||||
if (channel == 2) |
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; |
||||
else |
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER; |
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; |
||||
SLDataSource audioSrc = {&buffer_queue, &format_pcm}; |
||||
|
||||
//配置音频池
|
||||
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; |
||||
SLDataSink audioSnk = {&loc_outmix, NULL}; |
||||
|
||||
//创建音频播放器
|
||||
const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME}; |
||||
const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; |
||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, |
||||
3, ids, req); |
||||
LOGI("CreateAudioPlayer=%d", result); |
||||
|
||||
//关联播放器
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); |
||||
LOGI("bqPlayerObject Realize=%d", result); |
||||
|
||||
//获取播放接口
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); |
||||
LOGI("GetInterface bqPlayerPlay=%d", result); |
||||
|
||||
//获取缓冲队列接口
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, |
||||
&bqPlayerBufferQueue); |
||||
LOGI("GetInterface bqPlayerBufferQueue=%d", result); |
||||
|
||||
//注册缓冲队列回调
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL); |
||||
LOGI("RegisterCallback=%d", result); |
||||
|
||||
//获取音效接口
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, |
||||
&bqPlayerEffectSend); |
||||
LOGI("GetInterface effect=%d", result); |
||||
|
||||
//获取音量接口
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); |
||||
LOGI("GetInterface volume=%d", result); |
||||
|
||||
//开始播放音乐
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); |
||||
LOGI("SetPlayState=%d", result); |
||||
} |
||||
|
||||
int createAudioPlayer(int *rate, int *channel, const char *file_name) { |
||||
//注册相关组件
|
||||
av_register_all(); |
||||
aFormatCtx = avformat_alloc_context(); |
||||
|
||||
//打开音频文件
|
||||
if (avformat_open_input(&aFormatCtx, file_name, NULL, NULL) != 0) { |
||||
LOGE("Couldn't open file:%s\n", file_name); |
||||
return -1; // Couldn't open file
|
||||
} |
||||
|
||||
//寻找stream信息
|
||||
if (avformat_find_stream_info(aFormatCtx, NULL) < 0) { |
||||
LOGE("Couldn't find stream information."); |
||||
return -1; |
||||
} |
||||
|
||||
//寻找音频stream
|
||||
int i; |
||||
audioStream = -1; |
||||
for (i = 0; i < aFormatCtx->nb_streams; i++) { |
||||
if (aFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && |
||||
audioStream < 0) { |
||||
audioStream = i; |
||||
} |
||||
} |
||||
if (audioStream == -1) { |
||||
LOGE("Couldn't find audio stream!"); |
||||
return -1; |
||||
} |
||||
//获取解码器context
|
||||
aCodecCtx = aFormatCtx->streams[audioStream]->codec; |
||||
//寻找音频解码器
|
||||
AVCodec *aCodec = avcodec_find_decoder(aCodecCtx->codec_id); |
||||
if (!aCodec) { |
||||
fprintf(stderr, "Unsupported codec!\n"); |
||||
return -1; |
||||
} |
||||
//打开解码器
|
||||
if (avcodec_open2(aCodecCtx, aCodec, NULL) < 0) { |
||||
LOGE("Could not open codec."); |
||||
return -1; |
||||
} |
||||
aFrame = av_frame_alloc(); |
||||
// 设置格式转换
|
||||
swr = swr_alloc(); |
||||
av_opt_set_int(swr, "in_channel_layout", aCodecCtx->channel_layout, 0); |
||||
av_opt_set_int(swr, "out_channel_layout", aCodecCtx->channel_layout, 0); |
||||
av_opt_set_int(swr, "in_sample_rate", aCodecCtx->sample_rate, 0); |
||||
av_opt_set_int(swr, "out_sample_rate", aCodecCtx->sample_rate, 0); |
||||
av_opt_set_sample_fmt(swr, "in_sample_fmt", aCodecCtx->sample_fmt, 0); |
||||
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); |
||||
swr_init(swr); |
||||
|
||||
// 分配PCM数据缓存
|
||||
outputBufferSize = 8196; |
||||
outputBuffer = (uint8_t *) malloc(sizeof(uint8_t) * outputBufferSize); |
||||
// 返回sample rate和channels
|
||||
*rate = aCodecCtx->sample_rate; |
||||
*channel = aCodecCtx->channels; |
||||
return 0; |
||||
} |
||||
|
||||
// 获取PCM数据, 自动回调获取
|
||||
int getPCM(void **pcm, size_t *pcmSize) { |
||||
while (av_read_frame(aFormatCtx, &packet) >= 0) { |
||||
int frameFinished = 0; |
||||
//音频流
|
||||
if (packet.stream_index == audioStream) { |
||||
avcodec_decode_audio4(aCodecCtx, aFrame, &frameFinished, &packet); |
||||
//解码完一帧数据
|
||||
if (frameFinished) { |
||||
// data_size为音频数据所占的字节数
|
||||
int data_size = av_samples_get_buffer_size( |
||||
aFrame->linesize, aCodecCtx->channels, |
||||
aFrame->nb_samples, aCodecCtx->sample_fmt, 1); |
||||
|
||||
if (data_size > outputBufferSize) { |
||||
outputBufferSize = (size_t) data_size; |
||||
outputBuffer = (uint8_t *) realloc(outputBuffer, sizeof(uint8_t) * outputBufferSize); |
||||
} |
||||
|
||||
// 音频格式转换
|
||||
swr_convert(swr, &outputBuffer, aFrame->nb_samples, |
||||
(uint8_t const **) (aFrame->extended_data), |
||||
aFrame->nb_samples); |
||||
|
||||
// 返回pcm数据
|
||||
*pcm = outputBuffer; |
||||
*pcmSize = (size_t) data_size; |
||||
return 0; |
||||
} |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
// 释放相关资源
|
||||
int releaseAudioPlayer() { |
||||
av_packet_unref(&packet); |
||||
av_free(outputBuffer); |
||||
av_free(aFrame); |
||||
avcodec_close(aCodecCtx); |
||||
avformat_close_input(&aFormatCtx); |
||||
return 0; |
||||
} |
||||
|
||||
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_AudioPlayer_playAudio |
||||
(JNIEnv * env, jclass jobject, jstring filePath) { |
||||
|
||||
int rate, channel; |
||||
const char *file_name = (*env)->GetStringUTFChars(env, filePath, NULL); |
||||
LOGI("file_name=%s", file_name); |
||||
|
||||
// 创建音频解码器
|
||||
createAudioPlayer(&rate, &channel, file_name); |
||||
|
||||
// 创建播放引擎
|
||||
createEngine(); |
||||
|
||||
// 创建缓冲队列音频播放器
|
||||
createBufferQueueAudioPlayer(rate, channel, SL_PCMSAMPLEFORMAT_FIXED_16); |
||||
|
||||
// 启动音频播放
|
||||
bqPlayerCallback(bqPlayerBufferQueue, NULL); |
||||
} |
||||
|
||||
//停止播放,释放相关资源
|
||||
JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_AudioPlayer_stop |
||||
(JNIEnv * env, jclass jobject) { |
||||
if (bqPlayerObject != NULL) { |
||||
(*bqPlayerObject)->Destroy(bqPlayerObject); |
||||
bqPlayerObject = NULL; |
||||
bqPlayerPlay = NULL; |
||||
bqPlayerBufferQueue = NULL; |
||||
bqPlayerEffectSend = NULL; |
||||
bqPlayerVolume = NULL; |
||||
} |
||||
|
||||
if (outputMixObject != NULL) { |
||||
(*outputMixObject)->Destroy(outputMixObject); |
||||
outputMixObject = NULL; |
||||
outputMixEnvironmentalReverb = NULL; |
||||
} |
||||
|
||||
if (engineObject != NULL) { |
||||
(*engineObject)->Destroy(engineObject); |
||||
engineObject = NULL; |
||||
engineEngine = NULL; |
||||
} |
||||
|
||||
// 释放解码器相关资源
|
||||
releaseAudioPlayer(); |
||||
} |
@ -0,0 +1,45 @@ |
||||
package com.frank.ffmpeg; |
||||
|
||||
import android.media.AudioFormat; |
||||
import android.media.AudioManager; |
||||
import android.media.AudioTrack; |
||||
|
||||
/** |
||||
* 音频播放器 |
||||
* Created by frank on 2018/2/1. |
||||
*/ |
||||
|
||||
public class AudioPlayer { |
||||
static { |
||||
System.loadLibrary("media-handle"); |
||||
} |
||||
//调用AudioTrack播放
|
||||
public native void play(String audioPath); |
||||
//调用OpenSL ES播放
|
||||
public native void playAudio(String audioPath); |
||||
//调用OpenSL ES播放
|
||||
public native void stop(String audioPath); |
||||
|
||||
/** |
||||
* 创建一个AudioTrack对象 |
||||
* @param sampleRate 采样率 |
||||
* @param channels 声道布局 |
||||
* @return AudioTrack |
||||
*/ |
||||
public AudioTrack createAudioTrack(int sampleRate, int channels){ |
||||
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; |
||||
int channelConfig; |
||||
if(channels == 1){ |
||||
channelConfig = AudioFormat.CHANNEL_OUT_MONO; |
||||
}else if(channels == 2){ |
||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO; |
||||
}else{ |
||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO; |
||||
} |
||||
|
||||
int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat); |
||||
|
||||
return new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, |
||||
bufferSizeInBytes, AudioTrack.MODE_STREAM); |
||||
} |
||||
} |
Loading…
Reference in new issue