int main(int argc, char *argv[]) { if(alcIsExtensionPresent(NULL, "ALC_SOFT_loopback") == ALC_FALSE) { fputs("Your OpenAL implementation doesn't support the " "\"ALC_SOFT_loopback\" extension, required for this test. " "Sorry.\n", stderr); exit(EXIT_FAILURE); } ALCint alc_major, alc_minor; alcGetIntegerv(NULL, ALC_MAJOR_VERSION, 1, &alc_major); alcGetIntegerv(NULL, ALC_MINOR_VERSION, 1, &alc_minor); if(alc_major<1 || (alc_major==1 && alc_minor<1)) fputs("Warning : ALC_SOFT_loopback has been written against " "the OpenAL 1.1 specification.\n", stderr); #define HELPER(X) X = alcGetProcAddress(NULL, #X) HELPER(alcLoopbackOpenDeviceSOFT); HELPER(alcIsRenderFormatSupportedSOFT); HELPER(alcRenderSamplesSOFT); #undef HELPER #define HELPER(X) X = alcGetEnumValue(NULL, #X) HELPER(ALC_BYTE_SOFT); HELPER(ALC_UNSIGNED_BYTE_SOFT); HELPER(ALC_SHORT_SOFT); HELPER(ALC_UNSIGNED_SHORT_SOFT); HELPER(ALC_INT_SOFT); HELPER(ALC_UNSIGNED_INT_SOFT); HELPER(ALC_FLOAT_SOFT); HELPER(ALC_MONO_SOFT); HELPER(ALC_STEREO_SOFT); HELPER(ALC_QUAD_SOFT); HELPER(ALC_5POINT1_SOFT); HELPER(ALC_6POINT1_SOFT); HELPER(ALC_7POINT1_SOFT); HELPER(ALC_FORMAT_CHANNELS_SOFT); HELPER(ALC_FORMAT_TYPE_SOFT); #undef HELPER ALCdevice *loopback_device = alcLoopbackOpenDeviceSOFT(NULL); if(!loopback_device) { fputs("Could not open loopback device.\n", stderr); exit(EXIT_FAILURE); } if(alcIsRenderFormatSupportedSOFT(loopback_device, 22050, ALC_STEREO_SOFT, ALC_SHORT_SOFT) == ALC_FALSE) { fputs("The loopback device does not support the " "required render format.\n", stderr); alcCloseDevice(loopback_device); exit(EXIT_FAILURE); } ALCint attrlist[11] = { ALC_MONO_SOURCES, 0, ALC_STEREO_SOURCES, 255, ALC_FREQUENCY, 22050, ALC_FORMAT_CHANNELS_SOFT, ALC_STEREO_SOFT, ALC_FORMAT_TYPE_SOFT, ALC_SHORT_SOFT, 0 }; ALCcontext *ctx = alcCreateContext(loopback_device, attrlist); alcMakeContextCurrent(ctx); alGetError(); /* Clear the error state */ if(argc<=1) { fprintf(stderr, "Usage : %s <small_oggfile>\n", argv[0]); alcCloseDevice(loopback_device); exit(EXIT_FAILURE); } SF_INFO sndinfo; SNDFILE *snd = sf_open(argv[1], SFM_READ, &sndinfo); if(!snd) { fprintf(stderr, "Failed to open \"%s\" : %s\n", argv[1], sf_strerror(snd)); alcCloseDevice(loopback_device); exit(EXIT_FAILURE); } if(sndinfo.channels != 2) { fprintf(stderr, "The source sound file has %d channels " "(exactly 2 are required).\n", sndinfo.channels); alcCloseDevice(loopback_device); exit(EXIT_FAILURE); } short *data = malloc(sndinfo.frames*2*sizeof(short)); printf("Reading from '%s'...\n", argv[1]); sf_readf_short(snd, data, sndinfo.frames); sf_close(snd); ALuint audio_buffer; alGenBuffers(1, &audio_buffer); alBufferData(audio_buffer, AL_FORMAT_STEREO16, data, sndinfo.frames*2*sizeof(short), sndinfo.samplerate); free(data); ALuint audio_source; alGenSources(1, &audio_source); alSourcei(audio_source, AL_BUFFER, audio_buffer); unsigned num_frames = sndinfo.frames; ALCshort *rendered_samples = malloc(num_frames*2*sizeof(ALCshort)); sndinfo.samplerate = 22050; sndinfo.channels = 2; sndinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS | SF_ENDIAN_FILE; snd = sf_open("rendered.ogg", SFM_WRITE, &sndinfo); alSourcePlay(audio_source); puts("Rendering frames..."); unsigned chunk_size = 4096, off; for(off=0 ; off<(num_frames-chunk_size)*2 ; off+=2*chunk_size) alcRenderSamplesSOFT(loopback_device, rendered_samples+off, chunk_size); puts("Writing to 'rendered.ogg'..."); sf_write_short(snd, rendered_samples, off-s); sf_close(snd); free(rendered_samples); alDeleteSources(1, &audio_source); alDeleteBuffers(1, &audio_buffer); alcCloseDevice(loopback_device); alcMakeContextCurrent(NULL); alcDestroyContext(ctx); exit(EXIT_SUCCESS); }
/** * @brief Initializes the output with echo cancelling enabled * @return true on success, false otherwise * Creates a loopback device and a proxy source on the main output device. * If this function returns true only the proxy source should be used for * audio output. */ bool OpenAL2::initOutputEchoCancel() { // check for the needed extensions for echo cancelation if (alcIsExtensionPresent(alOutDev, "ALC_SOFT_loopback") != AL_TRUE) { qDebug() << "Device doesn't support loopback"; return false; } if (alIsExtensionPresent("AL_SOFT_source_latency") != AL_TRUE) { qDebug() << "Device doesn't support source latency"; return false; } if (!loadOpenALExtensions(alOutDev)) { qDebug() << "Couldn't load needed OpenAL extensions"; return false; } // source for proxy output alGenSources(1, &alProxySource); checkAlError(); // configuration for the loopback device ALCint attrs[] = {ALC_FORMAT_CHANNELS_SOFT, ALC_MONO_SOFT, ALC_FORMAT_TYPE_SOFT, ALC_SHORT_SOFT, ALC_FREQUENCY, Audio::AUDIO_SAMPLE_RATE, 0}; // End of List alProxyDev = alcLoopbackOpenDeviceSOFT(NULL); checkAlcError(alProxyDev); if (!alProxyDev) { qDebug() << "Couldn't create proxy device"; alDeleteSources(1, &alProxySource); // cleanup source return false; } if (!alcIsRenderFormatSupportedSOFT(alProxyDev, attrs[5], attrs[1], attrs[3])) { qDebug() << "Unsupported format for loopback"; alcCloseDevice(alProxyDev); // cleanup loopback dev alDeleteSources(1, &alProxySource); // cleanup source return false; } alProxyContext = alcCreateContext(alProxyDev, attrs); checkAlcError(alProxyDev); if (!alProxyContext) { qDebug() << "Couldn't create proxy context"; alcCloseDevice(alProxyDev); // cleanup loopback dev alDeleteSources(1, &alProxySource); // cleanup source return false; } if (!alcMakeContextCurrent(alProxyContext)) { qDebug() << "Cannot activate proxy context"; alcDestroyContext(alProxyContext); alcCloseDevice(alProxyDev); // cleanup loopback dev alDeleteSources(1, &alProxySource); // cleanup source return false; } qDebug() << "Echo cancelation enabled"; return true; }