Browse Source

Microphone input implemented.

RecordingDevice class added
(number)love.audio.getRecordingDeviceCount() and (table)love.audio.getRecordingDevices() exposed
(bool):startRecording([samples, sampleRate, bitDepth, channels], (SoundData):stopRecording([SoundData]),
(SoundData):getData([SoundData]), (number):getSampleCount(), (number):getSampleRate(), (number):getBitDepth(),
(number):getChannels(), (string):getName(), (number):getID(), (bool):isRecording() member functions exposed
previously existed stub implementation removed
getFormat moved to love::audio::openal::Audio
getFormat arguments order swapped to (bitDepth, channels)
getFormat now returns AL_NONE if format is invalid

--HG--
branch : minor-mic-input
Raidho 8 years ago
parent
commit
87f0b2811e

+ 8 - 0
CMakeLists.txt

@@ -312,10 +312,14 @@ set(LOVE_SRC_MODULE_AUDIO_ROOT
 	src/modules/audio/Audio.h
 	src/modules/audio/Source.cpp
 	src/modules/audio/Source.h
+	src/modules/audio/RecordingDevice.cpp
+	src/modules/audio/RecordingDevice.h
 	src/modules/audio/wrap_Audio.cpp
 	src/modules/audio/wrap_Audio.h
 	src/modules/audio/wrap_Source.cpp
 	src/modules/audio/wrap_Source.h
+	src/modules/audio/wrap_RecordingDevice.cpp
+	src/modules/audio/wrap_RecordingDevice.h
 )
 
 set(LOVE_SRC_MODULE_AUDIO_NULL
@@ -323,6 +327,8 @@ set(LOVE_SRC_MODULE_AUDIO_NULL
 	src/modules/audio/null/Audio.h
 	src/modules/audio/null/Source.cpp
 	src/modules/audio/null/Source.h
+	src/modules/audio/null/RecordingDevice.cpp
+	src/modules/audio/null/RecordingDevice.h
 )
 
 set(LOVE_SRC_MODULE_AUDIO_OPENAL
@@ -332,6 +338,8 @@ set(LOVE_SRC_MODULE_AUDIO_OPENAL
 	src/modules/audio/openal/Pool.h
 	src/modules/audio/openal/Source.cpp
 	src/modules/audio/openal/Source.h
+	src/modules/audio/openal/RecordingDevice.cpp
+	src/modules/audio/openal/RecordingDevice.h
 )
 
 set(LOVE_SRC_MODULE_AUDIO

+ 1 - 0
src/common/types.cpp

@@ -72,6 +72,7 @@ static const TypeBits *createTypeFlags()
 
 	// Audio.
 	b[AUDIO_SOURCE_ID] = (one << AUDIO_SOURCE_ID) | b[OBJECT_ID];
+	b[AUDIO_RECORDING_DEVICE_ID] = (one << AUDIO_RECORDING_DEVICE_ID) | b[OBJECT_ID];
 
 	// Sound.
 	b[SOUND_SOUND_DATA_ID] = (one << SOUND_SOUND_DATA_ID) | b[DATA_ID];

+ 1 - 0
src/common/types.h

@@ -73,6 +73,7 @@ enum Type
 
 	// Audio
 	AUDIO_SOURCE_ID,
+	AUDIO_RECORDING_DEVICE_ID,
 
 	// Sound
 	SOUND_SOUND_DATA_ID,

+ 8 - 21
src/modules/audio/Audio.h

@@ -28,6 +28,7 @@
 #include "common/Module.h"
 #include "common/StringMap.h"
 #include "Source.h"
+#include "RecordingDevice.h"
 
 namespace love
 {
@@ -189,34 +190,20 @@ public:
 	virtual float getDopplerScale() const = 0;
 
 	/**
-	 * Begins recording audio input from the microphone.
+	 * @return Number of recording devices.
 	 **/
-	virtual void record() = 0;
+	virtual int getRecordingDeviceCount() const = 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.
+	 * @param index Index number of recording device. 0 is default device.
+	 * @return Selected recording device.
 	 **/
-	virtual love::sound::SoundData *getRecordedData() = 0;
+	virtual RecordingDevice *getRecordingDevice(int index) const = 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.
+	 * @return Index number of a recording device, -1 if not present.
 	 **/
-	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;
+	virtual int getRecordingDeviceIndex(RecordingDevice *device) const = 0;
 
 	/**
 	 * Gets the distance model used for attenuation.

+ 37 - 0
src/modules/audio/RecordingDevice.cpp

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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.
+ **/
+
+#include "RecordingDevice.h"
+
+namespace love
+{
+namespace audio
+{
+
+RecordingDevice::RecordingDevice()
+{
+}
+
+RecordingDevice::~RecordingDevice()
+{
+}
+
+} //audio
+} //love

+ 107 - 0
src/modules/audio/RecordingDevice.h

@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented = 0; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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_RECORDING_DEVICE_H
+#define LOVE_AUDIO_RECORDING_DEVICE_H
+
+#include "common/Object.h"
+#include "sound/SoundData.h"
+
+#include <string>
+
+namespace love
+{
+namespace audio
+{
+
+class RecordingDevice : public love::Object
+{
+public:
+	RecordingDevice();
+	virtual ~RecordingDevice();
+
+	/**
+	 * Begins audio input recording process. using default (previous) parameters.
+	 * @return True if recording started successfully.
+	 **/
+	virtual bool startRecording() = 0;
+
+	/**
+	 * Begins audio input recording process.
+	 * @param samples Number of samples to buffer.
+	 * @param sampleRate Desired sample rate.
+	 * @param bitDepth Desired bit depth (8 or 16).
+	 * @param channels Desired number of channels. 
+	 * @return True if recording started successfully.
+	 **/
+	virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels) = 0;
+
+	/** 
+	 * Stops audio input recording.
+	 **/
+	virtual void stopRecording() = 0;
+
+	/**
+	 * Retreives recorded data. 
+	 * @param soundData Reference to a SoundData to fill.
+	 * @return number of samples obtained from device.
+	 **/
+	virtual int getData(love::sound::SoundData *soundData) = 0;
+
+	/**
+	 * @return C string device name.
+	 **/ 
+	virtual const char *getName() const = 0;
+
+	/**
+	 * @return Unique ID number.
+	 **/ 
+	virtual int getID() const = 0;
+
+	/**
+	 * @return Number of samples currently recorded.
+	 **/
+	virtual int getSampleCount() const = 0;
+
+	/**
+	 * @return Sample rate for recording.
+	 **/
+	virtual int getSampleRate() const = 0;
+
+	/**
+	 * @return Bit depth for recording.
+	 **/
+	virtual int getBitDepth() const = 0;
+
+	/**
+	 * @return Number of channels for recording.
+	 **/
+	virtual int getChannels() const = 0;
+
+	/**
+	 * @return True if currently recording.
+	 **/
+	virtual bool isRecording() const = 0;
+}; //RecordingDevice
+
+} //audio
+} //love
+
+#endif //LOVE_AUDIO_RECORDING_DEVICE_H

+ 6 - 10
src/modules/audio/null/Audio.cpp

@@ -144,23 +144,19 @@ float Audio::getDopplerScale() const
 	return 1.0f;
 }
 
-void Audio::record()
+int Audio::getRecordingDeviceCount() const
 {
+	return 0;
 }
 
-love::sound::SoundData *Audio::getRecordedData()
-{
-	return NULL;
-}
-
-love::sound::SoundData *Audio::stopRecording(bool)
+love::audio::RecordingDevice *Audio::getRecordingDevice(int index) const
 {
-	return NULL;
+	return nullptr;
 }
 
-bool Audio::canRecord()
+int Audio::getRecordingDeviceIndex(love::audio::RecordingDevice *device) const
 {
-	return false;
+	return -1;
 }
 
 Audio::DistanceModel Audio::getDistanceModel() const

+ 4 - 4
src/modules/audio/null/Audio.h

@@ -24,6 +24,7 @@
 // LOVE
 #include "audio/Audio.h"
 
+#include "RecordingDevice.h"
 #include "Source.h"
 
 namespace love
@@ -70,10 +71,9 @@ public:
 	void setDopplerScale(float scale);
 	float getDopplerScale() const;
 
-	void record();
-	love::sound::SoundData *getRecordedData();
-	love::sound::SoundData *stopRecording(bool returnData);
-	bool canRecord();
+	int getRecordingDeviceCount() const;
+	love::audio::RecordingDevice *getRecordingDevice(int index) const;
+	int getRecordingDeviceIndex(love::audio::RecordingDevice *device) const;
 
 	DistanceModel getDistanceModel() const;
 	void setDistanceModel(DistanceModel distanceModel);

+ 97 - 0
src/modules/audio/null/RecordingDevice.cpp

@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented = 0; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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.
+ **/
+
+#include "RecordingDevice.h"
+#include "Audio.h"
+
+namespace love
+{
+namespace audio
+{
+namespace null
+{
+
+const char *RecordingDevice::name = "null";
+
+RecordingDevice::RecordingDevice(const char *, int)
+{
+}
+
+RecordingDevice::~RecordingDevice()
+{
+}
+
+bool RecordingDevice::startRecording()
+{
+	return false;
+}
+
+bool RecordingDevice::startRecording(int, int, int, int)
+{
+	return false;
+}
+
+void RecordingDevice::stopRecording()
+{
+}
+
+int RecordingDevice::getData(love::sound::SoundData*)
+{
+	return 0;
+}
+
+int RecordingDevice::getSampleCount() const
+{
+	return 0;
+}
+
+int RecordingDevice::getSampleRate() const
+{
+	return 8000;
+}
+
+int RecordingDevice::getBitDepth() const
+{
+	return 16;
+}
+
+int RecordingDevice::getChannels() const
+{
+	return 1;
+}
+
+const char *RecordingDevice::getName() const
+{
+	return name;
+}
+
+int RecordingDevice::getID() const
+{
+	return 0;
+}
+
+bool RecordingDevice::isRecording() const
+{
+	return false;
+}
+
+} //null
+} //audio
+} //love

+ 59 - 0
src/modules/audio/null/RecordingDevice.h

@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented = 0; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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_NULL_RECORDING_DEVICE_H
+#define LOVE_AUDIO_NULL_RECORDING_DEVICE_H
+
+#include "audio/RecordingDevice.h"
+#include "sound/SoundData.h"
+
+namespace love
+{
+namespace audio
+{
+namespace null
+{
+
+class RecordingDevice : public love::audio::RecordingDevice
+{
+public:
+	RecordingDevice(const char *name, int id);
+	virtual ~RecordingDevice();
+	virtual bool startRecording();
+	virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels);
+	virtual void stopRecording();
+	virtual int getData(love::sound::SoundData *soundData);
+	virtual const char *getName() const;
+	virtual int getID() const;
+	virtual int getSampleCount() const;
+	virtual int getSampleRate() const;
+	virtual int getBitDepth() const;
+	virtual int getChannels() const;
+	virtual bool isRecording() const;
+
+private:
+	static const char *name;
+}; //RecordingDevice
+
+} //null
+} //audio
+} //love
+
+#endif //LOVE_AUDIO_NULL_RECORDING_DEVICE_H

+ 68 - 57
src/modules/audio/openal/Audio.cpp

@@ -20,10 +20,11 @@
 
 #include "Audio.h"
 #include "common/delay.h"
-
+#include "RecordingDevice.h"
 #include "sound/Decoder.h"
 
 #include <cstdlib>
+#include <iostream>
 
 namespace love
 {
@@ -67,9 +68,29 @@ void Audio::PoolThread::setFinish()
 	finish = true;
 }
 
+ALenum Audio::getFormat(int bitDepth, int channels)
+{
+	if (bitDepth != 8 && bitDepth != 16)
+		return AL_NONE;
+
+	if (channels == 1)
+		return bitDepth == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
+	else if (channels == 2)
+		return bitDepth == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16;
+	#ifdef AL_EXT_MCFORMATS
+	else if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
+	{
+		if (channels == 6)
+			return bitDepth == 8 ? AL_FORMAT_51CHN8 : AL_FORMAT_51CHN16;
+		else if (channels == 8)
+			return bitDepth == 8 ? AL_FORMAT_71CHN8 : AL_FORMAT_71CHN16;
+	}
+	#endif
+	return AL_NONE;
+}
+
 Audio::Audio()
 	: device(nullptr)
-	, capture(nullptr)
 	, context(nullptr)
 	, pool(nullptr)
 	, poolThread(nullptr)
@@ -89,25 +110,28 @@ Audio::Audio()
 	if (!alcMakeContextCurrent(context) || alcGetError(device) != ALC_NO_ERROR)
 		throw love::Exception("Could not make context current.");
 
-	/*std::string captureName(alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
-	const ALCchar * devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
-	while (*devices)
+	//find and push default capture device
+	//AL may not return actual device name string when asked directly
+	std::string defaultname;
+	ALCdevice *defaultdevice = alcCaptureOpenDevice(NULL, 8000, 8, 1);
+	if (alGetError() == AL_NO_ERROR)
 	{
-		std::string device(devices);
-		devices += device.size() + 1;
-		if (device.find("Mic") != std::string::npos || device.find("mic") != std::string::npos)
-		{
-			captureName = device;
-		}
+		defaultname = alcGetString(defaultdevice, ALC_CAPTURE_DEVICE_SPECIFIER);
+		alcCaptureCloseDevice(defaultdevice);
+		capture.push_back(new RecordingDevice(defaultname.c_str(), 0));
 	}
 
-	capture = alcCaptureOpenDevice(captureName.c_str(), 8000, AL_FORMAT_MONO16, 262144); // about 32 seconds
-
-	if (!capture)
+	const ALCchar *devstr = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
+	size_t offset = 0;
+	while (true)
 	{
-		// 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;
-	}*/
+		if (devstr[offset] == '\0')
+			break;
+		std::string str((ALCchar*)&devstr[offset]);
+		if (str != defaultname)
+			capture.push_back(new RecordingDevice(str.c_str(), capture.size()));
+		offset += str.length() + 1;
+	}
 
 	// pool must be allocated after AL context.
 	try
@@ -118,8 +142,10 @@ Audio::Audio()
 	{
 		alcMakeContextCurrent(nullptr);
 		alcDestroyContext(context);
-		//if (capture) alcCaptureCloseDevice(capture);
 		alcCloseDevice(device);
+		for (unsigned int i = 0; i < capture.size(); i++)
+			delete capture[i];
+
 		throw;
 	}
 
@@ -137,8 +163,9 @@ Audio::~Audio()
 
 	alcMakeContextCurrent(nullptr);
 	alcDestroyContext(context);
-	//if (capture) alcCaptureCloseDevice(capture);
 	alcCloseDevice(device);
+	for (unsigned int i = 0; i < capture.size(); i++)
+		delete capture[i];
 }
 
 
@@ -265,44 +292,6 @@ float Audio::getDopplerScale() const
 	return alGetFloat(AL_DOPPLER_FACTOR);
 }
 
-void Audio::record()
-{
-	if (!canRecord()) return;
-	alcCaptureStart(capture);
-}
-
-love::sound::SoundData *Audio::getRecordedData()
-{
-	if (!canRecord())
-		return NULL;
-	int samplerate = 8000;
-	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);
-}
-
 Audio::DistanceModel Audio::getDistanceModel() const
 {
 	return distanceModel;
@@ -347,6 +336,28 @@ void Audio::setDistanceModel(DistanceModel distanceModel)
 	}
 }
 
+int Audio::getRecordingDeviceCount() const
+{
+	return capture.size();
+}
+
+love::audio::RecordingDevice *Audio::getRecordingDevice(int index) const
+{
+	if (index < 0 || (unsigned int)index >= capture.size())
+		return nullptr;
+
+	return capture[index];
+}
+
+int Audio::getRecordingDeviceIndex(love::audio::RecordingDevice *device) const
+{
+	for (unsigned int i = 0; i < capture.size(); i++)
+		if (device == capture[i])
+			return i;
+
+	return -1;
+}
+
 } // openal
 } // audio
 } // love

+ 15 - 6
src/modules/audio/openal/Audio.h

@@ -29,6 +29,7 @@
 
 // LOVE
 #include "audio/Audio.h"
+#include "audio/RecordingDevice.h"
 #include "common/config.h"
 #include "sound/SoundData.h"
 
@@ -65,6 +66,15 @@ public:
 	Audio();
 	~Audio();
 
+	/**
+	 * Gets the OpenAL format identifier based on number of
+	 * channels and bits.
+	 * @param channels.
+	 * @param bitDepth Either 8-bit samples, or 16-bit samples.
+	 * @return One of AL_FORMAT_*, or AL_NONE if unsupported format.
+	 **/
+	static ALenum getFormat(int bitDepth, int channels);
+
 	// Implements Module.
 	const char *getName() const;
 
@@ -95,10 +105,9 @@ public:
 	void setDopplerScale(float scale);
 	float getDopplerScale() const;
 
-	void record();
-	love::sound::SoundData *getRecordedData();
-	love::sound::SoundData *stopRecording(bool returnData);
-	bool canRecord();
+	int getRecordingDeviceCount() const;
+	love::audio::RecordingDevice *getRecordingDevice(int index) const;
+	int getRecordingDeviceIndex(love::audio::RecordingDevice *device) const;
 
 	DistanceModel getDistanceModel() const;
 	void setDistanceModel(DistanceModel distanceModel);
@@ -108,8 +117,8 @@ private:
 	// The OpenAL device.
 	ALCdevice *device;
 
-	// The OpenAL capture device (microphone).
-	ALCdevice *capture;
+	// The OpenAL capture devices.
+	std::vector<love::audio::RecordingDevice*> capture;
 
 	// The OpenAL context.
 	ALCcontext *context;

+ 156 - 0
src/modules/audio/openal/RecordingDevice.cpp

@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented = 0; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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.
+ **/
+
+#include "RecordingDevice.h"
+#include "Audio.h"
+
+namespace love
+{
+namespace audio
+{
+namespace openal
+{
+
+class InvalidFormatException : public love::Exception
+{
+public:
+
+	InvalidFormatException(int channels, int bitdepth)
+		: Exception("Recording %d channels with %d bits per sample is not supported.", channels, bitdepth)
+	{
+	}
+
+};
+
+RecordingDevice::RecordingDevice(const char *name, int id) 
+	: name(name)
+	, id(id)
+{
+}
+
+RecordingDevice::~RecordingDevice()
+{
+	if (!isRecording())
+		return;
+
+	alcCaptureStop(device);
+	alcCaptureCloseDevice(device);
+}
+
+bool RecordingDevice::startRecording()
+{
+	return startRecording(samples, sampleRate, bitDepth, channels);
+}
+
+bool RecordingDevice::startRecording(int samples, int sampleRate, int bitDepth, int channels)
+{
+	if (isRecording())
+	{
+		alcCaptureStop(device);
+		alcCaptureCloseDevice(device);
+	}
+
+	ALenum format = Audio::getFormat(bitDepth, channels);
+	if (format == AL_NONE)
+		throw InvalidFormatException(channels, bitDepth);
+
+	device = alcCaptureOpenDevice(name.c_str(), sampleRate, format, samples);
+	if (device == nullptr)
+		return false;
+
+	alcCaptureStart(device);
+	this->samples = samples;
+	this->sampleRate = sampleRate;
+	this->bitDepth = bitDepth;
+	this->channels = channels;
+	return true;
+}
+
+void RecordingDevice::stopRecording()
+{
+	if (!isRecording())
+		return;
+
+	alcCaptureStop(device);
+	alcCaptureCloseDevice(device);
+	device = nullptr;
+}
+
+int RecordingDevice::getData(love::sound::SoundData *soundData)
+{
+	if (!isRecording())
+		return 0;
+
+	int samples = getSampleCount();
+	if (samples == 0)
+		return 0;
+
+	//resize internal buffer to proper size
+	if (samples != soundData->getSampleCount())
+		soundData->load(samples, sampleRate, bitDepth, channels);
+
+	alcCaptureSamples(device, soundData->getData(), samples);
+
+	return samples;
+}
+
+int RecordingDevice::getSampleCount() const
+{
+	if (!isRecording())
+		return 0;
+
+	ALCint samples;
+	alcGetIntegerv(device, ALC_CAPTURE_SAMPLES, sizeof(ALCint), &samples);
+	return (int)samples;
+}
+
+int RecordingDevice::getSampleRate() const
+{
+	return sampleRate;
+}
+
+int RecordingDevice::getBitDepth() const
+{
+	return bitDepth;
+}
+
+int RecordingDevice::getChannels() const
+{
+	return channels;
+}
+
+const char *RecordingDevice::getName() const
+{
+	return name.c_str();
+}
+
+int RecordingDevice::getID() const
+{
+	return id;
+}
+
+bool RecordingDevice::isRecording() const
+{
+	return device == nullptr ? false : true;
+}
+
+} //openal
+} //audio
+} //love

+ 78 - 0
src/modules/audio/openal/RecordingDevice.h

@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented = 0; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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_OPENAL_RECORDING_DEVICE_H
+#define LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H
+
+#ifdef LOVE_APPLE_USE_FRAMEWORKS
+#ifdef LOVE_IOS
+#include <OpenAL/alc.h>
+#include <OpenAL/al.h>
+#else
+#include <OpenAL-Soft/alc.h>
+#include <OpenAL-Soft/al.h>
+#endif
+#else
+#include <AL/alc.h>
+#include <AL/al.h>
+#endif
+
+#include "audio/RecordingDevice.h"
+#include "sound/SoundData.h"
+
+namespace love
+{
+namespace audio
+{
+namespace openal
+{
+
+class RecordingDevice : public love::audio::RecordingDevice
+{
+public:
+	RecordingDevice(const char *name, int id);
+	virtual ~RecordingDevice();
+	virtual bool startRecording();
+	virtual bool startRecording(int samples, int sampleRate, int bitDepth, int channels);
+	virtual void stopRecording();
+	virtual int getData(love::sound::SoundData *soundData);
+	virtual const char *getName() const;
+	virtual int getID() const;
+	virtual int getSampleCount() const;
+	virtual int getSampleRate() const;
+	virtual int getBitDepth() const;
+	virtual int getChannels() const;
+	virtual bool isRecording() const;
+
+private:
+	int samples = 8192;
+	int sampleRate = 8000;
+	int bitDepth = 16;
+	int channels = 1;
+	std::string name;
+	int id;
+	ALCdevice *device = nullptr;
+}; //RecordingDevice
+
+} //openal
+} //audio
+} //love
+
+#endif //LOVE_AUDIO_OPENAL_RECORDING_DEVICE_H

+ 10 - 37
src/modules/audio/openal/Source.cpp

@@ -20,6 +20,7 @@
 
 #include "Source.h"
 #include "Pool.h"
+#include "Audio.h"
 #include "common/math.h"
 
 // STD
@@ -119,8 +120,8 @@ Source::Source(Pool *pool, love::sound::SoundData *soundData)
 	, channels(soundData->getChannels())
 	, bitDepth(soundData->getBitDepth())
 {
-	ALenum fmt = getFormat(soundData->getChannels(), soundData->getBitDepth());
-	if (fmt == 0)
+	ALenum fmt = Audio::getFormat(soundData->getBitDepth(), soundData->getChannels());
+	if (fmt == AL_NONE)
 		throw InvalidFormatException(soundData->getChannels(), soundData->getBitDepth());
 
 	staticBuffer.set(new StaticDataBuffer(fmt, soundData->getData(), (ALsizei) soundData->getSize(), sampleRate), Acquire::NORETAIN);
@@ -141,7 +142,7 @@ Source::Source(Pool *pool, love::sound::Decoder *decoder)
 	, decoder(decoder)
 	, unusedBufferTop(MAX_BUFFERS - 1)
 {
-	if (getFormat(decoder->getChannels(), decoder->getBitDepth()) == 0)
+	if (Audio::getFormat(decoder->getBitDepth(), decoder->getChannels()) == AL_NONE)
 		throw InvalidFormatException(decoder->getChannels(), decoder->getBitDepth());
 
 	alGenBuffers(MAX_BUFFERS, streamBuffers);
@@ -162,8 +163,8 @@ Source::Source(Pool *pool, int sampleRate, int bitDepth, int channels)
 	, channels(channels)
 	, bitDepth(bitDepth)
 {
-	ALenum fmt = getFormat(channels, bitDepth);
-	if (fmt == 0)
+	ALenum fmt = Audio::getFormat(bitDepth, channels);
+	if (fmt == AL_NONE)
 		throw InvalidFormatException(channels, bitDepth);
 
 	alGenBuffers(MAX_BUFFERS, streamBuffers);
@@ -688,7 +689,7 @@ bool Source::queueAtomic(void *data, ALsizei length)
 		if (buffer == AL_NONE)
 			return false;
 
-		alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate);
+		alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
 		alSourceQueueBuffers(source, 1, &buffer);
 		unusedBufferPop();
 	}
@@ -699,7 +700,7 @@ bool Source::queueAtomic(void *data, ALsizei length)
 			return false;
 
 		//stack acts as queue while stopped
-		alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate);
+		alBufferData(buffer, Audio::getFormat(bitDepth, channels), data, length, sampleRate);
 		unusedBufferQueue(buffer);
 	}
 	bufferedBytes += length;
@@ -966,34 +967,6 @@ void Source::setFloatv(float *dst, const float *src) const
 	dst[2] = src[2];
 }
 
-ALenum Source::getFormat(int channels, int bitDepth) const
-{
-	if (channels == 1 && bitDepth == 8)
-		return AL_FORMAT_MONO8;
-	else if (channels == 1 && bitDepth == 16)
-		return AL_FORMAT_MONO16;
-	else if (channels == 2 && bitDepth == 8)
-		return AL_FORMAT_STEREO8;
-	else if (channels == 2 && bitDepth == 16)
-		return AL_FORMAT_STEREO16;
-
-#ifdef AL_EXT_MCFORMATS
-	if (alIsExtensionPresent("AL_EXT_MCFORMATS"))
-	{
-		if (channels == 6 && bitDepth == 8)
-			return AL_FORMAT_51CHN8;
-		else if (channels == 6 && bitDepth == 16)
-			return AL_FORMAT_51CHN16;
-		else if (channels == 8 && bitDepth == 8)
-			return AL_FORMAT_71CHN8;
-		else if (channels == 8 && bitDepth == 16)
-			return AL_FORMAT_71CHN16;
-	}
-#endif
-
-	return 0;
-}
-
 ALuint Source::unusedBufferPeek()
 {
 	return (unusedBufferTop < 0) ? AL_NONE : unusedBuffers[unusedBufferTop];
@@ -1029,9 +1002,9 @@ int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d)
 	// OpenAL implementations are allowed to ignore 0-size alBufferData calls.
 	if (decoded > 0)
 	{
-		int fmt = getFormat(d->getChannels(), d->getBitDepth());
+		int fmt = Audio::getFormat(d->getBitDepth(), d->getChannels());
 
-		if (fmt != 0)
+		if (fmt != AL_NONE)
 			alBufferData(buffer, fmt, d->getBuffer(), decoded, d->getSampleRate());
 		else
 			decoded = 0;

+ 0 - 9
src/modules/audio/openal/Source.h

@@ -163,15 +163,6 @@ private:
 
 	void setFloatv(float *dst, const float *src) const;
 
-	/**
-	 * Gets the OpenAL format identifier based on number of
-	 * channels and bits.
-	 * @param channels Either 1 (mono) or 2 (stereo).
-	 * @param bitDepth Either 8-bit samples, or 16-bit samples.
-	 * @return One of AL_FORMAT_*, or 0 if unsupported format.
-	 **/
-	ALenum getFormat(int channels, int bitDepth) const;
-
 	int streamAtomic(ALuint buffer, love::sound::Decoder *d);
 
 	ALuint unusedBufferPeek();

+ 24 - 46
src/modules/audio/wrap_Audio.cpp

@@ -257,49 +257,6 @@ int w_getDopplerScale(lua_State *L)
 	return 1;
 }
 
-int w_record(lua_State *)
-{
-	instance()->record();
-	return 0;
-}
-
-int w_getRecordedData(lua_State *L)
-{
-	love::sound::SoundData *sd = instance()->getRecordedData();
-	if (!sd)
-		lua_pushnil(L);
-	else
-	{
-		luax_pushtype(L, SOUND_SOUND_DATA_ID, sd);
-		sd->release();
-	}
-	return 1;
-}
-
-int w_stopRecording(lua_State *L)
-{
-	if (luax_optboolean(L, 1, true))
-	{
-		love::sound::SoundData *sd = instance()->stopRecording(true);
-		if (!sd)
-			lua_pushnil(L);
-		else
-		{
-			luax_pushtype(L, SOUND_SOUND_DATA_ID, sd);
-			sd->release();
-		}
-		return 1;
-	}
-	instance()->stopRecording(false);
-	return 0;
-}
-
-int w_canRecord(lua_State *L)
-{
-	luax_pushboolean(L, instance()->canRecord());
-	return 1;
-}
-
 int w_setDistanceModel(lua_State *L)
 {
 	const char *modelStr = luaL_checkstring(L, 1);
@@ -320,6 +277,27 @@ int w_getDistanceModel(lua_State *L)
 	return 1;
 }
 
+int w_getRecordingDeviceCount(lua_State *L)
+{
+	lua_pushnumber(L, instance()->getRecordingDeviceCount());
+	return 1;
+}
+
+int w_getRecordingDevices(lua_State *L)
+{
+	int count = instance()->getRecordingDeviceCount();
+	lua_createtable(L, count, 0);
+
+	for (int i = 0; i < count; i++)
+	{
+		RecordingDevice *device = instance()->getRecordingDevice(i);
+		luax_pushtype(L, AUDIO_RECORDING_DEVICE_ID, device);
+		lua_rawseti(L, -2, i + 1);
+	}
+
+	return 1;
+}
+
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 {
@@ -339,17 +317,17 @@ static const luaL_Reg functions[] =
 	{ "getVelocity", w_getVelocity },
 	{ "setDopplerScale", w_setDopplerScale },
 	{ "getDopplerScale", w_getDopplerScale },
-	/*{ "record", w_record },
-	{ "getRecordedData", w_getRecordedData },
-	{ "stopRecording", w_stopRecording },*/
 	{ "setDistanceModel", w_setDistanceModel },
 	{ "getDistanceModel", w_getDistanceModel },
+	{ "getRecordingDeviceCount", w_getRecordingDeviceCount },
+	{ "getRecordingDevices", w_getRecordingDevices },
 	{ 0, 0 }
 };
 
 static const lua_CFunction types[] =
 {
 	luaopen_source,
+	luaopen_recordingdevice,
 	0
 };
 

+ 1 - 0
src/modules/audio/wrap_Audio.h

@@ -26,6 +26,7 @@
 #include "common/runtime.h"
 #include "Audio.h"
 #include "wrap_Source.h"
+#include "wrap_RecordingDevice.h"
 
 namespace love
 {

+ 201 - 0
src/modules/audio/wrap_RecordingDevice.cpp

@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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.
+ **/
+
+#include "wrap_RecordingDevice.h"
+#include "wrap_Audio.h"
+
+#include "sound/SoundData.h"
+#include "sound/Sound.h"
+
+#define soundInstance() (Module::getInstance<love::sound::Sound>(Module::M_SOUND))
+
+namespace love
+{
+namespace audio
+{
+
+RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx)
+{
+	return luax_checktype<RecordingDevice>(L, idx, AUDIO_RECORDING_DEVICE_ID);
+}
+
+int w_RecordingDevice_startRecording(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	if (lua_gettop(L) > 1)
+	{
+		int samples = luaL_checkinteger(L, 2);
+		int sampleRate = luaL_checkinteger(L, 3);
+		int bitDepth = luaL_checkinteger(L, 4);
+		int channels = luaL_checkinteger(L, 5);
+		luax_catchexcept(L, [&](){ 
+			lua_pushboolean(L, d->startRecording(samples, sampleRate, bitDepth, channels)); 
+		});
+	}
+	else
+		luax_catchexcept(L, [&](){ 
+			lua_pushboolean(L, d->startRecording()); 
+		});
+	return 1;
+}
+
+int w_RecordingDevice_stopRecording(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	int samples = d->getSampleCount();
+	if (samples == 0)
+	{
+		lua_pushnil(L);
+		return 1;
+	}
+
+	love::sound::SoundData *s = nullptr;
+	if (lua_gettop(L) > 1)
+	{
+		if (luax_istype(L, 2, SOUND_SOUND_DATA_ID))
+			s = luax_totype<love::sound::SoundData>(L, 2, SOUND_SOUND_DATA_ID);
+		else
+			return luaL_typerror(L, 2, "SoundData");
+
+		s->retain();
+	}
+	else
+	{
+		luax_catchexcept(L, [&](){ 
+			s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); 
+		});
+	}
+
+	luax_catchexcept(L, [&](){ 
+		d->getData(s);
+		d->stopRecording();
+	});
+
+	luax_pushtype(L, SOUND_SOUND_DATA_ID, s);
+	s->release();
+	return 1;
+}
+
+int w_RecordingDevice_getData(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	int samples = d->getSampleCount();
+	if (samples == 0)
+	{
+		lua_pushnil(L);
+		return 1;
+	}
+
+	love::sound::SoundData *s = nullptr;
+	if (lua_gettop(L) > 1)
+	{
+		if (luax_istype(L, 2, SOUND_SOUND_DATA_ID))
+			s = luax_totype<love::sound::SoundData>(L, 2, SOUND_SOUND_DATA_ID);
+		else
+			return luaL_typerror(L, 2, "SoundData");
+
+		s->retain();
+	}
+	else
+	{
+		luax_catchexcept(L, [&](){ 
+			s = soundInstance()->newSoundData(samples, d->getSampleRate(), d->getBitDepth(), d->getChannels()); 
+		});
+	}
+
+	luax_catchexcept(L, [&](){ d->getData(s); });
+
+	luax_pushtype(L, SOUND_SOUND_DATA_ID, s);
+	s->release();
+	return 1;
+}
+
+int w_RecordingDevice_getSampleCount(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	lua_pushnumber(L, d->getSampleCount());
+	return 1;
+}
+
+int w_RecordingDevice_getSampleRate(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	lua_pushnumber(L, d->getSampleRate());
+	return 1;
+}
+
+int w_RecordingDevice_getBitDepth(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	lua_pushnumber(L, d->getBitDepth());
+	return 1;
+}
+
+int w_RecordingDevice_getChannels(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	lua_pushnumber(L, d->getChannels());
+	return 1;
+}
+
+int w_RecordingDevice_getName(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	lua_pushstring(L, d->getName());
+	return 1;
+}
+
+int w_RecordingDevice_getID(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	lua_pushnumber(L, d->getID());
+	return 1;
+}
+
+int w_RecordingDevice_isRecording(lua_State *L)
+{
+	RecordingDevice *d = luax_checkrecordingdevice(L, 1);
+	lua_pushboolean(L, d->isRecording());
+	return 1;
+}
+
+static const luaL_Reg w_RecordingDevice_functions[] =
+{
+	{ "startRecording", w_RecordingDevice_startRecording },
+	{ "stopRecording", w_RecordingDevice_stopRecording },
+	{ "getData", w_RecordingDevice_getData },
+	{ "getSampleCount", w_RecordingDevice_getSampleCount },
+	{ "getSampleRate", w_RecordingDevice_getSampleRate },
+	{ "getBitDepth", w_RecordingDevice_getBitDepth },
+	{ "getChannels", w_RecordingDevice_getChannels },
+	{ "getName", w_RecordingDevice_getName },
+	{ "getID", w_RecordingDevice_getID },
+	{ "isRecording", w_RecordingDevice_isRecording },
+	{ 0, 0 }
+};
+
+extern "C" int luaopen_recordingdevice(lua_State *L)
+{
+	int ret = luax_register_type(L, AUDIO_RECORDING_DEVICE_ID, "RecordingDevice", w_RecordingDevice_functions, nullptr);
+	return ret;
+}
+
+} //audio
+} //love

+ 40 - 0
src/modules/audio/wrap_RecordingDevice.h

@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2006-2016 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 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_WRAP_RECORDING_DEVICE_H
+#define LOVE_AUDIO_WRAP_RECORDING_DEVICE_H
+
+// LOVE
+#include "common/runtime.h"
+#include "RecordingDevice.h"
+
+namespace love
+{
+namespace audio
+{
+
+RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx);
+extern "C" int luaopen_recordingdevice(lua_State *L);
+
+} // audio
+} // love
+
+#endif //LOVE_AUDIO_WRAP_RECORDING_DEVICE_H
+

+ 1 - 2
src/modules/sound/SoundData.h

@@ -54,11 +54,10 @@ public:
 
 	void setSample(int i, float sample);
 	float getSample(int i) const;
+	void load(int samples, int sampleRate, int bitDepth, int channels, void *newData = 0);
 
 private:
 
-	void load(int samples, int sampleRate, int bitDepth, int channels, void *newData = 0);
-
 	uint8 *data;
 	size_t size;