PortAudio: Device unavailable - c++

What do I do wrong?
I have been trying to run this code but every time I run it, it says:
Error: Device unavailable
My Linux: Ubuntu 17.04.
Any help?
#include <portaudio.h>
#include <iostream>
#define SAMPLE_RATE (48000)
static float *data;
PaStream *stream;
int callback(const void *pVoid, void *pVoid1, unsigned long i, const PaStreamCallbackTimeInfo *pInfo,
PaStreamCallbackFlags i1, void *pVoid2);
int main() {
PaError err;
/* Open an audio I/O stream. */
err = Pa_OpenDefaultStream(&stream,
0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
SAMPLE_RATE,
paFramesPerBufferUnspecified, /* frames per buffer */
callback, /* this is your callback function */
&data); /*This is a pointer that will be passed to
your callback*/
if (err != paNoError) {
std::cout << "Error: " << Pa_GetErrorText(err) << std::endl;
auto a = Pa_GetLastHostErrorInfo();
if (a->errorCode != 0) {
std::cout << "Host error: " << a->errorText << std::endl;
}
return 1;
}
return 0;
}
int callback(const void *pVoid, void *pVoid1, unsigned long i, const PaStreamCallbackTimeInfo *pInfo,
PaStreamCallbackFlags i1, void *pVoid2) {
// Do something with the stream...
return 0;
}
I do not run any programs that use audio in any way.

You should properly init before openening a stream, and start by debugging from there..
Here is an example you can work with.

Related

Real time audio processing with Portaudio C++

I'm trying to make a C++ application to transmit audio via a VoIP protocol between 2 clients (using UDP).
I'm working with Portaudio C library and I have issues to encapsulate this lib. In order to send the recorded audio to another client, I'd like to get sound samples as it is recorded (real time).
For the moment I can only record sound, and then, play what I recorded.
I'm not comfortable at all with this library and any help would be appreciated.
Here's what I've done so far.
Audio.cpp -> Callback methods:
static int PaRecordCallback(const void *input, void *output, unsigned long frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
Audio *audio = reinterpret_cast<Audio *>(userData);
return audio->RecordCallback(input, output, frameCount, timeInfo, statusFlags);
}
static int PaPlayCallback(const void *input, void *output, unsigned long frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
Audio *audio = reinterpret_cast<Audio *>(userData);
return audio->PlayCallback(input, output, frameCount, timeInfo, statusFlags);
}
int Audio::RecordCallback(const void *input, void *output, unsigned long &frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags &statusFlags)
{
std::cout << "Frame index:\t\t" << _recordedFrameIndex << std::endl << "Max frame index:\t" << _maxFrameIndex << std::endl << "--------------" << std::endl;
const SAMPLE *rptr = static_cast<const SAMPLE *>(input);
SAMPLE *wptr = &_recordedSamples[_recordedFrameIndex * NUM_CHANNELS];
unsigned long framesLeft = _maxFrameIndex - _recordedFrameIndex;
unsigned long framesToCalc;
int finished;
if (framesLeft < frameCount) {
framesToCalc = framesLeft;
finished = paComplete;
} else {
framesToCalc = frameCount;
finished = paContinue;
}
for (unsigned long i = 0; i < framesToCalc; i++) {
*wptr++ = *rptr++;
if (NUM_CHANNELS == 2)
*wptr++ = *rptr++;
}
_recordedFrameIndex += framesToCalc;
return finished;
}
int Audio::PlayCallback(const void *input, void *output, unsigned long &frameCount, \
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags &statusFlags)
{
SAMPLE *rptr = &_recordedSamples[_playedFrameIndex * NUM_CHANNELS];
SAMPLE *wptr = static_cast<SAMPLE *>(output);
unsigned long framesLeft = _maxFrameIndex - _playedFrameIndex;
unsigned int i;
int finished;
if (framesLeft < frameCount) {
for (i = 0; i < framesLeft; i++) {
*wptr++ = *rptr++;
if (NUM_CHANNELS == 2)
*wptr++ = *rptr++;
}
for (; i < frameCount; i++) {
*wptr++ = 0;
if (NUM_CHANNELS == 2)
*wptr++ = 0;
}
_playedFrameIndex += framesLeft;
finished = paComplete;
} else {
for (i = 0; i < frameCount; i++) {
*wptr++ = *rptr++;
if (NUM_CHANNELS == 2)
*wptr++ = *rptr++;
}
_playedFrameIndex += frameCount;
finished = paContinue;
}
return finished;
}
Audio.cpp -> Record and Play methods:
void Audio::Record()
{
if (!_recordStream) {
OpenRecordStream();
_recordedFrameIndex = 0;
_err = Pa_StartStream(_recordStream);
if (_err != paNoError)
AudioError("Audio::Record -> Pa_StartStream()");
std::cout << "Audio record stream started." << std::endl;
std::cout << "Recording ..." << std::endl;
_recording = true;
fflush(stdout);
} else if (_recording)
Pa_Sleep(1000);
}
void Audio::Play()
{
if (!_playStream) {
OpenPlayStream();
_playedFrameIndex = 0;
_err = Pa_StartStream(_playStream);
if (_err != paNoError)
AudioError("Audio::Play -> Pa_StartStream()");
std::cout << "Audio play stream started." << std::endl;
std::cout << "Playing ..." << std::endl;
_playing = true;
fflush(stdout);
} else if (_playing)
Pa_Sleep(500);
}
Audio.hpp -> Class audio:
#include <portaudio.h>
typedef int16_t SAMPLE;
#define PA_SAMPLE_TYPE paInt16
#define PRINTF_S_FORMAT "%.8f"
#define SAMPLE_RATE 44100
#define SAMPLE_SILENCE 0.0f
#define FRAMES_PER_BUFFER 1
#define NUM_SECONDS 5
#define NUM_CHANNELS 2
#define DITHER_FLAG 0
#define WRITE_TO_FILE 0
#define SAMPLE_SIZE NUM_SECONDS * SAMPLE_RATE * NUM_CHANNELS
class Audio
{
public:
Audio();
~Audio();
void Record();
void Play();
void OpenRecordStream();
void OpenPlayStream();
void CloseRecordStream();
void ClosePlayStream();
const bool &isRecording() const;
const bool &isPlaying() const;
const PaStream *GetRecordStream() const;
const PaStream *GetPlayStream() const;
void GetSamples(SAMPLE *);
void SetSamples(SAMPLE *);
int RecordCallback(const void *, void *, unsigned long &, \
const PaStreamCallbackTimeInfo *, PaStreamCallbackFlags &);
int PlayCallback(const void *, void *, unsigned long &, \
const PaStreamCallbackTimeInfo *, PaStreamCallbackFlags &);
bool _recording;
bool _playing;
protected:
private:
// Functions:
void AudioError(const std::string &);
// Variables:
PaError _err;
PaStream *_playStream;
PaStream *_recordStream;
SAMPLE *_samplesToPlay;
SAMPLE *_recordedSamples;
unsigned long _recordedFrameIndex;
unsigned long _playedFrameIndex;
unsigned long _maxFrameIndex;
PaStreamParameters _inputParameters;
PaStreamParameters _outputParameters;
};
I apologize if it's very long, but I want you to have all the necessary informations to understand my problem.
I don't ask questions often, so I really need some help here.
Thank you.
You need to use a full duplex callback to record and play in real time so you catch the input buffer in chunks for your recordings (when you want to record) and send the recordings (or even the incoming sound) to the output buffer, also in chunks.
The chunksize normally would be from 64 to 4096 frames, and each frame normally would contain 2 samples (channel L and channel R)
The full duplex callback is a kind of circular buffer that brings you x frames when the input buffer is filled by the ADC and where you fill the output buffer with x frames for being ready for when the DAC asks for it.

Serial communication c++

I'm trying do some serial communication between my pc and an arduino ATmega2560
First the microntroller's program :
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.write('A');
}
The arduino program is very basic, his aim is to check the next program which is on the pc.
The main.cpp :
#include <iostream>
#include "SerialPort.h"
using namespace std;
int main()
{
SerialPort port("com3", 9600);
while (1)
{
//Receive
unsigned char dataR;
port.receive(dataR, 1);
cout << dataR << endl;
}
return 0;
}
The SerialPort.h:
#include <windows.h>
#include <iostream>
class SerialPort
{
public:
//Constructors
SerialPort();
SerialPort(const char* port, unsigned long BaudRate);
//Initialization
void Initialize(const char* port, unsigned long BaudRate);
//Serial I/O
void receive(unsigned char &data, unsigned int byteSize);
void transmit(unsigned char *data, unsigned int byteSize);
//State
void connect();
void disconnect();
bool isConnected();
//Destructor
~SerialPort();
private:
HANDLE handler;
bool isConnect;
};
And the SerialPort.cpp :
#include "SerialPort.h"
/*Constructors*/
SerialPort::SerialPort()
: isConnect(false) {}
SerialPort::SerialPort(const char* port, unsigned long BaudRate)
: isConnect(false)
{
Initialize(port, BaudRate);
}
/*Initialization*/
void SerialPort::Initialize(const char* port, unsigned long BaudRate)
{
handler = CreateFile(port, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handler == INVALID_HANDLE_VALUE)
{
std::cout << "ERROR!::Error during opening port" << port << std::endl;
return;
}
DCB serialParameters;
if (!GetCommState(handler, &serialParameters)) /*Get com parameters*/
{
std::cout << "ERROR!::failed to get current serial parameters" << std::endl;
return;
}
serialParameters.DCBlength = sizeof(DCB);
serialParameters.BaudRate = BaudRate;
serialParameters.ByteSize = 1; /*8 bit data format*/
serialParameters.StopBits = TWOSTOPBITS;
serialParameters.Parity = PARITY_NONE;
if (!SetCommState(handler, &serialParameters)) /*Send modified com parameters*/
{
std::cout << "ALERT!:Failed to set THE Serial port parameters" << std::endl;
return;
}
isConnect = true;
PurgeComm(handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
}
/*Serial I/O*/
void SerialPort::receive(unsigned char &data, unsigned int byteSize)
{
ReadFile(handler, &data, byteSize, NULL, NULL);
}
void SerialPort::transmit(unsigned char *data, unsigned int byteSize)
{
WriteFile(handler, data, byteSize, NULL, NULL);
}
/*State*/
void SerialPort::connect()
{
isConnect = true;
}
void SerialPort::disconnect()
{
isConnect = false;
}
bool SerialPort::isConnected()
{
return isConnect;
}
/*Destructors*/
SerialPort::~SerialPort()
{
if (isConnect)
{
isConnect = false;
CloseHandle(handler);
}
}
I've an issue with this program : I don't receive the right data. Where I should get on the terminal
A
A
A
...
I get weird characters made of ? in a square
I hope you understood my problem
Thanks
The DCB ByteSize parameter is in bits. You have specified a UART frame with one data bit - which is not supported by the hardware at either end.
For a conventional N,8,1 data frame, use
serialParameters.ByteSize = 8 ;
serialParameters.StopBits = ONESTOPBIT ;
serialParameters.Parity = NOPARITY ;
ByteSize is perhaps a misleading name. It defines the number of bits between the start and stop bit in an UART frame. Most commonly this is 8, but for pure ASCII data transfer 7 might be used - historically at least.
The Atmel AVR UART supports frames with 5 to 9 data bits. The PC's UART may be virtual, but will typically be compatible with the 16550 UART, which supported 5 to 8 bit data frames, however these days you are more likely to be using USB-Serial adapter, and the UART on the USB/Serial bridge may not support all 16550 modes - the common FTDI232R for example only supports 7 or 8 bit frames, while Prolific PL2303 supportts 5 to 8. It probably pays to avoid unconventional frames and stick to N,8,1 if you want to be sure it will work on a range of hardware.
From the Arduino documentation on Serial.begin() (emphasis added):
An optional second argument configures the data, parity, and stop
bits. The default is 8 data bits, no parity, one stop bit.
I see this in your code (with no second parameter):
Serial.begin(9600);
and this
serialParameters.StopBits = TWOSTOPBITS;
I think that may be your problem.

How to encode and decode audio data with opus?

I'm working on a voice chat and I need to compress my audio data. I record and play the audio data via the Qt Framework. If I record and play the audio data without compressing it everything is fine. If I compress,decompress and play the audio data I just hear a cracking sound.
Edit: I had a look at the demo code and I tried to use that code.
I can hear something but it is very laggy. If I increase the size of pcm_bytes to e.g 40000 it sounds better but my voice has still lags and cracking sounds.
This is the line (audioinput.cpp at the bottom):
speaker->write((const char*)pcm_bytes,3840);
codecopus.cpp:
#include "codecopus.h"
CodecOpus::CodecOpus()
{
}
void CodecOpus::initDecoder(opus_int32 samplingRate, int channels) //decoder
{
int error;
decoderState = opus_decoder_create(samplingRate,channels,&error);
if(error == OPUS_OK){
std::cout << "Created Opus Decoder struct" << std::endl;
}
}
void CodecOpus::initEncoder(opus_int32 samplingRate, int channels) // Encoder
{
int error;
encoderState = opus_encoder_create(samplingRate,channels,OPUS_APPLICATION_VOIP,&error);
error = opus_encoder_ctl(encoderState,OPUS_SET_BITRATE(64000));
if(error == OPUS_OK){
std::cout << "Created Opus Encoder struct" << std::endl;
}
}
opus_int32 CodecOpus::encodeData(const opus_int16 *pcm, int frameSize, unsigned char *data, opus_int32 maxDataBytes) //Encoder
{
opus_int32 i = opus_encode(encoderState,pcm,frameSize,data,maxDataBytes);
return i;
}
int CodecOpus::decodeData(const unsigned char *data, opus_int32 numberOfBytes,opus_int16* pcm,int frameSizeInSec) //Decoder
{
int i = opus_decode(decoderState,data,numberOfBytes,pcm,frameSizeInSec,0);
return i;
}
CodecOpus::~CodecOpus()
{
opus_decoder_destroy(this->decoderState);
opus_encoder_destroy(this->encoderState);
}
audioinput.h:
#ifndef AUDIOINPUT_H
#define AUDIOINPUT_H
#include <QAudioFormat>
#include <iostream>
#include <QAudioInput>
#include <QAudioOutput>
#include <thread>
#include "codecopus.h"
#include "QDebug"
class AudioInput : public QObject
{
Q_OBJECT
public:
AudioInput();
~AudioInput();
void startRecording();
void CreateNewAudioThread();
private:
CodecOpus opus;
unsigned char cbits[4000] = {};
opus_int16 in[960*2*sizeof(opus_int16)] = {};
opus_int16 out[5760*2] = {};
unsigned char *pcm_bytes;
int MAX_FRAME_SIZE;
QAudioFormat audioFormat;
QAudioInput *audioInput;
QIODevice *mic;
QByteArray data;
int micFrameSize;
QAudioOutput *audioOutput;
QIODevice *speaker;
QAudioFormat speakerAudioFormat;
public slots:
void OnAudioNotfiy();
};
#endif // AUDIOINPUT_H
audioinput.cpp:
#include "audioinput.h"
AudioInput::AudioInput() : audioFormat(),pcm_bytes(new unsigned char[40000])
{
audioFormat.setSampleRate(48000);
audioFormat.setChannelCount(2);
audioFormat.setSampleSize(16);
audioFormat.setSampleType(QAudioFormat::SignedInt);
audioFormat.setByteOrder(QAudioFormat::LittleEndian);
audioFormat.setCodec("audio/pcm");
speakerAudioFormat.setSampleRate(48000);
speakerAudioFormat.setChannelCount(2);
speakerAudioFormat.setSampleSize(16);
speakerAudioFormat.setSampleType(QAudioFormat::SignedInt);
speakerAudioFormat.setByteOrder(QAudioFormat::LittleEndian);
speakerAudioFormat.setCodec("audio/pcm");
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if(!info.isFormatSupported(audioFormat)){
std::cout << "Mic Format not supported!" << std::endl;
audioFormat = info.nearestFormat(audioFormat);
}
QAudioDeviceInfo speakerInfo = QAudioDeviceInfo::defaultOutputDevice();
if(!speakerInfo.isFormatSupported(speakerAudioFormat)){
std::cout << "Speaker Format is not supported!" << std::endl;
speakerAudioFormat = info.nearestFormat(speakerAudioFormat);
}
std::cout << speakerAudioFormat.sampleRate() << audioFormat.sampleRate() << speakerAudioFormat.channelCount() << audioFormat.channelCount() << std::endl;
audioInput = new QAudioInput(audioFormat);
audioOutput = new QAudioOutput(speakerAudioFormat);
audioInput->setNotifyInterval(20);
micFrameSize = (audioFormat.sampleRate()/1000)*20;
opus.initEncoder(audioFormat.sampleRate(),audioFormat.channelCount());
opus.initDecoder(speakerAudioFormat.sampleRate(),speakerAudioFormat.channelCount());
MAX_FRAME_SIZE = 6*960;
connect(audioInput,SIGNAL(notify()),this,SLOT(OnAudioNotfiy()));
}
AudioInput::~AudioInput()
{
}
void AudioInput::startRecording()
{
mic = audioInput->start();
speaker = audioOutput->start();
std::cout << "Recording started!" << std::endl;
}
void AudioInput::CreateNewAudioThread()
{
std::thread t1(&AudioInput::startRecording,this);
t1.detach();
}
void AudioInput::OnAudioNotfiy()
{
data = mic->readAll();
std::cout << "data size" <<data.size() << std::endl;
if(data.size() > 0){
pcm_bytes = reinterpret_cast<unsigned char*>(data.data());
//convert
for(int i=0;i<2*960;i++){ //TODO HARDCODED
in[i]=pcm_bytes[2*i+1]<<8|pcm_bytes[2*i];
}
opus_int32 compressedBytes = opus.encodeData(in,960,cbits,4000);
opus_int32 decompressedBytes = opus.decodeData(cbits,compressedBytes,out,MAX_FRAME_SIZE);
for(int i = 0; i<2*decompressedBytes;i++) //TODO HARDCODED
{
pcm_bytes[2*i]=out[i]&0xFF;
pcm_bytes[2*i+1]=(out[i]>>8)&0xFF;
}
speaker->write((const char*)pcm_bytes,3840);
}
}
1)You encode only 960 bytes, while the buffer is much larger. You must split the buffer into several equal parts and pass them to the encoder. The size of the part is 120, 240, 480, 960, 1920, and 2880.
2)Use qFromLittleEndian()/qToLittleEndian() functions or type casting when converting from char array to opus_int16 array/from opus_int16 array to char array. This will prevent cracking and poor sound quality.
Example:
void voice::slot_read_audio_input()
{
// Audio settings:
// Sample Rate=48000
// Sample Size=16
// Channel Count=1
// Byte Order=Little Endian
// Sample Type= UnSignedInt
// Encoder settings:
// Sample Rate=48000
// Channel Count=1
// OPUS_APPLICATION_VOIP
// Decoder settings:
// Sample Rate=48000
// Channel Count=1
QByteArray audio_buffer;//mic
QByteArray output_audio_buffer;//speaker
int const OPUS_INT_SIZE=2;//sizeof(opus_int16)
int const FRAME_SIZE=960;
int const MAX_FRAME_SIZE=1276;
int FRAME_COUNT=3840/FRAME_SIZE/OPUS_INT_SIZE;// 3840 is a sample size= voice_input->bytesReady;
opus_int16 input_frame[FRAME_SIZE] = {};
opus_int16 output_frame[FRAME_SIZE] = {};
unsigned char compressed_frame[MAX_FRAME_SIZE] = {};
unsigned char decompressed_frame[FRAME_SIZE*OPUS_INT_SIZE] = {};
audio_buffer.resize(voice_input->bytesReady());
output_audio_buffer.resize(FRAME_SIZE*OPUS_INT_SIZE);
input->read(audio_buffer.data(),audio_buffer.size());
for(int i=0;i<FRAME_COUNT;i++)
{
// convert from LittleEndian
for(int j=0;j<FRAME_SIZE;j++)
{
input_frame[j]=qFromLittleEndian<opus_int16>(audio_buffer.data()+j*OPUS_INT_SIZE);
// or use this:
// input_frame[j]=static_cast<short>(static_cast<unsigned char>(audio_buffer.at(OPUS_INT_SIZE*j+1))<<8|static_cast<unsigned char>(audio_buffer.at(OPUS_INT_SIZE*j)));
}
opus_int32 compressedBytes = opus_encode(enc, input_frame,FRAME_SIZE,compressed_frame,MAX_FRAME_SIZE);
opus_int32 decompressedBytes = opus_decode(dec,compressed_frame,compressedBytes,output_frame,FRAME_SIZE,0);
// conver to LittleEndian
for(int j = 0; j<decompressedBytes;j++)
{
qToLittleEndian(output_frame[j],output_audio_buffer.data()+j*OPUS_INT_SIZE);
// or use this:
// decompressed_frame[OPUS_INT_SIZE*j]=output_frame[j]&0xFF;
// decompressed_frame[OPUS_INT_SIZE*j+1]=(output_frame[j]>>8)&0xFF;
}
audio_buffer.remove(0,FRAME_SIZE*OPUS_INT_SIZE);
output->write(output_audio_buffer,FRAME_SIZE*OPUS_INT_SIZE);
// or use this:
// output->write(reinterpret_cast<char*>(decompressed_frame),FRAME_SIZE*OPUS_INT_SIZE);
}
}
I had a long answer ready about how you are misinterpreting the return value of opus.decodeData as the number of bytes, where the correct interpretation is "number of decoded samples per channel". But it still looks like you account for that in the byte conversion routine later on. So I'm not precisely sure where the bug is.
In general I think you are making the conversion from unsigned char <-> int16 more complicated than it needs to be. You should be able to just pass the audio buffer directly to / from opus and reinterpret its pointer to the needed type inline, without having to manually do bit manipulations to convert and copy between different buffers. The audio device should give you little-endian data but if there is a mismatch you can do a basic byte swapping routine
for (int c = 0; c < numSamples; c++)
{
unsigned char tmp = data[2 * c];
data[2 * c] = data[2 * c + 1];
data[2 * c + 1] = tmp;
}
I don't see it here but I assume you also have code to only consume exactly 960 samples at a time from the mic and keep the rest in the buffer for the next frame, otherwise you'll drop data.
Not that it matters much, but you can also replace 4000 in cbits with 1275, which is the maximum opus packet size.

portaudio only taking one sample?

I am currently working with portaudio with an application that records, and I seem to have some issues collecting the samples. From what I can see is only one sample being stored, and the callback is only being called once, and that is it, even though the variable NUM_OF_SECONDS is set to 30 seconds.
I currently running out of ideas of what I can test, and how I can debug this, so I've come here, any suggestions to how I can debug my problem?
Here is the code:
main.cpp:
#include <record.h>
int main()
{
record somethis;
somethis.start_record();
return 0;
}
record.h
#pragma once
#include <iostream> // Functionality: COUT
#include "portaudio.h"
#include <stdio.h>
#include <stdlib.h>
#include <chrono> //Functionality: Sleep
#include <thread> //Functionality: Sleep
#include <algorithm> //Functionality: fill_n
#define SAMPLE_RATE (44100)
typedef float SAMPLE;
#define NUM_SECONDS 30
#define NUM_CHANNELS 2
#define SAMPLE_SILENCE 0.0f
#define PA_SAMPLE_TYPE paFloat32
#define FRAMES_PER_BUFFER (512)
#define TRUE (1==1)
#define FALSE (!TRUE)
#define WRITE_TO_FILE TRUE
typedef struct
{
int frameIndex;
int maxFrameindex;
SAMPLE *recordedSamples;
}
paTestData;
class record {
public:
record();
void start_record();
private:
PaStreamParameters inputParameters,
outputParameters;
PaStream* stream;
PaError err = paNoError;
paTestData data;
int totalFrames;
int numSamples;
int numBytes;
SAMPLE max, val;
double average;
int recordCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, void *userData);
static int recordCallbackSub(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{
auto pThis = reinterpret_cast<record*>(userData); // get back the this pointer.
return pThis->recordCallback( inputBuffer, outputBuffer,framesPerBuffer, timeInfo,statusFlags, nullptr);
}
};
record.cpp
#include "record.h"
record::record()
{
std::cout << "Record object made" << std::endl;
std::cout << "Portaudio Version: " << Pa_GetVersion() << std::endl;
this->data.maxFrameindex = this->totalFrames = NUM_SECONDS * SAMPLE_RATE;
this->data.frameIndex = 0;
this->numSamples = this->totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
this->data.recordedSamples = new SAMPLE[numSamples]; /* From now on, recordedSamples is initialised. */
if( this->data.recordedSamples == NULL )
{
std::cout << "Could not allocate record array" << std::endl;
exit(1);
}
for(int i=0; i<numSamples; i++ )
{
this->data.recordedSamples[i] = 0;
}
int err = Pa_Initialize();
if( err == paNoError )
{
std::cout << "No error in init" << std::endl;
std::cout << "PortAudio init: "<< Pa_GetErrorText( err ) << std::endl;
}
else
{
printf( "PortAudio error: %s\n", Pa_GetErrorText( err ) );
exit(1);
}
this->inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
if (this->inputParameters.device == paNoDevice) {
std::cout << "Error: No default input device" << std::endl;
exit(1);
}
this->inputParameters.channelCount = 1; /* stereo input */
this->inputParameters.sampleFormat = PA_SAMPLE_TYPE;
this->inputParameters.suggestedLatency = Pa_GetDeviceInfo( this->inputParameters.device )->defaultLowInputLatency;
this->inputParameters.hostApiSpecificStreamInfo = NULL;
std::cout << "Device name: " <<Pa_GetDeviceInfo(this->inputParameters.device)->name << std::endl;
std::cout << "Max inputChannels: " <<Pa_GetDeviceInfo(this->inputParameters.device)->maxInputChannels << std::endl;
}
int record::recordCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{
std::cout << "Callback called" << std::endl;
this->data = (paTestData&) userData;
const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
SAMPLE *wptr = &this->data.recordedSamples[this->data.frameIndex * NUM_CHANNELS];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = this->data.maxFrameindex - this->data.frameIndex;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
//(void) userData;
if( framesLeft < framesPerBuffer )
{
framesToCalc = framesLeft;
finished = paComplete;
}
else
{
framesToCalc = framesPerBuffer;
finished = paContinue;
}
if( inputBuffer == NULL )
{
for(int i=0; i<framesToCalc; i++ )
{
*wptr++ = SAMPLE_SILENCE; /* left */
if( NUM_CHANNELS == 2 ) *wptr++ = SAMPLE_SILENCE; /* right */
}
}
else
{
for(int i=0; i<framesToCalc; i++ )
{
*wptr++ = *rptr++; /* left */
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */
}
}
this->data.frameIndex += framesToCalc;
return finished;
}
void record::start_record()
{
err = Pa_OpenStream(
&this->stream,
&this->inputParameters,
NULL, /* &outputParameters, */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
&record::recordCallbackSub,
this );
if( err != paNoError )
{
std::cout << "Something wrong - open_stream check" << std::endl;
std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
exit(1);
}
this->err = Pa_StartStream( this->stream );
if( err != paNoError )
{
std::cout << "Something wrong in stream check" << std::endl;
std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
exit(1);
}
std::cout << "Waiting for playback to finish" << std::endl;
while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
{
Pa_Sleep(1000);
printf("index = %d\n", this->data.frameIndex ); fflush(stdout);
}
if( err < 0 )
{
std::cout << "error check with isStreamActive - something wrong" << std::endl;
std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
exit(1);
}
err = Pa_CloseStream( stream );
if( err != paNoError )
{
std::cout << "error check with close_stream- something wrong" << std::endl;
std::cout << "PortAudio error: "<< Pa_GetErrorText( err ) << std::endl;
exit(1);
}
std::cout << "Number of entries: " << sizeof(this->data.recordedSamples)/sizeof(this->data.recordedSamples[0]) << std::endl;
/* Measure maximum peak amplitude. */
max = 0;
average = 0.0;
for(int i=0; i<numSamples; i++ )
{
val = this->data.recordedSamples[i];
std::cout << "i: " << i << " : "<< val << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if( val < 0 ) val = -val; /* ABS */
if( val > max )
{
max = val;
}
average += val;
}
average = average / (double)numSamples;
std::cout<<"sample max amplitude = " << max << std::endl;
std::cout<<"sample average = " << average << std::endl;
if (WRITE_TO_FILE)
{
FILE *fid;
fid = fopen("recorded.wav", "wb");
if( fid == NULL )
{
printf("Could not open file.");
}
else
{
fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
fclose( fid );
printf("Wrote data to 'recorded.raw'\n");
}
}
std::cout << "Everythin done!" << std::endl;
}
Update:
I've from some debugging notices that the callback is only being called once, and after it returns, becomes the stream inactive, hence making the it impossible to make calls to the callback function. Why does the stream become inactive?
Your first callback
static int recordCallbackSub(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{
auto pThis = reinterpret_cast<record*>(userData); // get back the this pointer.
return pThis->recordCallback( inputBuffer, outputBuffer,framesPerBuffer, timeInfo,statusFlags, nullptr);
}
calls your second callback
int record::recordCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{
std::cout << "Callback called" << std::endl;
this->data = (paTestData&) userData;
....
}
with the userData parameter set to nullptr. You then cast the nullptr into a paTestData& and set your data member variable to the result, which I suspect you didn't intend to do.
Delete the this->data = (paTestData&) userData; line.

Using Opus with PortAudio

I'm having trouble for using opus with Port audio.
I need to read data audio from a stream using PortAudio, encoding data, decoding data and writing data. If I just read and write, everything works well. But when encoding and decoding, all I can hear is snow with my voice in background.
Here a part of my code:
I/O stream header:
#define NUM_CHANNELS (2)
#define PA_SAMPLE_TYPE paInt24
#define SAMPLE_RATE (48000)
#define FRAMES_PER_BUFFER (1024)
#define SAMPLE_SIZE (3)
#define FRAME_SIZE (960)
class SoundSystem
{
private:
PaStream *_stream;
int _readBufferSize;
PaStreamParameters _inputParam;
PaStreamParameters _outputParam;
unsigned char *_readBuffer;
public:
SoundSystem();
~SoundSystem();
// Init Stream
bool initPa();
bool openStream();
bool startStream();
bool initStream();
// Init params stream
bool initParams() const;
bool initInputParams();
bool initOutputParams();
bool initParams();
// I/O
bool writeOnStream(unsigned cha\
r *buff);
bool readFromStream();
// Utils
void cleanReadBuffer();
int getReadBufferSize() const;
unsigned char *getReadBuffer() const;
};
I/O stream .cpp:
SoundSystem::SoundSystem()
{
_stream = NULL;
_readBufferSize = FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE;
_readBuffer= new unsigned char [_readBufferSize];
}
SoundSystem::~SoundSystem()
{
}
bool SoundSystem::initPa()
{
if ((Pa_Initialize()) != paNoError)
return (false);
return (true);
}
bool SoundSystem::openStream()
{
if ((Pa_OpenStream(&_stream, &_inputParam, &_outputParam, SAMPLE_RATE,
FRAMES_PER_BUFFER, paClipOff, NULL, NULL)) != paNoError)
return (false);
return (true);
}
bool SoundSystem::startStream()
{
if ((Pa_StartStream(_stream)) != paNoError)
return (false);
return (true);
}
bool SoundSystem::initStream()
{
if ((openStream()) == false)
std::cerr << "can not open stream" << std::endl;
if ((startStream()) == false)
std::cerr << "cannot start stream" <<std::endl;
return (true);
}
bool SoundSystem::initParams()
{
if ((initPa()) == false)
std::cerr << "can not ijnit PA" << std::endl;
initInputParams();
initOutputParams();
return (true);
}
bool SoundSystem::initInputParams()
{
if ((_inputParam.device = Pa_GetDefaultInputDevice()) == paNoDevice)
return (false);
_inputParam.channelCount = 2;
_inputParam.sampleFormat = PA_SAMPLE_TYPE;
_inputParam.suggestedLatency = Pa_GetDeviceInfo(_inputParam.device)->defaultLowInputLatency;
_inputParam.hostApiSpecificStreamInfo = NULL;
return (true);
}
bool SoundSystem::initOutputParams()
{
if ((_outputParam.device = Pa_GetDefaultInputDevice()) == paNoDevice)
return (false);
_outputParam.channelCount = 2;
_outputParam.sampleFormat = PA_SAMPLE_TYPE;
_outputParam.suggestedLatency = Pa_GetDeviceInfo(_outputParam.device)->defaultLowInputLatency;
_outputParam.hostApiSpecificStreamInfo = NULL;
return (true);
}
bool SoundSystem::writeOnStream(unsigned char *buff)
{
if ((Pa_WriteStream(_stream, buff, FRAMES_PER_BUFFER)) != paNoError)
{
std::cout << "FAIL WRITE" <<std::endl;
return (false);
}
return (true);
}
bool SoundSystem::readFromStream()
{
if ((Pa_ReadStream(_stream, _readBuffer, FRAMES_PER_BUFFER)) != paNoError)
return (false);
return (true);
}
void SoundSystem::cleanReadBuffer()
{
for (int i = 0; i != _readBufferSize; i++)
_readBuffer[i] = 0;
}
int SoundSystem::getReadBufferSize() const
{enter code here
return (_readBufferSize);
}
unsigned char* SoundSystem::getReadBuffer() const { return (_readBuffer); }
Encode header:
#define FRAME_SIZE (960)
#define SAMPLE_RATE (48000)
#define CHANNELS (2)
#define APPLICATION OPUS_APPLICATION_VOIP
#define MAX_FRAME_SIZE (6*960)
class EncoderSystem
{
private:
OpusEncoder *_encode;
OpusDecoder *_decode;
opus_int16 _in[FRAME_SIZE*CHANNELS];
opus_int16 _out[MAX_FRAME_SIZE*CHANNELS];
int _nbBytes;
public:
EncoderSystem();
~EncoderSystem();
bool encoderCreate();
bool decoderCreate();
unsigned char* encode(unsigned char *, int);
unsigned char* decode(unsigned char *, int);
int getEncodeLen() const;
};
Encode .cpp:
EncoderSystem::EncoderSystem()
{
}
EncoderSystem::~EncoderSystem()
{
}
bool EncoderSystem::encoderCreate()
{
int error;
if ((_encode = opus_encoder_create(SAMPLE_RATE, CHANNELS, OPUS_APPLICATION_VOIP, &error)) == NU\
LL)
{
std::cerr << "Can not create encode" <<std::endl;
return (false);
}
return (true);
}
bool EncoderSystem::decoderCreate()
{
int error;
if ((_decode = opus_decoder_create(SAMPLE_RATE, CHANNELS, &error)) == NULL)
{
std::cerr << "Can not create decoder" <<std::endl;
return (false);
}
return (true);
}
unsigned char* EncoderSystem::encode(unsigned char *data, int size)
{
unsigned char *c_bits = new unsigned char [size];
memcpy(_in, data, size);
/* Encode the frame. */
_nbBytes = opus_encode(_encode, _in, FRAME_SIZE, c_bits, size);
if (_nbBytes<0)
{
std::cerr << "cannot decode" << std::endl;
return NULL;
}
return (c_bits);
}
unsigned char* EncoderSystem::decode(unsigned char *data, int size)
{
int frame_size = opus_decode(_decode, data, size, _out,
MAX_FRAME_SIZE * CHANNELS * 2, 0);
unsigned char *pcm_bytes = new unsigned char [MAX_FRAME_SIZE * CHANNELS * 2];
if (frame_size<0)
{
std::cerr << "cannot decode" << std::endl;
return (NULL);
}
memcpy(pcm_bytes, _out, size);
return (pcm_bytes);
}
int EncoderSystem::getEncodeLen() const { return (this->_nbBytes); }
I really need you, thanks a lot to take your time to help me.
#define PA_SAMPLE_TYPE paInt24
That's probably your problem. As far as I know the standard OPUS codecs take 16-bit integers or 32-bit floating point samples. These correspond to the PortAudio sample types paInt16 and paFloat32.
I recommend getting the types of all your sample buffers correct. Using unsigned char* for formatted sample data is asking for trouble. You need to understand what data types are expected by PortAudio functions and by the OPUS codec functions.