From 094faf36179fb643fff360cb03ae202c6dd7cd14 Mon Sep 17 00:00:00 2001 From: Seiji Emery Date: Wed, 5 Sep 2012 15:56:07 -0700 Subject: [PATCH 1/2] Added audio docs. --- .DS_Store | Bin 0 -> 6148 bytes audio.cpp | 99 +++++++++++++++--- audio.h | 77 +++++++++++--- field.cpp | 1 + field.h | 13 +++ interface.xcodeproj/project.pbxproj | 6 ++ .../UserInterfaceState.xcuserstate | Bin 20210 -> 26817 bytes .../xcdebugger/Breakpoints.xcbkptlist | 14 +++ main.cpp | 2 +- particle.cpp | 67 ++++++++++++ particle.h | 30 ++++++ 11 files changed, 281 insertions(+), 28 deletions(-) create mode 100644 .DS_Store create mode 100644 particle.cpp create mode 100644 particle.h diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1b81a9d586ba146115f9796a1435be157470c08f GIT binary patch literal 6148 zcmeI0&1xGl6ot>Voo0qMU?`%SJITENuhWZf<73oM$(Z+Ge)|{0BBsR0nh~y!NpdLd*ODz?$Giz+W^Rp zu|R?pXP86W=^c28@VDE6gn)*)_5E^aq^eA`G)5DEElAYJprPcl%%wIGz?IB(R#yA_ zKdII3Y(3eI;y8MCdXg=aS&mJ;Q04pS%q*vwN~I`1*m5PRx*Utbw!9P7O#1S%F^d-y0na*XuZn;_h|yVt4QG?Xg&WUVr)O-ZAm> zGCMvte80VpW_jG)#yK?RO%;#u92pjavS!zB_JD5S_yz~Ev7i8klc#LRR1 zT=eV5|4%kIlZmRUq*+lQ5D095-z1}_8)yT8z~hGZhn~yrapdDS7wy(Ta29}oPV*+5 z53qiKrF_p)jw}Un$wucM$VGl`&l{2$w5y3C$l4OKFH5Z-k}gX8rC~G$j%-J z1pcP&D!P3BzrjEKe;^Qe#8784os8|Fdp~^E@_G8vH}esGGZA2#B%tN~EPU0BL{7>v KBm#lJ1K~Fyd3K8c literal 0 HcmV?d00001 diff --git a/audio.cpp b/audio.cpp index c05d98ce1d..26ccb19087 100644 --- a/audio.cpp +++ b/audio.cpp @@ -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 #include @@ -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); diff --git a/audio.h b/audio.h index 2daa5abd94..27915648dc 100644 --- a/audio.h +++ b/audio.h @@ -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 diff --git a/field.cpp b/field.cpp index 4b0d2dc86d..29fe7af813 100644 --- a/field.cpp +++ b/field.cpp @@ -103,3 +103,4 @@ void field_render() glEnd(); } + diff --git a/field.h b/field.h index 8867123fdf..9c565861b9 100644 --- a/field.h +++ b/field.h @@ -9,6 +9,8 @@ #ifndef interface_field_h #define interface_field_h +#include + // 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 diff --git a/interface.xcodeproj/project.pbxproj b/interface.xcodeproj/project.pbxproj index a47dfaf8e4..0a0a767709 100644 --- a/interface.xcodeproj/project.pbxproj +++ b/interface.xcodeproj/project.pbxproj @@ -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 = ""; }; + B6BDAE4315F6BE53002A07DF /* particle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = particle.cpp; sourceTree = ""; }; C6859E8B029090EE04C91782 /* test_c_plus.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = test_c_plus.1; sourceTree = ""; }; D40BDFD413404BA300B0BE1F /* GLUT.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLUT.framework; path = /System/Library/Frameworks/GLUT.framework; sourceTree = ""; }; D40BDFD613404BB300B0BE1F /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; @@ -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; }; diff --git a/interface.xcodeproj/project.xcworkspace/xcuserdata/Seiji.xcuserdatad/UserInterfaceState.xcuserstate b/interface.xcodeproj/project.xcworkspace/xcuserdata/Seiji.xcuserdatad/UserInterfaceState.xcuserstate index 9269fb4058fb5ed26c5306db31cadf2f263a3a28..ffea69364a3fb90c1b3bbc5db6276faaa35c8518 100644 GIT binary patch literal 26817 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 literal 20210 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 + + diff --git a/main.cpp b/main.cpp index e99002231c..e657acf0fc 100644 --- a/main.cpp +++ b/main.cpp @@ -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); } } diff --git a/particle.cpp b/particle.cpp new file mode 100644 index 0000000000..c5cc15162b --- /dev/null +++ b/particle.cpp @@ -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; + } + } + } +} + + + + + + + + + diff --git a/particle.h b/particle.h new file mode 100644 index 0000000000..68982377cf --- /dev/null +++ b/particle.h @@ -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 + +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 From 908a8532230fa62582fcf95f941facb5178350eb Mon Sep 17 00:00:00 2001 From: Seiji Emery Date: Fri, 7 Sep 2012 16:15:19 -0700 Subject: [PATCH 2/2] Minor changes: added an additional audio buffer (captures audio input), with a corresponding read method. This buffer is optional, and toggleable via a preprocessor macro. Also added const-correctness for Audio write methods. --- audio.cpp | 92 ++++++++++++++++-- audio.h | 46 +++++++-- interface.xcodeproj/project.pbxproj | 8 ++ .../UserInterfaceState.xcuserstate | Bin 26817 -> 25873 bytes 4 files changed, 128 insertions(+), 18 deletions(-) diff --git a/audio.cpp b/audio.cpp index 26ccb19087..13f6d54ec9 100644 --- a/audio.cpp +++ b/audio.cpp @@ -8,7 +8,7 @@ /** * @file audio.cpp - * Low level audio i/o wrapper around portaudio. + * Low level audio i/o portaudio wrapper. * * @author Seiji Emery * @@ -56,25 +56,56 @@ int audioCallback (const void *inputBuffer, float *input = (float*)inputBuffer; float *output = (float*)outputBuffer; - if (input != NULL) { + #if WRITE_AUDIO_INPUT_TO_OUTPUT + if (input != NULL) {// && Audio::writeAudioInputToOutput) { // combine input into data buffer // temp variables (frames and bufferPos need to remain untouched so they can be used in the second block of code) unsigned int f = (unsigned int)frames, p = data->bufferPos; for (; p < data->bufferLength && f > 0; --f, ++p) { + #if WRITE_AUDIO_INPUT_TO_BUFFER + data->buffer[p].l += + data->inputBuffer[p].l = (*input++) * data->inputGain; + data->buffer[p].r += + data->inputBuffer[p].r = (*input++) * data->inputGain; + #else data->buffer[p].l += (*input++) * data->inputGain; data->buffer[p].r += (*input++) * data->inputGain; + #endif } if (f > 0) { // handle data->buffer wraparound for (p = 0; f > 0; --f, ++p) { + #if WRITE_AUDIO_INPUT_TO_BUFFER + data->buffer[p].l += + data->inputBuffer[p].l = (*input++) * data->inputGain; + data->buffer[p].r += + data->inputBuffer[p].r = (*input++) * data->inputGain; + #else data->buffer[p].l += (*input++) * data->inputGain; data->buffer[p].r += (*input++) * data->inputGain; + #endif + } + } + } + #elif WRITE_AUDIO_INPUT_TO_BUFFER + if (input != NULL) {// && Audio::writeAudioInputToBuffer) { + unsigned int f = (unsigned int)frames, + p = data->bufferPos; + for (; p < data->bufferLength && f > 0; --f, ++p) { + data->inputBuffer[p].l = (*input++) * data->inputGain; + data->inputBuffer[p].r = (*input++) * data->inputGain; + } + if (f > 0) { + // handle data->buffer wraparound + for (p = 0; f > 0; --f, ++p) { + data->inputBuffer[p].l = (*input++) * data->inputGain; + data->inputBuffer[p].r = (*input++) * data->inputGain; } } } - + #endif // Write data->buffer into outputBuffer if (data->bufferPos + frames >= data->bufferLength) { // wraparound: write first section (end of buffer) first @@ -143,7 +174,8 @@ error: */ bool Audio::terminate () { - if (!initialized) return; + if (!initialized) + return true; initialized = false; // err = Pa_StopStream(stream); // if (err != paNoError) goto error; @@ -171,7 +203,9 @@ error: * @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) { +void Audio::writeAudio (unsigned int offset, unsigned int length, float const *left, float const *right) { + if (data->buffer == NULL) + return; if (length > data->bufferLength) { fprintf(stderr, "Audio::writeAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength); length = data->bufferLength; @@ -200,7 +234,9 @@ void Audio::writeAudio (unsigned int offset, unsigned int length, float *left, f * @param[in] left Left component. * @param[in] right Right component. */ -void Audio::writeTone (unsigned int offset, unsigned int length, float left, float right) { +void Audio::writeTone (unsigned int offset, unsigned int length, float const left, float const right) { + if (data->buffer == NULL) + return; if (length > data->bufferLength) { fprintf(stderr, "Audio::writeTone length exceeded (%d). Truncating to %d.\n", length, data->bufferLength); length = data->bufferLength; @@ -230,7 +266,9 @@ void Audio::writeTone (unsigned int offset, unsigned int length, float left, flo * @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) { +void Audio::addAudio (unsigned int offset, unsigned int length, float const *left, float const *right) { + if (data->buffer == NULL) + return; if (length > data->bufferLength) { fprintf(stderr, "Audio::addAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength); length = data->bufferLength; @@ -260,7 +298,9 @@ void Audio::addAudio (unsigned int offset, unsigned int length, float *left, flo * @param[in] left Left component. * @param[in] right Right component. */ -void Audio::addTone (unsigned int offset, unsigned int length, float left, float right) { +void Audio::addTone (unsigned int offset, unsigned int length, float const left, float const right) { + if (data->buffer == NULL) + return; if (length > data->bufferLength) { fprintf(stderr, "Audio::writeTone length exceeded (%d). Truncating to %d.\n", length, data->bufferLength); length = data->bufferLength; @@ -286,6 +326,8 @@ void Audio::addTone (unsigned int offset, unsigned int length, float left, float * @param[in] length Length of section to clear. */ void Audio::clearAudio(unsigned int offset, unsigned int length) { + if (data->buffer == NULL) + return; if (length > data->bufferLength) { fprintf(stderr, "Audio::clearAudio length exceeded (%d). Truncating to %d.\n", length, data->bufferLength); length = data->bufferLength; @@ -304,7 +346,39 @@ void Audio::clearAudio(unsigned int offset, unsigned int length) { } } - +/** + * Read audio input into the target buffer. + * @param[in] offset Offset from the start of the input audio buffer to read from. + * @param[in] length Length of the target buffer. + * @param[out] left Left channel of the target buffer. + * @param[out] right Right channel of the target buffer. + */ +void Audio::readAudioInput (unsigned int offset, unsigned int length, float *left, float *right) { +#if WRITE_AUDIO_INPUT_TO_BUFFER + if (data->inputBuffer == NULL) + return; + if (length + offset > data->bufferLength) { + fprintf(stderr, "Audio::readAudioInput length exceeded (%d + %d). Truncating to %d + %d.\n", offset, length, offset, data->bufferLength - offset); + length = data->bufferLength - offset; + } + unsigned int p = data->bufferPos + offset; + if (p > data->bufferLength) + p -= data->bufferLength; + for (; p < data->bufferLength && length > 0; --length, ++p) { + *left++ = data->inputBuffer[p].l; + *right++ = data->inputBuffer[p].r; + } + if (length > 0) { + p = 0; + for (; length > 0; --length, ++p) { + *left++ = data->inputBuffer[p].l; + *right++ = data->inputBuffer[p].r; + } + } +#else + return; +#endif +} diff --git a/audio.h b/audio.h index 27915648dc..f626b8afde 100644 --- a/audio.h +++ b/audio.h @@ -13,6 +13,24 @@ // Note: main documentation in audio.cpp +/** + * If enabled, direct the audio callback to write the audio input buffer + * directly into the audio output buffer. + */ +#define WRITE_AUDIO_INPUT_TO_OUTPUT 1 +/** + * If enabled, create an additional buffer to store audio input + * and direct the audio callback to write the audio input to this buffer. + */ +#define WRITE_AUDIO_INPUT_TO_BUFFER 0 + +// Note: I initially used static const bools within the Audio class and normal +// 'if' blocks instead of preprocessor - under the assumption that the compiler +// would optimize out the redundant code. +// However, as that apparently did not work (for some reason or another - even +// with full compiler optimization turned on), I've switched to using preprocessor +// macros instead (which is probably faster anyways (at compile-time)). + /** * Low level audio interface. * @@ -30,12 +48,16 @@ public: static bool terminate (); // 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 writeAudio (unsigned int offset, unsigned int length, float const *left, float const *right); + static void addAudio (unsigned int offset, unsigned int length, float const *left, float const *right); + static void writeTone (unsigned int offset, unsigned int length, float const left, float const right); + static void addTone (unsigned int offset, unsigned int length, float const left, float const right); static void clearAudio (unsigned int offset, unsigned int length); + // Read data from internal 'input' audio buffer to an external audio buffer. + // (*only* works if WRITE_AUDIO_INPUT_TO_BUFFER is enabled). + static void readAudioInput (unsigned int offset, unsigned int length, float *left, float *right); + /** * Set the audio input gain. (multiplier applied to mic input) */ @@ -69,7 +91,7 @@ private: */ struct BufferFrame{ float l, r; - } *buffer; + } *buffer, *inputBuffer; /** * Length of the audio buffer. */ @@ -88,14 +110,20 @@ private: AudioData () : bufferPos(0) { inputGain = 1.0f; buffer = new BufferFrame[bufferLength]; - for (unsigned int i = 0; i < bufferLength; ++i) { - buffer[i].l = buffer[i].r = 0; - } + memset((float*)buffer, 0, sizeof(float) * bufferLength * 2); + #if WRITE_AUDIO_INPUT_TO_BUFFER + inputBuffer = new BufferFrame[bufferLength]; + memset((float*)inputBuffer, 0, sizeof(float) * bufferLength * 2); + #else + inputBuffer = NULL; + #endif } ~AudioData () { delete[] buffer; + #if WRITE_AUDIO_INPUT_TO_BUFFER + delete[] inputBuffer; + #endif } - }*data; /** * Internal audio stream handle. diff --git a/interface.xcodeproj/project.pbxproj b/interface.xcodeproj/project.pbxproj index 0a0a767709..8380081120 100644 --- a/interface.xcodeproj/project.pbxproj +++ b/interface.xcodeproj/project.pbxproj @@ -216,6 +216,10 @@ "\"$(SRCROOT)\"", /usr/local/lib, ); + OTHER_CPLUSPLUSFLAGS = ( + "-O3", + "$(OTHER_CFLAGS)", + ); PRODUCT_NAME = interface; }; name = Debug; @@ -233,6 +237,10 @@ "\"$(SRCROOT)\"", /usr/local/lib, ); + OTHER_CPLUSPLUSFLAGS = ( + "-O3", + "$(OTHER_CFLAGS)", + ); PRODUCT_NAME = interface; }; name = Release; diff --git a/interface.xcodeproj/project.xcworkspace/xcuserdata/Seiji.xcuserdatad/UserInterfaceState.xcuserstate b/interface.xcodeproj/project.xcworkspace/xcuserdata/Seiji.xcuserdatad/UserInterfaceState.xcuserstate index ffea69364a3fb90c1b3bbc5db6276faaa35c8518..de265ecd95d91a842eaab269b0f930607119f430 100644 GIT binary patch literal 25873 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 literal 26817 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