Jump to content

Aros/Developer/AHIDrivers

From Wikibooks, open books for an open world
Navbar for the Aros wikibook
Aros User
Aros User Docs
Aros User FAQs
Aros User Applications
Aros User DOS Shell
Aros/User/AmigaLegacy
Aros Dev Docs
Aros Developer Docs
Porting Software from AmigaOS/SDL
For Zune Beginners
Zune .MUI Classes
For SDL Beginners
Aros Developer BuildSystem
Specific platforms
Aros x86 Complete System HCL
Aros x86 Audio/Video Support
Aros x86 Network Support
Aros Intel AMD x86 Installing
Aros Storage Support IDE SATA etc
Aros Poseidon USB Support
x86-64 Support
Motorola 68k Amiga Support
Linux and FreeBSD Support
Windows Mingw and MacOSX Support
Android Support
Arm Raspberry Pi Support
PPC Power Architecture
RISC-V support
misc
Aros Public License

Retargetable Audio Devices

[edit | edit source]

For other sound cards, a system called AHI was developed to support other sound cards other than using Paula (Amiga(TM)). AHI uses the ahi.device and extra drivers to support different sound cards which is set in the AHI preferences (in Prefs drawer). It can be programmed in a similar way as the old Amiga audio.device. Better information is included with the AHI Developer files which you can download from the AHI dev or AHI old website.

Additional site are Wikipedia page, Devices

The HDAudio AHI driver carried its own controller code, welded to PCI and to a single analog codec, so display-adapter audio functions (HDMI/DP) could not be supported without duplicating all of it.

hdaudio.hidd now provides a bus-agnostic HD Audio controller base class (CLID_Hidd_HDA): reset, CORB/RIRB verb transport, stream management, interrupts, and the DMA cache maintenance non-coherent platforms require. How a controller is attached is delegated to subclass overrides (HWInit/HWExit), keeping bus knowledge out of the hidd.

AHI drivers use it by subclassing, finding and owning their PCI functions themselves, and support any number of controllers. hdaudio.audio is reworked onto the hidd, keeping the analog codec support and claiming ordinary HDA functions. The new nvhdmi.audio driver (mode ID 45) builds on the same class to drive NVidia display-audio functions and their HDMI/DP codec path - digital converters, pin presence/ELD selection and audio infoframes - which the analog driver could never reach.

Units 0 - 3 can be shared by as many programs as you define channels for them. Music Unit blocks the hardware which it is set to exclusively, so that no other program can play sound at the same time through this hardware. That's what <ahi-device>.audio was invented, it is a virtual hardware for which sends its sound data to the unit you set it up. This way, although music unit pull-down option blocks <ahi-device>.audio exclusively, other programs can still send sound to unit 0 - 3. Normally all programs use unit 0. Only very few programs use the music unit.

AHI drivers basically use AHI as master and the sound hardware as slave. This only makes sense for old audio hardware which is not DMA capable. But then you need to compensate for the mismatch between host clock and codec local oscillator. A 20ppm mismatch doesn't sound much but results in 20ppm*48kHz=0.96 samples/second. Could be that AHI takes care of this issue. Nevertheless you need to keep an eye on the sound hardware FIFO/buffer for under-/overruns.

It works on two models, as a device driver (High-level) or library (Low-level). Although this confused me at first, having programmed the device model it is because this is simple just allowing you to send sound streams and the library model deals with samples for use with trackers which are preloaded.

WildMidi for midi conversion


Library Approach

[edit | edit source]

The library (Low-level) approach use AHI functions like AHI_SetSound(), AHI_SetVol() and so on. In reality, this way has one big problem: if you work with this method, you will not have any mixing functions - your program will lock ahi.device and while your program is running, all other ahi programs will not work. The only advantage of low-level coding as mentioned in the documentation is "low overhead and much more advanced control over the playing sounds".

Here you get exclusive access to the audio hardware, and can do almost whatever you want, including monitoring.

Disadvantage is obviously, that the audio hardware is blocked for all other programs. Most of the drivers don't care about this situation, and if a program tries to access the hardware it mostly trashes everything and you need to restart your program, or re-allocate the audio hardware.

From AHI6 on, there is a nonblocking AHI mode called "device mode", but it doesn't allow recording. Playback only and has bad timing. Good enough for the Playback of something, but too bad for real time response.

To use two samples, you need to use the library model, you would open the ahi.device and extract the library base from the AHI device structure.

Device Approach

[edit | edit source]

In this way you just use ahi.device as standard amiga device, and use CMD_WRITE for sending raw data to the AHI. With hi-level ahi coding you are allowed to mix sound, there are no 'locks' of ahi.device anymore and so on. For example, for mp3 playing, or for mod players, you just depack the data needed by the CPU and use this unpacked raw data with CMD_WRITE.

The device interface is much easier to program, and is suitable for system niose or mp3 players. It has a fix latency of 20ms, which suffices in most (non-musical) situations. It is non-blocking the hardware, so it is the first choice when it comes to quick play some audio.

It also supports the CMD_READ message but

  • As soon as you read, it blocks AHI exclusively.
  • it can sometimes gives the odd click while recording via device interfaces

All you do is use the CMD_WRITE command with the sample info set in the AHI IORequest structure, for more samples you will need to copy the IORequest and use that for the other samples, and so on. Essentially the structure is sent as a message to the AHI daemon, which is standard for Exec devices, so that is why it needs a copy. Otherwise it would try to link a message in the list which is already in there, of the same address, then crash!

I would start with the device API, not least because it's very simple. When you have loaded/generated sample data, opened the AHI device and allocated IORequest(s), you can use the Exec library functions (DoIO, SendIO, BeginIO...) to play the samples. However, there may be a limited amount of AHI channels, so IIRC in that case lower priority sounds will be queued and played later. You could create your own mixer routine which basically "streams" data using double-buffered IO requests (there is an example in the AHI SDK about double buffering).

Do I really need to record via ahi.device? If it is just the monitor feature, you can use ahi's internal monitor functionality (which has the lowest possible latency and might use the hardware possibility to monitor) or you can read, manipulate and copy to the outbuffer your data using the lib interface. The latency will be usually 20ms, depending on the driver, the application has no control over this.

You could also use datatypes.library to play samples but cannot say if it's very accurate timing wisely, but it's at least very simple to use.

CreateMsgPort
CreateIORequest
OpenDevice (ahi.device)

loop
    {
    depack some music data
    fill AHIdatas
    SendIO((struct IORequest *) AHIdatas);
    }

do for sound (which will be played on second channel (it's only solution to make it works at the same time by ahi):

CreateMsgPort
CreateIORequest
OpenDevice (ahi.device)

fill AHIdatas

DoIO/sendio

Find the default audio ID such as for unit 0 or the default unit. Then call AHI_AllocAudioA() and pass it the ID, or AHI_DEFALUT_ID, and a AHIA_Channels tag with the minimum channels you need. Then check if it allocated the channels. If so you should know it has enough channels and you can then AHI_FreeAudio(). If not that should mean it doesn't have enough provided you have passed all the required tags.

AHI device interface plays streams, not samples. AHI mixes as many streams together as you have channels set in the prefs. If you try to play more streams than channels are available, the additional streams are muted.

If you need to synchronise two samples (perhaps for stereo) then you can issue a CMD_STOP, do your CMD_WRITEs, then issue a CMD_START to start play. What you have gotta watch is that is affects all AHI applications and not just your own.

Library Approach

[edit | edit source]

The library (Low-level) approach use AHI functions like AHI_SetSound(), AHI_SetVol() and so on. In reality, this way has one big problem: if you work with this method, you will not have any mixing functions - your program will lock ahi.device and while your program is running, all other ahi programs will not work. The only advantage of low-level coding as mentioned in the documentation is "low overhead and much more advanced control over the playing sounds".

Here you get exclusive access to the audio hardware, and can do almost whatever you want, including monitoring.

Disadvantage is obviously, that the audio hardware is blocked for all other programs. Most of the drivers don't care about this situation, and if a program tries to access the hardware it mostly trashes everything and you need to restart your program, or re-allocate the audio hardware.

From AHI6 on, there is a nonblocking AHI mode called "device mode", but it doesn't allow recording. Playback only and has bad timing. Good enough for Playback something, but too bad for real time response.

To use two samples, you need to use the library model, you would open the ahi.device and extract the library base from the AHI device structure.

This brings me to another point. Are your sounds mono or stereo? As you would have read the proper way for stereo is to tell AHI to centre the pan and to give it a stereo sample. I don't know if it returns an error if it can't do it, possibly will accept the write but with a muted channel as it looks like you found out.

Another thing about multiple CMD_WRITEs from different AHI Requests. AHI will treat each instance separately and will mix the sound together on the same track. Providing the hardware supports it, the high level API can only offer panning and not let specify a direct track AFAIK.

If you want to play multiple samples with only one channel through the device API, you have to create one stream from the samples yourself.

The AHI API uses OpenDevice to do CMD_READ, CMD_WRITE, CMD_START, CMD_STOP.


Setting up

[edit | edit source]
if (AHImp=CreateMsgPort()) {
  if (AHIio=(struct AHIRequest *)CreateIORequest(AHImp,sizeof(struct AHIRequest))) {
     AHIio->ahir_Version = 6;
     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 which to write.

Playing a Sound

[edit | edit source]
// 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);
// fill

  AHIios[0]->ahir_Std.io_Message.mn_Node.ln_Pri = 127;
  AHIios[0]->ahir_Std.io_Command  = CMD_WRITE;
  AHIios[0]->ahir_Std.io_Data     = raw_data;
  AHIios[0]->ahir_Std.io_Length   = size_of_buffer;
  AHIios[0]->ahir_Std.io_Offset   = 0;
  AHIios[0]->ahir_Frequency       = 48000;     // freq
  AHIios[0]->ahir_Type            = AHIST_S16S;// 16b
  AHIios[0]->ahir_Volume          = 0x10000;   // vol.
  AHIios[0]->ahir_Position        = 0x8000;   
  AHIios[0]->ahir_Link            = NULL;

// send

SendIO((struct IORequest *) AHIios[0]);

The AHI_IORequest 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.

Freeing Audio

[edit | edit source]
  • call AHI_ControlAudio() with play to false so make sure nothing is being played
  • unload the sounds using AHI_UnloadSound() to make sure sounds get unloaded
  • then call AHI_FreeAudio()

Closing down

[edit | edit source]

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

  • Does a CloseDevice()
  • then DeleteIORequest()
  • and last DeleteMsgPort()
if (!AHIDevice)
   CloseDevice((struct IORequest *)AHIio);
   DeleteIORequest((struct IORequest *)AHIio);
   DeleteIORequest((struct IORequest *)AHIio2);
   DeleteMsgPort(AHImp);


Updating Sound often

[edit | edit source]

If you want to 'update' your sound on a regular base then there is already functionality available.

In AllocAudio() you can provide a playerfunction using the AHIA_PlayerFunc field.

AHIA_PlayerFunc If you are going to play a musical score, you should use this "interrupt" source instead of VBLANK or CIA timers in order to get the best result with all audio drivers. If you cannot use this, you must not use any "non-realtime" modes (see AHI_GetAudioAttrsA() in the autodocs, the AHIDB_Realtime tag).

AHIA_PlayerFreq If non-zero, it enables timing and specifies how many times per second PlayerFunc will be called. This must be specified if AHIA_PlayerFunc is! It is suggested that you keep the frequency below 100–200 Hz. Since the frequency is a fixpoint number AHIA_PlayerFreq should be less than 13107200 (that's 200 Hz).

That way it is for example possible to write some kind of replayer that decides which sounds needs to be stopped, or for example be slided, volume turned up/down etc.

Just make the main loop wait for the player to be 'done'.

You could do that by messaging, but for example also by using a signal. In order to stop the player you could use a boolean (that you set by pressing a button or whatever you want) that the player checks and then signals the main loop to quit.


Examples

[edit | edit source]
/*
** This program uses the device interface to play a sampled sound.
** The input is read from THE DEFAULT INPUT, make sure you
** start it with "PlayTest pri < mysample.raw" !
** Where pri is a number from -128 to +127 (may be omitted)
** The sample should be 8 bit signed, mono (see TYPE).
**
** PLEASE NOTE that earlier versions of this example contained a bug
** that sometimes DeleteIORequest'ed a pointer that was AllocMem'ed!
*/

#include <devices/ahi.h>
#include <dos/dosasl.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/ahi.h>
#include <stdlib.h>

#define FREQUENCY  8000
#define TYPE       AHIST_M8S
#define BUFFERSIZE 20000

struct MsgPort    *AHImp     = NULL;
struct AHIRequest *AHIios[2] = {NULL, NULL};
struct AHIRequest *AHIio     = NULL;
APTR               AHIiocopy = NULL;
BYTE               AHIDevice = -1;

BYTE buffer1[BUFFERSIZE];
BYTE buffer2[BUFFERSIZE];

void cleanup(LONG rc)
{
    if(!AHIDevice)
        CloseDevice((struct IORequest *)AHIio);
    DeleteIORequest((struct IORequest *)AHIio);
    FreeMem(AHIiocopy, sizeof(struct AHIRequest));
    DeleteMsgPort(AHImp);
    exit(rc);
}

int main(int argc, char *argv[])
{
    BYTE *p1 = buffer1, *p2 = buffer2;
    void *tmp;
    ULONG signals, length;
    struct AHIRequest *link = NULL;
    LONG priority = 0;
    BYTE pri;

    if(argc == 2) {
        StrToLong(argv[1], &priority);
    }
    pri = priority;
    Printf("Sound priority: %ld\n", pri);

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

    if(AHIDevice) {
        Printf("Unable to open %s/0 version 4\n", AHINAME);
        cleanup(RETURN_FAIL);
    }

// Make a copy of the request (for double buffering)
    AHIiocopy = AllocMem(sizeof(struct AHIRequest), MEMF_ANY);
    if(! AHIiocopy) {
        cleanup(RETURN_FAIL);
    }
    CopyMem(AHIio, AHIiocopy, sizeof(struct AHIRequest));
    AHIios[0] = AHIio;
    AHIios[1] = AHIiocopy;

    SetIoErr(0);

    for(;;) {

// Fill buffer
        length = Read(Input(), p1, BUFFERSIZE);

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

        if(link) {

// Wait until the last buffer is finished (== the new buffer is started)
            signals = Wait(SIGBREAKF_CTRL_C | (1L << AHImp->mp_SigBit));

// Check for Ctrl-C and abort if pressed
            if(signals & SIGBREAKF_CTRL_C) {
                SetIoErr(ERROR_BREAK);
                break;
            }

// Remove the reply and abort on error
            if(WaitIO((struct IORequest *) link)) {
                SetIoErr(ERROR_WRITE_PROTECTED);
                break;
            }
        }

// Check for end-of-sound, and wait until it is finished before aborting
        if(length != BUFFERSIZE) {
            WaitIO((struct IORequest *) AHIios[0]);
            break;
        }

        link = AHIios[0];

// Swap buffer and request pointers, and restart
        tmp    = p1;
        p1     = p2;
        p2     = tmp;

        tmp    = AHIios[0];
        AHIios[0] = AHIios[1];
        AHIios[1] = tmp;
    }


// Abort any pending iorequests
    AbortIO((struct IORequest *) AHIios[0]);
    WaitIO((struct IORequest *) AHIios[0]);

    if(link) { // Only if the second request was started
        AbortIO((struct IORequest *) AHIios[1]);
        WaitIO((struct IORequest *) AHIios[1]);
    }

    if(IoErr()) {
        PrintFault(IoErr(), argv[0]);
        cleanup(RETURN_ERROR);
    }

    cleanup(RETURN_OK);
    return RETURN_OK; // Make compiler happy
}
/*
** This program uses the device interface to sample sound data.
** The output is written to THE DEFAULT OUTPUT, make sure you
** start it with "RecordTest > mysample.raw" !
*/

#include <devices/ahi.h>
#include <dos/dosasl.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/ahi.h>
#include <stdlib.h>

#define FREQUENCY 8000
#define TYPE       AHIST_M8S
#define BUFFERSIZE 20000

struct Library    *AHIBase;
struct MsgPort    *AHImp = NULL;
struct AHIRequest *AHIio = NULL;
BYTE               AHIDevice = -1;

BYTE buffer1[BUFFERSIZE];
BYTE buffer2[BUFFERSIZE];

void cleanup(LONG rc)
{
    if(!AHIDevice)
        CloseDevice((struct IORequest *)AHIio);
    DeleteIORequest((struct IORequest *)AHIio);
    DeleteMsgPort(AHImp);
    exit(rc);
}

void main(int argc, char *argv[])
{
    BYTE *p1 = buffer1, *p2 = buffer2, *tmp;
    ULONG signals;

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

    if(AHIDevice) {
        Printf("Unable to open %s/0 version 4\n", AHINAME);
        cleanup(RETURN_FAIL);
    }

// Initialize the first read
    AHIio->ahir_Std.io_Command = CMD_READ;
    AHIio->ahir_Std.io_Data = &buffer1;
    AHIio->ahir_Std.io_Length = BUFFERSIZE;
    AHIio->ahir_Std.io_Offset = 0;
    AHIio->ahir_Frequency = FREQUENCY;
    AHIio->ahir_Type = TYPE;
    if(!DoIO((struct IORequest *) AHIio)) {

// The first buffer is now filled
        SetIoErr(NULL);

        for(;;) {
            ULONG length;

            length = AHIio->ahir_Std.io_Actual;

// Initialize the second read (note that io_Offset is not cleared!)
            AHIio->ahir_Std.io_Data = p2;
            AHIio->ahir_Std.io_Length = BUFFERSIZE;
            AHIio->ahir_Frequency = FREQUENCY;
            AHIio->ahir_Type = TYPE;
            SendIO((struct IORequest *) AHIio);

// While the second read is in progress, save the first buffer to stdout
            if(Write(Output(), p1, length) != length) {
                break;
            }

            signals = Wait(SIGBREAKF_CTRL_C | (1L << AHImp->mp_SigBit));

            if(signals & SIGBREAKF_CTRL_C) {
                SetIoErr(ERROR_BREAK);
                break;
            }

// Remove the reply
            if(WaitIO((struct IORequest *) AHIio)) {
                SetIoErr(ERROR_READ_PROTECTED);
                break;
            }

// Swap buffer pointers and repeat
            tmp = p1;
            p1 = p2;
            p2 = tmp;
        }

// Abort any pending iorequests
        AbortIO((struct IORequest *) AHIio);
        WaitIO((struct IORequest *) AHIio);
    }

    if(IoErr()) {
        PrintFault(IoErr(), argv[0]);  // Oh, common! It's not MY fault that this
        // routine prints to stdout instead of stderr!
        cleanup(RETURN_ERROR);
    }

    cleanup(RETURN_OK);
}


struct MsgPort    *AHIPort = NULL;
struct AHIRequest *AHIReq = NULL;
BYTE               AHIDevice = -1;
UBYTE              unit = AHI_DEFAULT_UNIT;

static int write_ahi_output (char * output_data, int output_size);
static void close_ahi_output ( void );

static int
open_ahi_output ( void ) {
    if (AHIPort = CreateMsgPort())
    {
        if (AHIReq = (struct AHIRequest *) CreateIORequest(AHIPort, sizeof(struct AHIRequest)))
        {
            AHIReq->ahir_Version = 4;
            if (!(AHIDevice = OpenDevice(AHINAME, unit, (struct IORequest *) AHIReq, NULL)))
            {
                send_output = write_ahi_output;
                close_output = close_ahi_output;
                return 0;
            }
            DeleteIORequest((struct IORequest *) AHIReq);
            AHIReq = NULL;

        }
        DeleteMsgPort(AHIPort);
        AHIPort = NULL;
    }

    return -1;
}

static int
write_ahi_output (char * output_data, int output_size) {
    if (!CheckIO((struct IORequest *) AHIReq))
    {
        WaitIO((struct IORequest *) AHIReq);
        //AbortIO((struct IORequest *) AHIReq);
    }

    AHIReq->ahir_Std.io_Command = CMD_WRITE;
    AHIReq->ahir_Std.io_Flags = 0;
    AHIReq->ahir_Std.io_Data = output_data;
    AHIReq->ahir_Std.io_Length = output_size;
    AHIReq->ahir_Std.io_Offset = 0;
    AHIReq->ahir_Frequency = rate;
    AHIReq->ahir_Type = AHIST_S16S;
    AHIReq->ahir_Volume = 0x10000;
    AHIReq->ahir_Position = 0x8000;
    AHIReq->ahir_Link = NULL;
    SendIO((struct IORequest *) AHIReq);
     
    return 0;
}

static void
close_ahi_output ( void ) {
if (!CheckIO((struct IORequest *) AHIReq)) {
    WaitIO((struct IORequest *) AHIReq);
    AbortIO((struct IORequest *) AHIReq);
}

if (AHIReq) {
    CloseDevice((struct IORequest *) AHIReq);
    AHIDevice = -1;
    DeleteIORequest((struct IORequest *) AHIReq);
    AHIReq = NULL;
}

if (AHIPort) {
    DeleteMsgPort(AHIPort);
    AHIPort = NULL;
}

}

High level ahi for sound playback - The idea is to create several i/o requests and then when you want to play a sound you pick one that is free and then simply start CMD_WRITE to it with BeginIO() and then mark the i/o request as in use (ch->busy field in above code). What the SoundIO() function does is check for replies from ahi.device that some i/o request has finished and then simply marks them as not in use any more. If no i/o request is free the PlaySound function simply interrupts the one that has been playing for longest with AbortIO()/WaitIO() and then reuses that one.

char *snd_buffer[5];
int sound_file_size[5];

int number;

struct Process *sound_player;
int sound_player_done = 0;

void load_sound(char *name, int number)
{

   FILE *fp_filename;

   if((fp_filename = fopen(name,"rb")) == NULL)
     { printf("can't open sound file\n");exit(0);} ;

   fseek (fp_filename,0,SEEK_END);
   sound_file_size[number] = ftell(fp_filename);
   fseek (fp_filename,0,SEEK_SET);

   snd_buffer[number]=(char *)malloc(sound_file_size[number]);

   fread(snd_buffer[number],sound_file_size[number],1,fp_filename);

   //printf("%d\n",sound_file_size[number]);

   fclose(fp_filename);

 //  free(snd_buffer[number]);

}

void play_sound_routine(void)

{

struct MsgPort    *AHImp_sound     = NULL;
struct AHIRequest *AHIios_sound[2] = {NULL,NULL};
struct AHIRequest *AHIio_sound     = NULL;
BYTE               AHIDevice_sound = -1;
//ULONG sig_sound;

//-----open/setup ahi

    if((AHImp_sound=CreateMsgPort()) != NULL) {
    if((AHIio_sound=(struct AHIRequest *)CreateIORequest(AHImp_sound,sizeof(struct AHIRequest))) != NULL) {
      AHIio_sound->ahir_Version = 4;
      AHIDevice_sound=OpenDevice(AHINAME,0,(struct IORequest *)AHIio_sound,0);
    }
  }

  if(AHIDevice_sound) {
    Printf("Unable to open %s/0 version 4\n",AHINAME);
    goto sound_panic;
  }

  AHIios_sound[0]=AHIio_sound;
  SetIoErr(0);

    AHIios_sound[0]->ahir_Std.io_Message.mn_Node.ln_Pri = 127;
    AHIios_sound[0]->ahir_Std.io_Command  = CMD_WRITE;
    AHIios_sound[0]->ahir_Std.io_Data     = snd_buffer[number];//sndbuf;
    AHIios_sound[0]->ahir_Std.io_Length   = sound_file_size[number];//fib_snd.fib_Size;
    AHIios_sound[0]->ahir_Std.io_Offset   = 0;
    AHIios_sound[0]->ahir_Frequency       = 8000;//44100;
    AHIios_sound[0]->ahir_Type            = AHIST_M8S;//AHIST_M16S;
    AHIios_sound[0]->ahir_Volume          = 0x10000;          // Full volume
    AHIios_sound[0]->ahir_Position        = 0x8000;           // Centered
    AHIios_sound[0]->ahir_Link            = NULL;

    DoIO((struct IORequest *) AHIios_sound[0]);

sound_panic:

  //printf("are we on sound_exit?\n");
  if(!AHIDevice_sound)
    CloseDevice((struct IORequest *)AHIio_sound);
  DeleteIORequest((struct IORequest *)AHIio_sound);
  DeleteMsgPort(AHImp_sound);
  sound_player_done = 1;

}

void stop_sound(void)
{

     Signal(&sound_player->pr_Task, SIGBREAKF_CTRL_C );
     while(sound_player_done !=1){};
     sound_player_done=0;
}

void play_sound(int num)

{
      number=num;

         #ifdef __MORPHOS__

         sound_player = CreateNewProcTags(
							NP_Entry, &play_sound_routine,
							NP_Priority, 1,
							NP_Name, "Ahi raw-sound-player Process",
                          //  NP_Input, Input(),
                          //  NP_CloseInput, FALSE,
                          //  NP_Output, Output(),
                          //  NP_CloseOutput, FALSE,

                            NP_CodeType, CODETYPE_PPC,

							TAG_DONE);

         #else

         sound_player = CreateNewProcTags(
							NP_Entry, &play_sound_routine,
							NP_Priority, 1,
							NP_Name, "Ahi raw-sound-player Process",
                          //  NP_Input, Input(),
                          //  NP_CloseInput, FALSE,
                          //  NP_Output, Output(),
                          //  NP_CloseOutput, FALSE,

							TAG_DONE);
         #endif

         Delay(10); // little delay for make sounds finish

}



Low level for music playback

These steps will allow you to use low-level AHI functions:
- Create message port and AHIRequest with appropriate functions from exec.library.
- Open the device with OpenDevice() giving AHI_NO_UNIT as a unit.
- Get interface to the library with GetInterface() giving as the first parameter io_Device field of IORequest.
Once you get the AHI interface, its functions can be used. To start playing sounds you need to allocate audio (optionally you can ask user for Audio mode and frequency). Then you need to Load samples to use with AHI. You do it with AHI_AllocAudio(), AHI_ControlAudio() and AHI_LoadSound().

use the functions AHI_SetVol() to set volume, AHI_SetFreq() to set frequency, AHI_SetSound() to play the sounds.


Other Examples

[edit | edit source]

Master volume utility

Anyone can make such a relative simple utility. It's a matter of calling AHI_SetEffect() with a master volume structure. You can make a window with a slider and call said function easily.

You write to AHI device, and AHI will write to a sound card, the native hardware, or even to a file. These options are user-configurable. AHI also performs the software mixing duties so that more than one sound can be played simultaneously.

AHI provides four 'units' for audio. This makes it possible to have a program play on the native hardware, and another play on a sound card by attaching the appropriate AHI driver to a unit number. For the software developer, AHI provides two ways to play audio. One is the AUDIO: DOS device. AHI can create a volume called AUDIO: that works like an AmigaDOS volume. You can read and write data directly to it and it plays through the speakers. This is the easiest way to write PCM, but it's not the best.

First of all, if a user takes the AUDIO: entry out of the mountlist, your program won't work and you get bombarded with stupid support questions like 'I've got AHI, why doesn't it work?'. The better option is to send IORequests to AHI. This allows you to control volume and balance settings while the program runs (with AUDIO: you set these when you open the file and you can't change them without closing and re-opening AUDIO:) and you can use a neat trick called double-buffering to improve efficiency. Double-buffering allows you to fill one audio buffer while another one is playing. This kind of asynchronous operation can prevent 'choppy' audio on slower systems.

We initialize AHI and then prepare and send AHI request to the ahi.device.

It's very important to calculate the number of bytes you want AHI to read from the buffer. You can cause a nasty crash indeed if it's incorrect! To do this, multiply the PCM count by the number of channels by the number of AHI buffers.

A quick note about volume and position: AHI uses a fairly arcane datatype called Fixed. A Fixed number consists of 32 bits: a sign bit, a 15-bit integer part, and a 16-bit fractional part. Construct the AHI request, multiply this number by 0x00010000 to convert it to the fixed value. If you use this code as part of a DOS background process, can change the volume and balance on the fly so the next sample that's queued will be played louder or quieter. It's also possible to interrupt AHI so the change takes effect immediately, but won't go into that.

Once the request is sent, we put the requisite bits in to check for CTRL-C and any AHI interrupt messages. Then it's time to swap buffers around.


Hooks

[edit | edit source]

An old idea which is best avoided if possible. The hook function should be used to play/control the sample(s). It is called at the frequency that it was initialized with (in your case 100).

So in your 'normal' code you would flip a switch somewhere, telling the hookfunction to start playing a sample (or do with it whatever you want).

In the hookfunction you then start playing the sample and/or apply effects with the ahi ctrl function (and others).

One such example could be for instance be that module (as in well now .mod fileformat) data is being processed for each channel and apply effect, etc.

In your case it would be a bit simpler as a mod player. You want to start playing a note and stop it at your will. For instance when a counter reached a certain value.

The (probable) reason your number it not printing is because that routine is called _a lot_ every second

The gist is that you have to find a mechanism (that suits your purpose the best) that uses your mouseclicks (or keyclicks) to 'feed' the player (hookfunct) and find something that 'tells' the player to do something else with the played sample (stopping it, applying an effect etc).

You can use the data property of the hookfunc to 'give' / push a structure to your 'replay' routine so that you can for example tell the player that a certain sample started being played. The player can then decide (if counter reached a value for example) to actually stop the sample from playing and setting/changing the status in that structure so that the main program knows the sample can be 'played'/triggered again.


/*
 * Copyright (C) 2005 Mark Olsen
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <exec/exec.h>
#include <devices/ahi.h>
#include <proto/exec.h>
#define USE_INLINE_STDARG
#include <proto/ahi.h>
#include <utility/hooks.h>

#include "../game/q_shared.h"
#include "../client/snd_local.h"

struct AHIdata *ad;

struct AHIChannelInfo
{
	struct AHIEffChannelInfo aeci;
	ULONG offset;
};

struct AHIdata
{
	struct MsgPort *msgport;
	struct AHIRequest *ahireq;
	int ahiopen;
	struct AHIAudioCtrl *audioctrl;
	void *samplebuffer;
	struct Hook EffectHook;
	struct AHIChannelInfo aci;
	unsigned int readpos;
};

#if !defined(__AROS__)
ULONG EffectFunc()
{
	struct Hook *hook = (struct Hook *)REG_A0;
	struct AHIEffChannelInfo *aeci = (struct AHIEffChannelInfo *)REG_A1;

	struct AHIdata *ad;

	ad = hook->h_Data;

	ad->readpos = aeci->ahieci_Offset[0];

	return 0;
}

static struct EmulLibEntry EffectFunc_Gate =
{
	TRAP_LIB, 0, (void (*)(void))EffectFunc
};
#else
AROS_UFH3(ULONG, EffectFunc,
          AROS_UFHA(struct Hook *, hook, A0),
          AROS_UFHA(struct AHIAudioCtrl *, aac, A2),
          AROS_UFHA(struct AHIEffChannelInfo *, aeci, A1)
         )
{
    AROS_USERFUNC_INIT
    
	struct AHIdata *ad;

	ad = hook->h_Data;

	ad->readpos = aeci->ahieci_Offset[0];

	return 0;

    AROS_USERFUNC_EXIT
}
#endif

qboolean SNDDMA_Init(void)
{
	ULONG channels;
	ULONG speed;
	ULONG bits;

	ULONG r;

	struct Library *AHIBase;

	struct AHISampleInfo sample;

	cvar_t *sndbits;
	cvar_t *sndspeed;
	cvar_t *sndchannels;

	char modename[64];

	if (ad)
		return;

	sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
	sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
	sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);

	speed = sndspeed->integer;

	if (speed == 0)
		speed = 22050;

	ad = AllocVec(sizeof(*ad), MEMF_ANY);
	if (ad)
	{
		ad->msgport = CreateMsgPort();
		if (ad->msgport)
		{
			ad->ahireq = (struct AHIRequest *)CreateIORequest(ad->msgport, sizeof(struct AHIRequest));
			if (ad->ahireq)
			{
				ad->ahiopen = !OpenDevice("ahi.device", AHI_NO_UNIT, (struct IORequest *)ad->ahireq, 0);
				if (ad->ahiopen)
				{
					AHIBase = (struct Library *)ad->ahireq->ahir_Std.io_Device;

					ad->audioctrl = AHI_AllocAudio(AHIA_AudioID, AHI_DEFAULT_ID,
					                               AHIA_MixFreq, speed,
					                               AHIA_Channels, 1,
					                               AHIA_Sounds, 1,
					                               TAG_END);

					if (ad->audioctrl)
					{
						AHI_GetAudioAttrs(AHI_INVALID_ID, ad->audioctrl,
						                  AHIDB_BufferLen, sizeof(modename),
						                  AHIDB_Name, (ULONG)modename,
						                  AHIDB_MaxChannels, (ULONG)&channels,
						                  AHIDB_Bits, (ULONG)&bits,
						                  TAG_END);

						AHI_ControlAudio(ad->audioctrl,
						                 AHIC_MixFreq_Query, (ULONG)&speed,
						                 TAG_END);

						if (bits == 8 || bits == 16)
						{
							if (channels > 2)
								channels = 2;

							dma.speed = speed;
							dma.samplebits = bits;
							dma.channels = channels;
#if !defined(__AROS__)
							dma.samples = 2048*(speed/11025);
#else
							dma.samples = 16384*(speed/11025);
#endif
							dma.submission_chunk = 1;

#if !defined(__AROS__)
							ad->samplebuffer = AllocVec(2048*(speed/11025)*(bits/8)*channels, MEMF_ANY);
#else
							ad->samplebuffer = AllocVec(16384*(speed/11025)*(bits/8)*channels, MEMF_ANY);
#endif
							if (ad->samplebuffer)
							{
								dma.buffer = ad->samplebuffer;

								if (channels == 1)
								{
									if (bits == 8)
										sample.ahisi_Type = AHIST_M8S;
									else
										sample.ahisi_Type = AHIST_M16S;
								}
								else
								{
									if (bits == 8)
										sample.ahisi_Type = AHIST_S8S;
									else
										sample.ahisi_Type = AHIST_S16S;
								}

								sample.ahisi_Address = ad->samplebuffer;
#if !defined(__AROS__)
								sample.ahisi_Length = (2048*(speed/11025)*(bits/8))/AHI_SampleFrameSize(sample.ahisi_Type);
#else
								sample.ahisi_Length = (16384*(speed/11025)*(bits/8))/AHI_SampleFrameSize(sample.ahisi_Type);								
#endif

								r = AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &sample, ad->audioctrl);
								if (r == 0)
								{
									r = AHI_ControlAudio(ad->audioctrl,
									                     AHIC_Play, TRUE,
									                     TAG_END);

									if (r == 0)
									{
										AHI_Play(ad->audioctrl,
										         AHIP_BeginChannel, 0,
										         AHIP_Freq, speed,
										         AHIP_Vol, 0x10000,
										         AHIP_Pan, 0x8000,
										         AHIP_Sound, 0,
										         AHIP_EndChannel, NULL,
										         TAG_END);

										ad->aci.aeci.ahie_Effect = AHIET_CHANNELINFO;
										ad->aci.aeci.ahieci_Func = &ad->EffectHook;
										ad->aci.aeci.ahieci_Channels = 1;

#if !defined(__AROS__)
										ad->EffectHook.h_Entry = (void *)&EffectFunc_Gate;
#else
										ad->EffectHook.h_Entry = (IPTR (*)())&EffectFunc;
#endif
										ad->EffectHook.h_Data = ad;
										AHI_SetEffect(&ad->aci, ad->audioctrl);

										Com_Printf("Using AHI mode \"%s\" for audio output\n", modename);
										Com_Printf("Channels: %d bits: %d frequency: %d\n", channels, bits, speed);

										return 1;
									}
								}
							}
							FreeVec(ad->samplebuffer);
						}
						AHI_FreeAudio(ad->audioctrl);
					}
					else
						Com_Printf("Failed to allocate AHI audio\n");

					CloseDevice((struct IORequest *)ad->ahireq);
				}
				DeleteIORequest((struct IORequest *)ad->ahireq);
			}
			DeleteMsgPort(ad->msgport);
		}
		FreeVec(ad);
	}

	return 0;
}

int SNDDMA_GetDMAPos(void)
{
	return ad->readpos*dma.channels;
}

void SNDDMA_Shutdown(void)
{
	struct Library *AHIBase;

	if (ad == 0)
		return;

	AHIBase = (struct Library *)ad->ahireq->ahir_Std.io_Device;

	ad->aci.aeci.ahie_Effect = AHIET_CHANNELINFO|AHIET_CANCEL;
	AHI_SetEffect(&ad->aci.aeci, ad->audioctrl);
	AHI_ControlAudio(ad->audioctrl,
	                 AHIC_Play, FALSE,
	                 TAG_END);

	AHI_FreeAudio(ad->audioctrl);
	FreeVec(ad->samplebuffer);
	CloseDevice((struct IORequest *)ad->ahireq);
	DeleteIORequest((struct IORequest *)ad->ahireq);
	DeleteMsgPort(ad->msgport);
	FreeVec(ad);

	ad = 0;
}

void SNDDMA_Submit(void)
{
}

void SNDDMA_BeginPainting (void)
{
}
/*
Copyright (C) 2006-2007 Mark Olsen

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either Version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <exec/exec.h>
#include <devices/ahi.h>
#include <proto/exec.h>
#define USE_INLINE_STDARG
#include <proto/ahi.h>

#include "quakedef.h"
#include "sound.h"

struct AHIChannelInfo
{
	struct AHIEffChannelInfo aeci;
	ULONG offset;
};

struct ahi_private
{
	struct MsgPort *msgport;
	struct AHIRequest *ahireq;
	struct AHIAudioCtrl *audioctrl;
	void *samplebuffer;
	struct Hook EffectHook;
	struct AHIChannelInfo aci;
	unsigned int readpos;
};

ULONG EffectFunc()
{
	struct Hook *hook = (struct Hook *)REG_A0;
	struct AHIEffChannelInfo *aeci = (struct AHIEffChannelInfo *)REG_A1;

	struct ahi_private *p;

	p = hook->h_Data;

	p->readpos = aeci->ahieci_Offset[0];

	return 0;
}

static struct EmulLibEntry EffectFunc_Gate =
{
	TRAP_LIB, 0, (void (*)(void))EffectFunc
};

void ahi_shutdown(struct SoundCard *sc)
{
	struct ahi_private *p = sc->driverprivate;

	struct Library *AHIBase;

	AHIBase = (struct Library *)p->ahireq->ahir_Std.io_Device;

	p->aci.aeci.ahie_Effect = AHIET_CHANNELINFO|AHIET_CANCEL;
	AHI_SetEffect(&p->aci.aeci, p->audioctrl);
	AHI_ControlAudio(p->audioctrl,
	                 AHIC_Play, FALSE,
	                 TAG_END);

	AHI_FreeAudio(p->audioctrl);

	CloseDevice((struct IORequest *)p->ahireq);
	DeleteIORequest((struct IORequest *)p->ahireq);

	DeleteMsgPort(p->msgport);

	FreeVec(p->samplebuffer);

	FreeVec(p);
}

int ahi_getdmapos(struct SoundCard *sc)
{
	struct ahi_private *p = sc->driverprivate;

	sc->samplepos = p->readpos*sc->channels;

	return sc->samplepos;
}

void ahi_submit(struct SoundCard *sc, unsigned int count)
{
}

qboolean ahi_init(struct SoundCard *sc, int rate, int channels, int bits)
{
	struct ahi_private *p;
	ULONG r;

	char name[64];

	struct Library *AHIBase;

	struct AHISampleInfo sample;

	p = AllocVec(sizeof(*p), MEMF_ANY);
	if (p)
	{
		p->msgport = CreateMsgPort();
		if (p->msgport)
		{
			p->ahireq = (struct AHIRequest *)CreateIORequest(p->msgport, sizeof(struct AHIRequest));
			if (p->ahireq)
			{
				r = !OpenDevice("ahi.device", AHI_NO_UNIT, (struct IORequest *)p->ahireq, 0);
				if (r)
				{
					AHIBase = (struct Library *)p->ahireq->ahir_Std.io_Device;

					p->audioctrl = AHI_AllocAudio(AHIA_AudioID, AHI_DEFAULT_ID,
					                               AHIA_MixFreq, rate,
					                               AHIA_Channels, 1,
					                               AHIA_Sounds, 1,
					                               TAG_END);

					if (p->audioctrl)
					{
						AHI_GetAudioAttrs(AHI_INVALID_ID, p->audioctrl,
						                  AHIDB_BufferLen, sizeof(name),
						                  AHIDB_Name, (ULONG)name,
						                  AHIDB_MaxChannels, (ULONG)&channels,
						                  AHIDB_Bits, (ULONG)&bits,
						                  TAG_END);

						AHI_ControlAudio(p->audioctrl,
						                 AHIC_MixFreq_Query, (ULONG)&rate,
						                 TAG_END);

						if (bits == 8 || bits == 16)
						{
							if (channels > 2)
								channels = 2;

							sc->speed = rate;
							sc->samplebits = bits;
							sc->channels = channels;
							sc->samples = 16384*(rate/11025);

							p->samplebuffer = AllocVec(16384*(rate/11025)*(bits/8)*channels, MEMF_CLEAR);
							if (p->samplebuffer)
							{
								sc->buffer = p->samplebuffer;

								if (channels == 1)
								{
									if (bits == 8)
										sample.ahisi_Type = AHIST_M8S;
									else
										sample.ahisi_Type = AHIST_M16S;
								}
								else
								{
									if (bits == 8)
										sample.ahisi_Type = AHIST_S8S;
									else
										sample.ahisi_Type = AHIST_S16S;
								}

								sample.ahisi_Address = p->samplebuffer;
								sample.ahisi_Length = (16384*(rate/11025)*(bits/8))/AHI_SampleFrameSize(sample.ahisi_Type);

								r = AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &sample, p->audioctrl);
								if (r == 0)
								{
									r = AHI_ControlAudio(p->audioctrl,
									                     AHIC_Play, TRUE,
									                     TAG_END);

									if (r == 0)
									{
										AHI_Play(p->audioctrl,
										         AHIP_BeginChannel, 0,
										         AHIP_Freq, rate,
										         AHIP_Vol, 0x10000,
										         AHIP_Pan, 0x8000,
										         AHIP_Sound, 0,
										         AHIP_EndChannel, NULL,
										         TAG_END);

										p->aci.aeci.ahie_Effect = AHIET_CHANNELINFO;
										p->aci.aeci.ahieci_Func = &p->EffectHook;
										p->aci.aeci.ahieci_Channels = 1;

										p->EffectHook.h_Entry = (void *)&EffectFunc_Gate;
										p->EffectHook.h_Data = p;

										AHI_SetEffect(&p->aci, p->audioctrl);

										Com_Printf("Using AHI mode \"%s\" for audio output\n", name);
										Com_Printf("Channels: %d bits: %d frequency: %d\n", channels, bits, rate);

										sc->driverprivate = p;

										sc->GetDMAPos = ahi_getdmapos;
										sc->Submit = ahi_submit;
										sc->Shutdown = ahi_shutdown;

										return 1;
									}
								}
							}
							FreeVec(p->samplebuffer);
						}
						AHI_FreeAudio(p->audioctrl);
					}
					else
						Com_Printf("Failed to allocate AHI audio\n");

					CloseDevice((struct IORequest *)p->ahireq);
				}
				DeleteIORequest((struct IORequest *)p->ahireq);
			}
			DeleteMsgPort(p->msgport);
		}
		FreeVec(p);
	}

	return 0;
}

SoundInitFunc AHI_Init = ahi_init;
#include <config.h>

#include <devices/ahi.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <utility/hooks.h>

#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/ahi.h>

#include <stdio.h>
#include <math.h>
#include <limits.h>

#include "version.h"

static const char version[] = "$VER: PlaySineEverywhere " VERS "\n\r";

long __oslibversion = 37;

struct Library *AHIBase = NULL;

int
PlaySineEverywhere(void);

void
SoundFunc(struct Hook            *hook,
          struct AHIAudioCtrl    *actrl,
          struct AHISoundMessage *sm);


int
main(int argc, char *argv[])
{
    int rc = RETURN_OK;

    if(argc != 1) {
        fprintf(stderr, "Usage: %s\n", argv[ 0 ]);
        rc = RETURN_ERROR;
    } else {
        struct MsgPort *mp = CreateMsgPort();

        if(mp != NULL) {
            struct AHIRequest *io = (struct AHIRequest *)
                                    CreateIORequest(mp, sizeof(struct AHIRequest));

            if(io != NULL) {
                // We use 32 bit samples, so we need version 6.
                io->ahir_Version = 6;

                if(OpenDevice(AHINAME, AHI_NO_UNIT, (struct IORequest *) io, 0)
                        == 0) {
                    AHIBase = (struct Library *) io->ahir_Std.io_Device;

                    rc = PlaySineEverywhere();

                    CloseDevice((struct IORequest *) io);
                } else {
                    fprintf(stderr, "Unable to open '" AHINAME "' version 6.\n");
                    rc = RETURN_FAIL;
                }

                DeleteIORequest((struct IORequest *) io);
            } else {
                fprintf(stderr, "Unable to create IO request.\n");
                rc = RETURN_FAIL;
            }

            DeleteMsgPort(mp);
        } else {
            fprintf(stderr, "Unable to create message port.\n");
            rc = RETURN_FAIL;
        }
    }

    return rc;
}

int
PlaySineEverywhere(void)
{
    int   rc = RETURN_OK;
    int   sine_length = 44100 / 100;
    BYTE *sine_m8s;
    BYTE *sine_s8s;
    WORD *sine_s16s;
    WORD *sine_m16s;
    LONG *sine_s32s;
    LONG *sine_m32s;

    sine_m8s  = AllocVec(1 * sizeof(BYTE) * sine_length, MEMF_ANY | MEMF_PUBLIC);
    sine_s8s  = AllocVec(2 * sizeof(BYTE) * sine_length, MEMF_ANY | MEMF_PUBLIC);
    sine_m16s = AllocVec(1 * sizeof(WORD) * sine_length, MEMF_ANY | MEMF_PUBLIC);
    sine_s16s = AllocVec(2 * sizeof(WORD) * sine_length, MEMF_ANY | MEMF_PUBLIC);
    sine_m32s = AllocVec(1 * sizeof(LONG) * sine_length, MEMF_ANY | MEMF_PUBLIC);
    sine_s32s = AllocVec(2 * sizeof(LONG) * sine_length, MEMF_ANY | MEMF_PUBLIC);

    if(sine_m8s != NULL &&
            sine_s8s != NULL &&
            sine_m16s != NULL &&
            sine_s16s != NULL &&
            sine_m32s != NULL &&
            sine_s32s != NULL) {
        ULONG mode = AHI_INVALID_ID;
        int   i;

        for(i = 0; i < sine_length; ++i) {
            double value = sin(i * 2 * M_PI / sine_length);

            sine_m8s[ i ] = (BYTE)(SCHAR_MAX * value);
            sine_m16s[ i ] = (WORD)(SHRT_MAX * value);
            sine_m32s[ i ] = (LONG)(LONG_MAX * value);

            sine_s8s[ i * 2 + 0 ] = (BYTE)(SCHAR_MAX * value);
            sine_s8s[ i * 2 + 1 ] = (BYTE)(SCHAR_MAX * value);
            sine_s16s[ i * 2 + 0 ] = (WORD)(SHRT_MAX * value);
            sine_s16s[ i * 2 + 1 ] = (WORD)(SHRT_MAX * value);
            sine_s32s[ i * 2 + 0 ] = (LONG)(LONG_MAX * value);
            sine_s32s[ i * 2 + 1 ] = (LONG)(LONG_MAX * value);
        }

        while(rc == RETURN_OK &&
                (mode = AHI_NextAudioID(mode)) != AHI_INVALID_ID) {
            struct AHIAudioCtrl *actrl;
            char                 name[ 64 ];
            struct Hook          sound_hook = {
                { NULL, NULL },
                HookEntry,
                (HOOKFUNC) SoundFunc,
                FindTask(NULL)
            };

            AHI_GetAudioAttrs(mode, NULL,
                              AHIDB_Name, (IPTR) &name,
                              AHIDB_BufferLen, 64,
                              TAG_DONE);

            printf("Mode 0x%08lx: %s\n", mode, name);

            actrl = AHI_AllocAudio(AHIA_AudioID,   mode,
                                   AHIA_MixFreq,   44100,
                                   AHIA_Channels,  1,
                                   AHIA_Sounds,    6,
                                   AHIA_SoundFunc, (IPTR) &sound_hook,
                                   AHIA_UserData,  0,
                                   TAG_DONE);

            if(actrl != NULL) {
                struct AHISampleInfo sample_m8s  = { AHIST_M8S,  sine_m8s,  sine_length };
                struct AHISampleInfo sample_s8s  = { AHIST_S8S,  sine_s8s,  sine_length };
                struct AHISampleInfo sample_m16s = { AHIST_M16S, sine_m16s, sine_length };
                struct AHISampleInfo sample_s16s = { AHIST_S16S, sine_s16s, sine_length };
                struct AHISampleInfo sample_m32s = { AHIST_M32S, sine_m32s, sine_length };
                struct AHISampleInfo sample_s32s = { AHIST_S32S, sine_s32s, sine_length };

                if(AHI_LoadSound(0, AHIST_SAMPLE, &sample_m8s,  actrl) == AHIE_OK &&
                        AHI_LoadSound(1, AHIST_SAMPLE, &sample_s8s,  actrl) == AHIE_OK &&
                        AHI_LoadSound(2, AHIST_SAMPLE, &sample_m16s, actrl) == AHIE_OK &&
                        AHI_LoadSound(3, AHIST_SAMPLE, &sample_s16s, actrl) == AHIE_OK &&
                        AHI_LoadSound(4, AHIST_SAMPLE, &sample_m32s, actrl) == AHIE_OK &&
                        AHI_LoadSound(5, AHIST_SAMPLE, &sample_s32s, actrl) == AHIE_OK) {

                    AHI_Play(actrl,
                             AHIP_BeginChannel, 0,
                             AHIP_Sound,        0,
                             AHIP_Freq,         44100,
                             AHIP_Vol,          0x10000,
                             AHIP_Pan,          0x00000,
                             AHIP_EndChannel,   0,
                             TAG_DONE);

                    // Now, when everything is "armed", lets start processing.

                    SetSignal(0, SIGF_SINGLE);

                    if(AHI_ControlAudio(actrl,
                                        AHIC_Play, TRUE,
                                        TAG_DONE) == AHIE_OK) {
                        Wait(SIGF_SINGLE);

                        AHI_ControlAudio(actrl,
                                         AHIC_Play, FALSE,
                                         TAG_DONE);
                    } else {
                        fprintf(stderr, "Unable start playback.\n");
                        rc = RETURN_ERROR;
                    }

                    // AHI_FreeAudio() will unload the sounds
                } else {
                    fprintf(stderr, "Unable load sound.\n");
                    rc = RETURN_ERROR;
                }

                AHI_FreeAudio(actrl);
            } else {
                fprintf(stderr, "Unable to allocate audio.\n");
                rc = RETURN_ERROR;
            }
        }
    } else {
        fprintf(stderr, "Unable to allocate memory for sine\n");
    }

    FreeVec(sine_m8s);
    FreeVec(sine_s8s);
    FreeVec(sine_s16s);
    FreeVec(sine_m16s);
    FreeVec(sine_s32s);
    FreeVec(sine_m32s);

    return rc;
}

void
SoundFunc(struct Hook            *hook,
          struct AHIAudioCtrl    *actrl,
          struct AHISoundMessage *sm)
{
    struct Task *task = hook->h_Data;
    ULONG        cnt;

    ++((ULONG) actrl->ahiac_UserData);
    cnt = (ULONG) actrl->ahiac_UserData;

    if(cnt == 100) {
        AHI_SetSound(0, AHI_NOSOUND, 0, 0, actrl, AHISF_NONE);
    } else if(cnt == 101) {
        Signal(task, SIGF_SINGLE);
    } else {
        UWORD sound = cnt % 6;

        AHI_SetSound(0, sound, 0, 0, actrl, AHISF_NONE);

        if((sound == 0)) {
            AHI_SetVol(0, 0x10000, 0x10000 * cnt / 6 / (100 / 6), actrl, AHISF_NONE);
        }
    }
}


Double-buffering is required though.

/* Simple sample player for AHI using the low-level API
   with double buffered AHIST_DYNAMICSAMPLE sounds.

   Usage: DoubleBuffer < [raw sample file]

   the file must be in mono 16 bit signed big endian format sampled
   in 17640 Hz.

   This software is Public Domain. */

#include <devices/ahi.h>
#include <exec/exec.h>
#include <proto/ahi.h>
#include <proto/dos.h>
#include <proto/exec.h>

#define EQ ==
#define MINBUFFLEN 10000

struct Library    *AHIBase;
struct MsgPort    *AHImp = NULL;
struct AHIRequest *AHIio = NULL;
BYTE               AHIDevice = -1;

BYTE signal = -1;

struct AHIAudioModeRequester *req = NULL;
struct AHIAudioCtrl *actrl = NULL;

BOOL DBflag = FALSE; // double buffer flag
ULONG BufferLen = NULL;

struct AHISampleInfo Sample0 = {
    AHIST_M16S,
    NULL,
    NULL,
};

struct AHISampleInfo Sample1 = {
    AHIST_M16S,
    NULL,
    NULL,
};

__asm __saveds ULONG SoundFunc(register __a0 struct Hook *hook,
                               register __a2 struct AHIAudioCtrl *actrl,
                               register __a1 struct AHISoundMessage *smsg)
{
    if(DBflag = !DBflag) // Flip and test
        AHI_SetSound(0, 1, 0, 0, actrl, NULL);
    else
        AHI_SetSound(0, 0, 0, 0, actrl, NULL);
    Signal(actrl->ahiac_UserData, (1L << signal));
    return NULL;
}

struct Hook SoundHook = {
    0, 0,
    SoundFunc,
    NULL,
    NULL,
};


int main()
{
    ULONG mixfreq, playsamples, readsamples, signals, i;

    if(AHImp = CreateMsgPort()) {
        if(AHIio = (struct AHIRequest *)CreateIORequest(AHImp, sizeof(struct AHIRequest))) {
            AHIio->ahir_Version = 4;  // Open at least version 4 of 'ahi.device'.
            if(!(AHIDevice = OpenDevice(AHINAME, AHI_NO_UNIT, (struct IORequest *)AHIio, NULL))) {
                AHIBase = (struct Library *)AHIio->ahir_Std.io_Device;

                if(req = AHI_AllocAudioRequest(
                             AHIR_PubScreenName, "",
                             AHIR_TitleText, "Select a mode and rate",
                             AHIR_InitialMixFreq, 17640,
                             AHIR_DoMixFreq, TRUE,
                             TAG_DONE)) {
                    if(AHI_AudioRequest(req, TAG_DONE)) {
                        if(actrl = AHI_AllocAudio(
                                       AHIA_AudioID, req->ahiam_AudioID,
                                       AHIA_MixFreq, req->ahiam_MixFreq,
                                       AHIA_Channels, 1,
                                       AHIA_Sounds, 2,
                                       AHIA_SoundFunc, &SoundHook,
                                       AHIA_UserData, FindTask(NULL),
                                       TAG_DONE)) {
                            AHI_GetAudioAttrs(AHI_INVALID_ID, actrl,
                                              AHIDB_MaxPlaySamples, &playsamples,
                                              TAG_DONE);
                            AHI_ControlAudio(actrl,
                                             AHIC_MixFreq_Query, &mixfreq,
                                             TAG_DONE);
                            BufferLen = playsamples * 17640 / mixfreq;
                            if(BufferLen < MINBUFFLEN)
                                BufferLen = MINBUFFLEN;

                            Sample0.ahisi_Length = BufferLen;
                            Sample1.ahisi_Length = BufferLen;
                            Sample0.ahisi_Address = AllocVec(BufferLen * 2, MEMF_PUBLIC | MEMF_CLEAR);
                            Sample1.ahisi_Address = AllocVec(BufferLen * 2, MEMF_PUBLIC | MEMF_CLEAR);

                            if(Sample0.ahisi_Address && Sample1.ahisi_Address) {
                                if((!(AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &Sample0, actrl))) &&
                                        (!(AHI_LoadSound(1, AHIST_DYNAMICSAMPLE, &Sample1, actrl)))) {
                                    if((signal = AllocSignal(-1)) != -1) {
                                        if(!(AHI_ControlAudio(actrl,
                                                              AHIC_Play, TRUE,
                                                              TAG_DONE))) {
// Everything is set up now. Let's load the buffers and start rockin'...
                                            Read(Input(), Sample0.ahisi_Address, BufferLen * 2);

// The new AHI_PlayA() function is demonstrated...
                                            AHI_Play(actrl,
                                                     AHIP_BeginChannel, 0,
                                                     AHIP_Freq, 17640,
                                                     AHIP_Vol, 0x10000L,
                                                     AHIP_Pan, 0x8000L,
                                                     AHIP_Sound, 0,
                                                     AHIP_Offset, 0,
                                                     AHIP_Length, 0,
                                                     AHIP_EndChannel, NULL,
                                                     TAG_DONE);
                                            /* These functions were available in V2, too.
                                                                  AHI_SetFreq(0,17640,actrl,AHISF_IMM);
                                                                  AHI_SetVol(0,0x10000L,0x8000L,actrl,AHISF_IMM);
                                                                  AHI_SetSound(0,0,0,0,actrl,AHISF_IMM);
                                            */
                                            for(;;) {
                                                signals = Wait((1L << signal) | SIGBREAKF_CTRL_C);
                                                if(signals & SIGBREAKF_CTRL_C)
                                                    break;

                                                if(DBflag) {
                                                    readsamples = Read(Input(), Sample1.ahisi_Address, BufferLen * 2) / 2;
                                                    if(readsamples < BufferLen) {
                                                        // Clear rest of buffer
                                                        for(i = readsamples; i < BufferLen; i++)
                                                            ((WORD *)Sample1.ahisi_Address)[i] = 0;
                                                        Wait(1L << signal);
                                                        // Clear other buffer
                                                        for(i = 0; i < BufferLen; i++)
                                                            ((WORD *)Sample0.ahisi_Address)[i] = 0;
                                                        break;
                                                    }
                                                } else {
                                                    readsamples = Read(Input(), Sample0.ahisi_Address, BufferLen * 2) / 2;
                                                    if(readsamples < BufferLen) {
                                                        // Clear rest of buffer
                                                        for(i = readsamples; i < BufferLen; i++)
                                                            ((WORD *)Sample0.ahisi_Address)[i] = 0;
                                                        Wait(1L << signal);
                                                        // Clear other buffer
                                                        for(i = 0; i < BufferLen; i++)
                                                            ((WORD *)Sample1.ahisi_Address)[i] = 0;
                                                        break;
                                                    }
                                                }


                                            }
                                            if(signals & SIGBREAKF_CTRL_C)
                                                Printf("***Break\n");
                                            else
                                                Wait(1L << signal); // Wait for half-loaded buffer to finish.

                                            AHI_ControlAudio(actrl,
                                                             AHIC_Play, FALSE,
                                                             TAG_DONE);
                                        }
                                        FreeSignal(signal);
                                        signal = -1;
                                    }

                                } else
                                    Printf("Cannot intialize sample buffers\n");
                            } else
                                Printf("Out of memory.\n");

                            FreeVec(Sample0.ahisi_Address);
                            FreeVec(Sample1.ahisi_Address);
                            Sample0.ahisi_Address = NULL;
                            Sample1.ahisi_Address = NULL;
                            AHI_FreeAudio(actrl);
                            actrl = NULL;
                        } else
                            Printf("Unable to allocate sound hardware\n");
                    } else {
                        if(IoErr() EQ NULL)
                            Printf("Aborted.\n");
                        else if(IoErr() EQ ERROR_NO_MORE_ENTRIES)
                            Printf("No available audio modes.\n");
                        else if(IoErr() EQ ERROR_NO_FREE_STORE)
                            Printf("Out of memory.\n");
                    }

                    AHI_FreeAudioRequest(req);
                    req = NULL;
                } else
                    Printf("Out of memory.\n");

                CloseDevice((struct IORequest *)AHIio);
                AHIDevice = -1; // Good habit, IMHO.
            }
            DeleteIORequest((struct IORequest *)AHIio);
            AHIio = NULL;
        }
        DeleteMsgPort(AHImp);
        AHImp = NULL;
    }
}
/* Simple sample player for AHI using the low-level API.
   Hardcoded sample names.

   This software is Public Domain. */

#include <devices/ahi.h>
#include <exec/exec.h>
#include <proto/ahi.h>
#include <proto/dos.h>
#include <proto/exec.h>

#define USE_AHI_V4 TRUE

#define CHANNELS   2
#define MAXSAMPLES 16

#define INT_FREQ   50

char *ID = "$VER: SimplePlay 1.3 (23.4.97)\r\n";

APTR samples[MAXSAMPLES] = { 0 };

struct {
    BOOL      FadeVolume;
    Fixed     Volume;
    sposition Position;
} channelstate[CHANNELS];

struct {
    struct AHIEffDSPMask mask;
    UBYTE                mode[CHANNELS];
} maskeffect = {0};
struct AHIEffDSPEcho   echoeffect = {0};

struct Library      *AHIBase;
struct MsgPort      *AHImp     = NULL;
struct AHIRequest   *AHIio     = NULL;
BYTE                 AHIDevice = -1;
struct AHIAudioCtrl *actrl     = NULL;

LONG mixfreq = 0;

/* Prototypes */

BOOL  OpenAHI(void);
void  CloseAHI(void);
BOOL  AllocAudio(void);
void  FreeAudio(void);
UWORD LoadSample(unsigned char *, ULONG);
void  UnloadSample(UWORD);
int   main(void);

/******************************************************************************
** PlayerFunc *****************************************************************
******************************************************************************/

__asm __interrupt __saveds static void PlayerFunc(
    register __a0 struct Hook *hook,
    register __a2 struct AHIAudioCtrl *actrl,
    register __a1 APTR ignored)
{

    int i;

    for(i = 0; i < CHANNELS; i++) {

        if(channelstate[i].FadeVolume) {

            channelstate[i].Volume = (channelstate[i].Volume * 90) / 100; // Fade volume

            if(channelstate[i].Volume == 0) {
                channelstate[i].FadeVolume = FALSE;
            }

            AHI_SetVol(i, channelstate[i].Volume, channelstate[i].Position,
                       actrl, AHISF_IMM);
        }
    }
    return;
}

struct Hook PlayerHook = {
    0, 0,
    (ULONG(*)()) PlayerFunc,
    NULL,
    NULL,
};

/******************************************************************************
**** OpenAHI ******************************************************************
******************************************************************************/

/* Open the device for low-level usage */

BOOL OpenAHI(void)
{

    if(AHImp = CreateMsgPort()) {
        if(AHIio = (struct AHIRequest *)CreateIORequest(
                       AHImp, sizeof(struct AHIRequest))) {

#if USE_AHI_V4
            AHIio->ahir_Version = 4;
#else
            AHIio->ahir_Version = 2;
#endif

            if(!(AHIDevice = OpenDevice(AHINAME, AHI_NO_UNIT,
                                        (struct IORequest *) AHIio, NULL))) {
                AHIBase = (struct Library *) AHIio->ahir_Std.io_Device;
                return TRUE;
            }
        }
    }
    FreeAudio();
    return FALSE;
}


/******************************************************************************
**** CloseAHI *****************************************************************
******************************************************************************/

/* Close the device */

void CloseAHI(void)
{

    if(! AHIDevice)
        CloseDevice((struct IORequest *)AHIio);
    AHIDevice = -1;
    DeleteIORequest((struct IORequest *)AHIio);
    AHIio = NULL;
    DeleteMsgPort(AHImp);
    AHImp = NULL;
}


/******************************************************************************
**** AllocAudio ***************************************************************
******************************************************************************/

/* Ask user for an audio mode and allocate it */

BOOL AllocAudio(void)
{
    struct AHIAudioModeRequester *req;
    BOOL   rc = FALSE;

    req = AHI_AllocAudioRequest(
              AHIR_PubScreenName, NULL,
              AHIR_TitleText,     "Select a mode and rate",
              AHIR_DoMixFreq,     TRUE,
              TAG_DONE);

    if(req) {
        if(AHI_AudioRequest(req, TAG_DONE)) {
            actrl = AHI_AllocAudio(
                        AHIA_AudioID,         req->ahiam_AudioID,
                        AHIA_MixFreq,         req->ahiam_MixFreq,
                        AHIA_Channels,        CHANNELS,
                        AHIA_Sounds,          MAXSAMPLES,
                        AHIA_PlayerFunc,      &PlayerHook,
                        AHIA_PlayerFreq,      INT_FREQ << 16,
                        AHIA_MinPlayerFreq,   INT_FREQ << 16,
                        AHIA_MaxPlayerFreq,   INT_FREQ << 16,
                        TAG_DONE);
            if(actrl) {
                // Get real mixing frequency
                AHI_ControlAudio(actrl, AHIC_MixFreq_Query, &mixfreq, TAG_DONE);
                rc = TRUE;
            }
        }
        AHI_FreeAudioRequest(req);
    }
    return rc;
}


/******************************************************************************
**** FreeAudio ****************************************************************
******************************************************************************/

/* Release the audio hardware */

void FreeAudio()
{

    AHI_FreeAudio(actrl);
    actrl = NULL;
}

/******************************************************************************
**** LoadSample ***************************************************************
******************************************************************************/

/* Load a (raw) 8 or 16 bit sample from disk. The sample ID is returned
   (or AHI_NOSOUND on error). */

UWORD LoadSample(char *filename, ULONG type)
{
    struct AHISampleInfo sample;
    APTR *samplearray = samples;
    UWORD id = 0, rc = AHI_NOSOUND;
    BPTR file;

    // Find a free sample slot

    while(*samplearray) {
        id++;
        samplearray++;
        if(id >= MAXSAMPLES) {
            return AHI_NOSOUND;
        }
    }

    file = Open(filename, MODE_OLDFILE);

    if(file) {
        int length;

        Seek(file, 0, OFFSET_END);
        length = Seek(file, 0, OFFSET_BEGINNING);
        *samplearray = AllocVec(length, MEMF_PUBLIC);
        if(*samplearray) {
            Read(file, *samplearray, length);

            sample.ahisi_Type = type;
            sample.ahisi_Address = *samplearray;
#if USE_AHI_V4
            sample.ahisi_Length = length / AHI_SampleFrameSize(type);
#else
            sample.ahisi_Length = length / (type == AHIST_M16S ? 2 : 1);
#endif
            if(! AHI_LoadSound(id, AHIST_SAMPLE, &sample, actrl)) {
                rc = id;
            }
        }
        Close(file);
    }
    return rc;
}


/******************************************************************************
**** UnloadSample *************************************************************
******************************************************************************/

void UnloadSample(UWORD id)
{

    AHI_UnloadSound(id, actrl);
    FreeVec(samples[id]);
    samples[id] = NULL;
}

/******************************************************************************
**** main *********************************************************************
******************************************************************************/

int main()
{
    UWORD sample, ahem, louise;
    int i;

    for(i = 0; i < CHANNELS; i++) {
        channelstate[i].FadeVolume = FALSE;
    }

    if(OpenAHI()) {
        if(AllocAudio()) {
            ahem   = LoadSample("Projekt:ahi/samples/ASS-14.sb", AHIST_M8S);
            louise = LoadSample("Projekt:ahi/samples/LouiseR.sw", AHIST_M16S);
            if((ahem != AHI_NOSOUND) && (louise != AHI_NOSOUND)) {

                // Start feeding samples to sound hardware
                if(!(AHI_ControlAudio(actrl,
                                      AHIC_Play, TRUE,
                                      TAG_DONE))) {

#if USE_AHI_V4
                    Printf("Playing two samples, one with echo and one without.\n");

                    // Turn on echo on channel 1
                    // No error checking here, but you should do that.
                    maskeffect.mask.ahie_Effect = AHIET_DSPMASK;
                    maskeffect.mask.ahiedm_Channels = CHANNELS;
                    maskeffect.mode[0] = AHIEDM_DRY;
                    maskeffect.mode[1] = AHIEDM_WET;
                    AHI_SetEffect(&maskeffect, actrl);

                    echoeffect.ahie_Effect     = AHIET_DSPECHO;
                    echoeffect.ahiede_Delay    = mixfreq / 4; // 250 ms
                    echoeffect.ahiede_Feedback = 0x8000;      // 50 %
                    echoeffect.ahiede_Mix      = 0x10000;     // 100% and...
                    echoeffect.ahiede_Cross    = 0;           // ...0% gives faster echo code.
                    AHI_SetEffect(&echoeffect, actrl);

                    AHI_Play(actrl,
                             // A forever-looping sample on channel 0
                             AHIP_BeginChannel,  0,
                             AHIP_Freq,          17640,
                             AHIP_Vol,           0x10000,
                             AHIP_Pan,           0xc000,
                             AHIP_Sound,         louise,
                             AHIP_EndChannel,    NULL,

                             // A oneshot sample on channel 1
                             AHIP_BeginChannel,  1,
                             AHIP_Freq,          22254,
                             AHIP_Vol,           0x10000,
                             AHIP_Pan,           0x4000,
                             AHIP_Sound,         ahem,
                             AHIP_LoopSound,     AHI_NOSOUND,
                             AHIP_EndChannel,    NULL,

                             TAG_DONE);
#else
                    Printf("Playing two samples.\n");

                    // A forever-looping sample on channel 0
                    AHI_SetFreq(0, 17640, actrl, AHISF_IMM);
                    AHI_SetVol(0, 0x10000, 0xc000, actrl, AHISF_IMM);
                    AHI_SetSound(0, louise, 0, 0, actrl, AHISF_IMM);

                    // A oneshot sample on channel 1
                    AHI_SetFreq(1, 22254, actrl, AHISF_IMM);
                    AHI_SetVol(1, 0x10000, 0x4000, actrl, AHISF_IMM);
                    AHI_SetSound(1, ahem, 0, 0, actrl, AHISF_IMM);
                    AHI_SetSound(1, AHI_NOSOUND, 0, 0, actrl, 0);
#endif
                    channelstate[0].Volume   = 0x10000;
                    channelstate[0].Position = 0xc000;
                    channelstate[1].Volume   = 0x10000;
                    channelstate[1].Position = 0x4000;

                    // Wait 5 seconds

                    Delay(5 * TICKS_PER_SECOND);

#if USE_AHI_V4
                    Printf("Turning on echo on channel 0.\n");

                    // Turn on echo on channel 0, turn off channel 1
                    // No error checking here, but you should do that.
                    maskeffect.mode[0] = AHIEDM_WET;
                    maskeffect.mode[1] = AHIEDM_DRY;
                    AHI_SetEffect(&maskeffect, actrl);
#endif

                    // Wait 5 seconds

                    Delay(5 * TICKS_PER_SECOND);

                    Printf("Fading away the sound on channel 0... ");
                    Flush(Output());

                    // Fade away channel 0

                    channelstate[0].FadeVolume = TRUE;

                    while(channelstate[0].FadeVolume) {
                        Delay(1);
                    }

                    Printf("Done!\n");

                    // Wait a sec...

                    Delay(TICKS_PER_SECOND);

                    // Stop sounds
                    AHI_ControlAudio(actrl,
                                     AHIC_Play, FALSE,
                                     TAG_DONE);


#if USE_AHI_V4
                    // Cancel effects

                    maskeffect.mask.ahie_Effect = AHIET_DSPMASK | AHIET_CANCEL;
                    AHI_SetEffect(&maskeffect, actrl);

                    echoeffect.ahie_Effect     = AHIET_DSPECHO | AHIET_CANCEL;
                    AHI_SetEffect(&echoeffect, actrl);
#endif

                }
            }

            // Ask AHI to unload all loaded samples and free the memory for them
            for(sample = 0; sample < MAXSAMPLES; sample++) {
                UnloadSample(sample);
            }

        }
        FreeAudio();
    }
    CloseAHI();

    return 0;
}


AmiArcadia also uses AHI and has c src and ScummVM AGA from AmiNet....all the source code is there. To create the AHI callback hook you'll also need to include the SDI header files.


References

[edit | edit source]

Still to be edited and possibly redone...

You may need to supply AHI_MinPlayerFreq & AHI_MaxPlayerFreq?

AHIA_PlayerFreq (Fixed) - If non-zero, enables timing and specifies how many times per second PlayerFunc will be called. This must be specified if AHIA_PlayerFunc is! Do not use any extreme frequencies. The result of MixFreq/PlayerFreq must fit an UWORD, i.e. it must be less or equal to 65535. It is also suggested that you keep the result over 80. For normal use this should not be a problem. Note that the data type is Fixed, not integer. 50 Hz is 50 16.

Default is a reasonable value. Don't depend on it.

AHIA_MinPlayerFreq (Fixed) - The minimum frequency (AHIA_PlayerFreq) you will use. You MUST supply this if you are using the device's interrupt feature!

AHIA_MaxPlayerFreq (Fixed) - The maximum frequency (AHIA_PlayerFreq) you will use. You MUST supply this if you are using the device's interrupt feature!

I don't see anything in the documentation limiting the high-end frequency, just keep in mind that AHI has to be able to finish the callback function completely in time to be able to make it to the next callback. How big is your callback function?

AHI_GetAudioAttrs() should have TAG_DONE

What is AHIR_DoMixFreq? remove AHIR_DoMixFreq tag from AHI_AllocAudio() call, I don't think it should be there.

decode audio to a sample buffer and feed buffer to AHI using normal double buffering method from a subprocess. can AHI buffer the sound in a music interrupt, to play it later? How would I do what you suggest? never used library API, sorry. I always use CMD_WRITE to play the sound. It does not work. Sooner than later io requests go out of sync due to task switching.

Advice about number of channels set up

CMD_FLUSH
CMD_READ
CMD_RESET
CMD_START
CMD_STOP
CMD_WRITE

CloseDevice
NSCMD_DEVICEQUERY
OpenDevice
ahi.device
if(AHI_GetAudioAttrs(AHI_DEFAULT_ID, NULL, AHIDB_BufferLen, 100, AHIDB_Inputs, &num_inputs))
//if(AHI_GetAudioAttrs(AHI_INVALID_ID, Record_AudioCtrl, AHIDB_BufferLen, 100, AHIDB_Inputs, &num_inputs))
{
printf("getaudioattrs worked\n");

STRPTR input_name[num_inputs-1][100];

printf("num inputs is %i\navailable inputs:\n",num_inputs);

for(int a=0; a!=num_inputs; a++)
{
//AHI_GetAudioAttrs(AHI_INVALID_ID, Record_AudioCtrl, AHIDB_BufferLen, 100, AHIDB_InputArg, a, AHIDB_Input, &input_name[a]);
AHI_GetAudioAttrs(AHI_DEFAULT_ID, NULL, AHIDB_BufferLen, 100, AHIDB_InputArg, a, AHIDB_Input, &input_name[a]);
printf("%i: %s\n",a,input_name[a]);
}

//AHI_ControlAudio(Record_AudioCtrl, AHIC_Input, &selected_input, TAG_DONE);
//AHI_ControlAudio(Record_AudioCtrl, AHIC_Input, 1, TAG_DONE);
//AHI_ControlAudio(NULL, AHIC_Input, 1, TAG_DONE);
//AHI_ControlAudio(NULL, AHIC_Input, &selected_input, TAG_DONE);

}
//changed second argument from..

AHIDevice=OpenDevice(AHINAME,0,(struct IORequest *)AHIio,NULL);

//to this..

AHIDevice=OpenDevice(AHINAME,AHI_NO_UNIT,(struct IORequest *)AHIio,NULL); 


AHI library calls

[edit | edit source]

AHI_AllocAudioA

[edit | edit source]
audioctrl = AHI_AllocAudioA( tags );
struct AHIAudioCtrl *AHI_AllocAudioA( struct TagItem * );
audioctrl = AHI_AllocAudio( tag1, ... );
struct AHIAudioCtrl *AHI_AllocAudio( Tag, ... );

AHI_AllocAudioRequestA

[edit | edit source]
requester = AHI_AllocAudioRequestA( tags );
struct AHIAudioModeRequester *AHI_AllocAudioRequestA(struct TagItem * );
requester = AHI_AllocAudioRequest( tag1, ... );
struct AHIAudioModeRequester *AHI_AllocAudioRequest( Tag, ... );

AHI_AudioRequestA

[edit | edit source]
success = AHI_AudioRequestA( requester, tags );
BOOL AHI_AudioRequestA( struct AHIAudioModeRequester *, struct TagItem * );
result = AHI_AudioRequest( requester, tag1, ... );
BOOL AHI_AudioRequest( struct AHIAudioModeRequester *, Tag, ... );

AHI_BestAudioIDA

[edit | edit source]
ID = AHI_BestAudioIDA( tags );
ULONG AHI_BestAudioIDA( struct TagItem * );
ID = AHI_BestAudioID( tag1, ... );
ULONG AHI_BestAudioID( Tag, ... );

AHI_ControlAudioA

[edit | edit source]
error = AHI_ControlAudioA( audioctrl, tags );
ULONG AHI_ControlAudioA( struct AHIAudioCtrl *, struct TagItem * );
error = AHI_ControlAudio( AudioCtrl, tag1, ...);
ULONG AHI_ControlAudio( struct AHIAudioCtrl *, Tag, ... );

AHI_FreeAudio

[edit | edit source]
AHI_FreeAudio( audioctrl );
void AHI_FreeAudio( struct AHIAudioCtrl * );

AHI_FreeAudioRequest

[edit | edit source]
AHI_FreeAudioRequest( requester );
void AHI_FreeAudioRequest( struct AHIAudioModeRequester * );

AHI_GetAudioAttrsA

[edit | edit source]
success = AHI_GetAudioAttrsA( ID, [audioctrl], tags );
BOOL AHI_GetAudioAttrsA( ULONG, struct AHIAudioCtrl *, struct TagItem * );
success = AHI_GetAudioAttrs( ID, [audioctrl], attr1, &result1, ...);
BOOL AHI_GetAudioAttrs( ULONG, struct AHIAudioCtrl *, Tag, ... );

AHI_LoadSound

[edit | edit source]
error = AHI_LoadSound( sound, type, info, audioctrl );
ULONG AHI_LoadSound( UWORD, ULONG, IPTR, struct AHIAudioCtrl * );

AHI_NextAudioID

[edit | edit source]
next_ID = AHI_NextAudioID( last_ID );
ULONG AHI_NextAudioID( ULONG );

AHI_PlayA

[edit | edit source]
AHI_PlayA( audioctrl, tags );
void AHI_PlayA( struct AHIAudioCtrl *, struct TagItem * );
AHI_Play( AudioCtrl, tag1, ...);
void AHI_Play( struct AHIAudioCtrl *, Tag, ... );

AHI_SampleFrameSize

[edit | edit source]
size = AHI_SampleFrameSize( sampletype );
ULONG AHI_SampleFrameSize( ULONG );

AHI_SetEffect

[edit | edit source]
error = AHI_SetEffect( effect, audioctrl );
ULONG AHI_SetEffect( IPTR, struct AHIAudioCtrl * );

AHI_SetFreq

[edit | edit source]
AHI_SetFreq( channel, freq, audioctrl, flags );
void AHI_SetFreq( UWORD, ULONG, struct AHIAudioCtrl *, ULONG );

AHI_SetSound

[edit | edit source]
AHI_SetSound( channel, sound, offset, length, audioctrl, flags );
void AHI_SetSound( UWORD, UWORD, ULONG, LONG, struct AHIAudioCtrl *, ULONG );

AHI_SetVol

[edit | edit source]
AHI_SetVol( channel, volume, pan, audioctrl, flags );
void AHI_SetVol( UWORD, Fixed, sposition, struct AHIAudioCtrl *, ULONG );

AHI_UnloadSound

[edit | edit source]
AHI_UnloadSound( sound, audioctrl );
void AHI_UnloadSound( UWORD, struct AHIAudioCtrl * );