# Amiga DSP

# 1 Anti-Aliasing Low-Pass Filter

The hardware filter (which improves sound quality at low sample rates, but can make it sound muffled at high sample rates) can be disabled and enabled via custom chip register pokes:

enum FILTER_ACTION
{
  FILTER_QUERY = 0,
  FILTER_SET = 1,
};

int AUDIO_filter(enum FILTER_ACTION action, int value)
{
  const UBYTE bit = 0x02;
  const UBYTE mask = ~bit;
  volatile UBYTE *CIA_A_PRA = (UBYTE *) 0xBFE001UL;
  UBYTE old = 0, new;
  switch (action)
  {
    case FILTER_QUERY:
      old = *CIA_A_PRA;
      break;
    case FILTER_SET:
      if (! value)
      {
        old = *CIA_A_PRA;
        new = old;
        new |= bit;
        *CIA_A_PRA = new;
      }
      else
      {
        old = *CIA_A_PRA;
        new = old;
        new &= mask;
        *CIA_A_PRA = new;
      }
  }
  return !!(old & bit);
}

Should probably disable interrupts or use inline assembly to avoid race conditions and ensure atomic changes.

# 2 Audio Generation

Non-interactive audio generation can use large block sizes to reduce overhead.

# 2.1 Task Priority

If you know you can meet the deadline, you can raise the priority of your dsp to reduce the likelihood of dropouts due to other activity on the system.

If you don’t meet the deadline this will cause a system lockup processing audio at 100% load.

#define AUDIO_PRIORITY 21
ULONG oldpri = SetTaskPri(FindTask(0), AUDIO_PRIORITY);

For multimedia, set graphics task to higher priority than audio task to avoid frame drops, the graphics task loops using WaitTOF().

# 2.2 Double Buffering

Following audio.device example at https://wiki.amigaos.net/wiki/Audio_Device#Double_Buffered_Sound_Example, calculate two buffers, enqueue the second while the first is still playing (so that it will follow seamlessly). Then when each buffer finishes, fill it with new audio and re-enqueue it.

dsp(&state, aio[0]->ioa_Data, aio[0]->ioa_Length);
dsp(&state, aio[1]->ioa_Data, aio[1]->ioa_Length);
BeginIO((struct IORequest *) aio[0]);
BeginIO((struct IORequest *) aio[1]);
int w = 0;
while (1)
{
  ULONG sig = Wait(SIGBREAKF_CTRL_C | (1 << port[w]->mp_SigBit));
  if (sig & SIGBREAKF_CTRL_C) { break; }
  while (! GetMsg(port[w])) { }
  dsp(&state, aio[w]->ioa_Data, aio[w]->ioa_Length);
  BeginIO((struct IORequest *) aio[w]);
  w = ! w;
}

# 2.3 Four Synchronized Channels

Use audio.device CMD_STOP before enqueuing the first block for each channel with BeginIO(), and CMD_START afterwards to start all channels simultaneously. This avoids weird phasing effects from channels not being exactly synchronized.

Blocksize and sample rate should be common between all aios.

// stop audio
aio->ioa_Request.io_Command = CMD_STOP;
aio->ioa_Request.io_Flags = IOF_QUICK;
DoIO((struct IORequest *) aio);
// calculate first two blocks
dsp(&state, aios[0][0]->ioa_Data, aios[0][1]->ioa_Data, aios[0][2]->ioa_Data, aios[0][3]->ioa_Data, blocksize);
dsp(&state, aios[1][0]->ioa_Data, aios[1][1]->ioa_Data, aios[1][2]->ioa_Data, aios[1][3]->ioa_Data, blocksize);
// enqueue first block
BeginIO((struct IORequest *) aios[0][0]);
BeginIO((struct IORequest *) aios[0][1]);
BeginIO((struct IORequest *) aios[0][2]);
BeginIO((struct IORequest *) aios[0][3]);
int w = 0;
bool first = true;
while (1)
{
  // enqueue next block
  BeginIO((struct IORequest *) aios[! w][0]);
  BeginIO((struct IORequest *) aios[! w][1]);
  BeginIO((struct IORequest *) aios[! w][2]);
  BeginIO((struct IORequest *) aios[! w][3]);
  if (first)
  {
    // start audio
    aio->ioa_Request.io_Command = CMD_START;
    aio->ioa_Request.io_Flags = IOF_QUICK;
    DoIO((struct IORequest *) aio);
    first = false;
  }
  // wait for replies
  if (SIGBREAKF_CTRL_C & Wait(SIGBREAKF_CTRL_C | (1 << port[w][0]->mp_SigBit))) { break; }
  while (! GetMsg(port[w][0])) { }
  if (SIGBREAKF_CTRL_C & Wait(SIGBREAKF_CTRL_C | (1 << port[w][1]->mp_SigBit))) { break; }
  while (! GetMsg(port[w][1])) { }
  if (SIGBREAKF_CTRL_C & Wait(SIGBREAKF_CTRL_C | (1 << port[w][2]->mp_SigBit))) { break; }
  while (! GetMsg(port[w][2])) { }
  if (SIGBREAKF_CTRL_C & Wait(SIGBREAKF_CTRL_C | (1 << port[w][3]->mp_SigBit))) { break; }
  while (! GetMsg(port[w][3])) { }
  // fill next block
  dsp(&state, aios[w][0]->ioa_Data, aios[w][1]->ioa_Data, aios[w][2]->ioa_Data, aios[w][3]->ioa_Data, blocksize);
  // swap buffers
  w = ! w;
}