From 585e3ec3eea2142197b09ca17206142fd6406d8a Mon Sep 17 00:00:00 2001 From: Bill Meltsner Date: Sun, 11 Apr 2010 12:22:40 -0400 Subject: [PATCH] Added sound input and recording functionality. --- src/modules/audio/Audio.h | 202 +++++++++++++++++------------ src/modules/audio/null/Audio.cpp | 19 +++ src/modules/audio/null/Audio.h | 5 + src/modules/audio/openal/Audio.cpp | 46 +++++++ src/modules/audio/openal/Audio.h | 8 ++ src/modules/audio/wrap_Audio.cpp | 109 ++++++++++------ src/modules/audio/wrap_Audio.h | 4 + src/modules/sound/SoundData.cpp | 7 + src/modules/sound/SoundData.h | 1 + 9 files changed, 278 insertions(+), 123 deletions(-) diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index f65bb5549..4fc31f8ab 100644 --- a/src/modules/audio/Audio.h +++ b/src/modules/audio/Audio.h @@ -16,48 +16,48 @@ * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. -**/ - -#ifndef LOVE_AUDIO_AUDIO_H -#define LOVE_AUDIO_AUDIO_H - -#include +**/ + +#ifndef LOVE_AUDIO_AUDIO_H +#define LOVE_AUDIO_AUDIO_H + +#include #include "Source.h" - -namespace love -{ -namespace sound -{ - class Decoder; - class SoundData; -} -namespace audio -{ - /** - * The Audio module is responsible for playing back raw sound samples. - **/ - class Audio : public Module - { - public: - - /** - * Destructor. - **/ - virtual ~Audio(){}; - - virtual Source * newSource(love::sound::Decoder * decoder) = 0; - virtual Source * newSource(love::sound::SoundData * soundData) = 0; - - /** - * Gets the current number of simulatenous playing sources. - * @return The current number of simulatenous playing sources. + +namespace love +{ +namespace sound +{ + class Decoder; + class SoundData; +} +namespace audio +{ + /** + * The Audio module is responsible for playing back raw sound samples. + **/ + class Audio : public Module + { + public: + + /** + * Destructor. + **/ + virtual ~Audio(){}; + + virtual Source * newSource(love::sound::Decoder * decoder) = 0; + virtual Source * newSource(love::sound::SoundData * soundData) = 0; + + /** + * Gets the current number of simultaneous playing sources. + * @return The current number of simultaneous playing sources. + **/ + virtual int getNumSources() const = 0; + + /** + * Gets the maximum supported number of simultaneous playing sources. + * @return The maximum supported number of simultaneous playing sources. **/ - virtual int getNumSources() const = 0; - - /** - * Gets the maximum supported number of simulatenous playing sources. - * @return The maximum supported number of simulatenous playing sources. - **/ virtual int getMaxSources() const = 0; /** @@ -121,49 +121,79 @@ namespace audio * Gets the master volume. * @return The current master volume. **/ - virtual float getVolume() const = 0; - - /** - * Gets the position of the listener. - * @param v A float array of size 3 containing (x,y,z) in that order. - **/ - virtual void getPosition(float * v) const = 0; - - /** - * Sets the position of the listener. - * @param v A float array of size 3 containing [x,y,z] in that order. - **/ - virtual void setPosition(float * v) = 0; - - /** - * Gets the orientation of the listener. - * @param v A float array of size 6 containing [x,y,z] for the forward - * vector, followed by [x,y,z] for the up vector. - **/ - virtual void getOrientation(float * v) const = 0; - - /** - * Sets the orientation of the listener. - * @param v A float array of size 6 containing [x,y,z] for the forward - * vector, followed by [x,y,z] for the up vector. - **/ - virtual void setOrientation(float * v) = 0; - - /** - * Gets the velocity of the listener. - * @param v A float array of size 3 containing [x,y,z] in that order. - **/ - virtual void getVelocity(float * v) const = 0; - - /** - * Sets the velocity of the listener. - * @param v A float array of size 3 containing [x,y,z] in that order. - **/ - virtual void setVelocity(float * v) = 0; - - }; // Audio - -} // audio -} // love - -#endif // LOVE_AUDIO_AUDIO_H + virtual float getVolume() const = 0; + + /** + * Gets the position of the listener. + * @param v A float array of size 3 containing (x,y,z) in that order. + **/ + virtual void getPosition(float * v) const = 0; + + /** + * Sets the position of the listener. + * @param v A float array of size 3 containing [x,y,z] in that order. + **/ + virtual void setPosition(float * v) = 0; + + /** + * Gets the orientation of the listener. + * @param v A float array of size 6 containing [x,y,z] for the forward + * vector, followed by [x,y,z] for the up vector. + **/ + virtual void getOrientation(float * v) const = 0; + + /** + * Sets the orientation of the listener. + * @param v A float array of size 6 containing [x,y,z] for the forward + * vector, followed by [x,y,z] for the up vector. + **/ + virtual void setOrientation(float * v) = 0; + + /** + * Gets the velocity of the listener. + * @param v A float array of size 3 containing [x,y,z] in that order. + **/ + virtual void getVelocity(float * v) const = 0; + + /** + * Sets the velocity of the listener. + * @param v A float array of size 3 containing [x,y,z] in that order. + **/ + virtual void setVelocity(float * v) = 0; + + /** + * Begins recording audio input from the microphone. + **/ + virtual void record() = 0; + + /** + * Gets a section of recorded audio. + * Per OpenAL, the measurement begins from the start of the + * audio data in memory, which is after the last time this function + * was called. If this function has not been called yet this recording + * session, it just grabs from the beginning. + * @return All the recorded SoundData thus far. + **/ + virtual love::sound::SoundData * getRecordedData() = 0; + + /** + * Stops recording and, if passed true, returns all the recorded audio + * not already gotten by getRecordedData. + * @param returnData Whether to return recorded audio. + * @return if returnData, all the recorded audio yet to be gotten, + * otherwise NULL. + **/ + virtual love::sound::SoundData * stopRecording(bool returnData) = 0; + + /** + * Checks whether LOVE is able to record audio input. + * @return hasMic Whether LOVE has a microphone enabled. + **/ + virtual bool canRecord() = 0; + + }; // Audio + +} // audio +} // love + +#endif // LOVE_AUDIO_AUDIO_H diff --git a/src/modules/audio/null/Audio.cpp b/src/modules/audio/null/Audio.cpp index 2a42cd4f0..d33608f42 100644 --- a/src/modules/audio/null/Audio.cpp +++ b/src/modules/audio/null/Audio.cpp @@ -132,6 +132,25 @@ namespace null void Audio::setVelocity(float *) { } + + void Audio::record() + { + } + + love::sound::SoundData * Audio::getRecordedData() + { + return NULL; + } + + love::sound::SoundData * Audio::stopRecording(bool) + { + return NULL; + } + + bool Audio::canRecord() + { + return false; + } } // null } // audio diff --git a/src/modules/audio/null/Audio.h b/src/modules/audio/null/Audio.h index 591eb8ea5..676240a1b 100644 --- a/src/modules/audio/null/Audio.h +++ b/src/modules/audio/null/Audio.h @@ -68,6 +68,11 @@ namespace null void setOrientation(float * v); void getVelocity(float * v) const; void setVelocity(float * v); + + void record(); + love::sound::SoundData * getRecordedData(); + love::sound::SoundData * stopRecording(bool returnData); + bool canRecord(); }; // Audio diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 6786a5a8d..44a06b07d 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -20,6 +20,8 @@ #include "Audio.h" +#include + namespace love { namespace audio @@ -44,6 +46,14 @@ namespace openal if(alcGetError(device) != ALC_NO_ERROR) throw love::Exception("Could not make context current."); + + capture = alcCaptureOpenDevice(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER), + love::sound::Decoder::DEFAULT_SAMPLE_RATE, AL_FORMAT_MONO16, 1048576); // about 23 seconds + + if (!capture) { + // We're not going to prevent LOVE from running without a microphone, but we should warn, at least + std::cerr << "Warning, couldn't open capture device! No audio input!" << std::endl; + } // pool must be allocated after AL context. pool = new Pool(); @@ -61,6 +71,7 @@ namespace openal alcMakeContextCurrent(0); alcDestroyContext(context); + alcCaptureCloseDevice(capture); alcCloseDevice(device); } @@ -188,6 +199,41 @@ namespace openal { alListenerfv(AL_VELOCITY, v); } + + void Audio::record() + { + if (!canRecord()) return; + alcCaptureStart(capture); + } + + love::sound::SoundData * Audio::getRecordedData() + { + if (!canRecord()) return NULL; + int samplerate = love::sound::Decoder::DEFAULT_SAMPLE_RATE; + ALCint samples; + alcGetIntegerv(capture, ALC_CAPTURE_SAMPLES, 4, &samples); + void * data = malloc(samples * (2/sizeof(char))); + alcCaptureSamples(capture, data, samples); + love::sound::SoundData * sd = new love::sound::SoundData(data, samples, samplerate, 16, 1); + free(data); + return sd; + } + + love::sound::SoundData * Audio::stopRecording(bool returnData) + { + if (!canRecord()) return NULL; + love::sound::SoundData * sd = NULL; + if (returnData) { + sd = getRecordedData(); + } + alcCaptureStop(capture); + return sd; + } + + bool Audio::canRecord() + { + return (capture != NULL); + } } // openal } // audio diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index 990325df3..3ba7ed6c3 100644 --- a/src/modules/audio/openal/Audio.h +++ b/src/modules/audio/openal/Audio.h @@ -59,6 +59,9 @@ namespace openal // The OpenAL device. ALCdevice * device; + + // The OpenAL capture device (microphone). + ALCdevice * capture; // The OpenAL context. ALCcontext * context; @@ -107,6 +110,11 @@ namespace openal void setOrientation(float * v); void getVelocity(float * v) const; void setVelocity(float * v); + + void record(); + love::sound::SoundData * getRecordedData(); + love::sound::SoundData * stopRecording(bool returnData); + bool canRecord(); }; // Audio diff --git a/src/modules/audio/wrap_Audio.cpp b/src/modules/audio/wrap_Audio.cpp index 1d540f8cd..b382576e2 100644 --- a/src/modules/audio/wrap_Audio.cpp +++ b/src/modules/audio/wrap_Audio.cpp @@ -26,6 +26,8 @@ #include +#include + namespace love { namespace audio @@ -200,6 +202,36 @@ namespace audio lua_pushnumber(L, v[2]); return 3; } + + int w_record(lua_State *) + { + instance->record(); + return 0; + } + + int w_getRecordedData(lua_State * L) + { + love::sound::SoundData * sd = instance->getRecordedData(); + luax_newtype(L, "SoundData", SOUND_SOUND_DATA_T, (void*)sd); + return 1; + } + + int w_stopRecording(lua_State * L) + { + if (luax_optboolean(L, 1, true)) { + love::sound::SoundData * sd = instance->stopRecording(true); + luax_newtype(L, "SoundData", SOUND_SOUND_DATA_T, (void*)sd); + return 1; + } + instance->stopRecording(false); + return 0; + } + + int w_canRecord(lua_State * L) { + luax_pushboolean(L, instance->canRecord()); + return 1; + } + // List of functions to wrap. static const luaL_Reg functions[] = { @@ -218,6 +250,9 @@ namespace audio { "getOrientation", w_getOrientation }, { "setVelocity", w_setVelocity }, { "getVelocity", w_getVelocity }, + { "record", w_record }, + { "getRecordedData", w_getRecordedData }, + { "stopRecording", w_stopRecording }, { 0, 0 } }; @@ -230,47 +265,47 @@ namespace audio { if(instance == 0) { - // Try OpenAL first. - try - { - instance = new love::audio::openal::Audio(); - } - catch(love::Exception & e) - { - std::cout << e.what() << std::endl; - } + // Try OpenAL first. + try + { + instance = new love::audio::openal::Audio(); + } + catch(love::Exception & e) + { + std::cout << e.what() << std::endl; + } } else - instance->retain(); - - if(instance == 0) - { - // Fall back to nullaudio. - try - { - instance = new love::audio::null::Audio(); - } - catch(love::Exception & e) - { - std::cout << e.what() << std::endl; - } - } - - if(instance == 0) + instance->retain(); + + if(instance == 0) + { + // Fall back to nullaudio. + try + { + instance = new love::audio::null::Audio(); + } + catch(love::Exception & e) + { + std::cout << e.what() << std::endl; + } + } + + if(instance == 0) return luaL_error(L, "Could not open any audio module."); - WrappedModule w; - w.module = instance; - w.name = "audio"; - w.flags = MODULE_T; - w.functions = functions; - w.types = types; - - luax_register_module(L, w); - - if (luaL_loadbuffer(L, (const char *)audio_lua, sizeof(audio_lua), "audio.lua") == 0) - lua_call(L, 0, 0); - + WrappedModule w; + w.module = instance; + w.name = "audio"; + w.flags = MODULE_T; + w.functions = functions; + w.types = types; + + luax_register_module(L, w); + + if (luaL_loadbuffer(L, (const char *)audio_lua, sizeof(audio_lua), "audio.lua") == 0) + lua_call(L, 0, 0); + return 0; } diff --git a/src/modules/audio/wrap_Audio.h b/src/modules/audio/wrap_Audio.h index d2a6e271b..90908e3ce 100644 --- a/src/modules/audio/wrap_Audio.h +++ b/src/modules/audio/wrap_Audio.h @@ -46,6 +46,10 @@ namespace audio int w_getOrientation(lua_State * L); int w_setVelocity(lua_State * L); int w_getVelocity(lua_State * L); + int w_record(lua_State * L); + int w_getRecordedData(lua_State * L); + int w_stopRecording(lua_State * L); + int w_canRecord(lua_State * L); extern "C" LOVE_EXPORT int luaopen_love_audio(lua_State * L); } // audio diff --git a/src/modules/sound/SoundData.cpp b/src/modules/sound/SoundData.cpp index 159c229e7..ca3175fd9 100644 --- a/src/modules/sound/SoundData.cpp +++ b/src/modules/sound/SoundData.cpp @@ -63,6 +63,13 @@ namespace sound { data = (char*)malloc(size); } + + SoundData::SoundData(void * d, int samples, int sampleRate, int bits, int channels) + : data(0), size(samples*(bits/8)), sampleRate(sampleRate), bits(bits), channels(channels) + { + data = (char*)malloc(size); + memcpy(data, d, size); + } SoundData::~SoundData() { diff --git a/src/modules/sound/SoundData.h b/src/modules/sound/SoundData.h index 5228c5ae6..eb8978369 100644 --- a/src/modules/sound/SoundData.h +++ b/src/modules/sound/SoundData.h @@ -45,6 +45,7 @@ namespace sound SoundData(Decoder * decoder); SoundData(int samples, int sampleRate, int bits, int channels); + SoundData(void * d, int samples, int sampleRate, int bits, int channels); virtual ~SoundData();