Closed fatalfeel closed 7 years ago
This is something I intend to support, yes. It will require an extension to the EFX API to allow an auxiliary effect slot to "send" to another auxiliary effect slot instead of the output, but aside from coming to an agreement on how an app can do it, it shouldn't be terribly difficult to implement in OpenAL Soft itself.
for(i = 0;i < auxslots->count;i++) { const ALeffectslot slot = auxslots->slot[i]; ALeffectState state = slot->Params.EffectState; V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer, state->OutChannels); }
ctx = ctx->next; in alu.c
slot is in while loop calculate.. just only judge which is last effect and mix with sampleout channel otherwise is catch previous sample out as next slot->WetBuffer.
if( ctx = ctx->next == NULL) is_lasteffect = true;
V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer, state->OutChannels, is_lasteffect);
done~
static ALvoid ALequalizerState_process(ALequalizerState state, ALuint SamplesToDo, const ALfloat (restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALuint NumChannels) { ... ... if( is_lasteffect ) { for(ft = 0;ft < MAX_EFFECT_CHANNELS;ft++) { for(kt = 0;kt < NumChannels;kt++) { ALfloat gain = state->Gain[ft][kt]; if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD)) continue;
for(it = 0;it < td;it++)
SamplesOut[kt][base+it] += gain * Samples[3][ft][it];
}
}
} else { memcpy(SamplesIn[ft][base], Samples[3][ft], sizeof(Samples[3][ft])); }
}
Would be easier to allow the update method to take an optional ALeffectslot*
parameter, and it can set the panning gains and output buffer using that as the target. Then ensure the effect slots are correctly ordered in the ActiveAuxSlot list so that ones that write to other effect slots are processed first, and the later ones will have the results of the earlier ones. And you got to be careful that you can't link up circular dependencies.
ok... may ask what time you want to finish this function?
have any plan to do now?
//I do this way
//main program
/*
/ This file contains an example for applying reverb to a sound. /
/ Effect object functions / static LPALGENEFFECTS alGenEffects; static LPALDELETEEFFECTS alDeleteEffects; static LPALISEFFECT alIsEffect; static LPALEFFECTI alEffecti; static LPALEFFECTIV alEffectiv; static LPALEFFECTF alEffectf; static LPALEFFECTFV alEffectfv; static LPALGETEFFECTI alGetEffecti; static LPALGETEFFECTIV alGetEffectiv; static LPALGETEFFECTF alGetEffectf; static LPALGETEFFECTFV alGetEffectfv;
/ Auxiliary Effect Slot object functions / static LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; static LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; static LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; static LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; static LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; static LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; static LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; static LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; static LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; static LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; static LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; static LPALCRESETDEVICESOFT alcResetDeviceSOFT;
struct WHEADER { unsigned char riff[4]; // RIFF string unsigned int overall_size ; // overall size of file in bytes unsigned char wave[4]; // WAVE string unsigned char fmt_chunk_marker[4]; // fmt string with trailing null char unsigned int length_of_fmt; // length of the format data unsigned int format_type; // format type. 1-PCM, 3- IEEE float, 6 - 8bit A law, 7 - 8bit mu law unsigned int channels; // no.of channels unsigned int sample_rate; // sampling rate (blocks per second) unsigned int byterate; // SampleRate NumChannels BitsPerSample/8 unsigned int block_align; // NumChannels BitsPerSample/8 unsigned int bits_per_sample; // bits per sample, 8- 8bits, 16- 16 bits etc unsigned char data_chunk_header [4]; // DATA string or FLLR string unsigned int data_size; // NumSamples NumChannels * BitsPerSample/8 - size of the next chunk that will be read };
static ALuint LoadSound(char filename) { ALuint al_buffer = 0; int read; char format_name[8] = {0}; unsigned char buffer4[4]; unsigned char buffer2[2]; struct WHEADER header; long num_samples; long size_of_each_sample; float duration_in_seconds; ALenum format; FILE ptr; unsigned char* raw_data = NULL;
ptr = fopen(filename, "rb");
if (ptr == NULL)
{
printf("Error opening file\n");
return al_buffer;
}
// read header parts
read = fread(header.riff, sizeof(header.riff), 1, ptr);
printf("(1-4): %s \n", header.riff);
read = fread(buffer4, sizeof(buffer4), 1, ptr);
printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);
// convert little endian to big endian 4 byte int
header.overall_size = buffer4[0] | (buffer4[1]<<8) | (buffer4[2]<<16) | (buffer4[3]<<24);
printf("(5-8) Overall size: bytes:%u, Kb:%u \n", header.overall_size, header.overall_size/1024);
read = fread(header.wave, sizeof(header.wave), 1, ptr);
printf("(9-12) Wave marker: %s\n", header.wave);
read = fread(header.fmt_chunk_marker, sizeof(header.fmt_chunk_marker), 1, ptr);
printf("(13-16) Fmt marker: %s\n", header.fmt_chunk_marker);
read = fread(buffer4, sizeof(buffer4), 1, ptr);
printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);
// convert little endian to big endian 4 byte integer
header.length_of_fmt = buffer4[0] | (buffer4[1] << 8) | (buffer4[2] << 16) | (buffer4[3] << 24);
printf("(17-20) Length of Fmt header: %u \n", header.length_of_fmt);
read = fread(buffer2, sizeof(buffer2), 1, ptr); printf("%u %u \n", buffer2[0], buffer2[1]);
header.format_type = buffer2[0] | (buffer2[1] << 8);
if (header.format_type == 1)
strcpy(format_name,"PCM");
else if (header.format_type == 6)
strcpy(format_name, "A-law");
else if (header.format_type == 7)
strcpy(format_name, "Mu-law");
printf("(21-22) Format type: %u %s \n", header.format_type, format_name);
read = fread(buffer2, sizeof(buffer2), 1, ptr);
printf("%u %u \n", buffer2[0], buffer2[1]);
header.channels = buffer2[0] | (buffer2[1] << 8);
printf("(23-24) Channels: %u \n", header.channels);
read = fread(buffer4, sizeof(buffer4), 1, ptr);
printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);
header.sample_rate = buffer4[0] |(buffer4[1] << 8) |(buffer4[2] << 16) | (buffer4[3] << 24);
printf("(25-28) Sample rate: %u\n", header.sample_rate);
read = fread(buffer4, sizeof(buffer4), 1, ptr);
printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);
header.byterate = buffer4[0] |(buffer4[1] << 8) | (buffer4[2] << 16) | (buffer4[3] << 24);
printf("(29-32) Byte Rate: %u , Bit Rate:%u\n", header.byterate, header.byterate*8);
read = fread(buffer2, sizeof(buffer2), 1, ptr);
printf("%u %u \n", buffer2[0], buffer2[1]);
header.block_align = buffer2[0] | (buffer2[1] << 8);
printf("(33-34) Block Alignment: %u \n", header.block_align);
read = fread(buffer2, sizeof(buffer2), 1, ptr);
printf("%u %u \n", buffer2[0], buffer2[1]);
header.bits_per_sample = buffer2[0] | (buffer2[1] << 8);
printf("(35-36) Bits per sample: %u \n", header.bits_per_sample);
read = fread(header.data_chunk_header, sizeof(header.data_chunk_header), 1, ptr);
printf("(37-40) Data Marker: %s \n", header.data_chunk_header);
read = fread(buffer4, sizeof(buffer4), 1, ptr);
printf("%u %u %u %u\n", buffer4[0], buffer4[1], buffer4[2], buffer4[3]);
header.data_size = buffer4[0] | (buffer4[1] << 8) | (buffer4[2] << 16) | (buffer4[3] << 24 );
printf("(41-44) Size of data chunk: %u \n", header.data_size);
// calculate no.of samples
num_samples = (8 * header.data_size) / (header.channels * header.bits_per_sample);
printf("Number of samples:%lu \n", num_samples);
size_of_each_sample = (header.channels * header.bits_per_sample) / 8;
printf("Size of each sample:%ld bytes\n", size_of_each_sample);
// calculate duration of file
duration_in_seconds = (float) header.overall_size / header.byterate;
printf("Approx.Duration in seconds=%f\n", duration_in_seconds);
// read each sample from data chunk if PCM
if (header.format_type == 1)
{
raw_data = malloc(size_of_each_sample*num_samples);
read = fread(raw_data, size_of_each_sample*num_samples, 1, ptr);
if(header.bits_per_sample == 8)
{
if(header.channels == 1)
format = AL_FORMAT_MONO8;
else if(header.channels == 2)
format = AL_FORMAT_STEREO8;
}
else if(header.bits_per_sample == 16)
{
if(header.channels == 1)
format = AL_FORMAT_MONO16;
else if(header.channels == 2)
format = AL_FORMAT_STEREO16;
}
alGenBuffers(1, &al_buffer);
alBufferData(al_buffer, format, raw_data, size_of_each_sample*num_samples, header.sample_rate);
free(raw_data);
} // if (header.format_type == 1)
printf("Closing file..\n");
fclose(ptr);
return al_buffer;
}
/AL_EQUALIZER_LOW_CUTOFF Hz [50.0, 800.0] 200.0 AL_EQUALIZER_MID1_CENTER Hz [200.0, 3000.0] 500.0 AL_EQUALIZER_MID2_CENTER Hz [1000.0, 8000.0] 3000.0 AL_EQUALIZER_HIGH_CUTOFF Hz [4000.0, 16000.0] 6000.0/ static ALuint LoadEffect_EQ(void) { ALuint effect00 = 0; ALenum err;
/* Create the effect00 object and check if we can do EAX reverb. */
alGenEffects(1, &effect00);
if(alGetEnumValue("AL_EFFECT_EQUALIZER") != 0)
{
printf("Using Equalizer\n");
alEffecti(effect00, AL_EFFECT_TYPE, AL_EFFECT_EQUALIZER);
alEffectf(effect00, AL_EQUALIZER_LOW_GAIN, 7.8f);
alEffectf(effect00, AL_EQUALIZER_LOW_CUTOFF, 64.0f);
alEffectf(effect00, AL_EQUALIZER_MID1_GAIN, 7.8f);
alEffectf(effect00, AL_EQUALIZER_MID1_CENTER, 250.0f);
alEffectf(effect00, AL_EQUALIZER_MID1_WIDTH, 1.0f);
alEffectf(effect00, AL_EQUALIZER_MID2_GAIN, 1.0f);
alEffectf(effect00, AL_EQUALIZER_MID2_CENTER, 4000.0f);
alEffectf(effect00, AL_EQUALIZER_MID2_WIDTH, 1.0f);
alEffectf(effect00, AL_EQUALIZER_HIGH_GAIN, 1.0f);
alEffectf(effect00, AL_EQUALIZER_HIGH_CUTOFF, 8000.0f);
}
/* Check if an error occured, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
fprintf(stderr, "OpenAL error: %s\n", alGetString(err));
if(alIsEffect(effect00))
alDeleteEffects(1, &effect00);
return 0;
}
return effect00;
}
/* LoadEffect loads the given reverb properties into a new OpenAL effect00
object, and returns the new effect00 ID. / static ALuint LoadEffect(const EFXEAXREVERBPROPERTIES reverb) { ALuint effect00 = 0; ALenum err;
/ Create the effect00 object and check if we can do EAX reverb. / alGenEffects(1, &effect00); if(alGetEnumValue("AL_EFFECT_EAXREVERB") != 0) { printf("Using EAX Reverb\n");
/* EAX Reverb is available. Set the EAX effect00 type then load the
* reverb properties. */
alEffecti(effect00, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
alEffectf(effect00, AL_EAXREVERB_DENSITY, reverb->flDensity);
alEffectf(effect00, AL_EAXREVERB_DIFFUSION, reverb->flDiffusion);
alEffectf(effect00, AL_EAXREVERB_GAIN, reverb->flGain);
alEffectf(effect00, AL_EAXREVERB_GAINHF, reverb->flGainHF);
alEffectf(effect00, AL_EAXREVERB_GAINLF, reverb->flGainLF);
alEffectf(effect00, AL_EAXREVERB_DECAY_TIME, reverb->flDecayTime);
alEffectf(effect00, AL_EAXREVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);
alEffectf(effect00, AL_EAXREVERB_DECAY_LFRATIO, reverb->flDecayLFRatio);
alEffectf(effect00, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);
alEffectf(effect00, AL_EAXREVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay);
alEffectfv(effect00, AL_EAXREVERB_REFLECTIONS_PAN, reverb->flReflectionsPan);
alEffectf(effect00, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);
alEffectf(effect00, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);
alEffectfv(effect00, AL_EAXREVERB_LATE_REVERB_PAN, reverb->flLateReverbPan);
alEffectf(effect00, AL_EAXREVERB_ECHO_TIME, reverb->flEchoTime);
alEffectf(effect00, AL_EAXREVERB_ECHO_DEPTH, reverb->flEchoDepth);
alEffectf(effect00, AL_EAXREVERB_MODULATION_TIME, reverb->flModulationTime);
alEffectf(effect00, AL_EAXREVERB_MODULATION_DEPTH, reverb->flModulationDepth);
alEffectf(effect00, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF);
alEffectf(effect00, AL_EAXREVERB_HFREFERENCE, reverb->flHFReference);
alEffectf(effect00, AL_EAXREVERB_LFREFERENCE, reverb->flLFReference);
alEffectf(effect00, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);
alEffecti(effect00, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
} else { printf("Using Standard Reverb\n");
/* No EAX Reverb. Set the standard reverb effect00 type then load the
* available reverb properties. */
alEffecti(effect00, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
alEffectf(effect00, AL_REVERB_DENSITY, reverb->flDensity);
alEffectf(effect00, AL_REVERB_DIFFUSION, reverb->flDiffusion);
alEffectf(effect00, AL_REVERB_GAIN, reverb->flGain);
alEffectf(effect00, AL_REVERB_GAINHF, reverb->flGainHF);
alEffectf(effect00, AL_REVERB_DECAY_TIME, reverb->flDecayTime);
alEffectf(effect00, AL_REVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);
alEffectf(effect00, AL_REVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);
alEffectf(effect00, AL_REVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay);
alEffectf(effect00, AL_REVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);
alEffectf(effect00, AL_REVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);
alEffectf(effect00, AL_REVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF);
alEffectf(effect00, AL_REVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);
alEffecti(effect00, AL_REVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
}
/ Check if an error occured, and clean up if so. / err = alGetError(); if(err != AL_NO_ERROR) { fprintf(stderr, "OpenAL error: %s\n", alGetString(err)); if(alIsEffect(effect00)) alDeleteEffects(1, &effect00); return 0; }
return effect00; }
int main(int argc, char *argv) { EFXEAXREVERBPROPERTIES reverb = EFX_REVERB_PRESET_CONCERTHALL; ALuint source, buffer, effect00, effect01, slot00, slot01; ALenum state; ALCint attr[5] = { ALC_HRTF_SOFT, ALC_FALSE, ALC_OUTPUT_LIMITER_SOFT, ALC_FALSE, 0}; ALCdevice device;
/* Print out usage if no arguments were specified */
if(argc < 2)
{
fprintf(stderr, "Usage: %s [-device <name] <filename>\n", argv[0]);
return 1;
}
/* Initialize OpenAL, and check for EFX support. */
argv++; argc--;
if(InitAL(&argv, &argc) != 0)
return 1;
if(!alcIsExtensionPresent(alcGetContextsDevice(alcGetCurrentContext()), "ALC_EXT_EFX"))
{
fprintf(stderr, "Error: EFX not supported\n");
CloseAL();
return 1;
}
/* Define a macro to help load the function pointers. */
LOAD_PROC(alGenEffects);
LOAD_PROC(alDeleteEffects);
LOAD_PROC(alIsEffect);
LOAD_PROC(alEffecti);
LOAD_PROC(alEffectiv);
LOAD_PROC(alEffectf);
LOAD_PROC(alEffectfv);
LOAD_PROC(alGetEffecti);
LOAD_PROC(alGetEffectiv);
LOAD_PROC(alGetEffectf);
LOAD_PROC(alGetEffectfv);
LOAD_PROC(alGenAuxiliaryEffectSlots);
LOAD_PROC(alDeleteAuxiliaryEffectSlots);
LOAD_PROC(alIsAuxiliaryEffectSlot);
LOAD_PROC(alAuxiliaryEffectSloti);
LOAD_PROC(alAuxiliaryEffectSlotiv);
LOAD_PROC(alAuxiliaryEffectSlotf);
LOAD_PROC(alAuxiliaryEffectSlotfv);
LOAD_PROC(alGetAuxiliaryEffectSloti);
LOAD_PROC(alGetAuxiliaryEffectSlotiv);
LOAD_PROC(alGetAuxiliaryEffectSlotf);
LOAD_PROC(alGetAuxiliaryEffectSlotfv);
device = alcGetContextsDevice(alcGetCurrentContext());
LOAD_PROC(device, alcResetDeviceSOFT);
/* Initialize SDL_sound. */
//Sound_Init();
if(!alcResetDeviceSOFT(device, attr))
printf("Failed to reset device: %s\n", alcGetString(device, alcGetError(device)));
/* Load the sound into a buffer. */
//buffer = LoadSound(argv[0]);
buffer = LoadSound(argv[0]);
if(!buffer)
{
CloseAL();
Sound_Quit();
return 1;
}
/* Load the reverb into an effect00. */
effect00 = LoadEffect(&reverb);
//effect00 = LoadEffect_EQ();
if(!effect00)
{
alDeleteBuffers(1, &buffer);
Sound_Quit();
CloseAL();
return 1;
}
slot00 = 0;
alGenAuxiliaryEffectSlots(1, &slot00);
alAuxiliaryEffectSloti(slot00, AL_EFFECTSLOT_EFFECT, effect00);
assert(alGetError()==AL_NO_ERROR && "Failed to set effect00 slot00");
////////////// //effect01 = LoadEffect(&reverb); effect01 = LoadEffect_EQ(); if(!effect01) { alDeleteBuffers(1, &buffer); Sound_Quit(); CloseAL(); return 1; }
slot01 = 0;
alGenAuxiliaryEffectSlots(1, &slot01);
alAuxiliaryEffectSloti(slot01, AL_EFFECTSLOT_EFFECT, effect01);
assert(alGetError()==AL_NO_ERROR && "Failed to set effect01 slot01");
/* Create the source to play the sound with. */
source = 0;
alGenSources(1, &source);
alSourcei(source, AL_BUFFER, buffer);
/* Connect the source to the effect00 slot00. This tells the source to use the
* effect00 slot00 'slot00', on send #0 with the AL_FILTER_NULL filter object.
*/
alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot00, 0, AL_FILTER_NULL);
assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot01, 1, AL_FILTER_NULL);
assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
/* Play the sound until it finishes. */
alSourcePlay(source);
do {
al_nssleep(10000000);
alGetSourcei(source, AL_SOURCE_STATE, &state);
} while(alGetError() == AL_NO_ERROR && state == AL_PLAYING);
/* All done. Delete resources, and close down SDL_sound and OpenAL. */
alDeleteSources(1, &source);
alDeleteAuxiliaryEffectSlots(1, &slot00);
alDeleteEffects(1, &effect00);
alDeleteBuffers(1, &buffer);
//Sound_Quit();
CloseAL();
return 0;
}
//alu.c void aluMixData(ALCdevice device, ALvoid buffer, ALsizei size) { ALsizei SamplesToDo; ALvoice voice, voice_end; ALeffectslot slot; ALsource source; ALCcontext ctx; FPUCtl oldMode; ALsizei i, c; ALsizei k; //by stone ALeffectState prev_state;
SetMixerFPUMode(&oldMode);
while(size > 0)
{
SamplesToDo = mini(size, BUFFERSIZE);
for(c = 0;c < device->Dry.NumChannels;c++)
memset(device->Dry.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
if(device->Dry.Buffer != device->FOAOut.Buffer)
for(c = 0;c < device->FOAOut.NumChannels;c++)
memset(device->FOAOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
if(device->Dry.Buffer != device->RealOut.Buffer)
for(c = 0;c < device->RealOut.NumChannels;c++)
memset(device->RealOut.Buffer[c], 0, SamplesToDo*sizeof(ALfloat));
IncrementRef(&device->MixCount);
if((slot=device->DefaultSlot) != NULL)
{
CalcEffectSlotParams(device->DefaultSlot, device);
for(c = 0;c < slot->NumChannels;c++)
memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
}
ctx = ATOMIC_LOAD(&device->ContextList, almemory_order_acquire);
while(ctx)
{
const struct ALeffectslotArray *auxslots;
auxslots = ATOMIC_LOAD(&ctx->ActiveAuxSlots, almemory_order_acquire);
UpdateContextSources(ctx, auxslots);
for(i = 0;i < auxslots->count;i++)
{
ALeffectslot *slot = auxslots->slot[i];
for(c = 0;c < slot->NumChannels;c++)
memset(slot->WetBuffer[c], 0, SamplesToDo*sizeof(ALfloat));
}
/* source processing */
voice = ctx->Voices;
voice_end = voice + ctx->VoiceCount;
for(;voice != voice_end;++voice)
{
source = ATOMIC_LOAD(&(*voice)->Source, almemory_order_acquire);
if(source
&&
ATOMIC_LOAD(&(*voice)->Playing, almemory_order_relaxed)
&&
(*voice)->Step > 0)
{
if(!MixSource(*voice, source, device, SamplesToDo))
{
ATOMIC_STORE(&(*voice)->Source, NULL, almemory_order_relaxed);
ATOMIC_STORE(&(*voice)->Playing, false, almemory_order_release);
}
}
}
prev_state = NULL; //by stone
/* effect slot processing */
for(i = 0;i < auxslots->count;i++)
{
ALeffectslot* slot = auxslots->slot[i];
ALeffectState* state = slot->Params.EffectState;
if( prev_state )
{
for(k=0; k<prev_state->OutChannels; k++ )
memcpy(slot->WetBuffer[k], prev_state->OutBuffer[k], sizeof(ALfloat)*BUFFERSIZE);
}
V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer, state->OutChannels);
prev_state = state;
}
ctx = ctx->next;
}
if(device->DefaultSlot != NULL)
{
const ALeffectslot *slot = device->DefaultSlot;
ALeffectState *state = slot->Params.EffectState;
V(state,process)(SamplesToDo, slot->WetBuffer, state->OutBuffer, state->OutChannels);
}
/* Increment the clock time. Every second's worth of samples is
* converted and added to clock base so that large sample counts don't
* overflow during conversion. This also guarantees an exact, stable
* conversion. */
device->SamplesDone += SamplesToDo;
device->ClockBase += (device->SamplesDone/device->Frequency) * DEVICE_CLOCK_RES;
device->SamplesDone %= device->Frequency;
IncrementRef(&device->MixCount);
if(device->HrtfHandle)
{
HrtfDirectMixerFunc HrtfMix;
DirectHrtfState *state;
int lidx, ridx;
if(device->AmbiUp)
ambiup_process(device->AmbiUp,
device->Dry.Buffer, device->Dry.NumChannels,
SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
);
lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
ridx = GetChannelIdxByName(device->RealOut, FrontRight);
assert(lidx != -1 && ridx != -1);
HrtfMix = SelectHrtfMixer();
state = device->Hrtf;
for(c = 0;c < device->Dry.NumChannels;c++)
{
HrtfMix(device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
device->Dry.Buffer[c], state->Offset, state->IrSize,
SAFE_CONST(ALfloat2*,state->Chan[c].Coeffs),
state->Chan[c].Values, SamplesToDo
);
}
state->Offset += SamplesToDo;
}
else if(device->AmbiDecoder)
{
if(device->Dry.Buffer != device->FOAOut.Buffer)
bformatdec_upSample(device->AmbiDecoder,
device->Dry.Buffer, SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer),
device->FOAOut.NumChannels, SamplesToDo
);
bformatdec_process(device->AmbiDecoder,
device->RealOut.Buffer, device->RealOut.NumChannels,
SAFE_CONST(ALfloatBUFFERSIZE*,device->Dry.Buffer), SamplesToDo
);
}
else if(device->AmbiUp)
{
ambiup_process(device->AmbiUp,
device->RealOut.Buffer, device->RealOut.NumChannels,
SAFE_CONST(ALfloatBUFFERSIZE*,device->FOAOut.Buffer), SamplesToDo
);
}
else if(device->Uhj_Encoder)
{
int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
if(lidx != -1 && ridx != -1)
{
/* Encode to stereo-compatible 2-channel UHJ output. */
EncodeUhj2(device->Uhj_Encoder,
device->RealOut.Buffer[lidx], device->RealOut.Buffer[ridx],
device->Dry.Buffer, SamplesToDo
);
}
}
else if(device->Bs2b)
{
int lidx = GetChannelIdxByName(device->RealOut, FrontLeft);
int ridx = GetChannelIdxByName(device->RealOut, FrontRight);
if(lidx != -1 && ridx != -1)
{
/* Apply binaural/crossfeed filter */
bs2b_cross_feed(device->Bs2b, device->RealOut.Buffer[lidx],
device->RealOut.Buffer[ridx], SamplesToDo);
}
}
if(buffer)
{
ALfloat (*OutBuffer)[BUFFERSIZE] = device->RealOut.Buffer;
ALsizei OutChannels = device->RealOut.NumChannels;
DistanceComp *DistComp;
if(device->LimiterGain > 0.0f)
{
/* Limiter attack drops -80dB in 50ms. */
const ALfloat AttackRate = powf(0.0001f, 1.0f/(device->Frequency*0.05f));
/* Limiter release raises +80dB in 1s. */
const ALfloat ReleaseRate = powf(0.0001f, 1.0f/(device->Frequency*1.0f));
/* Use NFCtrlData for temp gain storage. */
device->LimiterGain = ApplyLimiter(OutBuffer, OutChannels,
AttackRate, ReleaseRate, device->LimiterGain, device->NFCtrlData,
SamplesToDo
);
}
DistComp = device->ChannelDelay;
Write_##T(SAFE_CONST(ALfloatBUFFERSIZE*,(a)), (b), (c), (d), (e)); \
buffer = (T*)buffer + (d)*(e); \
} while(0) switch(device->FmtType) { case DevFmtByte: WRITE(ALbyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtUByte: WRITE(ALubyte, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtShort: WRITE(ALshort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtUShort: WRITE(ALushort, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtInt: WRITE(ALint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtUInt: WRITE(ALuint, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; case DevFmtFloat: WRITE(ALfloat, OutBuffer, buffer, DistComp, SamplesToDo, OutChannels); break; }
}
size -= SamplesToDo;
}
RestoreFPUMode(&oldMode);
}
That won't work as well as you expect. That only happens to work because of the implied order effects are processed in relative to when the slots were created, and will break as soon as you try to start using more effects with different sources. It's also overwriting the mix each successive effect has from sources.
It's something I'll get around to doing sometime after the coming release, which should be soon.
ok.... thanks waiting waiting
today I try this way and if have 2 effects mixed .. its sound nice code base: 2017/05/02
int main(int argc, char *argv) { EFXEAXREVERBPROPERTIES reverb = EFX_REVERB_PRESET_UNDERWATER; ALuint source, buffer, effect00, effect01, slot00, slot01; ALenum state; ALCint attr[5] = { ALC_HRTF_SOFT, ALC_FALSE, ALC_OUTPUT_LIMITER_SOFT, ALC_FALSE, 0}; ALCdevice device;
/* Print out usage if no arguments were specified */
if(argc < 2)
{
fprintf(stderr, "Usage: %s [-device <name] <filename>\n", argv[0]);
return 1;
}
/* Initialize OpenAL, and check for EFX support. */
argv++; argc--;
if(InitAL(&argv, &argc) != 0)
return 1;
if(!alcIsExtensionPresent(alcGetContextsDevice(alcGetCurrentContext()), "ALC_EXT_EFX"))
{
fprintf(stderr, "Error: EFX not supported\n");
CloseAL();
return 1;
}
/* Define a macro to help load the function pointers. */
LOAD_PROC(alGenEffects);
LOAD_PROC(alDeleteEffects);
LOAD_PROC(alIsEffect);
LOAD_PROC(alEffecti);
LOAD_PROC(alEffectiv);
LOAD_PROC(alEffectf);
LOAD_PROC(alEffectfv);
LOAD_PROC(alGetEffecti);
LOAD_PROC(alGetEffectiv);
LOAD_PROC(alGetEffectf);
LOAD_PROC(alGetEffectfv);
LOAD_PROC(alGenAuxiliaryEffectSlots);
LOAD_PROC(alDeleteAuxiliaryEffectSlots);
LOAD_PROC(alIsAuxiliaryEffectSlot);
LOAD_PROC(alAuxiliaryEffectSloti);
LOAD_PROC(alAuxiliaryEffectSlotiv);
LOAD_PROC(alAuxiliaryEffectSlotf);
LOAD_PROC(alAuxiliaryEffectSlotfv);
LOAD_PROC(alGetAuxiliaryEffectSloti);
LOAD_PROC(alGetAuxiliaryEffectSlotiv);
LOAD_PROC(alGetAuxiliaryEffectSlotf);
LOAD_PROC(alGetAuxiliaryEffectSlotfv);
device = alcGetContextsDevice(alcGetCurrentContext());
LOAD_PROC(device, alcResetDeviceSOFT);
/* Initialize SDL_sound. */
//Sound_Init();
if(!alcResetDeviceSOFT(device, attr))
printf("Failed to reset device: %s\n", alcGetString(device, alcGetError(device)));
/* Load the sound into a buffer. */
//buffer = LoadSound(argv[0]);
buffer = LoadSound(argv[0]);
if(!buffer)
{
CloseAL();
Sound_Quit();
return 1;
}
/* Load the reverb into an effect00. */
effect00 = LoadEffect(&reverb);
//effect00 = LoadEffect_EQ();
if(!effect00)
{
alDeleteBuffers(1, &buffer);
Sound_Quit();
CloseAL();
return 1;
}
slot00 = 0;
alGenAuxiliaryEffectSlots(1, &slot00);
alAuxiliaryEffectSloti(slot00, AL_EFFECTSLOT_EFFECT, effect00);
assert(alGetError()==AL_NO_ERROR && "Failed to set effect00 slot00");
////////////// //effect01 = LoadEffect(&reverb); effect01 = LoadEffect_EQ(); //effect01 = LoadCompressor(); if(!effect01) { alDeleteBuffers(1, &buffer); Sound_Quit(); CloseAL(); return 1; }
slot01 = 0;
alGenAuxiliaryEffectSlots(1, &slot01);
alAuxiliaryEffectSloti(slot01, AL_EFFECTSLOT_EFFECT, effect01);
assert(alGetError()==AL_NO_ERROR && "Failed to set effect01 slot01");
/* Create the source to play the sound with. */
source = 0;
alGenSources(1, &source);
alSourcei(source, AL_BUFFER, buffer);
/* Connect the source to the effect00 slot00. This tells the source to use the
* effect00 slot00 'slot00', on send #0 with the AL_FILTER_NULL filter object.
*/
alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot00, 0, AL_FILTER_NULL);
assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot01, 1, AL_FILTER_NULL);
assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
/* Play the sound until it finishes. */
alSourcePlay(source);
do {
al_nssleep(10000000);
alGetSourcei(source, AL_SOURCE_STATE, &state);
} while(alGetError() == AL_NO_ERROR && state == AL_PLAYING);
/* All done. Delete resources, and close down SDL_sound and OpenAL. */
alDeleteSources(1, &source);
alDeleteAuxiliaryEffectSlots(1, &slot00);
alDeleteAuxiliaryEffectSlots(1, &slot01);
alDeleteEffects(1, &effect00);
alDeleteEffects(1, &effect01);
alDeleteBuffers(1, &buffer);
//Sound_Quit();
CloseAL();
return 0;
}
I use codebase 20170502 to android system mix with eq, reverb, hrtf, compressor set limiter disable because i use compressor
It's works fine
///////////////////////////////////////////////////////////////////// OpenAL32/Include/alSource.h //#define DEFAULT_SENDS 2 to
//////////////////////////////////// ALint h_attr[] = { ALC_HRTF_SOFT, ALC_TRUE, ALC_HRTF_ID_SOFT, 0, //SELECT 0-44100KHZ or 1-48000KHZ ALC_OUTPUT_LIMITER_SOFT, ALC_FALSE, 0 };
alcResetDeviceSOFT(s_al_device, h_attr);
then use as follows alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot00, 0, AL_FILTER_NULL); alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot01, 1, AL_FILTER_NULL); alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot02, 2, AL_FILTER_NULL);
they will auto mix all effect in android
a question need help~ in mixer.c slot wetbuffer voice->Send[1].Params[0].Gains, in channel 0 Current = 1, 1.73205066 voice->Send[1].Params[1].Gains, in channel 1 Current = 1, -1.73205066
then go to
MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer, parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize);
and slot->WetBuffer address = voice->Send[send].Buffer
why use 1, 1.7 and 1, -1.7 mix source to slot wetbuffer why not 1,0 and 0,1 mix source to slot wetbuffer +-1.7 are angles? (like FFT)
can we modify to ? voice->Send[1].Params[0].Gains, in channel 0 Current = 1, 1.73205066 voice->Send[1].Params[1].Gains, in channel 1 Current = 1, +1.73205066 voice->Send[1].Params[2].Gains, in channel 2 Current = 1, 1.73205066 voice->Send[1].Params[3].Gains, in channel 3 Current = 1, -1.73205066
OpenAL32/Include/alSource.h //#define DEFAULT_SENDS 2 to
define DEFAULT_SENDS 4
There's no need to do that. Just request how many you want when creating a context or resetting the device.
ALC_HRTF_ID_SOFT, 0, //SELECT 0-44100KHZ or 1-48000KHZ
Don't assume that. The sample rate of the HRTFs are system dependent (depends on the HRTFs the user has installed, the order the system enumerated them in, and what the user may have set as the preferred one).
alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot00, 0, AL_FILTER_NULL); alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot01, 1, AL_FILTER_NULL); alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot02, 2, AL_FILTER_NULL);
This will mix the source to multiple effects that are processed separately. Your previous change to copy the output of one to the input of the next breaks the necessary effects processing chain, and only does what you want by luck.
why use 1, 1.7 and 1, -1.7 mix source to slot wetbuffer why not 1,0 and 0,1 mix source to slot wetbuffer
Because the wet buffer is first-order B-Format (with N3D scaling), not stereo. That retains easy compatibility for effects that process in mono, and allows other effects to do surround sound (and full 3D) processing without relying on specific speaker layouts.
thanks a lot for ur answer and define DEFAULT_SENDS 4 need to 4 or alsource.c will Set error
case AL_AUXILIARY_SEND_FILTER: LockEffectSlotsRead(Context); LockFiltersRead(device); if(!((ALuint)values[1] < (ALuint)device->NumAuxSends && (values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL) && (values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL))) { UnlockFiltersRead(device); UnlockEffectSlotsRead(Context); SET_ERROR_AND_RETURN_VALUE(Context, AL_INVALID_VALUE, AL_FALSE); }
thanks a lot for ur answer and define DEFAULT_SENDS 4 need to 4 or alsource.c will Set error
It sets an error because you're using more than were allocated, which is why you request more:
ALCint attr[] = {
ALC_MAX_AUXILIARY_SENDS, 4,
0
};
context = alcCreateContext(device, attr);
roger that thanks
A. this is current openal flow, its parallel add effect to outbuffer http://www.mediafire.com/view/ak91ug2suk21of5/al_a.PNG# B. this is step by step serial process effect http://www.mediafire.com/view/ytnqtzxo1a3xgo7/al_b.PNG#
why Openal use A flow to process effect and add to out not B which is step by step then add to out?
Use the B the sampleIn address are the same, source only need mix once to WETbuffer its faster than A
A is the current flow because that's how EFX is designed. You do want the effects to be processed separately sometimes, since there isn't always a desired order (e.g. multiple reverb zones, or separate chorus and reverb processors). A new feature is needed to do multiple effects in series.
There's already a feature request for chaining multiple effects, so I'm closing this as a duplicate.
thanks a lot man
and I want to mix slot00 and slot01 or (efffect00 and effect01 and .....)
efffect00 -> effect01 -> effect02 and final out~
many effects mix and not one~~~ can support it?