將usb 聲卡集成到android4.0上


1. 任務分解
1.1 android 使用了tinyalsa庫,看了一下代碼,這個庫實在功能太弱,google老是放棄穩定的好用的東西不用,本身亂搞,或許是由於licence的問題?因而第一個子任務是將原來android 2.3.3上的alsa庫移植上來。
1.2 內核要打開配置,至少把UAC打開
1.3 找一個usb聲卡,淘寶上7塊錢就能買到,真他媽便宜,不知道這些人怎麼掙錢的!
1.4 使用編譯出來的alsa庫進行測試,驗證可正常使用
1.5 使用android自帶的tinyalsa進行播放測試,由於android的HAL層是調用這個庫的,因此這個還非測試不可
1.6 修改audio_hw(android聲音的HAL層)代碼,將UAC聲卡接入到android4.0中去
1.7 使用android自帶的Music.apk,SoundRecorder.apk進行最後測試,我想這兩個過了其它軟件應該沒問題了吧
2. 各子任務的具體實施
2.1 移植alsa庫
將原來android2.3.3/external/alsa-lib,android2.3.3/external/alsa-utils
這兩個庫cp到 android4.0/external/中 直接編譯生成 相應的alsa_aplay,alsa_amixer,alsa_ctl,並cp到目標板子上,
將alsa的配置文件也從android2.3.3 複製到目標板子/system/usr/share/alsa 目錄下
具體配置文件爲 
./pcm/center_lfe.conf
./pcm/surround40.conf
./pcm/surround50.conf
./pcm/dmix.conf
./pcm/rear.conf
./pcm/front.conf
./pcm/surround71.conf
./pcm/surround51.conf
./pcm/dsnoop.conf
./pcm/dpl.conf
./pcm/default.conf
./pcm/modem.conf
./pcm/side.conf
./pcm/iec958.conf
./pcm/surround41.conf
./alsa.conf
./cards/aliases.conf

adb shell進入目標板
執行alsa_amixer ,alsa_aplay -l 執行成功,到此alsa工具移植成功,這點看來沒有太大難度

2.2 打開kernel配置,menuconfig,打開以下配置
========================================================
Device Drivers --->
<*> Sound card support --->
     <*> Advanced Linux Sound Architecture --->
  • USB sound devices ---> 
    <*> USB Audio/MIDI driver
    ========================================================
    從新編譯內核,並燒寫到目標板子上
    2.3 找一個usb聲卡,插入usb端口
    並使用dmesg查看內核日誌輸出,從dmesg上應該能夠看見UAC設備的驅動加載信息
    ls /dev/snd/*
    /dev/snd/pcmC0D0c
    /dev/snd/pcmC0D0p
    /dev/snd/pcmC1D0c //uac capture
    /dev/snd/pcmC1D0p //uac play out

    說明聲卡已經加載成功了
    2.4 使用alsa 工具測試
    列出設備
    alsa_aplay -l
    alsa_aplay 001.wav -D default:CARD=USB Set  default:CARD=USB Set是從alsa_aplay -l中列出的設備名
    可正常聽到聲音
    2.5 使用tinyalsa測試
    # tinyplay -d 1 001.wav //竟然提示invalid parameter 只能看代碼了


    external/tinyalsa/tinyplay.c
    發現主要問題在於tinyplay只打開了設備0多的設備不理,這點真比alsa庫差遠了,只能山寨一下了,修改以下紅色字體部分


    void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
                     unsigned int rate, unsigned int bits);


    int main(int argc, char **argv)
    {
        FILE *file;
        struct wav_header header;
        unsigned int device = 0;
              unsigned int card = 0;



        argv += 2;
        while (*argv) {
    if (strcmp(*argv, "-d") == 0) {
                argv++;
                device = atoi(*argv);
                    }
                    if (strcmp(*argv, "-c") == 0) {
                argv++;//命令行多一個 -c 選項,這樣能夠選擇聲卡了
                card = atoi(*argv);
                    }
            argv++;
        }


        fread(&header, sizeof(struct wav_header), 1, file);


        if ((header.riff_id != ID_RIFF) ||
            (header.riff_fmt != ID_WAVE) ||
            (header.fmt_id != ID_FMT) ||
            (header.audio_format != FORMAT_PCM) ||
            (header.fmt_sz != 16)) {
            fprintf(stderr, "Error: '%s' is not a PCM riff/wave file\n", argv[1]);
            fclose(file);
            return 1;
        }


        play_sample(file,card, device, header.num_channels, header.sample_rate,
                    header.bits_per_sample);


        fclose(file);


        return 0;
    }
    void play_sample(FILE *file,unsigned int card, unsigned int device, unsigned int channels,
                     unsigned int rate, unsigned int bits)
    {
        struct pcm_config config;
        struct pcm *pcm;
        char *buffer;
        int size;
        int num_read;
    //config 的配置很重要,tinyalsa直接從wav文件把channels,rate,等讀出來並配置進聲卡,而像我買的7塊錢聲卡,功能很弱從windows的設備管理器上看,只支持 16000hz, 16bits,2 channels
    //因此我只好錄一個 16000hz,16bits,2 channel的001.wav文件了
    //若是config 配置出錯,將看到 invalid parameter的錯誤提示,這點和alsa_aplay的功能 就差遠了
        config.channels = channels;
        config.rate = rate;
        config.period_size = 1024;
        config.period_count = 4;
        if (bits == 32)
            config.format = PCM_FORMAT_S32_LE;
        else if (bits == 16)
            config.format = PCM_FORMAT_S16_LE;
        config.start_threshold = 0;
        config.stop_threshold = 0;
        config.silence_threshold = 0;


        pcm = pcm_open(card, device, PCM_OUT, &config);//原來是pcm = pcm_open(0, device, PCM_OUT, &config);
        if (!pcm || !pcm_is_ready(pcm)) {
            fprintf(stderr, "Unable to open PCM device %u (%s)\n",
                    device, pcm_get_error(pcm));
            return;
        }


        size = pcm_get_buffer_size(pcm);
    buffer = malloc(size);
        if (!buffer) {
            fprintf(stderr, "Unable to allocate %d bytes\n", size);
            free(buffer);
            pcm_close(pcm);
            return;
        }


        printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);


        do {
            num_read = fread(buffer, 1, size, file);
            if (num_read > 0) {
                if (pcm_write(pcm, buffer, num_read)) {
                    fprintf(stderr, "Error playing sample\n");
                    break;
                }
            }
        } while (num_read > 0);


        free(buffer);
        pcm_close(pcm);
    }

              按以上修改完文件後從新編譯,並執行
    # tinyplay -d 1 001.wav
       成功從UAC聽到聲音了,看來集成到android HAL只差一步了,由於android HAL也是用的tinyalsa庫的


    2.6 修改audio_hw(android聲音的HAL層)代碼,將UAC聲卡接入到android4.0中去
    HAL層代碼位置:
    hardware/targetboard目標板/audio/audio_hw.c
    找到 pcm_open(card, port, PCM_OUT,&config); 的地方把相應的config配置成和tinyplay.c里正確的配置
    像個人聲卡就是16000hz 16bits,2 channels
    從新編譯  生成 audio.primary.$(TARGET_BOARD_PLATFORM).so 並更新到目標板上
    從新 啓動 mediaserver進程 (kill掉這個進程,就會自動重啓,並從新加載audio.primary.xxx.so庫)
    使用android音樂播放,播放mp3,OK可成功聽到聲音了
    使用android錄音機, 可成功錄音了
    android

相關文章
相關標籤/搜索