mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 19:21:16 +02:00
Merge pull request #3489 from Atlante45/radio_js
AudioInjector stereo fix
This commit is contained in:
commit
60d6a0bf0d
7 changed files with 50 additions and 20 deletions
|
@ -10,7 +10,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers2Finished.fbx";
|
var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers.fbx";
|
||||||
var soundURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/FamilyStereo.raw";
|
var soundURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/FamilyStereo.raw";
|
||||||
|
|
||||||
var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||||
|
@ -20,7 +20,7 @@ audioOptions.loop = true;
|
||||||
audioOptions.isStereo = true;
|
audioOptions.isStereo = true;
|
||||||
var injector = null;
|
var injector = null;
|
||||||
|
|
||||||
var sound = new Sound(soundURL);
|
var sound = new Sound(soundURL, audioOptions.isStereo);
|
||||||
|
|
||||||
var entity = null;
|
var entity = null;
|
||||||
var properties = null;
|
var properties = null;
|
||||||
|
@ -31,14 +31,16 @@ function update() {
|
||||||
print("Sound file downloaded");
|
print("Sound file downloaded");
|
||||||
var position = Vec3.sum(MyAvatar.position,
|
var position = Vec3.sum(MyAvatar.position,
|
||||||
Vec3.multiplyQbyV(MyAvatar.orientation,
|
Vec3.multiplyQbyV(MyAvatar.orientation,
|
||||||
{ x: 0, y: 0.3, z: -1 }));
|
{ x: 0, y: -0.3, z: -2 }));
|
||||||
var rotation = Quat.multiply(MyAvatar.orientation,
|
var rotation = Quat.multiply(MyAvatar.orientation,
|
||||||
Quat.fromPitchYawRollDegrees(0, -90, 0));
|
Quat.fromPitchYawRollDegrees(0, -90, 0));
|
||||||
entity = Entities.addEntity({
|
entity = Entities.addEntity({
|
||||||
type: "Model",
|
type: "Model",
|
||||||
position: position,
|
position: position,
|
||||||
rotation: rotation,
|
rotation: rotation,
|
||||||
dimensions: { x: 0.5, y: 0.5, z: 0.5 },
|
dimensions: { x: 0.391,
|
||||||
|
y: 1.000,
|
||||||
|
z: 1.701 },
|
||||||
modelURL: modelURL
|
modelURL: modelURL
|
||||||
});
|
});
|
||||||
properties = Entities.getEntityProperties(entity);
|
properties = Entities.getEntityProperties(entity);
|
||||||
|
|
|
@ -78,11 +78,13 @@ void AudioInjector::injectAudio() {
|
||||||
|
|
||||||
// pack the position for injected audio
|
// pack the position for injected audio
|
||||||
int positionOptionOffset = injectAudioPacket.size();
|
int positionOptionOffset = injectAudioPacket.size();
|
||||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getPosition()), sizeof(_options.getPosition()));
|
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getPosition()),
|
||||||
|
sizeof(_options.getPosition()));
|
||||||
|
|
||||||
// pack our orientation for injected audio
|
// pack our orientation for injected audio
|
||||||
int orientationOptionOffset = injectAudioPacket.size();
|
int orientationOptionOffset = injectAudioPacket.size();
|
||||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getOrientation()), sizeof(_options.getOrientation()));
|
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getOrientation()),
|
||||||
|
sizeof(_options.getOrientation()));
|
||||||
|
|
||||||
// pack zero for radius
|
// pack zero for radius
|
||||||
float radius = 0;
|
float radius = 0;
|
||||||
|
@ -103,7 +105,7 @@ void AudioInjector::injectAudio() {
|
||||||
quint16 outgoingInjectedAudioSequenceNumber = 0;
|
quint16 outgoingInjectedAudioSequenceNumber = 0;
|
||||||
while (_currentSendPosition < soundByteArray.size() && !_shouldStop) {
|
while (_currentSendPosition < soundByteArray.size() && !_shouldStop) {
|
||||||
|
|
||||||
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
|
int bytesToCopy = std::min(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
|
||||||
soundByteArray.size() - _currentSendPosition);
|
soundByteArray.size() - _currentSendPosition);
|
||||||
memcpy(injectAudioPacket.data() + positionOptionOffset,
|
memcpy(injectAudioPacket.data() + positionOptionOffset,
|
||||||
&_options.getPosition(),
|
&_options.getPosition(),
|
||||||
|
@ -116,10 +118,12 @@ void AudioInjector::injectAudio() {
|
||||||
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
|
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
|
||||||
|
|
||||||
// pack the sequence number
|
// pack the sequence number
|
||||||
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, &outgoingInjectedAudioSequenceNumber, sizeof(quint16));
|
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes,
|
||||||
|
&outgoingInjectedAudioSequenceNumber, sizeof(quint16));
|
||||||
|
|
||||||
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
|
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
|
||||||
memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + _currentSendPosition, bytesToCopy);
|
memcpy(injectAudioPacket.data() + numPreAudioDataBytes,
|
||||||
|
soundByteArray.data() + _currentSendPosition, bytesToCopy);
|
||||||
|
|
||||||
// grab our audio mixer from the NodeList, if it exists
|
// grab our audio mixer from the NodeList, if it exists
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
|
|
||||||
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
|
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
|
||||||
|
|
||||||
|
if (sound->isStereo()) {
|
||||||
|
const_cast<AudioInjectorOptions*>(injectorOptions)->setIsStereo(true);
|
||||||
|
}
|
||||||
AudioInjector* injector = new AudioInjector(sound, *injectorOptions);
|
AudioInjector* injector = new AudioInjector(sound, *injectorOptions);
|
||||||
|
|
||||||
QThread* injectorThread = new QThread();
|
QThread* injectorThread = new QThread();
|
||||||
|
|
|
@ -30,7 +30,9 @@ InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, const In
|
||||||
|
|
||||||
const uchar MAX_INJECTOR_VOLUME = 255;
|
const uchar MAX_INJECTOR_VOLUME = 255;
|
||||||
|
|
||||||
int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
int InjectedAudioStream::parseStreamProperties(PacketType type,
|
||||||
|
const QByteArray& packetAfterSeqNum,
|
||||||
|
int& numAudioSamples) {
|
||||||
// setup a data stream to read from this packet
|
// setup a data stream to read from this packet
|
||||||
QDataStream packetStream(packetAfterSeqNum);
|
QDataStream packetStream(packetAfterSeqNum);
|
||||||
|
|
||||||
|
@ -38,6 +40,9 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray
|
||||||
packetStream.skipRawData(NUM_BYTES_RFC4122_UUID);
|
packetStream.skipRawData(NUM_BYTES_RFC4122_UUID);
|
||||||
|
|
||||||
packetStream >> _isStereo;
|
packetStream >> _isStereo;
|
||||||
|
if (isStereo()) {
|
||||||
|
_ringBuffer.resizeForFrameSize(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO);
|
||||||
|
}
|
||||||
|
|
||||||
// pull the loopback flag and set our boolean
|
// pull the loopback flag and set our boolean
|
||||||
uchar shouldLoopback;
|
uchar shouldLoopback;
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
|
|
||||||
// procedural audio version of Sound
|
// procedural audio version of Sound
|
||||||
Sound::Sound(float volume, float frequency, float duration, float decay, QObject* parent) :
|
Sound::Sound(float volume, float frequency, float duration, float decay, QObject* parent) :
|
||||||
QObject(parent)
|
QObject(parent),
|
||||||
|
_isStereo(false)
|
||||||
{
|
{
|
||||||
static char monoAudioData[MAX_PACKET_SIZE];
|
static char monoAudioData[MAX_PACKET_SIZE];
|
||||||
static int16_t* monoAudioSamples = (int16_t*)(monoAudioData);
|
static int16_t* monoAudioSamples = (int16_t*)(monoAudioData);
|
||||||
|
@ -69,8 +70,9 @@ Sound::Sound(float volume, float frequency, float duration, float decay, QObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound::Sound(const QUrl& sampleURL, QObject* parent) :
|
Sound::Sound(const QUrl& sampleURL, bool isStereo, QObject* parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
|
_isStereo(isStereo),
|
||||||
_hasDownloaded(false)
|
_hasDownloaded(false)
|
||||||
{
|
{
|
||||||
// assume we have a QApplication or QCoreApplication instance and use the
|
// assume we have a QApplication or QCoreApplication instance and use the
|
||||||
|
@ -82,12 +84,14 @@ Sound::Sound(const QUrl& sampleURL, QObject* parent) :
|
||||||
|
|
||||||
QNetworkReply* soundDownload = networkAccessManager.get(QNetworkRequest(sampleURL));
|
QNetworkReply* soundDownload = networkAccessManager.get(QNetworkRequest(sampleURL));
|
||||||
connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished);
|
connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished);
|
||||||
connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError)));
|
connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||||
|
this, SLOT(replyError(QNetworkReply::NetworkError)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound::Sound(const QByteArray byteArray, QObject* parent) :
|
Sound::Sound(const QByteArray byteArray, QObject* parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
_byteArray(byteArray),
|
_byteArray(byteArray),
|
||||||
|
_isStereo(false),
|
||||||
_hasDownloaded(true)
|
_hasDownloaded(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -149,11 +153,20 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
|
||||||
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
|
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
|
||||||
int16_t* destinationSamples = (int16_t*) _byteArray.data();
|
int16_t* destinationSamples = (int16_t*) _byteArray.data();
|
||||||
|
|
||||||
for (int i = 1; i < numSourceSamples; i += 2) {
|
|
||||||
if (i + 1 >= numSourceSamples) {
|
if (_isStereo) {
|
||||||
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2);
|
for (int i = 0; i < numSourceSamples; i += 4) {
|
||||||
} else {
|
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2);
|
||||||
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 4);
|
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 1; i < numSourceSamples; i += 2) {
|
||||||
|
if (i + 1 >= numSourceSamples) {
|
||||||
|
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2);
|
||||||
|
} else {
|
||||||
|
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2)
|
||||||
|
+ (sourceSamples[i + 1] / 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,19 @@ class Sound : public QObject {
|
||||||
|
|
||||||
Q_PROPERTY(bool downloaded READ hasDownloaded)
|
Q_PROPERTY(bool downloaded READ hasDownloaded)
|
||||||
public:
|
public:
|
||||||
Sound(const QUrl& sampleURL, QObject* parent = NULL);
|
Sound(const QUrl& sampleURL, bool isStereo = false, QObject* parent = NULL);
|
||||||
Sound(float volume, float frequency, float duration, float decay, QObject* parent = NULL);
|
Sound(float volume, float frequency, float duration, float decay, QObject* parent = NULL);
|
||||||
Sound(const QByteArray byteArray, QObject* parent = NULL);
|
Sound(const QByteArray byteArray, QObject* parent = NULL);
|
||||||
void append(const QByteArray byteArray);
|
void append(const QByteArray byteArray);
|
||||||
|
|
||||||
|
bool isStereo() const { return _isStereo; }
|
||||||
bool hasDownloaded() const { return _hasDownloaded; }
|
bool hasDownloaded() const { return _hasDownloaded; }
|
||||||
|
|
||||||
const QByteArray& getByteArray() { return _byteArray; }
|
const QByteArray& getByteArray() { return _byteArray; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QByteArray _byteArray;
|
QByteArray _byteArray;
|
||||||
|
bool _isStereo;
|
||||||
bool _hasDownloaded;
|
bool _hasDownloaded;
|
||||||
|
|
||||||
void trimFrames();
|
void trimFrames();
|
||||||
|
|
|
@ -48,7 +48,8 @@ EntityScriptingInterface ScriptEngine::_entityScriptingInterface;
|
||||||
|
|
||||||
static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) {
|
static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) {
|
||||||
QUrl soundURL = QUrl(context->argument(0).toString());
|
QUrl soundURL = QUrl(context->argument(0).toString());
|
||||||
QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL), QScriptEngine::ScriptOwnership);
|
bool isStereo = context->argument(1).toBool();
|
||||||
|
QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL, isStereo), QScriptEngine::ScriptOwnership);
|
||||||
|
|
||||||
return soundScriptValue;
|
return soundScriptValue;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue