CHƢƠNG 2 : TÌM HIỂU TỔNG ĐÀI ASTERISK
3.2 Cấu trúc file âm thanh
3.2.3 Xử lí tập tin wave
Là một dạng tập tin dùng để lưu trữ dữ liệu âm thanh số(dạng sóng) và nó là một trong những định dạng phổ biến nhất của hệ điều hành Windows.
Một file RIFF chứa một hay nhiều loại chunks.Mỗi chunk gồm có loại chunk và dữ liệu theo sau loại chunk đó,đồng thời chứa con trỏ để trỏ đến chunk kế tiếp.
Cấu trúc file âm thanh gồm ba khối: khối mơ tả dạng RIFF,khối thuộc tính “fmt “ và khối dữ liệu “data” trong đó khối thuộc tính “fmt” và khối dữ liệu “data” là 2 khối con của khối mơ tả dạng RIFF.
Hình 3.2: Cấu trúc tập tin âm thanh Khối mô tả dạng RIFF: Khối mô tả dạng RIFF:
Khối này xác định dạng RIFF và có kích thước là 12 byte gồm các trường.
Hình 3.3 : Làn sóng dạng RIFF
Khới th ̣c tính “fmt ”:
Khối này xác định các thuộc tính của dữ liệu âm thanh và có kích thước là 24 byte.
Cấu trúc khối fmt như sau:
class WavFormatChunk : WavChunk {
// kiểu mã hoá dữ liệu âm thanh public ushort wFormatTag;
// có hai giá trị: bằng 1 cho âm thanh mono, bằng 2 cho âm thanh stereo
public ushort wChannels;
// cho biết tốc độ lấy mẫu (11025-11,025kHz,22050-22,05 kHz…)
public ulong dwSamplesPerSec;
//số byte trung bình yêu cầu trong 1 giây để phát lại mẫu dữ liệu của sóng âm
public ulong dwAvgBytesPerSec;
// mẫu <= 8 bit sẽ yêu cầu 1 byte, mẫu 9-16 bit sẽ yêu cầu 2 bytes
public ushort wBlockAlign;
// cho biết số bit trong một mẫu dữ liệu(dạng byte hay word) public ushort wBitsPerSample;
}
Khối này xác định các thuộc tính của dữ liệu âm thanh và có kích thước là 24 byte gồm các trường:
• Subchunk1ID:
- Kích thước: 4 byte
- Chứ c năng: chứa chuỗi “fmt ”. • Subchunk1Size:
- Kích thước: 4 byte
- Chức năng :cho biết tổng kích thước của các trường thuộc khối thuộc tính đứng phía sau trường này.
(đối với tập tin wave khơng nén thì Subchunk1Size bằng 16) • AudioFormat:
- Kích thước: 2 byte
- Chức năng: cho ta biết dạng nén của dữ liệu trong tập tin wave. Giá trị mô tả
- 0 :Không xác định
- 1: Không nén (PCM-Pulse Code Modulation) Một số giá trị thơng dụng
• NumChannels: Kích thước: 2 byte
Chức năng: cho biết số kênh của tập tin wave. (Mono=1,Stereo=2,v.v)
• SampleRate:
- Kích thước: 4 byte
- Chức năng: cho biết số mẫu trên 1s và đây chính là tần số lấy mẫu của tập tin wave.
• ByteRate:
- Kích thước: 4 byte
- Chức năng:cho biết số byte trên 1 s ứng với tần số lấy mẫu trên. (ByteRate=SampleRate*NumChannels*(BitsperSample/8))
• BlockAlign:
- Kích thước:2 byte
- Chức năng: cho biết số byte của 1 mẫu gồm tất cả các kênh. • BitsPerSample: cho biết số bit trên 1 mẫu chỉ tính cho 1 kênh. ( 8 bit=8,16 bit=16,v.v)
Data Chunk - "dữ liệu"
Chunk sóng dữ liệu chứa dữ liệu mẫu âm thanh kỹ thuật số có thể được giải mã bằng cách sử dụng định dạng và phương pháp nén quy định tại Chunk Định dạng Wave.
Đối với mẫu âm thanh 8 bit,dữ liệu của “data” bao gồm các giá trị 1 byte(từ 0-255) của các mẫu âm thanh. Với mẫu âm thanh 16 bits,mỗi mẫu dữ liệu gồm 2 bytes (-32768 đến 32767).
Cấu trúc :
typedef struct
{
ID chunkID; Long chunkSize;
unsigned char waveformData [];
} DataChunk;
ChunkSize là số lượng byte trong đoạn.
Độ phân giải bit và các thông tin khác nhận được từ các đoạn định dạng(Format).
Các mảng waveformData chứa dữ liệu dạng sóng thực tế. Dữ liệu được sắp xếp thành các khung mẫu.
Số lượng các khung mẫu trong waveformData được xác định bằng cách chia này chunkSize wBlockAlign đoạn định dạng.
Chunk dữ liệu là cần thiết vì chỉ có một Chunk dữ liệu có thể xuất hiện trong một WAVE.
Làn sóng các tập tin thường chứa chỉ có một đoạn dữ liệu, nhưng chúng có thể chứa nhiều hơn một nếu họ được chứa trong một Chunk Danh sách Wave ("wavl").
Hình 3.4: Định dạng dữ liệu Chunk
Mẫu kĩ thuật số đa kênh âm thanh được lưu trữ như các dữ liệu sóng xen kẽ (như âm thanh stereo và surround).
Sóng tập tin được lưu trữ bằng cách lướt qua các mẫu âm thanh cho mỗi kênh trước khi tiến tới mẫu tiếp theo. Điều này được thực hiện để các tập tin âm thanh có thể được phát hoặc xem trực tiếp trước khi tồn bộ tập tin có thể được đọc. Đây là tiện dụng khi phát một file lớn từ đĩa (có thể khơng hồn tồn phù hợp với bộ nhớ) hoặc trình chiếu một tập tin qua Internet.
Các giá trị trong sơ đồ dưới đây sẽ được lưu trữ trong một file Wave theo thứ tự được liệt kê trong cột giá trị (trên xuống dưới).
Hình 3.5: Làn sóng mẫu Interlaced Stereo
Một điểm về dữ liệu mẫu mà có thể gây ra một số nhầm lẫn khi mẫu được biểu diễn với 8-bit, được quy định như giá trị unsigned. Tất cả các mẫu khác - bit kích thước được quy định cụ thể như các giá trị đã kí kết. Ví dụ một mẫu 16-bit có thể là -32.768-32767 với một điểm giữa (im
lặng) ở 0.
Như đã đề cập trước đó, tất cả các khối RIFF (bao gồm cả WAVE "dữ liệu" khối) phải được liên kết từ. Nếu dữ liệu mẫu sử dụng một số lẻ của byte, một byte padding với một giá trị không phải được đặt ở phần cuối của dữ liệu mẫu. Các đoạn "dữ liệu" tiêu đề của kích thước khơng nên bao gồm byte này.
Fact Chunk - "thực tế"
Một đoạn thông tin thực tế nén phụ thuộc mã về nội dung của tập tin sóng. Đó là yêu cầu tất cả các định dạng WAVE nén và khi các dữ liệu dạng sóng được chứa bên trong một đoạn "wavl DANH SÁCH", nhưng không cần thiết cho các tập tin định dạng không nén PCM WAVE (nén mã 1) có chứa các dữ liệu dạng sóng bên trong một đoạn "dữ liệu" .
Hình 3.6: Định dạng thực tế Chunk
Định dạng Phụ thuộc dữ liệu :
Hiện tại chỉ có một lĩnh vực quy định cho các dữ liệu phụ thuộc vào định dạng. Đó là một giá trị duy nhất 4-byte quy định cụ thể số lượng mẫu trong đoạn dữ liệu dạng sóng. Giá trị này có thể được sử dụng với các mẫu ,mỗi giá trị thứ hai quy định trong định dạng đoạn để tính tốn chiều dài dạng sóng trong vài giây.
Khi định dạng WAVE mới được giới thiệu, các đoạn thực tế sẽ được mở rộng, lĩnh vực phụ được thêm vào sau khi số lượng quy định của mẫu nghiên cứu lấy.
Các ứng dụng có thể sử dụng kích thước đoạn thực tế để xác định các lĩnh vực có mặt trong đoạn.
3.2.4 Đọc file RIFF:
Làn sóng tập tin header theo cấu trúc tập tin định dạng RIFF chuẩn. 8 byte đầu tiên trong tập tin tiêu đề trong đó có một ID đoạn "RIFF" và
một đoạn kích thước bằng kích thước tập tin trừ đi 8 byte được sử dụng bởi tiêu đề.
Vì vậy, chúng ta cần phải biết tổng chiều dài của tất cả các tập tin để xác định ChunkSize và đọc NumChannels SampleRate và BitsPerSample.
private void WaveHeaderIN( string spath) {
FileStream fs = new FileStream(spath, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs); length = ( int )fs.Length - 8 ;
fs.Position = 22 ; channels = br.ReadInt16(); fs.Position = 24 ; samplerate = br.ReadInt32(); fs.Position = 34 ; BitsPerSample = br.ReadInt16(); DataLength = ( int )fs.Length - 44 ; br.Close ();
fs.Close(); }
Như chúng ta biết, các kênh được lưu trữ trong tiêu đề WAV trong số byte 22, chúng tơi di chuyển vị trí hiện tại của tập tin đến vị trí này và kích thước của nó là 2 byte, do đó chúng tơi sử dụng br.ReadInt16 () để đọc chỉ có 2 byte.
private void WaveHeaderOUT( string sPath) {
FileStream fs = new FileStream(sPath, FileMode.Create, FileAccess.Write );
BinaryWriter bw = new BinaryWriter(fs); fs.Position = 0;
bw.Write(new char[4] { 'R', 'I', 'F', 'F' }); bw.Write(length);
bw.Write(new char[8] {'W','A','V','E','f','m','t',' '}); bw.Write((int)16);
bw.Write((short)1); bw.Write(channels); bw.Write(samplerate );
bw.Write((int)(samplerate * ((BitsPerSample * channels) / 8))); bw.Write((short )((BitsPerSample * channels) / 8));
bw.Write(new char[4] {'d','a','t','a'}); bw.Write(DataLength);
bw.Close(); fs.Close(); }
Khai báo "RIFF" như là một mảng của char, không phải là chuỗi và sử dụng loại int để lưu trữ 4 byte và loại ngắn để lưu trữ 2 byte. public void Merge( string [] files, string outfile)
{
WaveIO wa_IN = new WaveIO(); WaveIO wa_out = new WaveIO();
wa_out.DataLength = 0; wa_out.length = 0; //Gather header data
foreach (string path in files) {
wa_IN.WaveHeaderIN(@path);
wa_out.DataLength += wa_IN.DataLength; wa_out.length += wa_IN.length;
}
//Recontruct new header
wa_out.BitsPerSample = wa_IN.BitsPerSample; wa_out.channels = wa_IN.channels;
wa_out.samplerate = wa_IN.samplerate; wa_out.WaveHeaderOUT(@outfile); foreach (string path in files)
{
FileStream fs = new FileStream(@path, FileMode.Open, FileAccess.Read);
byte[] arrfile = new byte[fs.Length - 44]; fs.Position = 44;
fs.Read(arrfile, 0, arrfile.Length); fs.Close();
BinaryWriter bw = new BinaryWriter(fo); bw.Write(arrfile);
bw.Close(); fo.Close();
}
Chúng ta cần để tính tốn tổng chiều dài và chiều dài dữ liệu của tất cả các tập tin và sau đó xác định các kênh, SampleRate và BitsPerSample của file.
Tất cả chúng ta cần làm là gọi phương thức kết và quy định cụ thể các tập tin đầu vào và tập tin đầu ra.
string [] files = new string [ 2 ] { @" ..\đường dẫn\file.wav" , @"C:\WINDOWS\Media\Windows\XP Shutdown.wav" }; WaveIO wa = new WaveIO();