Multichannel soundcard output

Hello all,

I need to play different soundfiles on different channels of my multichannel external soundcard - e.g. sound1.mp3 goes to channel 1+2, sound2.mp3 goes to channel 3+4 and sound3.mp3 plays on channel 5+6.
How do i do this? I have tried achieving this by changing the FMOD_Channel_SetSpeakerMix, but it does not change anything.

Thank you very much for all helps!

Here is my modified version of ofSoundPlayer. The function “setOutputChannels” is supposed to set the output channel of the sound, but dosent work.

  
  
  
  
#include "poexTapeSoundPlayer.h"  
  
//#ifdef POEX_TAPE_SOUND_PLAYER;  
  
#include "ofUtils.h"  
  
  
  
bool ptFmodInitialized_ = false;  
bool ptUseSpectrum_ = false;  
float ptfftValues_[8192];			//  
float ptfftInterpValues_[8192];			//  
float ptfftSpectrum_[8192];		// maximum #ofFmodSoundPlayer is 8192, in fmodex....  
  
  
// ---------------------  static vars  
static FMOD_CHANNELGROUP * channelgroup;  
static FMOD_SYSTEM       * sys;  
  
// ------------------------------------------------------------  
// ------------------------------------------------------------  
  
  
// now, the individual sound player:  
//------------------------------------------------------------  
poexTapeSoundPlayer::poexTapeSoundPlayer(){  
	bLoop 			= false;  
	bLoadedOk 		= false;  
	pan 			= 0.5f;  
	volume 			= 1.0f;  
	internalFreq 	= 44100;  
	speed 			= 1;  
	bPaused 		= false;  
	isStreaming		= false;  
}  
  
  
  
  
//---------------------------------------  
// this should only be called once  
void poexTapeSoundPlayer::initializeFmod(){  
	if(!ptFmodInitialized_){  
		FMOD_System_Create(&sys);  
		FMOD_System_Init(sys, 32, FMOD_INIT_NORMAL, NULL);  //do we want just 32 channels?  
		FMOD_System_GetMasterChannelGroup(sys, &channelgroup);  
		ptFmodInitialized_ = true;  
	}  
}  
  
  
//---------------------------------------  
void poexTapeSoundPlayer::closeFmod(){  
	if(ptFmodInitialized_){  
		FMOD_System_Close(sys);  
		ptFmodInitialized_ = false;  
	}  
}  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::loadSound(string fileName, bool stream){  
  
	  
	//TagLib::FileRef f("Latex Solar Beef.mp3");  
	//TagLib::String artist = f.tag()->artist(); // artist == "Frank Zappa"  
	  
	  
	fileName = ofToDataPath(fileName);  
	  
	// fmod uses IO posix internally, might have trouble  
	// with unicode paths...  
	// says this code:  
	// [http://66.102.9.104/search?q=cache:LM47mq8hytwJ:www.cleeker.com/doxygen/audioengine--fmod-8cpp-source.html+FSOUND-Sample-Load+cpp&hl=en&ct=clnk&cd=18&client=firefox-a](http://66.102.9.104/search?q=cache:LM47mq8hytwJ:www.cleeker.com/doxygen/audioengine--fmod-8cpp-source.html+FSOUND-Sample-Load+cpp&hl=en&ct=clnk&cd=18&client=firefox-a)  
	// for now we use FMODs way, but we could switch if  
	// there are problems:  
	  
	bMultiPlay = false;  
	  
	// [1] init fmod, if necessary  
	  
	initializeFmod();  
	  
	// [2] try to unload any previously loaded sounds  
	// & prevent user-created memory leaks  
	// if they call "loadSound" repeatedly, for example  
	  
	unloadSound();  
	  
	// [3] load sound  
	  
	//choose if we want streaming  
	int fmodFlags =  FMOD_SOFTWARE;  
	if(stream)fmodFlags =  FMOD_SOFTWARE | FMOD_CREATESTREAM;  
	  
	result = FMOD_System_CreateSound(sys,fileName.c_str(),  fmodFlags, NULL, &sound);  
	  
	if (result != FMOD_OK){  
		bLoadedOk = false;  
		ofLog(OF_LOG_ERROR,"ofFmodSoundPlayer: Could not load sound file %s", fileName.c_str() );  
	} else {  
		bLoadedOk = true;  
		FMOD_Sound_GetLength(sound, &length, FMOD_TIMEUNIT_PCM);  
		isStreaming = stream;  
	}  
	  
	/*  
	void* ptr1;  
    void* ptr2;             // ???  
    unsigned int length1;   // ???  
    unsigned int length2;   // ???  
    data_left_channel = new int[length];  
    data_right_channel = new int[length];  
    FMOD_Sound_Lock(sound, 0, length, &ptr1, &ptr2, &length1, &length2);  
    for (int i=0 ; i<length ; i++)  
    {  
        data_left_channel[i] = ((int*)ptr1)[i]>>16;  
        data_right_channel[i] = (((int*)ptr1)[i]<<16)>>16;  
    }  
    FMOD_Sound_Unlock(sound, ptr1, ptr2, length, length2);  
	*/  
}  
  
  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::unloadSound(){  
	if (bLoadedOk){  
		stop();						// try to stop the sound  
		if(!isStreaming)FMOD_Sound_Release(sound);  
	}  
}  
  
//------------------------------------------------------------  
bool poexTapeSoundPlayer::getIsPlaying(){  
	  
	if (!bLoadedOk) return false;  
	  
	int playing = 0;  
	FMOD_Channel_IsPlaying(channel, &playing);  
	return (playing != 0 ? true : false);  
}  
  
//------------------------------------------------------------  
float poexTapeSoundPlayer::getSpeed(){  
	return speed;  
}  
  
//------------------------------------------------------------  
float poexTapeSoundPlayer::getPan(){  
	return pan;  
}  
  
//------------------------------------------------------------  
unsigned int poexTapeSoundPlayer::getLength(){  
	unsigned int qua;  
	FMOD_Sound_GetLength(sound, &qua, FMOD_TIMEUNIT_MS);  
	return qua;  
}  
  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::setVolume(float vol){  
	if (getIsPlaying() == true){  
		FMOD_Channel_SetVolume(channel, vol);  
	}  
	volume = vol;  
}  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::setPosition(float pct){  
	if (getIsPlaying() == true){  
		int sampleToBeAt = (int)(length * pct);  
		FMOD_Channel_SetPosition(channel, sampleToBeAt, FMOD_TIMEUNIT_PCM);  
	}  
}  
//----------------  
void poexTapeSoundPlayer::setOutputChannels(int channelNo){  
	  
	switch (channelNo) {  
			  
		case 1 :   
			// Process for test = 1  
			FMOD_Channel_SetSpeakerMix(channel, 1.0f, 1.0f, 0, 0, 0, 0, 0, 0);   
			break;  
			  
		case 2 :   
			// Process for test = 5  
			FMOD_Channel_SetSpeakerMix(channel, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);   
			break;  
		case 3 :   
			// Process for test = 5  
			FMOD_Channel_SetSpeakerMix(channel, 0, 0, 0, 0, 1.0f, 1.0f, 0, 0);   
			break;  
	}  
			  
	  
}  
  
  
//------------------------------------------------------------  
float poexTapeSoundPlayer::getPosition(){  
	if (getIsPlaying() == true){  
		unsigned int sampleImAt;  
		  
		FMOD_Channel_GetPosition(channel, &sampleImAt, FMOD_TIMEUNIT_PCM);  
		  
		float pct = 0.0f;  
		if (length > 0){  
			pct = sampleImAt / (float)length;  
		}  
		return pct;  
	} else {  
		return 0;  
	}  
}  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::setPan(float p){  
	if (getIsPlaying() == true){  
		FMOD_Channel_SetPan(channel,p);  
	}  
	pan = p;  
}  
  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::setPaused(bool bP){  
	if (getIsPlaying() == true){  
		FMOD_Channel_SetPaused(channel,bP);  
		bPaused = bP;  
	}  
}  
  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::setSpeed(float spd){  
	if (getIsPlaying() == true){  
		FMOD_Channel_SetFrequency(channel, internalFreq * spd);  
	}  
	speed = spd;  
}  
  
  
//------------------------------------------------------------  
void poexTapeSoundPlayer::setLoop(bool bLp){  
	if (getIsPlaying() == true){  
		FMOD_Channel_SetMode(channel,  (bLp == true) ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);  
	}  
	bLoop = bLp;  
}  
  
// ----------------------------------------------------------------------------  
void poexTapeSoundPlayer::setMultiPlay(bool bMp){  
	bMultiPlay = bMp;		// be careful with this...  
}  
  
// ----------------------------------------------------------------------------  
void poexTapeSoundPlayer::play(){  
	  
	// if it's a looping sound, we should try to kill it, no?  
	// or else people will have orphan channels that are looping  
	if (bLoop == true){  
		FMOD_Channel_Stop(channel);  
	}  
	  
	// if the sound is not set to multiplay, then stop the current,  
	// before we start another  
	if (!bMultiPlay){  
		FMOD_Channel_Stop(channel);  
	}  
	  
	FMOD_System_PlaySound(sys, FMOD_CHANNEL_FREE, sound, bPaused, &channel);  
	  
	FMOD_Channel_GetFrequency(channel, &internalFreq);  
	FMOD_Channel_SetVolume(channel,volume);  
	FMOD_Channel_SetPan(channel,pan);  
	FMOD_Channel_SetFrequency(channel, internalFreq * speed);  
	FMOD_Channel_SetMode(channel,  (bLoop == true) ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);  
	  
	//fmod update() should be called every frame - according to the docs.  
	//we have been using fmod without calling it at all which resulted in channels not being able  
	//to be reused.  we should have some sort of global update function but putting it here  
	//solves the channel bug  
	FMOD_System_Update(sys);  
	  
}  
  
// ----------------------------------------------------------------------------  
void poexTapeSoundPlayer::stop(){  
	FMOD_Channel_Stop(channel);  
}  
  
  
  
  
  
  
  

You actually have to dig a little bit deeper into FMOD to get it working with multiple sound cards, namely, in the OF initializeFmod() call. The call to FMOD_System_Create() needs to be done for each sound card that you’re creating. I had some code for working with multiple cards, but it’s from OF0061 and wouldn’t be super helpful, I do know though that it’s not super difficult. The FMOD library download comes with developer examples, one of which is called multiplesoundcard and walks you through it. Something like this:

  
FMOD_SYSTEM    *one, *two;  
  
int numdrivers, count;  
FMOD_System_Create(&one);  
FMOD_System_GetNumDrivers(one, &numdrivers);  
for (count=0; count < numdrivers; count++)  
{  
    char name[256];  
  
    result = FMOD_System_GetDriverInfo(one, count, name, 256, 0);  
    cout << count << " " << name << endl;  
}  
  
FMOD_System_SetDriver(one, driver);  
FMOD_System_Init(one, 32, FMOD_INIT_NORMAL, NULL);  
  
FMOD_System_Create(&two);  
FMOD_System_GetNumDrivers(two, &numdrivers);  
for (count=0; count < numdrivers; count++)  
{  
    char name[256];  
  
    result = FMOD_System_GetDriverInfo(two, count, name, 256, 0);  
    cout << count << " " << name << endl;  
}  
  
FMOD_System_SetDriver(two, driver);  
FMOD_System_Init(two, 32, FMOD_INIT_NORMAL, NULL);  

Hope that helps.

Hi, I’m facing exactly the same problem. @luckmann, how did you solved it?
In theory it is a single physical soundcard, so you dont really need to create many FMOD_SYSTEM, right?

Hi.

As i remenber i only used one instance of the FMOD_SYSTEM, but as i recall the FMOD does not support multichannel audio directly, but it does support surround output. The way i solved it was to setup my soundcard in osx to act as surround (http://guides.macrumors.com/Configuring_5.1_Surround_Output_in_Mac_OS_X) and afterwards use FMOD_Channel_SetSpeakerMix to route the sound to the different “speakers”/channels.

Hope it helps :slight_smile:

Thanks @luckmann for your help.
Finally I’ve managed it to work with a 7.1 setup and here is the code:

hi, @vcuculo
I used your code to load a wav file, but I got result equals to FMOD_ERR_REVERB_INSTANCE from FMOD_System_CreateSound function. I am on 64 bits windows 8.1 system. Do you have any clue why it happened?

 int main( ){
   
    ofxMultiSpeakerSoundPlayer soundPlayer;
    if(soundPlayer.loadSound("ring.wav", true)){
        soundPlayer.playTo(0);//back speaker
    }

}

Looking at the FMOD_RESULT error codes it seems that a

Specified instance in FMOD_REVERB_PROPERTIES couldn’t be set. Most likely because it is an invalid instance number or the reverb doesn’t exist.

So, check if you set any FMOD_REVERB_PROPERTIES in your code, using setReverbProperties method

thank you for your reply @vcuculo
I can play the wav file when I set speaker mode to FMOD_SPEAKERMODE_STEREO in initializeFmod function. My PC has got 5.1 channel sound card, how can I set that in initializeFmod function

void ofxMultiSpeakerSoundPlayer::initializeFmod()
{
    FMOD_SPEAKERMODE  speakermode;
    int numdrivers;

    if(!bFmodInitialized2_)
    {
        FMOD_System_Create(&sys);
        FMOD_System_SetDriver(sys, 0); // setup driver 7.1 with index 7
        FMOD_System_SetSpeakerMode(sys, FMOD_SPEAKERMODE_STEREO);

#ifdef TARGET_LINUX
        FMOD_System_SetOutput(sys,FMOD_OUTPUTTYPE_ALSA);
#endif

        FMOD_System_Init(sys, 32, FMOD_INIT_NORMAL, NULL);  //do we want just 32 channels?
        FMOD_System_GetMasterChannelGroup(sys, &channelgroup);
        bFmodInitialized2_ = true;
    }
}

I set the driver to 0 because that is main sound device as selected by the operating system, is that correct?

Try setting the speakerMode to FMOD_SPEAKERMODE_5POINT1

I was trying to use this library. It used to work but now is having this problem :

`Severity	Code	Description	Project	File	Line	Suppression State	Detail Description
Error (active)		object of abstract class type "ofxMultiSpeakerSoundPlayer" is not allowed:	Multichanneltest	e:\Julito\Openframeworks\apps\myApps\MutichannelTestVIEJA\src\ofApp.h	37		            pure virtual function "ofBaseSoundPlayer::load" has no overrider
            pure virtual function "ofBaseSoundPlayer::unload" has no overrider
            pure virtual function "ofBaseSoundPlayer::getPosition" has no overrider
            pure virtual function "ofBaseSoundPlayer::getPositionMS" has no overrider
            pure virtual function "ofBaseSoundPlayer::isPlaying" has no overrider
            pure virtual function "ofBaseSoundPlayer::getSpeed" has no overrider
            pure virtual function "ofBaseSoundPlayer::getPan" has no overrider
            pure virtual function "ofBaseSoundPlayer::isLoaded" has no overrider
            pure virtual function "ofBaseSoundPlayer::getVolume" has no overrider
Error	C2259	'ofxMultiSpeakerSoundPlayer': cannot instantiate abstract class (compiling source file src\main.cpp)	Multichanneltest	e:\julito\openframeworks\apps\myapps\mutichanneltestvieja\src\ofApp.h	37	
Error	C2259	'ofxMultiSpeakerSoundPlayer': cannot instantiate abstract class (compiling source file src\ofApp.cpp)	Multichanneltest	e:\julito\openframeworks\apps\myapps\mutichanneltestvieja\src\ofApp.h	37	
Warning	C4101	'numdrivers': unreferenced local variable	Multichanneltest	E:\Julito\Openframeworks\apps\myApps\MutichannelTestVIEJA\src\ofxMultiSpeakerSoundPlayer.cpp	180	
Warning	C4101	'speakermode': unreferenced local variable	Multichanneltest	E:\Julito\Openframeworks\apps\myApps\MutichannelTestVIEJA\src\ofxMultiSpeakerSoundPlayer.cpp	179	
`

Hi @Julian_Puppo, it seems that ofBaseSoundPlayer received an update, and it includes some new methods, as load, unload, getPosition ecc.
Eventually you can update the plugin code from the repo and make a pull request.

1 Like