I am searching for a way to dynamically find the system path for HID (USB) devices.
On my Ubuntu machine it's /dev/usb/hiddevX, but I suppose there are distros where hiddevices get mounted elsewhere.
To be more specific: I need this in a C program to open a HID device - this should work on every system, no matter where the devices are mounted.
Current function:
int openDevice(int vendorId, int productId) {
char devicePath[24];
unsigned int numIterations = 10, i, handle;
for (i = 0; i < numIterations; i++) {
sprintf(devicePath, "/dev/usb/hiddev%d", i);
if((handle = open(devicePath, O_RDONLY)) >= 0) {
if(isDesiredDevice(handle, vendorId, productId))
break;
close(handle);
}
}
if (i >= numIterations) {
ThrowException(Exception::TypeError(String::New("Could not find device")));
}
return handle;
}
It works well but I don't like the hardcoded /dev/usb/hiddev
Edit: It turned out that some other programmers use fallbacks to /dev/usb/hid/hiddevX and /dev/hiddevX, so I built in those fallbacks too.
Updated method:
/**
* Returns the correct handle (file descriptor) on success
*
* #param int vendorId
* #param int productId
* #return int
*/
int IO::openDevice(int vendorId, int productId) {
char devicePath[24];
const char *devicePaths[] = {
"/dev/usb/hiddev\%d",
"/dev/usb/hid/hiddev\%d",
"/dev/hiddev\%d",
NULL
};
unsigned int numIterations = 15, i, j, handle;
for (i = 0; devicePaths[i]; i++) {
for (j = 0; j < numIterations; j++) {
sprintf(devicePath, devicePaths[i], j);
if((handle = open(devicePath, O_RDONLY)) >= 0) {
if(isDesiredDevice(handle, vendorId, productId))
return handle;
close(handle);
}
}
};
ThrowException(Exception::Error(String::New("Couldn't find device!")));
return 0;
};
Related
I'm currently working on a project that involves bringing information back from devices plugged in via true usb. To solve this, I'm attempting to use libusb in my C++ project. I'm on Windows 10, using C++ and QT Creator with QT version 5.2.1. I can get the vendor ID and product ID of my device with .idVendor and idProduct. However, my serial number is returning as 0 with .iSerialNumber. I'll post my code below and hopefully someone can help me figure out what I'm missing or suggest another library for me to try. Thanks!
bool CPumpFinder::pollTrueUSB(boolean &deviceFound, Device &newDevice)
{
boolean usbFound = FALSE;
boolean accuCheckFound = FALSE;
AccuGuideCom *acc = new AccuGuideCom();
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
libusb_context *ctx = NULL; //a libusb session
int r; //for return values
ssize_t cnt; //holding number of devices in list
r = libusb_init(&ctx); //initialize a library session
if(r < 0) {
return 1;
}
libusb_set_debug(ctx, 3); //set verbosity level to 3, as suggested in the documentation
cnt = libusb_get_device_list(ctx, &devs); //get the list of devices
if(cnt < 0) {
}
ssize_t i; //for iterating through the list
for(i = 0; i < cnt; i++) {
printdev(devs[i], usbFound, accuCheckFound); //print specs of this device
if(usbFound == TRUE)
{
if(accuCheckFound == TRUE)
{
newDevice.displayName = "ACCU-CHEK GUIDE";
newDevice.type = DEVICE_ACCUCHECKGUIDE;
newDevice.vendorID = accuVID;
newDevice.productID = accuPID;
newDevice.serial = accuSerial;
}
deviceFound = TRUE;
break;
}
}
libusb_free_device_list(devs, 1); //free the list, unref the devices in it
libusb_exit(ctx); //close the session
return deviceFound;
}
boolean CPumpFinder::printdev(libusb_device *dev, boolean &usbFound, boolean
&accuChekFound) {
libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
return usbFound;
}
uint16 vID = (uint16)desc.idVendor;
uint16 pID = (uint16)desc.idProduct; //desc.iSerialNumber
//#define ACCUCHEK_GUIDE_VENDORID (uint16)0x173A
//#define ACCUCHEK_GUIDE_PRODUCTID (uint16)0x21D5
if((vID == 5946) & (pID == 8661))
{
accuPID = pID;
accuVID = vID;
//Do serial?
accuSerial = "Test";
write_text_to_log_file("we did it");
accuChekFound = TRUE;
usbFound = TRUE;
}
if(usbFound == TRUE)
{
libusb_config_descriptor *config;
libusb_get_config_descriptor(dev, 0, &config);
const libusb_interface *inter;
const libusb_interface_descriptor *interdesc;
const libusb_endpoint_descriptor *epdesc;
for(int i=0; i<(int)config->bNumInterfaces; i++) {
inter = &config->interface[i];
for(int j=0; j<inter->num_altsetting; j++) {
interdesc = &inter->altsetting[j];
for(int k=0; k<(int)interdesc->bNumEndpoints; k++) {
epdesc = &interdesc->endpoint[k];
}
}
}
libusb_free_config_descriptor(config);
if(accuChekFound == TRUE)
{
return accuChekFound;
}
return usbFound;
}
return usbFound;
}
I have a server. When I try to run the server with multi threading, it crashes. After that I found, I have to manually provide locking in openssl to avoid crashing. After providing static lock also, server gets crashing. I will attach my code here. can anyone say what's wrong in it?
Code:
BIO *bio_err = NULL;
static pthread_mutex_t *lock_cs;
static long *lock_count;
void pthreads_locking_callback(int mode, int type, const char *file,int line)
{
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(&(lock_cs[type]));
lock_count[type]++;
} else {
pthread_mutex_unlock(&(lock_cs[type]));
}
}
void pthreads_thread_id(CRYPTO_THREADID *tid)
{
unsigned long data = (unsigned long)pthread_self();
CRYPTO_THREADID_set_numeric(tid, (unsigned long)data);
}
void thread_setup(void)
{
int i;
lock_cs = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks()* sizeof(pthread_mutex_t));
lock_count = (long int *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
for (i = 0; i < CRYPTO_num_locks(); i++) {
lock_count[i] = 0;
pthread_mutex_init(&(lock_cs[i]), NULL);
}
CRYPTO_THREADID_set_callback(pthreads_thread_id);
CRYPTO_set_locking_callback(pthreads_locking_callback);
}
void thread_cleanup(void)
{
int i;
CRYPTO_set_locking_callback(NULL);
BIO_printf(bio_err, "cleanup\n");
for (i = 0; i < CRYPTO_num_locks(); i++) {
pthread_mutex_destroy(&(lock_cs[i]));
BIO_printf(bio_err, "%8ld:%s\n", lock_count[i], CRYPTO_get_lock_name(i));
}
OPENSSL_free(lock_cs);
OPENSSL_free(lock_count);
BIO_printf(bio_err, "done cleanup\n");
}
a question: Vendor says that for some encryption purpose uses PKCS#1 V2.1 OAEP with SHA-256... Is that even possible?
I have checked and re-checked openssl and all they have is RSA public key encrypt with OAEP padding which is supposed to be PKCS#1 V2.1 with SHA1
So what can I do? How can I use SHA256 in RSA PUBLIC KEY encryption?
IS it even possible?
Best regards,
EDITED: ANSWER HOW TO USE RSA ENCRYPTION USING OPENSSL OAEP PADDING AND SHA256 DIGEST
#include "openssl/rsa.h"
#include <openssl/err.h>
#define RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1 154
int RSA_padding_add_PKCS1_OAEP_mgf1_SHA256(unsigned char *to, int tlen,
const unsigned char *from, int flen,
const unsigned char *param, int plen,
const EVP_MD *md, const EVP_MD *mgf1md)
{
int i, emlen = tlen - 1;
unsigned char *db, *seed;
unsigned char *dbmask, seedmask[EVP_MAX_MD_SIZE];
int mdlen;
if (md == NULL)
md = EVP_sha256(); //HERE IS THE ACTUAL USE OF SHAR256 digest!
if (mgf1md == NULL)
mgf1md = md;
mdlen = EVP_MD_size(md);
if (flen > emlen - 2 * mdlen - 1)
{
RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1,
RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
return 0;
}
if (emlen < 2 * mdlen + 1)
{
RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1, RSA_R_KEY_SIZE_TOO_SMALL);
return 0;
}
to[0] = 0;
seed = to + 1;
db = to + mdlen + 1;
if (!EVP_Digest((void *)param, plen, db, NULL, md, NULL))
return 0;
memset(db + mdlen, 0,
emlen - flen - 2 * mdlen - 1);
db[emlen - flen - mdlen - 1] = 0x01;
memcpy(db + emlen - flen - mdlen, from, (unsigned int)flen);
if (RAND_bytes(seed, mdlen) <= 0)
return 0;
#ifdef PKCS_TESTVECT
memcpy(seed,
"\xaa\xfd\x12\xf6\x59\xca\xe6\x34\x89\xb4\x79\xe5\x07\x6d\xde\xc2\xf0\x6c\xb5\x8f",
20);
#endif
dbmask = (unsigned char*)OPENSSL_malloc(emlen - mdlen);
if (dbmask == NULL)
{
RSAerr(RSA_F_RSA_PADDING_ADD_PKCS1_OAEP_MGF1, ERR_R_MALLOC_FAILURE);
return 0;
}
if (PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md) < 0)
return 0;
for (i = 0; i < emlen - mdlen; i++)
db[i] ^= dbmask[i];
if (PKCS1_MGF1(seedmask, mdlen, db, emlen - mdlen, mgf1md) < 0)
return 0;
for (i = 0; i < mdlen; i++)
seed[i] ^= seedmask[i];
OPENSSL_free(dbmask);
return 1;
}
int RSA_padding_add_PKCS1_OAEP_SHA256(unsigned char *to, int tlen,
const unsigned char *from, int flen,
const unsigned char *param, int plen)
{
return RSA_padding_add_PKCS1_OAEP_mgf1_SHA256(to, tlen, from, flen,
param, plen, NULL, NULL);
}
static int RSA_eay_public_encrypt_SHA256(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding)
{
BIGNUM *f, *ret;
int i, j, k, num = 0, r = -1;
unsigned char *buf = NULL;
BN_CTX *ctx = NULL;
#ifdef OPENSSL_FIPS
if (FIPS_selftest_failed())
{
FIPSerr(FIPS_F_RSA_EAY_PUBLIC_ENCRYPT, FIPS_R_FIPS_SELFTEST_FAILED);
goto err;
}
if (FIPS_module_mode() && !(rsa->flags & RSA_FLAG_NON_FIPS_ALLOW)
&& (BN_num_bits(rsa->n) < OPENSSL_RSA_FIPS_MIN_MODULUS_BITS))
{
RSAerr(RSA_F_RSA_EAY_PUBLIC_ENCRYPT, RSA_R_KEY_SIZE_TOO_SMALL);
return -1;
}
#endif
if (BN_num_bits(rsa->n) > OPENSSL_RSA_MAX_MODULUS_BITS)
{
RSAerr(RSA_F_RSA_EAY_PUBLIC_ENCRYPT, RSA_R_MODULUS_TOO_LARGE);
return -1;
}
if (BN_ucmp(rsa->n, rsa->e) <= 0)
{
RSAerr(RSA_F_RSA_EAY_PUBLIC_ENCRYPT, RSA_R_BAD_E_VALUE);
return -1;
}
/* for large moduli, enforce exponent limit */
if (BN_num_bits(rsa->n) > OPENSSL_RSA_SMALL_MODULUS_BITS)
{
if (BN_num_bits(rsa->e) > OPENSSL_RSA_MAX_PUBEXP_BITS)
{
RSAerr(RSA_F_RSA_EAY_PUBLIC_ENCRYPT, RSA_R_BAD_E_VALUE);
return -1;
}
}
if ((ctx = BN_CTX_new()) == NULL) goto err;
BN_CTX_start(ctx);
f = BN_CTX_get(ctx);
ret = BN_CTX_get(ctx);
num = BN_num_bytes(rsa->n);
buf = (unsigned char*)OPENSSL_malloc(num);
if (!f || !ret || !buf)
{
RSAerr(RSA_F_RSA_EAY_PUBLIC_ENCRYPT, ERR_R_MALLOC_FAILURE);
goto err;
}
switch (padding)
{
case RSA_PKCS1_PADDING:
i = RSA_padding_add_PKCS1_type_2(buf, num, from, flen);
break;
#ifndef OPENSSL_NO_SHA
case RSA_PKCS1_OAEP_PADDING:
i = RSA_padding_add_PKCS1_OAEP_SHA256(buf, num, from, flen, NULL, 0);
break;
#endif
case RSA_SSLV23_PADDING:
i = RSA_padding_add_SSLv23(buf, num, from, flen);
break;
case RSA_NO_PADDING:
i = RSA_padding_add_none(buf, num, from, flen);
break;
default:
RSAerr(RSA_F_RSA_EAY_PUBLIC_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
goto err;
}
if (i <= 0) goto err;
if (BN_bin2bn(buf, num, f) == NULL) goto err;
if (BN_ucmp(f, rsa->n) >= 0)
{
/* usually the padding functions would catch this */
RSAerr(RSA_F_RSA_EAY_PUBLIC_ENCRYPT, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
goto err;
}
if (rsa->flags & RSA_FLAG_CACHE_PUBLIC)
if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, CRYPTO_LOCK_RSA, rsa->n, ctx))
goto err;
if (!rsa->meth->bn_mod_exp(ret, f, rsa->e, rsa->n, ctx,
rsa->_method_mod_n)) goto err;
/* put in leading 0 bytes if the number is less than the
* length of the modulus */
j = BN_num_bytes(ret);
i = BN_bn2bin(ret, &(to[num - j]));
for (k = 0; k<(num - i); k++)
to[k] = 0;
r = num;
err:
if (ctx != NULL)
{
BN_CTX_end(ctx);
BN_CTX_free(ctx);
}
if (buf != NULL)
{
OPENSSL_cleanse(buf, num);
OPENSSL_free(buf);
}
return(r);
}
int RSA_public_encrypt_sha256(int flen, const unsigned char *from, unsigned char *to,
RSA *rsa, int padding)
{
return(RSA_eay_public_encrypt_SHA256(flen, from, to, rsa, padding));
}
Just add these few functions and call RSA_public_encrypt_sha256 instead of RSA_public_encrypt and voila you have RSA_OAEP_SHA256
Well i know this is abusing the openssl code, but this is a solution if you cannot compile openssl lib yourself, like i cannot because i received this as a part of an ARM platform
All thanks go to JARIQ in the answer below!
Thank you!
I am not sure about the OpenSSL API but in PKCS#11 API when you are using RSA encryption with OAEP padding you can specify message digest algorithm and also a mask generation function as you can see in my code sample (take a look at _03_EncryptAndDecryptSinglePartOaepTest() method) . It is written in C# but I believe it should be easily understandable. However I have never tried anything else than SHA1.
More information can be found in RFC 3447 and PKCS#11 specification (chapter 12.1.7 and chapter 12.1.8).
EDIT for OpenSSL:
In OpenSSL RSA encryption with public key and OAEP padding is performed in this order:
you need to pass RSA_PKCS1_OAEP_PADDING flag to function RSA_public_encrypt() implemented in rsa_crpt.c
RSA_public_encrypt() then calls function RSA_eay_public_encrypt() implemented in rsa_eay.c (unless you are using some cryptographic hardware device via ENGINE)
RSA_eay_public_encrypt() then calls function RSA_padding_add_PKCS1_OAEP() implemented in rsa_oaep.c
This uses SHA1 which seems to be currently the only option implemented in OpenSSL but I believe it should be possible to slightly modify code in rsa_oaep.c file to achieve what you need.
I am new to openNI and Kinect as a beginner I tried implementing OpenNI SimpleViewer through the Tutorial in OpenNI.org at this link : http://openni.org/docs2/Tutorial/smpl_simple_view.html
I have implemented the code as described in this tutorial but I am still facing problem as The program is throwing exception and it is not showing any output.
I have linked all the libraries and installed the kinect properly. I have also included the OpenCV library with openNI.
Here is My C++ Code that I have Implemented :
int main()
{
rc = context.InitFromXmlFile(XML_PATH,&errors);
if(rc == XN_STATUS_NO_NODE_PRESENT)
{
XnChar strError[1024];
errors.ToString(strError,1024);
printf("%s\n",strError);
return(rc);
}
else if(rc != XN_STATUS_OK)
{
printf("Open failed : %s\n", xnGetStatusString(rc));
return(rc);
}
rc = context.FindExistingNode(XN_NODE_TYPE_DEPTH,depth);
if(rc != XN_STATUS_OK)
{
printf("No Depth Node Found. Check XML!\n");
return -1;
}
depth.GetMetaData(depthMD);
image.GetMetaData(imageMD);
if(imageMD.FullXRes() != depthMD.FullXRes() || imageMD.FullYRes() != depthMD.FullYRes())
{
printf("The device depth and image resolution must be equal\n");
return -1;
}
if(imageMD.PixelFormat() != XN_PIXEL_FORMAT_RGB24)
{
printf("The Device Image Format must be RGB24\n");
return -1;
}
unsigned short textMapX = (((unsigned short)(depthMD.FullXRes() - 1)/512) + 1 * 512);
unsigned short textMapY = (((unsigned short)(depthMD.FullYRes() - 1)/512) + 1 * 512);
XnRGB24Pixel* pTextMap = (XnRGB24Pixel*) malloc(textMapX * textMapY * sizeof(XnRGB24Pixel));
rc = context.WaitOneUpdateAll(depth);
depth.GetMetaData(depthMD);
image.GetMetaData(imageMD);
const XnDepthPixel* pDepth = depthMD.Data();
const XnUInt8* pImage = imageMD.Data();
unsigned int imageScale = GL_WIN_SIZE_X/depthMD.FullXRes();
xnOSMemSet(depthHist,0,MAX * sizeof(float));
unsigned int numberOfPoints = 0;
for(XnUInt y = 0; y < depthMD.YRes(); ++ y)
{
for(XnUInt x = 0; x < depthMD.XRes(); ++x, ++pDepth);
{
if(*pDepth != 0)
{
depthHist[*pDepth]++;
numberOfPoints++;
}
}
}
for(int index = 1; index < MAX; index++)
{
depthHist[index] += depthHist[index - 1];
}
if(numberOfPoints)
{
for(int index = 1; index < MAX; index++)
{
depthHist[index] = (unsigned int)(256 * (1.0f - (depthHist[index]/numberOfPoints)));
}
}
return 0;
}
I am also facing problem understanding the later part of the code.
i am having problems understanding how the audio part of the sdl library works
now, i know that when you initialize it, you have to specify the frequency and a >>callback<< function, which i think is then called automatically at the given frequency.
can anyone who worked with the sdl library write a simple example that would use sdl_audio to generate a 440 hz square wave (since it is the simplest waveform) at a sampling frequency of 44000 hz?
The Introduction to SDL (2011 cached version: 2) has got a neat example of using SDL Sound library that should get you started: http://www.libsdl.org/intro.en/usingsound.html
EDIT: Here is a working program that does what you asked for. I modified a bit the code found here: http://www.dgames.org/beep-sound-with-sdl/
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#include <queue>
#include <cmath>
const int AMPLITUDE = 28000;
const int FREQUENCY = 44100;
struct BeepObject
{
double freq;
int samplesLeft;
};
class Beeper
{
private:
double v;
std::queue<BeepObject> beeps;
public:
Beeper();
~Beeper();
void beep(double freq, int duration);
void generateSamples(Sint16 *stream, int length);
void wait();
};
void audio_callback(void*, Uint8*, int);
Beeper::Beeper()
{
SDL_AudioSpec desiredSpec;
desiredSpec.freq = FREQUENCY;
desiredSpec.format = AUDIO_S16SYS;
desiredSpec.channels = 1;
desiredSpec.samples = 2048;
desiredSpec.callback = audio_callback;
desiredSpec.userdata = this;
SDL_AudioSpec obtainedSpec;
// you might want to look for errors here
SDL_OpenAudio(&desiredSpec, &obtainedSpec);
// start play audio
SDL_PauseAudio(0);
}
Beeper::~Beeper()
{
SDL_CloseAudio();
}
void Beeper::generateSamples(Sint16 *stream, int length)
{
int i = 0;
while (i < length) {
if (beeps.empty()) {
while (i < length) {
stream[i] = 0;
i++;
}
return;
}
BeepObject& bo = beeps.front();
int samplesToDo = std::min(i + bo.samplesLeft, length);
bo.samplesLeft -= samplesToDo - i;
while (i < samplesToDo) {
stream[i] = AMPLITUDE * std::sin(v * 2 * M_PI / FREQUENCY);
i++;
v += bo.freq;
}
if (bo.samplesLeft == 0) {
beeps.pop();
}
}
}
void Beeper::beep(double freq, int duration)
{
BeepObject bo;
bo.freq = freq;
bo.samplesLeft = duration * FREQUENCY / 1000;
SDL_LockAudio();
beeps.push(bo);
SDL_UnlockAudio();
}
void Beeper::wait()
{
int size;
do {
SDL_Delay(20);
SDL_LockAudio();
size = beeps.size();
SDL_UnlockAudio();
} while (size > 0);
}
void audio_callback(void *_beeper, Uint8 *_stream, int _length)
{
Sint16 *stream = (Sint16*) _stream;
int length = _length / 2;
Beeper* beeper = (Beeper*) _beeper;
beeper->generateSamples(stream, length);
}
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_AUDIO);
int duration = 1000;
double Hz = 440;
Beeper b;
b.beep(Hz, duration);
b.wait();
return 0;
}
Good luck.
A boiled-down variant of the beeper-example, reduced to the bare minimum (with error-handling).
#include <math.h>
#include <SDL.h>
#include <SDL_audio.h>
const int AMPLITUDE = 28000;
const int SAMPLE_RATE = 44100;
void audio_callback(void *user_data, Uint8 *raw_buffer, int bytes)
{
Sint16 *buffer = (Sint16*)raw_buffer;
int length = bytes / 2; // 2 bytes per sample for AUDIO_S16SYS
int &sample_nr(*(int*)user_data);
for(int i = 0; i < length; i++, sample_nr++)
{
double time = (double)sample_nr / (double)SAMPLE_RATE;
buffer[i] = (Sint16)(AMPLITUDE * sin(2.0f * M_PI * 441.0f * time)); // render 441 HZ sine wave
}
}
int main(int argc, char *argv[])
{
if(SDL_Init(SDL_INIT_AUDIO) != 0) SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
int sample_nr = 0;
SDL_AudioSpec want;
want.freq = SAMPLE_RATE; // number of samples per second
want.format = AUDIO_S16SYS; // sample type (here: signed short i.e. 16 bit)
want.channels = 1; // only one channel
want.samples = 2048; // buffer-size
want.callback = audio_callback; // function SDL calls periodically to refill the buffer
want.userdata = &sample_nr; // counter, keeping track of current sample number
SDL_AudioSpec have;
if(SDL_OpenAudio(&want, &have) != 0) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
if(want.format != have.format) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
SDL_PauseAudio(0); // start playing sound
SDL_Delay(1000); // wait while sound is playing
SDL_PauseAudio(1); // stop playing sound
SDL_CloseAudio();
return 0;
}
SDL 2 C example
The following code produces a sinusoidal sound, it is adapted from: https://codereview.stackexchange.com/questions/41086/play-some-sine-waves-with-sdl2
main.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SDL2/SDL.h>
const double ChromaticRatio = 1.059463094359295264562;
const double Tao = 6.283185307179586476925;
Uint32 sampleRate = 48000;
Uint32 frameRate = 60;
Uint32 floatStreamLength = 1024;
Uint32 samplesPerFrame;
Uint32 msPerFrame;
double practicallySilent = 0.001;
Uint32 audioBufferLength = 48000;
float *audioBuffer;
SDL_atomic_t audioCallbackLeftOff;
Sint32 audioMainLeftOff;
Uint8 audioMainAccumulator;
SDL_AudioDeviceID AudioDevice;
SDL_AudioSpec audioSpec;
SDL_Event event;
SDL_bool running = SDL_TRUE;
typedef struct {
float *waveform;
Uint32 waveformLength;
double volume;
double pan;
double frequency;
double phase;
} voice;
void speak(voice *v) {
float sample;
Uint32 sourceIndex;
double phaseIncrement = v->frequency/sampleRate;
Uint32 i;
if (v->volume > practicallySilent) {
for (i = 0; (i + 1) < samplesPerFrame; i += 2) {
v->phase += phaseIncrement;
if (v->phase > 1)
v->phase -= 1;
sourceIndex = v->phase*v->waveformLength;
sample = v->waveform[sourceIndex]*v->volume;
audioBuffer[audioMainLeftOff+i] += sample*(1-v->pan);
audioBuffer[audioMainLeftOff+i+1] += sample*v->pan;
}
}
else {
for (i=0; i<samplesPerFrame; i+=1)
audioBuffer[audioMainLeftOff+i] = 0;
}
audioMainAccumulator++;
}
double getFrequency(double pitch) {
return pow(ChromaticRatio, pitch-57)*440;
}
int getWaveformLength(double pitch) {
return sampleRate / getFrequency(pitch)+0.5f;
}
void buildSineWave(float *data, Uint32 length) {
Uint32 i;
for (i=0; i < length; i++)
data[i] = sin(i*(Tao/length));
}
void logSpec(SDL_AudioSpec *as) {
printf(
" freq______%5d\n"
" format____%5d\n"
" channels__%5d\n"
" silence___%5d\n"
" samples___%5d\n"
" size______%5d\n\n",
(int) as->freq,
(int) as->format,
(int) as->channels,
(int) as->silence,
(int) as->samples,
(int) as->size
);
}
void logVoice(voice *v) {
printf(
" waveformLength__%d\n"
" volume__________%f\n"
" pan_____________%f\n"
" frequency_______%f\n"
" phase___________%f\n",
v->waveformLength,
v->volume,
v->pan,
v->frequency,
v->phase
);
}
void logWavedata(float *floatStream, Uint32 floatStreamLength, Uint32 increment) {
printf("\n\nwaveform data:\n\n");
Uint32 i=0;
for (i = 0; i < floatStreamLength; i += increment)
printf("%4d:%2.16f\n", i, floatStream[i]);
printf("\n\n");
}
void audioCallback(void *unused, Uint8 *byteStream, int byteStreamLength) {
float* floatStream = (float*) byteStream;
Sint32 localAudioCallbackLeftOff = SDL_AtomicGet(&audioCallbackLeftOff);
Uint32 i;
for (i = 0; i < floatStreamLength; i++) {
floatStream[i] = audioBuffer[localAudioCallbackLeftOff];
localAudioCallbackLeftOff++;
if (localAudioCallbackLeftOff == audioBufferLength)
localAudioCallbackLeftOff = 0;
}
SDL_AtomicSet(&audioCallbackLeftOff, localAudioCallbackLeftOff);
}
int init(void) {
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
SDL_AudioSpec want;
SDL_zero(want);
want.freq = sampleRate;
want.format = AUDIO_F32;
want.channels = 2;
want.samples = floatStreamLength;
want.callback = audioCallback;
AudioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &audioSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (AudioDevice == 0) {
printf("\nFailed to open audio: %s\n", SDL_GetError());
return 1;
}
printf("want:\n");
logSpec(&want);
printf("audioSpec:\n");
logSpec(&audioSpec);
if (audioSpec.format != want.format) {
printf("\nCouldn't get Float32 audio format.\n");
return 2;
}
sampleRate = audioSpec.freq;
floatStreamLength = audioSpec.size / 4;
samplesPerFrame = sampleRate / frameRate;
msPerFrame = 1000 / frameRate;
audioMainLeftOff = samplesPerFrame * 8;
SDL_AtomicSet(&audioCallbackLeftOff, 0);
if (audioBufferLength % samplesPerFrame)
audioBufferLength += samplesPerFrame - (audioBufferLength % samplesPerFrame);
audioBuffer = malloc(sizeof(float) * audioBufferLength);
return 0;
}
int onExit(void) {
SDL_CloseAudioDevice(AudioDevice);
SDL_Quit();
return 0;
}
int main(int argc, char *argv[]) {
float syncCompensationFactor = 0.0016;
Sint32 mainAudioLead;
Uint32 i;
voice testVoiceA;
voice testVoiceB;
voice testVoiceC;
testVoiceA.volume = 1;
testVoiceB.volume = 1;
testVoiceC.volume = 1;
testVoiceA.pan = 0.5;
testVoiceB.pan = 0;
testVoiceC.pan = 1;
testVoiceA.phase = 0;
testVoiceB.phase = 0;
testVoiceC.phase = 0;
testVoiceA.frequency = getFrequency(45);
testVoiceB.frequency = getFrequency(49);
testVoiceC.frequency = getFrequency(52);
Uint16 C0waveformLength = getWaveformLength(0);
testVoiceA.waveformLength = C0waveformLength;
testVoiceB.waveformLength = C0waveformLength;
testVoiceC.waveformLength = C0waveformLength;
float sineWave[C0waveformLength];
buildSineWave(sineWave, C0waveformLength);
testVoiceA.waveform = sineWave;
testVoiceB.waveform = sineWave;
testVoiceC.waveform = sineWave;
if (init())
return 1;
SDL_Delay(42);
SDL_PauseAudioDevice(AudioDevice, 0);
while (running) {
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
running = SDL_FALSE;
}
}
for (i = 0; i < samplesPerFrame; i++)
audioBuffer[audioMainLeftOff+i] = 0;
speak(&testVoiceA);
speak(&testVoiceB);
speak(&testVoiceC);
if (audioMainAccumulator > 1) {
for (i=0; i<samplesPerFrame; i++) {
audioBuffer[audioMainLeftOff+i] /= audioMainAccumulator;
}
}
audioMainAccumulator = 0;
audioMainLeftOff += samplesPerFrame;
if (audioMainLeftOff == audioBufferLength)
audioMainLeftOff = 0;
mainAudioLead = audioMainLeftOff - SDL_AtomicGet(&audioCallbackLeftOff);
if (mainAudioLead < 0)
mainAudioLead += audioBufferLength;
if (mainAudioLead < floatStreamLength)
printf("An audio collision may have occured!\n");
SDL_Delay(mainAudioLead * syncCompensationFactor);
}
onExit();
return 0;
}
Compile and run:
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -lSDL2 -lm
./main.out
Should be easy to turn this into a simple piano with: https://github.com/cirosantilli/cpp-cheat/blob/f734a2e76fbcfc67f707ae06be7a2a2ef5db47d1/c/interactive/audio_gen.c#L44
For wav manipulation, also check the official examples:
http://hg.libsdl.org/SDL/file/e12c38730512/test/testresample.c
http://hg.libsdl.org/SDL/file/e12c38730512/test/loopwave.c
Tested on Ubuntu 19.10, SDL 2.0.10.
This is a minimal example of how to play a sine wave in SDL2.
Make sure to call SDL_Init(SDL_INIT_AUDIO) before creating an instance of Sound.
Sound.h
#include <cstdint>
#include <SDL2/SDL.h>
class Sound
{
public:
Sound();
~Sound();
void play();
void stop();
const double m_sineFreq;
const double m_sampleFreq;
const double m_samplesPerSine;
uint32_t m_samplePos;
private:
static void SDLAudioCallback(void *data, Uint8 *buffer, int length);
SDL_AudioDeviceID m_device;
};
Sound.cpp
#include "Sound.h"
#include <cmath>
#include <iostream>
Sound::Sound()
: m_sineFreq(1000),
m_sampleFreq(44100),
m_samplesPerSine(m_sampleFreq / m_sineFreq),
m_samplePos(0)
{
SDL_AudioSpec wantSpec, haveSpec;
SDL_zero(wantSpec);
wantSpec.freq = m_sampleFreq;
wantSpec.format = AUDIO_U8;
wantSpec.channels = 1;
wantSpec.samples = 2048;
wantSpec.callback = SDLAudioCallback;
wantSpec.userdata = this;
m_device = SDL_OpenAudioDevice(NULL, 0, &wantSpec, &haveSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (m_device == 0)
{
std::cout << "Failed to open audio: " << SDL_GetError() << std::endl;
}
}
Sound::~Sound()
{
SDL_CloseAudioDevice(m_device);
}
void Sound::play()
{
SDL_PauseAudioDevice(m_device, 0);
}
void Sound::stop()
{
SDL_PauseAudioDevice(m_device, 1);
}
void Sound::SDLAudioCallback(void *data, Uint8 *buffer, int length)
{
Sound *sound = reinterpret_cast<Sound*>(data);
for(int i = 0; i < length; ++i)
{
buffer[i] = (std::sin(sound->m_samplePos / sound->m_samplesPerSine * M_PI * 2) + 1) * 127.5;
++sound->m_samplePos;
}
}