diff --git a/examples/airGuitar.js b/examples/airGuitar.js index c0e06add30..2c3d0409fa 100644 --- a/examples/airGuitar.js +++ b/examples/airGuitar.js @@ -132,15 +132,16 @@ function checkHands(deltaTime) { } function playChord(position, volume) { - var options = new AudioInjectionOptions(); - options.position = position; - options.volume = volume; if (Audio.isInjectorPlaying(soundPlaying)) { print("stopped sound"); Audio.stopInjector(soundPlaying); } + print("Played sound: " + whichChord + " at volume " + options.volume); - soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], options); + soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], { + position: position, + volume: volume + }); } function keyPressEvent(event) { diff --git a/examples/audioBall.js b/examples/audioBall.js index 7e7126be06..ca666285a9 100644 --- a/examples/audioBall.js +++ b/examples/audioBall.js @@ -32,10 +32,10 @@ function updateEntity(deltaTime) { if (Math.random() < CHANCE_OF_PLAYING_SOUND) { // play a sound at the location of the entity - var options = new AudioInjectionOptions(); - options.position = entityPosition; - options.volume = 0.75; - Audio.playSound(sound, options); + Audio.playSound(sound, { + position: entityPosition, + volume: 0.75 + }); } var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; diff --git a/examples/avatarCollision.js b/examples/avatarCollision.js index 6c0886464c..4bd0adf69a 100644 --- a/examples/avatarCollision.js +++ b/examples/avatarCollision.js @@ -17,9 +17,11 @@ var SOUND_TRIGGER_CLEAR = 1000; // milliseconds var SOUND_TRIGGER_DELAY = 200; // milliseconds var soundExpiry = 0; var DateObj = new Date(); -var audioOptions = new AudioInjectionOptions(); -audioOptions.volume = 0.5; -audioOptions.position = { x: 0, y: 0, z: 0 }; + +var audioOptions = { + volume: 0.5, + position: { x: 0, y: 0, z: 0 } +} var hitSounds = new Array(); hitSounds[0] = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/Hit1.raw"); diff --git a/examples/birdSongs.js b/examples/birdSongs.js index 94e013d92b..680cb025ad 100644 --- a/examples/birdSongs.js +++ b/examples/birdSongs.js @@ -33,20 +33,22 @@ function maybePlaySound(deltaTime) { // Set the location and other info for the sound to play var whichBird = Math.floor(Math.random() * birds.length); //print("playing sound # " + whichBird); - var options = new AudioInjectionOptions(); - var position = { x: lowerCorner.x + Math.random() * (upperCorner.x - lowerCorner.x), - y: lowerCorner.y + Math.random() * (upperCorner.y - lowerCorner.y), - z: lowerCorner.z + Math.random() * (upperCorner.z - lowerCorner.z) }; - options.position = position; - options.volume = BIRD_MASTER_VOLUME; - // + var position = { + x: lowerCorner.x + Math.random() * (upperCorner.x - lowerCorner.x), + y: lowerCorner.y + Math.random() * (upperCorner.y - lowerCorner.y), + z: lowerCorner.z + Math.random() * (upperCorner.z - lowerCorner.z) + }; + var options = { + position: position, + volume: BIRD_MASTER_VOLUME + }; var entityId = Entities.addEntity({ - type: "Sphere", - position: position, - dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }, - color: birds[whichBird].color, - lifetime: 10 - }); + type: "Sphere", + position: position, + dimensions: { x: BIRD_SIZE, y: BIRD_SIZE, z: BIRD_SIZE }, + color: birds[whichBird].color, + lifetime: 10 + }); if (useLights) { var lightId = Entities.addEntity({ diff --git a/examples/botProceduralWayPoints.js b/examples/botProceduralWayPoints.js index fc9c0dda74..0f8b369470 100644 --- a/examples/botProceduralWayPoints.js +++ b/examples/botProceduralWayPoints.js @@ -172,13 +172,11 @@ function playRandomSound() { } function playRandomFootstepSound() { - - var whichSound = Math.floor((Math.random() * footstepSounds.length)); - var options = new AudioInjectionOptions(); - options.position = Avatar.position; - options.volume = 1.0; - Audio.playSound(footstepSounds[whichSound], options); - + var whichSound = Math.floor((Math.random() * footstepSounds.length)); + Audio.playSound(footstepSounds[whichSound], { + position: Avatar.position, + volume: 1.0 + }); } // Facial Animation diff --git a/examples/bot_procedural.js b/examples/bot_procedural.js index f445162038..80f83fcdfa 100644 --- a/examples/bot_procedural.js +++ b/examples/bot_procedural.js @@ -134,13 +134,11 @@ function playRandomSound() { } function playRandomFootstepSound() { - - var whichSound = Math.floor((Math.random() * footstepSounds.length)); - var options = new AudioInjectionOptions(); - options.position = Avatar.position; - options.volume = 1.0; - Audio.playSound(footstepSounds[whichSound], options); - + var whichSound = Math.floor((Math.random() * footstepSounds.length)); + Audio.playSound(footstepSounds[whichSound], { + position: Avatar.position, + volume: 1.0 + }); } // ************************************ Facial Animation ********************************** diff --git a/examples/clap.js b/examples/clap.js index 9cc79a1c92..bf71f13cea 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -89,11 +89,11 @@ function maybePlaySound(deltaTime) { } function playClap(volume, position) { - var options = new AudioInjectionOptions(); - options.position = position; - options.volume = 1.0; var clip = Math.floor(Math.random() * numberOfSounds); - Audio.playSound(claps[clip], options); + Audio.playSound(claps[clip], { + position: position, + volume: volume + }); } var FASTEST_CLAP_INTERVAL = 150.0; diff --git a/examples/drumStick.js b/examples/drumStick.js index d0560057c0..1af9ffc3dd 100644 --- a/examples/drumStick.js +++ b/examples/drumStick.js @@ -63,8 +63,11 @@ function checkSticks(deltaTime) { // Waiting for change in velocity direction or slowing to trigger drum sound if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) { state[palm] = 0; - var options = new AudioInjectionOptions(); - options.position = Controller.getSpatialControlPosition(palm * 2 + 1); + + var options = { + position: Controller.getSpatialControlPosition(palm * 2 + 1); + } + if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; } options.volume = strokeSpeed[palm]; diff --git a/examples/editVoxels.js b/examples/editVoxels.js index e450f2d1d4..0747b9269f 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -68,9 +68,11 @@ var numColors = 9; var whichColor = 0; // Starting color is 'Copy' mode // Create sounds for for every script actions that require one -var audioOptions = new AudioInjectionOptions(); -audioOptions.volume = 1.0; -audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0 } ); // start with audio slightly above the avatar +// start with audio slightly above the avatar +var audioOptions = { + position: Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0 } ), + volume: 1.0 +}; function SoundArray() { this.audioOptions = audioOptions diff --git a/examples/entityBirds.js b/examples/entityBirds.js index bbc35a5f58..d18513ba49 100644 --- a/examples/entityBirds.js +++ b/examples/entityBirds.js @@ -135,10 +135,10 @@ function updateBirds(deltaTime) { // Tweeting behavior if (birds[i].tweeting == 0) { if (Math.random() < CHANCE_OF_TWEETING) { - var options = new AudioInjectionOptions(); - options.position = properties.position; - options.volume = 0.75; - Audio.playSound(birds[i].tweetSound, options); + Audio.playSound(birds[i].tweetSound, { + position: properties.position, + volume: 0.75 + }); birds[i].tweeting = 10; } } else { diff --git a/examples/entityScripts/playSoundOnClick.js b/examples/entityScripts/playSoundOnClick.js index 4bc523a7aa..4ab83a1952 100644 --- a/examples/entityScripts/playSoundOnClick.js +++ b/examples/entityScripts/playSoundOnClick.js @@ -14,14 +14,6 @@ (function(){ var bird; - function playSound(entityID) { - var options = new AudioInjectionOptions(); - var position = MyAvatar.position; - options.position = position; - options.volume = 0.5; - Audio.playSound(bird, options); - }; - this.preload = function(entityID) { print("preload("+entityID.id+")"); bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); @@ -29,6 +21,9 @@ this.clickDownOnEntity = function(entityID, mouseEvent) { print("clickDownOnEntity()..."); - playSound(); + Audio.playSound(bird, { + position: MyAvatar.position, + volume: 0.5 + }); }; }) diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js index 98702dcfdd..07be090c31 100644 --- a/examples/entityScripts/playSoundOnEnterOrLeave.js +++ b/examples/entityScripts/playSoundOnEnterOrLeave.js @@ -14,13 +14,12 @@ (function(){ var bird; - function playSound() { - var options = new AudioInjectionOptions(); - var position = MyAvatar.position; - options.position = position; - options.volume = 0.5; - Audio.playSound(bird, options); - }; + function playSound(entityID) { + Audio.playSound(bird, { + position: MyAvatar.position, + volume: 0.5 + }); + } this.preload = function(entityID) { print("preload("+entityID.id+")"); @@ -31,7 +30,7 @@ playSound(); }; - this.leaveEntity = function(entityID) { + this.leaveEntity = function(entityID) { playSound(); }; }) diff --git a/examples/frisbee.js b/examples/frisbee.js index c534a8b3fb..7e266de34b 100644 --- a/examples/frisbee.js +++ b/examples/frisbee.js @@ -177,10 +177,10 @@ function playSound(sound, position) { if (!SOUNDS_ENABLED) { return; } - var options = new AudioInjectionOptions(); - options.position = position; - options.volume = 1.0; - Audio.playSound(sound, options); + + Audio.playSound(sound,{ + position: position + }); } function cleanupFrisbees() { diff --git a/examples/grenadeLauncher.js b/examples/grenadeLauncher.js index bca067326a..e95d8dd79d 100644 --- a/examples/grenadeLauncher.js +++ b/examples/grenadeLauncher.js @@ -44,8 +44,9 @@ var targetLaunchSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/ var gunModel = "http://public.highfidelity.io/models/attachments/HaloGun.fst"; -var audioOptions = new AudioInjectionOptions(); -audioOptions.volume = 0.9; +var audioOptions { + volume: 0.9 +} var shotsFired = 0; diff --git a/examples/gun.js b/examples/gun.js index 385664226c..76084ce013 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -43,8 +43,9 @@ var targetLaunchSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/ var gunModel = "http://public.highfidelity.io/models/attachments/HaloGun.fst"; -var audioOptions = new AudioInjectionOptions(); -audioOptions.volume = 0.9; +var audioOptions = { + volume: 0.9 +} var shotsFired = 0; diff --git a/examples/headMove.js b/examples/headMove.js index b1f1c4ab7d..957686bb20 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -72,15 +72,11 @@ var WATCH_AVATAR_DISTANCE = 2.5; var sound = new Sound("http://public.highfidelity.io/sounds/Footsteps/FootstepW2Right-12db.wav"); function playSound() { - var options = new AudioInjectionOptions(); - var position = MyAvatar.position; - options.position = position; - options.volume = 1.0; - Audio.playSound(sound, options); + Audio.playSound(sound, { + position: MyAvatar.position + }); } - - function pullBack() { saveCameraState(); cameraPosition = Vec3.subtract(MyAvatar.position, Vec3.multiplyQbyV(Camera.getOrientation(), { x: 0, y: -hipsToEyes, z: -hipsToEyes * WATCH_AVATAR_DISTANCE })); diff --git a/examples/inWorldTestTone.js b/examples/inWorldTestTone.js index 590bb6c342..b3bf91d14d 100644 --- a/examples/inWorldTestTone.js +++ b/examples/inWorldTestTone.js @@ -19,11 +19,9 @@ var soundPlaying = false; function update(deltaTime) { if (!Audio.isInjectorPlaying(soundPlaying)) { - var options = new AudioInjectionOptions(); - options.position = { x:0, y:0, z:0 }; - options.volume = 1.0; - options.loop = true; - soundPlaying = Audio.playSound(sound, options); + soundPlaying = Audio.playSound(sound, { + loop: true + }); print("Started sound loop"); } } diff --git a/examples/lobby.js b/examples/lobby.js index 63ea1654a9..1b6596efa7 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -37,7 +37,14 @@ var panelsCenterShift = Vec3.subtract(panelsCenter, orbCenter); var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8}; -var HELMET_ATTACHMENT_URL = "https://hifi-public.s3.amazonaws.com/models/attachments/IronManMaskOnly.fbx" +var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx" + +var droneSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.raw") +var currentDrone = null; + +var latinSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.raw") +var elevatorSound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.raw") +var currentMusak = null; function reticlePosition() { var RETICLE_DISTANCE = 1; @@ -87,6 +94,12 @@ function drawLobby() { // add an attachment on this avatar so other people see them in the lobby MyAvatar.attach(HELMET_ATTACHMENT_URL, "Neck", {x: 0, y: 0, z: 0}, Quat.fromPitchYawRollDegrees(0, 0, 0), 1.15); + + // start the drone sound + currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true }); + + // start one of our musak sounds + playRandomMusak(); } } @@ -112,11 +125,35 @@ function changeLobbyTextures() { Overlays.editOverlay(panelWall, textureProp); } +function playRandomMusak() { + chosenSound = null; + + if (latinSound.downloaded && elevatorSound.downloaded) { + chosenSound = Math.random < 0.5 ? latinSound : elevatorSound; + } else if (latinSound.downloaded) { + chosenSound = latinSound; + } else if (elevatorSound.downloaded) { + chosenSound = elevatorSound; + } + + if (chosenSound) { + currentMusak = Audio.playSound(chosenSound, { stereo: true, localOnly: true }) + } else { + currentMusak = null; + } +} + function cleanupLobby() { Overlays.deleteOverlay(panelWall); Overlays.deleteOverlay(orbShell); Overlays.deleteOverlay(reticle); + Audio.stopInjector(currentDrone); + currentDrone = null; + + Audio.stopInjector(currentMusak); + currentMusak = null; + panelWall = false; orbShell = false; reticle = false; diff --git a/examples/playSound.js b/examples/playSound.js index 4130db5b16..efcda0b42b 100644 --- a/examples/playSound.js +++ b/examples/playSound.js @@ -15,12 +15,11 @@ var bird = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Animals/bushtit_1.raw"); function maybePlaySound(deltaTime) { if (Math.random() < 0.01) { - // Set the location and other info for the sound to play - var options = new AudioInjectionOptions(); - var position = MyAvatar.position; - options.position = position; - options.volume = 0.5; - Audio.playSound(bird, options); + // Set the location and other info for the sound to play + Audio.playSound(bird, { + position: MyAvatar.position, + volume: 0.5 + }); } } diff --git a/examples/playSoundLoop.js b/examples/playSoundLoop.js index 3122f13f37..b84c475d1a 100644 --- a/examples/playSoundLoop.js +++ b/examples/playSoundLoop.js @@ -20,10 +20,12 @@ var sound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw" //var sound = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Cocktail+Party+Snippets/Bandcamp.wav"); var soundPlaying = false; -var options = new AudioInjectionOptions(); -options.position = Vec3.sum(Camera.getPosition(), Quat.getFront(MyAvatar.orientation)); -options.volume = 0.5; -options.loop = true; +var options = { + position: Vec3.sum(Camera.getPosition(), Quat.getFront(MyAvatar.orientation)), + volume: 0.5, + loop: true +} + var playing = false; var ball = false; diff --git a/examples/playSoundOrbit.js b/examples/playSoundOrbit.js index 2c44a4535a..d98f7d0768 100644 --- a/examples/playSoundOrbit.js +++ b/examples/playSoundOrbit.js @@ -19,24 +19,23 @@ var distance = 1; var debug = 0; function playSound() { - var options = new AudioInjectionOptions(); - currentTime += deltaTime; + currentTime += deltaTime; var s = distance * Math.sin(currentTime); var c = distance * Math.cos(currentTime); - var soundOffset = { x:s, y:0, z:c }; + var soundOffset = { x:s, y:0, z:c }; - if (debug) { - print("t=" + currentTime + "offset=" + soundOffset.x + "," + soundOffset.y + "," + soundOffset.z); - } + if (debug) { + print("t=" + currentTime + "offset=" + soundOffset.x + "," + soundOffset.y + "," + soundOffset.z); + } - var avatarPosition = MyAvatar.position; - var soundPosition = Vec3.sum(avatarPosition,soundOffset); + var avatarPosition = MyAvatar.position; + var soundPosition = Vec3.sum(avatarPosition,soundOffset); - options.position = soundPosition - options.volume = 1.0; - Audio.playSound(soundClip, options); + Audio.playSound(soundClip, { + position: soundPosition + }); } Script.setInterval(playSound, 250); diff --git a/examples/playSoundWave.js b/examples/playSoundWave.js index f152effb47..c5e69f5cd6 100644 --- a/examples/playSoundWave.js +++ b/examples/playSoundWave.js @@ -14,11 +14,10 @@ Script.include("libraries/globals.js"); var soundClip = new Sound(HIFI_PUBLIC_BUCKET + "sounds/Cocktail%20Party%20Snippets/Walken1.wav"); function playSound() { - var options = new AudioInjectionOptions(); - var position = MyAvatar.position; - options.position = position; - options.volume = 0.5; - Audio.playSound(soundClip, options); + Audio.playSound(soundClip, { + position: MyAvatar.position, + volume: 0.5 + }); } Script.setInterval(playSound, 10000); diff --git a/examples/radio.js b/examples/radio.js index 293867398a..fc09fb184e 100644 --- a/examples/radio.js +++ b/examples/radio.js @@ -15,10 +15,12 @@ var modelURL = HIFI_PUBLIC_BUCKET + "models/entities/radio/Speakers.fbx"; var soundURL = HIFI_PUBLIC_BUCKET + "sounds/FamilyStereo.raw"; var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); -var audioOptions = new AudioInjectionOptions(); -audioOptions.volume = 0.5; -audioOptions.loop = true; -audioOptions.isStereo = true; +var audioOptions = { + volume: 0.5, + loop: true, + stereo: true +} + var injector = null; var sound = new Sound(soundURL, audioOptions.isStereo); diff --git a/examples/spaceInvadersExample.js b/examples/spaceInvadersExample.js index 8d69f066d6..dd5ac9e875 100644 --- a/examples/spaceInvadersExample.js +++ b/examples/spaceInvadersExample.js @@ -217,7 +217,8 @@ function update(deltaTime) { if (invaderStepOfCycle % stepsPerSound == 0) { // play the move sound - var options = new AudioInjectionOptions(); + var options = {}; + if (soundInMyHead) { options.position = { x: MyAvatar.position.x + 0.0, y: MyAvatar.position.y + 0.1, @@ -225,7 +226,7 @@ function update(deltaTime) { } else { options.position = getInvaderPosition(invadersPerRow / 2, numberOfRows / 2); } - options.volume = 1.0; + Audio.playSound(moveSounds[currentMoveSound], options); // get ready for next move sound @@ -330,7 +331,7 @@ function fireMissile() { lifetime: 5 }); - var options = new AudioInjectionOptions(); + var options = {} if (soundInMyHead) { options.position = { x: MyAvatar.position.x + 0.0, y: MyAvatar.position.y + 0.1, @@ -338,7 +339,7 @@ function fireMissile() { } else { options.position = missilePosition; } - options.volume = 1.0; + Audio.playSound(shootSound, options); missileFired = true; @@ -380,7 +381,7 @@ function deleteIfInvader(possibleInvaderEntity) { Entities.deleteEntity(myMissile); // play the hit sound - var options = new AudioInjectionOptions(); + var options = {}; if (soundInMyHead) { options.position = { x: MyAvatar.position.x + 0.0, y: MyAvatar.position.y + 0.1, @@ -388,7 +389,7 @@ function deleteIfInvader(possibleInvaderEntity) { } else { options.position = getInvaderPosition(row, column); } - options.volume = 1.0; + Audio.playSound(hitSound, options); } } diff --git a/examples/toyball.js b/examples/toyball.js index b41dd2bda5..1cd6de16eb 100644 --- a/examples/toyball.js +++ b/examples/toyball.js @@ -113,10 +113,7 @@ function checkControllerSide(whichSide) { inHand: true }; Entities.editEntity(closestEntity, properties); - var options = new AudioInjectionOptions(); - options.position = ballPosition; - options.volume = 1.0; - Audio.playSound(catchSound, options); + Audio.playSound(catchSound, { position: ballPosition }); return; // exit early } @@ -156,10 +153,7 @@ function checkControllerSide(whichSide) { } // Play a new ball sound - var options = new AudioInjectionOptions(); - options.position = ballPosition; - options.volume = 1.0; - Audio.playSound(newSound, options); + Audio.playSound(newSound, { position: ballPosition}); return; // exit early } @@ -207,10 +201,7 @@ function checkControllerSide(whichSide) { rightHandEntity = false; } - var options = new AudioInjectionOptions(); - options.position = ballPosition; - options.volume = 1.0; - Audio.playSound(throwSound, options); + Audio.playSound(throwSound, { position: ballPosition }); } } } diff --git a/examples/walk.js b/examples/walk.js index 279516aa2a..5a0df72f26 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -88,7 +88,6 @@ Script.update.connect(function(deltaTime) { animateAvatar(deltaTime, speed); break; } - case state.EDIT_STANDING: { motion.curAnim = motion.selStand; motion.direction = FORWARDS; @@ -559,9 +558,11 @@ function getLeanRoll(deltaTime, speed) { function playFootstep(side) { - var options = new AudioInjectionOptions(); - options.position = Camera.getPosition(); - options.volume = 0.3; + options = { + position: Camera.getPosition(), + volume: 0.3 + } + var soundNumber = 2; // 0 to 2 if (side === RIGHT && motion.makesFootStepSounds) { Audio.playSound(walkAssets.footsteps[soundNumber + 1], options); @@ -2609,4 +2610,4 @@ function animateAvatar(deltaTime, speed) { } // Begin by setting an internal state -state.setInternalState(state.STANDING); \ No newline at end of file +state.setInternalState(state.STANDING); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bb19ce3ca5..6463c159c4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -418,7 +418,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _trayIcon->show(); // set the local loopback interface for local sounds from audio scripts - AudioScriptingInterface::getInstance().setLocalLoopbackInterface(&_audio); + AudioScriptingInterface::getInstance().setLocalAudioInterface(&_audio); #ifdef HAVE_RTMIDI // setup the MIDIManager @@ -454,23 +454,22 @@ Application::~Application() { // ask the datagram processing thread to quit and wait until it is done _nodeThread->quit(); _nodeThread->wait(); + + // kill any audio injectors that are still around + AudioScriptingInterface::getInstance().stopAllInjectors(); // stop the audio process QMetaObject::invokeMethod(&_audio, "stop"); - + // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); - - // kill any audio injectors that are still around - AudioScriptingInterface::getInstance().stopAllInjectors(); _octreeProcessor.terminate(); _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _entityEditSender.terminate(); - VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown Menu::getInstance()->deleteLater(); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 96121d9a8c..2dc94e6027 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -32,19 +32,21 @@ #include #include +#include + +#include #include #include #include #include #include -#include - -#include "Audio.h" #include "Menu.h" #include "Util.h" #include "PositionalAudioStream.h" +#include "Audio.h" + static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0; static const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300; @@ -1366,24 +1368,34 @@ void Audio::startDrumSound(float volume, float frequency, float duration, float _drumSoundSample = 0; } -void Audio::handleAudioByteArray(const QByteArray& audioByteArray, const AudioInjectorOptions& injectorOptions) { - if (audioByteArray.size() > 0) { - QAudioFormat localFormat = _outputFormat; +bool Audio::outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector) { + if (injector->getLocalBuffer()) { + QAudioFormat localFormat = _desiredOutputFormat; + localFormat.setChannelCount(isStereo ? 2 : 1); - if (!injectorOptions.isStereo()) { - localFormat.setChannelCount(1); - } + QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), + localFormat, this); + localOutput->setVolume(volume); - QAudioOutput* localSoundOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), localFormat, this); + // add this to our list of local injected outputs, we will need to clean it up when the injector says it is done + _injectedOutputInterfaces.insert(injector, localOutput); - QIODevice* localIODevice = localSoundOutput->start(); - if (localIODevice) { - localIODevice->write(audioByteArray); - } else { - qDebug() << "Unable to handle audio byte array. Error:" << localSoundOutput->error(); - } - } else { - qDebug() << "Audio::handleAudioByteArray called with an empty byte array. Sound is likely still downloading."; + connect(injector, &AudioInjector::finished, this, &Audio::cleanupLocalOutputInterface); + + localOutput->start(injector->getLocalBuffer()); + return localOutput->state() == QAudio::ActiveState; + } + + return false; +} + +void Audio::cleanupLocalOutputInterface() { + QAudioOutput* outputInterface = _injectedOutputInterfaces.value(sender()); + if (outputInterface) { + qDebug() << "Stopping a QAudioOutput interface since injector" << sender() << "is finished"; + + outputInterface->stop(); + outputInterface->deleteLater(); } } diff --git a/interface/src/Audio.h b/interface/src/Audio.h index cc0da78198..d8e5c5386c 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -155,7 +155,7 @@ public slots: void selectAudioFilterBassCut(); void selectAudioFilterSmiley(); - virtual void handleAudioByteArray(const QByteArray& audioByteArray, const AudioInjectorOptions& options); + virtual bool outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector); void sendDownstreamAudioStatsPacket(); @@ -180,11 +180,11 @@ signals: void processInboundAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format); void processLocalAudio(unsigned int sampleTime, const QByteArray& samples, const QAudioFormat& format); +private slots: + void cleanupLocalOutputInterface(); private: void outputFormatChanged(); -private: - QByteArray firstInputFrame; QAudioInput* _audioInput; QAudioFormat _desiredInputFormat; @@ -256,10 +256,6 @@ private: float _iconColor; qint64 _iconPulseTimeReference; - /// Audio callback in class context. - inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight); - - bool _processSpatialAudio; /// Process received audio by spatial audio hooks unsigned int _spatialAudioStart; /// Start of spatial audio interval (in sample rate time base) unsigned int _spatialAudioFinish; /// End of spatial audio interval (in sample rate time base) @@ -372,6 +368,8 @@ private: AudioOutputIODevice _audioOutputIODevice; WeakRecorderPointer _recorder; + + QHash _injectedOutputInterfaces; }; diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 1ee71ee32d..b5a7be9849 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -13,9 +13,13 @@ #define hifi_AbstractAudioInterface_h #include +#include #include "AudioInjectorOptions.h" +class AudioInjector; +class AudioInjectorLocalBuffer; + class AbstractAudioInterface : public QObject { Q_OBJECT public: @@ -24,7 +28,7 @@ public: virtual void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen) = 0; virtual void startDrumSound(float volume, float frequency, float duration, float decay) = 0; public slots: - virtual void handleAudioByteArray(const QByteArray& audioByteArray, const AudioInjectorOptions& options) = 0; + virtual bool outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector) = 0; }; Q_DECLARE_METATYPE(AbstractAudioInterface*) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 45e1eb57e1..1743504883 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -21,6 +21,14 @@ #include "AudioInjector.h" +QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in) { + return engine->newQObject(in); +} + +void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out) { + out = qobject_cast(object.toQObject()); +} + AudioInjector::AudioInjector(QObject* parent) : QObject(parent), _sound(NULL), @@ -28,7 +36,8 @@ AudioInjector::AudioInjector(QObject* parent) : _shouldStop(false), _loudness(0.0f), _isFinished(false), - _currentSendPosition(0) + _currentSendPosition(0), + _localBuffer(NULL) { } @@ -38,10 +47,17 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO _shouldStop(false), _loudness(0.0f), _isFinished(false), - _currentSendPosition(0) + _currentSendPosition(0), + _localBuffer(NULL) { } +AudioInjector::~AudioInjector() { + if (_localBuffer) { + _localBuffer->stop(); + } +} + void AudioInjector::setOptions(AudioInjectorOptions& options) { _options = options; } @@ -50,9 +66,54 @@ float AudioInjector::getLoudness() { return _loudness; } +void AudioInjector::injectAudio() { + if (_options.localOnly) { + injectLocally(); + } else { + injectToMixer(); + } +} + +void AudioInjector::injectLocally() { + bool success = false; + if (_localAudioInterface) { + const QByteArray& soundByteArray = _sound->getByteArray(); + + if (soundByteArray.size() > 0) { + _localBuffer = new AudioInjectorLocalBuffer(_sound->getByteArray(), this); + _localBuffer->open(QIODevice::ReadOnly); + _localBuffer->setShouldLoop(_options.loop); + + QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(bool, _options.stereo), + Q_ARG(qreal, _options.volume), + Q_ARG(AudioInjector*, this)); + + + + if (!success) { + qDebug() << "AudioInjector::injectLocally could not output locally via _localAudioInterface"; + } + } else { + qDebug() << "AudioInjector::injectLocally called without any data in Sound QByteArray"; + } + + } else { + qDebug() << "AudioInjector::injectLocally cannot inject locally with no local audio interface present."; + } + + if (!success) { + // we never started so we are finished, call our stop method + stop(); + } + +} + const uchar MAX_INJECTOR_VOLUME = 0xFF; -void AudioInjector::injectAudio() { +void AudioInjector::injectToMixer() { QByteArray soundByteArray = _sound->getByteArray(); if (_currentSendPosition < 0 || @@ -75,7 +136,7 @@ void AudioInjector::injectAudio() { packetStream << QUuid::createUuid(); // pack the stereo/mono type of the stream - packetStream << _options.isStereo(); + packetStream << _options.stereo; // pack the flag for loopback uchar loopbackFlag = (uchar) true; @@ -83,13 +144,13 @@ void AudioInjector::injectAudio() { // pack the position for injected audio int positionOptionOffset = injectAudioPacket.size(); - packetStream.writeRawData(reinterpret_cast(&_options.getPosition()), - sizeof(_options.getPosition())); + packetStream.writeRawData(reinterpret_cast(&_options.position), + sizeof(_options.position)); // pack our orientation for injected audio int orientationOptionOffset = injectAudioPacket.size(); - packetStream.writeRawData(reinterpret_cast(&_options.getOrientation()), - sizeof(_options.getOrientation())); + packetStream.writeRawData(reinterpret_cast(&_options.orientation), + sizeof(_options.orientation)); // pack zero for radius float radius = 0; @@ -97,23 +158,23 @@ void AudioInjector::injectAudio() { // pack 255 for attenuation byte int volumeOptionOffset = injectAudioPacket.size(); - quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume(); + quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; packetStream << volume; - packetStream << _options.ignorePenumbra(); + packetStream << _options.ignorePenumbra; QElapsedTimer timer; timer.start(); int nextFrame = 0; int numPreAudioDataBytes = injectAudioPacket.size(); - bool shouldLoop = _options.getLoop(); + bool shouldLoop = _options.loop; // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; while (_currentSendPosition < soundByteArray.size() && !_shouldStop) { - int bytesToCopy = std::min(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, + int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, soundByteArray.size() - _currentSendPosition); // Measure the loudness of this frame @@ -125,12 +186,12 @@ void AudioInjector::injectAudio() { _loudness /= (float)(bytesToCopy / sizeof(int16_t)); memcpy(injectAudioPacket.data() + positionOptionOffset, - &_options.getPosition(), - sizeof(_options.getPosition())); + &_options.position, + sizeof(_options.position)); memcpy(injectAudioPacket.data() + orientationOptionOffset, - &_options.getOrientation(), - sizeof(_options.getOrientation())); - volume = MAX_INJECTOR_VOLUME * _options.getVolume(); + &_options.orientation, + sizeof(_options.orientation)); + volume = MAX_INJECTOR_VOLUME * _options.volume; memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume)); // resize the QByteArray to the right size @@ -175,3 +236,13 @@ void AudioInjector::injectAudio() { _isFinished = true; emit finished(); } + +void AudioInjector::stop() { + _shouldStop = true; + + if (_options.localOnly) { + // we're only a local injector, so we can say we are finished right away too + _isFinished = true; + emit finished(); + } +} diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 68acd3b887..13188c5977 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -18,20 +18,29 @@ #include #include +#include "AudioInjectorLocalBuffer.h" #include "AudioInjectorOptions.h" #include "Sound.h" +class AbstractAudioInterface; + class AudioInjector : public QObject { Q_OBJECT public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); + ~AudioInjector(); bool isFinished() const { return _isFinished; } int getCurrentSendPosition() const { return _currentSendPosition; } + + AudioInjectorLocalBuffer* getLocalBuffer() const { return _localBuffer; } + bool isLocalOnly() const { return _options.localOnly; } + + void setLocalAudioInterface(AbstractAudioInterface* localAudioInterface) { _localAudioInterface = localAudioInterface; } public slots: void injectAudio(); - void stop() { _shouldStop = true; } + void stop(); void setOptions(AudioInjectorOptions& options); void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } float getLoudness(); @@ -39,15 +48,22 @@ public slots: signals: void finished(); private: + void injectToMixer(); + void injectLocally(); + Sound* _sound; AudioInjectorOptions _options; bool _shouldStop; float _loudness; bool _isFinished; int _currentSendPosition; - + AbstractAudioInterface* _localAudioInterface; + AudioInjectorLocalBuffer* _localBuffer; }; Q_DECLARE_METATYPE(AudioInjector*) +QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const& in); +void injectorFromScriptValue(const QScriptValue& object, AudioInjector*& out); + #endif // hifi_AudioInjector_h diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.cpp b/libraries/audio/src/AudioInjectorLocalBuffer.cpp new file mode 100644 index 0000000000..bdf084091d --- /dev/null +++ b/libraries/audio/src/AudioInjectorLocalBuffer.cpp @@ -0,0 +1,74 @@ +// +// AudioInjectorLocalBuffer.cpp +// libraries/audio/src +// +// Created by Stephen Birarda on 2014-11-11. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AudioInjectorLocalBuffer.h" + +AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent) : + QIODevice(parent), + _rawAudioArray(rawAudioArray), + _shouldLoop(false), + _isStopped(false), + _currentOffset(0) +{ + +} + +void AudioInjectorLocalBuffer::stop() { + _isStopped = true; + QIODevice::close(); +} + +qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) { + if (!_isStopped) { + + // first copy to the end of the raw audio + int bytesToEnd = _rawAudioArray.size() - _currentOffset; + + int bytesRead = maxSize; + + if (maxSize > bytesToEnd) { + bytesRead = bytesToEnd; + } + + memcpy(data, _rawAudioArray.data() + _currentOffset, bytesRead); + + // now check if we are supposed to loop and if we can copy more from the beginning + if (_shouldLoop && maxSize != bytesRead) { + bytesRead += recursiveReadFromFront(data + bytesRead, maxSize - bytesRead); + } else { + _currentOffset += bytesRead; + } + + return bytesRead; + } else { + return 0; + } +} + +qint64 AudioInjectorLocalBuffer::recursiveReadFromFront(char* data, qint64 maxSize) { + // see how much we can get in this pass + int bytesRead = maxSize; + + if (bytesRead > _rawAudioArray.size()) { + bytesRead = _rawAudioArray.size(); + } + + // copy that amount + memcpy(data, _rawAudioArray.data(), bytesRead); + + // check if we need to call ourselves again and pull from the front again + if (bytesRead < maxSize) { + return bytesRead + recursiveReadFromFront(data, maxSize); + } else { + _currentOffset = bytesRead; + return bytesRead; + } +} \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorLocalBuffer.h b/libraries/audio/src/AudioInjectorLocalBuffer.h new file mode 100644 index 0000000000..8b32c6fbc7 --- /dev/null +++ b/libraries/audio/src/AudioInjectorLocalBuffer.h @@ -0,0 +1,40 @@ +// +// AudioInjectorLocalBuffer.h +// libraries/audio/src +// +// Created by Stephen Birarda on 2014-11-11. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AudioInjectorLocalBuffer_h +#define hifi_AudioInjectorLocalBuffer_h + +#include + +class AudioInjectorLocalBuffer : public QIODevice { + Q_OBJECT +public: + AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent); + + void stop(); + + qint64 readData(char* data, qint64 maxSize); + qint64 writeData(const char* data, qint64 maxSize) { return 0; } + + void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; } + +private: + + qint64 recursiveReadFromFront(char* data, qint64 maxSize); + + QByteArray _rawAudioArray; + bool _shouldLoop; + bool _isStopped; + + int _currentOffset; +}; + +#endif // hifi_AudioInjectorLocalBuffer_h \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 670bea2fef..df435cf2cc 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -9,33 +9,60 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "AudioInjectorOptions.h" -AudioInjectorOptions::AudioInjectorOptions(QObject* parent) : - QObject(parent), - _position(0.0f, 0.0f, 0.0f), - _volume(1.0f), - _loop(false), - _orientation(glm::vec3(0.0f, 0.0f, 0.0f)), - _isStereo(false), - _ignorePenumbra(false) +AudioInjectorOptions::AudioInjectorOptions() : + position(0.0f, 0.0f, 0.0f), + volume(1.0f), + loop(false), + orientation(glm::vec3(0.0f, 0.0f, 0.0f)), + stereo(false), + ignorePenumbra(false), + localOnly(false) { + } -AudioInjectorOptions::AudioInjectorOptions(const AudioInjectorOptions& other) { - _position = other._position; - _volume = other._volume; - _loop = other._loop; - _orientation = other._orientation; - _isStereo = other._isStereo; - _ignorePenumbra = other._ignorePenumbra; +QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInjectorOptions& injectorOptions) { + QScriptValue obj = engine->newObject(); + obj.setProperty("position", vec3toScriptValue(engine, injectorOptions.position)); + obj.setProperty("volume", injectorOptions.volume); + obj.setProperty("loop", injectorOptions.loop); + obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation)); + obj.setProperty("stereo", injectorOptions.stereo); + obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra); + obj.setProperty("localOnly", injectorOptions.localOnly); + return obj; } -void AudioInjectorOptions::operator=(const AudioInjectorOptions& other) { - _position = other._position; - _volume = other._volume; - _loop = other._loop; - _orientation = other._orientation; - _isStereo = other._isStereo; - _ignorePenumbra = other._ignorePenumbra; -} \ No newline at end of file +void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) { + if (object.property("position").isValid()) { + vec3FromScriptValue(object.property("position"), injectorOptions.position); + } + + if (object.property("volume").isValid()) { + injectorOptions.volume = object.property("volume").toNumber(); + } + + if (object.property("loop").isValid()) { + injectorOptions.loop = object.property("loop").toBool(); + } + + if (object.property("orientation").isValid()) { + quatFromScriptValue(object.property("orientation"), injectorOptions.orientation); + } + + if (object.property("stereo").isValid()) { + injectorOptions.stereo = object.property("stereo").toBool(); + } + + if (object.property("ignorePenumbra").isValid()) { + injectorOptions.ignorePenumbra = object.property("ignorePenumbra").toBool(); + } + + if (object.property("localOnly").isValid()) { + injectorOptions.localOnly = object.property("localOnly").toBool(); + } + } \ No newline at end of file diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index 1ccd85be7e..4fd3a0b7ae 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -12,54 +12,26 @@ #ifndef hifi_AudioInjectorOptions_h #define hifi_AudioInjectorOptions_h -#include +#include #include #include -#include - -class AudioInjectorOptions : public QObject { - Q_OBJECT - - Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) - Q_PROPERTY(float volume READ getVolume WRITE setVolume) - Q_PROPERTY(bool loop READ getLoop WRITE setLoop) - Q_PROPERTY(bool isStereo READ isStereo WRITE setIsStereo) - Q_PROPERTY(bool ignorePenumbra READ ignorePenumbra WRITE setIgnorePenumbra) +class AudioInjectorOptions { public: - AudioInjectorOptions(QObject* parent = 0); - AudioInjectorOptions(const AudioInjectorOptions& other); - void operator=(const AudioInjectorOptions& other); - - const glm::vec3& getPosition() const { return _position; } - void setPosition(const glm::vec3& position) { _position = position; } - - float getVolume() const { return _volume; } - void setVolume(float volume) { _volume = volume; } - - bool getLoop() const { return _loop; } - void setLoop(bool loop) { _loop = loop; } - - const glm::quat& getOrientation() const { return _orientation; } - void setOrientation(const glm::quat& orientation) { _orientation = orientation; } - - const bool isStereo() const { return _isStereo; } - void setIsStereo(const bool isStereo) { _isStereo = isStereo; } - - const bool ignorePenumbra() const {return _ignorePenumbra; } - void setIgnorePenumbra(bool ignorePenumbra) { _ignorePenumbra = ignorePenumbra; } - -private: - glm::vec3 _position; - float _volume; - bool _loop; - glm::quat _orientation; - bool _isStereo; - bool _ignorePenumbra; + AudioInjectorOptions(); + glm::vec3 position; + float volume; + bool loop; + glm::quat orientation; + bool stereo; + bool ignorePenumbra; + bool localOnly; }; -Q_DECLARE_METATYPE(AudioInjectorOptions) +Q_DECLARE_METATYPE(AudioInjectorOptions); + +QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInjectorOptions& injectorOptions); +void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions); #endif // hifi_AudioInjectorOptions_h diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/audio/src/AudioScriptingInterface.cpp index 33afcdb095..cb010ef11d 100644 --- a/libraries/audio/src/AudioScriptingInterface.cpp +++ b/libraries/audio/src/AudioScriptingInterface.cpp @@ -11,15 +11,20 @@ #include "AudioScriptingInterface.h" +void registerAudioMetaTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, injectorOptionsToScriptValue, injectorOptionsFromScriptValue); + qScriptRegisterMetaType(engine, soundToScriptValue, soundFromScriptValue); +} + AudioScriptingInterface& AudioScriptingInterface::getInstance() { static AudioScriptingInterface staticInstance; return staticInstance; } AudioScriptingInterface::AudioScriptingInterface() : - _localLoopbackInterface(NULL) + _localAudioInterface(NULL) { - qRegisterMetaType("AudioInjectorOptions"); + } void AudioScriptingInterface::stopAllInjectors() { @@ -37,24 +42,10 @@ void AudioScriptingInterface::stopAllInjectors() { } } -void AudioScriptingInterface::playLocalSound(Sound* sound, const AudioInjectorOptions* injectorOptions) { - if (sound->isStereo()) { - const_cast(injectorOptions)->setIsStereo(true); - } +AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) { - // assume that localAudioInterface could be on a separate thread, use Qt::AutoConnection to handle properly - QMetaObject::invokeMethod(_localLoopbackInterface, "handleAudioByteArray", - Qt::AutoConnection, - Q_ARG(QByteArray, sound->getByteArray()), - Q_ARG(const AudioInjectorOptions&, *injectorOptions)); -} - -AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) { - - if (sound->isStereo()) { - const_cast(injectorOptions)->setIsStereo(true); - } - AudioInjector* injector = new AudioInjector(sound, *injectorOptions); + AudioInjector* injector = new AudioInjector(sound, injectorOptions); + injector->setLocalAudioInterface(_localAudioInterface); QThread* injectorThread = new QThread(); diff --git a/libraries/audio/src/AudioScriptingInterface.h b/libraries/audio/src/AudioScriptingInterface.h index 5cd0f9e99a..0017806b40 100644 --- a/libraries/audio/src/AudioScriptingInterface.h +++ b/libraries/audio/src/AudioScriptingInterface.h @@ -18,8 +18,6 @@ #include "AudioInjector.h" #include "Sound.h" -const AudioInjectorOptions DEFAULT_INJECTOR_OPTIONS; - class AudioScriptingInterface : public QObject { Q_OBJECT public: @@ -27,13 +25,12 @@ public: void stopAllInjectors(); - void setLocalLoopbackInterface(AbstractAudioInterface* audioInterface) { _localLoopbackInterface = audioInterface; } + void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } public slots: static float getLoudness(AudioInjector* injector); - void playLocalSound(Sound *sound, const AudioInjectorOptions* injectorOptions = NULL); - AudioInjector* playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL); + AudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); void stopInjector(AudioInjector* injector); bool isInjectorPlaying(AudioInjector* injector); @@ -43,6 +40,9 @@ public slots: private: AudioScriptingInterface(); QList< QPointer > _activeInjectors; - AbstractAudioInterface* _localLoopbackInterface; + AbstractAudioInterface* _localAudioInterface; }; + +void registerAudioMetaTypes(QScriptEngine* engine); + #endif // hifi_AudioScriptingInterface_h diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 2266385425..4c520f27ce 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -29,6 +29,15 @@ #include "AudioEditBuffer.h" #include "Sound.h" + +QScriptValue soundToScriptValue(QScriptEngine* engine, Sound* const& in) { + return engine->newQObject(in); +} + +void soundFromScriptValue(const QScriptValue& object, Sound*& out) { + out = qobject_cast(object.toQObject()); +} + // procedural audio version of Sound Sound::Sound(float volume, float frequency, float duration, float decay, QObject* parent) : QObject(parent), diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index b8fdc6b458..f7b51891f0 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -14,6 +14,7 @@ #include #include +#include class Sound : public QObject { Q_OBJECT @@ -44,4 +45,9 @@ private slots: void replyError(QNetworkReply::NetworkError code); }; +Q_DECLARE_METATYPE(Sound*) + +QScriptValue soundToScriptValue(QScriptEngine* engine, Sound* const& in); +void soundFromScriptValue(const QScriptValue& object, Sound*& out); + #endif // hifi_Sound_h diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 56f820ab97..63384371bd 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -166,8 +166,8 @@ void Player::pausePlayer() { void Player::setupAudioThread() { _audioThread = new QThread(); - _options.setPosition(_avatar->getPosition()); - _options.setOrientation(_avatar->getOrientation()); + _options.position = _avatar->getPosition(); + _options.orientation = _avatar->getOrientation(); _injector.reset(new AudioInjector(_recording->getAudio(), _options), &QObject::deleteLater); _injector->moveToThread(_audioThread); _audioThread->start(); @@ -292,8 +292,8 @@ void Player::play() { qDebug() << "WARNING: Player couldn't find head data."; } - _options.setPosition(_avatar->getPosition()); - _options.setOrientation(_avatar->getOrientation()); + _options.position = _avatar->getPosition(); + _options.orientation = _avatar->getOrientation(); _injector->setOptions(_options); } @@ -360,7 +360,7 @@ void Player::setCurrentTime(int currentTime) { } void Player::setVolume(float volume) { - _options.setVolume(volume); + _options.volume = volume; if (_injector) { _injector->setOptions(_options); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 40602645f1..2691b10273 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -74,14 +74,6 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { out = qobject_cast(object.toQObject()); } -QScriptValue injectorToScriptValue(QScriptEngine* engine, AudioInjector* const &in) { - return engine->newQObject(in); -} - -void injectorFromScriptValue(const QScriptValue &object, AudioInjector* &out) { - out = qobject_cast(object.toQObject()); -} - QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) { return engine->newQObject(in); } @@ -234,7 +226,6 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin return true; } -Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*) Q_SCRIPT_DECLARE_QMETAOBJECT(LocalVoxels, QString) void ScriptEngine::init() { @@ -254,6 +245,7 @@ void ScriptEngine::init() { registerMenuItemProperties(this); registerAnimationTypes(this); registerAvatarTypes(this); + registerAudioMetaTypes(this); Bitstream::registerTypes(this); qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValue); @@ -275,9 +267,6 @@ void ScriptEngine::init() { QScriptValue soundMetaObject = newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); globalObject().setProperty("Sound", soundMetaObject); - QScriptValue injectionOptionValue = scriptValueFromQMetaObject(); - globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); - QScriptValue localVoxelsValue = scriptValueFromQMetaObject(); globalObject().setProperty("LocalVoxels", localVoxelsValue);