web-dev-qa-db-ja.com

コンピュータでは、音声は数字でどのように表されますか?

私はすべてがどのようになり、数字で表されるかを考えるのが好きです。たとえば、平文はASCIIのようなコードで表され、画像はRGB値で表されます。これらは、テキストと画像を表現する最も簡単な方法です。

オーディオを数値で表す最も簡単な方法は何ですか?私はオーディオで動作するプログラムの書き方を学びたいと思っており、これは開始するのに良い方法だと思いました。しかし、インターネット上で良い説明を見つけることができないようです。

44
Jeremy Ruten

物理的には、おそらくご存知のように、オーディオは振動です。通常、私たちは約20Hzから20,000Hzの間の空気の振動について話します。つまり、空気は毎秒20〜20,000回前後に移動しています。

その振動を測定して電気信号に変換すると(たとえば、マイクを使用して)、電圧が音と同じ波形で変化する電気信号が得られます。純音の仮説では、その波形は正弦関数の波形と一致します。

これで、アナログ信号である電圧が得られました。まだデジタルではありません。しかし、この電圧は(たとえば)-1Vと+ 1Vの間で変化することがわかっています。もちろん、電圧計をワイヤーに取り付けて電圧を読み取ることもできます。

任意に、電圧計の目盛りを変更します。ボルトを32767倍します。これにより、-1V -32767および+ 1V 2767が呼び出されます。ああ、それは最も近い整数に丸められます。

次に、電圧計をコンピューターに接続し、コンピューターに毎秒44,100回メーターを読み取るように指示します。 2番目の電圧計(他のステレオチャンネル用)を追加すると、オーディオCDにデータが記録されます。

このフォーマットはステレオ44,100 Hz、16ビットリニアPCMと呼ばれます。そして、それは実際には単なる電圧測定の集まりです。

90
derobert

オーディオはデジタルサンプルで表すことができます。基本的に、サンプラー(アナログ-デジタルコンバーターとも呼ばれます)は、1/fsごとにオーディオ信号の値を取得します。ここで、fsはサンプリング周波数です。次に、ADCは信号を量子化します。これは丸め演算です。したがって、信号の範囲が0〜3ボルト(フルスケール範囲)の場合、サンプルは、たとえば16ビットの数値に丸められます。この例では、16ビットの数値が1/fs /ごとに1回記録されます。

したがって、たとえば、ほとんどのWAV/MP3は44 kHzでオーディオ信号をサンプリングします。詳細はわかりませんが、「ナイキストサンプリングレート」と呼ばれるものがあります。サンプリング周波数は、目的の周波数の少なくとも2倍でなければならないというものです。したがって、WAV/MP3ファイルでは、せいぜいtp 22kHzの周波数を聞くことができます。

この領域には、詳細を詳しく説明することができます。最も単純な形式は確かにWAV形式です。非圧縮オーディオです。 mp3やoggなどの形式は、操作する前に解凍する必要があります。

8
devin

最小限のCオーディオ生成の例

以下の例では、純粋な1000k Hzの正弦波をraw形式で生成しています。一般的な44.1kHzサンプリングレートでは、約4秒間続きます。

main.c:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void) {
    FILE *f;
    const double PI2 = 2 * acos(-1.0);
    const double SAMPLE_FREQ = 44100;
    const unsigned int NSAMPLES = 4 * SAMPLE_FREQ;
    uint16_t ampl;
    uint8_t bytes[2];
    unsigned int t;

    f = fopen("out.raw", "wb");
    for (t = 0; t < NSAMPLES; ++t) {
        ampl = UINT16_MAX * 0.5 * (1.0 + sin(PI2 * t * 1000.0 / SAMPLE_FREQ));
        bytes[0] = ampl >> 8;
        bytes[1] = ampl & 0xFF;
        fwrite(bytes, 2, sizeof(uint8_t), f);
    }
    fclose(f);
    return EXIT_SUCCESS;
}

GitHubアップストリーム

生む out.raw

gcc -std=c99 -o main main.c -lm
./main

演奏する out.raw 直接:

Sudo apt-get install ffmpeg
ffplay -autoexit -f u16be -ar 44100 -ac 1 out.raw

または、より一般的なオーディオ形式に変換してから、より一般的なオーディオプレーヤーで再生します。

ffmpeg -f u16be -ar 44100 -ac 1 -i out.raw out.flac
vlc out.flac

生成されたFLACファイル: https://github.com/cirosantilli/media/blob/master/Canon.flac

パラメータの説明: https://superuser.com/a/1063230/128124

Ubuntu18.04でテスト済み。

CのDのカノン

これは、より興味深い合成例です。

結果: https://www.youtube.com/watch?v=JISozfHATms

main.c

#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

typedef uint16_t point_type_t;

double PI2;

void write_ampl(FILE *f, point_type_t ampl) {
    uint8_t bytes[2];
    bytes[0] = ampl >> 8;
    bytes[1] = ampl & 0xFF;
    fwrite(bytes, 2, sizeof(uint8_t), f);
}

/* https://en.wikipedia.org/wiki/Piano_key_frequencies */
double piano_freq(unsigned int i) {
    return 440.0 * pow(2, (i - 49.0) / 12.0);
}

/* Chord formed by the nth note of the piano. */
point_type_t piano_sum(unsigned int max_ampl, unsigned int time,
        double sample_freq, unsigned int nargs, unsigned int *notes) {
    unsigned int i;
    double sum = 0;
    for (i = 0 ; i < nargs; ++i)
        sum += sin(PI2 * time * piano_freq(notes[i]) / sample_freq);
    return max_ampl * 0.5 * (nargs + sum) / nargs;
}

enum notes {
    A0 = 1, AS0, B0,
    C1, C1S, D1, D1S, E1, F1, F1S, G1, G1S, A1, A1S, B1,
    C2, C2S, D2, D2S, E2, F2, F2S, G2, G2S, A2, A2S, B2,
    C3, C3S, D3, D3S, E3, F3, F3S, G3, G3S, A3, A3S, B3,
    C4, C4S, D4, D4S, E4, F4, F4S, G4, G4S, A4, A4S, B4,
    C5, C5S, D5, D5S, E5, F5, F5S, G5, G5S, A5, A5S, B5,
    C6, C6S, D6, D6S, E6, F6, F6S, G6, G6S, A6, A6S, B6,
    C7, C7S, D7, D7S, E7, F7, F7S, G7, G7S, A7, A7S, B7,
    C8,
};

int main(void) {
    FILE *f;
    PI2 = 2 * acos(-1.0);
    const double SAMPLE_FREQ = 44100;
    point_type_t ampl;
    point_type_t max_ampl = UINT16_MAX;
    unsigned int t, i;
    unsigned int samples_per_unit = SAMPLE_FREQ * 0.375;
    unsigned int *ip[] = {
        (unsigned int[]){4, 2, C3, E4},
        (unsigned int[]){4, 2, G3, D4},
        (unsigned int[]){4, 2, A3, C4},
        (unsigned int[]){4, 2, E3, B3},

        (unsigned int[]){4, 2, F3, A3},
        (unsigned int[]){4, 2, C3, G3},
        (unsigned int[]){4, 2, F3, A3},
        (unsigned int[]){4, 2, G3, B3},

        (unsigned int[]){4, 3, C3, G4, E5},
        (unsigned int[]){4, 3, G3, B4, D5},
        (unsigned int[]){4, 2, A3,     C5},
        (unsigned int[]){4, 3, E3, G4, B4},

        (unsigned int[]){4, 3, F3, C4, A4},
        (unsigned int[]){4, 3, C3, G4, G4},
        (unsigned int[]){4, 3, F3, F4, A4},
        (unsigned int[]){4, 3, G3, D4, B4},

        (unsigned int[]){2, 3, C4, E4, C5},
        (unsigned int[]){2, 3, C4, E4, C5},
        (unsigned int[]){2, 3, G3, D4, D5},
        (unsigned int[]){2, 3, G3, D4, B4},

        (unsigned int[]){2, 3, A3, C4, C5},
        (unsigned int[]){2, 3, A3, C4, E5},
        (unsigned int[]){2, 2, E3,     G5},
        (unsigned int[]){2, 2, E3,     G4},

        (unsigned int[]){2, 3, F3, A3, A4},
        (unsigned int[]){2, 3, F3, A3, F4},
        (unsigned int[]){2, 3, C3,     E4},
        (unsigned int[]){2, 3, C3,     G4},

        (unsigned int[]){2, 3, F3, A3, F4},
        (unsigned int[]){2, 3, F3, A3, C5},
        (unsigned int[]){2, 3, G3, B3, B4},
        (unsigned int[]){2, 3, G3, B3, G4},

        (unsigned int[]){2, 3, C4, E4, C5},
        (unsigned int[]){1, 3, C4, E4, E5},
        (unsigned int[]){1, 3, C4, E4, G5},
        (unsigned int[]){1, 2, G3,     G5},
        (unsigned int[]){1, 2, G3,     A5},
        (unsigned int[]){1, 2, G3,     G5},
        (unsigned int[]){1, 2, G3,     F5},

        (unsigned int[]){3, 3, A3, C4, E5},
        (unsigned int[]){1, 3, A3, C4, E5},
        (unsigned int[]){1, 3, E3, G3, E5},
        (unsigned int[]){1, 3, E3, G3, F5},
        (unsigned int[]){1, 3, E3, G3, E5},
        (unsigned int[]){1, 3, E3, G3, D5},
    };
    f = fopen("Canon.raw", "wb");
    for (i = 0; i < sizeof(ip) / sizeof(int*); ++i) {
        unsigned int *cur = ip[i];
        unsigned int total = samples_per_unit * cur[0];
        for (t = 0; t < total; ++t) {
            ampl = piano_sum(max_ampl, t, SAMPLE_FREQ, cur[1], &cur[2]);
            write_ampl(f, ampl);
        }
    }
    fclose(f);
    return EXIT_SUCCESS;
}

GitHubアップストリーム

YouTubeでは、次のように準備しました。

wget -O Canon.png https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/The_C_Programming_Language_logo.svg/564px-The_C_Programming_Language_logo.svg.png
ffmpeg -loop 1 -y -i Canon.png -i Canon.flac -shortest -acodec copy -vcodec vp9 Canon.mkv

説明されているように: https://superuser.com/questions/700419/how-to-convert-mp3-to-youtube-allowed-video-format/1472572#1472572

オーディオ生成のより物理指向のビューは次のとおりです。 コンピューターではオーディオは数字でどのように表されますか?

Ubuntu 18.04でテスト済み。

物理学

音声は、あらゆる瞬間の単一の数値としてエンコードされます。それを、瞬間的にWIDTH * HEIGHTの数値が必要なビデオと比較してください。

次に、この数値は、スピーカーの ダイヤフラム の線形変位に変換されます。

|   /
|  /
|-/
| | A   I   R
|-\
|  \
|   \
<-> displacement

|     /
|    /
|---/
|   | A I   R
|---\
|    \
|     \
<---> displacement

|       /
|      /
|-----/
|     | A I R
|-----\
|      \
|       \
<-----> displacement

変位は空気を前後に押し、圧力差を生成します。圧力差は P波 として空気中を移動します。

変位のみが重要です。一定の信号は、最大であっても音を出しません。ダイアフラムは固定位置に留まります。

サンプリング周波数 は、ディスプレイスメントを実行する速度を決定します。

44,1kHz は、人間が最大20kHzを聞くことができるため、および ナイキストシャノンサンプリング定理 のため、一般的なサンプリング周波数です。

サンプリング周波数は、ビデオのFPSに類似していますが、ビデオで一般的に見られる25(シネマ)〜144(ハードコアゲームモニター)の範囲と比較してはるかに高い値を持っています。

フォーマット

.rawは、振幅バイトのみを含み、メタデータを含まない、指定が不十分な形式です。

形式にはそのデータが含まれていないため、サンプリング頻度などのコマンドラインでいくつかのメタデータパラメーターを渡す必要があります。

必要なすべてのメタデータを含む他の非圧縮形式もあります。 .wav、参照: ゼロからのWAVファイル合成-C

しかし実際には、ほとんどの人はファイル/ストリーミングをはるかに小さくする圧縮形式のみを扱います。これらの形式の一部は、人間の耳の特性を考慮して、不可逆的な方法でオーディオをさらに圧縮します。 2019年の時点で最も人気のあるロイヤリティフリーのフォーマットは次のようです。

生物学

人間は主に周波数分解によって音を知覚します(別名 フーリエ変換 )。

これは内耳に異なる周波数で共振する部分があるためだと思います(TODO確認)。

したがって、音楽を合成するときは、特定の時点ではなく、周波数を加算するという観点から考えます。これは説明されています この例では

これは、各時点で20Hzから20kHzの間の1Dベクトルの観点から考えることにつながります。

数学的フーリエ変換は時間の概念を失うので、合成するときに行うことは、ポイントのグループを取得し、そのグループの周波数を合計して、そこでフーリエ変換を実行することです。

幸い、フーリエ変換は線形であるため、変位を直接合計して正規化することができます。

ポイントの各グループのサイズにより、時間 ハイゼンベルクの不確定性原理 と同じ数学によって媒介される周波数精度のトレードオフが発生します。

ウェーブレット は、この中間時間のより正確な数学的記述である可能性があります-周波数記述。

箱からすぐに一般的なトーンを生成する簡単な方法

驚くべきFFmpegライブラリはそれらのいくつかをカバーしています: Linux副鼻腔オーディオジェネレータ

音を数字で表す最も簡単な方法は、PCM(Pulse Code Modulation)です。これは、音の振幅が設定された周波数で記録されることを意味します(各振幅値はサンプルと呼ばれます)。たとえば、CD品質のサウンドは、周波数44100 Hzの16ビットサンプル(ステレオ)です。

サンプルは、整数(通常、8、12、16、24、または32ビット)または浮動小数点数(16ビットfloatまたは32ビットdouble)として表すことができます。番号は、署名されている場合と、署名されていない場合があります。

16ビットの符号付きサンプルの場合、値0は中央にあり、-32768と32767が最大振幅になります。 16ビットの符号なしサンプルの場合、値32768が中央になり、0と65535が最大振幅になります。

浮動小数点サンプルの場合、通常の形式は0が中央にあり、-1.0と1.0が最大振幅です。

PCMデータは、たとえばMP3を使用して圧縮できます。

4
Guffa

特定のサンプル周波数での波形のサンプルが最も基本的な表現になると思います。

3
Jimmy

波形をクローズアップで見たことがありますか? Y軸は、通常16ビットの整数として単純に表されます。

2
Chris

アナログ-デジタル変換などを調べます。それはあなたが始めるはずです。これらのデバイスは、オーディオ信号(正弦波)をデジタル表現に変換できます。したがって、16ビットADCは-32768〜32768の正弦を表すことができます。これは固定小数点です。浮動小数点で実行することもできます(ただし、パフォーマンス上の理由からはお勧めしませんが、範囲上の理由から必要になる場合があります)。数値を正弦波に変換すると、逆の(デジタルアナログ変換)ことが起こります。これは、DACと呼ばれるものによって処理されます。

2
sybreon

オーディオの再生を開始する良い方法は Processing および Minim を使用することだと思います。このプログラムは、マイクから音の周波数スペクトルを引き出します!

import ddf.minim.*;
import ddf.minim.analysis.*;

AudioInput in;
FFT fft;

void setup()
{
  size(1024, 600);
  noSmooth();
  Minim.start(this);
  in = Minim.getLineIn();
  fft = new FFT(in.bufferSize(), in.sampleRate());
}

void draw()
{
  background(0);
  fft.forward(in.mix);
  stroke(255);
  for(int i = 0; i < fft.specSize(); i++)
    line(i*2+1, height, i*2+1, height - fft.getBand(i)*10);
}

void stop()
{
  in.close();
  Minim.stop();
  super.stop();
}
2
Nathan

実際の類似オーディオをデジタル形式に変換するには、2つのステップが必要です。

  1. サンプリング
  2. 量子化

サンプリング

連続波形(この場合はオーディオ)がサンプリングされるレートは、サンプリングレートと呼ばれます。人間が知覚する周波数範囲は20〜20,000Hzです。ただし、CDはナイキストサンプリング定理を使用します。これは、44,100 Hzのサンプリングレートが0〜22,050Hzの範囲の周波数をカバーすることを意味します。

量子化

「サンプリング」フェーズから受け取った個別の値のセットは、有限数の値に変換する必要があります。 8ビットの量子化では256の可能な値が提供され、16ビットの量子化では最大65,536の値が提供されます。

1
Joel Menezes

答えはすべてサンプリング頻度に関連していますが、質問には対処していません。サウンドの特定のスナップショットには、多くの異なる周波数の個々の振幅が含まれていると思います(たとえば、キーボードでAとCの両方を同時に叩き、Aの音量を大きくします)。それはどのようにして16ビット数で記録されますか?振幅(音の大きさ)を測定するだけの場合、さまざまな音をどのように取得しますか?

ああ!このコメントから得られると思います。「この数値は、スピーカーのダイアフラムの線形変位に変換されます。」振動板が振動している速さによってノートが表示されます。そのため、毎秒44,000の異なる値が必要です。音符は1000ヘルツのオーダーのどこかにあるので、純粋な音符は毎秒約1000回振動板を出し入れします。オーケストレーション全体の録音には、いたるところにさまざまな音符があり、奇跡的に横隔膜の動きの1つの時間履歴に変換できます。 1秒あたり44,000回、ダイヤフラムは少し内側または外側に移動するように指示され、その単純な(長い)数のリストはビヨンセからベートーベンを表すことができます。

0
curious