Featured image of post オーディオレベルとかをディスプレイに表示する

オーディオレベルとかをディスプレイに表示する

ATmega328PでADCを使ってオーディオ信号を計ってディスプレイに表示した。

ADCに入力する電圧

アンプをDC(Direct - Coupled)構成で設計した。 その構成では入力オーディオ電圧は正負に振れるので,
入力電圧を絶対値回路に通してAVR内蔵10bit-ADCに入力します。

この回路の最大入力電圧 Vm の実効値は VADC が3.3Vなので

$$ V_m = \frac{3.3}{\sqrt 2} = 2.3 Vrms $$

となる。
入力のラインレベルは民生用音響機器では-10dBV1なので

$$ linelevel = 1.0 \times 10^{\frac{-10.0}{20.0}} = 0.316Vrms $$

余裕があるようだ。

ADCの出力値とADCの入力電圧との関係

AVRのADCに入力される電圧は入力回路から

$$ V_{ADC} = | Vin | $$

ADCの出力値1bit当たりの電圧は
AREF が 3.3V
ADCの解像度 10bit = 210 = 1024
のときに $$ V_{step} = \frac{3.3}{1024} = 0.0012695 V \approx 1.27 mV $$

となる。

入力電圧の実効値を得る処理

周期的に変化する交流入力電圧の実効値を得る処理です。

二乗平均平方根2 を交流入力電圧にしたい。 オーディオ信号処理

ピークレベルを得る処理

雰囲気だけ演出できたらいいので,レベル値の最大値を更新しながら周期的に減らすだけにしておいた。

ターゲットとする表示画面

  • 表示幅(文字数)
    16文字

  • 表示行数
    2行

オーディオレベルの表示イメージ

フルスケールで30目盛りを表示します. フルスケール表示中オーディオレベルメータ

0vuで28目盛りを表示し,後ろにピークレベル指針を表示します. 0vu表示中オーディオレベルメータ

-48vu未満でピークレベル指針のみを表示します. -48vu未満表示中オーディオレベルメータ

目盛りとADC値の対応

30個ある目盛りに対応するVU値表から10bit-ADCサンプル値表をGNU Octaveに計算させます.

単位の変換

VUメーター - Wikipedia
デシベル - Wikipedia これを参考に

多分こんな感じ

GNU Octave用オーディオレベルメーター計算スクリプト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
% オーディオレベルメータの値を計算する
% http://ak1211.com
% Copyright (c) 2015 Akihiro Yamamoto
%
%
% This software is released under the MIT License.
% http://opensource.org/licenses/mit-license.php
%

% VU値の表
%               1     2     3     4     5     6     7     8     9    10    11    12    13    14    15    16    17    18    19    20    21    22    23    24    25    26    27    28    29    30
vu_table = [ -48.0 -44.0 -40.0 -36.0 -32.5 -29.0 -25.5 -22.0 -20.0 -18.0 -16.0 -14.0 -13.0 -12.0 -11.0 -10.0 -9.00 -8.00 -7.00 -6.00 -5.00 -4.00 -3.00 -2.00 -1.50 -1.00 -0.50 0.000 +1.50 +3.00 ];

%
% volts[rms]の値に対応したdBuを返す
%
function dBu = dBu_of_volts (vRMS)
    dBu = 20.0 * log10 (vRMS / 0.775);
endfunction

%
% volts[rms]の値に対応したvuを返す
%
function vu = vu_of_volts (vRMS)
    vu = -4 + dBu_of_volts (vRMS);
endfunction

%
% dBuの値に対応したvolts[rms]を返す
%
function volts = volts_of_dBu (dBu)
    volts = 0.775 * 10 .** (dBu / 20.0);
endfunction


%
% VUの値に対応したvolts[rms]を返す
%
function volts = volts_of_vu (vu)
    volts = volts_of_dBu(vu + 4);
endfunction

%
% volts[rms]の値に対応したサンプル値を返す
%
function value = sample_value_of_volts (volts)
    adc_volts   = 3.3;
    sample_bits = 10;
    %
    resolution  = adc_volts / (2^sample_bits);
    value = floor (volts / resolution);
endfunction

%
% 計算結果を出力
%
values = sample_value_of_volts (volts_of_vu (vu_table));

printf ("static const uint16_t level_meter_tbl[LEVEL_METER_TABLE_SIZE] = {n");
printf ("t//最大値 +3vun");
printf("t");
printf ("%3d, ", values(1:15));
printf("n");
printf("t");
printf ("%3d, ", values(16:30));
printf("n");
printf ("};n");

レベルメーター表示関数

イメージ通りに表示する関数です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//
//オーディオレベルメータ表示
//
static void render_audio_level_meter( audio_level_t* audiolevel )
{
    const char msg[] = "LR";

    for (uint8_t linenum=0 ; linenum 0) {
            //レベル位置のレベルメーターキャラクタ書き込み
            idx = 1 + (level >> 1); //0の位置はLまたはR文字がいるため1から開始
            if ((level & 1) == 0) {
                //2の倍数なら左右表示
                nextDisplayBuffer[linenum].line[idx] = CHAR_LEFT_RIGHT_BAR;
                idx--;
            } else {
                //2の倍数でないなら左表示
                nextDisplayBuffer[linenum].line[idx] = CHAR_LEFT_BAR;
                idx--;
            }
            //レベル位置のレベルメーターキャラクタより下は左右表示
            for (; idx>=1 ; idx--) {
                //左右表示
                nextDisplayBuffer[linenum].line[idx] = CHAR_LEFT_RIGHT_BAR;
            }
        }
        //ピークレベル位置キャラクタ書き込み
        idx = 1 + (peak >> 1);  //0の位置はLまたはR文字がいるために1から開始
        if (peak > 0) {
            idx = idx + 1;      //ピークキャラクタはレベルメーターの右側
        }
        if (idx >= DISPLAY_WIDTH) {
            //over dB
            nextDisplayBuffer[linenum].line[DISPLAY_WIDTH-1] = CHAR_PEAK_LEVEL_OVER_DB;
        } else {
            if ((peak & 1) == 0) {
                //2の倍数なら左表示
                nextDisplayBuffer[linenum].line[idx] = CHAR_LEFT_PEAK_LEVEL;
            } else {
                //2の倍数でないなら右表示
                nextDisplayBuffer[linenum].line[idx] = CHAR_RIGHT_PEAK_LEVEL;
            }
        }
    }
}

現在の入力とボリュームレベル表示

1行目にこのように32段階電子ボリュームの現在指針表示を行ないます,2行目に現在の入力とボリューム数値表示を行ないます 指針によって32段階電子ボリュームのボリューム16表示中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
//
//現在のボリュームをメーターの形で表現
//
static void render_volume_meter( display_line_buf_t line, elevol_t volume )
{
    volume = elevol_validate_volume( volume );
    //バックグラウンド
    memset (line, CHAR_VOL_BACKGROUND, DISPLAY_WIDTH );
    //指針を書き込む
    uint8_t idx = volume >> 1;
    line[idx] = (volume & 1) ? CHAR_VOL_RIGHT_BAR : CHAR_VOL_LEFT_BAR;
}

//
//現在の入力とボリューム
//
static void render_input_source_and_volume( display_line_buf_t line, uint8_t src_num,  elevol_t volume )
{
    char buf[DISPLAY_WIDTH + 1];
    snprintf_P (buf, sizeof(buf), PSTR("SOURCE:%-3dVOL:%2d"),  src_num, volume);
    strncpy (line, buf, DISPLAY_WIDTH );
}

ディスプレイ表示ソースコード

オーディオレベル取り込み
audio_level_meter.h
audio_level_meter.c

ディスプレイ表示
display.h
display.c

comments powered by Disqus

This website uses cookies to improve your experience.
このサイトは「Googleアナリティクス」を使用しています。
Googleアナリティクスはデータの収集のためにCookieを使用しています。


Built with Hugo
テーマ StackJimmy によって設計されています。