amiga-bootcamp/11_libraries/ahi_programming.md
Ilia Sharin f8f8d1c834 docs(amiga): add Tier 4 content — AHI, cross-compilation, RTG, demoscene section
- New: 11_libraries/ahi_programming.md — AHI retargetable audio API
- New: 13_toolchain/cross_compilation_guide.md — cross-compiling for Amiga
- New: 08_graphics/rtg_programming.md — RTG Picasso96/CyberGraphX programming
- New: 17_demoscene/ — full demoscene techniques section:
  - copper_effects.md (6 techniques, 10 Pouet screenshots, antipatterns)
  - sprite_techniques.md (5 techniques, antipatterns)
  - pixel_tricks.md (5 techniques, antipatterns)
  - 3d_rendering.md (fixed-point math, 4 techniques, antipatterns)
  - timing_optimization.md (7 techniques, instruction timing tables)
  - README.md (section index with Mermaid diagrams)
  - images/ (10 authentic Amiga screenshots from Pouet.net)
- New: 05_reversing/games/ (4 copper-analysis screenshots)
- Updated: README index, TODO status (30/30 complete)
- Added external references: Pouet/Demozoo links, Scoopex YouTube
  tutorial series, Amiga Graphics Archive, coppershade.org
2026-05-13 17:49:28 -04:00

24 KiB
Raw Blame History

← Home · Libraries

AHI — Audio Hardware Interface Programming

What Is AHI?

AHI (Audio Hardware Interface) is AmigaOS's retargetable audio system. Introduced in 1996 by Martin Blom, it solves a fundamental problem: Paula has only 4 hardware audio channels at 8-bit resolution, locked to Chip RAM. AHI provides a hardware-agnostic API for 16-bit (and higher) multi-channel audio with software mixing, supporting Paula, sound cards (Delfina, Prisma Megamix, X-Surf), FPGA audio cores, and USB audio devices through a uniform interface.

AHI is to audio what RTG (Picasso96/CyberGraphX) is to graphics — a retargetable abstraction layer that decouples applications from hardware specifics.

graph TB
    subgraph "Application Layer"
        APP["Application"]
        MUI["MUI Sound Class"]
        DT["Datatypes Sound"]
    end

    subgraph "AHI System"
        AHIDEV["ahi.device<br/>OpenDevice / CMD_WRITE"]
        MIXER["AHI Software Mixer<br/>CallHookPkt MixerFunc"]
        CTRL["AHIAudioCtrl<br/>Channel management"]
    end

    subgraph "AHI Drivers"
        PAULA["paula.audio<br/>4× 8-bit DMA"]
        HIFI["paula.audio (HiFi)<br/>14-bit calibrated"]
        SOUNDCARD["Delfina / Prisma<br/>16-bit hardware"]
        FPGA["FPGA Audio Core<br/>TOSLink / I2S"]
        USB["USB Audio Class<br/>via Poseidon"]
    end

    APP --> AHIDEV
    MUI --> AHIDEV
    DT --> AHIDEV
    AHIDEV --> CTRL
    CTRL --> MIXER
    MIXER --> PAULA
    MIXER --> HIFI
    MIXER --> SOUNDCARD
    MIXER --> FPGA
    MIXER --> USB

    style AHIDEV fill:#e8f4fd,stroke:#2196f3,color:#333
    style MIXER fill:#fff3e0,stroke:#ff9800,color:#333

Why AHI Matters

Problem Without AHI With AHI
> 4 channels Manual software mixing — every app reinvents it Built-in N-channel mixer
16-bit audio Paula can't do it natively 16-bit playback on any hardware
Hardware independence Apps hard-coded to Paula registers Apps use AHI API, driver handles hardware
Recording Only via Paula's limited ADC Any recording-capable AHI device
Multi-app audio One app owns Paula — others get silence AHI arbitrates between applications

Architecture

Audio Pipeline

sequenceDiagram
    participant APP as Application
    participant AHI as ahi.device
    participant CTRL as AudioCtrl
    participant MIX as Software Mixer
    participant DRV as AHI Driver
    participant HW as Audio Hardware

    APP->>AHI: OpenDevice("ahi.device", unit, ioreq, 0)
    APP->>AHI: AHI_ControlAudio(tags: channels, freq, etc.)
    AHI->>CTRL: Allocate channels, configure
    APP->>AHI: AHI_LoadSound(sampleID, format, data)
    AHI->>DRV: AHIsub_LoadSound (prepare for hardware)
    APP->>AHI: AHI_Play(freq, vol, pan)
    AHI->>CTRL: Schedule sound on channel
    loop Every mixing period
        AHI->>MIX: CallHookPkt(MixerFunc)
        MIX->>MIX: Mix all active channels
        MIX->>DRV: Feed PCM buffer
        DRV->>HW: DMA / I2S / USB transfer
    end
    APP->>AHI: AHI_Stop()
    APP->>AHI: CloseDevice()

Component Map

Component Type Description
ahi.device Device Exec device — opened with OpenDevice()
AHIAudioCtrl Structure Controls channel allocation, mixing, format
AHI_Request IORequest Standard exec I/O request for device commands
Mixer Hook Hook AHI calls this to produce mixed PCM buffers
Player Hook Hook AHI calls this to advance module position (for trackers)
*.audio driver Shared library Hardware-specific driver in DEVS:AHI/

API Reference

Device Opening and Control

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

struct MsgPort *AHImp = NULL;
struct AHIRequest *AHIio = NULL;
struct AHIAudioCtrl *AudioCtrl = NULL;
BYTE AHIDevice = -1;

BOOL InitAHI(void)
{
    AHImp = CreateMsgPort();
    if (!AHImp) return FALSE;

    AHIio = (struct AHIRequest *)CreateIORequest(
        AHImp, sizeof(struct AHIRequest));

    if (!AHIio) { DeleteMsgPort(AHImp); return FALSE; }

    AHIio->ahir_Version = 4;  /* Request AHI v4+ */

    AHIDevice = OpenDevice(AHINAME, 0, (struct IORequest *)AHIio, 0);
    if (AHIDevice)
    {
        /* AHI not available — fall back to audio.device */
        DeleteIORequest((struct IORequest *)AHIio);
        DeleteMsgPort(AHImp);
        return FALSE;
    }

    return TRUE;
}

Audio Control — AHI_ControlAudio()

Configures the audio session (channels, sample rate, format):

/* Create audio control structure with desired parameters */
AudioCtrl = AHI_AllocAudio(
    AHIA_AudioID,     AHI_DEFAULT_ID,
    AHIA_MixFreq,     44100,
    AHIA_Channels,    4,           /* 4 simultaneous sounds */
    AHIA_Sounds,      16,          /* up to 16 loaded samples */
    AHIA_SampleType,  AHIST_M16S,  /* 16-bit mono signed */
    AHIA_PlayerFunc,  NULL,        /* no player hook (non-module) */
    AHIA_PlayerFreq,  50,          /* mixing frequency (Hz) */
    AHIA_MinMixFreq,  8000,
    TAG_DONE);

if (!AudioCtrl)
{
    /* Fallback: try fewer channels or lower sample rate */
    AudioCtrl = AHI_AllocAudio(
        AHIA_AudioID,    AHI_DEFAULT_ID,
        AHIA_MixFreq,    22050,
        AHIA_Channels,   2,
        AHIA_Sounds,     8,
        AHIA_SampleType, AHIST_M16S,
        TAG_DONE);
}

Sample Types

Constant Value Description
AHIST_M8S 0 8-bit mono signed
AHIST_M16S 2 16-bit mono signed (most common)
AHIST_S8S 4 8-bit stereo signed
AHIST_S16S 6 16-bit stereo signed
AHIST_M32S 10 32-bit mono signed
AHIST_S32S 14 32-bit stereo signed
AHIST_M16S_SYS 16-bit mono, system byte order
AHIST_S16S_SYS 16-bit stereo, system byte order

Loading and Playing Sounds

/* Define sample IDs */
enum {
    SND_JUMP = 0,
    SND_COIN,
    SND_EXPLODE,
    SND_MUSIC,
    SOUND_COUNT
};

/* Load a sample from memory */
APTR sampleData = LoadIFFSample("SYS:Sounds/jump.8svx");
ULONG sampleLength = GetSampleLength();

AHI_LoadSound(SND_JUMP,
    AHIST_M16S,          /* sample type */
    sampleData,           /* pointer to PCM data */
    sampleLength * 2,     /* number of BYTES (not samples!) */
    AudioCtrl);

/* Play the sound on channel 0 */
AHI_Play(AudioCtrl,
    AHIP_BeginChannel,  0,
    AHIP_Freq,          44100,      /* playback frequency */
    AHIP_Vol,           0x10000,    /* full volume (fixed-point 16.16) */
    AHIP_Pan,           0x8000,     /* center (0=left, 0x10000=right) */
    AHIP_Sound,         SND_JUMP,
    AHIP_EndChannel,    0,
    TAG_DONE);

/* Stop sound on channel 0 */
AHI_Play(AudioCtrl,
    AHIP_BeginChannel,  0,
    AHIP_Sound,         AHI_NOSOUND,
    AHIP_EndChannel,    0,
    TAG_DONE);

Volume and Panning

/* Volume: fixed-point 16.16 format */
/* 0x00000 = silence, 0x10000 = full volume, 0x20000 = +6dB boost */
AHI_SetVol(0, 0x10000, 0x8000, AudioCtrl, AHISF_IMM);

/* Pan: fixed-point 16.16 format */
/* 0x00000 = hard left, 0x8000 = center, 0x10000 = hard right */
AHI_SetVol(0, 0x8000, 0x00000, AudioCtrl, AHISF_IMM);  /* left only */

Setting Frequency

/* Change playback frequency of a running sound */
AHI_SetFreq(0, 22050, AudioCtrl, AHISF_IMM);

/* Play a sample at double speed (pitch shift) */
AHI_SetFreq(0, 88200, AudioCtrl, AHISF_IMM);

Cleanup

void CleanupAHI(void)
{
    if (AudioCtrl)
    {
        /* Stop all sounds first */
        for (int ch = 0; ch < 4; ch++)
            AHI_Play(AudioCtrl,
                AHIP_BeginChannel, ch,
                AHIP_Sound, AHI_NOSOUND,
                AHIP_EndChannel, ch,
                TAG_DONE);

        AHI_FreeAudio(AudioCtrl);
        AudioCtrl = NULL;
    }

    if (!AHIDevice)
    {
        CloseDevice((struct IORequest *)AHIio);
        AHIDevice = -1;
    }

    if (AHIio)
    {
        DeleteIORequest((struct IORequest *)AHIio);
        AHIio = NULL;
    }

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

Audio Mode Selection

Decision Guide

graph TD
    START["Need audio playback?"] --> Q1{"Need > 4 channels\\nor 16-bit?"}
    Q1 -->|No| Q2{"Target is stock\\nA500/A1200?"}
    Q1 -->|Yes| AHI["Use AHI\\n16-bit, N channels"]
    Q2 -->|Yes| PAULA["Use audio.device\\nor direct Paula"]
    Q2 -->|No| Q3{"Sound card\\navailable?"}
    Q3 -->|Yes| AHI
    Q3 -->|No| Q4{"Need recording?"}
    Q4 -->|Yes| AHI
    Q4 -->|No| PAULA

    style AHI fill:#e8f5e9,stroke:#4caf50,color:#333
    style PAULA fill:#fff3e0,stroke:#ff9800,color:#333

Audio Mode Comparison

Mode Channels Bit Depth Mixing Freq Latency Notes
audio.device 4 (hardware) 8-bit signed 2835 kHz max ~1 DMA cycle Native Paula — zero CPU for playback
AHI paula.audio 416 (software) 8/16-bit 800048000 Hz ~120 ms Software mixed, output via Paula
AHI HiFi 14-bit 2 (stereo) 14-bit 44100 Hz ~1020 ms Uses all 4 Paula channels for stereo
AHI Delfina 1664 16/24-bit 4410096000 ~5 ms Hardware DSP mixing
AHI Prisma Megamix 816 16/24-bit 44100192000 ~5 ms FPGA-based, I2S output
AHI USB Audio 28 16/24-bit 4410096000 ~10 ms Via Poseidon USB stack

Practical Cookbook

Cookbook 1: Simple Sound Effects Player

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

struct AHIAudioCtrl *ctrl;
APTR sounds[16];
ULONG soundLengths[16];

BOOL InitSoundSystem(void)
{
    ctrl = AHI_AllocAudio(
        AHIA_AudioID,    AHI_DEFAULT_ID,
        AHIA_MixFreq,    44100,
        AHIA_Channels,   4,
        AHIA_Sounds,     16,
        AHIA_SampleType, AHIST_M16S,
        TAG_DONE);
    return (ctrl != NULL);
}

BOOL LoadSound(ULONG id, const char *filename)
{
    /* Load raw 16-bit signed PCM from file */
    BPTR fh = Open(filename, MODE_OLDFILE);
    if (!fh) return FALSE;

    ULONG size = Seek(fh, 0, OFFSET_END);
    Seek(fh, 0, OFFSET_BEGINNING);

    APTR data = AllocMem(size, MEMF_ANY);
    if (!data) { Close(fh); return FALSE; }

    Read(fh, data, size);
    Close(fh);

    sounds[id] = data;
    soundLengths[id] = size;

    AHI_LoadSound(id, AHIST_M16S, data, size / 2, ctrl);
    return TRUE;
}

void PlaySFX(ULONG id, int channel, ULONG freq, ULONG volume)
{
    AHI_Play(ctrl,
        AHIP_BeginChannel, channel,
        AHIP_Freq,         freq,
        AHIP_Vol,          volume,
        AHIP_Pan,          0x8000,     /* center */
        AHIP_Sound,        id,
        AHIP_EndChannel,   channel,
        TAG_DONE);
}

void StopSFX(int channel)
{
    AHI_Play(ctrl,
        AHIP_BeginChannel, channel,
        AHIP_Sound,        AHI_NOSOUND,
        AHIP_EndChannel,   channel,
        TAG_DONE);
}

void CleanupSoundSystem(void)
{
    if (ctrl)
    {
        AHI_FreeAudio(ctrl);
        ctrl = NULL;
    }
    for (int i = 0; i < 16; i++)
    {
        if (sounds[i])
        {
            FreeMem(sounds[i], soundLengths[i]);
            sounds[i] = NULL;
        }
    }
}

Cookbook 2: Streaming Playback (Music / Module Player)

For continuous audio streaming (playing modules, MP3, WAV), use the Player and Mixer hooks:

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

#define BUFFER_SAMPLES 4096
#define NUM_BUFFERS    2

struct AHIAudioCtrl *ctrl;
struct Hook playerHook;
APTR buffers[NUM_BUFFERS];
int currentBuffer = 0;

/* Player hook — called at PlayerFreq rate.
   Fill the next buffer with decoded audio. */
LONG ASM PlayerFunc(REG(a0, struct Hook *hook),
                    REG(a2, struct AHIAudioCtrl *ctrl),
                    REG(a1, void *unused))
{
    APTR buf = buffers[currentBuffer];

    /* Decode next BUFFER_SAMPLES of audio into buf.
       This is where you'd call your MP3 decoder, MOD player, etc. */
    DecodeNextChunk(buf, BUFFER_SAMPLES);

    /* Tell AHI which buffer to play next */
    AHI_SetSound(0, AHI_NOSOUND, 0, 0, ctrl, 0);
    AHI_LoadSound(0, AHIST_S16S, buf, BUFFER_SAMPLES, ctrl);
    AHI_Play(ctrl,
        AHIP_BeginChannel, 0,
        AHIP_Freq,         44100,
        AHIP_Vol,          0x10000,
        AHIP_Pan,          0x8000,
        AHIP_Sound,        0,
        AHIP_EndChannel,   0,
        TAG_DONE);

    currentBuffer = 1 - currentBuffer;  /* ping-pong */
    return 0;
}

void StartStreaming(void)
{
    /* Allocate double-buffer */
    for (int i = 0; i < NUM_BUFFERS; i++)
        buffers[i] = AllocMem(BUFFER_SAMPLES * 4, MEMF_ANY);

    /* Set up player hook */
    playerHook.h_Entry = (HOOKFUNC)PlayerFunc;
    playerHook.h_Data  = NULL;

    ctrl = AHI_AllocAudio(
        AHIA_AudioID,     AHI_DEFAULT_ID,
        AHIA_MixFreq,     44100,
        AHIA_Channels,    2,
        AHIA_Sounds,      2,
        AHIA_SampleType,  AHIST_S16S,
        AHIA_PlayerFunc,  &playerHook,
        AHIA_PlayerFreq,  50,         /* 50 Hz callback */
        TAG_DONE);
}

void StopStreaming(void)
{
    if (ctrl) AHI_FreeAudio(ctrl);
    for (int i = 0; i < NUM_BUFFERS; i++)
        if (buffers[i]) FreeMem(buffers[i], BUFFER_SAMPLES * 4);
}

Cookbook 3: Enumerating Available Audio Modes

/* List all available AHI audio modes */
void ListAudioModes(void)
{
    struct AHIAudioModeRequester *req = NULL;
    ULONG modeID = AHI_INVALID_ID;

    while ((modeID = AHI_NextAudioID(modeID)) != AHI_INVALID_ID)
    {
        STRPTR name = AHI_GetAudioAttrs(modeID,
            AHIDB_Name,    TAG_DONE);
        STRPTR author = AHI_GetAudioAttrs(modeID,
            AHIDB_Author,  TAG_DONE);
        LONG bits = (LONG)AHI_GetAudioAttrs(modeID,
            AHIDB_Bits,    TAG_DONE);
        LONG chans = (LONG)AHI_GetAudioAttrs(modeID,
            AHIDB_MaxChannels, TAG_DONE);

        Printf("Mode: %s  Author: %s  Bits: %ld  Channels: %ld\n",
            name ? name : "(unknown)",
            author ? author : "(unknown)",
            bits, chans);
    }
}

Named Antipatterns

"The Memory Leak" — Not Freeing Sounds

/* BAD: Loading sounds without freeing them.
   Each AHI_LoadSound allocates driver resources.
   If you load the same sample repeatedly without
   AHI_UnloadSound, you leak driver memory. */
void PlayNewSFX(APTR data, ULONG len)
{
    static int id = 0;
    AHI_LoadSound(id, AHIST_M16S, data, len, ctrl);
    AHI_Play(ctrl, AHIP_BeginChannel, 0,
             AHIP_Freq, 44100, AHIP_Vol, 0x10000,
             AHIP_Sound, id, AHIP_EndChannel, 0, TAG_DONE);
    id++;  /* keeps allocating new sound IDs — never freed! */
}
/* CORRECT: Unload before reloading, or reuse sound IDs */
void PlaySFX(ULONG id, APTR data, ULONG len)
{
    AHI_UnloadSound(id, ctrl);     /* free previous */
    AHI_LoadSound(id, AHIST_M16S, data, len, ctrl);
    AHI_Play(ctrl, AHIP_BeginChannel, 0,
             AHIP_Freq, 44100, AHIP_Vol, 0x10000,
             AHIP_Sound, id, AHIP_EndChannel, 0, TAG_DONE);
}

"The Stale Pointer" — Using Sound Data After Free

/* BAD: AHI does NOT copy your sample data — it uses
   your pointer directly. If you free the data while
   the sound is playing, you feed garbage to the DAC. */
void PlayAndFree(APTR data, ULONG len)
{
    AHI_LoadSound(0, AHIST_M16S, data, len, ctrl);
    AHI_Play(ctrl, AHIP_BeginChannel, 0,
             AHIP_Sound, 0, AHIP_EndChannel, 0, TAG_DONE);
    FreeMem(data, len);  /* AHI still reading from data! */
}
/* CORRECT: Keep data alive until sound finishes.
   Use a notification hook or delay, then free. */
void PlayAndWait(APTR data, ULONG len)
{
    AHI_LoadSound(0, AHIST_M16S, data, len, ctrl);
    AHI_Play(ctrl, AHIP_BeginChannel, 0,
             AHIP_Sound, 0, AHIP_EndChannel, 0, TAG_DONE);

    /* Wait for sound to finish (approximate) */
    ULONG ms = (len / 2) * 1000 / 44100;
    Delay(ms / 20);  /* tick granularity */

    AHI_UnloadSound(0, ctrl);
    FreeMem(data, len);
}

"The Unchecked AllocAudio" — Assuming AHI Is Available

/* BAD: AHI may not be installed, or may not support
   the requested mode. NULL AudioCtrl = crash. */
ctrl = AHI_AllocAudio(AHIA_MixFreq, 48000, TAG_DONE);
AHI_Play(ctrl, ...);  /* crash if ctrl is NULL */
/* CORRECT: Always check, always fall back */
ctrl = AHI_AllocAudio(AHIA_MixFreq, 48000, TAG_DONE);
if (!ctrl)
    ctrl = AHI_AllocAudio(AHIA_MixFreq, 22050, TAG_DONE);
if (!ctrl)
    ctrl = AHI_AllocAudio(AHIA_MixFreq, 11025, TAG_DONE);
if (!ctrl)
{
    /* Fall back to audio.device (Paula) */
    UsePaulaDirectly();
}

"The Volume Overflow" — Exceeding 0x10000

/* BAD: AHI volume is fixed-point 16.16.
   0x10000 = 100% volume. Values above this can
   cause clipping distortion or driver-dependent
   behavior (some drivers clamp, some wrap). */
AHI_SetVol(0, 0x20000, 0x8000, ctrl, AHISF_IMM);  /* 200% — distorted! */
/* CORRECT: Stay within 0x00000 to 0x10000 */
AHI_SetVol(0, 0x10000, 0x8000, ctrl, AHISF_IMM);  /* 100% — clean */

Historical Context & Modern Analogies

Evolution of Amiga Audio

timeline
    title Amiga Audio Evolution
    1985 : OS 1.0 — Paula 4× 8-bit DMA\\naudio.device wraps Paula
    1987 : Soundtracker — 4-channel MOD format\\nDirect Paula register access
    1990 : OS 2.0 — No audio changes\\nTrackers dominate
    1992 : OctaMED — 8-channel mixing\\nSoftware mixing to Paula
    1995 : Play16 — 14-bit Paula hack\\nCalibrated PWM on all channels
    1996 : AHI 1.0 — Retargetable audio\\npaula.audio driver, software mixing
    1998 : AHI 2.x — Recording support\\nDelfina DSP sound card driver
    2000 : AHI 3.x — USB audio\\nPoseidon stack integration
    2005 : AHI 4.x — FPGA audio\\nPrisma Megamix, TOSLink
    2015 : AHI 6.x — 32-bit floating point\\nModern ARM/FPGA platforms

Competitive Landscape

Platform Audio System Max Channels Bit Depth Software Mixing Year
Amiga Paula DMA hardware 4 (hardware) 8-bit No — fixed 4 channels 1985
Amiga AHI Retargetable API 264 8/16/24/32-bit Yes (configurable) 1996
PC Sound Blaster DSP commands 1 (stereo from SB16) 8/16-bit No 1989
PC DirectSound Windows API Unlimited 16-bit Yes 1995
Mac OS Sound Manager OS API 16 16-bit Yes 1991
Atari ST Yamaha PSG chip 3 square waves N/A No — synth only 1985

Modern Analogies

Amiga AHI Concept Modern Equivalent Notes
AHI_AllocAudio() AudioContext.create() (Web Audio) / alcOpenDevice() (OpenAL) Audio session creation
AHI_LoadSound() AudioBuffer (Web Audio) / AL_BUFFER (OpenAL) Upload PCM to audio system
AHI_Play() AudioBufferSourceNode.start() / alSourcePlay() Trigger playback
AHI_SetVol() GainNode.gain (Web Audio) / alSourcef(AL_GAIN) Volume control
AHI_SetFreq() playbackRate (Web Audio) / AL_PITCH (OpenAL) Pitch/speed control
Player Hook ScriptProcessorNode / AudioWorklet (Web Audio) Callback-driven streaming
Mixer Hook Custom audio graph (Web Audio) / mixing callback Software mixing pipeline
paula.audio driver Built-in audio driver (any OS) Default hardware driver
AHI sound card drivers ASIO / CoreAudio / ALSA drivers Hardware-specific backends
AHIA_MixFreq sampleRate (Web Audio) Master mixing frequency
AHISF_IMM flag audioContext.currentTime Immediate vs scheduled

Use Cases

Application AHI Mode Channels Notable Pattern
AmigaAmp HiFi 14-bit Stereo++ 2 (stereo) MP3 decoding via mpega.library, streaming playback
DeliTracker paula.audio or HiFi 48 MOD/S3M/XM multi-format module player
HippoPlayer paula.audio 4 Lightweight module player
Timidity AHI 16-bit 2 MIDI to WAV rendering via GUS patches
Games (post-1996) AHI default 48 SFX playback with software mixing
Video editors AHI default 2 Audio scrubbing with accurate sync
Speech synthesis AHI 8-bit 1 narrator.device output via AHI
Software instruments Prisma/FPGA 16+ Low-latency real-time synthesis

FPGA / MiSTer Impact

AHI is highly relevant to FPGA-based Amiga implementations:

Platform AHI Driver Audio Path Notes
Minimig paula.audio Paula DMA (emulated) FPGA implements Paula registers — AHI works transparently
MiSTer Amiga paula.audio FPGA Paula Same as Minimig — standard Paula emulation
MiSTer + Prisma Megamix prisma.audio FPGA I2S output True 16/24-bit audio via FPGA core
Vampire (Apollo Core) paula.audio + custom FPGA audio DAC Higher-quality DAC than original Paula
PiStorm paula.audio Paula (via FPGA bus) PiStorm uses real Paula chip on A1200/A600

Note

AHI's paula.audio driver works on all FPGA Amiga implementations because they faithfully emulate Paula's DMA registers. No special FPGA driver is needed for basic AHI support.


FAQ

Q: Does AHI replace audio.device? A: No — AHI is a higher-level system. audio.device still exists for direct Paula access. AHI's paula.audio driver uses audio.device internally (or direct Paula registers) as its output path. Applications can use either API.

Q: What's the minimum AmigaOS version for AHI? A: AHI requires AmigaOS 2.04+ (V37). It works on any Amiga with enough RAM. The system is a third-party addition, not part of the ROM — it installs into DEVS: and LIBS:.

Q: Can I use AHI and audio.device at the same time? A: It depends on the driver. The paula.audio driver claims Paula's audio channels, which means audio.device can't use them simultaneously. Other drivers (Delfina, Prisma, USB) use separate hardware, so Paula remains free for audio.device.

Q: How do I get the best audio quality on a stock Amiga? A: Use AHI with the "HiFi 14-bit Stereo++" mode of paula.audio. This uses all 4 Paula channels in a calibrated PWM configuration to achieve ~14-bit resolution at 44100 Hz stereo. The downside: no channels left for system sounds, and CPU usage is higher.

Q: What's the latency of AHI software mixing? A: Typically 120 ms depending on AHIA_PlayerFreq and the driver. At 50 Hz player frequency, the buffer is ~20 ms (44100/50 = 882 samples). Lower latency requires higher player frequency but increases CPU load.

Q: Can AHI play MP3/OGG/FLAC files directly? A: No — AHI is a raw PCM output system. It plays sample buffers, not compressed formats. You need a decoder library (mpega.library for MP3, ogg.player for OGG) to decompress into PCM, then feed the PCM to AHI via streaming playback (Player Hook).

Q: How do I install AHI on my Amiga? A: Download the AHI distribution from Aminet (driver/audio/ahiusr.lha). Extract to SYS:, run the installer. It places ahi.device in DEVS:, driver libraries in DEVS:AHI/, and preferences in SYS:Prefs/.


References

SDK & Documentation

  • AHI Developer's Guide — Martin Blom, distributed with AHI SDK
  • AHI User Guide — Aminet driver/audio/ahiusr.lha
  • AHI SDK — Aminet dev/misc/ahidev.lha

NDK Headers

  • devices/ahi.h — AHI device commands, sample type constants
  • libraries/ahi_sub.h — AHI driver sub-functions (for driver authors)