Amiga C Tutorial

Sound

The Amiga comes with its own sound chip called Paula and it is programmed via the audio.device. You can create sound on this by opening the device, setting the appropiate parameters and sending the raw sound data to the device to be played. If sound is in a particular format, such as 8SVX, MUS, WAV etc then the sound data must be extracted from the files and then sent to the audio.device. This can be a complex process, although datatypes can help a lot to reduce the amount of code required to produce sound. The sound data has to be stored in chip memory to be played and this can limit the size of the sound file to be played unless various buffering techniques are used.

Setting up

To use the audio.device the following things need to happen. A message port needs to be opened to enable so device can reply to our commands using the CreatePort() function, then some audioIO Ports need to be created using CreateExtIO() function and then the I/O block is set up with various parameters such as channels to use and how many, and finally OpenDevice() is called to open the audio.device. Most of the functions are from the Exec library.

To access the functions, include the following at the top of your code:
#include <exec/exec.h>
#include <proto/exec.c>
#include <clib/alib_protos.h>
#include <devices/audio.h>

  e.g.
 struct MsgPort *port;
 int error, k ;
 bool devopened; 
 struct IOAudio *aio[1]; 
   
if (!(port=CreatePort(0,0))) { /* Create message port */
     error = 1;
     goto bailout;
   }
   for(k=0; k<AIOCNT; k++) {
      if (!(aio[k]=(struct IOAudio *)CreateExtIO(port,sizeof(struct IOAudio)))) { /* Create AudioIO blocks */
          error = k + 2; 
          goto bailout;
      }
   }
   /* Set up request to Audio port */
   aio[0]->ioa_Request.io_Command = ADCMD_ALLOCATE;
   aio[0]->ioa_Request.io_Flags = ADIOF_NOWAIT;
   aio[0]->ioa_AllocKey = 0;
   aio[0]->ioa_Data = whichannel;
   aio[0]->ioa_Length = sizeof(whichannel);
/* Open the audio device and allocate a channel */
   if(!(OpenDevice("audio.device", 0L, (struct IORequest *) aio[0] ,0L)))
     devopened = TRUE;
   else { 
      error = 5; 
      goto bailout;
   }

For AmigaOS 4, replace CreatePort with IExec->CreateMsgPort(), OpenDevice with 'IExec->OpenDevice'. CreateExtIO is part of the old Alib functions, so, replace this with IExec->CreateIORequest().

Creating sound

To create a sound, data is passed to an IOAudio structure using parameters such as io_Command to CMD_WRITE to write to the audio port, io_Flags to ADIOF_PERVOL flags for period (speed) and volume of the sample, ioa_Data for the sample data, ioa_Length for length of the sample in bytes, ioa_Period for the sampling rate in clock/sample rate, ioa_Volume for volume setting, and ioa_Cycles for number of repeats (usually 1). Then the sample is played using the BeginIO() function, the sample could be of any length so we then must use WaitIO() to wait for the sample to finishing playing.

aio[0]->ioa_Request.io_Command =CMD_WRITE;
aio[0]->ioa_Request.io_Flags =ADIOF_PERVOL;
aio[0]->ioa_Data =oneshot;
aio[0]->ioa_Length =osize;
aio[0]->ioa_Period =period;
aio[0]->ioa_Volume =volume;
aio[0]->ioa_Cycles =1;
if (osize <= MAXSAMPLE) 
      BeginIO((struct IORequest *)(aout0=aio[0]));
 
if (aout0) 
      WaitIO((struct IORequest *)aout0); 

Closing Down

Once we have played our sounds then any structure or devices need to be closed. The audio.device can be closed using the CloseDevice() function, then any blocks freed using DeleteExtIO() function and the message port closed using DeletePort() function.
e.g.

   if (devopened)
   {
     CloseDevice((struct IORequest *) aio[0]);
     devopened = FALSE;
   }
   for (k=0; k<AIOCNT; k++)
   {
     if (aio[k]) DeleteExtIO((struct IORequest *)aio[k]), aio[k] = NULL;
   }
   if (port) DeletePort(port), port = NULL;

For more examples, see the Play8SVX.c sample code in the Examples/IFF drawer on the Amiga Developer CD in the NDK folders or on Aminet.
For AmigaOS 4, replace DeleteExitIO() with IExec->DeleteIORequest, replace DeletePort with IExec->DeleteMsgPort().

Retargetable Audio Devices

For other sound cards, a system called AHI was developed to support other sound cards other than using Paula. AHI uses the ahi.device and drive drivers to support different sound cards which is set in the AHI preferences. You can program it in a similar way as the old audio.device. More information is included with the AHI Developer files which you can download from the AmigaOS AHI wiki or Aminet. AHI is included with AmigaOS 4 SDK.

Include files
#include <proto/ahi.h>
#include <devices/ahi.h>

Setting up

struct MsgPort *AHImp;
struct AHIRequest *AHio;
BYTE AHIDevice; if (AHImp=CreateMsgPort()) {   if (AHIio=(struct AHIRequest *)CreateIORequest(AHImp,sizeof(struct AHIRequest))) {      AHIio->ahir_Version = 4;      AHIDevice = OpenDevice(AHINAME,0,(struct IORequest *)AHIio,NULL); }

This will create a new message port, the create some IORequest structures and finally open the AHI device for writing to.

Playing a Sound

   /* Play buffer */
   AHIio->ahir_Std.io_Message.mn_Node.ln_Pri = pri;
   AHIio->ahir_Std.io_Command = CMD_WRITE;
   AHIio->ahir_Std.io_Data = p1;
   AHIio->ahir_Std.io_Length = length;
   AHIio->ahir_Std.io_Offset = 0;
   AHIio->ahir_Frequency = FREQUENCY;
   AHIio->ahir_Type = TYPE;
   AHIio->ahir_Volume = 0x10000; // Full volume
   AHIio->ahir_Position = 0x8000; // Centered
   AHIio->ahir_Link = link;
   SendIO((struct IORequest *) AHIio);

The AHIio request structure is similar to audio device structures. p1 points to the actual raw sound data, length is the size of the data buffer, Frequency is the amount to reply at e.g. 8000 Hertz, Type is type of music data e.g. AHIST_M8S, then the Volume and Position of speakers. SendIO will start playing the sound and you may use WaitIO to wait until the buffer is played before starting on next block of data.

Closing down

Once you have finished with the AHI device, you need to close down the device.
e.g.

 if (!AHIDevice)
   CloseDevice((struct IORequest *)AHIio);
 DeleteIORequest((struct IORequest *)AHIio);
 DeleteIORequest((struct IORequest *)AHIio2);
 DeleteMsgPort(AHImp);

For AmigaOS 4, prefix any OS functions with 'IExec->'.

Next Page