From 20d12d7f971723668ab2045f1f4b4cc967097772 Mon Sep 17 00:00:00 2001 From: Dimitri Diakopoulos Date: Mon, 25 Jul 2016 09:58:40 -0700 Subject: [PATCH] simple recording functionality for later encoding tests --- examples/src/Main.cpp | 17 ++++++-- include/libnyquist/AudioDevice.h | 75 ++++++++++++-------------------- include/libnyquist/Common.h | 2 + include/libnyquist/RingBuffer.h | 23 ++++------ src/AudioDevice.cpp | 71 ++++++++++++++++++++++++------ 5 files changed, 109 insertions(+), 79 deletions(-) diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 4da00c2..14b8845 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -83,9 +83,20 @@ int main(int argc, const char **argv) try // 1 + 2 channel musepack //loader.Load(fileData.get(), "test_data/ad_hoc/44_16_stereo.mpc"); - loader.Load(fileData.get(), "test_data/ad_hoc/44_16_mono.mpc"); + //loader.Load(fileData.get(), "test_data/ad_hoc/44_16_mono.mpc"); } + fileData->samples.reserve(44100 * 2); + fileData->channelCount = 1; + fileData->frameSize = 32; + fileData->lengthSeconds = 2.0; + fileData->sampleRate = 44100; + + // Test Recording Capabilities of Audio Device + std::cout << "Starting recording for two seconds..." << std::endl; + std::vector recordingBuffer; + myDevice.Record(44100 * 2, fileData->samples); + // Libnyquist does not (currently) perform sample rate conversion if (fileData->sampleRate != desiredSampleRate) { @@ -106,8 +117,8 @@ int main(int argc, const char **argv) try myDevice.Play(fileData->samples); } - int encoderStatus = WavEncoder::WriteFile({2, PCM_16, DITHER_NONE }, fileData.get(), "encoded.wav"); - std::cout << "Encoder Status: " << encoderStatus << std::endl; + //int encoderStatus = WavEncoder::WriteFile({2, PCM_16, DITHER_NONE }, fileData.get(), "encoded.wav"); + //std::cout << "Encoder Status: " << encoderStatus << std::endl; return EXIT_SUCCESS; } diff --git a/include/libnyquist/AudioDevice.h b/include/libnyquist/AudioDevice.h index c6500e4..8717076 100644 --- a/include/libnyquist/AudioDevice.h +++ b/include/libnyquist/AudioDevice.h @@ -23,70 +23,49 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// This file implements a simple sound file player based on RtAudio for testing / example purposes. + #ifndef AUDIO_DEVICE_H #define AUDIO_DEVICE_H -// This file implements a simple sound file player based on RtAudio for testing / example purposes. - #include "Common.h" #include "RingBuffer.h" - #include "rtaudio/RtAudio.h" -#include -#include - namespace nqr { -const uint32_t FRAME_SIZE = 512; -const int32_t CHANNELS = 2; -const int32_t BUFFER_LENGTH = FRAME_SIZE * CHANNELS; + const uint32_t FRAME_SIZE = 512; + const int32_t CHANNELS = 2; + const int32_t BUFFER_LENGTH = FRAME_SIZE * CHANNELS; -struct AudioDeviceInfo -{ - uint32_t id; - uint32_t numChannels; - uint32_t sampleRate; - uint32_t frameSize; - bool isPlaying = false; -}; + struct AudioDeviceInfo + { + uint32_t id; + uint32_t numChannels; + uint32_t sampleRate; + uint32_t frameSize; + bool isPlaying = false; + }; -class AudioDevice -{ - NO_MOVE(AudioDevice); - std::unique_ptr rtaudio; -public: + class AudioDevice + { + NO_MOVE(AudioDevice); + std::unique_ptr rtaudio; + public: - AudioDeviceInfo info; + AudioDeviceInfo info; - AudioDevice(int numChannels, int sampleRate, int deviceId = -1) - { - rtaudio = std::unique_ptr(new RtAudio); - info.id = (deviceId != -1) ? deviceId : rtaudio->getDefaultOutputDevice(); - info.numChannels = numChannels; - info.sampleRate = sampleRate; - info.frameSize = FRAME_SIZE; - } + AudioDevice(int numChannels, int sampleRate, int deviceId = -1); + ~AudioDevice(); - ~AudioDevice() - { - if (rtaudio) - { - rtaudio->stopStream(); - if (rtaudio->isStreamOpen()) - { - rtaudio->closeStream(); - } - } - } - - bool Open(const int deviceId); - - bool Play(const std::vector & data); + bool Open(const int deviceId); + bool Play(const std::vector & data); + + bool Record(const uint32_t lengthInSamples, std::vector & recordingBuffer); - static void ListAudioDevices(); -}; + static void ListAudioDevices(); + }; } // end namespace nqr diff --git a/include/libnyquist/Common.h b/include/libnyquist/Common.h index 1084272..7c4cfc7 100644 --- a/include/libnyquist/Common.h +++ b/include/libnyquist/Common.h @@ -26,6 +26,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef LIBNYQUIST_COMMON_H #define LIBNYQUIST_COMMON_H +#include #include #include #include @@ -36,6 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include + #include "PostProcess.h" #include "Dither.h" diff --git a/include/libnyquist/RingBuffer.h b/include/libnyquist/RingBuffer.h index bcfa315..01c9496 100644 --- a/include/libnyquist/RingBuffer.h +++ b/include/libnyquist/RingBuffer.h @@ -43,18 +43,12 @@ class RingBufferT public: // Constructs a RingBufferT with size = 0 - RingBufferT() : mData(nullptr), mAllocatedSize(0), mWriteIndex(0), mReadIndex(0) { - - } + RingBufferT() : mData(nullptr), mAllocatedSize(0), mWriteIndex(0), mReadIndex(0) {} // Constructs a RingBufferT with \a count maximum elements. - RingBufferT(size_t count) : mAllocatedSize(0) - { - resize(count); - } + RingBufferT(size_t count) : mAllocatedSize(0) { resize(count); } - RingBufferT(RingBufferT &&other) - : mData(other.mData), mAllocatedSize(other.mAllocatedSize), mWriteIndex(0), mReadIndex(0) + RingBufferT(RingBufferT &&other) : mData(other.mData), mAllocatedSize(other.mAllocatedSize), mWriteIndex(0), mReadIndex(0) { other.mData = nullptr; other.mAllocatedSize = 0; @@ -81,7 +75,7 @@ class RingBufferT clear(); } - // Invalidates the internal buffer and resets read / write indices to 0. \note Must be synchronized with both read and write threads. + // Invalidates the internal buffer and resets read / write indices to 0. Must be synchronized with both read and write threads. void clear() { mWriteIndex = 0; @@ -91,16 +85,16 @@ class RingBufferT // Returns the maximum number of elements. size_t getSize() const { - return mAllocatedSize - 1; } - // Returns the number of elements available for wrtiing. \note Only safe to call from the write thread. + + // Returns the number of elements available for writing. Only safe to call from the write thread. size_t getAvailableWrite() const { return getAvailableWrite(mWriteIndex, mReadIndex); } - // Returns the number of elements available for wrtiing. \note Only safe to call from the read thread. + // Returns the number of elements available for reading. Only safe to call from the read thread. size_t getAvailableRead() const { return getAvailableRead(mWriteIndex, mReadIndex); @@ -188,7 +182,7 @@ class RingBufferT return writeIndex + mAllocatedSize - readIndex; } - T *mData; + T * mData; size_t mAllocatedSize; std::atomic mWriteIndex, mReadIndex; }; @@ -196,4 +190,3 @@ class RingBufferT typedef RingBufferT RingBuffer; #endif - diff --git a/src/AudioDevice.cpp b/src/AudioDevice.cpp index 9632908..11e80d5 100644 --- a/src/AudioDevice.cpp +++ b/src/AudioDevice.cpp @@ -32,23 +32,43 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace nqr; static RingBufferT buffer(BUFFER_LENGTH); +static RingBufferT record_buffer(BUFFER_LENGTH / 2); static int rt_callback(void * output_buffer, void * input_buffer, uint32_t num_bufferframes, double stream_time, RtAudioStreamStatus status, void * user_data) { - if (status) std::cerr << "[rtaudio] Buffer over or underflow" << std::endl; + if (status) std::cerr << "[rtaudio] buffer over or underflow" << std::endl; - if (buffer.getAvailableRead()) - { - buffer.read((float*) output_buffer, BUFFER_LENGTH); - } - else - { - memset(output_buffer, 0, BUFFER_LENGTH * sizeof(float)); - } + // Playback + if (buffer.getAvailableRead()) buffer.read((float*) output_buffer, BUFFER_LENGTH); + else memset(output_buffer, 0, BUFFER_LENGTH * sizeof(float)); + + // Recording + if (record_buffer.getAvailableWrite()) record_buffer.write((float*) input_buffer, BUFFER_LENGTH / 2); return 0; } +AudioDevice::AudioDevice(int numChannels, int sampleRate, int deviceId) +{ + rtaudio = std::unique_ptr(new RtAudio); + info.id = (deviceId != -1) ? deviceId : rtaudio->getDefaultOutputDevice(); + info.numChannels = numChannels; + info.sampleRate = sampleRate; + info.frameSize = FRAME_SIZE; +} + +AudioDevice::~AudioDevice() +{ + if (rtaudio) + { + rtaudio->stopStream(); + if (rtaudio->isStreamOpen()) + { + rtaudio->closeStream(); + } + } +} + bool AudioDevice::Open(const int deviceId) { if (!rtaudio) throw std::runtime_error("rtaudio not created yet"); @@ -59,10 +79,12 @@ bool AudioDevice::Open(const int deviceId) outputParams.firstChannel = 0; RtAudio::StreamParameters inputParams; - inputParams.deviceId = info.id; - inputParams.nChannels = info.numChannels; + inputParams.deviceId = rtaudio->getDefaultInputDevice(); + inputParams.nChannels = 1; inputParams.firstChannel = 0; + std::cout << "Input Device Id: " << inputParams.deviceId << std::endl; + rtaudio->openStream(&outputParams, &inputParams, RTAUDIO_FLOAT32, info.sampleRate, &info.frameSize, &rt_callback, (void*) & buffer); if (rtaudio->isStreamOpen()) @@ -99,11 +121,34 @@ bool AudioDevice::Play(const std::vector & data) int writeCount = 0; - while(writeCount < sizeInFrames) + while (writeCount < sizeInFrames) { bool status = buffer.write((data.data() + (writeCount * BUFFER_LENGTH)), BUFFER_LENGTH); if (status) writeCount++; } return true; -} \ No newline at end of file +} + +bool AudioDevice::Record(const uint32_t lengthInSamples, std::vector & recordingBuffer) +{ + uint32_t recordedSamples = 0; + + // Allocate memory upfront (revisit this later...) + recordingBuffer.resize(lengthInSamples + (BUFFER_LENGTH)); // + a little padding + + while (recordedSamples < lengthInSamples) + { + if (record_buffer.getAvailableRead()) + { + if(record_buffer.read(recordingBuffer.data() + recordedSamples, BUFFER_LENGTH / 2)) + { + recordedSamples += (BUFFER_LENGTH / 2); + } + + std::cout << recordedSamples << std::endl; + } + } + + return true; +}