mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 14:58:03 +02:00
Merge pull request #12226 from kencooke/audio-options-pitch
Add "pitch" property to allow pitch shifting an audio effect
This commit is contained in:
commit
3da8e5249c
3 changed files with 45 additions and 25 deletions
|
@ -427,28 +427,11 @@ AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const fl
|
||||||
options.stereo = sound->isStereo();
|
options.stereo = sound->isStereo();
|
||||||
options.position = position;
|
options.position = position;
|
||||||
options.volume = volume;
|
options.volume = volume;
|
||||||
|
options.pitch = 1.0f / stretchFactor;
|
||||||
|
|
||||||
QByteArray samples = sound->getByteArray();
|
QByteArray samples = sound->getByteArray();
|
||||||
if (stretchFactor == 1.0f) {
|
|
||||||
return playSoundAndDelete(samples, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int standardRate = AudioConstants::SAMPLE_RATE;
|
return playSoundAndDelete(samples, options);
|
||||||
const int resampledRate = standardRate * stretchFactor;
|
|
||||||
const int channelCount = sound->isStereo() ? 2 : 1;
|
|
||||||
|
|
||||||
AudioSRC resampler(standardRate, resampledRate, channelCount);
|
|
||||||
|
|
||||||
const int nInputFrames = samples.size() / (channelCount * sizeof(int16_t));
|
|
||||||
const int maxOutputFrames = resampler.getMaxOutput(nInputFrames);
|
|
||||||
QByteArray resampled(maxOutputFrames * channelCount * sizeof(int16_t), '\0');
|
|
||||||
|
|
||||||
int nOutputFrames = resampler.render(reinterpret_cast<const int16_t*>(samples.data()),
|
|
||||||
reinterpret_cast<int16_t*>(resampled.data()),
|
|
||||||
nInputFrames);
|
|
||||||
|
|
||||||
Q_UNUSED(nOutputFrames);
|
|
||||||
return playSoundAndDelete(resampled, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) {
|
AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) {
|
||||||
|
@ -461,12 +444,40 @@ AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer,
|
||||||
return sound;
|
return sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) {
|
AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) {
|
||||||
AudioInjectorPointer injector = AudioInjectorPointer::create(buffer, options);
|
|
||||||
|
|
||||||
if (!injector->inject(&AudioInjectorManager::threadInjector)) {
|
if (options.pitch == 1.0f) {
|
||||||
qWarning() << "AudioInjector::playSound failed to thread injector";
|
|
||||||
|
AudioInjectorPointer injector = AudioInjectorPointer::create(buffer, options);
|
||||||
|
|
||||||
|
if (!injector->inject(&AudioInjectorManager::threadInjector)) {
|
||||||
|
qWarning() << "AudioInjector::playSound failed to thread injector";
|
||||||
|
}
|
||||||
|
return injector;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
const int standardRate = AudioConstants::SAMPLE_RATE;
|
||||||
|
const int resampledRate = AudioConstants::SAMPLE_RATE / glm::clamp(options.pitch, 1/16.0f, 16.0f); // limit to 4 octaves
|
||||||
|
const int numChannels = options.ambisonic ? AudioConstants::AMBISONIC :
|
||||||
|
(options.stereo ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||||
|
|
||||||
|
AudioSRC resampler(standardRate, resampledRate, numChannels);
|
||||||
|
|
||||||
|
// create a resampled buffer that is guaranteed to be large enough
|
||||||
|
const int nInputFrames = buffer.size() / (numChannels * sizeof(int16_t));
|
||||||
|
const int maxOutputFrames = resampler.getMaxOutput(nInputFrames);
|
||||||
|
QByteArray resampledBuffer(maxOutputFrames * numChannels * sizeof(int16_t), '\0');
|
||||||
|
|
||||||
|
resampler.render(reinterpret_cast<const int16_t*>(buffer.data()),
|
||||||
|
reinterpret_cast<int16_t*>(resampledBuffer.data()),
|
||||||
|
nInputFrames);
|
||||||
|
|
||||||
|
AudioInjectorPointer injector = AudioInjectorPointer::create(resampledBuffer, options);
|
||||||
|
|
||||||
|
if (!injector->inject(&AudioInjectorManager::threadInjector)) {
|
||||||
|
qWarning() << "AudioInjector::playSound failed to thread pitch-shifted injector";
|
||||||
|
}
|
||||||
|
return injector;
|
||||||
}
|
}
|
||||||
return injector;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@ AudioInjectorOptions::AudioInjectorOptions() :
|
||||||
ambisonic(false),
|
ambisonic(false),
|
||||||
ignorePenumbra(false),
|
ignorePenumbra(false),
|
||||||
localOnly(false),
|
localOnly(false),
|
||||||
secondOffset(0.0f)
|
secondOffset(0.0f),
|
||||||
|
pitch(1.0f)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +41,7 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje
|
||||||
obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra);
|
obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra);
|
||||||
obj.setProperty("localOnly", injectorOptions.localOnly);
|
obj.setProperty("localOnly", injectorOptions.localOnly);
|
||||||
obj.setProperty("secondOffset", injectorOptions.secondOffset);
|
obj.setProperty("secondOffset", injectorOptions.secondOffset);
|
||||||
|
obj.setProperty("pitch", injectorOptions.pitch);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +89,12 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt
|
||||||
} else {
|
} else {
|
||||||
qCWarning(audio) << "Audio injector options: secondOffset is not a number";
|
qCWarning(audio) << "Audio injector options: secondOffset is not a number";
|
||||||
}
|
}
|
||||||
|
} else if (it.name() == "pitch") {
|
||||||
|
if (it.value().isNumber()) {
|
||||||
|
injectorOptions.pitch = it.value().toNumber();
|
||||||
|
} else {
|
||||||
|
qCWarning(audio) << "Audio injector options: pitch is not a number";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(audio) << "Unknown audio injector option:" << it.name();
|
qCWarning(audio) << "Unknown audio injector option:" << it.name();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ public:
|
||||||
bool ignorePenumbra;
|
bool ignorePenumbra;
|
||||||
bool localOnly;
|
bool localOnly;
|
||||||
float secondOffset;
|
float secondOffset;
|
||||||
|
float pitch; // multiplier, where 2.0f shifts up one octave
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AudioInjectorOptions);
|
Q_DECLARE_METATYPE(AudioInjectorOptions);
|
||||||
|
|
Loading…
Reference in a new issue