重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
引子Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中 进行播放,目前Android的Froyo版本设定了同时最多可以创建32个音频流,也就是说,Mixer最多会同时处理32个AudioTrack的数 据流。如何使用AudioTrackAudioTrack的主要代码位于 frameworks/base/media/libmedia/audiotrack.cpp中。现在先通过一个例子来了解一下如何使用 AudioTrack,ToneGenerator是android中产生电话拨号音和其他音调波形的一个实现,我们就以它为例子:ToneGenerator的初始化函数:bool ToneGenerator::initAudioTrack() { // Open audio track in mono, PCM 16bit//, default sampling rate, default buffer size mpAudioTrack = new AudioTrack(); mpAudioTrack-set(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0, 0, mThreadCanCallJava); if (mpAudioTrack-initCheck() != NO_ERROR) { LOGE("AudioTrack-initCheck failed"); goto initAudioTrack_exit; } mpAudioTrack-setVolume(mVolume, mVolume); mState = TONE_INIT; ...... } 可见,创建步骤很简单,先new一个AudioTrack的实例,然后调用set成员函数完成参数的设置并注册到AudioFlinger中,然后可以调 用其他诸如设置音量等函数进一步设置音频参数。其中,一个重要的参数是audioCallback,audioCallback是一个回调函数,负责响应 AudioTrack的通知,例如填充数据、循环播放、播放位置触发等等。回调函数的写法通常像这样:void ToneGenerator::audioCallback(int event, void* user, void *info) { if (event != AudioTrack::EVENT_MORE_DATA) return; AudioTrack::Buffer *buffer = static_castAudioTrack::Buffer *(info); ToneGenerator *lpToneGen = static_castToneGenerator *(user); short *lpOut = buffer-i16; unsigned int lNumSmp = buffer-size/sizeof(short); const ToneDescriptor *lpToneDesc = lpToneGen-mpToneDesc; if (buffer-size == 0) return; // Clear output buffer: WaveGenerator accumulates into lpOut buffer memset(lpOut, 0, buffer-size); ...... // 以下是产生音调数据的代码,略.... } 该函数首先判断事件的类型是否是EVENT_MORE_DATA,如果是,则后续的代码会填充相应的音频数据后返回,当然你可以处理其他事件,以下是可用的事件类型:enum event_type { EVENT_MORE_DATA = 0,// Request to write more data to PCM buffer. EVENT_UNDERRUN = 1,// PCM buffer underrun occured. EVENT_LOOP_END = 2,// Sample loop end was reached; playback restarted from loop start if loop count was not 0. EVENT_MARKER = 3,// Playback head is at the specified marker position (See setMarkerPosition()). EVENT_NEW_POS = 4,// Playback head is at a new position (See setPositionUpdatePeriod()). EVENT_BUFFER_END = 5// Playback head is at the end of the buffer. }; 开始播放:mpAudioTrack-start(); 停止播放:mpAudioTrack-stop(); 只要简单地调用成员函数start()和stop()即可。AudioTrack和AudioFlinger的通信机制通常,AudioTrack和AudioFlinger并不在同一个进程中,它们通过android中的binder机制建立联系。AudioFlinger是android中的一个service,在android启动时就已经被加载。下面这张图展示了他们两个的关系:图一AudioTrack和AudioFlinger的关系我们可以这样理解这张图的含义:audio_track_cblk_t实现了一个环形FIFO;AudioTrack是FIFO的数据生产者;AudioFlinger是FIFO的数据消费者。建立联系的过程下面的序列图展示了AudioTrack和AudioFlinger建立联系的过程:图二AudioTrack和AudioFlinger建立联系解释一下过程:Framework或者Java层通过JNI,new AudioTrack();根据StreamType等参数,通过一系列的调用getOutput();如有必要,AudioFlinger根据StreamType打开不同硬件设备;AudioFlinger为该输出设备创建混音线程: MixerThread(),并把该线程的id作为getOutput()的返回值返回给AudioTrack;AudioTrack通过binder机制调用AudioFlinger的createTrack();AudioFlinger注册该AudioTrack到MixerThread中;AudioFlinger创建一个用于控制的TrackHandle,并以IAudioTrack这一接口作为createTrack()的返回值;AudioTrack通过IAudioTrack接口,得到在AudioFlinger中创建的FIFO(audio_track_cblk_t);AudioTrack创建自己的监控线程:AudioTrackThread;自此,AudioTrack建立了和AudioFlinger的全部联系工作,接下来,AudioTrack可以:通过IAudioTrack接口控制该音轨的状态,例如start,stop,pause等等;通过对FIFO的写入,实现连续的音频播放;监控线程监控事件的发生,并通过audioCallback回调函数与用户程序进行交互;FIFO的管理 audio_track_cblk_taudio_track_cblk_t这个结构是FIFO实现的关键,该结构是在createTrack的时候,由AudioFlinger申请相 应的内存,然后通过IMemory接口返回AudioTrack的,这样AudioTrack和AudioFlinger管理着同一个 audio_track_cblk_t,通过它实现了环形FIFO,AudioTrack向FIFO中写入音频数据,AudioFlinger从FIFO 中读取音频数据,经Mixer后送给AudioHardware进行播放。audio_track_cblk_t的主要数据成员: user -- AudioTrack当前的写位置的偏移 userBase -- AudioTrack写偏移的基准位置,结合user的值方可确定真实的FIFO地址指针 server -- AudioFlinger当前的读位置的偏移 serverBase -- AudioFlinger读偏移的基准位置,结合server的值方可确定真实的FIFO地址指针 frameCount -- FIFO的大小,以音频数据的帧为单位,16bit的音频每帧的大小是2字节 buffers -- 指向FIFO的起始地址 out -- 音频流的方向,对于AudioTrack,out=1,对于AudioRecord,out=0audio_track_cblk_t的主要成员函数:framesAvailable_l()和framesAvailable()用于获取FIFO中可写的空闲空间的大小,只是加锁和不加锁的区别。uint32_t audio_track_cblk_t::framesAvailable_l() { uint32_t u = this-user; uint32_t s = this-server; if (out) { uint32_t limit = (s loopStart) ? s : loopStart; return limit + frameCount - u; } else { return frameCount + u - s; } } framesReady()用于获取FIFO中可读取的空间大小。uint32_t audio_track_cblk_t::framesReady() { uint32_t u = this-user; uint32_t s = this-server; if (out) { if (u loopEnd) { return u - s; } else { Mutex::Autolock _l(lock); if (loopCount = 0) { return (loopEnd - loopStart)*loopCount + u - s; } else { return UINT_MAX; } } } else { return s - u; } } 我们看看下面的示意图: _____________________________________________ ^ ^ ^ ^ buffer_start server(s) user(u) buffer_end 很明显,frameReady = u - s,frameAvalible = frameCount - frameReady = frameCount - u + s 可能有人会问,应为这是一个环形的buffer,一旦user越过了buffer_end以后,应该会发生下面的情况: _____________________________________________ ^ ^ ^ ^ buffer_start user(u) server(s) buffer_end这时候u在s的前面,用上面的公式计算就会错误,但是android使用了一些技巧,保证了上述公式一直成立。我们先看完下面三个函数的代码再分析:uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount) { uint32_t u = this-user; u += frameCount; ...... if (u = userBase + this-frameCount) { userBase += this-frameCount; } this-user = u; ...... return u; } bool audio_track_cblk_t::stepServer(uint32_t frameCount) { // the code below simulates lock-with-timeout // we MUST do this to protect the AudioFlinger server // as this lock is shared with the client. status_t err; err = lock.tryLock(); if (err == -EBUSY) { // just wait a bit usleep(1000); err = lock.tryLock(); } if (err != NO_ERROR) { // probably, the client just died. return false; } uint32_t s = this-server; s += frameCount; // 省略部分代码 // ...... if (s = serverBase + this-frameCount) { serverBase += this-frameCount; } this-server = s; cv.signal(); lock.unlock(); return true; } void* audio_track_cblk_t::buffer(uint32_t offset) const { return (int8_t *)this-buffers + (offset - userBase) * this-frameSize; } stepUser()和stepServer的作用是调整当前偏移的位置,可以看到,他们仅仅是把成员变量user或server的值加上需要移动 的数量,user和server的值并不考虑FIFO的边界问题,随着数据的不停写入和读出,user和server的值不断增加,只要处理得 当,user总是出现在server的后面,因此frameAvalible()和frameReady()中的算法才会一直成立。根据这种算 法,user和server的值都可能大于FIFO的大小:framCount,那么,如何确定真正的写指针的位置呢?这里需要用到userBase这一 成员变量,在stepUser()中,每当user的值越过(userBase+frameCount),userBase就会增加 frameCount,这样,映射到FIFO中的偏移总是可以通过(user-userBase)获得。因此,获得当前FIFO的写地址指针可以通过成员 函数buffer()返回:p = mClbk-buffer(mclbk-user);在AudioTrack中,封装了两个函数:obtainBuffer()和releaseBuffer()操作 FIFO,obtainBuffer()获得当前可写的数量和写指针的位置,releaseBuffer()则在写入数据后被调用,它其实就是简单地调用 stepUser()来调整偏移的位置。IMemory接口在createTrack的过程中,AudioFlinger会根据传入的frameCount参数,申请一块内存,AudioTrack可以通过 IAudioTrack接口的getCblk()函数获得指向该内存块的IMemory接口,然后AudioTrack通过该IMemory接口的 pointer()函数获得指向该内存块的指针,这块内存的开始部分就是audio_track_cblk_t结构,紧接着是大小为frameSize的 FIFO内存。IMemory-pointer() ----|_______________________________________________________ |__audio_track_cblk_t__|_______buffer of FIFO(size==frameCount)____|看看AudioTrack的createTrack()的代码就明白了:spIAudioTrack track = audioFlinger-createTrack(getpid(), streamType, sampleRate, format, channelCount, frameCount, ((uint16_t)flags) 16, sharedBuffer, output, status); // 得到IMemory接口 spIMemory cblk = track-getCblk(); mAudioTrack.clear(); mAudioTrack = track; mCblkMemory.clear(); mCblkMemory = cblk; // 得到audio_track_cblk_t结构 mCblk = static_castaudio_track_cblk_t*(cblk-pointer()); // 该FIFO用于输出 mCblk-out = 1; // Update buffer size in case it has been limited by AudioFlinger during track creation mFrameCount = mCblk-frameCount; if (sharedBuffer == 0) { // 给FIFO的起始地址赋值 mCblk-buffers = (char*)mCblk + sizeof(audio_track_cblk_t); } else { .......... } (DroidPhone)
创新互联专注于青原网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供青原营销型网站建设,青原网站制作、青原网页设计、青原网站官网定制、小程序开发服务,打造青原网络公司原创品牌,更为您提供青原网站排名全网营销落地服务。
media的意思是媒体。媒体文件要用媒体播放器来播放。
WAV文件记录的是声音的波形,自然界的任何一种声音都可以作为WAV文件来保存,而MID文件内保存的是一些音乐符号,就像乐谱(如五线谱、简谱)一样,所以播放出来的只有音乐。
单击【开始】按钮,在开始菜单中单击Windows Media Player命令,屏幕上弹出Windows Media Player窗口。
单击【文件】菜单的【打开】命令,在弹出的【打开】对话框中,选择要播放的多媒体文件并双击,开始播放选中的音乐文件,在播放控制区域中,显示出当前正在播放的文件进度。
安卓开发音频mic口接收20khz的波形的方法? 一、手机音频通信的特点 1、 通用性强:在智能手机普及的今天,手机的对外通信接口多种多样,而其中以3.5mm的音频接口通用新最强,基本所有的手机、平板电脑都会有这个接口,所以在一些要求通用性的设...
Apple
GarageBand
适用设备:iPhone,iPod
touch,iPad(iOS4.3以上)
移动版本和Mac版本的功能类似,可以和Mac版同步,但是也有独特之处。它配备了触摸型智能乐器、八音轨音序器还有一组电吉他音箱和模拟效果器的经典组合。
它的多样化选项足可以让你做一场精彩表演,不过唯一不能满足你的就是无法编辑midi音效。
尽管这样,iPad版GarageBand仍然是个推荐产品,它带键盘接口,堪称一款强大的速创作工具。而且它竟然是免费的。
拓展资料:
Flex
Time
和音乐套路匹配
音乐套路匹配是两项强大的工具,可帮你改进录音中的节奏和乐感。你可以修正单个音符的时点,让轨道的旋律更加紧凑,或轻松制作动听的歌曲。
Flex
Time
是一项全新的工具,可让你快速改变录音的时点,让歌曲拥有完美节奏。有些部分的演奏不在节点上?使用
Flex
Time
可快速修正时间错误。只需点击并拖动波形图的任意部分,就能修改一个音符或节拍的时点。你可移动、拉伸或缩短单个的音符,却不会影响录音的其他部分。Flex
Time
还能激发你的创意灵感:通过延长吉他即兴演奏、修改人声演唱、尝试不同的节奏,然后获得新的想法。用
Flex
Time
进行的改动均会突出显示,让你可以轻松查看。你可随时点击
Flex
Time
按钮,将你的修改与原始录音进行对比。
如果你正在处理多个不同的轨道,如吉他、贝斯、键盘、打击乐器,还有最底层的鼓循环片段。那么,很可能其中一个(或全部)轨道的节奏会有所偏差。音乐套路匹配能让所有轨道的节奏和谐统一。只需选定歌曲中的任意一条轨道,将它作为音乐套路轨道,其他所有轨道即可迅速与其匹配。如果不是每条轨道都需要调整,你可只选择需要改动的轨道。音乐套路匹配还能让Apple
复制Audio Source可以用Cool Edit Pro。
Cool Edit Pro是一个非常出色的数字音频编辑器,它能播放和录制多种文件格式的音频文件,并可以编辑合成文件并进行几种文件格式的转换。同它的上一个版本Cool
Edit 96相比,它不仅保留了Cool Edit 96的众多优点,而且大大提高了处理速度和软件功能。更让人惊讶的是,Cool Edit Pro还携带了一个相当专业且高效的音频处理工具——多轨编辑器。总而言之,对于喜爱编辑制作电脑音乐的朋友来说,Cool Edit Pro拥有的数码录音、专业编辑等强大功能绝对会让你兴奋不已。
Cool Edit Pro对系统硬件的要求可高可低,主要看你打算如何使用了。如果只准备用Cool Edit Pro制作一些简单的特殊音效或用于MIDI的辅助编曲工作,那么就只需要Windows XP、486以上、8MB内存、6MB可用硬盘空间就可以了。如果你想完全感受Cool Edit Pro的强大功能,从事复杂的硬盘录音和编辑操作,从事复杂的硬盘录音或编辑操作,就必须要586以上,16MB内存。
Cool Edit Pro的工作界面绝对令你耳目一新,可显示声音波形的窗口上下密密麻麻地布满了功能按钮,一副专业录音编辑台的架势。Cool Edit Pro界面又根据用户需要,分为波形编辑界面和多音轨编辑界面两个部分。
波形编辑界面主要用来对音轨文件进行播放、分析、编辑处理和文件格式转换。在播放音轨文件时,可以在波形窗口中观察单个波形的实时数字信号处理效果,并可截获其中某一段波形进行编辑处理。波形编辑界面主要由标题栏、菜单栏、工具栏、波形显示区、波形缩放栏、CD播放控制以及状态栏组成。
多音轨编辑界面与波形编辑界面相近,不同的是界面上的波形显示区被划分为四个区域。这四个区域可以同时用来实时检测声音文件中几个音轨的播放效果,如吉他、鼓声等等,并且可以对每个音轨进行音量、均衡、静音等编辑,然后再重新合成。波形显示区左侧分别对应每个音轨控制台,在音轨控制台上可以显示音轨名称、音量、录音/回放设备选择,还可以对指定音轨进行静音、录音等操作。
Cool Edit Pro播放声音文件的方法十分简单,单击工具栏上的Open an existing waveform按钮,在弹出的对话框中选择要播放的音频文件,然后单击工具栏上的Play按钮即可播放这个音频文件。在播放音频文件时,波形显示区中将会实时显示波形播放效果。如果要对其中一段音乐进行编辑,可以用鼠标直接在波形显示区选择这段波形,然后使用剪贴工具把它复制到剪贴板上。还可以把这段波形粘贴到另一个位置,也可以使用工具对它进行缩放调整,然后再粘贴。
使用Cool Edit Pro转换文件格式也十分简单方便,只要在File菜单中单击Save as命令,打开Save as对话框,选择需要的文件格式保存即可。