ofxPiMapper fixed for C++17 & oF 12.0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1076 lines
32 KiB

#include "ofOpenALSoundPlayer.h"
#ifdef OF_SOUND_PLAYER_OPENAL
#include "ofConstants.h"
#include "glm/gtc/constants.hpp"
#include "glm/common.hpp"
#include "ofLog.h"
#include "ofEvents.h"
#include <sndfile.h>
#if defined (TARGET_OF_IOS) || defined (TARGET_OSX)
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
#ifdef OF_USING_MPG123
#include <mpg123.h>
#endif
using namespace std;
static ALCdevice * alDevice = nullptr;
static ALCcontext * alContext = nullptr;
vector<float> ofOpenALSoundPlayer::window;
float ofOpenALSoundPlayer::windowSum = 0.f;
kiss_fftr_cfg ofOpenALSoundPlayer::systemFftCfg=0;
vector<float> ofOpenALSoundPlayer::systemWindowedSignal;
vector<float> ofOpenALSoundPlayer::systemBins;
vector<kiss_fft_cpx> ofOpenALSoundPlayer::systemCx_out;
static set<ofOpenALSoundPlayer*> & players(){
static set<ofOpenALSoundPlayer*> * players = new set<ofOpenALSoundPlayer*>;
return *players;
}
void ofOpenALSoundUpdate(){
alcProcessContext(alContext);
}
// ----------------------------------------------------------------------------
// from http://devmaster.net/posts/2893/openal-lesson-6-advanced-loading-and-error-handles
static string getALErrorString(ALenum error) {
switch(error) {
case AL_NO_ERROR:
return "AL_NO_ERROR";
case AL_INVALID_NAME:
return "AL_INVALID_NAME";
case AL_INVALID_ENUM:
return "AL_INVALID_ENUM";
case AL_INVALID_VALUE:
return "AL_INVALID_VALUE";
case AL_INVALID_OPERATION:
return "AL_INVALID_OPERATION";
case AL_OUT_OF_MEMORY:
return "AL_OUT_OF_MEMORY";
};
return "UNKWOWN_ERROR";
}
static string getALCErrorString(ALCenum error) {
switch(error) {
case ALC_NO_ERROR:
return "ALC_NO_ERROR";
case ALC_INVALID_DEVICE:
return "ALC_INVALID_DEVICE";
case ALC_INVALID_CONTEXT:
return "ALC_INVALID_CONTEXT";
case ALC_INVALID_ENUM:
return "ALC_INVALID_ENUM";
case ALC_INVALID_VALUE:
return "ALC_INVALID_VALUE";
case ALC_OUT_OF_MEMORY:
return "ALC_OUT_OF_MEMORY";
};
return "UNKWOWN_ERROR";
}
#ifdef OF_USING_MPG123
static string getMpg123EncodingString(int encoding) {
switch(encoding) {
case MPG123_ENC_16:
return "MPG123_ENC_16";
#if MPG123_API_VERSION>=36
case MPG123_ENC_24:
return "MPG123_ENC_24";
#endif
case MPG123_ENC_32:
return "MPG123_ENC_32";
case MPG123_ENC_8:
return "MPG123_ENC_8";
case MPG123_ENC_ALAW_8:
return "MPG123_ENC_ALAW_8";
case MPG123_ENC_FLOAT:
return "MPG123_ENC_FLOAT";
case MPG123_ENC_FLOAT_32:
return "MPG123_ENC_FLOAT_32";
case MPG123_ENC_FLOAT_64:
return "MPG123_ENC_FLOAT_64";
case MPG123_ENC_SIGNED:
return "MPG123_ENC_SIGNED";
case MPG123_ENC_SIGNED_16:
return "MPG123_ENC_SIGNED_16";
#if MPG123_API_VERSION>=36
case MPG123_ENC_SIGNED_24:
return "MPG123_ENC_SIGNED_24";
#endif
case MPG123_ENC_SIGNED_32:
return "MPG123_ENC_SIGNED_32";
case MPG123_ENC_SIGNED_8:
return "MPG123_ENC_SIGNED_8";
case MPG123_ENC_ULAW_8:
return "MPG123_ENC_ULAW_8";
case MPG123_ENC_UNSIGNED_16:
return "MPG123_ENC_UNSIGNED_16";
#if MPG123_API_VERSION>=36
case MPG123_ENC_UNSIGNED_24:
return "MPG123_ENC_UNSIGNED_24";
#endif
case MPG123_ENC_UNSIGNED_32:
return "MPG123_ENC_UNSIGNED_32";
case MPG123_ENC_UNSIGNED_8:
return "MPG123_ENC_UNSIGNED_8";
default:
return "MPG123_ENC_ANY";
}
}
#endif
#define BUFFER_STREAM_SIZE 4096
// now, the individual sound player:
//------------------------------------------------------------
ofOpenALSoundPlayer::ofOpenALSoundPlayer(){
bLoop = false;
bLoadedOk = false;
pan = 0.0f; // range for oF is -1 to 1,
volume = 1.0f;
internalFreq = 44100;
speed = 1;
bPaused = false;
isStreaming = false;
channels = 0;
duration = 0;
fftCfg = 0;
streamf = 0;
#ifdef OF_USING_MPG123
mp3streamf = 0;
#endif
players().insert(this);
}
// ----------------------------------------------------------------------------
ofOpenALSoundPlayer::~ofOpenALSoundPlayer(){
unload();
kiss_fftr_free(fftCfg);
players().erase(this);
if( players().empty() ){
close();
}
}
//---------------------------------------
// this should only be called once
void ofOpenALSoundPlayer::initialize(){
if( !alDevice ){
alDevice = alcOpenDevice( nullptr );
if( !alDevice ){
ofLogError("ofOpenALSoundPlayer") << "initialize(): couldn't open OpenAL default device";
return;
}else{
ofLogVerbose("ofOpenALSoundPlayer") << "initialize(): opening "<< alcGetString( alDevice, ALC_DEVICE_SPECIFIER );
}
// Create OpenAL context and make it current. If fails, close the OpenAL device that was just opened.
alContext = alcCreateContext( alDevice, nullptr );
if( !alContext ){
ALCenum err = alcGetError( alDevice );
ofLogError("ofOpenALSoundPlayer") << "initialize(): couldn't not create OpenAL context : "<< getALCErrorString( err );
close();
return;
}
if( alcMakeContextCurrent( alContext )==ALC_FALSE ){
ALCenum err = alcGetError( alDevice );
ofLogError("ofOpenALSoundPlayer") << "initialize(): couldn't not make current the create OpenAL context : "<< getALCErrorString( err );
close();
return;
};
alListener3f( AL_POSITION, 0,0,0 );
#ifdef OF_USING_MPG123
mpg123_init();
#endif
}
ofLogVerbose("ofOpenALSoundPlayer") << "initialize(): Done";
}
//---------------------------------------
void ofOpenALSoundPlayer::createWindow(int size){
if(int(window.size())!=size){
windowSum = 0;
window.resize(size);
// hanning window
for(int i = 0; i < size; i++){
window[i] = .54 - .46 * cos((glm::two_pi<float>() * i) / (size - 1));
windowSum += window[i];
}
}
}
//---------------------------------------
void ofOpenALSoundPlayer::close(){
// Destroy the OpenAL context (if any) before closing the device
if( alDevice ){
if( alContext ){
#ifdef OF_USING_MPG123
mpg123_exit();
#endif
alcMakeContextCurrent(nullptr);
alcDestroyContext(alContext);
alContext = nullptr;
}
if( alcCloseDevice( alDevice )==ALC_FALSE ){
ofLogNotice("ofOpenALSoundPlayer") << "initialize(): error closing OpenAL device.";
}
alDevice = nullptr;
}
}
// ----------------------------------------------------------------------------
bool ofOpenALSoundPlayer::sfReadFile(const std::filesystem::path& path, vector<short> & buffer, vector<float> & fftAuxBuffer){
SF_INFO sfInfo;
SNDFILE* f = sf_open(path.string().c_str(),SFM_READ,&sfInfo);
if(!f){
ofLogError("ofOpenALSoundPlayer") << "sfReadFile(): couldn't read \"" << path << "\"";
return false;
}
buffer.resize(sfInfo.frames*sfInfo.channels);
fftAuxBuffer.resize(sfInfo.frames*sfInfo.channels);
int subformat = sfInfo.format & SF_FORMAT_SUBMASK ;
if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE){
double scale ;
sf_command (f, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ;
if (scale < 1e-10)
scale = 1.0 ;
else
scale = 32700.0 / scale ;
sf_count_t samples_read = sf_read_float (f, &fftAuxBuffer[0], fftAuxBuffer.size());
if(samples_read<(int)fftAuxBuffer.size()){
ofLogWarning("ofOpenALSoundPlayer") << "sfReadFile(): read " << samples_read << " float samples, expected "
<< fftAuxBuffer.size() << " for \"" << path << "\"";
}
for (int i = 0 ; i < int(fftAuxBuffer.size()) ; i++){
fftAuxBuffer[i] *= scale ;
buffer[i] = 32565.0 * fftAuxBuffer[i];
}
}else{
sf_count_t frames_read = sf_readf_short(f,&buffer[0],sfInfo.frames);
if(frames_read<sfInfo.frames){
ofLogError("ofOpenALSoundPlayer") << "sfReadFile(): read " << frames_read << " frames from buffer, expected "
<< sfInfo.frames << " for \"" << path << "\"";
return false;
}
sf_seek(f,0,SEEK_SET);
frames_read = sf_readf_float(f,&fftAuxBuffer[0],sfInfo.frames);
if(frames_read<sfInfo.frames){
ofLogError("ofOpenALSoundPlayer") << "sfReadFile(): read " << frames_read << " frames from fft buffer, expected "
<< sfInfo.frames << " for \"" << path << "\"";
return false;
}
}
sf_close(f);
channels = sfInfo.channels;
duration = float(sfInfo.frames) / float(sfInfo.samplerate);
samplerate = sfInfo.samplerate;
return true;
}
#ifdef OF_USING_MPG123
//------------------------------------------------------------
bool ofOpenALSoundPlayer::mpg123ReadFile(const std::filesystem::path& path,vector<short> & buffer,vector<float> & fftAuxBuffer){
int err = MPG123_OK;
mpg123_handle * f = mpg123_new(nullptr,&err);
if(mpg123_open(f,path.string().c_str())!=MPG123_OK){
ofLogError("ofOpenALSoundPlayer") << "mpg123ReadFile(): couldn't read \"" << path << "\"";
return false;
}
mpg123_enc_enum encoding;
long int rate;
mpg123_getformat(f,&rate,&channels,(int*)&encoding);
if(encoding!=MPG123_ENC_SIGNED_16){
ofLogError("ofOpenALSoundPlayer") << "mpg123ReadFile(): " << getMpg123EncodingString(encoding)
<< " encoding for \"" << path << "\"" << " unsupported, expecting MPG123_ENC_SIGNED_16";
return false;
}
samplerate = rate;
size_t done=0;
size_t buffer_size = mpg123_outblock( f );
buffer.resize(buffer_size/2);
while(mpg123_read(f,(unsigned char*)&buffer[buffer.size()-buffer_size/2],buffer_size,&done)!=MPG123_DONE){
buffer.resize(buffer.size()+buffer_size/2);
};
buffer.resize(buffer.size()-(buffer_size/2-done/2));
mpg123_close(f);
mpg123_delete(f);
fftAuxBuffer.resize(buffer.size());
for(int i=0;i<(int)buffer.size();i++){
fftAuxBuffer[i] = float(buffer[i])/32565.f;
}
duration = float(buffer.size()/channels) / float(samplerate);
return true;
}
#endif
//------------------------------------------------------------
bool ofOpenALSoundPlayer::sfStream(const std::filesystem::path& path,vector<short> & buffer,vector<float> & fftAuxBuffer){
if(!streamf){
SF_INFO sfInfo;
streamf = sf_open(path.string().c_str(),SFM_READ,&sfInfo);
if(!streamf){
ofLogError("ofOpenALSoundPlayer") << "sfStream(): couldn't read \"" << path << "\"";
return false;
}
stream_subformat = sfInfo.format & SF_FORMAT_SUBMASK ;
if (stream_subformat == SF_FORMAT_FLOAT || stream_subformat == SF_FORMAT_DOUBLE){
sf_command (streamf, SFC_CALC_SIGNAL_MAX, &stream_scale, sizeof (stream_scale)) ;
if (stream_scale < 1e-10)
stream_scale = 1.0 ;
else
stream_scale = 32700.0 / stream_scale ;
}
channels = sfInfo.channels;
duration = float(sfInfo.frames) / float(sfInfo.samplerate);
samplerate = sfInfo.samplerate;
stream_samples_read = 0;
}
int curr_buffer_size = BUFFER_STREAM_SIZE*channels;
if(speed>1) curr_buffer_size *= (int)round(speed);
buffer.resize(curr_buffer_size);
fftAuxBuffer.resize(buffer.size());
if (stream_subformat == SF_FORMAT_FLOAT || stream_subformat == SF_FORMAT_DOUBLE){
sf_count_t samples_read = sf_read_float (streamf, &fftAuxBuffer[0], fftAuxBuffer.size());
stream_samples_read += samples_read;
if(samples_read<(int)fftAuxBuffer.size()){
fftAuxBuffer.resize(samples_read);
buffer.resize(samples_read);
setPosition(0);
if(!bLoop) stopThread();
stream_samples_read = 0;
stream_end = true;
}
for (int i = 0 ; i < int(fftAuxBuffer.size()) ; i++){
fftAuxBuffer[i] *= stream_scale ;
buffer[i] = 32565.0 * fftAuxBuffer[i];
}
}else{
sf_count_t frames_read = sf_readf_short(streamf,&buffer[0],curr_buffer_size/channels);
stream_samples_read += frames_read*channels;
if(frames_read<curr_buffer_size/channels){
fftAuxBuffer.resize(frames_read*channels);
buffer.resize(frames_read*channels);
setPosition(0);
if(!bLoop) stopThread();
stream_samples_read = 0;
stream_end = true;
}
for(int i=0;i<(int)buffer.size();i++){
fftAuxBuffer[i]=float(buffer[i])/32565.0f;
}
}
return true;
}
#ifdef OF_USING_MPG123
//------------------------------------------------------------
bool ofOpenALSoundPlayer::mpg123Stream(const std::filesystem::path& path,vector<short> & buffer,vector<float> & fftAuxBuffer){
if(!mp3streamf){
int err = MPG123_OK;
mp3streamf = mpg123_new(nullptr,&err);
if(mpg123_open(mp3streamf,path.string().c_str())!=MPG123_OK){
mpg123_close(mp3streamf);
mpg123_delete(mp3streamf);
ofLogError("ofOpenALSoundPlayer") << "mpg123Stream(): couldn't read \"" << path << "\"";
return false;
}
long int rate;
mpg123_getformat(mp3streamf,&rate,&channels,(int*)&stream_encoding);
if(stream_encoding!=MPG123_ENC_SIGNED_16){
ofLogError("ofOpenALSoundPlayer") << "mpg123Stream(): " << getMpg123EncodingString(stream_encoding)
<< " encoding for \"" << path << "\"" << " unsupported, expecting MPG123_ENC_SIGNED_16";
return false;
}
samplerate = rate;
mp3_buffer_size = mpg123_outblock( mp3streamf );
mpg123_seek(mp3streamf,0,SEEK_END);
off_t samples = mpg123_tell(mp3streamf);
duration = float(samples/channels) / float(samplerate);
mpg123_seek(mp3streamf,0,SEEK_SET);
}
int curr_buffer_size = mp3_buffer_size;
if(speed>1) curr_buffer_size *= (int)round(speed);
buffer.resize(curr_buffer_size);
fftAuxBuffer.resize(buffer.size());
size_t done=0;
if(mpg123_read(mp3streamf,(unsigned char*)&buffer[0],curr_buffer_size*2,&done)==MPG123_DONE){
setPosition(0);
buffer.resize(done/2);
fftAuxBuffer.resize(done/2);
if(!bLoop) stopThread();
stream_end = true;
}
for(int i=0;i<(int)buffer.size();i++){
fftAuxBuffer[i] = float(buffer[i])/32565.f;
}
return true;
}
#endif
//------------------------------------------------------------
bool ofOpenALSoundPlayer::stream(const std::filesystem::path& fileName, vector<short> & buffer){
#ifdef OF_USING_MPG123
if(ofFilePath::getFileExt(fileName)=="mp3" || ofFilePath::getFileExt(fileName)=="MP3" || mp3streamf){
if(!mpg123Stream(fileName,buffer,fftAuxBuffer)) return false;
}else
#endif
if(!sfStream(fileName,buffer,fftAuxBuffer)) return false;
fftBuffers.resize(channels);
int numFrames = buffer.size()/channels;
for(int i=0;i<channels;i++){
fftBuffers[i].resize(numFrames);
for(int j=0;j<numFrames;j++){
fftBuffers[i][j] = fftAuxBuffer[j*channels+i];
}
}
return true;
}
bool ofOpenALSoundPlayer::readFile(const std::filesystem::path& fileName, vector<short> & buffer){
#ifdef OF_USING_MPG123
if(ofFilePath::getFileExt(fileName)!="mp3" && ofFilePath::getFileExt(fileName)!="MP3"){
if(!sfReadFile(fileName,buffer,fftAuxBuffer)) return false;
}else{
if(!mpg123ReadFile(fileName,buffer,fftAuxBuffer)) return false;
}
#else
if(!sfReadFile(fileName,buffer,fftAuxBuffer)) return false;
#endif
fftBuffers.resize(channels);
int numFrames = buffer.size()/channels;
for(int i=0;i<channels;i++){
fftBuffers[i].resize(numFrames);
for(int j=0;j<numFrames;j++){
fftBuffers[i][j] = fftAuxBuffer[j*channels+i];
}
}
return true;
}
//------------------------------------------------------------
bool ofOpenALSoundPlayer::load(const std::filesystem::path& _fileName, bool is_stream){
std::filesystem::path fileName = ofToDataPath(_fileName);
bMultiPlay = false;
isStreaming = is_stream;
int err = AL_NO_ERROR;
// [1] init sound systems, if necessary
initialize();
// [2] try to unload any previously loaded sounds
// & prevent user-created memory leaks
// if they call "loadSound" repeatedly, for example
unload();
ALenum format=AL_FORMAT_MONO16;
bLoadedOk = false;
if(!isStreaming){
readFile(fileName, buffer);
}else{
stream(fileName, buffer);
}
int numFrames = buffer.size()/channels;
if(isStreaming){
buffers.resize(channels*2);
}else{
buffers.resize(channels);
}
alGenBuffers(buffers.size(), &buffers[0]);
if(channels==1){
sources.resize(1);
alGetError(); // Clear error.
alGenSources(1, &sources[0]);
err = alGetError();
if (err != AL_NO_ERROR){
ofLogError("ofOpenALSoundPlayer") << "loadSound(): couldn't generate source for \"" << fileName << "\": "
<< (int) err << " " << getALErrorString(err);
return false;
}
for(int i=0; i<(int)buffers.size(); i++){
alGetError(); // Clear error.
alBufferData(buffers[i],format,&buffer[0],buffer.size()*2,samplerate);
err = alGetError();
if (err != AL_NO_ERROR){
ofLogError("ofOpenALSoundPlayer:") << "loadSound(): couldn't create buffer for \"" << fileName << "\": "
<< (int) err << " " << getALErrorString(err);
return false;
}
if(isStreaming){
stream(fileName,buffer);
}
}
if(isStreaming){
alSourceQueueBuffers(sources[0],buffers.size(),&buffers[0]);
}else{
alSourcei (sources[0], AL_BUFFER, buffers[0]);
}
alSourcef (sources[0], AL_PITCH, 1.0f);
alSourcef (sources[0], AL_GAIN, 1.0f);
alSourcef (sources[0], AL_ROLLOFF_FACTOR, 0.0);
alSourcei (sources[0], AL_SOURCE_RELATIVE, AL_TRUE);
}else{
vector<vector<short> > multibuffer;
multibuffer.resize(channels);
sources.resize(channels);
alGenSources(channels, &sources[0]);
if(isStreaming){
for(int s=0; s<2;s++){
for(int i=0;i<channels;i++){
multibuffer[i].resize(buffer.size()/channels);
for(int j=0;j<numFrames;j++){
multibuffer[i][j] = buffer[j*channels+i];
}
alGetError(); // Clear error.
alBufferData(buffers[s*2+i],format,&multibuffer[i][0],buffer.size()/channels*2,samplerate);
err = alGetError();
if ( err != AL_NO_ERROR){
ofLogError("ofOpenALSoundPlayer") << "loadSound(): couldn't create stereo buffers for \"" << fileName << "\": " << (int) err << " " << getALErrorString(err);
return false;
}
alSourceQueueBuffers(sources[i],1,&buffers[s*2+i]);
stream(fileName,buffer);
}
}
}else{
for(int i=0;i<channels;i++){
multibuffer[i].resize(buffer.size()/channels);
for(int j=0;j<numFrames;j++){
multibuffer[i][j] = buffer[j*channels+i];
}
alGetError(); // Clear error.
alBufferData(buffers[i],format,&multibuffer[i][0],buffer.size()/channels*2,samplerate);
err = alGetError();
if (err != AL_NO_ERROR){
ofLogError("ofOpenALSoundPlayer") << "loadSound(): couldn't create stereo buffers for \"" << fileName << "\": "
<< (int) err << " " << getALErrorString(err);
return false;
}
alSourcei (sources[i], AL_BUFFER, buffers[i] );
}
}
for(int i=0;i<channels;i++){
err = alGetError();
if (err != AL_NO_ERROR){
ofLogError("ofOpenALSoundPlayer") << "loadSound(): couldn't create stereo sources for \"" << fileName << "\": "
<< (int) err << " " << getALErrorString(err);
return false;
}
// only stereo panning
if(i==0){
float pos[3] = {-1,0,0};
alSourcefv(sources[i],AL_POSITION,pos);
}else{
float pos[3] = {1,0,0};
alSourcefv(sources[i],AL_POSITION,pos);
}
alSourcef (sources[i], AL_ROLLOFF_FACTOR, 0.0);
alSourcei (sources[i], AL_SOURCE_RELATIVE, AL_TRUE);
}
}
bLoadedOk = true;
return bLoadedOk;
}
//------------------------------------------------------------
bool ofOpenALSoundPlayer::isLoaded() const{
return bLoadedOk;
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::threadedFunction(){
vector<vector<short> > multibuffer;
multibuffer.resize(channels);
while(isThreadRunning()){
std::unique_lock<std::mutex> lock(mutex);
for(int i=0; i<int(sources.size())/channels; i++){
int processed;
alGetSourcei(sources[i*channels], AL_BUFFERS_PROCESSED, &processed);
while(processed--)
{
stream("",buffer);
int numFrames = buffer.size()/channels;
if(channels>1){
for(int j=0;j<channels;j++){
multibuffer[j].resize(buffer.size()/channels);
for(int k=0;k<numFrames;k++){
multibuffer[j][k] = buffer[k*channels+j];
}
ALuint albuffer;
alSourceUnqueueBuffers(sources[i*channels+j], 1, &albuffer);
alBufferData(albuffer,AL_FORMAT_MONO16,&multibuffer[j][0],buffer.size()*2/channels,samplerate);
alSourceQueueBuffers(sources[i*channels+j], 1, &albuffer);
}
}else{
ALuint albuffer;
alSourceUnqueueBuffers(sources[i], 1, &albuffer);
alBufferData(albuffer,AL_FORMAT_MONO16,&buffer[0],buffer.size()*2/channels,samplerate);
alSourceQueueBuffers(sources[i], 1, &albuffer);
}
if(stream_end){
break;
}
}
ALint state;
alGetSourcei(sources[i*channels],AL_SOURCE_STATE,&state);
bool stream_running=false;
#ifdef OF_USING_MPG123
stream_running = streamf || mp3streamf;
#else
stream_running = streamf;
#endif
if(state != AL_PLAYING && stream_running && !stream_end){
alSourcePlayv(channels,&sources[i*channels]);
}
stream_end = false;
}
sleep(1);
}
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::update(ofEventArgs & args){
for(int i=1; i<int(sources.size())/channels; ){
ALint state;
alGetSourcei(sources[i*channels],AL_SOURCE_STATE,&state);
if(state != AL_PLAYING){
alDeleteSources(channels,&sources[i*channels]);
for(int j=0;j<channels;j++){
sources.erase(sources.begin()+i*channels);
}
}else{
i++;
}
}
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::unload(){
stop();
ofRemoveListener(ofEvents().update,this,&ofOpenALSoundPlayer::update);
// Only lock the thread where necessary.
{
std::unique_lock<std::mutex> lock(mutex);
// Delete sources before buffers.
alDeleteSources(sources.size(),&sources[0]);
alDeleteBuffers(buffers.size(),&buffers[0]);
sources.clear();
buffers.clear();
}
// Free resources and close file descriptors.
#ifdef OF_USING_MPG123
if(mp3streamf){
mpg123_close(mp3streamf);
mpg123_delete(mp3streamf);
}
mp3streamf = 0;
#endif
if(streamf){
sf_close(streamf);
}
streamf = 0;
bLoadedOk = false;
}
//------------------------------------------------------------
bool ofOpenALSoundPlayer::isPlaying() const{
if(sources.empty()) return false;
if(isStreaming) return isThreadRunning();
ALint state;
bool playing=false;
for(int i=0;i<(int)sources.size();i++){
alGetSourcei(sources[i],AL_SOURCE_STATE,&state);
playing |= (state == AL_PLAYING);
}
return playing;
}
//------------------------------------------------------------
bool ofOpenALSoundPlayer::isPaused() const{
if(sources.empty()) return false;
ALint state;
bool paused=true;
for(int i=0;i<(int)sources.size();i++){
alGetSourcei(sources[i],AL_SOURCE_STATE,&state);
paused &= (state == AL_PAUSED);
}
return paused;
}
//------------------------------------------------------------
float ofOpenALSoundPlayer::getSpeed() const{
return speed;
}
//------------------------------------------------------------
float ofOpenALSoundPlayer::getPan() const{
return pan;
}
//------------------------------------------------------------
float ofOpenALSoundPlayer::getVolume() const{
return volume;
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setVolume(float vol){
volume = vol;
if(sources.empty()) return;
if(channels==1){
alSourcef (sources[sources.size()-1], AL_GAIN, vol);
}else{
setPan(pan);
}
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setPosition(float pct){
setPositionMS(duration*pct*1000.f);
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setPositionMS(int ms){
if(sources.empty()) return;
#ifdef OF_USING_MPG123
if(mp3streamf){
mpg123_seek(mp3streamf,float(ms)/1000.f*samplerate,SEEK_SET);
}else
#endif
if(streamf){
stream_samples_read = sf_seek(streamf,float(ms)/1000.f*samplerate,SEEK_SET) * channels;
}else{
for(int i=0;i<(int)channels;i++){
alSourcef(sources[sources.size()-channels+i],AL_SEC_OFFSET,float(ms)/1000.f);
}
}
}
//------------------------------------------------------------
float ofOpenALSoundPlayer::getPosition() const{
if(duration==0 || sources.empty())
return 0;
else
return getPositionMS()/(1000.f*duration);
}
//------------------------------------------------------------
int ofOpenALSoundPlayer::getPositionMS() const{
if(sources.empty()) return 0;
float pos;
#ifdef OF_USING_MPG123
if(mp3streamf){
pos = float(mpg123_tell(mp3streamf)) / float(samplerate);
}else
#endif
if(streamf){
pos = float(stream_samples_read) / float(channels) / float(samplerate);
}else{
alGetSourcef(sources[sources.size()-1],AL_SEC_OFFSET,&pos);
}
return pos * 1000.f;
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setPan(float p){
if(sources.empty()) return;
p = glm::clamp(p, -1.f, 1.f);
pan = p;
if(channels==1){
float pos[3] = {p,0,0};
alSourcefv(sources[sources.size()-1],AL_POSITION,pos);
}else{
// calculates left/right volumes from pan-value (constant panning law)
// see: Curtis Roads: Computer Music Tutorial p 460
// thanks to jasch
float angle = p * 0.7853981633974483f; // in radians from -45. to +45.
float cosAngle = cos(angle);
float sinAngle = sin(angle);
float leftVol = (cosAngle - sinAngle) * 0.7071067811865475; // multiplied by sqrt(2)/2
float rightVol = (cosAngle + sinAngle) * 0.7071067811865475; // multiplied by sqrt(2)/2
for(int i=0;i<(int)channels;i++){
if(i==0){
alSourcef(sources[sources.size()-channels+i],AL_GAIN,leftVol*volume);
}else{
alSourcef(sources[sources.size()-channels+i],AL_GAIN,rightVol*volume);
}
}
}
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setPaused(bool bP){
if(sources.empty()) return;
std::unique_lock<std::mutex> lock(mutex);
if(bP){
alSourcePausev(sources.size(),&sources[0]);
if(isStreaming){
stopThread();
}
}else{
alSourcePlayv(sources.size(),&sources[0]);
if(isStreaming){
startThread();
}
}
bPaused = bP;
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setSpeed(float spd){
for(int i=0;i<channels;i++){
alSourcef(sources[sources.size()-channels+i],AL_PITCH,spd);
}
speed = spd;
}
//------------------------------------------------------------
void ofOpenALSoundPlayer::setLoop(bool bLp){
if(bMultiPlay) return; // no looping on multiplay
bLoop = bLp;
if(isStreaming) return;
for(int i=0;i<(int)sources.size();i++){
alSourcei(sources[i],AL_LOOPING,bLp?AL_TRUE:AL_FALSE);
}
}
// ----------------------------------------------------------------------------
void ofOpenALSoundPlayer::setMultiPlay(bool bMp){
if(isStreaming && bMp){
ofLogWarning("ofOpenALSoundPlayer") << "setMultiPlay(): sorry, no support for multiplay streams";
return;
}
bMultiPlay = bMp; // be careful with this...
if(sources.empty()) return;
if(bMultiPlay){
ofAddListener(ofEvents().update,this,&ofOpenALSoundPlayer::update);
}else{
ofRemoveListener(ofEvents().update,this,&ofOpenALSoundPlayer::update);
}
}
// ----------------------------------------------------------------------------
void ofOpenALSoundPlayer::play(){
std::unique_lock<std::mutex> lock(mutex);
int err = alGetError();
// if the sound is set to multiplay, then create new sources,
// do not multiplay on loop or we won't be able to stop it
if (bMultiPlay && !bLoop){
sources.resize(sources.size()+channels);
alGetError(); // Clear error.
alGenSources(channels, &sources[sources.size()-channels]);
err = alGetError();
if (err != AL_NO_ERROR){
ofLogError("ofOpenALSoundPlayer") << "play(): couldn't create multiplay stereo sources: "
<< (int) err << " " << getALErrorString(err);
return;
}
for(int i=0;i<channels;i++){
alSourcei (sources[sources.size()-channels+i], AL_BUFFER, buffers[i] );
// only stereo panning
if(i==0){
float pos[3] = {-1,0,0};
alSourcefv(sources[sources.size()-channels+i],AL_POSITION,pos);
}else{
float pos[3] = {1,0,0};
alSourcefv(sources[sources.size()-channels+i],AL_POSITION,pos);
}
alSourcef (sources[sources.size()-channels+i], AL_ROLLOFF_FACTOR, 0.0);
alSourcei (sources[sources.size()-channels+i], AL_SOURCE_RELATIVE, AL_TRUE);
}
err = glGetError();
if (err != AL_NO_ERROR){
ofLogError("ofOpenALSoundPlayer") << "play(): couldn't assign multiplay buffers: "
<< (int) err << " " << getALErrorString(err);
return;
}
}
alSourcePlayv(channels,&sources[sources.size()-channels]);
if(bMultiPlay){
ofAddListener(ofEvents().update,this,&ofOpenALSoundPlayer::update);
}
if(isStreaming){
setPosition(0);
stream_end = false;
startThread();
}
}
// ----------------------------------------------------------------------------
void ofOpenALSoundPlayer::stop(){
if(sources.empty()) return;
std::unique_lock<std::mutex> lock(mutex);
alSourceStopv(channels,&sources[sources.size()-channels]);
if(isStreaming){
setPosition(0);
stopThread();
}
}
// ----------------------------------------------------------------------------
void ofOpenALSoundPlayer::initFFT(int bands){
if(int(bins.size())==bands) return;
int signalSize = (bands-1)*2;
if(fftCfg!=0) kiss_fftr_free(fftCfg);
fftCfg = kiss_fftr_alloc(signalSize, 0, nullptr, nullptr);
cx_out.resize(bands);
bins.resize(bands);
createWindow(signalSize);
}
// ----------------------------------------------------------------------------
void ofOpenALSoundPlayer::initSystemFFT(int bands){
if(int(systemBins.size())==bands) return;
int signalSize = (bands-1)*2;
if(systemFftCfg!=0) kiss_fftr_free(systemFftCfg);
systemFftCfg = kiss_fftr_alloc(signalSize, 0, nullptr, nullptr);
systemCx_out.resize(bands);
systemBins.resize(bands);
createWindow(signalSize);
}
float * ofOpenALSoundPlayer::getCurrentBufferSum(int size){
if(int(windowedSignal.size())!=size){
windowedSignal.resize(size);
}
windowedSignal.assign(windowedSignal.size(),0);
for(int k=0;k<int(sources.size())/channels;k++){
if(!isStreaming){
ALint state;
alGetSourcei(sources[k*channels],AL_SOURCE_STATE,&state);
if( state != AL_PLAYING ) continue;
}
int pos;
alGetSourcei(sources[k*channels],AL_SAMPLE_OFFSET,&pos);
//if(pos+size>=(int)fftBuffers[0].size()) continue;
for(int i=0;i<channels;i++){
float gain;
alGetSourcef(sources[k*channels+i],AL_GAIN,&gain);
for(int j=0;j<size;j++){
if(pos+j<(int)fftBuffers[i].size())
windowedSignal[j]+=fftBuffers[i][pos+j]*gain;
else
windowedSignal[j]=0;
}
}
}
return &windowedSignal[0];
}
// ----------------------------------------------------------------------------
float * ofOpenALSoundPlayer::getSpectrum(int bands){
initFFT(bands);
bins.assign(bins.size(),0);
if(sources.empty()) return &bins[0];
int signalSize = (bands-1)*2;
getCurrentBufferSum(signalSize);
float normalizer = 2. / windowSum;
runWindow(windowedSignal);
kiss_fftr(fftCfg, &windowedSignal[0], &cx_out[0]);
for(int i= 0; i < bands; i++) {
bins[i] += sqrtf(cx_out[i].r * cx_out[i].r + cx_out[i].i * cx_out[i].i) * normalizer;
}
return &bins[0];
}
// ----------------------------------------------------------------------------
float * ofOpenALSoundPlayer::getSystemSpectrum(int bands){
initSystemFFT(bands);
systemBins.assign(systemBins.size(),0);
if(players().empty()) return &systemBins[0];
int signalSize = (bands-1)*2;
if(int(systemWindowedSignal.size())!=signalSize){
systemWindowedSignal.resize(signalSize);
}
systemWindowedSignal.assign(systemWindowedSignal.size(),0);
set<ofOpenALSoundPlayer*>::iterator it;
for(it=players().begin();it!=players().end();it++){
if(!(*it)->isPlaying()) continue;
float * buffer = (*it)->getCurrentBufferSum(signalSize);
for(int i=0;i<signalSize;i++){
systemWindowedSignal[i]+=buffer[i];
}
}
float normalizer = 2. / windowSum;
runWindow(systemWindowedSignal);
kiss_fftr(systemFftCfg, &systemWindowedSignal[0], &systemCx_out[0]);
for(int i= 0; i < bands; i++) {
systemBins[i] += sqrtf(systemCx_out[i].r * systemCx_out[i].r + systemCx_out[i].i * systemCx_out[i].i) * normalizer;
}
return &systemBins[0];
}
// ----------------------------------------------------------------------------
void ofOpenALSoundPlayer::runWindow(vector<float> & signal){
for(int i = 0; i < (int)signal.size(); i++)
signal[i] *= window[i];
}
#endif