Merge pull request #6161 from jherico/controllers

Fix hand poses, air guitar chord and instrument changing and RAW sound file playback.
This commit is contained in:
Brad Hefta-Gaub 2015-10-23 08:59:30 -07:00
commit 816cf3d516
3 changed files with 71 additions and 52 deletions

View file

@ -90,7 +90,7 @@ var audioInjector = null;
var selectorPressed = false; var selectorPressed = false;
var position; var position;
MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0); MyAvatar.attach(guitarModel, "Hips", {x: leftHanded ? -0.2 : 0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, leftHanded ? 75 : -75), 1.0);
function checkHands(deltaTime) { function checkHands(deltaTime) {
var strumVelocity = Controller.getPoseValue(strumHand).velocity; var strumVelocity = Controller.getPoseValue(strumHand).velocity;
@ -114,27 +114,32 @@ function checkHands(deltaTime) {
// Change guitars if button FWD (5) pressed // Change guitars if button FWD (5) pressed
if (Controller.getValue(changeGuitar)) { if (Controller.getValue(changeGuitar)) {
print("changeGuitar:" + changeGuitar);
if (!selectorPressed) { if (!selectorPressed) {
print("changeGuitar:" + changeGuitar);
guitarSelector += NUM_CHORDS; guitarSelector += NUM_CHORDS;
if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
guitarSelector = 0; guitarSelector = 0;
} }
print("new guitarBase: " + guitarSelector);
stopAudio(true);
selectorPressed = true; selectorPressed = true;
} }
} else { } else {
selectorPressed = false; selectorPressed = false;
} }
//print("selectorPressed:" + selectorPressed);
if (Controller.getValue(chord1)) { if (Controller.getValue(chord1)) {
whichChord = 1; whichChord = 1;
stopAudio(true);
} else if (Controller.getValue(chord2)) { } else if (Controller.getValue(chord2)) {
whichChord = 2; whichChord = 2;
stopAudio(true);
} else if (Controller.getValue(chord3)) { } else if (Controller.getValue(chord3)) {
whichChord = 3; whichChord = 3;
stopAudio(true);
} else if (Controller.getValue(chord4)) { } else if (Controller.getValue(chord4)) {
whichChord = 4; whichChord = 4;
stopAudio(true);
} }
var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;
@ -154,26 +159,27 @@ function checkHands(deltaTime) {
lastPosition = strumHandPosition; lastPosition = strumHandPosition;
} }
function playChord(position, volume) { function stopAudio(killInjector) {
if (audioInjector && audioInjector.isPlaying) { if (audioInjector && audioInjector.isPlaying) {
print("stopped sound"); print("stopped sound");
audioInjector.stop(); audioInjector.stop();
} }
if (killInjector) {
audioInjector = null;
}
}
function playChord(position, volume) {
stopAudio();
print("Played sound: " + whichChord + " at volume " + volume); print("Played sound: " + whichChord + " at volume " + volume);
if (!audioInjector) { if (!audioInjector) {
var index = guitarSelector + whichChord;
// FIXME - we apparenlty broke RAW file playback, so we need WAV files for all these chords. In the mean var chord = chords[guitarSelector + whichChord];
// time, we will just play the heyMan wave file for all chords
var chord = heyManWave; // chords[guitarSelector + whichChord];
audioInjector = Audio.playSound(chord, { position: position, volume: volume }); audioInjector = Audio.playSound(chord, { position: position, volume: volume });
print("audioInjector: " + JSON.stringify(audioInjector));
} else { } else {
print("audioInjector: " + JSON.stringify(audioInjector));
audioInjector.restart(); audioInjector.restart();
} }
} }
function keyPressEvent(event) { function keyPressEvent(event) {
@ -181,15 +187,19 @@ function keyPressEvent(event) {
keyVolume = 0.4; keyVolume = 0.4;
if (event.text == "1") { if (event.text == "1") {
whichChord = 1; whichChord = 1;
stopAudio(true);
playChord(MyAvatar.position, keyVolume); playChord(MyAvatar.position, keyVolume);
} else if (event.text == "2") { } else if (event.text == "2") {
whichChord = 2; whichChord = 2;
stopAudio(true);
playChord(MyAvatar.position, keyVolume); playChord(MyAvatar.position, keyVolume);
} else if (event.text == "3") { } else if (event.text == "3") {
whichChord = 3; whichChord = 3;
stopAudio(true);
playChord(MyAvatar.position, keyVolume); playChord(MyAvatar.position, keyVolume);
} else if (event.text == "4") { } else if (event.text == "4") {
whichChord = 4; whichChord = 4;
stopAudio(true);
playChord(MyAvatar.position, keyVolume); playChord(MyAvatar.position, keyVolume);
} }
} }
@ -197,6 +207,7 @@ function keyPressEvent(event) {
function scriptEnding() { function scriptEnding() {
MyAvatar.detachOne(guitarModel); MyAvatar.detachOne(guitarModel);
} }
// Connect a call back that happens every frame // Connect a call back that happens every frame
Script.update.connect(checkHands); Script.update.connect(checkHands);
Script.scriptEnding.connect(scriptEnding); Script.scriptEnding.connect(scriptEnding);

View file

@ -59,26 +59,18 @@ Sound::Sound(const QUrl& url, bool isStereo) :
void Sound::downloadFinished(const QByteArray& data) { void Sound::downloadFinished(const QByteArray& data) {
// replace our byte array with the downloaded data // replace our byte array with the downloaded data
QByteArray rawAudioByteArray = QByteArray(data); QByteArray rawAudioByteArray = QByteArray(data);
QString fileName = getURL().fileName(); QString fileName = getURL().fileName().toLower();
const QString WAV_EXTENSION = ".wav";
static const QString WAV_EXTENSION = ".wav";
static const QString RAW_EXTENSION = ".raw";
if (fileName.endsWith(WAV_EXTENSION)) { if (fileName.endsWith(WAV_EXTENSION)) {
QString headerContentType = "audio/x-wav";
//QByteArray headerContentType = reply->rawHeader("Content-Type");
// WAV audio file encountered
if (headerContentType == "audio/x-wav"
|| headerContentType == "audio/wav"
|| headerContentType == "audio/wave"
|| fileName.endsWith(WAV_EXTENSION)) {
QByteArray outputAudioByteArray; QByteArray outputAudioByteArray;
interpretAsWav(rawAudioByteArray, outputAudioByteArray); interpretAsWav(rawAudioByteArray, outputAudioByteArray);
downSample(outputAudioByteArray); downSample(outputAudioByteArray);
} else { trimFrames();
} else if (fileName.endsWith(RAW_EXTENSION)) {
// check if this was a stereo raw file // check if this was a stereo raw file
// since it's raw the only way for us to know that is if the file was called .stereo.raw // since it's raw the only way for us to know that is if the file was called .stereo.raw
if (fileName.toLower().endsWith("stereo.raw")) { if (fileName.toLower().endsWith("stereo.raw")) {
@ -88,10 +80,9 @@ void Sound::downloadFinished(const QByteArray& data) {
// Process as RAW file // Process as RAW file
downSample(rawAudioByteArray); downSample(rawAudioByteArray);
}
trimFrames(); trimFrames();
} else { } else {
qCDebug(audio) << "Network reply without 'Content-Type'."; qCDebug(audio) << "Unknown sound file type";
} }
_isReady = true; _isReady = true;

View file

@ -18,6 +18,7 @@
#include <QtCore/QJsonArray> #include <QtCore/QJsonArray>
#include <PathUtils.h> #include <PathUtils.h>
#include <NumericalConstants.h>
#include "StandardController.h" #include "StandardController.h"
#include "Logging.h" #include "Logging.h"
@ -653,7 +654,16 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
return Input(STANDARD_DEVICE, pose, ChannelType::POSE); return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
} }
static auto lastDebugTime = usecTimestampNow();
static auto debugRoutes = false;
static const auto DEBUG_INTERVAL = USECS_PER_SECOND;
void UserInputMapper::runMappings() { void UserInputMapper::runMappings() {
auto now = usecTimestampNow();
if (now - lastDebugTime > DEBUG_INTERVAL) {
lastDebugTime = now;
debugRoutes = true;
}
static auto deviceNames = getDeviceNames(); static auto deviceNames = getDeviceNames();
for (auto endpointEntry : this->_endpointsByInput) { for (auto endpointEntry : this->_endpointsByInput) {
endpointEntry.second->reset(); endpointEntry.second->reset();
@ -673,17 +683,17 @@ void UserInputMapper::runMappings() {
} }
applyRoute(route); applyRoute(route);
} }
debugRoutes = false;
} }
void UserInputMapper::applyRoute(const Route::Pointer& route) { void UserInputMapper::applyRoute(const Route::Pointer& route) {
if (route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Applying route " << route->json; qCDebug(controllers) << "Applying route " << route->json;
} }
if (route->conditional) { if (route->conditional) {
if (!route->conditional->satisfied()) { if (!route->conditional->satisfied()) {
if (route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Conditional failed"; qCDebug(controllers) << "Conditional failed";
} }
return; return;
@ -698,7 +708,7 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) {
// I press the button. The exception is if I'm wiring a control back to itself // I press the button. The exception is if I'm wiring a control back to itself
// in order to adjust my interface, like inverting the Y axis on an analog stick // in order to adjust my interface, like inverting the Y axis on an analog stick
if (!source->readable()) { if (!source->readable()) {
if (route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Source unreadable"; qCDebug(controllers) << "Source unreadable";
} }
return; return;
@ -708,14 +718,14 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) {
// THis could happen if the route destination failed to create // THis could happen if the route destination failed to create
// FIXME: Maybe do not create the route if the destination failed and avoid this case ? // FIXME: Maybe do not create the route if the destination failed and avoid this case ?
if (!destination) { if (!destination) {
if (route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Bad Destination"; qCDebug(controllers) << "Bad Destination";
} }
return; return;
} }
if (!destination->writeable()) { if (!destination->writeable()) {
if (route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Destination unwritable"; qCDebug(controllers) << "Destination unwritable";
} }
return; return;
@ -723,17 +733,24 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) {
// Fetch the value, may have been overriden by previous loopback routes // Fetch the value, may have been overriden by previous loopback routes
if (source->isPose()) { if (source->isPose()) {
if (route->debug) {
qCDebug(controllers) << "Applying pose";
}
Pose value = getPose(source); Pose value = getPose(source);
static const Pose IDENTITY_POSE { vec3(), quat() };
if (debugRoutes && route->debug) {
if (!value.valid) {
qCDebug(controllers) << "Applying invalid pose";
} else if (value == IDENTITY_POSE) {
qCDebug(controllers) << "Applying identity pose";
} else {
qCDebug(controllers) << "Applying valid pose";
}
}
// no filters yet for pose // no filters yet for pose
destination->apply(value, Pose(), source); destination->apply(value, Pose(), source);
} else { } else {
// Fetch the value, may have been overriden by previous loopback routes // Fetch the value, may have been overriden by previous loopback routes
float value = getValue(source); float value = getValue(source);
if (route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Value was " << value; qCDebug(controllers) << "Value was " << value;
} }
// Apply each of the filters. // Apply each of the filters.
@ -741,7 +758,7 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) {
value = filter->apply(value); value = filter->apply(value);
} }
if (route->debug) { if (debugRoutes && route->debug) {
qCDebug(controllers) << "Filtered value was " << value; qCDebug(controllers) << "Filtered value was " << value;
} }
@ -1148,13 +1165,13 @@ void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) {
// because standard -> action is the first set of routes added. // because standard -> action is the first set of routes added.
Route::List standardRoutes = mapping->routes; Route::List standardRoutes = mapping->routes;
standardRoutes.remove_if([](const Route::Pointer& value) { standardRoutes.remove_if([](const Route::Pointer& value) {
return (value->source->getInput().device == STANDARD_DEVICE); return (value->source->getInput().device != STANDARD_DEVICE);
}); });
_standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end()); _standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end());
Route::List deviceRoutes = mapping->routes; Route::List deviceRoutes = mapping->routes;
deviceRoutes.remove_if([](const Route::Pointer& value) { deviceRoutes.remove_if([](const Route::Pointer& value) {
return (value->source->getInput().device != STANDARD_DEVICE); return (value->source->getInput().device == STANDARD_DEVICE);
}); });
_deviceRoutes.insert(_deviceRoutes.begin(), deviceRoutes.begin(), deviceRoutes.end()); _deviceRoutes.insert(_deviceRoutes.begin(), deviceRoutes.begin(), deviceRoutes.end());
} }