Browse Source

Added sound input and recording functionality.

Bill Meltsner 15 years ago
parent
commit
585e3ec3ee

+ 117 - 87
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 <common/Module.h>
+**/
+
+#ifndef LOVE_AUDIO_AUDIO_H
+#define LOVE_AUDIO_AUDIO_H
+
+#include <common/Module.h>
 #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.
-		**/
-		virtual int getNumSources() const = 0;
-
-		/**
-		* Gets the maximum supported number of simulatenous playing sources.
-		* @return The maximum supported 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 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

+ 19 - 0
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

+ 5 - 0
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
 

+ 46 - 0
src/modules/audio/openal/Audio.cpp

@@ -20,6 +20,8 @@
 
 #include "Audio.h"
 
+#include <sound/Decoder.h>
+
 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

+ 8 - 0
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
 

+ 72 - 37
src/modules/audio/wrap_Audio.cpp

@@ -26,6 +26,8 @@
 
 #include <scripts/audio.lua.h>
 
+#include <common/runtime.h>
+
 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;
 	}
 

+ 4 - 0
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

+ 7 - 0
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()
 	{

+ 1 - 0
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();