size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
numFrames -= outFrames; frameCount0 -= outFrames; frameCount1 -= outFrames; do {
int32_t l0 = *in0++; int32_t r0 = *in0++; l0 = mul(l0, vl0); r0 = mul(r0, vr0); int32_t l = *in1++; int32_t r = *in1++;
l = mulAdd(l, vl1, l0) >> 12; r = mulAdd(r, vr1, r0) >> 12; // clamping... l = clamp16(l); r = clamp16(r);
*out++ = (r<<16) | (l & 0xFFFF); } while (--outFrames);
if (frameCount0 == 0) {
t0.bufferProvider->releaseBuffer(&b0); }
if (frameCount1 == 0) {
t1.bufferProvider->releaseBuffer(&b1); } }
if (buff != NULL) { delete [] buff; }
}
看不懂了吧??哈哈,知道有这回事就行了,专门搞数字音频的需要好好研究下了!
三再论共享audio_track_cblk_t
为什么要再论这个?因为我在网上找了下,有人说audio_track_cblk_t是一个环形buffer,环形buffer是什么意思?自己查查!
这个吗,和我之前的工作经历有关系,某BOSS费尽心机想搞一个牛掰掰的环形buffer,搞得我累死了。现在audio_track_cblk_t是环形buffer?我倒是想看看它是怎么实现的。 顺便我们要解释下,audio_track_cblk_t的使用和我之前说的Lock,读/写,Unlock不太一样。为何?
? 第一因为我们没在AF代码中看到有缓冲buffer方面的wait,MixThread只有当没有数据的时候会usleep一下。
? 第二,如果有多个track,多个audio_track_cblk_t的话,假如又是采用wait信号的办法,那么由于pthread库缺乏WaitForMultiObjects的机制,那么到底该等哪一个?这个问题是我们之前在做跨平台同步库的一个重要难题。
1. 写者的使用
我们集中到audio_track_cblk_t这个类,来看看写者是如何使用的。写者就是AudioTrack端,在这个类中,叫user
? framesAvailable,看看是否有空余空间 ? buffer,获得写空间起始地址 ? stepUser,更新user的位置。
2. 读者的使用
读者是AF端,在这个类中加server。
? framesReady,获得可读的位置 ? stepServer,更新读者的位置
看看这个类的定义:
struct audio_track_cblk_t {
Mutex lock; //同步锁 Condition cv;//CV volatile uint32_t user;//写者 volatile uint32_t server;//读者
uint32_t userBase;//写者起始位置 uint32_t serverBase;//读者起始位置 void* buffers; uint32_t frameCount; // Cache line boundary
uint32_t loopStart; //循环起始
uint32_t loopEnd; //循环结束 int loopCount;
uint8_t out; //如果是Track的话,out就是1,表示输出。 }
注意这是volatile,跨进程的对象,看来这个volatile也是可以跨进程的嘛。
? 唉,又要发挥下了。volatile只是告诉编译器,这个单元的地址不要cache到CPU的缓冲中。也就是每次取值的时候都要到实际内存中去读,而且可能读内存的时候先要锁一下总线。防止其他CPU核执行的时候同时去修改。由于是跨进程共享的内存,这块内存在两个进程都是能见到的,又锁总线了,又是同一块内存,volatile当然保证了同步一致性。
? loopStart和loopEnd这两个值是表示循环播放的起点和终点的,下面还有一个loopCount吗,表示循环播放次数的
那就分析下吧。 先看写者的那几个函数 4 写者分析
先用frameavail看看当前剩余多少空间,我们可以假设是第一次进来嘛。读者还在那sleep呢。 uint32_t audio_track_cblk_t::framesAvailable() {
Mutex::Autolock _l(lock); return framesAvailable_l(); }
int32_t audio_track_cblk_t::framesAvailable_l() {
uint32_t u = this->user; 当前写者位置,此时也为0 uint32_t s = this->server; //当前读者位置,此时为0 if (out) { out为1
uint32_t limit = (s < loopStart) ? s : loopStart;
我们不设循环播放时间吗。所以loopStart是初始值INT_MAX,所以limit=0 return limit + frameCount - u;
//返回0+frameCount-0,也就是全缓冲最大的空间。假设frameCount=1024帧 } }
然后调用buffer获得其实位置,buffer就是得到一个地址位置。 void* audio_track_cblk_t::buffer(uint32_t offset) const {
return (int8_t *)this->buffers + (offset - userBase) * this->frameSize; }
完了,我们更新写者,调用stepUser
uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) {
//framecount,表示我写了多少,假设这一次写了512帧
uint32_t u = this->user;//user位置还没更新呢,此时u=0;
u += frameCount;//u更新了,u=512
// Ensure that user is never ahead of server for AudioRecord if (out) {
//没甚,计算下等待时间 }
//userBase还是初始值为0,可惜啊,我们只写了1024的一半 //所以userBase加不了
if (u >= userBase + this->frameCount) { userBase += this->frameCount;
//但是这句话很重要,userBase也更新了。根据buffer函数的实现来看,似乎把这个
//环形缓冲铺直了....连绵不绝。 }
this->user = u;//喔,user位置也更新为512了,但是useBase还是0 return u; }
好了,假设写者这个时候sleep了,而读者起来了。 5 读者分析
uint32_t audio_track_cblk_t::framesReady() {
uint32_t u = this->user; //u为512 uint32_t s = this->server;//还没读呢,s为零
if (out) {
if (u < loopEnd) {
return u - s;//loopEnd也是INT_MAX,所以这里返回512,表示有512帧可读了
} else {
Mutex::Autolock _l(lock); if (loopCount >= 0) {
return (loopEnd - loopStart)*loopCount + u - s; } else {
return UINT_MAX; } } } else { return s - u; } }
使用完了,然后stepServer
bool audio_track_cblk_t::stepServer(uint32_t frameCount) {
status_t err; err = lock.tryLock(); uint32_t s = this->server;
s += frameCount; //读了512帧了,所以s=512 if (out) { }
没有设置循环播放嘛,所以不走这个 if (s >= loopEnd) { s = loopStart;
if (--loopCount == 0) { loopEnd = UINT_MAX; loopStart = UINT_MAX;