روش‌های فشرده‌سازی/ذخیره‌سازی داده‌های رسانه در فرمت‌های WAVE و JPEG، بخش 1

سلام! اولین سری از مقالات من بر روی مطالعه فشرده سازی تصویر/صوت و روش های ذخیره سازی مانند JPEG (تصویر) و WAVE (صدا) متمرکز خواهد بود و همچنین شامل نمونه هایی از برنامه هایی است که در عمل از این فرمت ها (.jpg، .wav) استفاده می کنند. در این قسمت به WAVE می پردازیم.

داستان

WAVE (فرمت فایل صوتی شکل موج) یک فرمت فایل کانتینری برای ذخیره ضبط یک جریان صوتی است. این ظرف معمولاً برای ذخیره صدای مدوله شده با کد پالس فشرده نشده استفاده می شود. (برگرفته از ویکی پدیا)

در سال 1991 همراه با RIFF توسط مایکروسافت و IBM (شرکت های پیشرو فناوری اطلاعات آن زمان) اختراع و منتشر شد.

ساختار فایل

فایل یک قسمت سرصفحه دارد، خود داده، اما فوتر ندارد. هدر در مجموع 44 بایت وزن دارد.
هدر شامل تنظیماتی برای تعداد بیت های نمونه، نرخ نمونه، عمق صدا و غیره است. اطلاعات مورد نیاز برای کارت صدا (همه مقادیر جدول عددی باید به ترتیب Little-Endian نوشته شوند)

نام بلوک
اندازه بلوک (B)
توضیحات/هدف
ارزش (برای برخی ثابت است

chunkId
4
تعریف فایل به عنوان محفظه رسانه
0x52494646 در Big-Endian ("RIFF")

chunkSize
4
اندازه کل فایل بدون chunkId و chunkSize
FILE_SIZE - 8

قالب
4
تعریف تایپ از RIFF
0x57415645 در Big-Endian ("WAVE")

subchunk1Id
4
به طوری که با ادامه فرمت فایل فضای بیشتری را اشغال می کند
0x666d7420 در Big-Endian ("fmt")

subchunk1Size
4
سرصفحه باقی مانده (بر حسب بایت)
16 به طور پیش فرض (برای کیس بدون فشرده سازی جریان صدا)

فرمت صوتی
2
فرمت صوتی (بستگی به روش فشرده سازی و ساختار داده های صوتی دارد)
1 (برای PCM، چیزی که ما در نظر داریم)

numChannels
2
تعداد کانال
1/2، ما 1 کانال می گیریم (3/4/5/6/7... - یک تراک صوتی خاص، به عنوان مثال 4 برای صدای چهارگانه و غیره)

نرخ نمونه
4
نرخ نمونه برداری صوتی (به هرتز)
هر چه بالاتر، صدا بهتر می شود، اما هرچه حافظه بیشتری برای ایجاد یک آهنگ صوتی با طول یکسان مورد نیاز باشد، مقدار توصیه شده 48000 است (قابل قبول ترین کیفیت صدا)

نرخ بایت
4
تعداد بایت در ثانیه
نرخ نمونه numChannels bitsPerSample (بیشتر)

blockAlign
2
تعداد بایت برای 1 نمونه
numChannels * bitsPerSample: 8

bitsPerSample
2
تعداد بیت در هر 1 نمونه (عمق)
هر عددی که مضرب 8 باشد. هر چه عدد بالاتر باشد، صدا بهتر و سنگین‌تر خواهد بود؛ از 32 بیت برای انسان تفاوتی وجود ندارد.

subchunk2Id
4
علامت مرجع داده (زیرا ممکن است عناصر سرصفحه دیگری بسته به فرمت صوتی وجود داشته باشد)
0x64617461 در Big-Endian ("داده")

subchunk2Size
4
اندازه ناحیه داده
اندازه داده ها به صورت int

داده ها
بایت * مدت زمان صدا
داده های صوتی
?

مثال WAVE

جدول قبلی را می توان به راحتی به یک ساختار در C ترجمه کرد، اما زبان امروز ما پایتون است. ساده ترین کاری که می توانید انجام دهید استفاده از "موج" است - یک تولید کننده نویز. برای این کار نیازی به بایت ریت و فشرده سازی بالا نداریم.
ابتدا، اجازه دهید ماژول های لازم را وارد کنیم:

# 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 کاهش دادم (با یک جریان صوتی استریو فشرده نشده 32 بیتی در 64 کیلوبایت، معلوم شد که اینطور است. 80 میلیون فایل 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 - ویکی پدیا

منبع: www.habr.com

اضافه کردن نظر