Linux オーディオ コレクションとローカライズされたプラットフォームで遭遇する落とし穴 (1)

Linux オーディオ コレクションとローカライズされたプラットフォームで遭遇する落とし穴 (1)

最近、私は国内プラットフォーム向けのソフトウェア プロジェクトの開発に取り組んでいます。これは、国産チップをベースにした Galaxy Kirin システムです。重要なモジュールの 1 つはオーディオ データの収集と再生であり、言うまでもなく、再生にはマイクの収集やデスクトップ システムのサウンドの収集が含まれます。Galaxy Kirin は単なる Linux なので、ALSA を直接使用しないほうが良いと考えている人も多く、私も最初はそう思っていましたが、実際に開発してみると、まだ自分で調査する必要があるさまざまな落とし穴があることがわかりました。ここに簡単に記録しておきます。

これらはすべて Linux ですが、チップは同じアーキテクチャと同じ命令セットに基づいていますが、最終的にチップの実装が異なることを考慮すると、ハードウェアの相互作用に関与するすべてのソフトウェア部分も異なり、最終的には通常の Linux の通常の使用方法では使用できないレベルのインターフェイスです。

Linux ALSA オーディオ コレクション

まず、Galaxy Kirin は Linux システムなので、最初に考慮すべきことは、Linux のデフォルトのサウンド カード ドライバーである ALSA (Advanced Linux Sound Architecture) を通じて収集することです。同時に ALSA Lib もあります。アプリケーション呼び出しのユーザー層にあり、その全体構造図は次のようになります。
ここに画像の説明を挿入します

アプリケーションは通常 alsa-lib を介して使用されますが、システムに alsa-lib がない場合は、コマンドで開発ライブラリをインストールすることで使用できます。例えば

sudo apt-get install libasound2-dev

もう 1 つ注意すべき点は、Android システムの場合、通常 alsa はシステム内に存在しませんが、その簡易版 tiny-alsa が存在し、インターフェイス名も異なりますが、一般的な呼び出しプロセスは同じです。

alsa オーディオ コレクションには、いくつかの主要な機能があります

#include <sys/asoundlib.h>

/***
 创建alsa pcm handle去连接设备
 @param handle: 返回创建的PCM handle
 @param name: 设备名称,ASCII编码
 @param stream: 标明采集或者播放(SND_PCM_STREAM_CAPTURE, SND_PCM_STREAM_PLAYBACK)
 @param mode: 打开模式(see SND_PCM_NONBLOCK, SND_PCM_ASYNC)
 @return: 0表示成功,小于0表示错误
*/
int snd_pcm_open( snd_pcm_t **handle, const char* name, int stream, int mode );

/***
 读取音频帧
 @param handle: PCM handle
 @param buffer: frames containing buffer
 @param size: frames to be read
 @return: 实际读取的音频帧个数,小于0表示错误
*/
ssize_t snd_pcm_readi( snd_pcm_t *handle, void *buffer, size_t size );

/***
 关闭
 @param handle: PCM handle
 @return: 实际读取的音频帧个数,小于0表示错误
*/
int snd_pcm_close( snd_pcm_t *handle );

/***
 准备使用PCM
 @param handle: PCM handle
 @return: 实际读取的音频帧个数,小于0表示错误
*/
int snd_pcm_prepare( snd_pcm_t *handle );

インターフェイスはシンプルでパラメータが少ないため、非常に使いやすいです。基本的に Linux での収集と再生の最初の選択肢です。これを呼び出す方法を示す簡単な例を次に示します。

  1. オーディオデバイスを開いてパラメータを設定します
SIMPLE_LOG("try open %s\n", device_name_.c_str());

int ret = snd_pcm_open(&alsa_pcm_, device_name_.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (!alsa_pcm_ || ret < 0)
{
    
    
	SIMPLE_LOG("open %s failed, ret: %d\n", device_name_.c_str(), ret);

	return false;
}

snd_pcm_hw_params_t* params;

snd_pcm_hw_params_alloca (&params);
snd_pcm_hw_params_any (alsa_pcm_, params);
snd_pcm_hw_params_set_access (alsa_pcm_, params,
	SND_PCM_ACCESS_RW_INTERLEAVED);

snd_pcm_format_t format;
switch (bits_per_sam_) {
    
    
case 8:
	format = SND_PCM_FORMAT_S8;
	break;
case 16:
	format = SND_PCM_FORMAT_S16_LE;
	break;
case 24:
	format = SND_PCM_FORMAT_S24_LE;
	break;
case 32:
	format = SND_PCM_FORMAT_S32_LE;
	break;
default:
	format = SND_PCM_FORMAT_S16_LE;
	break;
}
snd_pcm_hw_params_set_format (alsa_pcm_, params, format);

snd_pcm_hw_params_set_channels (alsa_pcm_, params, channel_count_);

unsigned int rate = sample_rate_;
snd_pcm_hw_params_set_rate_near (alsa_pcm_, params, &rate, NULL);

sample_size_ = channel_count_ * (bits_per_sam_/8);

/* Activate the parameters */
ret = snd_pcm_hw_params (alsa_pcm_, params);
if (ret < 0)
{
    
    
	SIMPLE_LOG("set param failed, ret: %d\n", ret);

	snd_pcm_close (alsa_pcm_);
	alsa_pcm_ = NULL;
	return false;
}
  1. 音声データの読み取り
bool AlsaCapture::ReadData()
{
    
    
    int read_size = 0;
    snd_pcm_uframes_t need_frames = real_sample_count_;
    for (;;)
    {
    
    
        if (read_size >= pcm_buf_.size())
        {
    
    
            break;
        }

        int ret = 0;
        while (true)
        {
    
    
            char* read_buf = &pcm_buf_[0] + read_size;
            ret = snd_pcm_readi(alsa_pcm_, read_buf, need_frames);
            if (ret >= 0)
            {
    
    
                break;
            }

            if (ret == -EAGAIN)
            {
    
    
                SIMPLE_LOG("snd_pcm_readi EAGAIN\n");
                return false;
            }

            if (AlsaXRunRecover(alsa_pcm_, ret) < 0)
            {
    
    
                SIMPLE_LOG("ALSA read error: %s\n", snd_strerror(ret));
                return false;
            }
        }

        read_size += ret * sample_size_;
        need_frames -= ret;
    }
    return true;
}

このようにして、オーディオ データの収集が完了しますが、2 番目のステップでデータを読み取る前に、最初に snd_pcm_prepare を呼び出す必要があります。そうしないと、データ収集が正常に実行されません。

ローカライズされたチップ プラットフォームで発生する問題

通常の Linux では、このように記述することで目的の音声収集機能を実現し、データをさらに整理してエンコードして送信することができます。しかし、国産チッププラットフォームのGalaxy Kirinシステムでは、デバイスをオープンする関数呼び出しやパラメータ設定はすべて成功するが、データ収集が常に異常であるか、意味のないノイズが返されるという問題が発生しました。または、読み取りインターフェイスが単に EAGAIN エラーを報告します。

最初は、ALSA のデフォルト デバイスに問題があるのではないかと思いました。問題のローカライズされたチップ プラットフォームには 2 つのサウンド カードがあり、1 つは正常に機能し、もう 1 つは無効なサウンド カードだったためです。この情報は、次のようなコマンド ラインを使用して表示できます。

サウンドカードを確認します。

猫/proc/asound/cards

収集機器を見る:

sudo レコード -l

再生デバイスを確認します。

猫遊び -l

そこで、システム設定でデフォルトのサウンドカードを設定してみましたが、キャラクターインターフェースを備えたALSA設定ツール「alsamixer」を推奨します。以下のコマンドでインストールできます。

sudo apt-get インストール alsa-utils

起動後、このようなインターフェイスがあります
ここに画像の説明を挿入します

しかし、変更後、デフォルトデバイスの変更は alsa 収集の結果に影響を及ぼさないことが判明しました。したがって、すべての録音デバイスをリストし、デバイス名を指定しても、同じ結果が発生します。何度も試みて失敗しましたが、最終的にはオーディオ デバイスのデータ収集に ALSA を使用することを諦め、より複雑な PulseAudio フレームワークを使用する必要がありました。最終結果は、上位レベルの PulseAudio が依然として有効なオーディオ デバイスと無効なオーディオ デバイスを正しく処理し、マイク/デスクトップ システム サウンドを正しく返すことも証明しています。具体的なプロセスについては次の記事で書きたいと思います。

ここに画像の説明を挿入します

ご協力のために作者 hbstream を追加してください。(転載の際は作者と出典を明記してください)


おすすめ

転載: blog.csdn.net/haibindev/article/details/128812467