Hi there,
here is a brief update of my status.
So far multitouch input (4 points) and graphics are working. The only part I’m struggling with is the audio streaming support. I started by creating a new class called ofxQNXSoundStream and by looking on how this was implemented on other platforms.
As far as I see on the iPhone, Win/Mac (rtAudio) and Linux (portaudio), those implementations rely on a callback which periodically calls:
- soundOutputPtr->audioOut(pcm_buf, int bufferSize, int nChannels);
On Android however, there isnt really a callback. From what I see, a new thread is created on the Java part of the application which calls a some method over JNI that calls the method above.
Since rtAudio and portaudio unfortunately dont support the PlayBook/QNX architecture, the only option is to use the QNX Sound Architecture (QSA) which is in some way similar to ALSA.
In a standalone sandbox app, I am able to play some noise on my playbook (I just filled the buffer with random values just like the audioOutputExample). In the code samples below I’ve already started to import some of the code into 3 methods:
- int openQNXAudio();
- void updateQNXAudio();
- void closeQNXAudio();
On setup, it will call openQNXAudio(). This will take care of opening the audio device and setting up the parameters. So my only issue is how I would call updateQNXAudio() for every update loop? Is there some clean way to do it in oF? Or should I try to call it manually in testApp::update()
The source code so far:
ofxQNXSoundStream.h
class ofxQNXSoundStream : public ofBaseSoundStream {
public:
ofxQNXSoundStream();
virtual ~ofxQNXSoundStream();
void listDevices();
void setDeviceID(int deviceID);
bool setup(int outChannels, int inChannels, int sampleRate, int bufferSize, int nBuffers);
bool setup(ofBaseApp * app, int outChannels, int inChannels, int sampleRate, int bufferSize, int nBuffers);
void setInput(ofBaseSoundInput * soundInput);
void setOutput(ofBaseSoundOutput * soundOutput);
void start();
void stop();
void close();
long unsigned long getTickCount();
int getNumInputChannels();
int getNumOutputChannels();
int openQNXAudio();
void updateQNXAudio();
void closeQNXAudio();
private:
long unsigned long tickCount;
int nInputChannels;
int nOutputChannels;
int sampleRate;
// Playbook specific
snd_pcm_t *pcm_handle;
snd_mixer_t *mixer_handle;
};
ofxQNXSoundStream.cpp
#include "ofSoundStream.h"
#include "ofMath.h"
#include "ofUtils.h"
#include "ofxQNXSoundStream.h"
static bool isSetup = false;
static bool isRunning = false;
static ofBaseSoundInput * soundInputPtr = NULL;
static ofBaseSoundOutput * soundOutputPtr = NULL;
ofxQNXSoundStream::ofxQNXSoundStream()
{
fprintf(stderr, "ofxQNXSoundStream::ofxQNXSoundStream()\n");
}
ofxQNXSoundStream::~ofxQNXSoundStream()
{
fprintf(stderr, "ofxQNXSoundStream::~ofxQNXSoundStream()\n");
}
void ofxQNXSoundStream::listDevices()
{
fprintf(stderr, "ofxQNXSoundStream::listDevices()\n");
}
void ofxQNXSoundStream::setDeviceID(int deviceID)
{
fprintf(stderr, "ofxQNXSoundStream::setDeviceID()\n");
}
void ofxQNXSoundStream::setInput(ofBaseSoundInput * soundInput)
{
fprintf(stderr, "ofxQNXSoundStream::setInput(ofBaseSoundInput)\n");
soundInputPtr = soundInput;
}
void ofxQNXSoundStream::setOutput(ofBaseSoundOutput * soundOutput)
{
fprintf(stderr, "ofxQNXSoundStream::setInput(ofBaseSoundOutput)\n");
soundOutputPtr = soundOutput;
}
bool ofxQNXSoundStream::setup(int outChannels, int inChannels, int _sampleRate, int bufferSize, int nBuffers)
{
fprintf(stderr, "ofxQNXSoundStream::setup(outChannels %d, inChannels %d, sampleRate %d, bufferSize %d, nBuffers %d)\n", outChannels, inChannels, _sampleRate, bufferSize, nBuffers);
nInputChannels = inChannels;
nOutputChannels = outChannels;
tickCount = 0;
sampleRate = _sampleRate;
//nBuffers = 1;
// Setup QNX
openQNXAudio();
// TODO
// Setup callback?
// ?
if(isRunning) {
stop();
close();
}
isSetup = true;
ofSoundStreamStart();
}
bool ofxQNXSoundStream::setup(ofBaseApp * app, int outChannels, int inChannels, int sampleRate, int bufferSize, int nBuffers)
{
fprintf(stderr, "ofxQNXSoundStream::setup(ofBaseApp)\n");
setInput(app);
setOutput(app);
setup(outChannels, inChannels, sampleRate, bufferSize, nBuffers);
}
void ofxQNXSoundStream::start()
{
fprintf(stderr, "ofxQNXSoundStream::start\n");
// TODO
if(isRunning)
ofSoundStreamStop();
}
void ofxQNXSoundStream::stop()
{
fprintf(stderr, "ofxQNXSoundStream::stop\n");
// TODO
}
void ofxQNXSoundStream::close()
{
fprintf(stderr, "ofxQNXSoundStream::close\n");
// TODO
//closeQNXAudio();
}
long unsigned long ofxQNXSoundStream::getTickCount()
{
fprintf(stderr, "ofxQNXSoundStream::getTickCount\n");
return tickCount;
}
//------------------------------------------------------------------------------
int ofxQNXSoundStream::getNumInputChannels(){
return nInputChannels;
}
//------------------------------------------------------------------------------
int ofxQNXSoundStream::getNumOutputChannels(){
return nOutputChannels;
}
int ofxQNXSoundStream::openQNXAudio(){
fprintf(stderr, "ofxQNXSoundStream::openQNXAudio\n");
int rtn;
int card = -1;
int dev = 0;
snd_pcm_channel_info_t pi;
snd_pcm_channel_params_t pp;
snd_mixer_group_t group;
snd_pcm_channel_setup_t setup;
int fragsize = -1;
int num_frags = -1;
int bsize;
// Open PCM
if ((rtn = snd_pcm_open_preferred (&pcm_handle, &card, &dev, SND_PCM_OPEN_PLAYBACK)) < 0)
{
fprintf(stderr, "device open");
return -1;
}
// disabling mmap is not actually required in this example but it is included to
// demonstrate how it is used when it is required.
if ((rtn = snd_pcm_plugin_set_disable (pcm_handle, PLUGIN_DISABLE_MMAP)) < 0)
{
fprintf(stderr, "snd_pcm_plugin_set_disable failed: %s\n", snd_strerror (rtn));
return -1;
}
// get pcm info
memset (&pi, 0, sizeof (pi));
pi.channel = SND_PCM_CHANNEL_PLAYBACK;
if ((rtn = snd_pcm_plugin_info (pcm_handle, &pi)) < 0)
{
fprintf(stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror (rtn));
return -1;
}
// setup pcm channel
memset (&pp, 0, sizeof (pp));
pp.mode = SND_PCM_MODE_BLOCK;
pp.channel = SND_PCM_CHANNEL_PLAYBACK;
pp.start_mode = SND_PCM_START_FULL;
pp.stop_mode = SND_PCM_STOP_STOP;
pp.buf.block.frag_size = pi.max_fragment_size;
if (fragsize != -1)
{
pp.buf.block.frag_size = fragsize;
}
pp.buf.block.frags_max = num_frags;
pp.buf.block.frags_min = 1;
pp.format.interleave = 1;
pp.format.rate = sampleRate;
pp.format.voices = nOutputChannels;
pp.format.format = SND_PCM_SFMT_S16_LE; // Signed 16-bit Little Endian
strcpy (pp.sw_mixer_subchn_name, "Wave playback channel");
if ((rtn = snd_pcm_plugin_params (pcm_handle, &pp)) < 0)
{
fprintf(stderr, "snd_pcm_plugin_params failed: %s\n", snd_strerror (rtn));
return -1;
}
if ((rtn = snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) {
fprintf(stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rtn));
return -1;
}
memset (&setup, 0, sizeof (setup));
memset (&group, 0, sizeof (group));
setup.channel = SND_PCM_CHANNEL_PLAYBACK;
setup.mixer_gid = &group.gid;
if ((rtn = snd_pcm_plugin_setup (pcm_handle, &setup)) < 0)
{
fprintf(stderr, "snd_pcm_plugin_setup failed: %s\n", snd_strerror (rtn));
return -1;
}
if (group.gid.name[0] == 0)
{
fprintf(stderr, "Mixer Pcm Group [%s] Not Set \n", group.gid.name);
return -1;
}
if ((rtn = snd_mixer_open (&mixer_handle, card, setup.mixer_device)) < 0)
{
fprintf(stderr, "snd_mixer_open failed: %s\n", snd_strerror (rtn));
return -1;
}
fprintf(stderr, "Format %s \n", snd_pcm_get_format_name (setup.format.format));
fprintf(stderr, "Frag Size %d \n", setup.buf.block.frag_size);
fprintf(stderr, "Total Frags %d \n", setup.buf.block.frags);
fprintf(stderr, "Rate %d \n", setup.format.rate);
fprintf(stderr, "Voices %d \n", setup.format.voices);
fprintf(stderr, "Mixer Pcm Group [%s]\n", group.gid.name);
bsize = setup.buf.block.frag_size;
//pcm_buf = malloc (bsize);
// Still need to allocate PCM_BUFFER
}
void ofxQNXSoundStream::updateQNXAudio() {
fprintf(stderr, "ofxQNXSoundStream::updateQNXAudio\n");
// get floats from app
// soundOutputPtr->audioOut(pcm_buf, int bufferSize, int nChannels);
// write pcm_buf to to device
// snd_pcm_plugin_write (pcm_handle, pcm_buf, n);
}
void ofxQNXSoundStream::closeQNXAudio(){
fprintf(stderr, "ofxQNXSoundStream::closeQNXAudio\n");
snd_pcm_plugin_flush (pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
snd_mixer_close (mixer_handle);
snd_pcm_close (pcm_handle);
}
Output info
Format Signed 16-bit Little Endian
Frag Size 2820
Total Frags 85
Rate 44100
Voices 2
Mixer Pcm Group [Wave playback channel]