طرق ضغط/تخزين بيانات الوسائط بتنسيقات WAVE وJPEG، الجزء الأول

مرحبًا! ستركز سلسلتي الأولى من المقالات على دراسة طرق ضغط الصور/الصوت وتخزينها مثل JPEG (الصورة) وWAVE (الصوت)، وستتضمن أيضًا أمثلة على البرامج التي تستخدم هذه التنسيقات (.jpg، .wav) عمليًا. في هذا الجزء سوف ننظر إلى WAVE.

قصة

WAVE (تنسيق ملف الصوت الموجي) هو تنسيق ملف حاوية لتخزين تسجيل دفق صوتي. تُستخدم هذه الحاوية عادةً لتخزين الصوت المعدل برمز النبض غير المضغوط. (مأخوذة من ويكيبيديا)

تم اختراعه ونشره في عام 1991 بالتعاون مع RIFF بواسطة Microsoft وIBM (شركات تكنولوجيا المعلومات الرائدة في ذلك الوقت).

هيكل الملف

يحتوي الملف على جزء رأس، وهو البيانات نفسها، ولكن لا يحتوي على تذييل. يزن الرأس إجمالي 44 بايت.
يحتوي الرأس على إعدادات لعدد البتات في العينة، ومعدل العينة، وعمق الصوت، وما إلى ذلك. المعلومات اللازمة لبطاقة الصوت. (يجب كتابة جميع قيم الجدول الرقمية بترتيب Little-Endian)

اسم الكتلة
حجم الكتلة (ب)
الوصف/الغرض
القيمة (بالنسبة للبعض ثابتة

ChunkId
4
تعريف الملف كحاوية وسائط
0x52494646 في Big-Endian ("RIFF")

حجم قطعة
4
حجم الملف بأكمله بدونchunkId وchunkSize
حجم الملف - 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
معدل أخذ العينات الصوتية (بالهرتز)
كلما كان الصوت أعلى، كلما كان الصوت أفضل، ولكن كلما زادت الذاكرة المطلوبة لإنشاء مسار صوتي بنفس الطول، القيمة الموصى بها هي 48000 (جودة الصوت الأكثر قبولًا)

byteRate
4
عدد البايتات في الثانية
معدل العينة numChannels بتات لكل عينة (المزيد)

blockAlign
2
عدد البايتات لعينة واحدة
عدد القنوات * بتات لكل عينة: 8

bitsPerSample
2
عدد البتات لكل عينة واحدة (العمق)
أي رقم من مضاعفات 8. كلما زاد الرقم، كلما كان الصوت أفضل وأثقل، من 32 بت لا يوجد فرق بالنسبة للبشر

subchunk2Id
4
العلامة المرجعية للبيانات (نظرًا لاحتمال وجود عناصر رأس أخرى اعتمادًا على تنسيق الصوت)
0x64617461 في Big-Endian("البيانات")

subchunk2Size
4
حجم منطقة البيانات
حجم البيانات في int

البيانات
معدل البايت * مدة الصوت
البيانات الصوتية
?

مثال الموجة

يمكن بسهولة ترجمة الجدول السابق إلى بنية في لغة 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)  # записываем в файл результат

مجموع

لقد تعلمت المزيد عن الصوت الرقمي وكيفية تخزينه. في هذه التدوينة لم نستخدم الضغط (تنسيق الصوت)، ولكن للنظر في كل واحدة من التنسيقات الشائعة، ستكون هناك حاجة إلى 10 مقالات، أتمنى أن تكون قد تعلمت شيئًا جديدًا لنفسك وهذا سيساعدك في التطورات المستقبلية.
شكرا لك!

مصادر

هيكل ملف WAV
واف - ويكيبيديا

المصدر: www.habr.com

إضافة تعليق