home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-10-12 | 65.9 KB | 1,800 lines |
- 4 AUDIO DEVICE
-
- 4.1 INTRODUCTION
-
- The Amiga was designed to be the first home computer which
- combined state of the art graphics and impressive sound
- capabilities. Although more than six years have passed since
- the first Amiga was released, very few other computers can
- compete with the sound quality and flexibility offered by the
- Amiga.
-
- In this chapter we look at how you can use the sound system to
- produce sound effects as well as compose melodies. Since the
- sound system has been built into the rest of the operating
- system, it will work smoothly together with other processes, and
- can without problems be used by several tasks at the same time.
-
-
-
- 4.1.1 SOUND
-
- Sound is simply vibrating air that is registered by our ears
- and interpreted as lovely music, speech, white noise, etc...
- Although the simplicity it is possible to produce almost
- unlimited number of variations of even a simple tune. Even a
- single note sound very different if it was made by a piano or
- violin. By altering the volume and rate it is played it is
- possible to produce even more variations.
-
- To better understand what sound is and how it is produced we
- will first look at some commonly used technical words.
-
- The "amplitude" of sound is the height of one of the waveforms.
- The higher amplitude the louder will the waveform sound.
-
- The "frequency" is the number of waveforms produced each
- second. The higher frequency the higher will the tone be.
-
- There exist a third sound variable called "timbre". The problem
- with it is that it is very hard to describe, but is still
- very important for how the sound will be perceived. It can
- be called "the sound identity". A violin and a piano can play
- a tone with the same amplitude and frequency, but it will
- still sound very different. Even two violins can sound very
- different.
-
- The problem with reproducing the timbre is that by altering the
- frequency or amplitude just a little (does not even need to be
- a noticeable change) it can completely change the timbre. If you
- have read about "chaos theories" the timbre can be described
- with the "butterfly effect"; even a small change in wind by a
- butterfly can tip the balance and in the end cause a hurricane
- on the other side of the world.
-
- By combining several frequencies each with its own amplitude
- and timbre we can produce unlimited numbers of variations.
-
-
-
- 4.1.2 DIFFERENT WAVEFORMS
-
- Although each waveform from a note is different from another
- we usually do not bother too much about it. It is usually
- enough to use just a few different elementary waveforms. By
- altering the frequency and amplitude of these waveforms we can
- then still produce millions of different sounds.
-
- The most commonly used waveforms are the sine, triangular and
- square waveforms. See illustration "Waveforms".
-
-
-
- 4.1.3 DIGITAL AND ANALOG WAVEFORMS
-
- When we are using computers we can only handle digital numbers.
- To convert these digital numbers into sound we must first
- convert the digital numbers to analog values which can then
- be sent to a loud speaker. See illustration "AnalogDigital".
-
- On each Amiga computer there exist four "sound channels" which
- each can convert digital numbers into analogue values. We can
- therefore play four different sounds at the same time, which
- means that very complex sounds can be produced.
-
- Two of the sound channels are connected to the right audio
- port, and the other two are connected to the left audio port.
- We can therefore produce stereo sound on the Amiga.
-
- The sound channels are sometimes called "eight-bit channels"
- because they convert eight-bit values into analog values. The
- Amiga can therefore produce sounds were the values in the
- waveforms ranges from -128 to 127. Compact Disk (CD) players
- use 16 bits and can therefore use even more values. For
- computers today eight bits are usually more than enough, and
- can produce very impressive sounds.
-
-
-
- 4.1.4 PLAY SAMPLED SOUNDS OR CREATE YOUR OWN TUNES
-
- On the Amiga you can create your own tunes by using a waveform
- and modulating it (changing the amplitude and frequency). You
- can also play sound data that has been sampled (converted from
- analog sound to digital data). By using sampled data you can
- reproduce very complex waveforms, and it will sound natural.
-
- To load and use already sampled data can very easily be done
- with help of the two utilities I have included in the "Sound"
- manual, "Easy Sound" and "Include Sound".
-
-
-
-
-
- 4.2 PREPARE THE AUDIO DEVICE
-
- 4.2.1 PRIORITY
-
- Since several programs may be running at the same time it may
- happen that two or more programs tries to use the same sound
- channel at the same time. To solve problems like this we have
- to use priorities.
-
- If a program tries to use a sound channel that some other
- program is already using two things may happen. If the new
- program has the same or lower priority than the other program
- the new one is refused any access to the sound channel and the
- old program may continue to use it. On the other hand, if the
- new program has higher priority it will "steal" the sound
- channel from the old program.
-
- Some sounds are more important than others. An emergency alarm
- is of course much more important than a background tune, and
- thus the emergency alarm should have higher priority. Commodore
- has suggested the following priorities:
-
- Type of sound Suggested priority
- --------------------------------------------
- Unstoppable sound 127
- Emergencies 95
- Attention 85
- Speech 75
- Information 60
- Music 0
- Sound effects -35
- Background music -90
- Silence -128
-
-
- If a sound is so important that no one what ever happens should
- be able to interrupt it we should use the "unstoppable sound
- priority". You should never start a sound with this priority.
- You should instead start it with a lower (for example with the
- "emergency") priority, and then increase it.
-
- The "emergency" priority should be used when something happens
- that is so important that the user must immediately know about
- it. Examples: the program is going to visit the guru if the
- user does not immediately do some thing.
-
- The "attention" priority is used to alert the user that
- something has happen and the user must as soon as possible know
- about it.
-
- The "speech" priority is used by the "Narrator Device" when it
- is producing synthesized speech. Spoken information is usually
- more important for the user than a nice tune.
-
- The "information" priority should be used when you want to tell
- the user something, but the user must not immediately know
- about it. This priority can be used when you want to tell the
- user to read something, or start to input some value etc...
-
- When you are plying music you should use the "music" priority.
- This is for programs that are using music as one of the primary
- functions. Background tunes should use the "background
- priority".
-
- The lowest priority level is "silent", and should actually never
- be used. If you do not want to play the sound you should free
- the sound channel instead.
-
- The priority can be varied inside each priority group. For
- example; soft and quiet instruments/notes should have lower
- priority than loud and characteristic instruments/notes. You
- can also alter the priority while the sound is being played.
- The first part of an emergency sound can for example have
- higher priority than the rest of the sound.
-
- Sadly there does not exist any header file that defines the
- different priorities. In my examples I will therefore use my
- own constants which are declared like this:
-
- Type of sound Priority
- ---------------------------
- SOUND_UNSTOPPABLE 127
- SOUND_EMERGENCIES 95
- SOUND_ATTENTION 85
- SOUND_SPEECH 75
- SOUND_INFORMATION 60
- SOUND_MUSIC 0
- SOUND_EFFECT -35
- SOUND_BACKGROUND -90
- SOUND_SILENCE -128
-
-
-
- 4.2.2 ALLOCATING CHANNELS
-
- There exist four sound channels which each one can convert
- digital sound data into analogue sound which is sent to a
- loudspeaker. The first and last sound channel is connected to
- the left audio port, while the second and third is connected to
- the right audio port.
-
- When you are reserving a sound channel you specify which
- channels you want to use by using a value where the first bit
- represents the first sound channel, the second bit the second
- sound channel and so on...
-
-
- Bit: 3 2 1 0
- -------------------------------------
- Value: 8 4 2 1
- Channel: 3 2 1 0
- Audio port: Left Right Right Left
-
- To reserve the first and last channel (both using the left
- sound channel) you would use the value 9 (1001[bin] = 9[dec]).
- To reserve the second and third channel (both using the right
- sound channel) you would use the value 6 (0110[bin] = 6[dec]).
- To reserve all four sound channels set the value to 15
- (1111[bin] = 15[dec]).
-
- Since other programs may be running at the same time it may
- happen that they are already using one or more of the sound
- channels. If they have lower priority than your program, you
- will "steal" the sound channel from them. However, if you have
- the same or lower priority, you are refused any access to that
- sound channel.
-
- The good thing is that you may use an array of allocation
- values, and if the first value could not be satisfied the
- audio device will automatically try the next value. If you are
- lucky there exist a combination of sound channels which is not
- currently being used.
-
- If you want to play some sounds in stereo you have to use one
- sound channel from the right and one from the left port. There
- exist four possible combinations which satisfy this, and the
- "allocation array" should therefore look like this:
-
- Dec Bin Description
- ---------------------------------------------------
- 3 0011 First left and first right channel.
- 5 0101 First left and second right channel.
- 10 1010 Second left and first right channel.
- 12 1100 Second left and second right channel.
-
- UBYTE allocation_array = { 3, 5, 10,12 };
-
-
- The audio device will first try all combinations in the
- allocation array before it will use the priority to "steal"
- the channels for you.
-
-
-
- 4.2.3 CREATE WAVEFORMS
-
- The audio device can play any type of waveform that is ranging
- from -128 up to 127. Usually you will use the normal sine,
- triangle, and square wave forms, but you can equally well play
- more complex waveforms and even directly sample sound.
-
- It is important to note that the audio device is a part of the
- special custom chips in the Amiga and can therefore only access
- sound data which is located in the "chip memory". The data must
- also start on a word boundary, and consists of an even number
- of bytes.
-
- The more data used for the waveform the more complex waves can
- be used. The smallest waveforms consists of only two values.
-
- Example: We will create a sin waveform with 16 values.
-
- /* If we are using mathematical functions */
- /* like sin() we must include this file: */
- #include <math.h>
-
- /* Define a constant for the number of values in */
- /* the wave form. It can then easily be changed. */
- #define SINE_DATA_LENGTH 16
-
- /* Declare a pointer to our sine wave: */
- BYTE *sine_wave;
-
- /* ... */
-
- /* Allocate some chip memory for the sine wave: */
- /* (All memory allocated by AllocMem() will */
- /* always start on a word boundary.) */
- sine_wave = (BYTE *) AllocMem( SINE_DATA_LENGTH, MEMF_CHIP );
-
- /* Have we got the memory? */
- if( !sine_wave )
- clean_up( "Not enough memory!" );
-
- /* Initialize the sine waveform: */
- for( loop = 0; loop < SINE_DATA_LENGTH; loop++ )
- sine_wave[ loop ] =
- 127 * sin( loop * 2 * PI / SINE_DATA_LENGTH );
-
-
-
- 4.2.4 NOTES AND FREQUENCIES
-
- On octave consists of 12 notes. Here is a list of the
- frequencies that represents the notes which are one octave
- higher than the middle octave on a piano:
-
- Note Frequency
- ---------------
- A 880.0
- A# 932.3
- B 987.8
- C 1046.5
- C# 1108.7
- D 1174.7
- D# 1244.5
- E 1318.5
- F 1396.9
- F# 1480.0
- G 1568.0
- G# 1661.2
-
- To change octave you simply double/half these values. When you
- double the values you go one octave up, and when you half the
- values you go one octave down. Example, A = 880, one octave
- lower A = 440, one octave higher A = 1760, two octaves higher
- and A = 3520.
-
- When the audio device should play a waveform you do not specify
- which frequency should be used. Instead you specify the
- "period" value which should be used for each sample (value) in
- the waveform. The period can be calculated with the following
- formula:
-
- Period = Clock ticks per second / Frequency / Number of samples
-
- The speed of the clock depends on if you have an European (PAL)
- or American (NTSC) Amiga. On an NTSC machine the clock ticks
- 3579545 times per second, while on a PAL machine it only ticks
- 3546895 times per second.
-
- Example: We want to play the sine waveform we previously
- made with the frequency 880 (note A). (We are using an
- European PAL Amiga.)
-
- period = 3546895 / 880 / SINE_DATA_LENGTH;
-
-
- Some extra information: If you have not already noticed it you
- may get the wrong answer when you are dividing values with each
- other. The problem is that C is always using the most accurate
- value in the formula as the base on which it does the
- calculations. If you multiply an integer value and a float value
- the C compiler will do the intermediate calculations with float
- values. If you on the other hand multiply two integers with
- each other, the C compiler will do the intermediate calculations
- with integer values.
-
- The problem is that if you divide two values with each other,
- and both are integers, the calculations will be preformed with
- integer values. Since you do not have any decimals you can end
- up with a completely wrong answer. With multiplications (and
- addition and subtraction as well) this will never cause any
- errors.
-
- When you are dividing two values with each other you should
- therefore always convert the value (which you divide the other
- value with) to a float (or double if necessary). Simply use
- normal casting. The rule is to always put the word "(float)"
- in front of the bottom value when you are using division. In
- our example the formula should look like this: (The second
- float is actually unnecessary, but it does not matter.)
-
- period = 3546895 / (float) 880 / (float) SINE_DATA_LENGTH;
-
-
-
- 4.2.5 THE AUDIO REQUEST BLOCK
-
- The audio device is controlled like any other device with help
- of request blocks. When you are using the audio device you
- should use the extended "IOAudio" structure which is declared
- in the header file "devices/audio.h" as this:
-
- struct IOAudio
- {
- struct IORequest ioa_Request;
- WORD ioa_AllocKey;
- UBYTE *ioa_Data;
- ULONG ioa_Length;
- UWORD ioa_Period;
- UWORD ioa_Volume;
- UWORD ioa_Cycles;
- struct Message ioa_WriteMsg;
- };
-
- ioa_Request: The top part of the request block consists as
- always of an IORequest structure. See below
- for more information about this structure.
-
-
- ioa_AllocKey: When you reserve a sound channel this field is
- given an unique "key" value. Each time you pass
- the request to the channel this value will be
- compared with the channel's current key value.
- If these two values are not the same, the channel
- has been stolen by some other program with higher
- priority, and your command is returned with the
- AUDIO_NOALLOCATION flag set.
-
- If you are going to use another request block,
- and not the one you used to allocate the channel
- with, you have to copy this key value to the
- other request block.
-
- ioa_Data: Pointer to the waveform data. Note that the data
- must be in chip memory, and start on a word
- boundary.
-
- ioa_Length: If you are going to play a waveform this filed
- should be given the length (number of samples/
- values in the wave data).
-
- ioa_Period: This field should be given a period value. The
- period value is the number of clock ticks that
- should be used for each sample (value in the
- waveform). See above for more information about
- how to calculate the period value.
-
- ioa_Volume: The volume, which should be set to any value
- between 0 and 64. The maximum value is 64, and
- the minimum value is 0 (silent). Many programs
- are always using the maximum volume, but if you
- use different volume for different situations
- you will gain a lot of "depth".
-
- Imagine an adventure game where most of the sound
- effects are played rather softly. The user will
- then use a higher volume on his/her own stereo
- (or whatever the sound ports are connected to) to
- compensate for the low volume. No imagine the
- feeling when a monster appear and you at the same
- time suddenly increase the volume to maximum!
-
- Sadly very few programs are using this very
- effective technique.
-
- ioa_Cycles: Before you play a waveform you should set this
- field to the number of times the waveform should
- be played. If you set this field to 0 the
- waveform will continuously be played until you
- aborts the command.
-
- ioa_WriteMsg: If you have set the flag "ADIO_WRITEMESSAGE" in
- the "io_Flag" field, and this request will start
- a sound, this message will be sent when the sound
- starts to be played. Remember to give the
- "mn_ReplyPort" field of the Message structure a
- pointer to a message port to which the message
- should be sent.
-
-
- The IORequest structure (which is a part of the IOAudio
- structure) is defined in the "exec/io.h" header file like this:
-
- struct IORequest
- {
- struct Message io_Message;
- struct Device *io_Device;
- struct Unit *io_Unit;
- UWORD io_Command;
- UBYTE io_Flags;
- BYTE io_Error;
- };
-
- io_Message: The top part consists of a Message structure which
- will be sent to us when the request has been
- completed (successfully or not).
-
- The Message structure is defined in the header file
- "exec/ports.h" like this:
-
- struct Message
- {
- struct Node mn_Node;
- struct MsgPort *mn_ReplyPort;
- UWORD mn_Length;
- };
-
- mn_Node: It is only one field of the Node structure
- that should be used, and it is the "ln_Pri"
- filed, which should be given this sound's
- priority value.
-
- mn_ReplyPort: This field should be given a pointer
- to the message port the reply should
- be sent to.
-
- mn_Length: This value can be ignored.
-
- io_Device: This field will automatically be initialized when
- you use the request block together with an
- OpenDevice() function call. It is simply a pointer
- to the device which this request block is made for.
-
- io_Unit: This field will automatically be initialized when
- you reserve an audio channel for this request
- block.
-
- io_Command: All commands should be set here. The following
- commands are accepted by the audio device, and may
- be used: (Will be explained below.)
-
- The unique audio device commands:
-
- ADCMD_FREE Free a sound channel.
- ADCMD_SETPREC Set priority.
- ADCMD_FINISH Stop playing the sound.
- ADCMD_PERVOL Set period and volume.
- ADCMD_LOCK Lock a sound channel.
- ADCMD_WAITCYCLE Wait for the cycle to finish.
- ADCMD_ALLOCATE Reserve a sound channel.
-
- The normal commands which are accepted by most of
- the devices:
-
- CMD_WRITE Play the waveform.
- CMD_READ Get a pointer to current req.
- CMD_STOP Stop and do not start any sound.
- CMD_START Start to play sound again.
- CMD_FLUSH Remove all queued requests.
- CMD_RESET Reset the audio device.
-
- io_Flags: The following flags may be used:
-
- IOF_QUICK If this flag is set no messages
- will be sent to the reply port.
- This will speed up the
- execution, and can be useful if
- you have to use a lot of
- request blocks.
-
- ADIOF_PERVOL When you start to play a sound
- you can either use the volume
- and period values which were
- used last time, or you may use
- new values. If you want to use
- new volume and period values
- you should set this flag, and
- also set the new values in
- this request block. Otherwise
- the old values will be used.
-
- ADIOF_SYNCCYCLE Set this flag if you want that
- the command should be
- synchronized with the wave form.
- If this flag is set, any
- modifications will first occur
- when the whole waveform (cycle)
- have been played.
-
- ADIOF_NOWAIT When you try to reserve a sound
- channel (with ADCMD_ALLOCATE)
- and the audio device can not
- find any channel for you, it
- will continue to try until it
- succeeds. The request will
- firs be returned when the
- request has successfully been
- completed.
-
- If you do not want to wait for
- the request to be completed
- you can set this flag. If the
- audio device can not find any
- sound channel it will
- immediately return the request
- with the "ADIOERR_ALLOCFAILED"
- error flag set.
-
- ADIOF_WRITEMESSAGE Normally you will only receive
- a message at your reply port
- when a request has been
- completed. It may however
- sometimes be necessary to know
- when an audio request have
- started.
-
- If you set this flag the
- message at the end of the
- request ("ioa_WriteMsg") will
- be sent. Remember to give the
- message structure a pointer to
- a reply port if you are using
- this flag.
-
- io_Error: If the request can not successfully be executed by
- the audio device, it will be returned with one of
- the following error flags:
-
- ADIOERR_NOALLOCATION When you reserve a sound
- channel you are given a
- "key" value (set in the
- "ioa_AllocKey" field).
- Every time you send a
- request this key value will
- be compared to the sound
- channel's current key
- value. If these two values
- do not match the channel
- have been "stolen" by some
- other program. The request
- will then be returned with
- this error flag set.
-
- Note that you do not have
- to free a sound channel
- that has been stolen from
- you.
-
-
- ADIOERR_ALLOCFAILED When you try to reserve a
- sound channel it may happen
- that all desired channels
- are occupied. If the audio
- device can not find a
- channel it will normally
- continue to try until it
- found one. However, if you
- have set the "ADIOF_NOWAIT"
- flag the request will
- immediately be returned if
- no sound channels could be
- allocated, and this error
- flag is then set.
-
- Remember to always check if
- you have received the
- channel or not if you are
- using the "ADIOF_NOWAIT"
- flag.
-
- ADIOERR_CHANNELSTOLEN If you have "locked" a
- channel, and another
- program tries to steal
- it, the lock request will
- be returned to you with
- this flag set.
-
- If the lock request is
- returned to you with this
- flag set you should as
- fast as possible clean up
- after you and unlock the
- channel. It is first when
- you free the locked channel
- the other program gets it.
-
- NOTE! You must free the
- channel as soon as
- possible! The other program
- has higher priority, but
- since you have locked the
- channel it can not get it.
-
-
-
- 4.2.6 OPEN THE AUDIO DEVICE
-
- The audio device is very similar to other devices. You open
- a message port through which the audio device can reply to
- you. You then allocate the request block(s), and finally open
- the device.
-
- 1. Open a message port. Since it is only our task and the
- audio device that will use the message port, we do not
- need to make it "public", therefore no name. Priority
- should as usual be set to 0, normal priority. (Do not
- mix up this priority for the message port with the
- priority for the sound channel, which will later be set
- in the request block.)
-
- struct MsgPort *replymp;
-
- replymp = (struct MsgPort *)
- CreatePort( NULL, 0 );
-
- if( !replymp )
- clean_up( "Could not create the reply port!" );
-
-
- 2. Allocate a request block of type IOAudio structure.
- The IOAudio structure is an extended version of the
- normal request block, and should therefore be allocated
- with help of the CreateExtIO() function with the size
- set to sizeof( struct IOAudio ).
-
- struct IOAudio *audio_req;
-
- audio_req = (struct IOAudio *)
- CreateExtIO( replymp, sizeof( struct IOAudio ) );
-
- if( !audio_req )
- clean_up( "Not enough memory!" );
-
-
- 3. Once the message port and the request block have
- successfully been created you can "open" (gain access to)
- the audio device.
-
- error = OpenDevice( AUDIONAME, 0, audio_req, 0 );
-
- if( error )
- clean_up( "Could not open the Audio Device!" );
-
-
- If you want you can reserve sound channels at the same
- time as you open the device. You must then:
-
- (a) Set the sound priority. The top part of the request
- block, the IORequest structure (ioa_Request) contains
- a Message structure ("io_Message") which contains a
- Node structure (mn_Node) which finally contains a
- "ln_Pri" field. (Piece of cake.) It is in this field
- you set the sound priority.
-
- (b) Give the "ioa_Data" field a pointer to an array were
- the desired channel combinations are listed. This
- array of desired channels are usually called "the
- allocation array".
-
- (c) Set the length of the allocation array in the
- "ioa_Length" field.
-
- If the "ioa_Length" is not zero when you try to open the
- device, the audio device will also try to reserve the
- desired channel(s). If the audio device can not find any
- sound channels free, it will immediately (regardless if
- you have set the "ADIOF_NOWAIT" flag or not) return with
- the "ADIOERR_ALLOCFAILED" error message.
-
-
-
- 4.2.7 RESERVE CHANNELS
-
- If you have not reserved any channel(s) when you opened the
- audio device, or you want to reserve even more channels, you
- should use the "ADCMD_ALLOCATE" command. The follwing things
- must be done:
-
- 1. Set the "ADCMD_ALLOCATE" flag in the "io_Command" field
- of the IORequest structure.
-
- audio_req->ioa_Request.io_Command = ADCMD_ALLOCATE;
-
- 2. Set the sound priority. The top part of the request block,
- the IORequest structure (ioa_Request) contains a Message
- structure ("io_Message") which contains a Node structure
- (mn_Node) which finally contains a "ln_Pri" field. It is
- in this field you set the sound priority. Be careful so
- you set the correct sound priority.
-
- audio_req->ioa_Request.io_Message.mn_Node.ln_Pri =
- SOUND_EFFECT;
-
- 3. Normally will the request first be returned when the
- audio device has successfully reserved the sound
- channel(s). If you set the "ADIOF_NOWAIT" flag in the
- "io_Flags" field of the IORequest structure, the request
- will immediately be returned, successfully or not. You can
- examine the "io_Error" field of the IORequest structure to
- see if the request was successful or not. If no channels
- could be reserved (allocated) the "ADIOERR_ALLOCFAILED"
- error flag is set.
-
- audio_req->ioa_Request.io_Flags = ADIOF_NOWAIT;
-
- 4. Give the "ioa_Data" field a pointer to an array were
- the desired channel combinations are listed.
-
- audio_req->ioa_Data = allocation_array;
-
- 5. Set the length of the allocation array in the
- "ioa_Length" field.
-
- audio_req->ioa_Length = sizeof( allocation_array );
-
- 6. The audio request block has now been properly initialized,
- and it should now be sent to the audio device. Normally
- you use the DoIO() and SendIO() commands, but they may not
- be used when you are allocating channels. Instead you must
- use the low level function BeginIO(), which is very
- similar to SendIO().
-
- The reason why you may not use the DoIO() and SendIO() is
- that they will erase some parts of the request block that
- may not be altered when you are using the audio device.
-
- Since BeginIO() is an asynchronous command, it will
- immediately return the control to the program, you have to
- wait for the request to be executed. Simply use the
- WaitIO() function which will put our program to sleep
- while we are waiting.
-
- WaitIO() will return 0 when the request has successfully
- been executed, else a non zero value is returned.
-
- BeginIO( audio_req );
-
- error = WaitIO( audio_req );
-
- if( error )
- clean_up( "No channels!" );
-
-
- After you have successfully allocated a channel you can
- check which channel(s) you received by looking at the
- "io_Unit" field of the IORequest structure. (Note that
- normally the "io_Unit" field of a IORequest structure
- contains a pointer to a Unit structure. However, when
- you are using the audio device this field is used to store
- an integer value, where the first four bits are used to
- identify which channels are reserved. As before, bit zero
- represents channel 0 (left), bit one channel 1 (right),
- bit two channel 2 (right) and finally bit three represents
- channel 3 (left).
-
- channel = (UBYTE) audio_req->ioa_Request.io_Unit;
-
- if( channel & 1 )
- printf( "First left channel!\n" );
-
- if( channel & 2 )
- printf( "First right channel!\n" );
-
- if( channel & 4 )
- printf( "Second right channel!\n" );
-
- if( channel & 8 )
- printf( "Second left channel!\n" );
-
-
-
- 4.2.8 LOCK CHANNELS
-
- After you have reserved a channel you may start to use it. When
- you reserved the sound channel was the "ioa_AllocKey" given an
- unique key number. This key number will be compared with the
- sound channels current key number, and if they do not match,
- the channel(s) has/have been stolen and your request is
- returned with the error flag "ADIOERR_NOALLOCATION" set.
-
- If you want to use several request blocks for the same
- channel(s) you must therefore copy the key value to all request
- blocks.
-
- When you are using the request block to send commands the key
- value will always be checked before any change is made. It can
- however sometimes be necessary to skip the request block and
- directly modify the hardware registers. Using request blocks
- take some time, and if you have to make changes several times
- each second it may not be fast enough. You must then directly
- modify the sound registers.
-
- Normally you are not allowed to hit the hardware like this, but
- when you are using the audio device you are allowed to do it.
- Of course, if you can manage with the request blocks you should
- use them.
-
- If you are using the hardware registers you may get into
- trouble if you are not careful. If some other program has
- stolen the channel(s) from you and you use the hardware
- registers, you will not receive any error messages. You can now
- end up with modifying the other programs sounds, and this is
- not to be recommended!
-
- Before you may use the hardware registers you must therefore
- "lock" the channel(s). To lock a channel you have to use a
- separate request block. This request block is allocated and
- pre-initialized as usual with help of the CreateExtIO()
- function: (We can of course use a different message port if
- you want to separate all lock messages, but it is usually
- easier to use only one message port for all audio messages.)
-
- struct IOAudio *audio_lock;
-
- audio_lock = (struct IOAudio *)
- CreateExtIO( replymp, sizeof( struct IOAudio ) );
-
- if( !audio_lock )
- clean_up( "Not enough memory!" );
-
-
- The lock should then have the following fields set:
-
- 1. The "ADCMD_LOCK" flag in the io_Command field of the
- IORequest structure must be set.
-
- audio_lock->ioa_Request.io_Command = ADCMD_LOCK;
-
- 2. The lock must be linked to the already opened audio
- device. The address to the device can be found in the
- "io_Device" field of the IORequest structure.
-
- audio_lock->ioa_Request.io_Device =
- audio_req->ioa_Request.io_Device;
-
- 3. You must tell the device which channels you want to
- lock. Normally you will try to lock the channels you
- previously have reserved, and thus you can find the
- channel value in the "io_Unit" field of the IORequest
- block.
-
- audio_lock->ioa_Request.io_Unit =
- audio_req->ioa_Request.io_Unit;
-
- 4. Since you are going to use a new request block for some
- channels you have previously reserved with another request
- block, you must copy the key value:
-
- audio_lock->ioa_AllocKey = audio_req->ioa_AllocKey;
-
-
- Once the lock request has been initialized it should be send
- by calling the BeginIO() function.
-
- BeginIO( audio_lock );
-
-
- Once you have lock the channel(s) the request block will first
- be sent back when some other programs tries to steal the sound
- channel(s) or when you free the channel(s). If some other
- program wants the channel(s) and has higher priority, the lock
- will be returned with the error flag "ADIOERR_CHANNELSTOLEN"
- set. The channel has not yet been stolen, but you MUST
- immediately clean up all necessary things and free the
- channel(s) as soon as possible so the other program can get
- them. You may NOT keep the channels once the lock has been
- returned!
-
- While you are cleaning up and freeing the channels the other
- program is put to sleep and does not know what is happening.
- If you are not fast enough the other program will continue to
- "sleep" although it has higher priority and should therefore
- immediately get the channels if you have not locked them. You
- must therefore free the channels as fast as possible.
-
- You may of course not modify the hardware registers any more
- after you have deallocated the audio channel(s).
-
- You can use the function CheckIO() to see if the lock has been
- sent back. See below for more information about how to free a
- sound channel.
-
- /* Has the lock been sent back? */
- if( CheckIO( audio_lock ) )
- {
- /* Another programs what our channel(s)! */
-
- /* Clean up. Reset any hardware */
- /* registers etc, if necessary. */
-
- /* Free the channel: */
- audio_req->ioa_Request.io_Command = ADCMD_FREE;
-
- /* Send the request: (We may use the DoIO() function */
- /* when we deallocate a channel.) */
- DoIO( audio_req );
- }
-
-
-
- 4.3 USE THE AUDIO DEVICE
-
- Once you have declared a request block and successfully opened
- the audio device you may start to use it. When you are playing
- sounds you may either use the request block to issue the sound
- commands, or you can directly modify the hardware registers.
- The first method is cleaner, but the second method is much
- faster and may sometimes be necessary if you intend to issue
- many commands.
-
-
- 4.3.1 PLAY SOUNDS
-
- To play some sound you have to do the following things:
-
- 1. Give the "ioa_Data" field of the request block a pointer to
- a waveform you want to play. (The waveform data must be
- located in chip memory, and start on an even byte address.)
-
- audio_req->ioa_Data = sine_wave;
-
- 2. Set the length of the waveform in the "ioa_Length" field.
- (Must be an even number of bytes.)
-
- audio_req->ioa_Length = SINE_DATA_LENGTH;
-
- 3. Set the number of times (cycles) the waveform should be
- played in the "ioa_Cycles" field. If you want to play the
- waveform continuously until you abort the request you
- should set the field to 0.
-
- audio_req->ioa_Cycles = 3;
-
- 4. Set the command "CMD_WRITE" flag in the "io_Command" field
- which tells the device that you want to play a tune.
-
- audio_req->ioa_Request.io_Command = CMD_WRITE;
-
- 5. If you want to use a specific volume and period values you
- must set the "ADIOF_PERVOL" flag in the "io_Flags" field.
- The values in the "ioa_Volume" and "ioa_Period" fields
- will then be used, else the previous volume and period
- values will be used.
-
- audio_req->ioa_Request.io_Flags = ADIOF_PERVOL;
- audio_req->ioa_Volume = 32;
- audio_req->ioa_Period = 3546895 / 880 / SINE_DATA_LENGTH;
-
- Once all d3esired values have been set you send the request to
- the audio device, by calling the BeginIO() function. If you
- want to wait for the sound to be completed you can use the
- WaitIO() function. Note that you should NEVER try to wait for
- a request that has not been started, or if the "ioa_Cycles"
- field has been set to 0 (forever). If the request already has
- been completed the WaitIO() function will immediately return.
-
- BeginIO( audio_req );
-
- error = WaitIO( audio_req );
-
- if( error )
- clean_up( "Error while playing the sound!" );
-
-
-
- 4.3.2 USE SEVERAL REQUEST BLOCKS
-
- Remember that you may not modify the request block once it has
- been sent, by calling BeginIO(), and have not yet been
- completed. Once the request has been completed you of course
- start to modify it again. If you want to play several notes at
- the same times in different sound channels, you may therefore
- have to use several request blocks.
-
- If you want to use some of the audio commands that is changing
- the sound which is currently being played you also have to a
- use separate request blocks. In many programs you will see
- four request blocks where each one handles one audio channel.
- Very often there also exist one or more extra request blocks
- that are used to modify sounds which are currently being
- played.
-
- If you have reserved a sound channel with a request block you
- can not use another request block to play sounds on it if you
- do not first copy some important values to the new request
- block.
-
- When you reserve a channel the request block is given a unique
- key value, and if this key value does not match with the sound
- channel's current key value the request will fail. Before you
- may use a new request block you must therefore first copy the
- "ioa_AllocKey" value to the new request.
-
- You must also copy the pointer to the audio device from the
- old request to the new one. The unit number and all stuff in
- the Message structure must also be copied. In the end, it is
- actually easier to copy the whole request block, even if some
- fields does not have to be copied.
-
- Here is a small demonstration on how to copy all values in a
- request block to a new one. The "audio_req" is a pointer to an
- already initialized audio request block, where "change_req" is
- a new and uninitialized request block.
-
- /* Source and destination pointers: */
- BYTE *first_ptr;
- BYTE *second_ptr;
-
-
- /* Allocate request blocks, open audio device, */
- /* reserve channels, etc... */
-
-
- /* Copy the first request block to the */
- /* second one: (byte by byte) */
-
- /* Get the start addresses of both request blocks: */
- first_ptr = (BYTE *) audio_req;
- second_ptr = (BYTE *) change_req;
-
- /* Copy byte by byte: */
- for( loop = 0; loop < sizeof( struct IOAudio ); loop++ )
- {
- /* Copy: */
- *second_ptr = *first_ptr;
-
- /* Next byte: */
- first_ptr++;
- second_ptr++;
- }
-
-
-
- 4.3.2 PLAY DOUBLE BUFFERED SOUNDS
-
- If you play several sounds after each other you will hear a
- "click" between the sounds. The reason why you hear this click
- is that once a sound has been completed it takes some time for
- your program to initialize a new request and send it to the
- audio device. Although this delay is very short it is long
- enough to be noticed, especially if several programs are
- running at the same time.
-
- Luckily there is a solution for this problem. Before the first
- sound has been complete you should send the next sound request.
- When the first sound has stopped the second sound is already
- available for the audio device, and it will immediately be
- played. Because of this, there is such a short interrupt
- between the sounds that you will not hear any annoying clicks.
-
- This technique of sending the next sound when the first one
- is still being played is usually called "double buffering",
- and can in many ways be compared with "double buffered
- displays" which is described in the manual "Graphics",
- chapter "Graphical Tricks".
-
- 1. Prepare the first request block and send it to the audio
- device.
-
- 2. Prepare the second request block, and send it to the audio
- device.
-
- 3. Wait for the first request block to be completed. When the
- request block is returned you prepare it again for a new
- sound, and send it to the audio device.
-
- 4. Wait for the second request block to be completed. When
- the request block is returned you prepare it again for a
- new sound, and send it to the audio device.
-
- 5. Jump back to step 3 until all sounds have been played.
-
- 6. Wait for both requests to be completed, and quit.
-
-
-
- 4.3.3 MODIFY THE HARDWARE REGISTERS
-
- Usually you should never try to modify hardware registers
- directly but instead use special functions that does it for
- you. However, when you are using the audio device you may alter
- the hardware registers of the sound channels. Modifying
- hardware registers directly is much faster than using functions
- that does it for you, and when playing sounds speed can
- sometimes be crucial.
-
- Before you may alter any of the sound registers you must first
- lock the channel(s)! When you modify hardware registers no one
- is no checking that you really own the channel(s). If you have
- reserved a channel and not locked it, some other program may
- steal it and you would not notice that. You could then end up
- altering the other programs sound channel(s), which is rather
- unpolite not to say dangerous.
-
- To use the hardware registers you have to declare an external
- array of "AudChannel" structures. The AudChannel structure is
- defined in the Custom structure which is declared in the header
- file "hardware/custom.h". The whole Custom structure is
- automatically initialized by the Amiga. The AudChannel array,
- which must be called "aud" is also initialized.
-
- The AudChannel structure is defined like this: (Declared in
- header file "hardware/custom.h")
-
- struct AudChannel
- {
- UWORD *ac_ptr;
- UWORD ac_len;
- UWORD ac_per;
- UWORD ac_vol;
- UWORD ac_dat;
- UWORD ac_pad[2];
- } aud[4];
-
- ac_ptr: Pointer to the waveform data.
-
- ac_len: The length of the waveform.
-
- ac_per: The period value.
-
- ac_vol: The volume.
-
- ac_dat: This field contains the first two bytes which will
- be played. The DMA channels will automatically
- move data from the waveform to this field, two
- bytes each time, when needed. You can alter these
- values directly, but this will take longer time
- than letting the DMA fetch the sound
- automatically, so it is not recommended.
-
- ac_pad[2]: Unused areas, should not be used.
-
-
- When you declare the "aud" array you probably have to use the
- keyword "far" since the hardware data is most certainly far
- away from your program. Remember that the aud array has been
- declared and initialized by the Amiga itself, and you should
- therefore declare it as an external array. Example:
-
- extern struct AudChannel far aud[];
-
-
- To use the hardware audio channels you must also declare an
- external UWORD which must be called "dmacon". This variable
- is also automatically initialized by the Amiga. Example:
-
- extern UWORD far dmacon;
-
-
- This "dmacon" variable controls the DMA channels. A DMA channel
- can be described as a data cable where data can be sent without
- disturbing the main processor. It is because of the DMA
- (DMA = "Direct Memory Access") you can play sounds at the same
- time as your program can use the main processor to do something
- else.
-
- It is this variable which controls all "DMA writing", and must
- therefore be used when you want to play a sound. To play a
- sound on one of the audio channels you have to give it a value
- where the leftmost bit is set (the DMAF_SETCLR constant) and
- the bit which correspond to the audio channel you want to start
- playing. Bit one represents the first audio channel, bit two
- the second audio channel and so on. You should however not use
- the direct values but instead the constants which are declared
- in the "hardware/dmabits.h" header file.
-
- DMAF_SETCLR This flag should always be set together with the
- channel(s) you want to be played.
-
- DMAF_AUD0 First left audio channel.
-
- DMAF_AUD1 First right audio channel.
-
- DMAF_AUD2 Second right audio channel.
-
- DMAF_AUD3 Second left audio channel.
-
- If you want to use several audio channels you simple set all
- desired channel flags separated with the binary OR operator "|".
- To start all audio channels do like this: (You must of course
- first have reserved and locked them before you may do this!)
-
- dmacon = DMAF_SETCLR|DMAF_AUD0|DMAF_AUD1|DMAF_AUD2|DMAF_AUD3;
-
-
- Since you can now directly alter the volume, period, and
- waveform by directly writing to the hardware the changes will
- be very simple and quick. Just remember to now and then check
- if your lock has been returned, and if so clean up after you
- and free the channel(s) as soon as possible. Do not be greedy
- and ignore the lock, too many programs do that which is very
- irritating!
-
-
-
- 4.4 CLEAN UP AFTERWARDS
-
- When your program terminates you have to clean up after
- yourself, as usual. It is important that you do not forget to
- do this since other programs are denied any access to the
- audio channels and memory you forget to deallocate. Remember
- that your programs must also be able to clean up after itself
- if it suddenly has to terminate because of some error.
-
-
-
- 4.4.1 UNLOCK CHANNELS
-
- All audio channels that you have reserved must be given back to
- the system before your program may terminate. If you have
- locked a channel and another programs wants it, you must return
- it as soon as possible. However, if you have not locked the
- channel and another program has stolen it you do not have to
- free it.
-
- To free one or more audio channels you simply send a request
- with the "ADCMD_FREE" command flag set. All channels which are
- selected in the "io_Unit" field of the request block will be
- resetted and given back to the system. Any locks on the
- channels will be removed.
-
- If some other program has stolen the channel(s) from you the
- request will be returned with the error message
- "ADIOERR_NOALLOCATION". You do not have to do anything with
- channel(s) which has/have been stolen from you. It is the other
- program's duty to clean it/them up.
-
- When you want to free a channel you may use the DoIO()
- function, instead of BeginIO(). Normally you should only use
- the BeginIO() function when working with the audio device, but
- when you free channel(s) the DoIO() will work equally well.
-
- audio_req->ioa_Request.io_Command = ADCMD_FREE;
-
- DoIO( audio_req );
-
-
-
- 4.4.2 REMOVE ALL MESSAGES AND CLOSE THE REPLY PORT
-
- You must close all message ports you have opened. Just remember
- to remove all messages before you close it. A short while loop
- that removes the messages until it can not find any more is
- usually enough.
-
- while( GetMsg( replymp ) )
- printf( "Collected a message at the reply port.\n" );
-
- DeletePort( replymp);
-
-
-
- 4.4.3 CLOSE THE DEVICE
-
- The audio device itself must of course also be closed if you
- have opened it. If you have open it several times with
- different request blocks you have to close all of them.
-
- CloseDevice( audio_req );
-
-
- 4.4.4 DEALLOCATE THE REQUEST BLOCKS
-
- All audio request blocks you have created must be deallocated
- with help of the DeleteExtIO() function. The size should be set
- to the size of an IOAudio structure. Just remember to close the
- device before you deallocate the request block.
-
- DeleteExtIO( audio_req, sizeof( struct IOAudio ) );
-
-
-
- 4.4.5 DEALLOCATE SOUND BUFFERS
-
- If you have allocated a sound buffer to store the waveform in
- you must also deallocate it. Remember to deallocate the same
- amount of memory as when you allocated it.
-
- FreeMem( square_wave, SINE_DATA_LENGTH );
-
-
-
- 4.5 AUDIO DEVICE COMMANDS
-
- The audio device is controlled by set of commands. We have
- looked at the ADCMD_ALLOCATE, ADCMD_FREE, and CMD_WRITE
- commands. Most things can be managed with these commands, but
- there exist a set of other commands that may sometimes be
- needed. These new commands can be divided into two groups. The
- first group consists of the general device commands which can
- be used on most devices. The second group consists of a set
- special commands that only the audio device can understand
- and handle.
-
- 4.5.1 GENERAL DEVICE COMMANDS
-
- Here is the complete list of general device commands, together
- with a short description:
-
- CMD_WRITE Start to play a sound on one channel.
- CMD_READ Check which request is currently playing.
- CMD_STOP Temporarily stop all audio requests.
- CMD_START Restart the previously stopped audio requests.
- CMD_FLUSH Remove all queued requests.
- CMD_RESET Reset the audio device.
-
-
- 4.5.1.1 CMD_WRITE
-
- The CMD_WRITE command is used when you want to start to play
- a sound on a specific sound channel. Note that it can only
- start one sound channel.
-
- This command has already been explained, so see previous
- sections for more information.
-
-
-
- 4.5.1.2 CMD_READ
-
- The CMD_READ command is used when you want to get the address
- of the request which is currently being used. You specify a
- sound channel you want to check by setting the corresponding
- bit in the io_Unit field. If a sound is currently being played
- on that channel the "io_Data" field of our request block will
- contain the address of the other request which is currently
- playing the sound. If no sound is being played the "io_Data"
- field is set to NULL.
-
- This command is useful if you have sent a lot of requests to
- the audio device and you want to see which request is currently
- being played. Note that you must copy the sound channels key
- value to your request before you can check it. The key value
- can be found in the "ioa_AllocKey" field of the request which
- reserved the channel. If your request block's "ioa_AllocKey"
- value does not match the sound channel's current key value the
- ADIOERR_NOALLOCATION error message is returned.
-
-
-
- 4.5.1.3 CMD_STOP
-
- The CMD_STOP command will temporarily stop all requests for the
- specified channel(s). All requests will be queued, and first
- processed when you later restart the channel(s) by using the
- CMD_START command. The "io_Unit" field of the request specifies
- which channels that should be stopped. Remember to copy the
- key value from the request block that reserved the sound
- channel(s). If your request block's "ioa_AllocKey" value does
- not match the sound channel's current key value the
- ADIOERR_NOALLOCATION error message is returned.
-
-
-
- 4.5.1.4 CMD_START
-
- The CMD_START command should be used when you want to restart
- one or more previously stopped sound channel(s). The "io_Unit"
- field of the request specifies which channels that should be
- restarted. Remember to copy the key value from the request
- block that reserved the sound channel(s). If your request
- block's "ioa_AllocKey" value does not match the sound channel's
- current key value the ADIOERR_NOALLOCATION error message is
- returned.
-
-
-
- 4.5.1.5 CMD_FLUSH
-
- The CMD_FLUSH command will remove all currently queued commands
- from the specified channels. Remember to copy the key value
- from the request block that reserved the sound channel(s). If
- your request block's "ioa_AllocKey" value does not match the
- sound channel's current key value the ADIOERR_NOALLOCATION
- error message is returned.
-
-
-
- 4.5.1.6 CMD_RESET
-
- The CMD_RESET command will reset all specified channels. All
- requests currently queued will be removed, the audio hardware
- registers are cleared, and if the channel has been stopped it
- is restarted. Remember to copy the key value from the request
- block that reserved the sound channel(s). If your request
- block's "ioa_AllocKey" value does not match the sound channel's
- current key value the ADIOERR_NOALLOCATION error message is
- returned.
-
-
-
- 4.5.2 SPECIAL AUDIO DEVICE COMMANDS
-
- Here is the complete list of special audio device commands.
- These commands can only be used with the audio device.
-
- ADCMD_ALLOCATE Tries to reserve one or more audio channels.
- ADCMD_FREE Frees one or more audio channels.
- ADCMD_SETPREC Change the priority of a sound.
- ADCMD_FINISH Finish (aborts) a sound.
- ADCMD_PERVOL Change the volume and period of a sound.
- ADCMD_LOCK Will lock audio one or more channel(s).
- ADCMD_WAITCYCLE Waits for a cycle to be completed.
-
-
-
- 4.5.1.1 ADCMD_ALLOCATE
-
- The ADCMD_ALLOCATE command will reserve one or more audio
- channels. This command has already been discussed, so see
- previous sections for more information about this command.
-
-
-
- 4.5.1.2 ADCMD_FREE
-
- The ADCMD_FREE command will free one or more audio channels
- that has(have) previously been stolen. This command has already
- been discussed, so see previous sections for more information
- about this command.
-
-
-
- 4.5.1.3 ADCMD_SETPREC
-
- The ADCMD_SETPREC command allows you to change the priority
- of a sound channel that has previously been reserved. You may
- for example want to use a very high priority at the beginning
- of the sound, and later decrease it. Remember to copy the key
- value from the request block that reserved the sound channel(s).
- If your request block's "ioa_AllocKey" value does not match the
- sound channel's current key value the ADIOERR_NOALLOCATION
- error message is returned.
-
-
-
- 4.5.1.4 ADCMD_FINISH
-
- The ADCMD_FINISH command will finish an already started sound.
- This may be needed if you have set a very high cycle value, or
- you are playing a sound "for ever" (the ioa_Cycle field set to
- zero.) This command has the same effect as calling the
- AbortIO() function. Both options are equally good, but the
- ADCMD_COMMAND looks a bit "cleaner" (at least some C
- programmers think so).
-
- Remember to copy the key value from the request block that
- reserved the sound channel(s). If your request block's
- "ioa_AllocKey" value does not match the sound channel's current
- key value the ADIOERR_NOALLOCATION error message is returned.
-
-
-
- 4.5.1.5 ADCMD_PERVOL
-
- The ADCMD_PERVOL command can be useful if you want to change
- the period and/or volume value(s) of a sound which is already
- playing. Just remember to set the ADIOF_PERVOL flag in the
- "io_Flags" field, or else your command will have no effect.
- Remember to also set the new volume and period values you want
- to be used in the "ioa_Volume" and "ioa_Period" fields.
-
- As always, remember to copy the key value from the request
- block that reserved the sound channel(s). If your request
- block's "ioa_AllocKey" value does not match the sound channel's
- current key value the ADIOERR_NOALLOCATION error message is
- returned.
-
-
-
- 4.5.1.6 ADCMD_LOCK
-
- The ADCMD_LOCK command is used to lock one or more channels.
- If some other program has higher priority and tries to steal
- our channel(s), the lock is returned to us. The channels are
- however still not stolen. When the lock is returned we should
- as fast as possible clean up and free the channels so the other
- program can get it.
-
- See the previous sections for more information about this
- command.
-
-
-
- 4.5.1.7 ADCMD_WAITCYCLE
-
- If you send a request with the "ADCMD_WAITCYCLE" command set,
- the request will first be sent back when the sound which is
- currently being played has reached the end of the cycle. If
- no sound is being played it will immediately return.
-
- Remember to copy the key value from the request block that
- reserved the sound channel(s). If your request block's
- "ioa_AllocKey" value does not match the sound channel's current
- key value the ADIOERR_NOALLOCATION error message is returned.
-
- 4.6 FUNCTIONS
-
-
- BeginIO()
-
- BeginIO() is a low level form of the SendIO() function. The
- advantage with BeginIO() is that no fields of the request
- block will be altered as which is the case with SendIO().
- When you are using the audio device you should almost always
- use the BeginIO() and not DoIO() or SendIO(). If you are
- freeing channels you may however use these functions too.
-
- Synopsis: BeginIO( req )
-
- req: (struct IORequest *) Pointer to the request you
- want to have executed. In this case a pointer to
- an IOAudio structure.
-
-
- DoIO()
-
- DoIO() is used to send requests to a device, and waits for it
- to be completed. While the program is waiting it is put to
- sleep so it will not waste any computer time. DoIO() will
- return first when the request have been completed or failed,
- and no message is therefore sent to the reply port. When you
- are using the audio device you may only use this function
- when you free audio channel(s).
-
- Synopsis: error = DoIO( req );
-
- error: (long) DoIO() will return first when the request has
- been completed or something has failed. If the
- request was successfully completed zero is returned,
- else an error number is returned. What error number
- depends on which device was used.
-
- req: (struct IORequest *) Pointer to the request you
- want to have executed. In this case a pointer to
- an IOAudio structure.
-
-
-
- SendIO()
-
- SendIO() is used to send requests to a device, but will
- return immediately without any delay. To check if the request
- have been completed use the CheckIO() function, or look
- at the request's reply port for any messages. Once the
- request has been completed you must remove the message at
- the reply port. (CheckIO() will not do it.) To remove a
- message use the function Remove(). Note that you may NOT
- close the device before all requests have been completed or
- aborted!
-
- When you are using the audio device you may only use this
- function when you free audio channel(s).
-
- Synopsis: SendIO( req )
-
- req: (struct IORequest *) Pointer to the request you
- want to have executed. In this case a pointer to
- an IOAudio structure.
-
-
-
- CheckIO()
-
- CheckIO() is used to check if a previously started request
- has been completed. Note that this function will not remove
- the message at the reply port. This must be done with the
- Remove() function.
-
- Synopsis: ptr = CheckIO( req );
-
- ptr: (long) CheckIO() will either return NULL if the
- request have not been completed or it will return a
- pointer to the request block.
-
- req: (struct IORequest *) Pointer to the request you
- want to check. In this case a pointer to
- an IOAudio structure.
-
-
-
-
- WaitIO()
-
- WaitIO() will wait for the request to be completed, and while
- the program is waiting it is put to sleep so no computer time
- is wasted.
-
- Synopsis: error = WaitIO( req );
-
- error: (long) WaitIO() will return first when the request,
- that has previously been sent, has been completed or
- something has failed. If the request was successfully
- completed zero is returned, else an error number is
- returned. What error number depends on which device
- was used.
-
- req: (struct IORequest *) Pointer to the request you
- want to wait for to be completed. In this case a
- pointer to an IOAudio structure. Note that the
- request must have already been sent to the device
- by either a SendIO() or BeginIO() function call.
-
-
- AbortIO()
-
- AbortIO() will try to abort a previously started request.
- Instead of using this function you can use the ADCMD_FINISH
- command as explained above.
-
- A request that is aborted will have its io_Error field set
- to IOERR_ABORTED (defined in header file "exec/errors.h").
-
- Synopsis: AbortIO( req )
-
- req: (struct IORequest *) Pointer to the request you
- want to abort. In this case a pointer to an IOAudio
- structure.
-
-
-
- CloseDevice()
-
- CloseDevice() will close a device. Note that you should NOT
- close the device before all started asynchronous requests
- have either been completed or aborted.
-
- Synopsis: CloseDevice( ioreq );
-
- ioreg: (struct IORequest *) Pointer to the device's
- request block. In this case a pointer to an IOAudio
- structure.
-
-
-
- OpenDevice()
-
- OpenDevice() will try to open the specified device.
-
- Synopsis: error = OpenDevice( name, unit, req, flags );
-
- error: (long) If OpenDevice() managed to open the device
- it returns 0, else an error number is returned.
- If you try to reserve one or more audio channels
- at the same time, and there are not any available,
- the error message "IOERR_ALLOCFAILED" is returned.
-
- name: (char *) Name of the device you want to open.
- The name of the audio device is defined as
- AUDIONAME in header file "audio.device".
-
- unit: (long) Not used by the audio device.
-
- req: (struct IORequest *) Pointer to a request block.
- In this case a pointer to an IOAudio structure.
-
- flags: (long) Not used by the Audio device.
-
-
-
- 4.7 EXAMPLES
-
- Example 1
- This program will play some notes (A to G#) with help of the
- Audio Device. It will use one of the audio channels (the
- first one which is free).
-
- Example 2
- This program is very similar to the previous example, but
- this time are we using double buffered sounds, and there will
- therefore not be any annoying "clicks" between the notes.
-
- The technique with double buffered sound is that while the
- first sound is played the second sound is already sent to
- the audio device. When the first sound terminates the second
- sound can immediately start without any delay. While the
- second sound is being played the first sound is prepared and
- sent and so on... Because there is never any delay between
- the sounds there will never be any annoying clicks.
-
- Example 3
- This program demonstrates how you can play some sampled data.
- Remember that sampled data is just a more complicated
- waveform.
-
- The sampled sound has been converted into numbers by
- "PrintSound", a utility included in the "Sound" manual.
-
- Example 4
- This program demonstrates how you can play a sound
- continuously. While the sound is being played we slowly alter
- the period and volume values.
-
- Example 5
- This example demonstrates how you can play sounds in STEREO.
- First we play a sound in the left channel, then we switch to
- the right, and then back again, and so on...
-
- In this example are we reserving the audio channels at the
- same time as we open the audio device. We use two audio
- requests, one for the left channel and the other one for the
- right channel.
-
- Example 6
- This program will play some notes (A to G#) with help of the
- Audio Device. It will use as many audio channels as possible,
- and we are modifying the hardware registers directly instead
- of using the special Audio Device commands.
-
- You are allowed to use the hardware registers directly if you
- make sure that no other task can steel them from you before
- you have cleared all necessary registers.
-
-