\ Sound card \ soundcrd.f --low level sound card software \ Tested on sf version 2.00.3 WinME { This file shows how to get buffer-level access to a sound card. Wave out (to sound card) is demonstrated by outputing a test tone to your speakers. Wave in is not shown (it is virtually identical to wave out), but comments on how to do it are given. Note that there are also Winmm.dll API commands to set volume, tone, in/out device, etc. but they are not covered here. An important commandset not shown here is the auxGetDevCaps, waveIn and waveOutGetDevCaps commandset. Use them to see what your card can do. Note that your sound card may not support all formats, and that in some cases the format is supported only through software translation routines that run in real time. I don't know how you detect whether a format is directly supported by the card or through a software routine, but you can get the device ID number from the system when you open the device and that should give a clue. Also, inputs from some devices may dictate your format. Your CD player input, for example, may only work in the 44.1Ks/s 16 bit stereo mode. Popular formats are given below. You can see what other formats your sound card supports by going to your windows sound recorder utility and looking at recording properties in the file menu. References: Multimedia section of Windows SDK Windows 95 Multimedia and ODBC Bible, Waite group press ISBN 1-57169-011-5 } LIBRARY WINMM.DLL 6 import: waveInOpen 3 import: waveInPrepareHeader 1 import: waveInStart 1 import: waveInClose 3 import: waveInGetErrorText 6 import: waveOutOpen 3 import: waveOutPrepareHeader 3 import: waveOutWrite 1 import: waveOutClose 3 import: waveOutGetErrorText 1 import: waveOutReset \ buffers may be any size as long as they hold an \ integer number of samples exactly: the larger the buffer the more efficient \ your processing can be, but the more latency (delay) you have. 11025 value srate \ sample rate 2 value smplbytes \ number of bytes per sample srate smplbytes * value bufsize \ a one second buffer at 11025 samples per second create wobuf0 bufsize allot \ wave out buffer 0 create wobuf1 bufsize allot \ wave out buffer 1 variable wohndl \ wave out handle create errbuf 256 allot \ holds an error string if you get one : woerr? ( result--) ?dup if errbuf 255 waveOutGetErrorText drop cr errbuf zcount type then ; create 1ChPCM-FormatTag \ wave format tag for PCM mono 11025 s/s 1 h, \ WAVE_FORMAT_PCM from MMREG.H file \ There are many formats--WAVE_FORMAT_PCM is pretty much the \ only uncompressed format that can be processed directly \ by DSP software without translation 1 h, \ number of channels: typically 1 (mono) or 2 (stereo) srate , \ samples per second standard values:11025, 22050, 44100 srate 2 * , \ ave bytes per sec: samples per second time block alignment 2 h, \ block alignment=2 bytes: that is, sixteen bits per (mono) sample smplbytes 8 * h, \ bits per sample 0 h, \ extra info \ CD quality is 2 channel, 16 bit 44100 s/s with byte alignment of 4 \ This format is normally the lowest rate you can get from the system: \ 8000 s/s 8 bit sample (u-law compressed--must be uncompressed for DSP processing) \ mono { create ulaw-FormatTag \ wave format tag for mono u-law, 8Ksps 7 h, \ WAVE_FORMAT_MULAW from MMREG.H file 1 h, \ number of channels 8000 , \ samples per second 8000 , \ ave bytes per sec 1 h, \ block alignment=byte 8 h, \ bits per sample 0 h, \ extra info } \ each buffer requires a header structure: this structure is used to register \ the buffer with the system create wohdr0 \ header to process blocks wobuf0 , \ address of buffer bufsize , \ length 0 , \ bytes played, not used 0 , \ user data, not used 0 , \ flags 0 , \ number of loops 0 , \ pointer to wave header tag struct--filled in by winders 0 , \ xtra \ 32 bytes total create wohdr1 \ header to process blocks wobuf1 , \ address of buffer bufsize , \ length 0 , \ bytes recorded, not used 0 , \ user data, not used 0 , \ flags 0 , \ number of loops 0 , \ pointer to wave header tag struct--filled in by winders 0 , \ xtra \ 32 bytes total \ Here is where your DSP routine goes. We will simply put a \ squarewave test tone into the buffer 10000 value amplitude \ PCM systems all seem to use signed integers, either 8 or 16 bits. 0 value smplcount \ counts samples till toggle of amplitude 6 value frequency \ output frequency=srate/(2*frequency) : squarewave ( addr--) \ assumes 2 bytes/sample dup bufsize + swap do amplitude i h! 1 +to smplcount smplcount frequency = if 0 to smplcount amplitude negate to amplitude then 2 +loop ; \ When opening the device, we must specify how we will handle device events: \ the common event will be that a buffer has been filled (on wavein) \ or emptied (on waveout). Here we tack the event handler to Swiftforth's \ console window message handler. We give the DLL the window handle and tell \ it that it is a window handle. console-window +order \ reopen the console window package [+switch sf-messages MM_WOM_DONE run: lparam @ squarewave \ buffer released: fill with new data wohndl @ lparam 32 waveOutWrite drop 0 ; \ and reregister buffer \ wparam has the device handle, but we use our own (see QUIET below) switch] console-window -order : TONE ( --) \ start the sound \ open the device wohndl \ address of handle WAVE_MAPPER \ device ID. WAVE_MAPPER allows the system to select any supporting device 1ChPCM-FormatTag \ format structure above hwnd \ sf window handle 0 CALLBACK_WINDOW \ callback is a window handle WaveOutOpen woerr? \ the handle is in the wohndl variable now. \ prepare the buffers wohndl @ wohdr0 32 waveOutPrepareHeader drop wohndl @ wohdr1 32 waveOutPrepareHeader drop \ fill buffers with intial data wobuf0 squarewave wobuf1 squarewave \ register them. This starts the tone going wohndl @ wohdr0 32 waveOutwrite woerr? wohndl @ wohdr1 32 waveOutWrite woerr? ; : QUIET wohndl @ 0 wohndl ! \ by putting zero in the handle, the callback \ buffer reregister will fail. \ we do this because we cannot close the device until all buffers are played \ or are reset by the command below. dup waveOutReset woerr? \ aborts playback and sends a buffer empty message for each registered buffer waveOutClose woerr? ; { wavein: wavein uses the same procedure as waveOut except waveIn commands are used: setup buffers, setup format structure, prepare headers, open the device and register the buffers. The message MM_WIM_DATA is used by the system, and it means the buffer is full instead of empty. } cr .( TONE for tone, QUIET for relief.) \ dixong@iomega.com