I have piano-like application, and when I'm pressing piano key, it plays a sound. In my app I wrote my own sine wave generator. App is wrote in Qt. I think that problem is with portAudio, but I can't find any solution for this.
I've recorded for you how my problem sounds: https://vocaroo.com/i/s1yiWjaJffTU
And here is my generator class:
soundEngine.h
#ifndef SOUNDENGINE_H
#define SOUNDENGINE_H
#include <QThread>
#include <math.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#define FREQUENCY 220
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int phase;
}
paTestData;
class SoundEngine : public QThread
{
Q_OBJECT
public:
bool turnOFF;
void run();
static int patestCallback( const void *inputBuffer, void *outputBuffer,unsigned long framesPerBuffer,const PaStreamCallbackTimeInfo* timeInfo,PaStreamCallbackFlags statusFlags,void *userData );
void generateSine();
void removeSine();
private:
paTestData data;
PaStream *stream;
PaError err;
bool isPressed;
};
#endif // SOUNDENGINE_H
soundEngine.cpp
#include "soundengine.h"
#include <QDebug>
void SoundEngine::run()
{
PaStreamParameters outputParameters;
int i;
double t;
turnOFF = false;
isPressed = false;
static unsigned long n=0;
for( i=0; i<TABLE_SIZE; i++, n++ )
{
t = (double)i/(double)SAMPLE_RATE;
data.sine[i] = 0;
//data.sine[i] = 0.3*sin(2 * M_PI * FREQUENCY * t);
/*data.sine[i] *= 1.0/2;
data.sine[i] += 0.5*sin(2 * M_PI * (FREQUENCY+110) * t);
data.sine[i] *= 2.0/3;
data.sine[i] += (1.0/3)*sin(2 * M_PI * (FREQUENCY+60) * t);
data.sine[i] *= 3.0/4;
data.sine[i] += (1.0/4)*sin(2 * M_PI * (FREQUENCY+160) * t);*/
}
data.phase = 0;
err = Pa_Initialize();
if(err != paNoError) qDebug()<<"Błąd przy inicjalizacji strumienia:"<<Pa_GetErrorText(err);
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
if (outputParameters.device == paNoDevice) qDebug()<<"Błąd: Brak domyślnego urządzenia wyjścia!";
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /*paNoFlag we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if(err != paNoError) qDebug()<<"Błąd przy otwieraniu strumienia:"<<Pa_GetErrorText(err);
//err = Pa_StartStream( stream );
if(err != paNoError) qDebug()<<"Błąd przy starcie strumienia:"<<Pa_GetErrorText(err);
while (turnOFF == false) {
Pa_Sleep(500);
}
//err = Pa_StopStream( stream );
if(err != paNoError) qDebug()<<"Błąd przy zatrzymywaniu strumienia:"<<Pa_GetErrorText(err);
err = Pa_CloseStream( stream );
if(err != paNoError) qDebug()<<"Błąd przy zamykaniu strumienia:"<<Pa_GetErrorText(err);
Pa_Terminate();
}
int SoundEngine::patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
paTestData *callData = (paTestData*)userData;
float *out = (float*)outputBuffer;
float sample;
unsigned long i;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
sample = callData->sine[callData->phase++];
*out++ = sample; /* left */
*out++ = sample; /* right */
if( callData->phase >= TABLE_SIZE ) callData->phase -= TABLE_SIZE;
}
return paContinue;
}
void SoundEngine::generateSine()
{
if(isPressed == false)
{
for(int i=0; i<TABLE_SIZE; i++)
{
data.sine[i] += 0.3*sin(2 * M_PI * 440 * ((double)i/(double)SAMPLE_RATE));
}
isPressed = true;
err = Pa_StartStream( stream );
}
}
void SoundEngine::removeSine()
{
err = Pa_StopStream( stream );
for(int i=0; i<TABLE_SIZE; i++)
{
data.sine[i] -= 0.3*sin(2 * M_PI * 440 * ((double)i/(double)SAMPLE_RATE));
}
isPressed = false;
}
When I'm pressing button, function
void SoundEngine::generateSine()
is running - it generates sound. When I release the button, method
void SoundEngine::removeSine()
removes the sound.
MODERATOR ATTENTION: This question seems to belong more to dsp.stackexchange than this forum.
There's nothing wrong with either your sound or PortAudio. The sound you're hearing at the end is just the result of the audio being abruptly stopped. Take a look at the following image of a sound that has a constant amplitude through the entire duration. This sound will have an audible pop at the end.
Conversely, if we attenuate the amplitude by modifying the waveform's envelope (the same sound as in image #1) so that it resembles the sound in image #2, we won't hear any abrupt change(s) in the sound at the end.
In conclusion, if your goal is to completely eliminate the pops that you're hearing, fade out (or fade in) your sound(s).
Related
I am struggling with outputting user selected audio tones with portaudio. I have based a function on the paex_sine.cpp and modified it somewhat based on this post multi-audio-tones-to-sound-card-using-portaudio
I am able to generate a sine wave on the fly by placing the sin(n * FREQ * 2 * PI / SAMPLE_RATE) calculation in paCallBackMethod() but the frequency isn't correct, and doesn't change based on user input.
Here is my function code.
void CDeepWaveDlg::generateSound(float freq)
{
pitch = tone;
offset = freq;
class Sine
{
public:
Sine() : stream(0), left_phase(0), right_phase(0)
{
}
bool open(PaDeviceIndex index)
{
PaStreamParameters outputParameters;
outputParameters.device = index;
if (outputParameters.device == paNoDevice) {
return false;
}
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
PaError err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
&Sine::paCallback,
this /* Using 'this' for userData so we can cast to Sine* in paCallback method */
);
if (err != paNoError)
{
/* Failed to open stream to device !!! */
return false;
}
err = Pa_SetStreamFinishedCallback(stream, &Sine::paStreamFinished);
if (err != paNoError)
{
Pa_CloseStream(stream);
stream = 0;
return false;
}
return true;
}
bool close()
{
if (stream == 0)
return false;
PaError err = Pa_CloseStream(stream);
stream = 0;
return (err == paNoError);
}
bool start()
{
if (stream == 0)
return false;
PaError err = Pa_StartStream(stream);
return (err == paNoError);
}
bool stop()
{
if (stream == 0)
return false;
PaError err = Pa_StopStream(stream);
return (err == paNoError);
}
private:
/* The instance callback, where we have access to every method/variable in object of class Sine */
int paCallbackMethod(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags)
{
float *out = (float*)outputBuffer;
unsigned long i;
(void)timeInfo; /* Prevent unused variable warnings. */
(void)statusFlags;
(void)inputBuffer;
for (i = 0; i<framesPerBuffer; i++)
{
float v = sin(i * pitch * 2.0 * M_PI /(float)SAMPLE_RATE);
float v2 = sin(i * (pitch + (float)offset) * 2.0 * M_PI /(float)SAMPLE_RATE);
*out++ = v;
*out++ = v2;
}
return paContinue;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int paCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
/* Here we cast userData to Sine* type so we can call the instance method paCallbackMethod, we can do that since
we called Pa_OpenStream with 'this' for userData */
return ((Sine*)userData)->paCallbackMethod(inputBuffer, outputBuffer,
framesPerBuffer,
timeInfo,
statusFlags);
}
void paStreamFinishedMethod()
{
printf("Stream Completed: %s\n", message);
}
/*
* This routine is called by portaudio when playback is done.
*/
static void paStreamFinished(void* userData)
{
return ((Sine*)userData)->paStreamFinishedMethod();
}
PaStream *stream;
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
char message[20];
};
PaError err;
Sine sine;
err = Pa_Initialize();
if (err != paNoError) goto error;
if (sine.open(Pa_GetDefaultOutputDevice()))
{
if (sine.start())
{
Pa_Sleep(NUM_SECONDS * 1000);
sine.stop();
}
sine.close();
}
Pa_Terminate();
The only way I am able to get close to the desired pitch is to increase frames per buffer, which is currently 64, but I still run into the problem of not changing frequency based on user selection.
This problem is definitely beyond my skill level, but I am hoping someone can help me understand what's going on here.Thanks in advance for any help.
hopefully one of you out there can help me!
I am trying to use the Adafruit SHT31-D (an i2c device) board with my Pi2. I am going off of this datasheet to guide my coding efforts. I am using Wiring Pi (wiringpi.com) to facilitate things.
I am able to successfully open a connection to the device, and sending commands seems to work fine, but I am unable to read data back! Here is the little mini library I have put together. I am hoping that one of you might have some experience with this sort of thing and be able to help me see where I've gone wrong.
To rule out any possible issues with the sensor hardware, I have tested it with my Arduino UNO and it works without issues.
Here is my C++ code:
SHT3x.h
#pragma once
/* Sensor Commands */
#define DEFAULT_SHT_ADDR 0x44
#define MEAS_HREP_STRETCH 0x2C06
#define MEAS_MREP_STRETCH 0x2C0D
#define MEAS_LREP_STRETCH 0x2C10
#define MEAS_HREP 0x2400
#define MEAS_MREP 0x240B
#define MEAS_LREP 0x2416
#include <cstdint>
class SHT3x {
public:
SHT3x(const uint8_t& i2cAddr);
float readHumidity(const uint16_t& command) const;
float readTempC(const uint16_t& command) const;
float readTempF(const uint16_t& command) const;
private:
int8_t _fd;
uint8_t _header;
uint32_t getMeasurement(const uint16_t& command) const;
void sendCommand(const uint16_t& command) const;
uint32_t receiveData(void) const;
};
SHT3x.cpp
#include <stdexcept>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include "SHT3x.h"
SHT3x::SHT3x(const uint8_t& i2cAddr) {
_fd = wiringPiI2CSetup(i2cAddr);
_header = i2cAddr << 1;
if (_fd < 0) {
throw std::runtime_error("Unable to connect");
}
}
float SHT3x::readHumidity(const uint16_t& command) const {
uint32_t raw_data = getMeasurement(command);
if (!raw_data) {
throw std::runtime_error("Bad Reading.");
}
uint16_t raw_humidity = raw_data & 0xFFFF;
float humidity = 100.0 * ((float) raw_humidity / (float) 0xFFFF);
return humidity;
}
float SHT3x::readTempC(const uint16_t& command) const {
uint32_t raw_data = getMeasurement(command);
if (!raw_data) {
throw std::runtime_error("Bad Reading.");
}
uint16_t raw_temp = raw_data >> 16;
float tempC = -45.0 + (175.0 * ((float) raw_temp / (float) 0xFFFF));
return tempC;
}
float SHT3x::readTempF(const uint16_t& command) const {
uint32_t raw_data = getMeasurement(command);
if (!raw_data) {
throw std::runtime_error("Bad Reading.");
}
uint16_t raw_temp = raw_data >> 16;
float tempF = -49.0 + (315.0 * ((float) raw_temp / (float) 0xFFFF));
return tempF;
}
uint32_t SHT3x::getMeasurement(const uint16_t& command) const {
try {
sendCommand(command);
} catch (std::runtime_error& e) {
throw;
}
return receiveData();
}
void SHT3x::sendCommand(const uint16_t& command) const {
// break command into bytes
uint8_t MSB = command >> 8;
uint8_t LSB = command & 0xFF;
// send header
int8_t ack = wiringPiI2CWrite(_fd, _header);
// send command
ack &= wiringPiI2CWrite(_fd, MSB);
ack &= wiringPiI2CWrite(_fd, LSB);
// handle errors
if (ack) {
throw std::runtime_error("Sending command failed.");
}
}
uint32_t SHT3x::receiveData(void) const {
uint32_t data;
// send header
uint8_t read_header = _header | 0x01;
int8_t ack = wiringPiI2CWrite(_fd, read_header);
// handle errors
if (ack) throw std::runtime_error("Unable to read data.");
// read data
data = wiringPiI2CRead(_fd);
for (size_t i = 0; i < 4; i++) {
printf("Data: %d\n", data);
data <<= 8;
if (i != 1) {
data |= wiringPiI2CRead(_fd);
} else {
wiringPiI2CRead(_fd); // skip checksum
}
}
wiringPiI2CRead(_fd); // second checksum
return data;
}
The SHT31 uses 16bit read and write, rather than using 2 8bit writes you might be better off using wiringpi's 16bit write. wiringPiI2CWriteReg16(). Same thing applies to the read.
Below is a very early copy of what I've done to read the sht31-d on a PI. It has no dependencies except i2c-dev. Heater enable/disable is not working, but softreset, clearstatus, getserial & get temp/humid are all fine.
/*
* Referances
* https://www.kernel.org/doc/Documentation/i2c/dev-interface
* https://github.com/adafruit/Adafruit_SHT31
* https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity_and_Temperature_Sensors/Sensirion_Humidity_and_Temperature_Sensors_SHT3x_Datasheet_digital.pdf
*
* This depends on i2c dev lib
* sudo apt-get install libi2c-dev
*
* Below is also a good one to have, but be careful i2cdump from the below cause the sht31 interface to become unstable for me
* and requires a hard-reset to recover correctly.
* sudo apt-get install i2c-tools
*
* on PI make sure below 2 commands are in /boot/config.txt
* dtparam=i2c_arm=on
* dtparam=i2c1_baudrate=10000
* I know we are slowing down the baurate from optimal, but it seems to be the most stable setting in my testing.
* add another 0 to the above baudrate for max setting, ie dtparam=i2c1_baudrate=100000
*/
#include <linux/i2c-dev.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <elf.h>
#include <unistd.h>
#define SHT31_INTERFACE_ADDR 1
#define SHT31_DEFAULT_ADDR 0x44
#define SHT31_READ_SERIALNO 0x3780
#define SHT31_MEAS_HIGHREP_STRETCH 0x2C06 // Doesn't work on PI
#define SHT31_MEAS_MEDREP_STRETCH 0x2C0D // Seems to work on PI but shouldn't
#define SHT31_MEAS_LOWREP_STRETCH 0x2C10 // Seems to work on PI but shouldn't
#define SHT31_MEAS_HIGHREP 0x2400 // Doesn't work on PI
#define SHT31_MEAS_MEDREP 0x240B
#define SHT31_MEAS_LOWREP 0x2416
#define SHT31_READSTATUS 0xF32D
#define SHT31_CLEARSTATUS 0x3041
#define SHT31_SOFTRESET 0x30A2
#define SHT31_HEATER_ENABLE 0x306D
#define SHT31_HEATER_DISABLE 0x3066
#define CHECK_BIT(var,pos) (((var)>>(pos)) & 1)
/*
* delay:
* Wait for some number of milliseconds
*********************************************************************************
*/
void delay (unsigned int howLong)
{
struct timespec sleeper, dummy ;
sleeper.tv_sec = (time_t)(howLong / 1000) ;
sleeper.tv_nsec = (long)(howLong % 1000) * 1000000 ;
nanosleep (&sleeper, &dummy) ;
}
/*
*
* CRC-8 formula from page 14 of SHT spec pdf
*
* Test data 0xBE, 0xEF should yield 0x92
*
* Initialization data 0xFF
* Polynomial 0x31 (x8 + x5 +x4 +1)
* Final XOR 0x00
*/
uint8_t crc8(const uint8_t *data, int len)
{
const uint8_t POLYNOMIAL = 0x31;
uint8_t crc = 0xFF;
int j;
int i;
for (j = len; j; --j ) {
crc ^= *data++;
for ( i = 8; i; --i ) {
crc = ( crc & 0x80 )
? (crc << 1) ^ POLYNOMIAL
: (crc << 1);
}
}
return crc;
}
/*
*
* buffer should return with data read, size defined by readsize
*********************************************************************************
*/
int writeandread(int fd, uint16_t sndword, uint8_t *buffer, int readsize)
{
int rtn;
uint8_t snd[3];
// Split the 16bit word into two 8 bits that are flipped.
snd[0]=(sndword >> 8) & 0xff;
snd[1]=sndword & 0xff;
rtn = write(fd, snd, 2);
if ( rtn != 2 ) {
return 1;
}
if (readsize > 0) {
delay(10);
rtn = read(fd, buffer, readsize);
if ( rtn < readsize) {
return 2;
}
}
return 0;
}
void printserialnum(int file)
{
uint8_t buf[10];
int rtn;
rtn = writeandread(file, SHT31_READ_SERIALNO, buf, 6);
if (rtn != 0)
printf("ERROR:- Get serial i2c %s failed\n",(rtn==1?"write":"read"));
else {
if (buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2))
printf("WARNING:- Get serial CRC check failed, don't trust result\n");
uint32_t serialNo = ((uint32_t)buf[0] << 24)
| ((uint32_t)buf[1] << 16)
| ((uint32_t)buf[3] << 8)
| (uint32_t)buf[4];
printf("Serial# = %d\n",serialNo);
}
}
void printtempandhumidity(int file)
{
uint8_t buf[10];
int rtn;
rtn = writeandread(file, SHT31_MEAS_MEDREP_STRETCH, buf, 6);
if (rtn != 0)
printf("ERROR:- Get temp/humidity i2c %s failed\n",(rtn==1?"write":"read"));
else {
if ( buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2))
printf("WARNING:- Get temp/humidity CRC check failed, don't trust results\n");
uint16_t ST, SRH;
ST = buf[0];
ST <<= 8;
ST |= buf[1];
SRH = buf[3];
SRH <<= 8;
SRH |= buf[4];
double stemp = ST;
stemp *= 175;
stemp /= 0xffff;
stemp = -45 + stemp;
double stempf = ST;
stempf *= 315;
stempf /= 0xffff;
stempf = -49 + stempf;
printf("Temperature %.2fc - %.2ff\n",stemp,stempf);
double shum = SRH;
shum *= 100;
shum /= 0xFFFF;
printf("Humidity %.2f%%\n",shum);
}
}
void printBitStatus(uint16_t stat)
{
printf("Status\n");
printf(" Checksum status %d\n", CHECK_BIT(stat,0));
printf(" Last command status %d\n", CHECK_BIT(stat,1));
printf(" Reset detected status %d\n", CHECK_BIT(stat,4));
printf(" 'T' tracking alert %d\n", CHECK_BIT(stat,10));
printf(" 'RH' tracking alert %d\n", CHECK_BIT(stat,11));
printf(" Heater status %d\n", CHECK_BIT(stat,13));
printf(" Alert pending status %d\n", CHECK_BIT(stat,15));
}
void printstatus(int file)
{
uint8_t buf[10];
int rtn;
rtn = writeandread(file, SHT31_READSTATUS, buf, 3);
if (rtn != 0)
printf("ERROR:- readstatus %s failed\n",(rtn==1?"write":"read"));
else {
if ( buf[2] != crc8(buf, 2))
printf("WARNING:- Get status CRC check failed, don't trust results\n");
uint16_t stat = buf[0];
stat <<= 8;
stat |= buf[1];
printBitStatus(stat);
}
}
void clearstatus(int file)
{
if( writeandread(file, SHT31_CLEARSTATUS, NULL, 0) != 0)
printf("ERROR:- sht31 clear status failed\n");
else
printf("Clearing status - ok\n");
}
void softreset(int file)
{
if( writeandread(file, SHT31_SOFTRESET, NULL, 0) != 0)
printf("ERROR:- sht31 soft reset failed\n");
else
printf("Soft reset - ok\n");
}
void enableheater(int file)
{
if( writeandread(file, SHT31_HEATER_ENABLE, NULL, 0) != 0)
printf("ERROR:- sht31 heater enable failed\n");
else
printf("Enabiling heater - ok\n");
}
void disableheater(int file)
{
if( writeandread(file, SHT31_HEATER_DISABLE, NULL, 0) != 0)
printf("ERROR:- sht31 heater enable failed\n");
else
printf("Disableing heater - ok\n");
}
int main()
{
int file;
char filename[20];
snprintf(filename, 19, "/dev/i2c-%d", SHT31_INTERFACE_ADDR);
file = open(filename, O_RDWR);
if (file < 0) {
printf("ERROR:- Can't open %s\n",filename);
exit(1);
}
if (ioctl(file, I2C_SLAVE, SHT31_DEFAULT_ADDR) < 0) {
printf("ERROR:- Connecting to sht31 I2C address 0x%02hhx\n", SHT31_DEFAULT_ADDR);
exit(1);
}
softreset(file);
printtempandhumidity(file);
printstatus(file);
close(file);
return 0;
}
I want to make a guitar effects program so I'm trying to run the example code pa_fuzz.c from PORTAUDIO API.
It works on paWDMKS host api. But when I connect the guitar using Behringer ucg 102 (for no lantency) the host api changes to paMME and I get errorCode 1 and message: "undefined external error".
Also in the visual studio output console I get tons of messages like:
'ConsoleApplication4.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\SHCore.dll'
'ConsoleApplication4.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\shlwapi.dll'
'ConsoleApplication4.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\shell32.dll'
'ConsoleApplication4.exe' (Win32): Unloaded 'C:\Windows\SysWOW64\dbgcore.dll'
'ConsoleApplication4.exe' (Win32): Unloaded 'C:\Program Files\Native Instruments\Rig Kontrol 3 Driver\asio\rig3asio32.dll'
Here is the code snippet for the "PaHostErrorInfo" object;
PaHostErrorInfo *err2 = Pa_GetLastHostErrorInfo();
printf(err2->errorText);
Here is the source code:
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
/*
** Note that many of the older ISA sound cards on PCs do NOT support
** full duplex audio (simultaneous record and playback).
** And some only support full duplex at lower sample rates.
*/
#define SAMPLE_RATE (8000)
#define PA_SAMPLE_TYPE paFloat32
#define FRAMES_PER_BUFFER (64)
typedef float SAMPLE;
float CubicAmplifier( float input );
static int fuzzCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData );
/* Non-linear amplifier with soft distortion curve. */
float CubicAmplifier( float input )
{
float output, temp;
if( input < 0.0 )
{
temp = input + 1.0f;
output = (temp * temp * temp) - 1.0f;
}
else
{
temp = input - 1.0f;
output = (temp * temp * temp) + 1.0f;
}
return output;
}
#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x))))
static int gNumNoInputs = 0;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int fuzzCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
SAMPLE *out = (SAMPLE*)outputBuffer;
const SAMPLE *in = (const SAMPLE*)inputBuffer;
unsigned int i;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) userData;
if( inputBuffer == NULL )
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = 0; /* left - silent */
*out++ = 0; /* right - silent */
}
gNumNoInputs += 1;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = FUZZ(*in++); /* left - distorted */
*out++ = *in++; /* right - clean */
}
}
return paContinue;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters inputParameters, outputParameters;
PaStream *stream;
PaError err;
PaDeviceInfo *info;
err = Pa_Initialize();
if( err != paNoError ) goto error;
info = Pa_GetDeviceInfo(1);
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
if (inputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default input device.\n");
goto error;
}
inputParameters.channelCount = 2; /* stereo input */
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
info = Pa_GetDeviceInfo(4);
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
if (outputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default output device.\n");
goto error;
}
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
&inputParameters,
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* paClipOff, */ /* we won't output out of range samples so don't bother clipping them */
fuzzCallback,
NULL );
PaHostErrorInfo *err2 = Pa_GetLastHostErrorInfo();
printf(err2->errorText);
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Hit ENTER to stop program.\n");
getchar();
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
printf("Finished. gNumNoInputs = %d\n", gNumNoInputs );
Pa_Terminate();
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return -1;
}
Can you guys please have a look on what can be the problem? I have installed BEHRINGER USB WDM AUDIO 2.8.40 drivers. (I use this usb interface with guitar rig)
I switched to another usb port and it worked. I think it's a bug from portaudio. I will leave this answer here. Now I have another problem. LATENCY :( is so frustrating to play guitar with latency.
What can be the cause of this latency? I'm connecting my guitar with behringer ucg102 (famous for no latency). And again, I use it with Guitar Rig where I have close to 0 latency. Is it a problem from portaudio library? This code is from their example "pa_fuzz.c"
New Edit: latency problem fixed by selecting the required input and output deivice.
I would like to know how to know the relationship of the portaudio test examples and how to get the frequency.I was wondering how i could send a specifc frequency to sound card?
1. How to know the specific frequency of that the code send to sound card
2. How i could send sin(2*pi*f*t) and sin((2*pi*f*t)-3.14) which is a delayed version. Here is the code. Any help is highly appreciated
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#define NUM_SECONDS (20)
#define SAMPLE_RATE (44100)
#define AMPLITUDE (0.9)
#define FRAMES_PER_BUFFER (64)
#define OUTPUT_DEVICE Pa_GetDefaultOutputDeviceID()
//#define OUTPUT_DEVICE (2)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
}
paTestData;
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase +=2.57; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d, devID = %d\n",
SAMPLE_RATE, FRAMES_PER_BUFFER, OUTPUT_DEVICE);
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) (AMPLITUDE * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
}
data.left_phase = data.right_phase = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play for %d seconds.\n", NUM_SECONDS ); fflush(stdout);
Pa_Sleep( NUM_SECONDS * 1000 );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}
A quick look suggests SAMPLE_RATE / TABLE_SIZE will give you you the cycles per second for the sine wave, which appears to work out to be ~220Hz, which is an A3 note. Though from the code I can see the left channel is getting this wave and the right channel is stepping through the table at 2.75x the base rate which I guess would be about 606Hz which doesn't hit a note, I expect this sample sounds quite horrible!
To send the Sine and a delayed version you just look up the delayed version using an offset to the phase parameter, taking care to wrap around properly, and add the waves together. You'll probably want to lower the AMPLITUDE to avoid clipping.
I am trying to play a wave file in RHEL6 using alsa library calls in my C Code in Qt. I am reading the wave file ("t15.wav") in a buffer(wave_buffer). The wave header has been stripped off since the alsa library requires raw PCM samples to be played. Further I have set up the PCM hardware & Software params using 'snd_pcm_hw_params(PCM, params)' & 'snd_pcm_sw_params_current(PCM, swparams)' and many other calls. I am writing the PCM samples on the PCM handle using 'snd_pcm_writei' command. For this purpose i am reading a chunk(32 or 1024 or 2048 or 4096 or 8192 bytes) of data from the wave_buffer and sending it for playing using the snd_pcm_writei command. If I choose a small chunk the audio quality falters but playback is uninterrupted. If I use a bigger chunk(greater than 4096 i.e. 8192) I get perfect audio quality but it is interrupted( When next chunk of data is required for playing ). My constraint is that I can have access to data in chunks only and not as a file or entire buffer. Can anybody help me in removing the interruptions in playing the wave data so that I can get uninterrupted audio playback. Following is my code :
The two variables buffer_time & period_time return the period size which is the size of chunk.
If buffer_time = 5000 & period_time=1000 the period_size returned by alsa library is 32 bytes //audio quality falters but no interruptions
If buffer_time = 500000 & period_time=100000 the period_size returned by alsa library is 8192 bytes //good audio quality but interrupted
Tuning these parameters seems useless as I have wasted a lot of time doing this. Please help me get through this problem-----
Stucture of Wave File :
Sample Rate : 44100
Bits per Sample : 16
Channels : 2
mainwindow.h----
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <alsa/asoundlib.h>
#define BLOCKSIZE 44100 * 2 * 2 // Sample Rate * Channels * Byte per Sample(Bits per sample / 8)
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
int init_alsa();
int play_snd();
~MainWindow();
snd_pcm_t *PCM;
snd_pcm_sframes_t delayp;
snd_pcm_sframes_t availp;
snd_pcm_sw_params_t *swparams;
snd_pcm_hw_params_t *params;
static snd_pcm_sframes_t period_size;
static snd_pcm_sframes_t buffer_size;
unsigned char wave_buffer[900000];
unsigned char play_buffer[BLOCKSIZE];
int filesize;
FILE *fp;
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp---
#include "mainwindow.h"
#include "ui_mainwindow.h"
snd_pcm_sframes_t MainWindow::period_size;
snd_pcm_sframes_t MainWindow::buffer_size;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
if((fp = fopen("t15.wav","rb"))==NULL)
printf("Error Opening File");
fseek(fp,0L,SEEK_END);
filesize = ftell(fp)-44;
fseek(fp,0L,SEEK_SET);
fseek(fp,44,SEEK_SET);
fread(wave_buffer,filesize,1,fp);
fclose(fp);
delayp = 0;
init_alsa();
play_snd();
}
MainWindow::~MainWindow()
{
delete ui;
}
int MainWindow::init_alsa()
{
unsigned int rate = 44100;
int err,dir;
unsigned int rrate = 44100;
snd_pcm_uframes_t size;
static unsigned int buffer_time = 500000;
static unsigned int period_time = 100000;
static int period_event = 0;
if ((err=snd_pcm_open(&PCM,"plughw:0,0",SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
fprintf(stderr, "Can't use sound: %s\n", snd_strerror(err));
return err;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_sw_params_alloca(&swparams);
//snd_pcm_nonblock(PCM,0);
/* choose all parameters */
err = snd_pcm_hw_params_any(PCM, params);
if (err < 0) {
printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
return err;
}
/* set hardware resampling */
err = snd_pcm_hw_params_set_rate_resample(PCM, params, 1);
if (err < 0) {
printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
return err;
}
/* set the interleaved read/write format */
err = snd_pcm_hw_params_set_access(PCM, params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
printf("Access type not available for playback: %s\n", snd_strerror(err));
return err;
}
/* set the sample format */
err = snd_pcm_hw_params_set_format(PCM, params, SND_PCM_FORMAT_S16_LE);
if (err < 0) {
printf("Sample format not available for playback: %s\n", snd_strerror(err));
return err;
}
/* set the count of channels */
err = snd_pcm_hw_params_set_channels(PCM, params, 2);
if (err < 0) {
printf("Channels count (%i) not available for playbacks: %s\n", 2, snd_strerror(err));
return err;
}
/* set the stream rate */
rrate = rate;
err = snd_pcm_hw_params_set_rate_near(PCM, params, &rrate, 0);
if (err < 0) {
printf("Rate %iHz not available for playback: %s\n", 44100, snd_strerror(err));
return err;
}
if (rrate != 44100) {
printf("Rate doesn't match (requested %iHz, get %iHz)\n", rrate, err);
return -EINVAL;
}
/* set the buffer time */
err = snd_pcm_hw_params_set_buffer_time_near(PCM, params, &buffer_time, &dir);
if (err < 0) {
printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
return err;
}
err = snd_pcm_hw_params_get_buffer_size(params, &size);
if (err < 0) {
printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
return err;
}
buffer_size = size;
/* set the period time */
err = snd_pcm_hw_params_set_period_time_near(PCM, params, &period_time, &dir);
if (err < 0) {
printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
return err;
}
err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
if (err < 0) {
printf("Unable to get period size for playback: %s\n", snd_strerror(err));
return err;
}
period_size = size;
/* write the parameters to device */
err = snd_pcm_hw_params(PCM, params);
if (err < 0) {
printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
return err;
}
printf("Size = %ld",period_size);
snd_pcm_sw_params_current(PCM, swparams); /* get the current swparams */
/* start the transfer when the buffer is almost full: */
/* (buffer_size / avail_min) * avail_min */
snd_pcm_sw_params_set_start_threshold(PCM, swparams, (buffer_size / period_size) * period_size);
/* allow the transfer when at least period_size samples can be processed */
/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
snd_pcm_sw_params_set_avail_min(PCM, swparams, period_event ? buffer_size : period_size);
snd_pcm_sw_params(PCM, swparams);/* write the parameters to the playback device */
return 1;
}
int MainWindow::play_snd()
{
int curr_pos = 0;
int buff_size = 0;
long val = 0;
while(curr_pos < filesize)
{
if(filesize-curr_pos >= period_size)
{
memcpy(play_buffer,wave_buffer+curr_pos,period_size);
buff_size = period_size;
curr_pos += buff_size;
}
else
{
memcpy(play_buffer,wave_buffer+curr_pos,filesize-curr_pos);
buff_size = filesize - curr_pos;
curr_pos += buff_size;
}
int i=1;
unsigned char *ptr = play_buffer;
while(buff_size > 0)
{
val = snd_pcm_writei(PCM,&play_buffer,buff_size);
if (val == -EAGAIN)
continue;
ptr += val * 2;
buff_size -= val;
}
}
return 0;
}
I have a similar C Code of alsa library which generates sine wave samples at runtime and plays them using same snd_pcm_writei command and it plays perfectly without any interruptions....This is the alsa library code---
/*
* This small demo sends a simple sinusoidal wave to your speakers.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include "alsa/asoundlib.h"
#include <sys/time.h>
#include <math.h>
static char *device = "plughw:0,0"; /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
static unsigned int rate = 44100; /* stream rate */
static unsigned int channels = 2; /* count of channels */
static unsigned int buffer_time = 5000; /* ring buffer length in us */
static unsigned int period_time = 1000; /* period time in us */
static double freq = 440; /* sinusoidal wave frequency in Hz */
static int resample = 1; /* enable alsa-lib resampling */
static int period_event = 0; /* produce poll event after each period */
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
static snd_output_t *output = NULL;
snd_pcm_sframes_t delayp;
snd_pcm_sframes_t availp;
static void generate_sine(const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
int count, double *_phase)
{
static double max_phase = 2. * M_PI;
double phase = *_phase;
double step = max_phase*freq/(double)rate;
unsigned char *samples[channels];
int steps[channels];
unsigned int chn;
int format_bits = snd_pcm_format_width(format);
unsigned int maxval = (1 << (format_bits - 1)) - 1;
int bps = format_bits / 8; /* bytes per sample */
int phys_bps = snd_pcm_format_physical_width(format) / 8;
int big_endian = snd_pcm_format_big_endian(format) == 1;
int to_unsigned = snd_pcm_format_unsigned(format) == 1;
int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
format == SND_PCM_FORMAT_FLOAT_BE);
/* verify and prepare the contents of areas */
for (chn = 0; chn < channels; chn++) {
samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
steps[chn] = areas[chn].step / 8;
samples[chn] += offset * steps[chn];
}
/* fill the channel areas */
while (count-- > 0) {
union {
float f;
int i;
} fval;
int res, i;
if (is_float)
{
fval.f = sin(phase) * maxval;
res = fval.i;
}
else
res = sin(phase) * maxval;
if (to_unsigned)
res ^= 1U << (format_bits - 1);
for (chn = 0; chn < channels; chn++) {
/* Generate data in native endian format */
if (big_endian) {
for (i = 0; i < bps; i++)
*(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
} else {
for (i = 0; i < bps; i++)
*(samples[chn] + i) = (res >> i * 8) & 0xff;
}
samples[chn] += steps[chn];
}
phase += step;
if (phase >= max_phase)
phase -= max_phase;
}
*_phase = phase;
}
static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access)
{
unsigned int rrate;
snd_pcm_uframes_t size;
int dir;
snd_pcm_hw_params_any(handle, params); /* choose all parameters */
snd_pcm_hw_params_set_rate_resample(handle, params, resample);/* set hardware resampling */
snd_pcm_hw_params_set_access(handle, params, access); /* set the interleaved read/write format */
snd_pcm_hw_params_set_format(handle, params, format); /* set the sample format */
snd_pcm_hw_params_set_channels(handle, params, channels); /* set the count of channels */
rrate = rate; /* set the stream rate */
snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);/* set the buffer time */
snd_pcm_hw_params_get_buffer_size(params, &size);
buffer_size = size;
snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);/* set the period time */
snd_pcm_hw_params_get_period_size(params, &size, &dir);
period_size = size;
snd_pcm_hw_params(handle, params); /* write the parameters to device */
return 0;
}
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
{
snd_pcm_sw_params_current(handle, swparams); /* get the current swparams */
/* start the transfer when the buffer is almost full: */
/* (buffer_size / avail_min) * avail_min */
snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
/* allow the transfer when at least period_size samples can be processed */
/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
snd_pcm_sw_params(handle, swparams);/* write the parameters to the playback device */
return 0;
}
/*
* Transfer method - write only
*/
static int write_loop(snd_pcm_t *handle, signed short *samples, snd_pcm_channel_area_t *areas)
{
double phase = 0;
signed short *ptr;
int err, cptr;
int i=0;
printf("Period Size = %ld",period_size);
while (1) {
fflush(stdout);
generate_sine(areas, 0, period_size, &phase);
ptr = samples;
cptr = period_size;
i=1;
while (cptr > 0) {
err = snd_pcm_writei(handle, ptr, cptr);
snd_pcm_avail_delay(handle,&availp,&delayp);
printf("available frames =%ld delay = %ld i = %d\n",availp,delayp,i);
if (err == -EAGAIN)
continue;
ptr += err * channels;
cptr -= err;
i++;
}
}
}
/*
* Transfer method - asynchronous notification
*/
int main()
{
snd_pcm_t *handle;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
signed short *samples;
unsigned int chn;
snd_pcm_channel_area_t *areas;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
snd_output_stdio_attach(&output, stdout, 0);
printf("Playback device is %s\n", device);
printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
printf("Sine wave rate is %.4fHz\n", freq);
snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0);
set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
set_swparams(handle, swparams);
samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
for (chn = 0; chn < channels; chn++) {
areas[chn].addr = samples;
areas[chn].first = chn * snd_pcm_format_physical_width(format);
areas[chn].step = channels * snd_pcm_format_physical_width(format);
}
write_loop(handle, samples, areas);
free(areas);
free(samples);
snd_pcm_close(handle);
return 0;
}
I solved the problem by altering the length argument of snd_pcm_writei...perviously i was giving it equal to the data contained in play_buffer...now i changed it to "buff_size/4" and the audio is playing perfectly without breaks. Actually it is the size after which the system should start buffering for new pcm samples as per my understanding. Previously it was buffering after playing the entire length buff_size and that resulted in breaks in audio output...