diff --git a/CMakeLists.txt b/CMakeLists.txt index a74fd7056..27fc045bf 100755 --- a/CMakeLists.txt +++ b/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 diff --git a/src/common/types.cpp b/src/common/types.cpp index 4bb44ecce..2385315c9 100644 --- a/src/common/types.cpp +++ b/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]; diff --git a/src/common/types.h b/src/common/types.h index a18191947..7b7d1e1ee 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -73,6 +73,7 @@ enum Type // Audio AUDIO_SOURCE_ID, + AUDIO_RECORDING_DEVICE_ID, // Sound SOUND_SOUND_DATA_ID, diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index 0a114a8ad..9a0d43aba 100644 --- a/src/modules/audio/Audio.h +++ b/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. diff --git a/src/modules/audio/RecordingDevice.cpp b/src/modules/audio/RecordingDevice.cpp new file mode 100644 index 000000000..f03470218 --- /dev/null +++ b/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 diff --git a/src/modules/audio/RecordingDevice.h b/src/modules/audio/RecordingDevice.h new file mode 100644 index 000000000..ac2d9150c --- /dev/null +++ b/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 + +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 diff --git a/src/modules/audio/null/Audio.cpp b/src/modules/audio/null/Audio.cpp index b2a0ffe4a..85d48c629 100644 --- a/src/modules/audio/null/Audio.cpp +++ b/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() +love::audio::RecordingDevice *Audio::getRecordingDevice(int index) const { - return NULL; + return nullptr; } -love::sound::SoundData *Audio::stopRecording(bool) +int Audio::getRecordingDeviceIndex(love::audio::RecordingDevice *device) const { - return NULL; -} - -bool Audio::canRecord() -{ - return false; + return -1; } Audio::DistanceModel Audio::getDistanceModel() const diff --git a/src/modules/audio/null/Audio.h b/src/modules/audio/null/Audio.h index e4143d447..a2c82e031 100644 --- a/src/modules/audio/null/Audio.h +++ b/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); diff --git a/src/modules/audio/null/RecordingDevice.cpp b/src/modules/audio/null/RecordingDevice.cpp new file mode 100644 index 000000000..9e62b4168 --- /dev/null +++ b/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 diff --git a/src/modules/audio/null/RecordingDevice.h b/src/modules/audio/null/RecordingDevice.h new file mode 100644 index 000000000..8fcb1bdad --- /dev/null +++ b/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 diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index c3b1bd05e..cb9b45f09 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/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 +#include 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 diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index b676abf8c..846e0e54c 100644 --- a/src/modules/audio/openal/Audio.h +++ b/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 capture; // The OpenAL context. ALCcontext *context; diff --git a/src/modules/audio/openal/RecordingDevice.cpp b/src/modules/audio/openal/RecordingDevice.cpp new file mode 100644 index 000000000..afe06ef69 --- /dev/null +++ b/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 diff --git a/src/modules/audio/openal/RecordingDevice.h b/src/modules/audio/openal/RecordingDevice.h new file mode 100644 index 000000000..3e4ae14ca --- /dev/null +++ b/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 +#include +#else +#include +#include +#endif +#else +#include +#include +#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 diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 47169c583..8c981489d 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/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; diff --git a/src/modules/audio/openal/Source.h b/src/modules/audio/openal/Source.h index 9bddfb73d..680cf5ddb 100644 --- a/src/modules/audio/openal/Source.h +++ b/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(); diff --git a/src/modules/audio/wrap_Audio.cpp b/src/modules/audio/wrap_Audio.cpp index 7a178abc8..90ee82a82 100644 --- a/src/modules/audio/wrap_Audio.cpp +++ b/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 }; diff --git a/src/modules/audio/wrap_Audio.h b/src/modules/audio/wrap_Audio.h index 0f358517d..3990f29d6 100644 --- a/src/modules/audio/wrap_Audio.h +++ b/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 { diff --git a/src/modules/audio/wrap_RecordingDevice.cpp b/src/modules/audio/wrap_RecordingDevice.cpp new file mode 100644 index 000000000..fbb5c3016 --- /dev/null +++ b/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(Module::M_SOUND)) + +namespace love +{ +namespace audio +{ + +RecordingDevice *luax_checkrecordingdevice(lua_State *L, int idx) +{ + return luax_checktype(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(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(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 diff --git a/src/modules/audio/wrap_RecordingDevice.h b/src/modules/audio/wrap_RecordingDevice.h new file mode 100644 index 000000000..d9dc9c2e4 --- /dev/null +++ b/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 + diff --git a/src/modules/sound/SoundData.h b/src/modules/sound/SoundData.h index 07b13c2d3..821fb291e 100644 --- a/src/modules/sound/SoundData.h +++ b/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;