1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-11-10 19:20:13 +00:00

Document the Haas Effect (#1302)

* Document Haas Effect

* More docs

* cleanup

* add comments

* Adjust comment

* More cleanup

* Fix

* Add description of the Haas Effect

* roman suggestion
This commit is contained in:
engineer124 2022-08-28 11:33:38 -04:00 committed by GitHub
parent b24b8ad096
commit e25bb1485a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 137 additions and 98 deletions

View file

@ -121,7 +121,7 @@ extern u8 gDefaultShortNoteGateTimeTable[16];
extern EnvelopePoint gDefaultEnvelope[4];
extern NoteSubEu gZeroNoteSub;
extern NoteSubEu gDefaultNoteSub;
extern u16 gHeadsetPanQuantization[64];
extern u16 gHaasEffectDelaySizes[64];
extern s16 D_8012FBA8[];
extern f32 gHeadsetPanVolume[128];
extern f32 gStereoPanVolume[128];

View file

@ -484,19 +484,19 @@ typedef struct SequenceLayer {
} SequenceLayer; // size = 0x80
typedef struct {
/* 0x0000 */ s16 adpcmdecState[0x10];
/* 0x0020 */ s16 finalResampleState[0x10];
/* 0x0040 */ s16 mixEnvelopeState[0x28];
/* 0x0090 */ s16 panResampleState[0x10];
/* 0x00B0 */ s16 panSamplesBuffer[0x20];
/* 0x00F0 */ s16 dummyResampleState[0x10];
} NoteSynthesisBuffers; // size = 0x110
/* 0x000 */ s16 adpcmdecState[16];
/* 0x020 */ s16 finalResampleState[16];
/* 0x040 */ s16 mixEnvelopeState[32];
/* 0x080 */ s16 unusedState[16];
/* 0x0A0 */ s16 haasEffectDelayState[32];
/* 0x0E0 */ s16 unkState[128];
} NoteSynthesisBuffers; // size = 0x1E0
typedef struct {
/* 0x00 */ u8 restart;
/* 0x01 */ u8 sampleDmaIndex;
/* 0x02 */ u8 prevHeadsetPanRight;
/* 0x03 */ u8 prevHeadsetPanLeft;
/* 0x02 */ u8 prevHaasEffectLeftDelaySize;
/* 0x03 */ u8 prevHaasEffectRightDelaySize;
/* 0x04 */ u8 reverbVol;
/* 0x05 */ u8 numParts;
/* 0x06 */ u16 samplePosFrac;
@ -559,11 +559,11 @@ typedef struct {
/* 0x01 */ u8 bookOffset : 2;
/* 0x01 */ u8 isSyntheticWave : 1;
/* 0x01 */ u8 hasTwoParts : 1;
/* 0x01 */ u8 usesHeadsetPanEffects2 : 1;
/* 0x01 */ u8 useHaasEffect : 1;
} bitField1;
/* 0x02 */ u8 gain; // Increases volume by a multiplicative scaling factor. Represented as a UQ4.4 number
/* 0x03 */ u8 headsetPanRight;
/* 0x04 */ u8 headsetPanLeft;
/* 0x03 */ u8 haasEffectLeftDelaySize;
/* 0x04 */ u8 haasEffectRightDelaySize;
/* 0x05 */ u8 reverbVol;
/* 0x06 */ u8 harmonicIndexCurAndPrev; // bits 3..2 store curHarmonicIndex, bits 1..0 store prevHarmonicIndex
/* 0x07 */ u8 unk_07;

View file

@ -556,7 +556,7 @@ NoteSubEu gDefaultNoteSub = {
{ 1, 1, 0, 0, 0, 0, 0, 0 }, { 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
u16 gHeadsetPanQuantization[64] = {
u16 gHaasEffectDelaySizes[64] = {
30 * SAMPLE_SIZE,
29 * SAMPLE_SIZE,
28 * SAMPLE_SIZE,

View file

@ -1,8 +1,9 @@
#include "global.h"
void Audio_InitNoteSub(Note* note, NoteSubEu* sub, NoteSubAttributes* attrs) {
f32 volRight, volLeft;
s32 smallPanIndex;
f32 volLeft;
f32 volRight;
s32 halfPanIndex;
u64 pad;
u8 strongLeft;
u8 strongRight;
@ -31,22 +32,22 @@ void Audio_InitNoteSub(Note* note, NoteSubEu* sub, NoteSubAttributes* attrs) {
sub->bitField0.stereoHeadsetEffects = stereoData.stereoHeadsetEffects;
sub->bitField0.usesHeadsetPanEffects = stereoData.usesHeadsetPanEffects;
if (stereoHeadsetEffects && (gAudioContext.soundMode == SOUNDMODE_HEADSET)) {
smallPanIndex = pan >> 1;
if (smallPanIndex > 0x3F) {
smallPanIndex = 0x3F;
halfPanIndex = pan >> 1;
if (halfPanIndex > 0x3F) {
halfPanIndex = 0x3F;
}
sub->headsetPanLeft = gHeadsetPanQuantization[smallPanIndex];
sub->headsetPanRight = gHeadsetPanQuantization[0x3F - smallPanIndex];
sub->bitField1.usesHeadsetPanEffects2 = true;
sub->haasEffectRightDelaySize = gHaasEffectDelaySizes[halfPanIndex];
sub->haasEffectLeftDelaySize = gHaasEffectDelaySizes[0x3F - halfPanIndex];
sub->bitField1.useHaasEffect = true;
volLeft = gHeadsetPanVolume[pan];
volRight = gHeadsetPanVolume[0x7F - pan];
} else if (stereoHeadsetEffects && (gAudioContext.soundMode == SOUNDMODE_STEREO)) {
strongLeft = strongRight = 0;
sub->headsetPanRight = 0;
sub->headsetPanLeft = 0;
sub->bitField1.usesHeadsetPanEffects2 = false;
sub->haasEffectLeftDelaySize = 0;
sub->haasEffectRightDelaySize = 0;
sub->bitField1.useHaasEffect = false;
volLeft = gStereoPanVolume[pan];
volRight = gStereoPanVolume[0x7F - pan];
@ -945,6 +946,7 @@ void Audio_NoteInitAll(void) {
note->playbackState.portamento.speed = 0;
note->playbackState.stereoHeadsetEffects = false;
note->startSamplePos = 0;
note->synthesisState.synthesisBuffers = AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, 0x1E0);
note->synthesisState.synthesisBuffers =
AudioHeap_AllocDmaMemory(&gAudioContext.miscPool, sizeof(NoteSynthesisBuffers));
}
}

View file

@ -4,7 +4,7 @@
// DMEM Addresses for the RSP
#define DMEM_TEMP 0x3C0
#define DMEM_UNCOMPRESSED_NOTE 0x580
#define DMEM_NOTE_PAN_TEMP 0x5C0
#define DMEM_HAAS_TEMP 0x5C0
#define DMEM_SCRATCH2 0x760 // = DMEM_TEMP + DMEM_2CH_SIZE + a bit more
#define DMEM_COMPRESSED_ADPCM_DATA 0x940 // = DMEM_LEFT_CH
#define DMEM_LEFT_CH 0x940
@ -14,6 +14,12 @@
#define DMEM_WET_LEFT_CH 0xC80
#define DMEM_WET_RIGHT_CH 0xE20 // = DMEM_WET_LEFT_CH + DMEM_1CH_SIZE
typedef enum {
/* 0 */ HAAS_EFFECT_DELAY_NONE,
/* 1 */ HAAS_EFFECT_DELAY_LEFT, // Delay left channel so that right channel is heard first
/* 2 */ HAAS_EFFECT_DELAY_RIGHT // Delay right channel so that left channel is heard first
} HaasEffectDelaySide;
Acmd* AudioSynth_LoadRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb);
Acmd* AudioSynth_SaveBufferOffset(Acmd* cmd, u16 dmem, u16 offset, s32 size, s16* buf);
Acmd* AudioSynth_SaveRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 size, SynthesisReverb* reverb);
@ -21,17 +27,25 @@ Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* cmd, s32 updat
Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s16* aiBuf,
s32 aiBufLen, Acmd* cmd, s32 updateIndex);
Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 numSamplesToLoad);
Acmd* AudioSynth_NoteApplyHeadsetPanEffects(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 bufLen,
s32 flags, s32 side);
Acmd* AudioSynth_ApplyHaasEffect(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 size, s32 flags,
s32 haasEffectDelaySide);
Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 aiBufLen,
u16 inBuf, s32 headsetPanSettings, s32 flags);
u16 dmemSrc, s32 haasEffectDelaySide, s32 flags);
Acmd* AudioSynth_FinalResample(Acmd* cmd, NoteSynthesisState* synthState, s32 size, u16 pitch, u16 inpDmem,
s32 resampleFlags);
u32 D_801304A0 = _SHIFTL(A_ENVMIXER, 24, 8);
u32 D_801304A4 = MK_CMD(DMEM_NOTE_PAN_TEMP >> 4, DMEM_RIGHT_CH >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
u32 D_801304A8 = MK_CMD(DMEM_LEFT_CH >> 4, DMEM_NOTE_PAN_TEMP >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
u32 D_801304AC = MK_CMD(DMEM_LEFT_CH >> 4, DMEM_RIGHT_CH >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
u32 sEnvMixerOp = _SHIFTL(A_ENVMIXER, 24, 8);
// Store the left dry channel in a temp space to be delayed to produce the haas effect
u32 sEnvMixerLeftHaasDmemDests =
MK_CMD(DMEM_HAAS_TEMP >> 4, DMEM_RIGHT_CH >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
// Store the right dry channel in a temp space to be delayed to produce the haas effect
u32 sEnvMixerRightHaasDmemDests =
MK_CMD(DMEM_LEFT_CH >> 4, DMEM_HAAS_TEMP >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
u32 sEnvMixerDefaultDmemDests =
MK_CMD(DMEM_LEFT_CH >> 4, DMEM_RIGHT_CH >> 4, DMEM_WET_LEFT_CH >> 4, DMEM_WET_RIGHT_CH >> 4);
u16 D_801304B0[] = {
0x7FFF, 0xD001, 0x3FFF, 0xF001, 0x5FFF, 0x9001, 0x7FFF, 0x8001,
@ -723,7 +737,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
s32 nParts;
s32 curPart;
s32 sampleDataStartPad;
s32 side;
s32 haasEffectDelaySide;
s32 resampledTempLen;
u16 sampleDmemBeforeResampling;
s32 sampleDataOffset;
@ -752,8 +766,8 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
synthState->samplePosFrac = 0;
synthState->curVolLeft = 0;
synthState->curVolRight = 0;
synthState->prevHeadsetPanRight = 0;
synthState->prevHeadsetPanLeft = 0;
synthState->prevHaasEffectLeftDelaySize = 0;
synthState->prevHaasEffectRightDelaySize = 0;
synthState->reverbVol = noteSubEu->reverbVol;
synthState->numParts = 0;
synthState->unk_1A = 1;
@ -1078,7 +1092,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
unk7 = noteSubEu->unk_07;
unkE = noteSubEu->unk_0E;
buf = &synthState->synthesisBuffers->panSamplesBuffer[0x18];
buf = synthState->synthesisBuffers->unkState;
if (unk7 != 0 && noteSubEu->unk_0E != 0) {
AudioSynth_DMemMove(cmd++, DMEM_TEMP, DMEM_SCRATCH2, aiBufLen * SAMPLE_SIZE);
thing = DMEM_SCRATCH2 - unk7;
@ -1095,21 +1109,24 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS
synthState->unk_1A = 1;
}
if (noteSubEu->headsetPanRight != 0 || synthState->prevHeadsetPanRight != 0) {
side = 1;
} else if (noteSubEu->headsetPanLeft != 0 || synthState->prevHeadsetPanLeft != 0) {
side = 2;
if ((noteSubEu->haasEffectLeftDelaySize != 0) || (synthState->prevHaasEffectLeftDelaySize != 0)) {
haasEffectDelaySide = HAAS_EFFECT_DELAY_LEFT;
} else if ((noteSubEu->haasEffectRightDelaySize != 0) || (synthState->prevHaasEffectRightDelaySize != 0)) {
haasEffectDelaySide = HAAS_EFFECT_DELAY_RIGHT;
} else {
side = 0;
haasEffectDelaySide = HAAS_EFFECT_DELAY_NONE;
}
cmd = AudioSynth_ProcessEnvelope(cmd, noteSubEu, synthState, aiBufLen, DMEM_TEMP, side, flags);
if (noteSubEu->bitField1.usesHeadsetPanEffects2) {
cmd = AudioSynth_ProcessEnvelope(cmd, noteSubEu, synthState, aiBufLen, DMEM_TEMP, haasEffectDelaySide, flags);
if (noteSubEu->bitField1.useHaasEffect) {
if (!(flags & A_INIT)) {
flags = A_CONTINUE;
}
cmd =
AudioSynth_NoteApplyHeadsetPanEffects(cmd, noteSubEu, synthState, aiBufLen * (s32)SAMPLE_SIZE, flags, side);
cmd = AudioSynth_ApplyHaasEffect(cmd, noteSubEu, synthState, aiBufLen * (s32)SAMPLE_SIZE, flags,
haasEffectDelaySide);
}
return cmd;
}
@ -1125,8 +1142,8 @@ Acmd* AudioSynth_FinalResample(Acmd* cmd, NoteSynthesisState* synthState, s32 si
}
Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 aiBufLen,
u16 inBuf, s32 headsetPanSettings, s32 flags) {
u32 phi_a1;
u16 dmemSrc, s32 haasEffectDelaySide, s32 flags) {
u32 dmemDests;
u16 curVolLeft;
u16 targetVolLeft;
s32 phi_t1;
@ -1171,30 +1188,36 @@ Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisS
synthState->curVolLeft = curVolLeft + (rampLeft * (aiBufLen >> 3));
synthState->curVolRight = curVolRight + (rampRight * (aiBufLen >> 3));
if (noteSubEu->bitField1.usesHeadsetPanEffects2) {
AudioSynth_ClearBuffer(cmd++, DMEM_NOTE_PAN_TEMP, DMEM_1CH_SIZE);
if (noteSubEu->bitField1.useHaasEffect) {
AudioSynth_ClearBuffer(cmd++, DMEM_HAAS_TEMP, DMEM_1CH_SIZE);
AudioSynth_EnvSetup1(cmd++, phi_t1 * 2, rampReverb, rampLeft, rampRight);
AudioSynth_EnvSetup2(cmd++, curVolLeft, curVolRight);
switch (headsetPanSettings) {
case 1:
phi_a1 = D_801304A4;
switch (haasEffectDelaySide) {
case HAAS_EFFECT_DELAY_LEFT:
// Store the left dry channel in a temp space to be delayed to produce the haas effect
dmemDests = sEnvMixerLeftHaasDmemDests;
break;
case 2:
phi_a1 = D_801304A8;
case HAAS_EFFECT_DELAY_RIGHT:
// Store the right dry channel in a temp space to be delayed to produce the haas effect
dmemDests = sEnvMixerRightHaasDmemDests;
break;
default:
phi_a1 = D_801304AC;
default: // HAAS_EFFECT_DELAY_NONE
dmemDests = sEnvMixerDefaultDmemDests;
break;
}
} else {
aEnvSetup1(cmd++, phi_t1 * 2, rampReverb, rampLeft, rampRight);
aEnvSetup2(cmd++, curVolLeft, curVolRight);
phi_a1 = D_801304AC;
dmemDests = sEnvMixerDefaultDmemDests;
}
aEnvMixer(cmd++, inBuf, aiBufLen, (sourceReverbVol & 0x80) >> 7, noteSubEu->bitField0.stereoHeadsetEffects,
aEnvMixer(cmd++, dmemSrc, aiBufLen, (sourceReverbVol & 0x80) >> 7, noteSubEu->bitField0.stereoHeadsetEffects,
noteSubEu->bitField0.usesHeadsetPanEffects, noteSubEu->bitField0.stereoStrongRight,
noteSubEu->bitField0.stereoStrongLeft, phi_a1, D_801304A0);
noteSubEu->bitField0.stereoStrongLeft, dmemDests, sEnvMixerOp);
return cmd;
}
@ -1242,61 +1265,75 @@ Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisS
return cmd;
}
Acmd* AudioSynth_NoteApplyHeadsetPanEffects(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 bufLen,
s32 flags, s32 side) {
u16 dest;
/**
* The Haas Effect gives directionality to sound by appling a small (< 35ms) delay to either the left or right channel.
* The delay is small enough that the sound is still perceived as one sound, but the channel that is not delayed will
* reach our ear first and give a sense of directionality. The sound is directed towards the opposite side of the delay.
*/
Acmd* AudioSynth_ApplyHaasEffect(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 size, s32 flags,
s32 haasEffectDelaySide) {
u16 dmemDest;
u16 pitch;
u8 prevPanShift;
u8 panShift;
u8 prevHaasEffectDelaySize;
u8 haasEffectDelaySize;
switch (side) {
case 1:
dest = DMEM_LEFT_CH;
panShift = noteSubEu->headsetPanRight;
prevPanShift = synthState->prevHeadsetPanRight;
synthState->prevHeadsetPanLeft = 0;
synthState->prevHeadsetPanRight = panShift;
switch (haasEffectDelaySide) {
case HAAS_EFFECT_DELAY_LEFT:
// Delay the sample on the left channel
// This allows the right channel to be heard first
dmemDest = DMEM_LEFT_CH;
haasEffectDelaySize = noteSubEu->haasEffectLeftDelaySize;
prevHaasEffectDelaySize = synthState->prevHaasEffectLeftDelaySize;
synthState->prevHaasEffectRightDelaySize = 0;
synthState->prevHaasEffectLeftDelaySize = haasEffectDelaySize;
break;
case 2:
dest = DMEM_RIGHT_CH;
panShift = noteSubEu->headsetPanLeft;
prevPanShift = synthState->prevHeadsetPanLeft;
synthState->prevHeadsetPanLeft = panShift;
synthState->prevHeadsetPanRight = 0;
case HAAS_EFFECT_DELAY_RIGHT:
// Delay the sample on the right channel
// This allows the left channel to be heard first
dmemDest = DMEM_RIGHT_CH;
haasEffectDelaySize = noteSubEu->haasEffectRightDelaySize;
prevHaasEffectDelaySize = synthState->prevHaasEffectRightDelaySize;
synthState->prevHaasEffectRightDelaySize = haasEffectDelaySize;
synthState->prevHaasEffectLeftDelaySize = 0;
break;
default:
default: // HAAS_EFFECT_DELAY_NONE
return cmd;
}
if (flags != A_INIT) {
// Slightly adjust the sample rate in order to fit a change in pan shift
if (panShift != prevPanShift) {
pitch = (((bufLen << 0xF) / 2) - 1) / ((bufLen + panShift - prevPanShift - 2) / 2);
aSetBuffer(cmd++, 0, DMEM_NOTE_PAN_TEMP, DMEM_TEMP, bufLen + panShift - prevPanShift);
// Slightly adjust the sample rate in order to fit a change in sample delay
if (haasEffectDelaySize != prevHaasEffectDelaySize) {
pitch = (((size << 0xF) / 2) - 1) / ((size + haasEffectDelaySize - prevHaasEffectDelaySize - 2) / 2);
aSetBuffer(cmd++, 0, DMEM_HAAS_TEMP, DMEM_TEMP, size + haasEffectDelaySize - prevHaasEffectDelaySize);
aResampleZoh(cmd++, pitch, 0);
} else {
aDMEMMove(cmd++, DMEM_NOTE_PAN_TEMP, DMEM_TEMP, bufLen);
aDMEMMove(cmd++, DMEM_HAAS_TEMP, DMEM_TEMP, size);
}
if (prevPanShift != 0) {
aLoadBuffer(cmd++, &synthState->synthesisBuffers->panResampleState[0x8], DMEM_NOTE_PAN_TEMP,
ALIGN16(prevPanShift));
aDMEMMove(cmd++, DMEM_TEMP, DMEM_NOTE_PAN_TEMP + prevPanShift, bufLen + panShift - prevPanShift);
if (prevHaasEffectDelaySize != 0) {
aLoadBuffer(cmd++, synthState->synthesisBuffers->haasEffectDelayState, DMEM_HAAS_TEMP,
ALIGN16(prevHaasEffectDelaySize));
aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP + prevHaasEffectDelaySize,
size + haasEffectDelaySize - prevHaasEffectDelaySize);
} else {
aDMEMMove(cmd++, DMEM_TEMP, DMEM_NOTE_PAN_TEMP, bufLen + panShift);
aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP, size + haasEffectDelaySize);
}
} else {
// Just shift right
aDMEMMove(cmd++, DMEM_NOTE_PAN_TEMP, DMEM_TEMP, bufLen);
aClearBuffer(cmd++, DMEM_NOTE_PAN_TEMP, panShift);
aDMEMMove(cmd++, DMEM_TEMP, DMEM_NOTE_PAN_TEMP + panShift, bufLen);
// Just apply a delay directly
aDMEMMove(cmd++, DMEM_HAAS_TEMP, DMEM_TEMP, size);
aClearBuffer(cmd++, DMEM_HAAS_TEMP, haasEffectDelaySize);
aDMEMMove(cmd++, DMEM_TEMP, DMEM_HAAS_TEMP + haasEffectDelaySize, size);
}
if (panShift) {
if (haasEffectDelaySize) { // != 0
// Save excessive samples for next iteration
aSaveBuffer(cmd++, DMEM_NOTE_PAN_TEMP + bufLen, &synthState->synthesisBuffers->panResampleState[0x8],
ALIGN16(panShift));
aSaveBuffer(cmd++, DMEM_HAAS_TEMP + size, synthState->synthesisBuffers->haasEffectDelayState,
ALIGN16(haasEffectDelaySize));
}
aAddMixer(cmd++, ALIGN64(bufLen), DMEM_NOTE_PAN_TEMP, dest, 0x7FFF);
aAddMixer(cmd++, ALIGN64(size), DMEM_HAAS_TEMP, dmemDest, 0x7FFF);
return cmd;
}