# 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;
}