Added audio docs.

This commit is contained in:
Seiji Emery 2012-09-05 15:56:07 -07:00
parent 933946320a
commit 094faf3617
11 changed files with 281 additions and 28 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View file

@ -5,6 +5,14 @@
// Created by Seiji Emery on 9/2/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
/**
* @file audio.cpp
* Low level audio i/o wrapper around portaudio.
*
* @author Seiji Emery
*
*/
#include <cstdlib>
#include <cstdio>
@ -18,7 +26,25 @@ Audio::AudioData *Audio::data;
PaStream *Audio::stream;
PaError Audio::err;
float Audio::AudioData::inputGain;
/**
* Audio callback used by portaudio.
* Communicates with Audio via a shared pointer to Audio::data.
* Writes input audio channels (if they exist) into Audio::data->buffer,
multiplied by Audio::data->inputGain.
* Then writes Audio::data->buffer into output audio channels, and clears
the portion of Audio::data->buffer that has been read from for reuse.
*
* @param[in] inputBuffer A pointer to an internal portaudio data buffer containing data read by portaudio.
* @param[out] outputBuffer A pointer to an internal portaudio data buffer to be read by the configured output device.
* @param[in] frames Number of frames that portaudio requests to be read/written.
(Valid size of input/output buffers = frames * number of channels (2) * sizeof data type (float)).
* @param[in] timeInfo Portaudio time info. Currently unused.
* @param[in] statusFlags Portaudio status flags. Currently unused.
* @param[in] userData Pointer to supplied user data (in this case, a pointer to Audio::data).
Used to communicate with external code (since portaudio calls this function from another thread).
* @return Should be of type PaStreamCallbackResult. Return paComplete to end the stream, or paContinue to continue (default).
Can be used to end the stream from within the callback.
*/
int audioCallback (const void *inputBuffer,
void *outputBuffer,
unsigned long frames,
@ -71,10 +97,14 @@ int audioCallback (const void *inputBuffer,
return paContinue;
}
/*
** Initializes portaudio, and creates and starts an audio stream.
/**
* Initialize portaudio and start an audio stream.
* Should be called at the beginning of program exection.
* @seealso Audio::terminate
* @return Returns true if successful or false if an error occurred.
Use Audio::getError() to retrieve the error code.
*/
void Audio::init()
bool Audio::init()
{
initialized = true;
@ -87,7 +117,7 @@ void Audio::init()
2, // input channels
2, // output channels
paFloat32, // sample format
44100, // sample rate
44100, // sample rate (hz)
256, // frames per buffer
audioCallback, // callback function
(void*)data); // user data to be passed to callback
@ -96,17 +126,22 @@ void Audio::init()
err = Pa_StartStream(stream);
if (err != paNoError) goto error;
return;
return paNoError;
error:
fprintf(stderr, "-- Failed to initialize portaudio --\n");
fprintf(stderr, "PortAudio error (%d): %s\n", err, Pa_GetErrorText(err));
exit(err); // replace w/ return value error code?
initialized = false;
delete[] data;
return false;
}
/*
** Closes the running audio stream, and deinitializes portaudio.
/**
* Close the running audio stream, and deinitialize portaudio.
* Should be called at the end of program execution.
* @return Returns true if the initialization was successful, or false if an error occured.
The error code may be retrieved by Audio::getError().
*/
void Audio::terminate ()
bool Audio::terminate ()
{
if (!initialized) return;
initialized = false;
@ -121,13 +156,21 @@ void Audio::terminate ()
err = Pa_Terminate();
if (err != paNoError) goto error;
return;
return true;
error:
fprintf(stderr, "-- portaudio termination error --\n");
fprintf(stderr, "PortAudio error (%d): %s\n", err, Pa_GetErrorText(err));
exit(err);
return false;
}
/**
* Write a stereo audio stream (float*) to the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of audio channels to be read.
* @param[in] left Left channel of the audio stream.
* @param[in] right Right channel of the audio stream.
*/
void Audio::writeAudio (unsigned int offset, unsigned int length, float *left, float *right) {
if (length > data->bufferLength) {
fprintf(stderr, "Audio::writeAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
@ -149,6 +192,14 @@ void Audio::writeAudio (unsigned int offset, unsigned int length, float *left, f
}
}
/**
* Write a repeated stereo sample (float) to the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of tone.
* @param[in] left Left component.
* @param[in] right Right component.
*/
void Audio::writeTone (unsigned int offset, unsigned int length, float left, float right) {
if (length > data->bufferLength) {
fprintf(stderr, "Audio::writeTone length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
@ -170,6 +221,15 @@ void Audio::writeTone (unsigned int offset, unsigned int length, float left, flo
}
}
/**
* Write a stereo audio stream (float*) to the audio buffer.
* Audio stream is added to the existing contents of the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of audio channels to be read.
* @param[in] left Left channel of the audio stream.
* @param[in] right Right channel of the audio stream.
*/
void Audio::addAudio (unsigned int offset, unsigned int length, float *left, float *right) {
if (length > data->bufferLength) {
fprintf(stderr, "Audio::addAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
@ -191,6 +251,15 @@ void Audio::addAudio (unsigned int offset, unsigned int length, float *left, flo
}
}
/**
* Write a repeated stereo sample (float) to the audio buffer.
* Sample is added to the existing contents of the audio buffer.
* Values should be clamped between -1.0f and 1.0f.
* @param[in] offset Write offset from the start of the audio buffer.
* @param[in] length Length of tone.
* @param[in] left Left component.
* @param[in] right Right component.
*/
void Audio::addTone (unsigned int offset, unsigned int length, float left, float right) {
if (length > data->bufferLength) {
fprintf(stderr, "Audio::writeTone length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);
@ -211,7 +280,11 @@ void Audio::addTone (unsigned int offset, unsigned int length, float left, float
}
}
}
/**
* Clear a section of the audio buffer.
* @param[in] offset Offset from the start of the audio buffer.
* @param[in] length Length of section to clear.
*/
void Audio::clearAudio(unsigned int offset, unsigned int length) {
if (length > data->bufferLength) {
fprintf(stderr, "Audio::clearAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength);

77
audio.h
View file

@ -11,43 +11,78 @@
#include "portaudio.h"
typedef short sample_t;
// Note: main documentation in audio.cpp
int audioCallback (const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
/*
** TODO: Docs
/**
* Low level audio interface.
*
* Contains static methods that write to an internal audio buffer, which
is read from by a portaudio callback.
* Responsible for initializing and terminating portaudio. Audio::init()
and Audio::terminate() should be called at the beginning and end of
program execution.
*/
class Audio {
public:
static void init ();
static void terminate ();
// Initializes portaudio. Should be called at the beginning of program execution.
static bool init ();
// Deinitializes portaudio. Should be called at the end of program execution.
static bool terminate ();
// Audio values clamped betewen -1.0f and 1.0f
// Write methods: write to internal audio buffer.
static void writeAudio (unsigned int offset, unsigned int length, float *left, float *right);
static void addAudio (unsigned int offset, unsigned int length, float *left, float *right);
static void writeTone (unsigned int offset, unsigned int length, float left, float right);
static void addTone (unsigned int offset, unsigned int length, float left, float right);
static void clearAudio (unsigned int offset, unsigned int length);
/**
* Set the audio input gain. (multiplier applied to mic input)
*/
static void setInputGain (float gain) {
data->inputGain = gain;
}
/**
* Get the internal portaudio error code (paNoError if none).
* Use in conjunction with Audio::init() or Audio::terminate(), as it is not
impacted by any other methods.
*/
const PaError getError () { return err; }
private:
/**
* Set to true by Audio::init() and false by Audio::terminate().
* Used to prevent Audio::terminate() from deleting uninitialized memory.
*/
static bool initialized;
/**
* Internal audio data.
* Used to communicate with the audio callback code via a shared pointer.
*/
static struct AudioData {
/**
* Internal (stereo) audio buffer.
* Written to by Audio I/O methods and the audio callback.
* As this is a ring buffer, it should not be written to directly thus methods
like Audio::writeAudio are provided.
*/
struct BufferFrame{
float l, r;
} *buffer;
/**
* Length of the audio buffer.
*/
const static unsigned int bufferLength = 1000;
/**
* Current position (start) within the ring buffer.
* Updated by the audio callback.
*/
unsigned int bufferPos;
/**
* Audio input gain (multiplier applied to the incoming audio stream).
* Use Audio::setInputGain() to modify this.
*/
static float inputGain;// = 1.f;
AudioData () : bufferPos(0) {
@ -62,13 +97,27 @@ private:
}
}*data;
/**
* Internal audio stream handle.
*/
static PaStream *stream;
/**
* Internal error code (used only by Audio::init() and Audio::terminate()).
*/
static PaError err;
Audio (); // prevent instantiation (private constructor)
friend int audioCallback (const void*, void*, unsigned long, const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void*);
};
// Audio callback called by portaudio.
int audioCallback (const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
#endif

View file

@ -103,3 +103,4 @@ void field_render()
glEnd();
}

13
field.h
View file

@ -9,6 +9,8 @@
#ifndef interface_field_h
#define interface_field_h
#include <glm/glm.hpp>
// Field is a lattice of vectors uniformly distributed FIELD_ELEMENTS^(1/3) on side
const int FIELD_ELEMENTS = 1000;
@ -18,4 +20,15 @@ int field_value(float *ret, float *pos);
void field_render();
void field_add(float* add, float *loc);
class Field {
public:
static void init ();
static int addTo (const glm::vec3 &pos, glm::vec3 &v);
private:
const static unsigned int fieldSize = 1000;
const static float fieldScale; // defined in cpp inline const float definitions not allowed in standard C++?! (allowed in C++0x)
static glm::vec3 field[fieldSize];
};
#endif

View file

@ -15,6 +15,7 @@
B6BDADE215F44AA5002A07DF /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6BDADD815F444C1002A07DF /* CoreAudio.framework */; };
B6BDADE315F44AB0002A07DF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6BDADDA15F444C9002A07DF /* AudioToolbox.framework */; };
B6BDADE415F44AC7002A07DF /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6BDADDC15F444D3002A07DF /* AudioUnit.framework */; };
B6BDAE4415F6BE53002A07DF /* particle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B6BDAE4315F6BE53002A07DF /* particle.cpp */; };
D40BDFD513404BA300B0BE1F /* GLUT.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D40BDFD413404BA300B0BE1F /* GLUT.framework */; };
D40BDFD713404BB300B0BE1F /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D40BDFD613404BB300B0BE1F /* OpenGL.framework */; };
D4EE3BBC15E45FFE00EE4C89 /* SerialInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D4EE3BBB15E45FFE00EE4C89 /* SerialInterface.cpp */; };
@ -46,6 +47,8 @@
B6BDADDA15F444C9002A07DF /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
B6BDADDC15F444D3002A07DF /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
B6BDADDE15F444DB002A07DF /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
B6BDAE4115F6BE4D002A07DF /* particle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = particle.h; sourceTree = "<group>"; };
B6BDAE4315F6BE53002A07DF /* particle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = particle.cpp; sourceTree = "<group>"; };
C6859E8B029090EE04C91782 /* test_c_plus.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = test_c_plus.1; sourceTree = "<group>"; };
D40BDFD413404BA300B0BE1F /* GLUT.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLUT.framework; path = /System/Library/Frameworks/GLUT.framework; sourceTree = "<absolute>"; };
D40BDFD613404BB300B0BE1F /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
@ -99,6 +102,8 @@
isa = PBXGroup;
children = (
08FB7796FE84155DC02AAC07 /* main.cpp */,
B6BDAE4115F6BE4D002A07DF /* particle.h */,
B6BDAE4315F6BE53002A07DF /* particle.cpp */,
D4EE3BC015E746E900EE4C89 /* world.h */,
D4EE3BC415EBD90C00EE4C89 /* network.h */,
D4EE3BC515EBD93400EE4C89 /* network.cpp */,
@ -189,6 +194,7 @@
D4EE3BC215E761B000EE4C89 /* util.cpp in Sources */,
D4EE3BC615EBD93600EE4C89 /* network.cpp in Sources */,
B6BDADD415F4085B002A07DF /* audio.cpp in Sources */,
B6BDAE4415F6BE53002A07DF /* particle.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -29,5 +29,19 @@
landmarkName = "main(int argc, char** argv)"
landmarkType = "7">
</FileBreakpoint>
<FileBreakpoint
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
isPathRelative = "1"
filePath = "particle.cpp"
timestampString = "368498289.590653"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "26"
endingLineNumber = "26"
landmarkName = "ParticleSystem::simulate (float deltaTime)"
landmarkType = "5">
</FileBreakpoint>
</FileBreakpoints>
</Bucket>

View file

@ -762,7 +762,7 @@ void key(unsigned char k, int x, int y)
field_add(add, pos);
}
if (k == 't') {
Audio::writeTone(0, 150, 0.5f, 0.5f);
Audio::writeTone(0, 400, 1.0f, 0.5f);
}
}

67
particle.cpp Normal file
View file

@ -0,0 +1,67 @@
//
// particle.cpp
// interface
//
// Created by Seiji Emery on 9/4/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#include "particle.h"
void ParticleSystem::simulate (float deltaTime) {
for (unsigned int i = 0; i < particleCount; ++i) {
// Move particles
particles[i].position += particles[i].velocity * deltaTime;
// Add gravity
particles[i].velocity.y -= gravity;
// Drag: decay velocity
particles[i].velocity *= 0.99;
// Add velocity from field
//Field::addTo(particles[i].velocity);
//particles[i].velocity += Field::valueAt(particles[i].position);
if (wrapBounds) {
// wrap around bounds
if (particles[i].position.x > bounds.x)
particles[i].position.x -= bounds.x;
else if (particles[i].position.x < 0.0f)
particles[i].position.x += bounds.x;
if (particles[i].position.y > bounds.y)
particles[i].position.y -= bounds.y;
else if (particles[i].position.y < 0.0f)
particles[i].position.y += bounds.y;
if (particles[i].position.z > bounds.z)
particles[i].position.z -= bounds.z;
else if (particles[i].position.z < 0.0f)
particles[i].position.z += bounds.z;
} else {
// Bounce at bounds
if (particles[i].position.x > bounds.x
|| particles[i].position.x < 0.f) {
particles[i].velocity.x *= -1;
}
if (particles[i].position.y > bounds.y
|| particles[i].position.y < 0.f) {
particles[i].velocity.y *= -1;
}
if (particles[i].position.z > bounds.z
|| particles[i].position.z < 0.f) {
particles[i].velocity.z *= -1;
}
}
}
}

30
particle.h Normal file
View file

@ -0,0 +1,30 @@
//
// particle.h
// interface
//
// Created by Seiji Emery on 9/4/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#ifndef interface_particle_h
#define interface_particle_h
#include <glm/glm.hpp>
class ParticleSystem {
public:
void simulate (float deltaTime);
void draw ();
private:
struct Particle {
glm::vec3 position, velocity;
} *particles;
unsigned int particleCount;
glm::vec3 bounds;
const static bool wrapBounds = false;
const static float gravity = 0.0001;
};
#endif