メディア データを WAVE および JPEG 形式で圧縮/保存する方法、パート 1

こんにちは! 最初の連載記事では、JPEG (画像) や WAVE (音声) などの画像/音声の圧縮と保存方法を学習することに焦点を当て、実際にこれらの形式 (.jpg、.wav) を使用するプログラムの例も紹介します。 このパートでは、WAVE について見ていきます。

ストーリー

WAVE (Waveform Audio File Format) は、オーディオ ストリームの録音を保存するためのコンテナ ファイル形式です。 このコンテナは通常、非圧縮パルス コード変調オーディオを保存するために使用されます。 (ウィキペディアより抜粋)

1991 年に Microsoft と IBM (当時の大手 IT 企業) によって RIFF とともに発明され、公開されました。

ファイル構造

ファイルにはデータ自体であるヘッダー部分がありますが、フッターはありません。 ヘッダーの重さは合計 44 バイトです。
ヘッダーには、サンプルのビット数、サンプル レート、サウンドの深さなどの設定が含まれています。 サウンドカードに必要な情報。 (数値テーブルの値はすべてリトルエンディアン順に記述する必要があります)

ブロック名
ブロックサイズ(B)
説明/目的
値 (一部の値は固定されています)

チャンクID
4
ファイルをメディアコンテナとして定義する
ビッグエンディアン (「RIFF」) の 0x52494646

チャンクサイズ
4
chunkId と chunkSize を除いたファイル全体のサイズ
ファイルサイズ - 8

形式でアーカイブしたプロジェクトを保存します.
4
RIFF からの型定義
ビッグエンディアンの 0x57415645 (「WAVE」)

サブチャンク1Id
4
ファイルがフォーマットの継続としてより多くのスペースを占めるようにするため
ビッグエンディアン (「fmt」) の 0x666d7420

サブチャンク1サイズ
4
残りのヘッダー (バイト単位)
デフォルトでは 16 (オーディオ ストリーム圧縮がない場合)

オーディオフォーマット
2
音声フォーマット (圧縮方法と音声データ構造によって異なります)
1 (PCM の場合、これが検討中のものです)

チャンネル数
2
チャンネル数
1/2、1 チャンネル (3/4/5/6/7... - 特定のオーディオ トラック、たとえばクアッド サウンドの場合は 4 など) を使用します。

サンプルレート
4
オーディオのサンプリング レート (ヘルツ単位)
値が高いほどサウンドは良くなりますが、同じ長さのオーディオ トラックを作成するのに必要なメモリが多くなります。推奨値は 48000 (最も許容できる音質) です。

バイトレート
4
1 秒あたりのバイト数
サンプルレート チャンネル数 サンプルあたりのビット数 (さらに)

ブロック整列
2
1サンプルのバイト数
チャンネル数 * サンプルあたりのビット数: 8

サンプルあたりのビット数
2
1サンプルあたりのビット数(深度)
8 の倍数の任意の数値。数値が大きいほど、オーディオはより良く、重くなります。32 ビットからは人間にとって違いはありません。

サブチャンク2Id
4
データ参照マーク(audioFormatによっては他のヘッダ要素がある場合があるため)
ビッグエンディアンの 0x64617461("データ")

サブチャンク2サイズ
4
データ領域サイズ
int 形式のデータのサイズ

データ
byteRate * 音声の長さ
音声データ
?

WAVEの例

前のテーブルは C の構造に簡単に変換できますが、今日の言語は Python です。 最も簡単な方法は、「波」、つまりノイズ発生器を使用することです。 このタスクでは、高い byteRate と圧縮は必要ありません。
まず、必要なモジュールをインポートしましょう。

# WAV.py

from struct import pack  # перевод py-объектов в базовые типы из C
from os import urandom  # функция для чтения /dev/urandom, для windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)])  # лямбда под windows, т.к. urandom'а в винде нет
from sys import argv, exit  # аргументы к проге и выход

if len(argv) != 3:  # +1 имя скрипта (-1, если будете замораживать)
    print('Usage: python3 WAV.py [num of samples] [output]')
    exit(1)

次に、サイズに応じてテーブルから必要な変数をすべて作成する必要があります。 その中の変数値は numSamples (サンプル数) にのみ依存します。 それらが多ければ多いほど、私たちの騒音は長く続くことになります。

numSamples = int(argv[1])
output_path = argv[2]

chunkId = b'RIFF'
Format = b'WAVE'
subchunk1ID = b'fmt '
subchunk1Size = b'x10x00x00x00'  # 0d16
audioFormat = b'x01x00'
numChannels = b'x02x00'  # 2-х каналов будет достаточно (стерео)
sampleRate = pack('<L', 1000)  # 1000 хватит, но если поставить больше, то шум будет слышен лучше. С 1000-ю он звучит, как ветер
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32 bit sound)
blockAlign = b'x08x00'  # numChannels * BPS / 8
subchunk2ID = b'data'
subchunk2Size = pack('<L', numSamples * 2 * 4)  # * numChannels * BPS / 8
chunkSize = pack('<L', 36 + numSamples * 2 * 4)  # 36 + subchunk2Size

data = urandom(1000 * 2 * 4 * numSamples)  # сам шум

残っているのは、(表のように) 必要な順序でそれらを書き留めることだけです。

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # записываем

それで、準備完了。 スクリプトを使用するには、必要なコマンド ライン引数を追加する必要があります。
python3 WAV.py [num of samples] [output]
サンプルの数 - カウント。 サンプル
出力 — 出力ファイルへのパス

ここにノイズのあるテスト オーディオ ファイルへのリンクがありますが、メモリを節約するために BPS を 1b/s に下げ、チャンネル数を 1 に下げました (32kbs の 64 ビット非圧縮ステレオ オーディオ ストリームでは、 80M の純粋な .wav ファイル、およびわずか 10): https://instaud.io/3Dcy

コード全体 (WAV.py) (コードには重複する変数値が多数あります。これは単なるスケッチです):

from struct import pack  # перевод py-объектов в базовые типы из C
from os import urandom  # функция для чтения /dev/urandom, для windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)])  # лямбда под windows, т.к. urandom'а в винде нет
from sys import argv, exit  # аргументы к проге и выход

if len(argv) != 3:  # +1 имя скрипта (-1, если будете замораживать)
    print('Usage: python3 WAV.py [num of samples] [output]')
    exit(1)

numSamples = int(argv[1])
output_path = argv[2]

chunkId = b'RIFF'
Format = b'WAVE'
subchunk1ID = b'fmt '
subchunk1Size = b'x10x00x00x00'  # 0d16
audioFormat = b'x01x00'
numChannels = b'x02x00'  # 2-х каналов будет достаточно (стерео) 
sampleRate = pack('<L', 1000)  # 1000 хватит, но можно и больше.
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32 bit sound)
blockAlign = b'x08x00'  # numChannels * BPS / 8
subchunk2ID = b'data'
subchunk2Size = pack('<L', numSamples * 2 * 4)  # * numChannels * BPS / 8
chunkSize = pack('<L', 36 + numSamples * 2 * 4)  # 36 + subchunk2Size

data = urandom(1000 * 2 * 4 * numSamples)  # сам шум

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # записываем в файл результат

合計

これで、デジタル サウンドとその保存方法についてもう少し詳しく学びました。 この投稿では圧縮 (audioFormat) は使用しませんでしたが、一般的なものをそれぞれ検討するには 10 記事が必要になります。自分にとって何か新しいことを学び、今後の開発に役立つことを願っています。
ありがとうございます!

ソース

WAVファイルの構造
WAV - ウィキペディア

出所: habr.com

コメントを追加します