Multichannel audio playback on RPi with octo sound card

Hi,

I’m writing a program to play back a 6 channel audio file on a Raspberry Pi in sync with some other activities. I have installed an octo sound 8 channel sound card and tested it ( using ‘speaker-test -c 8’). All channels are working fine.

When I play back a 6 channel audio file using ofSoundPlayer, it mixes all of the channels into a stereo mix. It’s hard to tell but it seems like all 6 channels are mixed to both the left and right channels.

I’ve looked into various solutions that oF users have come up but they use FMOD and from what I can tell, FMOD hasn’t been compiled for ARM. I know that multichannel playback is possible on the Pi outside of oF, so I’m wondering if anyone has any suggestions as to how I can make this work?

Many thanks,
Dennis.

I can’t test this at the moment without available hardware, but you should look into the following things.

  1. Check if getNumOutputChannels() is returning the 8 channels. Check here.

  2. If that works, check if you can set the soundBuffers into separate channels using setChannel(…).

Probably you’d need the file to be split into separate channels and then set each of them to the respective channels. I’d be interested to know if this works!

Hi,
you should check http://github.com/roymacdonald/ofxSoundObjects/

There’s an example that adresses a very simimilar to yours, https://github.com/roymacdonald/ofxSoundObjects/tree/master/example-soundPlayerMultiOutput

I haven’t tested it on linux but it should work fine.

cheers

Thank you both.
I’ll work on this a little bit and report back. The soundPlayerMultiOutput looks like it might be close to what I’m trying to do.

All best for now,
Dennis.

Hi Roy,

So I made a very simple app and I’m getting some errors when I try to compile it. The idea is to simply load a six channel wav and play it back through outputs 1 to 6 of my soundcard.

I’m writing and compiling the app on a Windows machine with Visual Studio Community 2015 to test the code before copying it over to the Pi and compiling it there. It’s just easier to test it on the Windows machine as the compile times are so much faster. Just to be clear, I’m not cross-compiling it. My usual workflow is - compile the app for Windows and when it works, copy it to the Pi and make the necessary adjustments until it compiles there too.

The main issue at the moment is that the compiler doesn’t recognise the ofSoundStreamSettings object. I’ve done a bit of searching and it looks like I’m using an older version of openFrameworks (of_v0.9.8_vs_release) which has a more basic version of the sound libraries included in the libs/sound/ folder. When I compile it on the Windows machine, I also get a bunch of errors relating to AudioToolBox.h which I’m presuming have something to do with OSX-related stuff?

The code is below for reference. As you can see, it borrows heavily from your example. Any comments or suggestions welcome.

Thanks,
Dennis.

the .h file:

#pragma once

#include "ofMain.h"
#include "ofxSoundObjects.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);

		ofSoundStream stream;
		ofxSoundOutputMultiplexer output;
		ofxSoundPlayerObject player;

};

and the .cpp file:

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

	player.load("six_channel_test_file.wav");

	// six channel audio file
	ofxSoundObject& playerOutput = output.getOrCreateChannelGroup({ 0, 1, 2, 3, 4, 5 });
	player.connectTo(playerOutput);
	player.play();

	// Setup the sound stream.
	ofSoundStreamSettings settings;
	settings.bufferSize = 256;
	settings.numBuffers = 1;
	settings.numInputChannels = 0;
	settings.numOutputChannels = count;
	settings.sampleRate = player.getSoundFile().getSampleRate();


	auto devices = ofSoundStreamListDevices();
	stream.printDeviceList();

	// IMPORTANT!!!
	// The following two lines of code is where you set which audio interface to use.
	// The number you put inside the square braquets in devices[ ] is the one
	// printed in the list in the console.
	// You can use a different input and output device.
	settings.setInDevice(devices[3]);
	settings.setOutDevice(devices[3]);


	stream.setup(settings);
	stream.setOutput(output);

}

//--------------------------------------------------------------
void ofApp::update(){

}

//--------------------------------------------------------------
void ofApp::draw(){

}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}

Hi Roy,

I downloaded the nightly build of the VS version of oF and most of the errors went away.
The AudioToolbox.h error persisted, so I located the audiotoolbox files and added them, but I’m now into a wormhole of trying to track down a load of apple sound-related files. Maybe there’s some declaration that could be placed in one of the ofxSoundObjects files that would prevent the compiler looking for these files in the first place?

Will try on a Pi later on this evening.

Many thanks,
Dennis.

So, just for the record, this does work on an RPi. I managed to get an 8 channel file playing back via an audioinjector octo card using the ofxSoundObjects-master addon.

For future reference, here’s what I did (.ccp and .h files listed below):

I used the nightly build of openframeworks for Arm 6.
I am running Jessie, not Stretch.

I had to remove everything from the ofxSoundObjects-master/libs folder as most of the files were causing compliation errors. Caveat: some of these libs may be needed if you’re doing more than just straight playback.

In the example .cpp file, the lines

settings.setInDevice(devices[3]);
settings.setOutDevice(devices[3]);

caused me trouble because the only soundcard I had installed was the octo card and there was no device with an index 3. I was getting a weird error for ages. Finally I switched those two lines to

settings.setInDevice(devices[0]);
settings.setOutDevice(devices[0]);

and everything worked fine.

I made a few small changes in some of the addon files to prevent errors showing up in the cmd window. For example, in

void ofxSoundPlayerObject::AudioOut

I commented out the line

buf.stereoPan(i.volumeLeftvolume, i.volumeRightvolume);

as this causes warnings for files with more than two channels.

REQUEST
The looping doesn’t seem to work for me. It would be great if this was operational.

CODE LISTINGS

.h FILE

#pragma once
#include "ofMain.h"
#include "ofxSoundObjects.h"
 class ofApp : public ofBaseApp{
  public:
    void setup();
    void update();
    void draw();
    void keyPressed(int key);
    void keyReleased(int key);
    void mouseMoved(int x, int y );
    void mouseDragged(int x, int y, int button);
    void mousePressed(int x, int y, int button);
    void mouseReleased(int x, int y, int button);
    void mouseEntered(int x, int y);
    void mouseExited(int x, int y);
    void windowResized(int w, int h);
    void dragEvent(ofDragInfo dragInfo);
    void gotMessage(ofMessage msg);
    ofSoundStream stream;
    ofxSoundOutputMultiplexer output;
    ofxSoundPlayerObject player;
  
};

.cpp FILE

#include "ofApp.h"
void ofApp::setup(){

// load an eight channel audio file
player.load(“eight_channel_test_file.wav”);

ofxSoundObject& playerOutput = output.getOrCreateChannelGroup({ 0, 1, 2, 3, 4, 5, 6, 7 });
player.connectTo(playerOutput);
player.play();
player.setLoop(true, 0);

// Setup the sound stream.
ofSoundStreamSettings settings;
settings.bufferSize = 256;
settings.numBuffers = 1;
settings.numInputChannels = 0;
settings.numOutputChannels = 8;
settings.sampleRate = player.getSoundFile().getSampleRate();

auto devices = ofSoundStreamListDevices();
stream.printDeviceList(); // I commented this out once I knew which device index I needed to use

// IMPORTANT!!!
// The following two lines of code is where you set which audio interface to use.
// The number you put inside the square braquets in devices[ ] is the one
// printed in the list in the console.
// You can use a different input and output device.
settings.setInDevice(devices[0]);
settings.setOutDevice(devices[0]);
stream.setup(settings);
stream.setOutput(output);

}
//--------------------------------------------------------------
void ofApp::update(){
  // this plays the file from the beginning every 10 seconds
  // It's a hack-y way to repeat a file.
  if( (int(ofGetElapsedTimef() ))% 10 == 0){  player.play();  }    
}
//--------------------------------------------------------------
void ofApp::draw(){
 // this displays some debug info which might be useful
 ofBackground( 255, 255, 255);
 ofSetColor( 0, 0, 0,);
 player.drawDebug( 10.0, 10.0);
}
// all the usual empty methods go after this ...
ETC ...

One final thing.
Typing
speaker-test -c 8
at the command line is a handy way to make sure your audio interface is outputting on all 8 channels.

Thanks again ayrous and Roy for your suggestions.
Hope this helps someone!
d.

1 Like

Hi,
great to know you got it to work.

Right, you need to use the latest nightly build.
the audiotoolbox.h is being added in a header file specific for macos that is part of libaudiodecoder. So just remove from your VS project the files ofxSoundObjects/libs/libaudiodecoder/include/audiodecodercoreaudio.h and ofxSoundObjects/libs/libaudiodecoder/include/audiodecodercoreaudio.cpp as well the folder ofxSoundObjects/libs/libaudiodecoder/include/apple.
libaudiodecoder has been a problematic on windows.
On the raspi it will use libsndfile, so there’s no need to even include libaudiodecoder

the lines

settings.setInDevice(devices[3]);
settings.setOutDevice(devices[3]);

have a big comment on top that specifies what you should do with these. Anyway I should probably default it to 0.

What’s that warning you are getting form the following line?

buf.stereoPan(i.volumeLeft*volume, i.volumeRight*volume);

There’s a problem with looping that is actually a bug in the openFrameworks core. I was working on a fix that I want to get finished soon and publish it. On the meanwhile check this https://github.com/roymacdonald/ofxSoundObjects/issues/20
I’m also working on the full implementation of ofSoundFile based on libsndfile, leaving libaudiodecoder only for mp3 decoding.
I should update soon.

The multioutput and multiinput examples are missing documentation as I made those on a rush. I’ll update that soon.

best