# Aros/Developer/AHIDrivers

* 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
* misc
* Aros Public License

## Retargetable Audio Devices

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. More information is included with the AHI Developer files which you can download from the AHI homepage or Aminet.
Wikipedia page
Amiga Sourceforge DevHelp
Devices
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.
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.

### Library Approach

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.

### Device Approach

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.
Then, when i need , i just do for sound (which will be played on second channel (it's only solution to make it works at the same time by ahi):
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.
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.
http://utilitybase.com/forum/index.php?action=vthread&forum=201&topic=1565&page=-1
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.
see here thread

### Setting up

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

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

* i call AHI_ControlAudion with play to false so that i make sure nothing is being played
* i unload the sounds using AHI_UnloadSound to make sure sounds get unloaded
* then i call AHI_FreeAudio

### Closing down

Once you have finished with the AHI device, you need to close down the device. e.g.
* Does a CloseDevice
* then DeletIORequest
* and last DeleteMsgPort

### Updating Sound often

Take a look at the simpleplay example.
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.
Please take a look at the PlaySineEverywhere.c example in the AHI developer archive.

### Misc

There are a number of things that are called "latency." The thing that concerns me most is the time between when audio (like a microphone) hits the input and when it comes out the monitor output. You can measure this by putting something with a short rise time (a cross stick sound is good) into one channel, and connect the output of that channel to the input of another channel. Record a few seconds on both channels. Stop the recording, zoom in on the waveform of the two channels, and measure the time difference between them. That's the input/output latency.
Latency when playing samples is trickier because it depends on the program that's supporting the VST instrument. If you have a MIDI keyboard with sounds you could choose a similar sound on the keyboard and from the VST library, connect the analog output of the sample playback channel to one input, connect the synth output to another input, play your sound, record it to two tracks, and look at the time difference between the tracks. That's not totally accurate but it will get you a ballpark measurement.
If you do the following literally (in your code):
you have to unload/free every used or not used but allocated channels/sounds.
For example something like this will loop around until the last allocated channel.
For(chan_no=0;chan_no<num_of_channels;chan_no++) { If(channel[chan_no]) free(channel[chan_no]); }
Maybe If(channel[chan_no]!=NULL)
To make certain you can NULL every sound bank before exit.

## Examples

Another example.
Double-buffering is required though.
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 PlaySnd function simply interrupts the one that has been playing for longest with AbortIO/WaitIO and then reuses that one.
Low level for music playback
use the functions AHI_SetVol to set volume, AHI_SetFreq to set frequency, AHI_SetSound to play the sounds.

## Other Examples

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. When I construct the AHI request, I multiply this number by 0x00010000 to convert it to the fixed value. If I use this code as part of a DOS background process, I 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 I 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

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

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