Put the common stuff on AudioInjector rather than AudioClient, so that assignment-client (which depends on script-engine) does not need to depend on audio-client.

This commit is contained in:
Howard Stearns 2015-06-19 14:20:00 -07:00
parent 37d6e21ce3
commit dc10e15e96
8 changed files with 83 additions and 79 deletions

View file

@ -45,7 +45,6 @@ extern "C" {
#include <PositionalAudioStream.h>
#include <SettingHandle.h>
#include <SharedUtil.h>
#include <SoundCache.h>
#include <UUID.h>
#include "AudioInjector.h"
@ -1339,71 +1338,3 @@ void AudioClient::audioStateChanged(QAudio::State state) {
emit audioFinished();
}
}
AudioInjector* AudioClient::playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position) {
if (soundUrl.isEmpty()) {
return NULL;
}
auto soundCache = DependencyManager::get<SoundCache>();
if (soundCache.isNull()) {
return NULL;
}
SharedSoundPointer sound = soundCache.data()->getSound(QUrl(soundUrl));
if (sound.isNull() || !sound->isReady()) {
return NULL;
}
// Quiet sound aren't really heard at all, so we can compress everything to the range [1-c, 1], if we play it all.
const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes.
const float compressedVolume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
// This is quite similar to AudioScriptingInterface::playSound() and should probably be refactored.
AudioInjectorOptions options;
options.stereo = sound->isStereo();
options.position = position;
options.volume = compressedVolume;
QByteArray samples = sound->getByteArray();
if (stretchFactor == 1.0f) {
return playSound(samples, options);
}
soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
const int channelCount = sound->isStereo() ? 2 : 1;
const int standardRate = AudioConstants::SAMPLE_RATE;
const int resampledRate = standardRate * stretchFactor;
const int nInputSamples = samples.size() / sizeof(int16_t);
const int nOutputSamples = nInputSamples * stretchFactor;
QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0');
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(samples.data());
soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount,
receivedSamples, nInputSamples, NULL,
reinterpret_cast<int16_t*>(resampled.data()), nOutputSamples, NULL,
&spec, &qualitySpec, 0);
if (soxError) {
qCDebug(audioclient) << "Unable to resample" << soundUrl << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate;
resampled = samples;
}
return playSound(resampled, options);
}
AudioInjector* AudioClient::playSound(const QByteArray& buffer, const AudioInjectorOptions options) {
QThread* injectorThread = new QThread();
injectorThread->setObjectName("Audio Injector Thread");
AudioInjector* injector = new AudioInjector(buffer, options);
injector->setLocalAudioInterface(this);
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
// connect the right slots and signals for AudioInjector and thread cleanup
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
return injector;
}

View file

@ -131,10 +131,6 @@ public:
static const float CALLBACK_ACCELERATOR_RATIO;
AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options);
AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position);
public slots:
void start();
void stop();

View file

@ -7,4 +7,10 @@ add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(networking shared)
# we use libsoxr for resampling
add_dependency_external_projects(soxr)
find_package(Soxr REQUIRED)
target_link_libraries(${TARGET_NAME} ${SOXR_LIBRARIES})
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SOXR_INCLUDE_DIRS})
link_hifi_libraries(networking shared)

View file

@ -16,10 +16,12 @@
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UUID.h>
#include <soxr.h>
#include "AbstractAudioInterface.h"
#include "AudioRingBuffer.h"
#include "AudioLogging.h"
#include "SoundCache.h"
#include "AudioInjector.h"
@ -284,3 +286,66 @@ void AudioInjector::stopAndDeleteLater() {
stop();
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
}
AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position) {
if (soundUrl.isEmpty()) {
return NULL;
}
auto soundCache = DependencyManager::get<SoundCache>();
if (soundCache.isNull()) {
return NULL;
}
SharedSoundPointer sound = soundCache.data()->getSound(QUrl(soundUrl));
if (sound.isNull() || !sound->isReady()) {
return NULL;
}
AudioInjectorOptions options;
options.stereo = sound->isStereo();
options.position = position;
options.volume = volume;
QByteArray samples = sound->getByteArray();
if (stretchFactor == 1.0f) {
return playSound(samples, options, NULL);
}
soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I);
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
const int channelCount = sound->isStereo() ? 2 : 1;
const int standardRate = AudioConstants::SAMPLE_RATE;
const int resampledRate = standardRate * stretchFactor;
const int nInputSamples = samples.size() / sizeof(int16_t);
const int nOutputSamples = nInputSamples * stretchFactor;
QByteArray resampled(nOutputSamples * sizeof(int16_t), '\0');
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(samples.data());
soxr_error_t soxError = soxr_oneshot(standardRate, resampledRate, channelCount,
receivedSamples, nInputSamples, NULL,
reinterpret_cast<int16_t*>(resampled.data()), nOutputSamples, NULL,
&spec, &qualitySpec, 0);
if (soxError) {
qCDebug(audio) << "Unable to resample" << soundUrl << "from" << nInputSamples << "@" << standardRate << "to" << nOutputSamples << "@" << resampledRate;
resampled = samples;
}
return playSound(resampled, options, NULL);
}
AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) {
QThread* injectorThread = new QThread();
injectorThread->setObjectName("Audio Injector Thread");
AudioInjector* injector = new AudioInjector(buffer, options);
injector->setLocalAudioInterface(localInterface);
injector->moveToThread(injectorThread);
// start injecting when the injector thread starts
connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio);
// connect the right slots and signals for AudioInjector and thread cleanup
connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit);
connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater);
injectorThread->start();
return injector;
}

View file

@ -45,6 +45,10 @@ public:
bool isLocalOnly() const { return _options.localOnly; }
void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; }
static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface);
static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position);
public slots:
void injectAudio();
void restart();

View file

@ -19,7 +19,6 @@
#include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h>
#include <AudioClient.h>
#include <DeferredLightingEffect.h>
#include <GlowEffect.h>
#include <Model.h>
@ -1095,11 +1094,15 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) {
return;
}
// Quiet sound aren't really heard at all, so we can compress everything to the range [1-c, 1], if we play it all.
const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes.
const float volume = (energyFactorOfFull * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
const float stretchFactor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
DependencyManager::get<AudioClient>()->playSound(collisionSoundURL, energyFactorOfFull, stretchFactor, position);
AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position);
}
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,

View file

@ -7,4 +7,4 @@ add_dependency_external_projects(glm)
find_package(GLM REQUIRED)
target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
link_hifi_libraries(shared octree gpu model fbx entities animation audio audio-client physics)
link_hifi_libraries(shared octree gpu model fbx entities animation audio physics)

View file

@ -11,7 +11,6 @@
#include "AudioScriptingInterface.h"
#include "AudioClient.h"
#include "ScriptAudioInjector.h"
#include "ScriptEngineLogging.h"
@ -47,7 +46,7 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const Audi
AudioInjectorOptions optionsCopy = injectorOptions;
optionsCopy.stereo = sound->isStereo();
return new ScriptAudioInjector(DependencyManager::get<AudioClient>()->playSound(sound->getByteArray(), optionsCopy));
return new ScriptAudioInjector(AudioInjector::playSound(sound->getByteArray(), optionsCopy, _localAudioInterface));
} else {
qCDebug(scriptengine) << "AudioScriptingInterface::playSound called with null Sound object.";