Fixed problems with ring buffer not handling stereo right, reset audio buffers on spacebar.

This commit is contained in:
Philip Rosedale 2013-06-20 18:49:05 -07:00
parent 56b8a5880d
commit 13b729d9b4
8 changed files with 101 additions and 45 deletions

View file

@ -537,6 +537,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_Space:
resetSensors();
_audio.reset();
break;
case Qt::Key_G:

View file

@ -28,7 +28,7 @@
// Uncomment the following definition to test audio device latency by copying output to input
//#define TEST_AUDIO_LOOPBACK
//#define SHOW_AUDIO_DEBUG
#define VISUALIZE_ECHO_CANCELLATION
@ -152,41 +152,50 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
AudioRingBuffer* ringBuffer = &_ringBuffer;
// if we've been reset, and there isn't any new packets yet
// just play some silence
// if there is anything in the ring buffer, decide what to do:
if (ringBuffer->getEndOfLastWrite()) {
if (!ringBuffer->isStarted() && ringBuffer->diffLastWriteNextOutput() < (PACKET_LENGTH_SAMPLES + _jitterBufferSamples)) {
if (!ringBuffer->isStarted() && ringBuffer->diffLastWriteNextOutput() < (PACKET_LENGTH_SAMPLES + _jitterBufferSamples * (ringBuffer->isStereo() ? 2 : 1))) {
//
// If not enough audio has arrived to start playback, keep waiting
//
//printLog("Held back, buffer has %d of %d samples required.\n",
// ringBuffer->diffLastWriteNextOutput(),
// PACKET_LENGTH_SAMPLES + parentAudio->_jitterBufferSamples);
} else if (ringBuffer->diffLastWriteNextOutput() < PACKET_LENGTH_SAMPLES) {
#ifdef SHOW_AUDIO_DEBUG
printLog("%i,%i,%i,%i\n",
_packetsReceivedThisPlayback,
ringBuffer->diffLastWriteNextOutput(),
PACKET_LENGTH_SAMPLES,
_jitterBufferSamples);
#endif
} else if (ringBuffer->isStarted() && (ringBuffer->diffLastWriteNextOutput()
< PACKET_LENGTH_SAMPLES * (ringBuffer->isStereo() ? 2 : 1))) {
//
// If we have run out of audio to send to the audio device, we have starved,
// so reset the ring buffer and packet counters.
// If we have started and now have run out of audio to send to the audio device,
// this means we've starved and should restart.
//
ringBuffer->setStarted(false);
_numStarves++;
_packetsReceivedThisPlayback = 0;
//printLog("Starved, remaining buffer msecs = %.0f\n",
// ringBuffer->diffLastWriteNextOutput() / PACKET_LENGTH_SAMPLES * AUDIO_CALLBACK_MSECS);
_wasStarved = 10; // Frames to render the indication that the system was starved.
_wasStarved = 10; // Frames for which to render the indication that the system was starved.
#ifdef SHOW_AUDIO_DEBUG
printLog("Starved, remaining samples = %.0f\n",
ringBuffer->diffLastWriteNextOutput());
#endif
} else {
//
// We are either already playing back, or we have enough audio to start playing back.
//
if (!ringBuffer->isStarted()) {
ringBuffer->setStarted(true);
//printLog("starting playback %0.1f msecs delayed, jitter = %d, pkts recvd: %d \n",
// (usecTimestampNow() - usecTimestamp(&parentAudio->_firstPacketReceivedTime))/1000.0,
// parentAudio->_jitterBufferSamples,
// parentAudio->_packetsReceivedThisPlayback);
#ifdef SHOW_AUDIO_DEBUG
printLog("starting playback %0.1f msecs delayed, jitter = %d, pkts recvd: %d \n",
(usecTimestampNow() - usecTimestamp(&_firstPacketReceivedTime))/1000.0,
_jitterBufferSamples,
_packetsReceivedThisPlayback);
#endif
}
//
// play whatever we have in the audio buffer
//
@ -313,6 +322,11 @@ static void outputPortAudioError(PaError error) {
}
}
void Audio::reset() {
_packetsReceivedThisPlayback = 0;
_ringBuffer.reset();
}
Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
_stream(NULL),
_ringBuffer(true),

View file

@ -33,6 +33,7 @@ public:
Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples);
~Audio();
void reset();
void render(int screenWidth, int screenHeight);
void addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBytes);

View file

@ -443,7 +443,16 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
}
// update balls
if (_balls) { _balls->simulate(deltaTime); }
if (_balls) {
_balls->moveOrigin(_position);
glm::vec3 lookAt = _head.getLookAtPosition();
if (glm::length(lookAt) > EPSILON) {
_balls->moveOrigin(lookAt);
} else {
_balls->moveOrigin(_position);
}
_balls->simulate(deltaTime);
}
// update torso rotation based on head lean
_skeleton.joint[AVATAR_JOINT_TORSO].rotation = glm::quat(glm::radians(glm::vec3(
@ -978,7 +987,6 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
// Render the balls
if (_balls) {
glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z);
_balls->render();
glPopMatrix();
}

View file

@ -7,36 +7,55 @@
// A cloud of spring-mass spheres to simulate the avatar body/skin. Each ball
// connects to as many as 4 neighbors, and executes motion according to a damped
// spring, while responding physically to other avatars.
//
//
#include <glm/glm.hpp>
#include "Util.h"
#include "sharedUtil.h"
#include "world.h"
#include "InterfaceConfig.h"
#include "Balls.h"
const float INITIAL_AREA = 0.2f;
const float BALL_RADIUS = 0.025f;
const glm::vec3 INITIAL_COLOR(0.62f, 0.74f, 0.91f);
Balls::Balls(int numberOfBalls) {
_numberOfBalls = numberOfBalls;
_balls = new Ball[_numberOfBalls];
for (unsigned int i = 0; i < _numberOfBalls; ++i) {
_balls[i].position = glm::vec3(1.0 + randFloat() * 0.5,
0.5 + randFloat() * 0.5,
1.0 + randFloat() * 0.5);
_balls[i].radius = 0.02 + randFloat() * 0.06;
_balls[i].position = randVector() * INITIAL_AREA;
_balls[i].targetPosition = _balls[i].position;
_balls[i].velocity = glm::vec3(0, 0, 0);
_balls[i].radius = BALL_RADIUS;
for (unsigned int j = 0; j < NUMBER_SPRINGS; ++j) {
_balls[i].links[j] = rand() % (numberOfBalls + 1);
if (_balls[i].links[j]-1 == i) { _balls[i].links[j] = 0; }
_balls[i].springLength[j] = 0.5;
}
_balls[i].links[j] = NULL;
}
}
_color = INITIAL_COLOR;
_origin = glm::vec3(0, 0, 0);
}
void Balls::moveOrigin(const glm::vec3& newOrigin) {
glm::vec3 delta = newOrigin - _origin;
if (glm::length(delta) > EPSILON) {
_origin = newOrigin;
for (unsigned int i = 0; i < _numberOfBalls; ++i) {
_balls[i].targetPosition += delta;
}
}
}
const bool RENDER_SPRINGS = true;
const bool RENDER_SPRINGS = false;
void Balls::render() {
// Render Balls NOTE: This needs to become something other that GlutSpheres!
glColor3f(0.62,0.74,0.91);
glColor3fv(&_color.x);
for (unsigned int i = 0; i < _numberOfBalls; ++i) {
glPushMatrix();
glTranslatef(_balls[i].position.x, _balls[i].position.y, _balls[i].position.z);
glutSolidSphere(_balls[i].radius, 15, 15);
glutSolidSphere(_balls[i].radius, 8, 8);
glPopMatrix();
}
@ -71,18 +90,22 @@ void Balls::simulate(float deltaTime) {
// Move particles
_balls[i].position += _balls[i].velocity * deltaTime;
_balls[i].targetPosition += _balls[i].velocity * deltaTime;
// Drag: decay velocity
_balls[i].velocity *= (1.f - CONSTANT_VELOCITY_DAMPING * deltaTime);
// Add noise
_balls[i].velocity += glm::vec3((randFloat() - 0.5) * NOISE_SCALE,
(randFloat() - 0.5) * NOISE_SCALE,
(randFloat() - 0.5) * NOISE_SCALE);
_balls[i].velocity += randVector() * NOISE_SCALE;
// Approach target position
for (unsigned int i = 0; i < _numberOfBalls; ++i) {
_balls[i].position += randFloat() * deltaTime * (_balls[i].targetPosition - _balls[i].position);
}
// Spring Force
/*
for (unsigned int j = 0; j < NUMBER_SPRINGS; ++j) {
if(_balls[i].links[j] > 0) {
float separation = glm::distance(_balls[i].position,
@ -96,7 +119,7 @@ void Balls::simulate(float deltaTime) {
//_balls[i].velocity *= (1.f - SPRING_DAMPING*deltaTime);
}
}
} */

View file

@ -9,12 +9,6 @@
#ifndef hifi_Balls_h
#define hifi_Balls_h
#include <glm/glm.hpp>
#include "Util.h"
#include "world.h"
#include "InterfaceConfig.h"
const int NUMBER_SPRINGS = 4;
class Balls {
@ -24,14 +18,19 @@ public:
void simulate(float deltaTime);
void render();
void setColor(const glm::vec3& c) { _color = c; };
void moveOrigin(const glm::vec3& newOrigin);
private:
struct Ball {
glm::vec3 position, velocity;
glm::vec3 position, targetPosition, velocity;
int links[NUMBER_SPRINGS];
float springLength[NUMBER_SPRINGS];
float radius;
} *_balls;
int _numberOfBalls;
glm::vec3 _origin;
glm::vec3 _color;
};
#endif

View file

@ -27,6 +27,12 @@ AudioRingBuffer::~AudioRingBuffer() {
delete[] _buffer;
}
void AudioRingBuffer::reset() {
_endOfLastWrite = _buffer;
_nextOutput = _buffer;
_isStarted = false;
}
int AudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
return parseAudioSamples(sourceBuffer + sizeof(PACKET_HEADER_MIXED_AUDIO), numBytes - sizeof(PACKET_HEADER_MIXED_AUDIO));
}

View file

@ -30,6 +30,7 @@ public:
AudioRingBuffer(bool isStereo);
~AudioRingBuffer();
void reset();
int parseData(unsigned char* sourceBuffer, int numBytes);
int parseAudioSamples(unsigned char* sourceBuffer, int numBytes);
@ -44,8 +45,11 @@ public:
bool isStarted() const { return _isStarted; }
void setStarted(bool isStarted) { _isStarted = isStarted; }
int diffLastWriteNextOutput() const;
bool isStereo() const { return _isStereo; }
protected:
// disallow copying of AudioRingBuffer objects
AudioRingBuffer(const AudioRingBuffer&);