真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

android波形,android畫波形圖

如何繪制Android的音樂播放器的波形

首先你的歌曲是要存在數(shù)據(jù)庫里的吧第一種方法 新建一個(gè)我的最愛歌曲的表 然后表里存歌名,歌曲地址,還有各種其他歌曲的參數(shù),看你的歌都有那些參數(shù)了 然后在播放列表中實(shí)現(xiàn)一個(gè)長(zhǎng)點(diǎn)擊事件,彈出一個(gè)菜單或者直接操作都行 然后把歌曲的各種信息存到表里就行了 我的最愛就直接是這張表就行了第二種方法 在你的歌曲表中新加一個(gè)bool列 然后通過這個(gè)列來表示你的歌曲是否在我的最愛中 當(dāng)你想要打開我的最愛時(shí)就檢測(cè)這個(gè)屬性就行了 是就在 不是就不再

成都創(chuàng)新互聯(lián)主營旅順口網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,成都APP應(yīng)用開發(fā),旅順口h5微信小程序定制開發(fā)搭建,旅順口網(wǎng)站營銷推廣歡迎旅順口等地區(qū)企業(yè)咨詢

[img]

android 怎么繪制時(shí)時(shí)音頻波形圖

安卓開發(fā)音頻mic口接收20khz的波形的方法? 一、手機(jī)音頻通信的特點(diǎn) 1、 通用性強(qiáng):在智能手機(jī)普及的今天,手機(jī)的對(duì)外通信接口多種多樣,而其中以3.5mm的音頻接口通用新最強(qiáng),基本所有的手機(jī)、平板電腦都會(huì)有這個(gè)接口,所以在一些要求通用性的設(shè)...

Android Audio System 之一:AudioTrack如何與AudioFlinger交換

引子Android Framework的音頻子系統(tǒng)中,每一個(gè)音頻流對(duì)應(yīng)著一個(gè)AudioTrack類的一個(gè)實(shí)例,每個(gè)AudioTrack會(huì)在創(chuàng)建時(shí)注冊(cè)到 AudioFlinger中,由AudioFlinger把所有的AudioTrack進(jìn)行混合(Mixer),然后輸送到AudioHardware中 進(jìn)行播放,目前Android的Froyo版本設(shè)定了同時(shí)最多可以創(chuàng)建32個(gè)音頻流,也就是說,Mixer最多會(huì)同時(shí)處理32個(gè)AudioTrack的數(shù) 據(jù)流。如何使用AudioTrackAudioTrack的主要代碼位于 frameworks/base/media/libmedia/audiotrack.cpp中。現(xiàn)在先通過一個(gè)例子來了解一下如何使用 AudioTrack,ToneGenerator是android中產(chǎn)生電話撥號(hào)音和其他音調(diào)波形的一個(gè)實(shí)現(xiàn),我們就以它為例子:ToneGenerator的初始化函數(shù):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; ...... } 可見,創(chuàng)建步驟很簡(jiǎn)單,先new一個(gè)AudioTrack的實(shí)例,然后調(diào)用set成員函數(shù)完成參數(shù)的設(shè)置并注冊(cè)到AudioFlinger中,然后可以調(diào) 用其他諸如設(shè)置音量等函數(shù)進(jìn)一步設(shè)置音頻參數(shù)。其中,一個(gè)重要的參數(shù)是audioCallback,audioCallback是一個(gè)回調(diào)函數(shù),負(fù)責(zé)響應(yīng) AudioTrack的通知,例如填充數(shù)據(jù)、循環(huán)播放、播放位置觸發(fā)等等?;卣{(diào)函數(shù)的寫法通常像這樣: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); ...... // 以下是產(chǎn)生音調(diào)數(shù)據(jù)的代碼,略.... } 該函數(shù)首先判斷事件的類型是否是EVENT_MORE_DATA,如果是,則后續(xù)的代碼會(huì)填充相應(yīng)的音頻數(shù)據(jù)后返回,當(dāng)然你可以處理其他事件,以下是可用的事件類型: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(); 只要簡(jiǎn)單地調(diào)用成員函數(shù)start()和stop()即可。AudioTrack和AudioFlinger的通信機(jī)制通常,AudioTrack和AudioFlinger并不在同一個(gè)進(jìn)程中,它們通過android中的binder機(jī)制建立聯(lián)系。AudioFlinger是android中的一個(gè)service,在android啟動(dòng)時(shí)就已經(jīng)被加載。下面這張圖展示了他們兩個(gè)的關(guān)系:圖一AudioTrack和AudioFlinger的關(guān)系我們可以這樣理解這張圖的含義:audio_track_cblk_t實(shí)現(xiàn)了一個(gè)環(huán)形FIFO;AudioTrack是FIFO的數(shù)據(jù)生產(chǎn)者;AudioFlinger是FIFO的數(shù)據(jù)消費(fèi)者。建立聯(lián)系的過程下面的序列圖展示了AudioTrack和AudioFlinger建立聯(lián)系的過程:圖二AudioTrack和AudioFlinger建立聯(lián)系解釋一下過程:Framework或者Java層通過JNI,new AudioTrack();根據(jù)StreamType等參數(shù),通過一系列的調(diào)用getOutput();如有必要,AudioFlinger根據(jù)StreamType打開不同硬件設(shè)備;AudioFlinger為該輸出設(shè)備創(chuàng)建混音線程: MixerThread(),并把該線程的id作為getOutput()的返回值返回給AudioTrack;AudioTrack通過binder機(jī)制調(diào)用AudioFlinger的createTrack();AudioFlinger注冊(cè)該AudioTrack到MixerThread中;AudioFlinger創(chuàng)建一個(gè)用于控制的TrackHandle,并以IAudioTrack這一接口作為createTrack()的返回值;AudioTrack通過IAudioTrack接口,得到在AudioFlinger中創(chuàng)建的FIFO(audio_track_cblk_t);AudioTrack創(chuàng)建自己的監(jiān)控線程:AudioTrackThread;自此,AudioTrack建立了和AudioFlinger的全部聯(lián)系工作,接下來,AudioTrack可以:通過IAudioTrack接口控制該音軌的狀態(tài),例如start,stop,pause等等;通過對(duì)FIFO的寫入,實(shí)現(xiàn)連續(xù)的音頻播放;監(jiān)控線程監(jiān)控事件的發(fā)生,并通過audioCallback回調(diào)函數(shù)與用戶程序進(jìn)行交互;FIFO的管理 audio_track_cblk_taudio_track_cblk_t這個(gè)結(jié)構(gòu)是FIFO實(shí)現(xiàn)的關(guān)鍵,該結(jié)構(gòu)是在createTrack的時(shí)候,由AudioFlinger申請(qǐng)相 應(yīng)的內(nèi)存,然后通過IMemory接口返回AudioTrack的,這樣AudioTrack和AudioFlinger管理著同一個(gè) audio_track_cblk_t,通過它實(shí)現(xiàn)了環(huán)形FIFO,AudioTrack向FIFO中寫入音頻數(shù)據(jù),AudioFlinger從FIFO 中讀取音頻數(shù)據(jù),經(jīng)Mixer后送給AudioHardware進(jìn)行播放。audio_track_cblk_t的主要數(shù)據(jù)成員: user -- AudioTrack當(dāng)前的寫位置的偏移 userBase -- AudioTrack寫偏移的基準(zhǔn)位置,結(jié)合user的值方可確定真實(shí)的FIFO地址指針 server -- AudioFlinger當(dāng)前的讀位置的偏移 serverBase -- AudioFlinger讀偏移的基準(zhǔn)位置,結(jié)合server的值方可確定真實(shí)的FIFO地址指針 frameCount -- FIFO的大小,以音頻數(shù)據(jù)的幀為單位,16bit的音頻每幀的大小是2字節(jié) buffers -- 指向FIFO的起始地址 out -- 音頻流的方向,對(duì)于AudioTrack,out=1,對(duì)于AudioRecord,out=0audio_track_cblk_t的主要成員函數(shù):framesAvailable_l()和framesAvailable()用于獲取FIFO中可寫的空閑空間的大小,只是加鎖和不加鎖的區(qū)別。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 可能有人會(huì)問,應(yīng)為這是一個(gè)環(huán)形的buffer,一旦user越過了buffer_end以后,應(yīng)該會(huì)發(fā)生下面的情況: _____________________________________________ ^ ^ ^ ^ buffer_start user(u) server(s) buffer_end這時(shí)候u在s的前面,用上面的公式計(jì)算就會(huì)錯(cuò)誤,但是android使用了一些技巧,保證了上述公式一直成立。我們先看完下面三個(gè)函數(shù)的代碼再分析: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的作用是調(diào)整當(dāng)前偏移的位置,可以看到,他們僅僅是把成員變量user或server的值加上需要移動(dòng) 的數(shù)量,user和server的值并不考慮FIFO的邊界問題,隨著數(shù)據(jù)的不停寫入和讀出,user和server的值不斷增加,只要處理得 當(dāng),user總是出現(xiàn)在server的后面,因此frameAvalible()和frameReady()中的算法才會(huì)一直成立。根據(jù)這種算 法,user和server的值都可能大于FIFO的大?。篺ramCount,那么,如何確定真正的寫指針的位置呢?這里需要用到userBase這一 成員變量,在stepUser()中,每當(dāng)user的值越過(userBase+frameCount),userBase就會(huì)增加 frameCount,這樣,映射到FIFO中的偏移總是可以通過(user-userBase)獲得。因此,獲得當(dāng)前FIFO的寫地址指針可以通過成員 函數(shù)buffer()返回:p = mClbk-buffer(mclbk-user);在AudioTrack中,封裝了兩個(gè)函數(shù):obtainBuffer()和releaseBuffer()操作 FIFO,obtainBuffer()獲得當(dāng)前可寫的數(shù)量和寫指針的位置,releaseBuffer()則在寫入數(shù)據(jù)后被調(diào)用,它其實(shí)就是簡(jiǎn)單地調(diào)用 stepUser()來調(diào)整偏移的位置。IMemory接口在createTrack的過程中,AudioFlinger會(huì)根據(jù)傳入的frameCount參數(shù),申請(qǐng)一塊內(nèi)存,AudioTrack可以通過 IAudioTrack接口的getCblk()函數(shù)獲得指向該內(nèi)存塊的IMemory接口,然后AudioTrack通過該IMemory接口的 pointer()函數(shù)獲得指向該內(nèi)存塊的指針,這塊內(nèi)存的開始部分就是audio_track_cblk_t結(jié)構(gòu),緊接著是大小為frameSize的 FIFO內(nèi)存。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結(jié)構(gòu) 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)


當(dāng)前名稱:android波形,android畫波形圖
分享地址:http://weahome.cn/article/dsohdse.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部