Example of playing back a sample in ofSoundStream

Here’s an example of how to load a sample and play it back using ofSoundStream.

I borrowed some code from the excellent Maximilian project:
http://forum.openframeworks.cc/t/maximilian—some-c+±objects-for-dsp-and-synthesis./3416/4

It only supports .wav samples (PCM format). The example included can play mono or stereo files. I’ve also included a simple ring modulation effect - just click the mouse button and drag to hear it. The sample playback also supports different speeds, just call play(0.5) to play at half speed for example. If you don’t specify looping by calling setLooping(true) the sample will only play once.

The example is based on the audioOutputExample, you just need to add the Sample.cpp and Sample.h files to your project as well as the testApp.cpp and .h files.

loadSample.zip

Thanks again Pierre

I made a few minor changes to suit my purposes - namely getting waveform data and waveform display from audio files. Thought someone might find it useful.

{edit} BTW: I had problems playing a mono file - i think the dataSize needs to be divided by number of channels or something?? Will have a look tomorrow. And consequently my waveform display is stereo, though perhaps not accurately so…ho hum…

:slight_smile:

SampleWithWaveform.zip

Fantastic…that’s very handy :slight_smile:

Mono samples should definitely work, the dataSize is correct, it’s looks like it should be divided by the num channels but actually you don’t need to.
If you can’t load your mono file, try exporting them with a program like Audacity, maybe there’s some junk in the file header that is confusing the file loading function. Or even better, send me the file and I’ll have a look at it.

So I got excited and implemented some more functions, namely:

  • reverse speed
  • pausing
  • separate play(), stop() and update() functions
  • added a bit of error checking in file loading

I also made the waveform display to be mono or stereo depending on the input file.

In the demo app I’ve also implemented basic sample scrubbing using the mouse.

Check the testApp::keyPressed() function for the new features.[attachment=1:yu04dypk]waveform.jpg[/attachment:yu04dypk]

sampleWithWaveform2.zip

Hey,

Is it possible to implement the same thing with ofSoundPlayer? I couldn’t find how to get the wave data. The reason why i’m asking is, the example you provided does not play .wav files with “ima adpcm” codec.

No its not possible at the moment.
If want to use another codec, try the Libsndfile library (www.mega-nerd.com/libsndfile/). There may be some examples of loading files with it on the forum…I think I’ve seen one.

Otherwise just use a sound editor to convert the file…unless you really need it in that format.

Hello Grimus, I’m trying to compile the LoadSample file but I get some error and warning messages. How can I solve it?
I have replaced your SRC file on a new Empty Project.

||Warning: .drectve -defaultlib:LIBCMT ' unrecognized| ||Warning: .drectve-defaultlib:OLDNAMES ’ unrecognized|
||Warning: .drectve -defaultlib:LIBCMT ' unrecognized| ||Warning: .drectve-defaultlib:OLDNAMES ’ unrecognized|
||Warning: .drectve -defaultlib:LIBCMT ' unrecognized| ||Warning: .drectve-defaultlib:OLDNAMES ’ unrecognized|
||Warning: .drectve -defaultlib:LIBCMT ' unrecognized| ||Warning: .drectve-defaultlib:OLDNAMES ’ unrecognized|
||Warning: .drectve -defaultlib:LIBCMT ' unrecognized| ||Warning: .drectve-defaultlib:OLDNAMES ’ unrecognized|
||Warning: .drectve -defaultlib:uuid.lib ' unrecognized| ||Warning: .drectve-defaultlib:uuid.lib ’ unrecognized|
||Warning: .drectve -defaultlib:LIBCMT ' unrecognized| ||Warning: .drectve-defaultlib:OLDNAMES ’ unrecognized|
||Warning: .drectve -defaultlib:LIBCMT ' unrecognized| ||Warning: .drectve-defaultlib:OLDNAMES ’ unrecognized|
||Warning: .drectve /DEFAULTLIB:"LIBC" /DEFAULTLIB:"OLDNAMES" ' unrecognized| obj\release\src\testApp.o:testApp.cpp|| undefined reference toSample::getChannels()’|
obj\release\src\testApp.o:testApp.cpp|| undefined reference to Sample::getChannels()'| obj\release\src\testApp.o:testApp.cpp|| undefined reference toSample::getChannels()’|
obj\release\src\testApp.o:testApp.cpp|| undefined reference to Sample::getChannels()'| obj\release\src\testApp.o:testApp.cpp|| undefined reference toSample::play(double)’|
obj\release\src\testApp.o:testApp.cpp|| undefined reference to Sample::play(double)'| obj\release\src\testApp.o:testApp.cpp|| undefined reference toSample::play()’|
obj\release\src\testApp.o:testApp.cpp|| undefined reference to Sample::play(double)'| obj\release\src\testApp.o:testApp.cpp|| undefined reference toSample::play()’|
obj\release\src\testApp.o:testApp.cpp|| undefined reference to Sample::play()'| obj\release\src\testApp.o:testApp.cpp|| undefined reference toSample::load(std::string)’|
obj\release\src\testApp.o:testApp.cpp|| undefined reference to Sample::setLooping(bool)'| obj\release\src\testApp.o:testApp.cpp:(.text$\_ZN7testAppD1Ev[testApp::~testApp()]+0x48)||undefined reference toSample::~Sample()’|
obj\release\src\testApp.o:testApp.cpp:(.text$_ZN7testAppD0Ev[testApp::~testApp()]+0x48)||undefined reference to Sample::~Sample()'| obj\release\src\main.o:main.cpp|| undefined reference toSample::Sample()’|
||=== Build finished: 15 errors, 17 warnings ===|

Hi,

Have you made sure to add the Sample.cpp and Sample.h to your project?

Well… I’ve dropped the Sample.cpp file and Sample.h in the ‘src’ folder but when I’m on Openframeworks they not appear in the project src folder, in the Management window

Yes you have to add it to the project. Are you on Windows? And are you using codeblocks or visual studio?

If you don’t know how to add files to your project you might need to re-read some of the tutorials.

MiniMaxima is defined in Sample.h so it looks like you haven’t added it to your project yet

Thx Grimus!
I solved those error messages modifying the .cbp project

But I still have 3 errors:

||=== emptyExample, release ===|
…\src\testApp.h|43|error: ‘MiniMaxima’ was not declared in this scope|
…\src\testApp.h|43|error: template argument 1 is invalid|
…\src\testApp.h|43|error: template argument 2 is invalid|
||=== Build finished: 3 errors, 0 warnings ===|

hi people …

thanks for this Pierre ! it has been superusefull !!

i’m willing to be able to draw a WAV file which is made with 4 channels for a quadrophonic sound installation and i would like to know if you think that this could be done ?

i’ve made some tryouts just hacking the code, but as i’m not familiar with audio stuff i’m sure i’m doing something wrong …

here what i hacked :

in Sample.h i added 2 more min and max for LC and RC channels …

  
typedef struct {  
	double minL;  
	double maxL;  
	double minLC;  
	double maxLC;  
	double minRC;  
	double maxRC;  
	double minR;  
	double maxR;  
	  
} MiniMaxima;  
  
  

in Sample.cpp, Sample::generateWaveform()

  
.../...		  void Sample::generateWaveForm(vector<MiniMaxima> * _waveForm)  
{  
  
	_waveForm->clear();  
  
	bool loopState = getIsLooping();  
	setLooping(false);  
	bool playState = getIsPlaying();  
	double tmpSpeed = getSpeed();  
	setSpeed(1.0f);  
    play();  
	// waveform display method based on this discussion [http://answers.google.com/answers/threadview/id/66003.html](http://answers.google.com/answers/threadview/id/66003.html)  
  
    double sampleL, sampleR, sampleLC, sampleRC;  
	while ((long)position<getLength()) {  
  
		MiniMaxima mm;  
		mm.minL = mm.minR = mm.maxL = mm.maxR = 0;  
  
		for (int i = 0; i < 256; i++){  
  
		    if(myChannels == 1) {  
                sampleL = update();  
                sampleR = sampleL;  
		    }  
		    else if (myChannels == 2){  
                sampleL = update()*0.5;  
                sampleR = update()*0.5;  
		    }  
		    else if (myChannels == 4){  
**                sampleL = update()*0.25;  
                sampleLC = update()*0.25;  
                sampleRC = update()*0.25;  
                sampleR = update()*0.25;**  
		    }  
			  
			mm.minL = MIN(mm.minL, sampleL);  
			mm.minR = MIN(mm.minR, sampleR);  
			mm.maxL = MAX(mm.maxL, sampleL);  
			mm.maxR = MAX(mm.maxR, sampleR);  
**			// for 4 channels  
			mm.minLC = MIN(mm.minLC, sampleLC);  
			mm.minRC = MIN(mm.minRC, sampleRC);  
			mm.maxLC = MAX(mm.maxLC, sampleLC);  
			mm.maxRC = MAX(mm.maxRC, sampleRC);**  
			  
		}  
  
		_waveForm->push_back(mm);  
  
 		//cout << mm.minR << " :: " << mm.maxR << " :: " << mm.minL << " :: " << mm.maxL << endl;  
	}  
  
	position = 0;  
	setLooping(loopState);  
	setSpeed(tmpSpeed);  
	if(playState) play();  
}  
  
  

Then i adapted the Sample::drawWaveform to consider the miniLC,maxLC,miniLC,maxRC …
The drawing is happening ok but not correct so i guess what i’m doing wrong (sure) is the stuff done in generateWaveform function …

Any idea of how to solve this ? i’ve dived looking for a trick on how to read 4 channel soundfiles within the links you mentioned, but i couldn’t find any advice …

Anyhelp much appreciated …

e*

Well solved myself … it was all there.

So i’ve just modified the given code to read 4 channel WAV files also …

in Sample.h i modified the MiniMaxima structure to support 4 channels (L,LC,RC,R)

  
  
typedef struct {  
	double minL;  
	double maxL;  
	double minLC;  
	double maxLC;  
	double minRC;  
	double maxRC;  
	double minR;  
	double maxR;  
	  
} MiniMaxima;  
  
  

in Sample.cpp modified the generateWaveform and drawWaveform functions. The drawWaveform has been extended with an in and out parameters that specify the portion of the sound wave you need to draw, so in=0.0 means the beginning of the sample and out=1.0 means the end of the sample.

i attach them in case anyone need to draw 4 channels …

  
void Sample::generateWaveForm(vector<MiniMaxima> * _waveForm)  
{  
  
	_waveForm->clear();  
  
	bool loopState = getIsLooping();  
	setLooping(false);  
	bool playState = getIsPlaying();  
	double tmpSpeed = getSpeed();  
	setSpeed(1.0f);  
    play();  
	// waveform display method based on this discussion [http://answers.google.com/answers/threadview/id/66003.html](http://answers.google.com/answers/threadview/id/66003.html)  
  
    double sampleL, sampleR, sampleLC, sampleRC;  
	while ((long)position<getLength()) {  
  
		MiniMaxima mm;  
		mm.minL = mm.minR = mm.maxL = mm.maxR = 0;  
		mm.minLC = mm.minRC = mm.maxLC = mm.maxRC = 0;  
  
		for (int i = 0; i < 256; i++){  
  
		    if(myChannels == 1) {  
                sampleL = update();  
                sampleR = sampleL;  
		    }  
		    else if (myChannels == 2){  
                sampleL = update()*0.5;  
                sampleR = update()*0.5;  
		    }  
		    else if (myChannels == 4){  
                sampleL = update()*0.25;  
                sampleR = update()*0.25;  
                sampleLC = update()*0.25;  
                sampleRC = update()*0.25;  
		    }  
			  
			// 1 or 2 channels  
			mm.minL = MIN(mm.minL, sampleL);  
			mm.minR = MIN(mm.minR, sampleR);  
  
			mm.maxL = MAX(mm.maxL, sampleL);  
			mm.maxR = MAX(mm.maxR, sampleR);  
			  
			// for 4 channels  
			if (myChannels == 4)  
			{				  
				mm.minLC = MIN(mm.minLC, sampleLC);  
				mm.minRC = MIN(mm.minRC, sampleRC);  
				  
				mm.maxLC = MAX(mm.maxLC, sampleLC);  
				mm.maxRC = MAX(mm.maxRC, sampleRC);  
		    }  
			  
		}  
  
		_waveForm->push_back(mm);  
	}  
  
	position = 0;  
	setLooping(loopState);  
	setSpeed(tmpSpeed);  
	if(playState) play();  
}  
  
//---------------------------------------------------------------  
  
void Sample::drawWaveForm(int _x, int _y, int _w, int _h,float _inPoint, float _outPoint, vector<MiniMaxima> * _waveForm)  
{  
	  
	glPushMatrix();  
	  
	glTranslated(_x, _y-(_h/8), 0);  
	  
	int start = int(_inPoint * float(_waveForm->size()));  
	int end   = int(_outPoint * float(_waveForm->size()));  
	int size = end - start;  
	float waveFormZoomX = (float)size/(float)_w;  
  
	for (unsigned int i = start+1; i < end; i++)  
	{  
		if(i-start>=0)  
		{  
			if(myChannels == 1) {  
  
				ofSetColor(200, 200, 255);  
				ofLine((i-1-start)/waveFormZoomX, _h + (int)(_waveForm->at(i-1).minR*_h), (i-start)/waveFormZoomX, _h +  (int)(_waveForm->at(i).maxR*_h));  
				ofLine((i-start)/waveFormZoomX, _h + (int)(_waveForm->at(i).maxR*_h), (i-start)/waveFormZoomX, _h + (int) (_waveForm->at(i).minR*_h));  
				  
			} else if (myChannels ==2) {  
  
				ofSetColor(200, 200, 255);  
				ofLine((i-1-start)/waveFormZoomX, (int)(_waveForm->at(i-1).minL*_h), (i-start)/waveFormZoomX, (int)(_waveForm->at(i).maxL*_h));  
				ofLine((i-start)/waveFormZoomX, (int)(_waveForm->at(i).maxL*_h), (i-start)/waveFormZoomX, (int) (_waveForm->at(i).minL*_h));  
				  
				ofLine((i-1-start)/waveFormZoomX, (_h/2) + (int)(_waveForm->at(i-1).minR*_h), (i-start)/waveFormZoomX, (_h/2) +  (int)(_waveForm->at(i).maxR*_h));  
				ofLine((i-start)/waveFormZoomX, (_h/2) + (int)(_waveForm->at(i).maxR*_h), (i-start)/waveFormZoomX, (_h/2) + (int) (_waveForm->at(i).minR*_h));  
			}  
			else if (myChannels ==4)   
			{		  
				// LEFT channel  
				ofSetColor(255, 0, 0);  
				//ofLine((i-1)/waveFormZoomX, _waveForm->at(i).minR*(float)_h, i/waveFormZoomX, _waveForm->at(i).maxR*(float)_h);  
				ofLine((i-1-start)/waveFormZoomX, (int)(_waveForm->at(i-1).minL*_h), (i-start)/waveFormZoomX, (int)(_waveForm->at(i).maxL*(_h/2)));  
				ofLine((i-start)/waveFormZoomX, (int)(_waveForm->at(i).maxL*_h), (i-start)/waveFormZoomX, (int) (_waveForm->at(i).minL*(_h/2)));  
				  
				// RIGHT channel  
				ofSetColor(0, 255, 0);  
				ofLine((i-1-start)/waveFormZoomX, (_h/4) + (int)(_waveForm->at(i-1).minR*(_h/2)), (i-start)/waveFormZoomX, (_h/4) +  (int)(_waveForm->at(i).maxR*(_h/2)));  
				ofLine((i-start)/waveFormZoomX, (_h/4) + (int)(_waveForm->at(i).maxR*(_h/2)), (i-start)/waveFormZoomX, (_h/4) + (int) (_waveForm->at(i).minR*(_h/2)));  
  
				// LEFT CENTER channel  
				ofSetColor(0, 0,255);  
				ofLine((i-1-start)/waveFormZoomX, (2*(_h/4)) + (int)(_waveForm->at(i-1).minLC*(_h/2)), (i-start)/waveFormZoomX, (2*(_h/4)) +  (int)(_waveForm->at(i).maxLC*(_h/2)));  
				ofLine((i-start)/waveFormZoomX, (2*(_h/4)) + (int)(_waveForm->at(i).maxLC*(_h/2)), (i-start)/waveFormZoomX, (2*(_h/4)) + (int) (_waveForm->at(i).minLC*(_h/2)));  
  
				// RIGHT CENTER channel  
				ofSetColor(255, 255, 255);  
				ofLine((i-1-start)/waveFormZoomX, (3*(_h/4)) + (int)(_waveForm->at(i-1).minRC*(_h/2)), (i-start)/waveFormZoomX, (3*(_h/4)) +  (int)(_waveForm->at(i).maxRC*(_h/2)));  
				ofLine((i-start)/waveFormZoomX, (3*(_h/4)) + (int)(_waveForm->at(i).maxRC*(_h/2)), (i-start)/waveFormZoomX, (3*(_h/4)) + (int) (_waveForm->at(i).minRC*(_h/2)));  
				  
			}  
		}  
	}  
	float waveFormDisplayScale = (float)size/(float)_w;  
	  
    if(myChannels == 1) {  
        ofLine(position/waveFormDisplayScale, -(float)_h*0.0, position/waveFormDisplayScale, (float)_h*2.0);  
    }  
    else if(myChannels ==2)  
    {  
        ofLine(position/waveFormDisplayScale, -(float)_h*0.5, position/waveFormDisplayScale, (float)_h*1.5);  
    }  
	  
	glPopMatrix();  
}  
  

e*

hi,

somehow my wav sound files only reads correctly when i change the read() in the class Sample

from
inFile.seekg(40, ios::beg);
to
inFile.seekg(36, ios::beg);

mac os 10.7.4, of007

any clue?

yota

well,

after the this line in the Sample class;

file.read((char*) &mBitsPerSample, sizeof(short));

i needed to find the correct place of dataSize;

char test[4] = { 0 };
int testSize = 0;

while (file.read(test, 4) && (test[0] != ‘d’ || test[1] != ‘a’ || test[2] != ‘t’ || test[3] != ‘a’))
{
file.read(reinterpret_cast<char*>(&testSize), 4);
file.seekg(testSize, std::ios_base::cur);
}
file.read((char*) &mDataSize, 4);

// read the data chunk
mData = new char[mDataSize];
file.read(mData, mDataSize);

Hey,
I’ve been using Sample.h and Sample.cpp a lot in my ios apps - really cool.
Question: how would I go about unloading samples from memory before loading new ones?
I’ve been monitoring the memory usage and it appears that the sample is added to memory each time I call Sample.load instead of being replaced resulting in crashes if samples are changed too often.

Thanks for any ideas!
Cheers
Alex

Hi Alex, haven’t looked at this in ages, however you’re right, there’s no provision for re-loading files (will cause a memory leak if you do).

It’s an easy fix though, do something like this (haven’t tested it though):

  
  
Sample::~Sample()  
{  
   clear();  
}  
Sample::clear()  
{  
    delete myData;  
    myChunkSize = NULL;  
    mySubChunk1Size = NULL;  
    myFormat = NULL;  
    myChannels = NULL;  
    mySampleRate = NULL;  
    myByteRate = NULL;  
    myBlockAlign = NULL;  
    myBitsPerSample = NULL;  
    myDataSize = NULL;  
}  
  
Sample::read()  
{  
   if(getIsLoaded) clear();  
  
...  
...  
}  
  

Hi Pierre,

Thanks that fixed it indeed!

I just added the delete bit in the load function so it automatically purges before loading a new sample:

  
bool Sample::load(string tmpPath) {  
      
      
    //delete myPath;  
    delete myData;  
    myChunkSize = NULL;  
    mySubChunk1Size = NULL;  
    myFormat = NULL;  
    myChannels = NULL;  
    mySampleRate = NULL;  
    myByteRate = NULL;  
    myBlockAlign = NULL;  
    myBitsPerSample = NULL;  
    myDataSize = NULL;  
      
    myPath = tmpPath;  
	bool result = read();  
	return result;  
}  

Cheers
Alex

Hi All, I’m trying to build something that displays a waveform of a file just like this but it seems the attached sample code is no longer available since the forum upgrade. Any chance someone can point me to an example somewhere that I can take a look at or re-attach a recent version?

Cheers, Stefan

I cannot download zip file from this thread. how can I get that ?

Hey ,
You can see the sound examples in openframeworks example folder .It contains a example called soundPlayerFFTExample. Hope it helps .
Cheers!
A more direct link from github: