simple recording functionality for later encoding tests

pull/18/head
Dimitri Diakopoulos 10 years ago
parent 26713c3fd8
commit 20d12d7f97

@ -83,9 +83,20 @@ int main(int argc, const char **argv) try
// 1 + 2 channel musepack // 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_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<float> recordingBuffer;
myDevice.Record(44100 * 2, fileData->samples);
// Libnyquist does not (currently) perform sample rate conversion // Libnyquist does not (currently) perform sample rate conversion
if (fileData->sampleRate != desiredSampleRate) if (fileData->sampleRate != desiredSampleRate)
{ {
@ -106,8 +117,8 @@ int main(int argc, const char **argv) try
myDevice.Play(fileData->samples); myDevice.Play(fileData->samples);
} }
int encoderStatus = WavEncoder::WriteFile({2, PCM_16, DITHER_NONE }, fileData.get(), "encoded.wav"); //int encoderStatus = WavEncoder::WriteFile({2, PCM_16, DITHER_NONE }, fileData.get(), "encoded.wav");
std::cout << "Encoder Status: " << encoderStatus << std::endl; //std::cout << "Encoder Status: " << encoderStatus << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

@ -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. 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 #ifndef AUDIO_DEVICE_H
#define 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 "Common.h"
#include "RingBuffer.h" #include "RingBuffer.h"
#include "rtaudio/RtAudio.h" #include "rtaudio/RtAudio.h"
#include <iostream>
#include <memory>
namespace nqr namespace nqr
{ {
const uint32_t FRAME_SIZE = 512; const uint32_t FRAME_SIZE = 512;
const int32_t CHANNELS = 2; const int32_t CHANNELS = 2;
const int32_t BUFFER_LENGTH = FRAME_SIZE * CHANNELS; const int32_t BUFFER_LENGTH = FRAME_SIZE * CHANNELS;
struct AudioDeviceInfo struct AudioDeviceInfo
{ {
uint32_t id; uint32_t id;
uint32_t numChannels; uint32_t numChannels;
uint32_t sampleRate; uint32_t sampleRate;
uint32_t frameSize; uint32_t frameSize;
bool isPlaying = false; bool isPlaying = false;
}; };
class AudioDevice class AudioDevice
{ {
NO_MOVE(AudioDevice); NO_MOVE(AudioDevice);
std::unique_ptr<RtAudio> rtaudio; std::unique_ptr<RtAudio> rtaudio;
public: public:
AudioDeviceInfo info; AudioDeviceInfo info;
AudioDevice(int numChannels, int sampleRate, int deviceId = -1) AudioDevice(int numChannels, int sampleRate, int deviceId = -1);
{ ~AudioDevice();
rtaudio = std::unique_ptr<RtAudio>(new RtAudio);
info.id = (deviceId != -1) ? deviceId : rtaudio->getDefaultOutputDevice();
info.numChannels = numChannels;
info.sampleRate = sampleRate;
info.frameSize = FRAME_SIZE;
}
~AudioDevice() bool Open(const int deviceId);
{ bool Play(const std::vector<float> & data);
if (rtaudio)
{ bool Record(const uint32_t lengthInSamples, std::vector<float> & recordingBuffer);
rtaudio->stopStream();
if (rtaudio->isStreamOpen())
{
rtaudio->closeStream();
}
}
}
bool Open(const int deviceId);
bool Play(const std::vector<float> & data);
static void ListAudioDevices(); static void ListAudioDevices();
}; };
} // end namespace nqr } // end namespace nqr

@ -26,6 +26,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef LIBNYQUIST_COMMON_H #ifndef LIBNYQUIST_COMMON_H
#define LIBNYQUIST_COMMON_H #define LIBNYQUIST_COMMON_H
#include <memory>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -36,6 +37,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <type_traits> #include <type_traits>
#include <numeric> #include <numeric>
#include <array> #include <array>
#include "PostProcess.h" #include "PostProcess.h"
#include "Dither.h" #include "Dither.h"

@ -43,18 +43,12 @@ class RingBufferT
public: public:
// Constructs a RingBufferT with size = 0 // 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. // Constructs a RingBufferT with \a count maximum elements.
RingBufferT(size_t count) : mAllocatedSize(0) RingBufferT(size_t count) : mAllocatedSize(0) { resize(count); }
{
resize(count);
}
RingBufferT(RingBufferT &&other) RingBufferT(RingBufferT &&other) : mData(other.mData), mAllocatedSize(other.mAllocatedSize), mWriteIndex(0), mReadIndex(0)
: mData(other.mData), mAllocatedSize(other.mAllocatedSize), mWriteIndex(0), mReadIndex(0)
{ {
other.mData = nullptr; other.mData = nullptr;
other.mAllocatedSize = 0; other.mAllocatedSize = 0;
@ -81,7 +75,7 @@ class RingBufferT
clear(); 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() void clear()
{ {
mWriteIndex = 0; mWriteIndex = 0;
@ -91,16 +85,16 @@ class RingBufferT
// Returns the maximum number of elements. // Returns the maximum number of elements.
size_t getSize() const size_t getSize() const
{ {
return mAllocatedSize - 1; 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 size_t getAvailableWrite() const
{ {
return getAvailableWrite(mWriteIndex, mReadIndex); 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 size_t getAvailableRead() const
{ {
return getAvailableRead(mWriteIndex, mReadIndex); return getAvailableRead(mWriteIndex, mReadIndex);
@ -188,7 +182,7 @@ class RingBufferT
return writeIndex + mAllocatedSize - readIndex; return writeIndex + mAllocatedSize - readIndex;
} }
T *mData; T * mData;
size_t mAllocatedSize; size_t mAllocatedSize;
std::atomic<size_t> mWriteIndex, mReadIndex; std::atomic<size_t> mWriteIndex, mReadIndex;
}; };
@ -196,4 +190,3 @@ class RingBufferT
typedef RingBufferT<float> RingBuffer; typedef RingBufferT<float> RingBuffer;
#endif #endif

@ -32,23 +32,43 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace nqr; using namespace nqr;
static RingBufferT<float> buffer(BUFFER_LENGTH); static RingBufferT<float> buffer(BUFFER_LENGTH);
static RingBufferT<float> 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) 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()) // Playback
{ if (buffer.getAvailableRead()) buffer.read((float*) output_buffer, BUFFER_LENGTH);
buffer.read((float*) output_buffer, BUFFER_LENGTH); else memset(output_buffer, 0, BUFFER_LENGTH * sizeof(float));
}
else // Recording
{ if (record_buffer.getAvailableWrite()) record_buffer.write((float*) input_buffer, BUFFER_LENGTH / 2);
memset(output_buffer, 0, BUFFER_LENGTH * sizeof(float));
}
return 0; return 0;
} }
AudioDevice::AudioDevice(int numChannels, int sampleRate, int deviceId)
{
rtaudio = std::unique_ptr<RtAudio>(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) bool AudioDevice::Open(const int deviceId)
{ {
if (!rtaudio) throw std::runtime_error("rtaudio not created yet"); if (!rtaudio) throw std::runtime_error("rtaudio not created yet");
@ -59,10 +79,12 @@ bool AudioDevice::Open(const int deviceId)
outputParams.firstChannel = 0; outputParams.firstChannel = 0;
RtAudio::StreamParameters inputParams; RtAudio::StreamParameters inputParams;
inputParams.deviceId = info.id; inputParams.deviceId = rtaudio->getDefaultInputDevice();
inputParams.nChannels = info.numChannels; inputParams.nChannels = 1;
inputParams.firstChannel = 0; 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); rtaudio->openStream(&outputParams, &inputParams, RTAUDIO_FLOAT32, info.sampleRate, &info.frameSize, &rt_callback, (void*) & buffer);
if (rtaudio->isStreamOpen()) if (rtaudio->isStreamOpen())
@ -99,11 +121,34 @@ bool AudioDevice::Play(const std::vector<float> & data)
int writeCount = 0; int writeCount = 0;
while(writeCount < sizeInFrames) while (writeCount < sizeInFrames)
{ {
bool status = buffer.write((data.data() + (writeCount * BUFFER_LENGTH)), BUFFER_LENGTH); bool status = buffer.write((data.data() + (writeCount * BUFFER_LENGTH)), BUFFER_LENGTH);
if (status) writeCount++; if (status) writeCount++;
} }
return true; return true;
} }
bool AudioDevice::Record(const uint32_t lengthInSamples, std::vector<float> & 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;
}

Loading…
Cancel
Save