สวัสดี! บทความชุดแรกของฉันจะมุ่งเน้นไปที่การศึกษาการบีบอัดภาพ/เสียง และวิธีการจัดเก็บ เช่น JPEG (ภาพ) และ WAVE (เสียง) และจะรวมตัวอย่างโปรแกรมที่ใช้รูปแบบเหล่านี้ (.jpg, .wav) ในทางปฏิบัติด้วย ในส่วนนี้เราจะมาดู WAVE กัน
เรื่องราว
WAVE (รูปแบบไฟล์เสียงรูปคลื่น) เป็นรูปแบบไฟล์คอนเทนเนอร์สำหรับจัดเก็บการบันทึกสตรีมเสียง โดยทั่วไปคอนเทนเนอร์นี้ใช้เพื่อจัดเก็บเสียงมอดูเลตรหัสพัลส์ที่ไม่มีการบีบอัด (นำมาจากวิกิพีเดีย)
ได้รับการคิดค้นและเผยแพร่ในปี 1991 ร่วมกับ RIFF โดย Microsoft และ IBM (บริษัทไอทีชั้นนำในยุคนั้น)
โครงสร้างไฟล์
ไฟล์มีส่วนหัว ซึ่งเป็นข้อมูล แต่ไม่มีส่วนท้าย ส่วนหัวมีน้ำหนักรวม 44 ไบต์
ส่วนหัวประกอบด้วยการตั้งค่าสำหรับจำนวนบิตในตัวอย่าง อัตราตัวอย่าง ความลึกของเสียง ฯลฯ ข้อมูลที่จำเป็นสำหรับการ์ดเสียง (ค่าตารางตัวเลขทั้งหมดต้องเขียนตามลำดับ Little-Endian)
ชื่อบล็อค
ขนาดบล็อก (B)
คำอธิบาย/วัตถุประสงค์
ค่า (สำหรับบางส่วนได้รับการแก้ไขแล้ว
รหัสก้อน
4
การกำหนดไฟล์เป็นคอนเทนเนอร์สื่อ
0x52494646 ใน Big-Endian (“RIFF”)
chunkSize
4
ขนาดของไฟล์ทั้งหมดโดยไม่มี chunkId และ chunkSize
FILE_SIZE - 8
รูป
4
พิมพ์คำจำกัดความจาก RIFF
0x57415645 ใน Big-Endian (“WAVE”)
subchunk1Id
4
เพื่อให้ไฟล์ใช้พื้นที่มากขึ้นตามรูปแบบที่ต่อเนื่อง
0x666d7420 ใน Big-Endian (“fmt”)
ย่อย1ขนาด
4
ส่วนหัวที่เหลือ (เป็นไบต์)
16 โดยค่าเริ่มต้น (สำหรับกรณีที่ไม่มีการบีบอัดสตรีมเสียง)
รูปแบบเสียง
2
รูปแบบเสียง (ขึ้นอยู่กับวิธีการบีบอัดและโครงสร้างข้อมูลเสียง)
1 (สำหรับ PCM ซึ่งเป็นสิ่งที่เรากำลังพิจารณา)
numChannels
2
จำนวนช่อง
1/2 เราจะใช้ 1 ช่องสัญญาณ (3/4/5/6/7... - แทร็กเสียงเฉพาะ เช่น 4 สำหรับเสียงสี่ช่อง เป็นต้น)
อัตราการสุ่มตัวอย่าง
4
อัตราการสุ่มตัวอย่างเสียง (เป็นเฮิรตซ์)
ยิ่งสูง เสียงก็จะยิ่งดี แต่ยิ่งต้องใช้หน่วยความจำมากขึ้นเพื่อสร้างแทร็กเสียงที่มีความยาวเท่ากัน ค่าที่แนะนำคือ 48000 (คุณภาพเสียงที่ยอมรับได้มากที่สุด)
ไบต์เรต
4
จำนวนไบต์ต่อวินาที
อัตราการสุ่มตัวอย่าง numChannels bitsPerSample (เพิ่มเติม)
บล็อกจัดตำแหน่ง
2
จำนวนไบต์สำหรับ 1 ตัวอย่าง
numChannels * bitsPerSample: 8
บิตต่อตัวอย่าง
2
จำนวนบิตต่อ 1 ตัวอย่าง (ความลึก)
ตัวเลขใดๆ ที่เป็นพหุคูณของ 8 ยิ่งตัวเลขสูง เสียงก็จะยิ่งดีและหนักขึ้นเท่านั้น จาก 32 บิต มนุษย์ก็ไม่มีความแตกต่างกัน
subchunk2Id
4
เครื่องหมายอ้างอิงข้อมูล (เนื่องจากอาจมีองค์ประกอบส่วนหัวอื่น ๆ ขึ้นอยู่กับรูปแบบเสียง)
0x64617461 ใน Big-Endian ("ข้อมูล")
ย่อย2ขนาด
4
ขนาดพื้นที่ข้อมูล
ขนาดของข้อมูลในหน่วย int
ข้อมูล
byteRate * ระยะเวลาเสียง
ข้อมูลเสียง
?
ตัวอย่างคลื่น
ตารางก่อนหน้านี้สามารถแปลเป็นโครงสร้างในภาษา 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 (ด้วยสตรีมเสียงสเตอริโอที่ไม่มีการบีบอัด 32 บิตที่ 64kbs มันกลายเป็น ไฟล์ .wav บริสุทธิ์ 80M และมีเพียง 10 ไฟล์เท่านั้น):
โค้ดทั้งหมด (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) แต่เพื่อพิจารณาแต่ละบทความยอดนิยม เราหวังว่าคุณจะได้เรียนรู้สิ่งใหม่ ๆ สำหรับตัวคุณเอง และสิ่งนี้จะช่วยคุณในการพัฒนาในอนาคต
ขอบคุณ!
แหล่งที่มา
ที่มา: will.com
