PI-PO

コンピュータで”ピーポーピーポー”音を作ってみました(後編)


救急車を走らせる

救急車(音源)が走ると、感じる音圧も周波数も変化します。
・音圧を変化させる
・周波数を変化させる。
・エコーを加える。
・音源の移動速度を変える。
この順番で処理を追加していきます。

音圧を変化させる

音源の速度は70km/h固定で目の前に来た時の最短距離を20mとして直線的に通過する感じで受ける音圧を変化させます。
救急車は前方の方が音が大きいですから近づく時は、遠ざかる時に比べて4倍の音の大きさに設定してみました。

###########################################
#サウンド6                               #
#                                         #
# Date: 2017.01.08                        #
# Programed: G-yan                        #
###########################################
require "saveWav.pl";

use constant PI => 3.14;
use constant TM1 => 0.03; #音圧を上げる時間[sec]
use constant S_MAX => 32767;
use constant SAMP_RATE => 44100;
use constant REC_TM => 20; #録音時間20[sec]
use constant DIS_LIS => 20; #自分との最短距離[m]
use constant DIS_P_SAMP => 70 * 1000 / 3600 / SAMP_RATE; #1サンプリング当たりの距離[m]

sub level {
        my $lev1 = $_[0];
        my $lev2;
        my $p01 = ($wavDataLength - $tMax / 2) * DIS_P_SAMP;
        my $p02 = sqrt($p01 ** 2 + DIS_LIS ** 2); #今の自分との直線距離
        my $lev01 = 1 / (($p02 / DIS_LIS) ** 2);

        if ($p01 <= 0) { #近づいてくるとき
                $lev01 *= 40;
        } else { #遠ざかるとき
                $lev01 *= 10;
        }
        if ($lev01 > 1) {
                $lev01 = 1;
        }
        $lev2 = $lev1 * $lev01;
        return $lev2;
}

sub makeWaveData {
        $wavSampleRate = SAMP_RATE;
        $wavDataLength = 0;
        $tMax = SAMP_RATE * REC_TM; #サンプリングレートと録音時間の積でデータ数を決定
        $t0 = 2 / SAMP_RATE; #サンプリング時間当たりの角度
        $t01 = $t0 * 970; #970Hz
        $t02 = $t0 * 960; #960Hz
        $t11 = $t0 * 770; #770Hz
        $t12 = $t0 * 760; #760Hz
        $t31 = 0;
        $t32 = 0;
        $i4 = 0;
        $sw = 1;
        while ($sw) {
                if (($i4++ & 0x1) == 0) {
                        $t21 = $t01;
                        $t22 = $t02;
                } else {
                        $t21 = $t11;
                        $t22 = $t12;
                }
                $i2 = $wavSampleRate * 0.65; #650msec
                $i3 = 0;
                for ($i1 = 0; $i1 < $i2; $i1++) {
                        if ($wavDataLength >= $tMax) { #終わり
                                $sw = 0;
                                last;
                        }
                        $t40 = sin(PI * $t31) * 0.7 + sin(PI * $t32) * 0.1;
                        $wavData[$wavDataLength++] = int($t40 * &level($i3)); #音圧を設定する
                        $t31 += $t21;
                        $t32 += $t22;
                        if ($t31 > 2) {
                                $t31 -= 2;
                        }
                        if ($t32 > 2) {
                                $t32 -= 2;
                        }
                        if ($i1 < $wavSampleRate * TM1) { #設定時間まで
                                if ($i3 < S_MAX) {
                                        $i3 += (S_MAX / ($wavSampleRate * TM1));
                                }
                                if ($i3 > S_MAX) {
                                        $i3 = S_MAX;
                                }
                        }
                        if (($i1 + $wavSampleRate * TM1) > $i2) {
                                if ($i3 > 0) {
                                        $i3 -= (S_MAX / ($wavSampleRate * TM1));
                                }
                                if ($i3 < 0) {
                                        $i3 = 0;
                                }
                        }
                }
        }
}

&makeWaveData;
&saveWaveData("s6");

サウンド6

周波数を変化させる

音源の今の位置と、0.1秒後に移動しているであろう位置のそれぞれの直線距離を算出してドップラー偏移量を加えてみます。
周波数が変化するのは音源だけではなく、0.65秒の高低の周期も変化するのがポイントです。

###########################################
#サウンド7                               #
#                                         #
# Date: 2017.01.08                        #
# Programed: G-yan                        #
###########################################
require "saveWav.pl";

use constant PI => 3.14;
use constant TM1 => 0.03; #音圧を上げる時間[sec]
use constant S_MAX => 32767;
use constant SAMP_RATE => 44100;
use constant REC_TM => 20; #録音時間20[sec]
use constant DIS_LIS => 20; #自分との最短距離[m]
use constant DIS_P_SAMP => 70 * 1000 / 3600 / SAMP_RATE; #1サンプリング当たりの距離[m]
use constant SOU_SPD => 340; #音速[m/s]

sub level {
        my $lev1 = $_[0];
        my $lev2;
        my $p01 = ($wavDataLength - $tMax / 2) * DIS_P_SAMP;
        my $p02 = sqrt($p01 ** 2 + DIS_LIS ** 2); #今の自分との直線距離
        my $lev01 = 1 / (($p02 / DIS_LIS) ** 2);

        if ($p01 <= 0) { #近づいてくるとき
                $lev01 *= 40;
        } else { #遠ざかるとき
                $lev01 *= 10;
        }
        if ($lev01 > 1) {
                $lev01 = 1;
        }
        $lev2 = $lev1 * $lev01;
        return $lev2;
}

sub doppler {
        my $t1 = $_[0];
        my $t2;
        my $p01 = ($wavDataLength - $tMax / 2) * DIS_P_SAMP;
        my $p02 = sqrt($p01 ** 2 + DIS_LIS ** 2); #今の自分との直線距離
        my $p11 = (($wavDataLength + SAMP_RATE / 10) - $tMax / 2) * DIS_P_SAMP;
        my $p12 = sqrt($p11 ** 2 + DIS_LIS ** 2); #0.1秒後の自分との直線距離

        $p30 = $p12 - $p02;
        $t2 = $t1 * (SOU_SPD / ($p30 * 10 + SOU_SPD)); #偏移の割合をかける
        return $t2;
}

sub makeWaveData {
        $wavSampleRate = SAMP_RATE;
        $wavDataLength = 0;
        $tMax = SAMP_RATE * REC_TM; #サンプリングレートと録音時間の積でデータ数を決定
        $t0 = 2 / SAMP_RATE; #サンプリング時間当たりの角度
        $t01 = $t0 * 970; #970Hz
        $t02 = $t0 * 960; #960Hz
        $t11 = $t0 * 770; #770Hz
        $t12 = $t0 * 760; #760Hz
        $t31 = 0;
        $t32 = 0;
        $i4 = 0;
        $sw = 1;
        while ($sw) {
                if (($i4++ & 0x1) == 0) {
                        $t21 = $t01;
                        $t22 = $t02;
                } else {
                        $t21 = $t11;
                        $t22 = $t12;
                }
                $i2 = $wavSampleRate * 0.65; #650msec
                $i2 = $i2 / &doppler(1); #ドップラー効果を加える
                $i3 = 0;
                for ($i1 = 0; $i1 < $i2; $i1++) {
                        if ($wavDataLength >= $tMax) { #終わり
                                $sw = 0;
                                last;
                        }
                        $t40 = sin(PI * $t31) * 0.7 + sin(PI * $t32) * 0.1;
                        $wavData[$wavDataLength++] = int($t40 * &level($i3)); #音圧を設定する
                        $t31 += &doppler($t21); #ドップラー効果を加える
                        $t32 += &doppler($t22); #ドップラー効果を加える
                        if ($t31 > 2) {
                                $t31 -= 2;
                        }
                        if ($t32 > 2) {
                                $t32 -= 2;
                        }
                        if ($i1 < $wavSampleRate * TM1) { #設定時間まで
                                if ($i3 < S_MAX) {
                                        $i3 += (S_MAX / ($wavSampleRate * TM1));
                                }
                                if ($i3 > S_MAX) {
                                        $i3 = S_MAX;
                                }
                        }
                        if (($i1 + $wavSampleRate * TM1) > $i2) {
                                if ($i3 > 0) {
                                        $i3 -= (S_MAX / ($wavSampleRate * TM1));
                                }
                                if ($i3 < 0) {
                                        $i3 = 0;
                                }
                        }
                }
        }
}

&makeWaveData;
&saveWaveData("s7");

サウンド7

エコーを加える

音が建物などの影響でエコーしているように聞こえるので、音源の10%をフィードバックして反響させます。
リングバッファへから0.3秒前のデータを取り出し、リアルに算出した量子データに加算することで実現します。
これで、時速70km/hで走っている救急車の音に近いものができました。

###########################################
#サウンド8                               #
#                                         #
# Date: 2017.01.08                        #
# Programed: G-yan                        #
###########################################
require "saveWav.pl";

use constant PI => 3.14;
use constant TM1 => 0.03; #音圧を上げる時間[sec]
use constant S_MAX => 32767;
use constant SAMP_RATE => 44100;
use constant REC_TM => 20; #録音時間20[sec]
use constant DIS_LIS => 20; #自分との最短距離[m]
use constant DIS_P_SAMP => 70 * 1000 / 3600 / SAMP_RATE; #1サンプリング当たりの距離[m]
use constant SOU_SPD => 340; #音速[m/s]
use constant ECHO_TM => SAMP_RATE * 0.3; #残音時間[sec]

$echoWp = 0;
$echoRp = 1;

sub echo {
        my $newDat = $_[0];
        my $t1;

        $t1 = $newDat + $echoBuf[$echoRp++];
        $echoBuf[$echoWp++] = $t1 * 0.1; #10%の残音
        if ($echoRp >= ECHO_TM) {
                $echoRp = 0;
        }
        if ($echoWp >= ECHO_TM) {
                $echoWp = 0;
        }
        return $t1;
}

sub level {
        my $lev1 = $_[0];
        my $lev2;
        my $p01 = ($wavDataLength - $tMax / 2) * DIS_P_SAMP;
        my $p02 = sqrt($p01 ** 2 + DIS_LIS ** 2); #今の自分との直線距離
        my $lev01 = 1 / (($p02 / DIS_LIS) ** 2);

        if ($p01 <= 0) { #近づいてくるとき
                $lev01 *= 40;
        } else { #遠ざかるとき
                $lev01 *= 10;
        }
        if ($lev01 > 1) {
                $lev01 = 1;
        }
        $lev2 = $lev1 * $lev01;
        return $lev2;
}

sub doppler {
        my $t1 = $_[0];
        my $t2;
        my $p01 = ($wavDataLength - $tMax / 2) * DIS_P_SAMP;
        my $p02 = sqrt($p01 ** 2 + DIS_LIS ** 2); #今の自分との直線距離
        my $p11 = (($wavDataLength + SAMP_RATE / 10) - $tMax / 2) * DIS_P_SAMP;
        my $p12 = sqrt($p11 ** 2 + DIS_LIS ** 2); #0.1秒後の自分との直線距離

        $p30 = $p12 - $p02;
        $t2 = $t1 * (SOU_SPD / ($p30 * 10 + SOU_SPD)); #偏移の割合をかける
        return $t2;
}

sub makeWaveData {
        $wavSampleRate = SAMP_RATE;
        $wavDataLength = 0;
        $tMax = SAMP_RATE * REC_TM; #サンプリングレートと録音時間の積でデータ数を決定
        $t0 = 2 / SAMP_RATE; #サンプリング時間当たりの角度
        $t01 = $t0 * 970; #970Hz
        $t02 = $t0 * 960; #960Hz
        $t11 = $t0 * 770; #770Hz
        $t12 = $t0 * 760; #760Hz
        $t31 = 0;
        $t32 = 0;
        $i4 = 0;
        $sw = 1;
        while ($sw) {
                if (($i4++ & 0x1) == 0) {
                        $t21 = $t01;
                        $t22 = $t02;
                } else {
                        $t21 = $t11;
                        $t22 = $t12;
                }
                $i2 = $wavSampleRate * 0.65; #650msec
                $i2 = $i2 / &doppler(1); #ドップラー効果を加える
                $i3 = 0;
                for ($i1 = 0; $i1 < $i2; $i1++) {
                        if ($wavDataLength >= $tMax) { #終わり
                                $sw = 0;
                                last;
                        }
                        $t40 = sin(PI * $t31) * 0.7 + sin(PI * $t32) * 0.1;
                        $t41 = &echo($t40); #エコーのデータを加算
                        $wavData[$wavDataLength++] = int($t41 * &level($i3)); #音圧を設定する
                        $t31 += &doppler($t21); #ドップラー効果を加える
                        $t32 += &doppler($t22); #ドップラー効果を加える
                        if ($t31 > 2) {
                                $t31 -= 2;
                        }
                        if ($t32 > 2) {
                                $t32 -= 2;
                        }
                        if ($i1 < $wavSampleRate * TM1) { #設定時間まで
                                if ($i3 < S_MAX) {
                                        $i3 += (S_MAX / ($wavSampleRate * TM1));
                                }
                                if ($i3 > S_MAX) {
                                        $i3 = S_MAX;
                                }
                        }
                        if (($i1 + $wavSampleRate * TM1) > $i2) {
                                if ($i3 > 0) {
                                        $i3 -= (S_MAX / ($wavSampleRate * TM1));
                                }
                                if ($i3 < 0) {
                                        $i3 = 0;
                                }
                        }
                }
        }
}

&makeWaveData;
&saveWaveData("s8");

サウンド8

音源の移動速度を変える

それでは、いよいよ目的のシミュレーションをしてみます。

まずは200km/h
use constant DIS_P_SAMP => 200 * 1000 / 3600 / SAMP_RATE; #1サンプリング当たりの距離[m]
サウンド9

そして300km/h
use constant DIS_P_SAMP => 300 * 1000 / 3600 / SAMP_RATE; #1サンプリング当たりの距離[m]
サウンド10

感想は…(^^; 迫力も無くいまいちな感じでした。

おしまい。