Здравейте! Първата ми серия статии ще бъде насочена към изучаване на методи за компресиране и съхранение на изображения/звуци, като JPEG (изображение) и WAVE (звучене), също така ще съдържа примери за програми, използващи тези формати (.jpg, .wav) на практика. В тази част ще разгледаме именно WAVE.
История
WAVE (Waveform Audio File Format) — формат файл-контейнер за съхранение на запис на аудио поток. Този контейнер обикновено се използва за съхранение на несжат звук в имперсивнo-кодова модулация. (Взето от Уикипедия)
Той е създаден и публикуван през 1991 година заедно с RIFF от компании Microsoft и IBM (водещите IT компании по това време).
Структурата на файла
Файлът има заглавна част, самите данни, но няма футер. Заглавието заема общо 44 байта.
В заглавието се намират настройки за броя битове в семпъла, честотата на дискретизация, дълбочината на звука и т.н. информация, необходима за звуковата карта. (Всички числови стойности в таблицата трябва да бъдат записани в Little-Endian ред.)
Име на блока
Размер на блока (B)
Описание/Предназначение
Стойност (при някои тя е фиксирана)
chunkId
4
Определение на файла като медиа-контейнер
0x52494646 в Big-Endian («RIFF»)
chunkSize
4
Размер на целия файл без chunkId и chunkSize
FILE_SIZE — 8
format
4
Определение на типа от RIFF
0x57415645 в Big-Endian («WAVE»)
subchunk1Id
4
За да заема файлът повече място, продължава форматът
0x666d7420 в Big-Endian («fmt «)
subchunk1Size
4
Оставащия хедер (в байтове)
16 по подразбиране (за случая без компресия на аудиопотока)
audioFormat
2
Аудио формат (зависи от метода на компресия и структурата на аудиоданните)
1 (за PCM, който разглеждаме)
numChannels
2
Брой канали
1/2, ние ще вземем 1 канал (3/4/5/6/7… — специфична аудиопътека, например 4 за квадрофонни звуци и т.н.)
sampleRate
4
Честота на семплиране на звука (в Херци)
Колкото повече, толкова по-добро ще бъде качеството на звука, но толкова повече памет ще е необходима за създаване на аудиопътека с една и съща дължина; препоръчителната стойност е 48000 (най-приемливо качество на звука)
byteRate
4
Брой байтове за 1 секунда
sampleRate numChannels bitsPerSample (по-долу)
blockAlign
2
Брой байтове за 1 семпъл
numChannels * bitsPerSample: 8
bitsPerSample
2
Брой битове за 1 семпъл (дълбочина)
Всяко число, кратно на 8. Колкото повече, толкова по-добре и по-тежко ще бъде аудиото; от 32 бита разлика няма за човека.
subchunk2Id
4
Маркер на отчета за начало на данните (тъй като могат да има и други елементи в заглавието в зависимост от audioFormat)
0x64617461 в Big-Endian («data»)
subchunk2Size
4
Размер на областта с данни
размер data в int.
data
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 битов звук)
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 [брой на семплите] [изход]
брой на семплите — кол. семпли
изход — път до изходния файл
Ето линк към тестовия аудиофайл с шум, но за икономия на памет, намалих BPS до 1b/s и броя на каналите до 1 (с 32-битов несжат стерео аудиопоток в 64kbs се получи 80M чист .wav файл, а сега само 10):
Целият код (WAV.py) (Кодът има множество дублирания на стойности на променливите, това е само чернова):
от struct import pack # преобразуване на py-обекти в основни типове от C
от os import urandom # функция за четене на /dev/urandom, за windows:
# от random import randint
# urandom = lambda sz: bytes([randint(0, 255) за _ в range(sz)]) # лямбда за windows, тъй като urandom не съществува в Windows
от sys import argv, exit # аргументи на програмата и изход
ако len(argv) != 3: # +1 име на скрипта (-1, ако ще замразявате)
print('Използване: python3 WAV.py [брой на проби] [изход]')
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-битен звук)
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) # самият шум
с отворен(output_path, 'wb') като fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # записваме резултата във файлаРезюме
Така вие научихте малко повече за цифровия звук и как той се съхранява. В този пост не използвахме компресия (audioFormat), но за разглеждане на всяко от популярните ще бъдат нужни 10 статии. Надявам се, че сте научили нещо ново и това ще ви помогне в бъдещите разработки.
Благодаря!
Източници
Източник: habr.com
