在 DOS 下使用Windows *.WAV 檔案

才智咖 人氣:2.36W

摘 要 該文介紹了Windows聲波檔案(*)的格式,然後分析了在DOS下不使用聲音適配卡播放聲波檔案的關鍵問題,並給出了程式清單。

在 DOS 下使用Windows *.WAV 檔案

關鍵詞 DOS應用軟體開發 多媒體聲波檔案在DOS應用軟體開發過程中,我們非常希望能在不附加任何硬體裝置的條件下實現一些簡單的多媒體功能。

過去許多文章中都討論過Windows影象檔案(*.BMP,*)的格式及其用於美化DOS程式介面的方法。在MS WIMDOWS3.1以後,Windows又提供了標準的聲波檔案(*),因此我們可以利用已有的聲波檔案鑲嵌在自己的軟體中,在DOS下實現語音或其它音響的播放,提高我們的軟體質量。

一、聲波檔案格式分析

*檔案作為多媒體中使用的聲波檔案格式之一,它是以RIFF格式為標準的。RIFF是英文Resource Interchange File Format的縮寫,每個WAV檔案的頭四個位元組便是“RIFF”。
常見的聲波檔案主要有兩種,分別對應於單聲道(11.025KHz取樣率、8Bit的取樣值)和雙聲道(44.1KHz取樣率、16Bit的取樣值)。這裡,取樣率是指:聲波訊號[模→數]轉換過程中單位時間內取樣的次數。取樣值是指每一次取樣周期內聲波模擬訊號的積分值,在程式設計播放過程中我們認為它是揚聲器在此週期單位時間段的音量。
*檔案由檔案頭和資料體兩大部分組成。其中檔案頭又分為RIFF/WAV檔案標識段和聲波資料格式說明段兩部分。
WAV檔案各部分內容及格式見附表。
對於單聲道聲波檔案,取樣資料為八位的`短整數(short int 00H-FFH);而對於雙聲道立體聲聲波檔案,每次取樣資料為一個16位的整數(int),高八位和低八位分別代表左右兩個聲道。
@@03A04400.GIF;*檔案格式說明表@@

二、WAV檔案程式設計

在沒有聲音適配卡的條件下,利用PC機內部揚聲器發聲需解決幾個關鍵問題。
首先是如何產生按指定取樣率要求的標準時間間隔段,以此為基礎控制揚聲器發聲。
由於此時間段要求精確且非常短暫,因此實現起來有一定的難度。解決該問題的思路是修改8253定時器晶片的計數器0(地址:040H)的初始值,改變系統時鐘中斷頻率使其和取樣率相一致,建立使用者的時鐘中斷例程,最終產生標準的時間間隔段。但是在我們修改原有系統時鐘中斷(Int 08H)以後,最終必須恢復原有18.2Hz的系統時鐘中斷。
其次是如何快速地開啟和關閉揚聲器。解決這個問題的方法是直接向8255晶片埠(地址:061H)寫操作。由於PC機機內揚聲器發聲只有開/閉兩種狀態,並不能控制音量大小。
因此還須考慮如何通過開閉揚聲器來摸擬實現音量大小的控制。實現方法是:在每個時間單位內通過改變揚聲器開啟延時的長短代表音量的大小。例如:對於8Bit單聲道聲波檔案,取樣資料的最大值是0FFH,那麼在每個標準時間單位內揚聲器開啟時間應為Delay=(取樣值/256)*標準時間段長度。在此思想下可以將該方法簡化,設揚聲器延時只有0、1(時間單位)兩種情況,即在每個時間單位內,如果取樣值大於128則發聲,如果取樣值小於128就不發聲。顯然這樣做是以拋棄大量聲波資訊為代價的,採用的資訊量只佔原有用資訊的1/12
8,所以這種方法產生的音質較差。

三、程式例項

下面是一個能播放11.025KHz/8Bit/單聲道聲波檔案的演示程式。關於使用*檔案的其它細節,可通過閱讀本程式得到。它採用了第二種延時方式,如果讀者有興趣提高音質可將其改成使用第一種方法,只需將newint08h中的聲音開/關判斷(與128比較)部分改成迴圈等待即可。
迴圈次數通過i=int(vol[counter]/256)*MAXTIMES得到。
式中MAXTIMES為延長一個標準時間單位的迴圈次數。
程度執行環境:486相容機,MS DOS6.0,TC2.0編譯系統。
/*/*/*
*檔案播放程式 DEMO.C,石寧 1994.12
*/*/*/
#include "dos.h"
#include "stdio.h"
#include"string.h"
#define MAXSIZE 50000
struct wave-file_head /*聲波*/
{ /*檔案頭*/
char riff_id[4];/*結構體*/
long int size0;
char wave-fmt[8];
lont int sizel;
int fmttag;
int channel;
long int samplespersec;
long int bytepersec;
int blockalign;
int bitpersamples;
} filehead;
long int datasize, counter=0;
unsigned char vol[MAXSIZE];
unsigned clkdiv;
int oldclk=0,running=1;
void soundon();
void soundoff();
void interrupt(*oldint8h)();
void interrupt newint8h()
{ /*使用者中斷例程*/
if(running)
{

unsigned int i;
disable();/*遮蔽中斷*/
running=0;
if(vol[counter]>=128)
{
i=inportb(0x61);/*開揚*/
i=i|0x03;
outportb(0x61,i);/*聲器*/
}