Beispiel #1
0
/**
 * @brief Open an audio output device
 */
bool OpenAL2::initOutput(const QString& deviceName)
{
    peerSources.clear();

    outputInitialized = false;
    if (!Settings::getInstance().getAudioOutDevEnabled()) {
        return false;
    }

    qDebug() << "Opening audio output" << deviceName;
    assert(!alOutDev);

    const QByteArray qDevName = deviceName.toUtf8();
    const ALchar* tmpDevName = qDevName.isEmpty() ? nullptr : qDevName.constData();
    alOutDev = alcOpenDevice(tmpDevName);

    if (!alOutDev) {
        qWarning() << "Cannot open output audio device" << deviceName;
        return false;
    }

    qDebug() << "Opened audio output" << deviceName;
    alOutContext = alcCreateContext(alOutDev, nullptr);
    checkAlcError(alOutDev);

    if (!alcMakeContextCurrent(alOutContext)) {
        qWarning() << "Cannot create output audio context";
        return false;
    }

    // try to init echo cancellation
    echoCancelSupported = initOutputEchoCancel();

    if (!echoCancelSupported) {
        // fallback to normal, no proxy device needed
        qDebug() << "Echo cancellation disabled";
        alProxyDev = alOutDev;
        alProxyContext = alOutContext;
    }

    alGenSources(1, &alMainSource);
    checkAlError();

    // init master volume
    alListenerf(AL_GAIN, Settings::getInstance().getOutVolume() * 0.01f);
    checkAlError();

    Core* core = Core::getInstance();
    if (core) {
        // reset each call's audio source
        core->getAv()->invalidateCallSources();
    }

    // ensure alProxyContext is active
    alcMakeContextCurrent(alProxyContext);
    outputInitialized = true;
    return true;
}
Beispiel #2
0
/**
 * @brief Open an audio output device
 */
bool Audio::initOutput(const QString& deviceName)
{
    outSources.clear();

    outputInitialized = false;
    if (!Settings::getInstance().getAudioOutDevEnabled())
        return false;

    qDebug() << "Opening audio output" << deviceName;
    assert(!alOutDev);

    const QByteArray qDevName = deviceName.toUtf8();
    const ALchar* tmpDevName = qDevName.isEmpty()
                               ? nullptr
                               : qDevName.constData();
    alOutDev = alcOpenDevice(tmpDevName);

    if (!alOutDev)
    {
        qWarning() << "Cannot open output audio device" << deviceName;
        return false;
    }

    qDebug() << "Opened audio output" << deviceName;
    alOutContext = alcCreateContext(alOutDev, nullptr);
    checkAlcError(alOutDev);

    if (!alcMakeContextCurrent(alOutContext))
    {
        qWarning() << "Cannot create output audio context";
        return false;
    }

    alGenSources(1, &alMainSource);
    checkAlError();

    // init master volume
    alListenerf(AL_GAIN, Settings::getInstance().getOutVolume() * 0.01f);
    checkAlError();

    Core* core = Core::getInstance();
    if (core)
    {
        // reset each call's audio source
        core->getAv()->invalidateCallSources();
    }

    outputInitialized = true;
    return true;
}
Beispiel #3
0
/**
 * @brief Set the master output volume.
 *
 * @param[in] volume   the master volume (between 0 and 1)
 */
void OpenAL::setOutputVolume(qreal volume)
{
    QMutexLocker locker(&audioLock);

    volume = std::max(0.0, std::min(volume, 1.0));

    alListenerf(AL_GAIN, static_cast<ALfloat>(volume));
    checkAlError();
}
Beispiel #4
0
/**
 * @brief Handle audio output
 */
void OpenAL2::doOutput()
{
    alcMakeContextCurrent(alOutContext);
    ALuint bufids[PROXY_BUFFER_COUNT];
    ALint processed = 0, queued = 0;
    alGetSourcei(alProxySource, AL_BUFFERS_PROCESSED, &processed);
    alGetSourcei(alProxySource, AL_BUFFERS_QUEUED, &queued);

    if (processed > 0) {
        // unqueue all processed buffers
        alSourceUnqueueBuffers(alProxySource, processed, bufids);
        // delete all but the first buffer, reuse first for new data
        alDeleteBuffers(processed - 1, bufids + 1);
    } else if (queued < PROXY_BUFFER_COUNT) {
        // create new buffer until the maximum is reached
        alGenBuffers(1, bufids);
    } else {
        alcMakeContextCurrent(alProxyContext);
        return;
    }

    ALdouble latency[2] = {0};
    alGetSourcedvSOFT(alProxySource, AL_SEC_OFFSET_LATENCY_SOFT, latency);
    checkAlError();

    ALshort outBuf[AUDIO_FRAME_SAMPLE_COUNT] = {0};
    alcMakeContextCurrent(alProxyContext);
    alcRenderSamplesSOFT(alProxyDev, outBuf, AUDIO_FRAME_SAMPLE_COUNT);
    checkAlcError(alProxyDev);

    alcMakeContextCurrent(alOutContext);
    alBufferData(bufids[0], AL_FORMAT_MONO16, outBuf, AUDIO_FRAME_SAMPLE_COUNT * 2, AUDIO_SAMPLE_RATE);
    alSourceQueueBuffers(alProxySource, 1, bufids);

    // initialize echo canceler if supported
    if (!filterer) {
        filterer = new_filter_audio(AUDIO_SAMPLE_RATE);
        int16_t filterLatency = latency[1] * 1000 * 2 + AUDIO_FRAME_DURATION;
        qDebug() << "Setting filter delay to: " << filterLatency << "ms";
        set_echo_delay_ms(filterer, filterLatency);
        enable_disable_filters(filterer, 1, 1, 1, 0);
    }

    // do echo cancel
    pass_audio_output(filterer, outBuf, AUDIO_FRAME_SAMPLE_COUNT);

    ALint state;
    alGetSourcei(alProxySource, AL_SOURCE_STATE, &state);
    if (state != AL_PLAYING) {
        qDebug() << "Proxy source underflow detected";
        alSourcePlay(alProxySource);
    }
    alcMakeContextCurrent(alProxyContext);
}
 void OpenALRenderableSource::updateImpl(int flags, const Listener& sceneListener) 
     throw(Exception)
 {
     Source *source = getSource();
     ALSourceHandle als = getALSource();
     
     clearAlError();
     
     if (flags & UPDATE_ATTRIBUTES) {
         // Distance attenuation
         if (source->isAttenuated()) {
             alSourcef(als, AL_REFERENCE_DISTANCE, source->getRadius());
             alSourcef(als, AL_ROLLOFF_FACTOR, 1.f);
         } else {
             alSourcef(als, AL_ROLLOFF_FACTOR, 0.f);
         }
         // Cone
         {
             Range<Scalar> angleRange = source->getAngleRange();
             alSourcef(als, AL_CONE_INNER_ANGLE, float(angleRange.min) * M_1_PI * 360.f);
             alSourcef(als, AL_CONE_OUTER_ANGLE, float(angleRange.max) * M_1_PI * 360.f);
             alSourcef(als, AL_CONE_OUTER_GAIN , 0.f);
         }
         // Relativity
         alSourcei(als, AL_SOURCE_RELATIVE, source->isRelative() ? AL_TRUE : AL_FALSE);
         // Looping
         alSourcei(als, AL_LOOPING, source->isLooping() ? AL_TRUE : AL_FALSE);
     }
     if (flags & UPDATE_GAIN) {
         // Gain
         alSourcef(als, AL_GAIN, source->getGain());
     }
     if (flags & UPDATE_LOCATION) {
         if (source->isRelative()) {
             alSource3f(als, AL_POSITION, source->getPosition());
             alSource3f(als, AL_VELOCITY, source->getVelocity());
             alSource3f(als, AL_DIRECTION, source->getDirection());
         } else {
             alSource3f(als, AL_POSITION, 
                 source->getPosition() - sceneListener.getPosition() );
             alSource3f(als, AL_VELOCITY, 
                 sceneListener.toLocalDirection(
                     source->getVelocity() - sceneListener.getVelocity()
                 ) );
             alSource3f(als, AL_DIRECTION, 
                 sceneListener.toLocalDirection(
                     source->getDirection()
                 ) );
         }
     }
     
     checkAlError();
 }
Beispiel #6
0
/**
 * @brief Returns the current output volume (between 0 and 1)
 */
qreal OpenAL::outputVolume() const
{
    QMutexLocker locker(&audioLock);

    ALfloat volume = 0.0;

    if (alOutDev) {
        alGetListenerf(AL_GAIN, &volume);
        checkAlError();
    }

    return volume;
}
 void OpenALRenderableSource::startPlayingImpl(Timestamp start) 
     throw(Exception)
 {
     if (!isPlayingImpl()) {
         // Make sure we have an attached sound
         attachALBuffers();
         
         // Tell the AL to start playing (from the specified position)
         clearAlError();
         ALuint als = getALSource();
         alSourcePlay(als);
         checkAlError();
         
         if (start != 0)
             seekImpl(start);
     }
 }
 void OpenALRenderableSource::attachALBuffers()
     throw(Exception)
 {
     if (!alBuffersAttached) {
         SharedPtr<Sound> sound = getSource()->getSound();
         
         if (!sound->isLoaded())
             sound->load();
         
         assert(!sound->isStreaming() && "OpenALRenderableSource can only handle streaming sounds");
         
         // Attachment to a simple sound, just assign the AL buffer to this AL source
         ALBufferHandle alBuffer = dynamic_cast<OpenALSimpleSound*>(sound.get())->getAlBuffer();
         ALSourceHandle alSource = getALSource();
         alSourcei(alSource, AL_BUFFER, alBuffer);
         alBuffersAttached = true;
         
         checkAlError();
     }
 }
Beispiel #9
0
/**
 * @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;
}