Android Audio 系统的主要内容:
当前分析AudioTrack的文章较多,先以AudioTrack为例进行分析。
JAVA层的AudioTrack class:framework\base\media\java\android\media\AudioTrack.java中。
AudioTrack的使用方法实例:
1 //根据采样率,采样精度,单双声道来得到frame的大小。 2 int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点 3 AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道 4 AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节 5 //注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。 6 //创建AudioTrack 7 AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, 8 AudioFormat.CHANNEL_CONFIGURATION_ STEREO, 9 AudioFormat.ENCODING_PCM_16BIT,bufsize,AudioTrack.MODE_STREAM);// 10 trackplayer.play() ;//开始 11 trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据 12 …. 13 trackplayer.stop();//停止播放 14 trackplayer.release();//释放底层资源。
AudioTrack.MODE_STREAM:AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
StreamType:这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。Android将系统的声音分为以下几类常见的(未写全):
为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。
从AudioTrack的使用实例来逐个分析其中用到的方法,首先是getMinBufferSize:
/** * Returns the minimum buffer size required for the successful creation of an AudioTrack * object to be created in the {@link #MODE_STREAM} mode. Note that this size doesn't * guarantee a smooth playback under load, and higher values should be chosen according to * the expected frequency at which the buffer will be refilled with additional data to play. * For example, if you intend to dynamically set the source sample rate of an AudioTrack * to a higher value than the initial source sample rate, be sure to configure the buffer size * based on the highest planned sample rate. * @param sampleRateInHz the source sample rate expressed in Hz. * @param channelConfig describes the configuration of the audio channels. * See {@link AudioFormat#CHANNEL_OUT_MONO} and * {@link AudioFormat#CHANNEL_OUT_STEREO} * @param audioFormat the format in which the audio data is represented. * See {@link AudioFormat#ENCODING_PCM_16BIT} and * {@link AudioFormat#ENCODING_PCM_8BIT} * @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed, * or {@link #ERROR} if unable to query for output properties, * or the minimum buffer size expressed in bytes. */ static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { int channelCount = 0; switch(channelConfig) { case AudioFormat.CHANNEL_OUT_MONO: case AudioFormat.CHANNEL_CONFIGURATION_MONO: channelCount = 1; break; case AudioFormat.CHANNEL_OUT_STEREO: case AudioFormat.CHANNEL_CONFIGURATION_STEREO: channelCount = 2; break; default: if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) { // input channel configuration features unsupported channels loge("getMinBufferSize(): Invalid channel configuration."); return ERROR_BAD_VALUE; } else { channelCount = Integer.bitCount(channelConfig); } } //目前只支持PCM8和PCM16精度的音频 if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT) && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) { loge("getMinBufferSize(): Invalid audio format."); return ERROR_BAD_VALUE; } //ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间 // sample rate, note these values are subject to change if ( (sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX) ) { loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate."); return ERROR_BAD_VALUE; } //调用native函数 int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); if (size <= 0) { loge("getMinBufferSize(): error querying hardware"); return ERROR; } else { return size; } }
native_get_min_buff_size函数进入了framework/base/core/jni/android_media_AudioTrack.cpp中的android_media_AudioTrack_get_min_buff_size:
// returns the minimum required size for the successful creation of a streaming AudioTrack // returns -1 if there was an error querying the hardware. static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, jint sampleRateInHertz, jint nbChannels, jint audioFormat) { size_t frameCount = 0; if (AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,sampleRateInHertz) != NO_ERROR) { return -1; } return frameCount * nbChannels * (audioFormat == ENCODING_PCM_16BIT ? 2 : 1); }
根据最小的framecount计算最小的buffersize。音频中最常见的是frame这个单位,一个frame就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多//声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来//才行。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。getMinBufSize函数完了后,我们得到一个满足最小要求的缓冲区大小。这样用户分配缓冲区就有了依据。下面就需要创建AudioTrack对象了
先看AudioTrack.java中的构造函数:
/** * Class constructor with audio session. Use this constructor when the AudioTrack must be * attached to a particular audio session. The primary use of the audio session ID is to * associate audio effects to a particular instance of AudioTrack: if an audio session ID * is provided when creating an AudioEffect, this effect will be applied only to audio tracks * and media players in the same session and not to the output mix. * When an AudioTrack is created without specifying a session, it will create its own session * which can be retrieved by calling the {@link #getAudioSessionId()} method. * If a non-zero session ID is provided, this AudioTrack will share effects attached to this * session * with all other media players or audio tracks in the same session, otherwise a new session * will be created for this track if none is supplied. * @param streamType the type of the audio stream. See * {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM}, * {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC}, * {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}. * @param sampleRateInHz the initial source sample rate expressed in Hz. * @param channelConfig describes the configuration of the audio channels. * See {@link AudioFormat#CHANNEL_OUT_MONO} and * {@link AudioFormat#CHANNEL_OUT_STEREO} * @param audioFormat the format in which the audio data is represented. * See {@link AudioFormat#ENCODING_PCM_16BIT} and * {@link AudioFormat#ENCODING_PCM_8BIT} * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read * from for playback. If using the AudioTrack in streaming mode, you can write data into * this buffer in smaller chunks than this size. If using the AudioTrack in static mode, * this is the maximum size of the sound that will be played for this instance. * See {@link #getMinBufferSize(int, int, int)} to determine the minimum required buffer size * for the successful creation of an AudioTrack instance in streaming mode. Using values * smaller than getMinBufferSize() will result in an initialization failure. * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM} * @param sessionId Id of audio session the AudioTrack must be attached to * @throws java.lang.IllegalArgumentException */ public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode, int sessionId) throws IllegalArgumentException { // mState already == STATE_UNINITIALIZED // remember which looper is associated with the AudioTrack instantiation Looper looper;
// 获得主线程的Looper,这个在MediaScanner中有相关介绍。 if ((looper = Looper.myLooper()) == null) { looper = Looper.getMainLooper(); } mInitializationLooper = looper;
//检查参数是否合法之类的,可以不管它 audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode); audioBuffSizeCheck(bufferSizeInBytes); if (sessionId < 0) { throw new IllegalArgumentException("Invalid audio session ID: "+sessionId); } int[] session = new int[1]; session[0] = sessionId; // native initialization
// 调用native层的native_setup,把自己的WeakReference传进去了 int initResult = native_setup(new WeakReference<AudioTrack>(this), mStreamType, mSampleRate, mChannels, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED } mSessionId = session[0]; if (mDataLoadMode == MODE_STATIC) { mState = STATE_NO_STATIC_DATA; } else { mState = STATE_INITIALIZED; } }
native_setup函数进入了framework/base/core/jni/android_media_AudioTrack.cpp中的android_media_AudioTrack_native_setup:
static int android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint streamType, jint sampleRateInHertz, jint javaChannelMask, jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) { ALOGV("sampleRate=%d, audioFormat(from Java)=%d, channel mask=%x, buffSize=%d", sampleRateInHertz, audioFormat, javaChannelMask, buffSizeInBytes); uint32_t afSampleRate; size_t afFrameCount; if (AudioSystem::getOutputFrameCount(&afFrameCount, (audio_stream_type_t) streamType) != NO_ERROR) { ALOGE("Error creating AudioTrack: Could not get AudioSystem frame count."); return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; } if (AudioSystem::getOutputSamplingRate(&afSampleRate, (audio_stream_type_t) streamType) != NO_ERROR) { ALOGE("Error creating AudioTrack: Could not get AudioSystem sampling rate."); return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; } // Java channel masks don't map directly to the native definition, but it's a simple shift // to skip the two deprecated channel configurations "default" and "mono". uint32_t nativeChannelMask = ((uint32_t)javaChannelMask) >> 2; if (!audio_is_output_channel(nativeChannelMask)) { ALOGE("Error creating AudioTrack: invalid channel mask %#x.", javaChannelMask); return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK; } //popCount是统计一个整数中有多少位为1的算法 int nbChannels = popcount(nativeChannelMask); // check the stream type audio_stream_type_t atStreamType; switch (streamType) { case AUDIO_STREAM_VOICE_CALL: case AUDIO_STREAM_SYSTEM: case AUDIO_STREAM_RING: case AUDIO_STREAM_MUSIC: case AUDIO_STREAM_ALARM: case AUDIO_STREAM_NOTIFICATION: case AUDIO_STREAM_BLUETOOTH_SCO: case AUDIO_STREAM_DTMF: atStreamType = (audio_stream_type_t) streamType; break; default: ALOGE("Error creating AudioTrack: unknown stream type."); return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; } // check the format. // This function was called from Java, so we compare the format against the Java constants if ((audioFormat != ENCODING_PCM_16BIT) && (audioFormat != ENCODING_PCM_8BIT)) { ALOGE("Error creating AudioTrack: unsupported audio format."); return AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT; } // for the moment 8bitPCM in MODE_STATIC is not supported natively in the AudioTrack C++ class // so we declare everything as 16bitPCM, the 8->16bit conversion for MODE_STATIC will be handled // in android_media_AudioTrack_native_write_byte() if ((audioFormat == ENCODING_PCM_8BIT)&& (memoryMode == MODE_STATIC)) { ALOGV("android_media_AudioTrack_native_setup(): requesting MODE_STATIC for 8bit \ buff size of %dbytes, switching to 16bit, buff size of %dbytes", buffSizeInBytes, 2*buffSizeInBytes); audioFormat = ENCODING_PCM_16BIT; // we will need twice the memory to store the data buffSizeInBytes *= 2; } // compute the frame count int bytesPerSample = audioFormat == ENCODING_PCM_16BIT ? 2 : 1; audio_format_t format = audioFormat == ENCODING_PCM_16BIT ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT; //根据Buffer大小和一个Frame大小来计算帧数。 int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample); jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { ALOGE("Can't find %s when setting up callback.", kClassPathName); return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; } if (jSession == NULL) { ALOGE("Error creating AudioTrack: invalid session ID pointer"); return AUDIOTRACK_ERROR; } jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); return AUDIOTRACK_ERROR; } int sessionId = nSession[0]; env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; // create the native AudioTrack object //创建真正的AudioTrack对象 sp<AudioTrack> lpTrack = new AudioTrack(); // initialize the callback information: // this data will be passed with every AudioTrack callback // AudioTrackJniStorage,就是一个保存一些数据的地方,这里边有一些有用的知识,下面再详细解释 AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage(); lpJniStorage->mStreamType = atStreamType; lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz); // we use a weak reference so the AudioTrack object can be garbage collected. lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); lpJniStorage->mCallbackData.busy = false; // initialize the native AudioTrack object switch (memoryMode) { case MODE_STREAM: lpTrack->set( atStreamType,// stream type sampleRateInHertz, format,// word length, PCM nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 0,// shared mem true,// thread can call Java sessionId);// audio session ID break; case MODE_STATIC: // AudioTrack is using shared memory //如果是static模式,需要用户一次性把数据写进去,然后再由audioTrack自己去把数据读出来, //所以需要一个共享内存这里的共享内存是指C++ AudioTrack和AudioFlinger之间共享的内容因为真正播放的工作是由AudioFlinger来完成的。 if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) { ALOGE("Error creating AudioTrack in static mode: error creating mem heap base"); goto native_init_failure; } lpTrack->set( atStreamType,// stream type sampleRateInHertz, format,// word length, PCM nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack lpJniStorage->mMemBase,// shared mem true,// thread can call Java sessionId);// audio session ID break; default: ALOGE("Unknown mode %d", memoryMode); goto native_init_failure; } if (lpTrack->initCheck() != NO_ERROR) { ALOGE("Error initializing AudioTrack"); goto native_init_failure; } nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { ALOGE("Error creating AudioTrack: Error retrieving session id pointer"); goto native_init_failure; } // read the audio session ID back from AudioTrack in case we create a new session nSession[0] = lpTrack->getSessionId(); env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; { // scope for the lock Mutex::Autolock l(sLock); sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData); } // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field // of the Java object (in mNativeTrackInJavaObj) setAudioTrack(env, thiz, lpTrack); //把C++AudioTrack对象指针保存到JAVA对象的一个变量中,这样,Native层的AudioTrack对象就和JAVA层的AudioTrack对象关联起来了.// save the JNI resources so we can free them later //ALOGV("storing lpJniStorage: %x\n", (int)lpJniStorage); env->SetIntField(thiz, javaAudioTrackFields.jniData, (int)lpJniStorage); return AUDIOTRACK_SUCCESS; // failures: native_init_failure: if (nSession != NULL) { env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); } env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class); env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref); delete lpJniStorage; env->SetIntField(thiz, javaAudioTrackFields.jniData, 0); return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; }
这个类其实就是一个辅助类,但是里边有一些知识很重要,尤其是Android封装的一套共享内存的机制。把这块搞清楚了,我们就能轻松得在两个进程间进行内存的拷贝。
class AudioTrackJniStorage { public: sp<MemoryHeapBase> mMemHeap; sp<MemoryBase> mMemBase; audiotrack_callback_cookie mCallbackData; audio_stream_type_t mStreamType; AudioTrackJniStorage() { mCallbackData.audioTrack_class = 0; mCallbackData.audioTrack_ref = 0; mStreamType = AUDIO_STREAM_DEFAULT; } ~AudioTrackJniStorage() { mMemBase.clear(); mMemHeap.clear(); } bool allocSharedMem(int sizeInBytes) { mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base"); if (mMemHeap->getHeapID() < 0) { return false; } mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
//注意用法,先弄一个MemoryHeapBase,再把MemoryHeapBase传入到MemoryBase中去。 return true; } };
MemroyHeapBase,MemoryBase是Android搞的一套基于Binder机制的对内存操作的类。既然是Binder机制,那么肯定有一个服务端Bnxxx,一个代理端Bpxxx。
MemoryXXX大概的使用方法如下:
BnXXX端先分配BnMemoryHeapBase和BnMemoryBase,
然后把BnMemoryBase传递到BpXXX
BpXXX就可以使用BpMemoryBase得到BnXXX端分配的共享内存了。
注意,既然是进程间共享内存,那么Bp端肯定使用memcpy之类的函数来操作内存,这些函数是没有同步保护的,而且Android也不可能在系统内部为这种共享内存去做增加同步保护。所以看来后续在操作这些共享内存的时候,肯定存在一个跨进程的同步保护机制。我们在后面讲实际播放的时候会碰到。
另外,这里的SharedBuffer最终会在Bp端也就是AudioFlinger那用到。
JAVA层到这一步后就是调用play和write了。JAVA层这两个函数没什么内容,都是直接转到native层干活了。先看看play函数对应的JNI函数:
static void android_media_AudioTrack_start(JNIEnv *env, jobject thiz) {
//从JAVA那个AudioTrack对象获取保存的C++层的AudioTrack对象指针
//从int类型直接转换成指针。要是以后ARM变成64位平台了,看google怎么改! sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for start()"); return; } lpTrack->start(); }
再看write。我们写的是short数组:
static jint android_media_AudioTrack_native_write_short(JNIEnv *env, jobject thiz, jshortArray javaAudioData, jint offsetInShorts, jint sizeInShorts, jint javaAudioFormat) { jint written = android_media_AudioTrack_native_write_byte(env, thiz, (jbyteArray) javaAudioData, offsetInShorts*2, sizeInShorts*2, javaAudioFormat); if (written > 0) { written /= 2; } return written; }
static jint android_media_AudioTrack_native_write_byte(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes, jint javaAudioFormat) { //ALOGV("android_media_AudioTrack_native_write_byte(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } // get the pointer for the audio data from the java array // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) jbyte* cAudioData = NULL; if (javaAudioData) { cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes); env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); //ALOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", // (int)written, (int)(sizeInBytes), (int)offsetInBytes); return written; }
jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, jint offsetInBytes, jint sizeInBytes) { // give the data to the native AudioTrack object (the data starts at the offset) ssize_t written = 0; // regular write() or copy the data to the AudioTrack's shared memory? if (track->sharedBuffer() == 0) {
//创建的是流的方式,所以没有共享内存在track中 written = track->write(data + offsetInBytes, sizeInBytes); // for compatibility with earlier behavior of write(), return 0 in this case if (written == (ssize_t) WOULD_BLOCK) { written = 0; } } else { if (audioFormat == ENCODING_PCM_16BIT) { // writing to shared memory, check for capacity if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { sizeInBytes = track->sharedBuffer()->size(); }
//STATIC模式的,就直接把数据拷贝到共享内存里 memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes); written = sizeInBytes; } else if (audioFormat == ENCODING_PCM_8BIT) {
//PCM8格式的要先转换成PCM16 // data contains 8bit data we need to expand to 16bit before copying // to the shared memory // writing to shared memory, check for capacity, // note that input data will occupy 2X the input space due to 8 to 16bit conversion if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) { sizeInBytes = track->sharedBuffer()->size() / 2; } int count = sizeInBytes; int16_t *dst = (int16_t *)track->sharedBuffer()->pointer(); const int8_t *src = (const int8_t *)(data + offsetInBytes); while (count--) { *dst++ = (int16_t)(*src++^0x80) << 8; } // even though we wrote 2*sizeInBytes, we only report sizeInBytes as written to hide // the 8bit mixer restriction from the user of this function written = sizeInBytes; } } return written; }
到这里,似乎很简单,JAVA层的AudioTrack,无非就是调用write函数,而实际由JNI层的C++ AudioTrack write数据。
未完,看累了,歇几天继续
Reprinted from:http://www.cnblogs.com/innost/archive/2011/01/09/1931457.html