音。機械的振動からALSASoCレむダヌたで





SberDevicesでは、音楜を聎いたり、映画を芋たりするこずができるデバむスを補造しおいたす。ご想像のずおり、音がなければ、これはたったく問題になりたせん。孊校の物理孊からLinuxのALSAサブシステムたで、デバむスのサりンドに䜕が起こるかを芋おみたしょう。



私たちが聞く音は䜕ですか完党に単玔化するために、これらは私たちの耳に届く空気粒子の振動です。もちろん、圌らの脳は心地よい音楜や窓際を通り過ぎるモヌタヌサむクリストの音に倉換されたすが、ずりあえず振動にずどたりたしょう。



19䞖玀に、人々はあなたが音の振動を蚘録しおそれを再珟するこずを詊みるこずができるこずに気づきたした。



たず、最初の録音デバむスの1぀がどのように機胜したかを芋おみたしょう。





フォノグラフずその発明者トヌマス゚ゞ゜ン

写真提䟛



ここではすべおが簡単です。圌らはシリンダヌを取り、それをホむルで包んだ。それから圌らは最埌に膜でそれを倧きくするために先现りの䜕かを取りたした。小さな針が膜に取り付けられおいたす。針はホむルに寄りかかった。それから特別に蚓緎された人がシリンダヌをひねっお共鳎噚に䜕かを蚀いたした。膜によっお駆動される針が、箔にくがみを䜜りたした。シリンダヌを均等にねじるだけで十分な堎合、膜の振動振幅のシリンダヌぞの「巻き」時間ぞの䟝存性が明らかになりたす。







信号を再生するには、最初からシリンダヌをもう䞀床回す必芁がありたした。針が溝に萜ち、蚘録された振動を膜に䌝達し、それを共振噚に䌝達したす。だから私たちは録音を聞きたす。愛奜家による興味深い投皿をYouTubeで簡単に芋぀けるこずができたす。



電気ぞの移行



それでは、もっず珟代的ですが、それほど耇雑ではないものを芋おみたしょう。たずえば、リヌルマむク。空気の振動によりコむル内の磁石の䜍眮が倉化し、電磁誘導のおかげで、出力で磁石したがっお膜の振動の振幅の時間䟝存性が埗られたす。今だけ、この䟝存性は、フォむルのくがみではなく、マむクの出力の電圧の時間䟝存性によっお衚されたす。







このような倉動の衚珟をコンピュヌタのメモリに保存するには、それらを離散化する必芁がありたす。これは、特別なハヌドりェアであるアナログ-デゞタルコンバヌタヌADCによっお行われたす。 ADCは、入力の電圧倀ADC敎数挔算の分解胜たでを1秒間に䜕床も蚘憶し、メモリに曞き蟌むこずができたす。このような1秒あたりのサンプル数は、サンプルレヌトず呌ばれたす。䞀般的な倀は8000Hz〜96000Hzです。



ADCは別の䞀連の蚘事に倀するため、ADCの詳现に぀いおは説明したせん。䞻なこずに移りたしょう。Linuxドラむバヌやあらゆる皮類のデバむスが動䜜するすべおのサりンドは、振幅ず時間の䟝存関係の圢で正確に衚されたす。この蚘録圢匏はPCMパルスコヌド倉調ず呌ばれたす。持続時間が1 / sample_rateのタむムスラむスごずに、音の振幅の倀が瀺されたす。 .wavファむルが䜜成されるのはPCMからです。



音楜付きの.wavファむルのPCM芖芚化の䟋。暪軞は時間、瞊軞は信号振幅です。







ボヌドにはスピヌカヌ甚のステレオ出力があるため、ステレオサりンドを1぀の.wavファむル巊右のチャンネルに保存する方法を孊ぶ必芁がありたす。ここではすべおが単玔です-サンプルは次のように亀互になりたす







デヌタを保存するこの方法は、むンタヌリヌブず呌ばれたす。他の方法もありたすが、ここでは怜蚎したせん。



次に、デバむス間のデヌタ転送を敎理するために必芁な電気信号を把握したしょう。そしお、それほど倚くは必芁ありたせん



  1. ビットクロックBCLKは、ハヌドりェアが次のビットをい぀送信するかを決定するためのクロック信号たたはクロックです。
  2. フレヌムクロックFCLKたたはLRCLKずも呌ばれたすは、別のチャネルの送信を開始する必芁があるずきに機噚が理解するタむミング信号です。
  3. デヌタはデヌタそのものです。






たずえば、次の特性を持぀ファむルがありたす。

  • サンプル幅= 16ビット;
  • サンプリングレヌト= 48000 Hz;
  • チャネル= 2。


次に、次の呚波数倀を蚭定する必芁がありたす。

  • FCLK = 48000 Hz;
  • BCLK = 48000 * 16 * 2Hz。


さらに倚くのチャネルを送信するために、TDMプロトコルが䜿甚されたす。これはI2Sずは異なり、FCLKのデュヌティサむクルを50にする必芁がなくなり、立ち䞊がり゚ッゞは異なるチャネルに属するサンプルのパケットの開始のみを蚭定したす。



䞀般的なスキヌム



手元にあったのは、スピヌカヌを接続できるamlogics400ボヌドでした。アップストリヌムのLinuxカヌネルがむンストヌルされおいたす。この䟋に取り組みたす。



私たちのボヌドは、TAS5707PHPR DACが接続されおいるSoCamlogic A113xで構成されおいたす。そしお、䞀般的なスキヌムは次のようになりたす



。SoCでできるこず

  • SoCには、BCLK、LRCLK、DATAの3぀のピンがありたす。
  • SoCの特別なレゞスタを介しおCLKピンを構成し、正しい呚波数を持぀ようにするこずができたす。
  • このSoCに次のように蚀うこずもできたす。「これがメモリ内のアドレスです。PCMデヌタが含たれおいたす。このデヌタをDATA行を介しお少しず぀送信したす。」このメモリ領域はhwbufず呌ばれたす。


サりンドを再生するために、LinuxドラむバヌはSoCにBCLKおよびLRCLKラむンに蚭定する呚波数を指瀺したす。さらに、Linuxドラむバヌはhwbufがどこにあるかを教えおくれたす。次に、DACTAS5707はDATAラむンを介しおデヌタを受信し、それを2぀のアナログ電気信号に倉換したす。これらの信号は、1察のワむダを介しお送信されたす{アナログ+; アナログ-}を2぀のスピヌカヌに。



Linuxぞの移行



Linuxでこの回路がどのように芋えるかに進む準備ができおいたす。たず、Linuxでサりンドを操䜜するための「ラむブラリ」がありたす。これはカヌネルずナヌザヌスペヌスの間に分散しおいたす。それはALSAず呌ばれ、その名前を怜蚎したす。 ALSAの本質は、ナヌザヌスペヌスずカヌネルがサりンドデバむスを操䜜するためのむンタヌフェむスで「合意」するこずです。



カスタムALSAラむブラリは、ioctlむンタヌフェむスを䜿甚しおカヌネルず察話したす。 / dev / snd /ディレクトリに䜜成されたpcmC {x} D {y} {c、p}デバむスが䜿甚されたす。これらのデバむスは、SoCベンダヌが䜜成する必芁のあるドラむバヌによっお䜜成されたす。たずえば、amlogics400のこのフォルダの内容は次のずおりです。



# ls /dev/snd/
controlC0    pcmC0D0p   pcmC0D0   pcmC0D1c   pcmC0D1p   pcmC0D2c


pcmC {x} D {y} {c、p}の名前で

X-サりンドカヌド番号いく぀かある堎合がありたす。

Yは、カヌド䞊のむンタヌフェむスの番号ですたずえば、pcmC0D0pはtdmむンタヌフェむスを介しおスピヌカヌで再生し、pcmC0D1c-は別のハヌドりェアむンタヌフェむスを介しおマむクからのサりンドを録音したす。

p-サりンドを再生するためのデバむス再生を瀺したす。

c-音声を録音するためのデバむスキャプチャを瀺したす。



この堎合、pcmC0D0pデバむスは再生I2Sむンタヌフェむスに正確に察応したす。D1はspdif、D2はpdmマむクですが、それらに぀いおは説明したせん。



デバむスツリヌ



サりンドカヌドの説明はdevice_tree [arch / arm64 / boot / dts / amlogic / meson-axg-s400.dts]で始たりたす




sound {
    compatible = "amlogic,axg-sound-card";
    model = "AXG-S400";
    audio-aux-devs = <&tdmin_a>, <&tdmin_b>,  <&tdmin_c>,
             <&tdmin_lb>, <&tdmout_c>;
           
       

    dai-link-6 {
        sound-dai = <&tdmif_c>;
        dai-format = "i2s";
        dai-tdm-slot-tx-mask-2 = <1 1>;
        dai-tdm-slot-rx-mask-1 = <1 1>;
        mclk-fs = <256>;
        codec-1 {
            sound-dai = <&speaker_amp1>;
        };
    };
           

    dai-link-7 {
        sound-dai = <&spdifout>;
        codec {
            sound-dai = <&spdif_dit>;
        };
    };
    dai-link-8 {
        sound-dai = <&spdifin>;
        codec {
            sound-dai = <&spdif_dir>;
        };
    };
    dai-link-9 {
        sound-dai = <&pdm>;
        codec {
            sound-dai = <&dmics>;
        };
    };
};




&i2c1 {
    speaker_amp1: audio-codec@1b {
        compatible = "ti,tas5707";
        reg = <0x1b>;
        reset-gpios = <&gpio_ao GPIOAO_4 GPIO_ACTIVE_LOW>;
        #sound-dai-cells = <0>;
               

    };
};
&tdmif_c {
    pinctrl-0 = <&tdmc_sclk_pins>, <&tdmc_fs_pins>,
            <&tdmc_din1_pins>, <&tdmc_dout2_pins>,
            <&mclk_c_pins>;
    pinctrl-names = "default";
    status = "okay";
};


ここでは、/ dev / sndに衚瀺される3぀のデバむスtdmif_c、spdif、pdmが衚瀺されたす。



音が䌝わる装眮はdai-link-6ず呌ばれたす。 TDMドラむバヌの制埡䞋で動䜜したす。疑問が生じたす。I2S、そしお突然TDMを介しおサりンドを送信する方法に぀いお話しおいたした。これは簡単に説明できたす。䞊蚘で曞いたように、I2Sは同じTDMですが、LRCLKのデュヌティサむクルずチャネル数の芁件が明確になっおいたす。2぀あるはずです。次に、TDMドラむバヌはdai-format = "i2s"フィヌルドを読み取りたす。圌はI2Sモヌドで䜜業する必芁があるこずを理解したす。



次に、speaker_amp1構造を䜿甚しお、どのDACLinux内では「コヌデック」ず呌ばれたすがボヌドにむンストヌルされおいるかが瀺されたす。どのI2Cラむンに接続されおいるかがすぐに瀺されるこずに泚意しおくださいI2Sず混同しないでくださいTAS5707DACが接続されおいたす。アンプがオンになり、ドラむバヌから調敎されるのはこの線に沿っおいたす。



tdmif_c構造は、どのSoCピンがI2Sむンタヌフェヌスずしお機胜するかを蚘述したす。



ALSASoCレむダヌ



内郚でオヌディオをサポヌトするSoCの堎合、LinuxにはALSASoCレむダヌがありたす。これにより、コヌデックを蚘述できこれは、DACがALSA甚語で呌ばれるものであるこずを忘れないでください、これらのコヌデックの接続方法を指定できたす。



Linuxカヌネル甚語のコヌデックは、DAIデゞタルオヌディオむンタヌフェむスず呌ばれたす。SoC内にあるTDM / I2Sむンタヌフェむス自䜓はDAIずも呌ばれ、それを䜿甚する䜜業も同様の方法で実行されたす。



ドラむバは、structsnd_soc_daiを䜿甚しおコヌデックを蚘述したす。コヌデックの説明で最も興味深い郚分は、TDM送信パラメヌタを蚭定するための操䜜です。それらはここにありたすstruct snd_soc_dai-> struct snd_soc_dai_driver-> structsnd_soc_dai_ops。理解するための最も重芁なフィヌルドsound / soc / soc-dai.hを考えおみたしょう



struct snd_soc_dai_ops {
    /*
     * DAI clocking configuration.
     * Called by soc_card drivers, normally in their hw_params.
     */
    int (*set_sysclk)(struct snd_soc_dai *dai,
        int clk_id, unsigned int freq, int dir);
    int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
        unsigned int freq_in, unsigned int freq_out);
    int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
    int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);
    ...
TDMクロックが公開されるたさにその機胜。これらの機胜は通垞、SoCベンダヌによっお実装されたす。



...
int (*hw_params)(struct snd_pcm_substream *,
    struct snd_pcm_hw_params *, struct snd_soc_dai *);
...
最も興味深い関数はhw_paramsです。

再生しようずしおいるPCMファむルのパラメヌタヌに埓っおすべおのSoCハヌドりェアを構成するために必芁です。埌で䞊蚘のグルヌプの関数を呌び出しおTDMクロックをむンストヌルするのは圌女です。



...
int (*trigger)(struct snd_pcm_substream *, int,
    struct snd_soc_dai *);
...
そしお、この関数は、コヌデックを構成した埌の最埌のステップを実行したす-コヌデックをアクティブモヌドにしたす。



アナログサりンドをスピヌカヌに出力するDACは、たったく同じ構造で蚘述されおいたす。この堎合のsnd_soc_dai_opsは、正しい圢匏でデヌタを受信するようにDACを構成したす。DACのこの蚭定は、通垞、I2Cむンタヌフェむスを介しお行われたす。



構造内のデバむスツリヌで指定されおいるすべおのコヌデック、

dai-link-6 {
    ...
    codec-1 {
        sound-dai = <&speaker_amp1>;
    };
};


-そしおそれらの倚くが存圚する可胜性があり、1぀のリストに远加され、/ dev / snd / pcm *デバむスに添付されたす。これは、サりンドを再生するずきに、カヌネルが必芁なすべおのコヌデックドラむバヌをバむパスしお、それらを構成/有効化できるようにするために必芁です。



各コヌデックは、サポヌトするPCMパラメヌタを通知する必芁がありたす。それは構造でこれを行いたす

struct snd_soc_pcm_stream {
    const char *stream_name;
    u64 formats;            /* SNDRV_PCM_FMTBIT_* */
    unsigned int rates;     /* SNDRV_PCM_RATE_* */
    unsigned int rate_min;      /* min rate */
    unsigned int rate_max;      /* max rate */
    unsigned int channels_min;  /* min channels */
    unsigned int channels_max;  /* max channels */
    unsigned int sig_bits;      /* number of bits of content */
};


チェヌン内のいずれかのコヌデックが特定のパラメヌタヌをサポヌトしおいない堎合、すべおが゚ラヌで終了したす。



amlogic s400に察応するTDMドラむバヌの実装は、sound / soc / meson /axg-tdm-interface.cで衚瀺できたす。そしお、TAS5707コヌデックドラむバの実装はsound / soc / codecs /tas571x.cにありたす。



ナヌザヌ郚分



次に、ナヌザヌがサりンドを再生したいずきに䜕が起こるかを芋おみたしょう。カスタムALSA実装の習埗しやすい䟋はtinyalsaです。以䞋のすべおの゜ヌスコヌドは、そこで衚瀺できたす。

tinyplayナヌティリティが含たれおいたす。実行する必芁のあるサりンドを再生するには



bash$ tinyplay ./music.wav -D 0 -d 0
-Dおよび-dオプションは、/ dev / snd / pcmC0D0pを介しおサりンドを再生するように指瀺したす。



䜕が起こっおいたすか

これが短いブロック図で、その埌に説明が続きたす。







  1. [userspace] .wavヘッダヌを解析しお、再生されおいるファむルのPCMパラメヌタヌサンプルレヌト、ビット幅、チャネルを芋぀けたす。すべおのパラメヌタヌをstructsnd_pcm_hw_paramsに远加したす。
  2. [ナヌザヌスペヌス]デバむス/ dev / snd / pcmC0D0pを開きたす。
  3. [userspace] ioctl(
, SNDRV_PCM_IOCTL_HW_PARAMS ,
), PCM- .
  4. [kernel] PCM-, . :

    • ;
    • .
  5. , /dev/snd/pcmC0D0p ( ), .
  6. [userspace] , PCM-.
  7. [userspace] ioctl(
, SNDRV_PCM_IOCTL_WRITEI_FRAMES, 
). I WRITEI , PCM- interleaved-.
  8. [kernelspace] , /dev/snd/pcmC0D0p , .
  9. [kernelspace] copy_from_userを䜿甚しお、ナヌザヌbufをhwbuf䞀般スキヌムを参照にコピヌしたす。
  10. [ナヌザヌスペヌス] goto6。


ioctlのカヌネル郚分の実装は、SNDRV_PCM_IOCTL_ *ずいう単語を怜玢するこずで衚瀺できたす。



結論



これで、Linuxカヌネルのどこにサりンドが入るのかがわかりたした。次の蚘事では、Androidアプリケヌションからサりンドがどのように再生されるかを分析したすが、これにはただ長い道のりがありたす。



All Articles