谢谢!!!
其实很简单,只是对声卡DSP芯片个端口的操作而已,这方面的资料的确很少,我这几天也在研究它,希望真的用小内存做出播发任意长度的文件,还想自己做个语音识别的程序。
其实控制声卡DSP芯片播发声音(这里只指播发WAV格式)有两种方式,第一是直接以块的方式,即把声音信息一并送入DSP芯片交给DSP芯片处理,这样的好处是播发流畅,而且不用操心采样率,直接交给DSP芯片就完了,而且在程序中表现想是多线程,即声音播放的同时,其实程序已经跑后面去了,所以很适合在游戏用到他,可不足之处就是因为DOS无大内存给你使用,所以播放的声音长度也就限制了,只能是64K以内,NEO中即使用此种方法。
第二中方法就是,你自己写一个模块实现采样率,再逐个把数据送入DSP芯片,这样的好处是(当然是你先修改时钟出发频率,然后自己设置一个中断,以几数的方式来实现以指定时间间隔往DSP芯片送数据为前提)可以播放任意长度的声音文件,可他的不足就是:因为你是以CPU来模拟实现采样率,所以在此线程中是先播发完文件才能执行后面的代码,最致命的就是播放声音可能不流畅,因为你是自己用实现的修改时钟出发频率,然后自己设置一个中断,进而记数来实现间隔,那在这个过程中你很难保证间隔是一直恒定的(比如要是另个程序当机,很可能回影响到你自己播放程序)......但我鼓励大家可以一试来实现本方法,有挑战些。
下面我给出第一法的一个程序,写的很好,董凯给的,不知道是他些的否?风格很好,很容易看懂
#include <dos.h>
#include <stdio.h>
#ifndef FP_OFF
#define FP_OFF(fp)   ((unsigned)(fp))
#endif
#ifndef FP_SEG
#define FP_SEG(fp)   ((unsigned)((unsigned long)(fp) >> 16))
#endif
#define DIGI_AUTODETECT       1 /* 让 Allegro 来选择一个数字声音驱动设备*/
#define DIGI_NONE             0 /* 无数字声音*/
#define DIGI_SB             100 /* Sound Blaster (自动检测类型)*/
#define DIGI_SB10           110 /* SB 1.0 (8 位单声道 单一发射 dma)*/
#define DIGI_SB15           115 /* SB 1.5 (8 位单声道 单一发射 dma)*/
#define DIGI_SB20           120 /* SB 2.0 (8 位单声道 自动初始化 dma)*/
#define DIGI_SBPRO          108 /* SB Pro (8 位立体声)*/
#define DIGI_SB16           116 /* SB16 (16 位立体声)*/
#define DIGI_AUDIODRIVE     200 /* ESS AudioDrive*/
#define DIGI_SOUNDSCAPE     300 /* Ensoniq Soundscape*/
/*midi_card 应当是以下值中的一个*/
#define MIDI_AUTODETECT     1 /* 让 Allegro 挑选一个 MIDI 声音驱动程序*/
#define MIDI_NONE           0 /* 无 MIDI 声音*/
#define MIDI_ADLIB           /* Adlib 或 SB FM 混合 (自动检测型号)*/
#define MIDI_OPL2            /* OPL2 混合 (单声道, 在 Adlib 和 SB 下使用)*/
#define MIDI_2XOPL2          /* 双 OPL2 混合 (立体声, 在 SB-Pro-I 下使用)*/
#define MIDI_OPL3            /* OPL3 混合 (立体声, SB Pro-II 及以上)*/
#define MIDI_SB_OUT          /* SB MIDI 界面*/
#define MIDI_MPU             /* MPU-401 MIDI 界面*/
#define MIDI_DIGMID          /* 基于采样的软件波表播放器*/
#define MIDI_AWE32
#define PCM_HEAD_LONG  50
#define PLAY_DELAY     100
typedef struct WaveData
{
   unsigned short sample_lenth;
   unsigned short rate;
   unsigned short channels;
   unsigned char  time_constant;
   char           bit_res;
   char           *sample;
}SAMPLE;
struct HeaderType
{
   long           riff;       /*RIFF类资源文件头部*/
   unsigned long  file_len;   /*文件长度*/
   char           wave[4];    /*"WAVE"标志*/
   char           fmt [4];    /*"fmt"标志*/
   char           NI1 [4];    /*过渡字节*/
   unsigned short format_type;/*格式类别(10H为PCM形式的声音数据)*/
   unsigned short Channels;   /*Channels 1 = 单声道; 2 = 立体声*/
   long           frequency;  /*采样频率*/
   long           trans_speed;/*音频数据传送速率*/
   char           NI2 [2];    /*过渡字节*/
   short          sample_bits;/*样本的数据位数(8/16)*/
   char           data[4];    /*数据标记符"data"*/
   unsigned long  wav_len;    /*语音数据的长度*/
   char           NI3 [4];    /*过渡字节*/
};
unsigned short g_base;        /*声卡基址*/
unsigned short g_port;
void   write_dsp( unsigned char value );
short  ResetDSP ( unsigned short Test );
void   set_mixer_reg(short index, char value);
short  install_sound(short digi_card, short midi_card, char *cfg_path);
short  play_sample(SAMPLE *spl, short vol, short pan, short freq, short loop);
SAMPLE *load_wav(char *filename);
char   play_back  (SAMPLE *wave);
void   stop_sample(SAMPLE *spl);
void   destroy_sample(SAMPLE *spl);
void   remove_sound(void);
#define reset_dsp() ResetDSP(g_port)
main()
{
   SAMPLE *wav;
   install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL);set_mixer_reg(0, 0);
   wav = load_wav("act.wav");set_mixer_reg(0x26, 0x0);
   play_sample(wav, 255, 128, 1000, 1); getch();destroy_sample(wav);
   wav = load_wav("win.wav");set_mixer_reg(0x26, 0x0f);
   play_sample(wav, 255, 128, 1000, 1); getch();destroy_sample(wav);
   wav = load_wav("down.wav");set_mixer_reg(0x26, 0);
   play_sample(wav, 255, 128, 1000, 1); getch();destroy_sample(wav);
   wav = load_wav("sound.wav");set_mixer_reg(0x26, 0xaa);
   play_sample(wav, 255, 128, 1000, 1); getch();destroy_sample(wav);
   wav = load_wav("levup.wav");set_mixer_reg(0x26, 0x55);
   play_sample(wav, 255, 128, 1000, 1);  destroy_sample(wav);
   getch();
   remove_sound();
}
short install_sound(short digi_card, short midi_card, char *cfg_path)
{
   if (digi_card)
   {
      if ( ResetDSP (0x220) )
      {
         /*基址为210h*/
         g_port = 0x220;
      }
      else if (ResetDSP (0x230))
      {
         /*基址为220h*/
         g_port = 0x230;
      }
      else if (ResetDSP (0x240))
      {
         /*基址为240h*/
         g_port = 0x240;
      }
      else
      {
         /*基址检测失败*/
         g_port = -1;
         return 0;
      }
   }
   if (midi_card)
   {}
   cfg_path += 0; /*只是为了不报警*/
   return 1;
}
/****************************************************************************
 检查一个声卡基址是否存在,如果存在则将声卡复位                             *
****************************************************************************/
short ResetDSP(unsigned short Test)
{
   /*重置DSP*/
   outportb (Test + 0x6, 1);
   delay(50);
   outportb (Test + 0x6, 0);
   delay(50);
   /*如果重置成功则检查*/
   if ((inportb(Test + 0xE) & 0x80 == 0x80) && (inportb(Test + 0xA) == 0xAA))
   {
      /*DSP被找到*/
      g_base = Test;
      return (1);
   }
   else
   {  /*找不到DSP*/
      return (0);
   }
}
/****************************************************************************
** 发送一个字节到声卡的DSP(数字信号处理芯片:Digital Signal Processor)     **
****************************************************************************/
void write_dsp(unsigned char value)
{
   /*等待DSP接收一个字节*/
   while ((inportb(g_base + 0xC) & 0x80) == 0x80);
   /*发送字节*/
   outportb (g_base + 0xC, value);
}
void set_mixer_reg(short index, char value)
{
   outportb (g_port + 4, index);
   outportb (g_port + 5, value);
}
/****************************************************************************
** 播放内存中的部分音频流                                                  **
****************************************************************************/
char play_back (SAMPLE *wave)
{
   long         LinearAddress;
   unsigned short page, offset;
   if ((!wave) || (!wave->sample))
   {
      printf("no sample!");
      return 0;
   }
   /*开启声卡*/
   write_dsp( 0xD1 );
   write_dsp( 0x40 );                    /*DSP第40h号命令 :设置采样频率*/
   write_dsp( wave -> time_constant );            /*Write time constant*/
   /*将音频流指针转换成线性地址*/
   LinearAddress = FP_SEG ( wave -> sample );
   LinearAddress = ( LinearAddress << 4 ) + FP_OFF ( wave->sample );
   page   = LinearAddress >> 16;      /*计算页*/
   offset = LinearAddress & 0xFFFF;   /*计算页偏移*/
   /*注意 :这个操作只能工作于DMA的第一通道*/
   outportb (0x0A, 5);                /*Mask 锁DMA通道一*/
   outportb (0x0C, 0);                /*清除DMA内部翻转标志*/
   outportb (0x0B, 0x49);             /*设置成回(播)放模式*/
   /*
    模式由下面几项组成:
    0x49 = 二进制 01 00 10 01
                  |  |  |  |
                  |  |  |  +- DMA通道 01
                  |  |  +---- 读操作 (从内存到DSP)
                  |  +------- 单一周期方式
                  +---------- 块方式
   */
   outportb ( 0x02, offset & 0x100);  /*将偏移量写入DMA控制器*/
   outportb ( 0x02, offset >> 8);
   outportb ( 0x83, page);             /*将页面写入DMA控制器*/
   outportb ( 0x03, wave->sample_lenth & 0x100);
   outportb ( 0x03, wave->sample_lenth >> 8);
   outportb ( 0x0A, 1 );               /*激活DMA通道一*/
   write_dsp( 0x14 );                  /*DSP第14h号命令 :单一周期回放*/
   write_dsp( wave -> sample_lenth & 0xFF );
   write_dsp( wave -> sample_lenth >> 8);
   return 1;
}
/****************************************************************************
** 将音频文件读入内存                                                      **
** 这个例程能操作标准的PCM文件头部                                         **
** 它覆行了许多错误检查                                                    **
****************************************************************************/
SAMPLE *load_wav(char *filename)
{
   struct HeaderType t_header;
   FILE   *wav_file;
   SAMPLE *voice;
   /*如果打不开文件...*/
   wav_file = fopen(filename, "rb");
   if (wav_file == NULL)
   {
    printf ("Can't open the file: %s\n", filename);
    return (NULL);
   }
   voice = (SAMPLE *)malloc(sizeof(SAMPLE));
   if ( voice == NULL)
   {  printf ( " Not mem!\n" );
      return NULL;
   }
   /*读取文件头部*/
   fread ( &t_header, sizeof(t_header), 1, wav_file );
   /*检查RIFF头*/
   if ( t_header.riff != 0x46464952)
   {
    printf ( " Not a wave file!\n" );
    return ( NULL );
   }
   /*检查通道*/
   if ( t_header.Channels != 1 )
   {
    printf ( "Not a MONO wave file!\n" );
    return ( NULL );
   }
   /*检查采样位数*/
   if ( t_header.sample_bits != 8 )
   {
    printf ( "Not an 8-bit wave file!\n" );
    return ( NULL );
   }
   voice->sample_lenth = (unsigned short)(t_header.file_len - PCM_HEAD_LONG);
   voice->time_constant= 256 - (1000000L / ( (voice->rate=t_header.frequency) * (voice->channels=t_header.Channels) ));
   if ( t_header.file_len - PCM_HEAD_LONG >= 0xffff )
   {
      voice->sample_lenth = 0xfffe;
   }
   if (voice->sample_lenth == 0) voice->sample_lenth = 1;
   voice->sample = (char *) malloc ( voice->sample_lenth + 1);/*申请内存*/
   if ( voice->sample == NULL)
   {
      printf ( " Not mem!!\n" );
      fclose ( wav_file ); /*关闭文件*/
      return NULL;
   }
   /*fseek(wav_file, 57L, SEEK_SET);*/
   /*读取采样数据流*/
   fread  ( voice->sample, voice->sample_lenth + 1, 1, wav_file );
   fclose ( wav_file ); /*关闭文件*/
   return voice;
}
short play_sample(SAMPLE *spl, short vol, short pan, short freq, short loop)
{
   vol += 0;
   pan += 0;
   freq+= 0;
   loop+= 0;
   if (spl)
   {
      /*开始回放*/
      play_back(spl);
      delay(PLAY_DELAY);
      /*停止 DMA 传送*/
      write_dsp(0xD0);
      return 1;
   }
   return 0;
}
void stop_sample(SAMPLE *spl)
{
/*   SAMPLE stop;
   char   sam[10];
   stop.sample = sam;
   stop.sample_lenth = 9;
   stop.time_constant= 9;
   play_back(&stop);*/
}
void destroy_sample(SAMPLE *spl)
{
   if (spl)
   {
      free(spl->sample);
      spl->sample = NULL;
      free(spl);
      spl = NULL;
   }
}
void remove_sound(void)
{
   /*关闭声卡*/
   write_dsp(0xD3);
   /*重置DSP*/
   reset_dsp();
}
第二方法,我这也有个代码,可是错的,根本编译不通过。
我本是早想写个声卡DSP的专题文章,就是因为一直没去把第二法实现所以一直没发,可能过两日我回发给这样的专题,好好的介绍一下声卡的控制......
(当然,我自己也在摸索阶段,因为基本没有关于DSP声卡芯片个端口具体详实的介绍,所以有时候很为难,我们大家也只好尽力而为)



											
	    

	


哦这样呀,那就小弟