mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-05-28 21:01:16 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
bf43db7c14
5 changed files with 248 additions and 61 deletions
|
@ -7,9 +7,11 @@
|
||||||
//
|
//
|
||||||
// Captures mouse clicks and edits voxels accordingly.
|
// Captures mouse clicks and edits voxels accordingly.
|
||||||
//
|
//
|
||||||
// click = create a new voxel on this face, same color as old
|
// click = create a new voxel on this face, same color as old (default color picker state)
|
||||||
// Alt + click = delete this voxel
|
// right click or control + click = delete this voxel
|
||||||
// shift + click = recolor this voxel
|
// shift + click = recolor this voxel
|
||||||
|
// 1 - 8 = pick new color from palette
|
||||||
|
// 9 = create a new voxel in front of the camera
|
||||||
//
|
//
|
||||||
// Click and drag to create more new voxels in the same direction
|
// Click and drag to create more new voxels in the same direction
|
||||||
//
|
//
|
||||||
|
@ -18,15 +20,41 @@ function vLength(v) {
|
||||||
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function vMinus(a, b) {
|
||||||
|
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
var NEW_VOXEL_SIZE = 1.0;
|
||||||
|
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
|
||||||
|
var ORBIT_RATE_ALTITUDE = 200.0;
|
||||||
|
var ORBIT_RATE_AZIMUTH = 90.0;
|
||||||
|
var PIXELS_PER_EXTRUDE_VOXEL = 16;
|
||||||
|
|
||||||
|
var oldMode = Camera.getMode();
|
||||||
|
|
||||||
var key_alt = false;
|
var key_alt = false;
|
||||||
var key_shift = false;
|
var key_shift = false;
|
||||||
var isAdding = false;
|
var isAdding = false;
|
||||||
|
var isExtruding = false;
|
||||||
|
var isOrbiting = false;
|
||||||
|
var orbitAzimuth = 0.0;
|
||||||
|
var orbitAltitude = 0.0;
|
||||||
|
var orbitCenter = { x: 0, y: 0, z: 0 };
|
||||||
|
var orbitPosition = { x: 0, y: 0, z: 0 };
|
||||||
|
var orbitRadius = 0.0;
|
||||||
|
var extrudeDirection = { x: 0, y: 0, z: 0 };
|
||||||
|
var extrudeScale = 0.0;
|
||||||
var lastVoxelPosition = { x: 0, y: 0, z: 0 };
|
var lastVoxelPosition = { x: 0, y: 0, z: 0 };
|
||||||
var lastVoxelColor = { red: 0, green: 0, blue: 0 };
|
var lastVoxelColor = { red: 0, green: 0, blue: 0 };
|
||||||
var lastVoxelScale = 0;
|
var lastVoxelScale = 0;
|
||||||
var dragStart = { x: 0, y: 0 };
|
var dragStart = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
var mouseX = 0;
|
||||||
|
var mouseY = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Create a table of the different colors you can choose
|
// Create a table of the different colors you can choose
|
||||||
var colors = new Array();
|
var colors = new Array();
|
||||||
colors[0] = { red: 237, green: 175, blue: 0 };
|
colors[0] = { red: 237, green: 175, blue: 0 };
|
||||||
|
@ -37,31 +65,70 @@ colors[4] = { red: 193, green: 99, blue: 122 };
|
||||||
colors[5] = { red: 255, green: 54, blue: 69 };
|
colors[5] = { red: 255, green: 54, blue: 69 };
|
||||||
colors[6] = { red: 124, green: 36, blue: 36 };
|
colors[6] = { red: 124, green: 36, blue: 36 };
|
||||||
colors[7] = { red: 63, green: 35, blue: 19 };
|
colors[7] = { red: 63, green: 35, blue: 19 };
|
||||||
var numColors = 6;
|
var numColors = 8;
|
||||||
var whichColor = 0;
|
var whichColor = -1; // Starting color is 'Copy' mode
|
||||||
|
|
||||||
// Create sounds for adding, deleting, recoloring voxels
|
// Create sounds for adding, deleting, recoloring voxels
|
||||||
var addSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Electronic/ElectronicBurst1.raw");
|
var addSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create.raw");
|
||||||
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Bubbles/bubbles1.raw");
|
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+delete.raw");
|
||||||
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Electronic/ElectronicBurst6.raw");
|
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+edit.raw");
|
||||||
|
var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Switches+and+sliders/toggle+switch+-+medium.raw");
|
||||||
var audioOptions = new AudioInjectionOptions();
|
var audioOptions = new AudioInjectionOptions();
|
||||||
|
audioOptions.volume = 0.5;
|
||||||
|
|
||||||
|
function setAudioPosition() {
|
||||||
|
var camera = Camera.getPosition();
|
||||||
|
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||||
|
audioOptions.position = Vec3.sum(camera, forwardVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNewVoxelPosition() {
|
||||||
|
var camera = Camera.getPosition();
|
||||||
|
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||||
|
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, NEW_VOXEL_DISTANCE_FROM_CAMERA));
|
||||||
|
return newPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixEulerAngles(eulers) {
|
||||||
|
var rVal = { x: 0, y: 0, z: eulers.z };
|
||||||
|
if (eulers.x >= 90.0) {
|
||||||
|
rVal.x = 180.0 - eulers.x;
|
||||||
|
rVal.y = eulers.y - 180.0;
|
||||||
|
} else if (eulers.x <= -90.0) {
|
||||||
|
rVal.x = 180.0 - eulers.x;
|
||||||
|
rVal.y = eulers.y - 180.0;
|
||||||
|
}
|
||||||
|
return rVal;
|
||||||
|
}
|
||||||
|
|
||||||
function mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
|
mouseX = event.x;
|
||||||
|
mouseY = event.y;
|
||||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
var intersection = Voxels.findRayIntersection(pickRay);
|
var intersection = Voxels.findRayIntersection(pickRay);
|
||||||
audioOptions.volume = 1.0;
|
audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction);
|
||||||
audioOptions.position = { x: intersection.voxel.x, y: intersection.voxel.y, z: intersection.voxel.z };
|
|
||||||
|
|
||||||
if (intersection.intersects) {
|
if (intersection.intersects) {
|
||||||
if (key_alt) {
|
if (event.isAlt) {
|
||||||
|
// start orbit camera!
|
||||||
|
var cameraPosition = Camera.getPosition();
|
||||||
|
oldMode = Camera.getMode();
|
||||||
|
Camera.setMode("independent");
|
||||||
|
isOrbiting = true;
|
||||||
|
Camera.keepLookingAt(intersection.intersection);
|
||||||
|
// get position for initial azimuth, elevation
|
||||||
|
orbitCenter = intersection.intersection;
|
||||||
|
var orbitVector = Vec3.subtract(cameraPosition, orbitCenter);
|
||||||
|
orbitRadius = vLength(orbitVector);
|
||||||
|
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
|
||||||
|
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
|
||||||
|
|
||||||
|
} else if (event.isRightButton || event.isControl) {
|
||||||
// Delete voxel
|
// Delete voxel
|
||||||
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
|
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
|
||||||
Audio.playSound(deleteSound, audioOptions);
|
Audio.playSound(deleteSound, audioOptions);
|
||||||
|
|
||||||
} else if (key_shift) {
|
} else if (event.isShifted) {
|
||||||
// Recolor Voxel
|
// Recolor Voxel
|
||||||
whichColor++;
|
|
||||||
if (whichColor == numColors) whichColor = 0;
|
|
||||||
Voxels.setVoxel(intersection.voxel.x,
|
Voxels.setVoxel(intersection.voxel.x,
|
||||||
intersection.voxel.y,
|
intersection.voxel.y,
|
||||||
intersection.voxel.z,
|
intersection.voxel.z,
|
||||||
|
@ -70,7 +137,9 @@ function mousePressEvent(event) {
|
||||||
Audio.playSound(changeColorSound, audioOptions);
|
Audio.playSound(changeColorSound, audioOptions);
|
||||||
} else {
|
} else {
|
||||||
// Add voxel on face
|
// Add voxel on face
|
||||||
var newVoxel = {
|
if (whichColor == -1) {
|
||||||
|
// Copy mode - use clicked voxel color
|
||||||
|
var newVoxel = {
|
||||||
x: intersection.voxel.x,
|
x: intersection.voxel.x,
|
||||||
y: intersection.voxel.y,
|
y: intersection.voxel.y,
|
||||||
z: intersection.voxel.z,
|
z: intersection.voxel.z,
|
||||||
|
@ -78,6 +147,16 @@ function mousePressEvent(event) {
|
||||||
red: intersection.voxel.red,
|
red: intersection.voxel.red,
|
||||||
green: intersection.voxel.green,
|
green: intersection.voxel.green,
|
||||||
blue: intersection.voxel.blue };
|
blue: intersection.voxel.blue };
|
||||||
|
} else {
|
||||||
|
var newVoxel = {
|
||||||
|
x: intersection.voxel.x,
|
||||||
|
y: intersection.voxel.y,
|
||||||
|
z: intersection.voxel.z,
|
||||||
|
s: intersection.voxel.s,
|
||||||
|
red: colors[whichColor].red,
|
||||||
|
green: colors[whichColor].green,
|
||||||
|
blue: colors[whichColor].blue };
|
||||||
|
}
|
||||||
|
|
||||||
if (intersection.face == "MIN_X_FACE") {
|
if (intersection.face == "MIN_X_FACE") {
|
||||||
newVoxel.x -= newVoxel.s;
|
newVoxel.x -= newVoxel.s;
|
||||||
|
@ -108,55 +187,113 @@ function mousePressEvent(event) {
|
||||||
function keyPressEvent(event) {
|
function keyPressEvent(event) {
|
||||||
key_alt = event.isAlt;
|
key_alt = event.isAlt;
|
||||||
key_shift = event.isShifted;
|
key_shift = event.isShifted;
|
||||||
|
var nVal = parseInt(event.text);
|
||||||
|
if (event.text == "0") {
|
||||||
|
print("Color = Copy");
|
||||||
|
whichColor = -1;
|
||||||
|
Audio.playSound(clickSound, audioOptions);
|
||||||
|
} else if ((nVal > 0) && (nVal <= numColors)) {
|
||||||
|
whichColor = nVal - 1;
|
||||||
|
print("Color = " + (whichColor + 1));
|
||||||
|
Audio.playSound(clickSound, audioOptions);
|
||||||
|
} else if (event.text == "9") {
|
||||||
|
// Create a brand new 1 meter voxel in front of your avatar
|
||||||
|
var color = whichColor;
|
||||||
|
if (color == -1) color = 0;
|
||||||
|
var newPosition = getNewVoxelPosition();
|
||||||
|
var newVoxel = {
|
||||||
|
x: newPosition.x,
|
||||||
|
y: newPosition.y ,
|
||||||
|
z: newPosition.z,
|
||||||
|
s: NEW_VOXEL_SIZE,
|
||||||
|
red: colors[color].red,
|
||||||
|
green: colors[color].green,
|
||||||
|
blue: colors[color].blue };
|
||||||
|
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||||
|
setAudioPosition();
|
||||||
|
Audio.playSound(addSound, audioOptions);
|
||||||
|
} else if (event.text == " ") {
|
||||||
|
// Reset my orientation!
|
||||||
|
var orientation = { x:0, y:0, z:0, w:1 };
|
||||||
|
Camera.setOrientation(orientation);
|
||||||
|
MyAvatar.orientation = orientation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyReleaseEvent(event) {
|
function keyReleaseEvent(event) {
|
||||||
key_alt = false;
|
key_alt = false;
|
||||||
key_shift = false;
|
key_shift = false;
|
||||||
}
|
}
|
||||||
function mouseMoveEvent(event) {
|
function mouseMoveEvent(event) {
|
||||||
|
if (isOrbiting) {
|
||||||
|
var cameraOrientation = Camera.getOrientation();
|
||||||
|
var origEulers = Quat.safeEulerAngles(cameraOrientation);
|
||||||
|
var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation));
|
||||||
|
var dx = event.x - mouseX;
|
||||||
|
var dy = event.y - mouseY;
|
||||||
|
orbitAzimuth += dx / ORBIT_RATE_AZIMUTH;
|
||||||
|
orbitAltitude += dy / ORBIT_RATE_ALTITUDE;
|
||||||
|
var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius,
|
||||||
|
y:Math.sin(orbitAltitude) * orbitRadius,
|
||||||
|
z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius };
|
||||||
|
orbitPosition = Vec3.sum(orbitCenter, orbitVector);
|
||||||
|
Camera.setPosition(orbitPosition);
|
||||||
|
mouseX = event.x;
|
||||||
|
mouseY = event.y;
|
||||||
|
}
|
||||||
if (isAdding) {
|
if (isAdding) {
|
||||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
// Watch the drag direction to tell which way to 'extrude' this voxel
|
||||||
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
|
if (!isExtruding) {
|
||||||
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
|
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
|
||||||
y: pickRay.origin.y - lastVoxelPosition.y,
|
y: pickRay.origin.y - lastVoxelPosition.y,
|
||||||
z: pickRay.origin.z - lastVoxelPosition.z };
|
z: pickRay.origin.z - lastVoxelPosition.z };
|
||||||
var distance = vLength(lastVoxelDistance);
|
var distance = vLength(lastVoxelDistance);
|
||||||
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
|
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
|
||||||
mouseSpot.x += pickRay.origin.x;
|
mouseSpot.x += pickRay.origin.x;
|
||||||
mouseSpot.y += pickRay.origin.y;
|
mouseSpot.y += pickRay.origin.y;
|
||||||
mouseSpot.z += pickRay.origin.z;
|
mouseSpot.z += pickRay.origin.z;
|
||||||
var dx = mouseSpot.x - lastVoxelPosition.x;
|
var dx = mouseSpot.x - lastVoxelPosition.x;
|
||||||
var dy = mouseSpot.y - lastVoxelPosition.y;
|
var dy = mouseSpot.y - lastVoxelPosition.y;
|
||||||
var dz = mouseSpot.z - lastVoxelPosition.z;
|
var dz = mouseSpot.z - lastVoxelPosition.z;
|
||||||
if (dx > lastVoxelScale) {
|
extrudeScale = lastVoxelScale;
|
||||||
lastVoxelPosition.x += lastVoxelScale;
|
extrudeDirection = { x: 0, y: 0, z: 0 };
|
||||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
isExtruding = true;
|
||||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
if (dx > lastVoxelScale) extrudeDirection.x = extrudeScale;
|
||||||
} else if (dx < -lastVoxelScale) {
|
else if (dx < -lastVoxelScale) extrudeDirection.x = -extrudeScale;
|
||||||
lastVoxelPosition.x -= lastVoxelScale;
|
else if (dy > lastVoxelScale) extrudeDirection.y = extrudeScale;
|
||||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
else if (dy < -lastVoxelScale) extrudeDirection.y = -extrudeScale;
|
||||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
else if (dz > lastVoxelScale) extrudeDirection.z = extrudeScale;
|
||||||
} else if (dy > lastVoxelScale) {
|
else if (dz < -lastVoxelScale) extrudeDirection.z = -extrudeScale;
|
||||||
lastVoxelPosition.y += lastVoxelScale;
|
else isExtruding = false;
|
||||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
} else {
|
||||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
// We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel
|
||||||
} else if (dy < -lastVoxelScale) {
|
var dx = event.x - mouseX;
|
||||||
lastVoxelPosition.y -= lastVoxelScale;
|
var dy = event.y - mouseY;
|
||||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) {
|
||||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection);
|
||||||
} else if (dz > lastVoxelScale) {
|
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||||
lastVoxelPosition.z += lastVoxelScale;
|
extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
mouseX = event.x;
|
||||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
mouseY = event.y;
|
||||||
} else if (dz < -lastVoxelScale) {
|
}
|
||||||
lastVoxelPosition.z -= lastVoxelScale;
|
|
||||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
|
||||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseReleaseEvent(event) {
|
function mouseReleaseEvent(event) {
|
||||||
|
if (isOrbiting) {
|
||||||
|
var cameraOrientation = Camera.getOrientation();
|
||||||
|
var eulers = Quat.safeEulerAngles(cameraOrientation);
|
||||||
|
MyAvatar.position = Camera.getPosition();
|
||||||
|
MyAvatar.orientation = cameraOrientation;
|
||||||
|
Camera.stopLooking();
|
||||||
|
Camera.setMode(oldMode);
|
||||||
|
Camera.setOrientation(cameraOrientation);
|
||||||
|
}
|
||||||
isAdding = false;
|
isAdding = false;
|
||||||
|
isOrbiting = false;
|
||||||
|
isExtruding = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.mousePressEvent.connect(mousePressEvent);
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
|
|
|
@ -37,6 +37,8 @@ static const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS * NUM_AUDI
|
||||||
|
|
||||||
static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0;
|
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;
|
||||||
|
|
||||||
// Mute icon configration
|
// Mute icon configration
|
||||||
static const int ICON_SIZE = 24;
|
static const int ICON_SIZE = 24;
|
||||||
static const int ICON_LEFT = 0;
|
static const int ICON_LEFT = 0;
|
||||||
|
@ -65,7 +67,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
|
||||||
_measuredJitter(0),
|
_measuredJitter(0),
|
||||||
_jitterBufferSamples(initialJitterBufferSamples),
|
_jitterBufferSamples(initialJitterBufferSamples),
|
||||||
_lastInputLoudness(0),
|
_lastInputLoudness(0),
|
||||||
_averageInputLoudness(0),
|
_noiseGateMeasuredFloor(0),
|
||||||
|
_noiseGateSampleCounter(0),
|
||||||
_noiseGateOpen(false),
|
_noiseGateOpen(false),
|
||||||
_noiseGateEnabled(true),
|
_noiseGateEnabled(true),
|
||||||
_noiseGateFramesToClose(0),
|
_noiseGateFramesToClose(0),
|
||||||
|
@ -82,6 +85,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
|
||||||
{
|
{
|
||||||
// clear the array of locally injected samples
|
// clear the array of locally injected samples
|
||||||
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
|
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
|
||||||
|
// Create the noise sample array
|
||||||
|
_noiseSampleFrames = new float[NUMBER_OF_NOISE_SAMPLE_FRAMES];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::init(QGLWidget *parent) {
|
void Audio::init(QGLWidget *parent) {
|
||||||
|
@ -351,26 +356,66 @@ void Audio::handleAudioInput() {
|
||||||
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
|
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
|
||||||
_inputFormat, _desiredInputFormat);
|
_inputFormat, _desiredInputFormat);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Impose Noise Gate
|
||||||
|
//
|
||||||
|
// The Noise Gate is used to reject constant background noise by measuring the noise
|
||||||
|
// floor observed at the microphone and then opening the 'gate' to allow microphone
|
||||||
|
// signals to be transmitted when the microphone samples average level exceeds a multiple
|
||||||
|
// of the noise floor.
|
||||||
|
//
|
||||||
|
// NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate.
|
||||||
|
// Make this value lower for more sensitivity and less rejection of noise.
|
||||||
|
// NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded
|
||||||
|
// to open the gate.
|
||||||
|
// NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames
|
||||||
|
// will we wait before closing the gate.
|
||||||
|
// NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor.
|
||||||
|
// More means better rejection but also can reject continuous things like singing.
|
||||||
|
// NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor?
|
||||||
|
|
||||||
|
|
||||||
float loudness = 0;
|
float loudness = 0;
|
||||||
float thisSample = 0;
|
float thisSample = 0;
|
||||||
int samplesOverNoiseGate = 0;
|
int samplesOverNoiseGate = 0;
|
||||||
|
|
||||||
const float NOISE_GATE_HEIGHT = 3.f;
|
const float NOISE_GATE_HEIGHT = 7.f;
|
||||||
const int NOISE_GATE_WIDTH = 5;
|
const int NOISE_GATE_WIDTH = 5;
|
||||||
const int NOISE_GATE_CLOSE_FRAME_DELAY = 30;
|
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
|
||||||
|
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
|
||||||
|
|
||||||
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||||
thisSample = fabsf(monoAudioSamples[i]);
|
thisSample = fabsf(monoAudioSamples[i]);
|
||||||
loudness += thisSample;
|
loudness += thisSample;
|
||||||
// Noise Reduction: Count peaks above the average loudness
|
// Noise Reduction: Count peaks above the average loudness
|
||||||
if (thisSample > (_averageInputLoudness * NOISE_GATE_HEIGHT)) {
|
if (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT)) {
|
||||||
samplesOverNoiseGate++;
|
samplesOverNoiseGate++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_lastInputLoudness = loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
_lastInputLoudness = loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||||
const float LOUDNESS_AVERAGING_FRAMES = 1000.f; // This will be about 10 seconds
|
|
||||||
_averageInputLoudness = (1.f - 1.f / LOUDNESS_AVERAGING_FRAMES) * _averageInputLoudness + (1.f / LOUDNESS_AVERAGING_FRAMES) * _lastInputLoudness;
|
|
||||||
|
|
||||||
|
float averageOfAllSampleFrames = 0.f;
|
||||||
|
_noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness;
|
||||||
|
if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
|
||||||
|
float smallestSample = FLT_MAX;
|
||||||
|
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i+= NOISE_GATE_FRAMES_TO_AVERAGE) {
|
||||||
|
float thisAverage = 0.0f;
|
||||||
|
for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) {
|
||||||
|
thisAverage += _noiseSampleFrames[j];
|
||||||
|
averageOfAllSampleFrames += _noiseSampleFrames[j];
|
||||||
|
}
|
||||||
|
thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE;
|
||||||
|
|
||||||
|
if (thisAverage < smallestSample) {
|
||||||
|
smallestSample = thisAverage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES;
|
||||||
|
_noiseGateMeasuredFloor = smallestSample;
|
||||||
|
_noiseGateSampleCounter = 0;
|
||||||
|
//qDebug("smallest sample = %.1f, avg of all = %.1f", _noiseGateMeasuredFloor, averageOfAllSampleFrames);
|
||||||
|
}
|
||||||
|
|
||||||
if (_noiseGateEnabled) {
|
if (_noiseGateEnabled) {
|
||||||
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
|
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
|
||||||
_noiseGateOpen = true;
|
_noiseGateOpen = true;
|
||||||
|
@ -487,7 +532,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
||||||
if (!_ringBuffer.isStarved() && _audioOutput->bytesFree() == _audioOutput->bufferSize()) {
|
if (!_ringBuffer.isStarved() && _audioOutput->bytesFree() == _audioOutput->bufferSize()) {
|
||||||
// we don't have any audio data left in the output buffer
|
// we don't have any audio data left in the output buffer
|
||||||
// we just starved
|
// we just starved
|
||||||
qDebug() << "Audio output just starved.";
|
//qDebug() << "Audio output just starved.";
|
||||||
_ringBuffer.setIsStarved(true);
|
_ringBuffer.setIsStarved(true);
|
||||||
_numFramesDisplayStarve = 10;
|
_numFramesDisplayStarve = 10;
|
||||||
}
|
}
|
||||||
|
@ -505,7 +550,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
||||||
if (!_ringBuffer.isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO
|
if (!_ringBuffer.isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO
|
||||||
+ (_jitterBufferSamples * 2))) {
|
+ (_jitterBufferSamples * 2))) {
|
||||||
// starved and we don't have enough to start, keep waiting
|
// starved and we don't have enough to start, keep waiting
|
||||||
qDebug() << "Buffer is starved and doesn't have enough samples to start. Held back.";
|
//qDebug() << "Buffer is starved and doesn't have enough samples to start. Held back.";
|
||||||
} else {
|
} else {
|
||||||
// We are either already playing back, or we have enough audio to start playing back.
|
// We are either already playing back, or we have enough audio to start playing back.
|
||||||
_ringBuffer.setIsStarved(false);
|
_ringBuffer.setIsStarved(false);
|
||||||
|
|
|
@ -45,7 +45,7 @@ public:
|
||||||
|
|
||||||
void render(int screenWidth, int screenHeight);
|
void render(int screenWidth, int screenHeight);
|
||||||
|
|
||||||
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _averageInputLoudness, 0.f); }
|
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); }
|
||||||
|
|
||||||
void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; }
|
void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; }
|
||||||
|
|
||||||
|
@ -109,7 +109,9 @@ private:
|
||||||
float _measuredJitter;
|
float _measuredJitter;
|
||||||
int16_t _jitterBufferSamples;
|
int16_t _jitterBufferSamples;
|
||||||
float _lastInputLoudness;
|
float _lastInputLoudness;
|
||||||
float _averageInputLoudness;
|
float _noiseGateMeasuredFloor;
|
||||||
|
float* _noiseSampleFrames;
|
||||||
|
int _noiseGateSampleCounter;
|
||||||
bool _noiseGateOpen;
|
bool _noiseGateOpen;
|
||||||
bool _noiseGateEnabled;
|
bool _noiseGateEnabled;
|
||||||
int _noiseGateFramesToClose;
|
int _noiseGateFramesToClose;
|
||||||
|
|
|
@ -26,7 +26,9 @@ glm::vec3 Vec3::multiplyQbyV(const glm::quat& q, const glm::vec3& v) {
|
||||||
glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) {
|
glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) {
|
||||||
return v1 + v2;
|
return v1 + v2;
|
||||||
}
|
}
|
||||||
|
glm::vec3 Vec3::subtract(const glm::vec3& v1, const glm::vec3& v2) {
|
||||||
|
return v1 - v2;
|
||||||
|
}
|
||||||
float Vec3::length(const glm::vec3& v) {
|
float Vec3::length(const glm::vec3& v) {
|
||||||
return glm::length(v);
|
return glm::length(v);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ public slots:
|
||||||
glm::vec3 multiply(const glm::vec3& v1, float f);
|
glm::vec3 multiply(const glm::vec3& v1, float f);
|
||||||
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
|
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
|
||||||
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
|
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
|
||||||
|
glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2);
|
||||||
float length(const glm::vec3& v);
|
float length(const glm::vec3& v);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue