0
0

Hello,

I’m creating a music sequencer application that consists of the following:
– 6 ‘instruments’: different types of sound, e.g. melody, bass, percussion.
– 6 loops (all of the same length) per instrument: e.g. melody 1, melody 2, melody 3.
– Combinations of the above that are generated dynamically.

This means that always a maximum of 6 sounds will be playing. It is possible that no sounds play at all for one bar. Basically it is a DJ mixing application.

I’m running into a few problems and I hope you can help me fix these.

  1. Syncing. This might sound a bit ridiculous, but when I run the playSound function 6 times by using a for-loop, the sounds wouldn’t start playing at [b:2n03vl8q]exactly[/b:2n03vl8q] the same moment, right? I mean, calculating this for-loop takes time, so the sounds are not completely in sync to start with. What is the best way, sync-wise, to start playing a group of sounds?

  2. Stitching. I looked into the realtimestitching example and this looks like the way to go for me. In order for real-time [b:2n03vl8q]dynamic[/b:2n03vl8q] stitching to work I need to set up some sort of queue of the next samples that need to be played. What is the best way of achieving this in FMOD?

I’m looking forward to your advice, thanks a lot in advance!

  • Abel.

BTW, The FMOD Ex version I’m using is 4.26.09.

  • You must to post comments
0
0

Hi again,

I tried experimenting with the setDelay() code, but I can’t get it to work properly.

When I use the code provided below, there is still an audible delay compared to (un-)pausing a ChannelGroup. So I reckon I am not implementing the setDelay() function the right way.

Your help is much appreciated!!!

  • Abel.

PS: the loaded samples are all of the same length: 7500 ms.

[code:3vxdgf2c]#include <string>

include <vector>

include <map>

include "../fmod/inc/fmod.hpp"

include "../fmod/inc/fmod_errors.h"

include "../fmod/common/wincompat.h"

void fModErrorCheck( FMOD_RESULT result )
{
if (result != FMOD_OK)
{
printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
exit(-1);
}
}

void fModVersionCheck( unsigned int version )
{
if ( version < FMOD_VERSION )
{
printf( "Error! You are using an old version of FMOD %08x. This program requires %08x\n", version, FMOD_VERSION );
exit(-1);
}
}

// simple 64-bit int handling types
typedef unsigned long long Uint64;

class Uint64P
{
public:
Uint64P(Uint64 val = 0)
: mHi(int(val >> 32)), mLo(int(val & 0xFFFFFFFF))
{ }

Uint64 value() const
{
    return (Uint64(mHi) &lt;&lt; 32) + mLo;
}

void operator+=(const Uint64P &amp;rhs)
{
    FMOD_64BIT_ADD(mHi, mLo, rhs.mHi, rhs.mLo);
}

unsigned int mHi;
unsigned int mLo;

};

using namespace std;

define NUM_SOUNDS 30

int main(int argc, char argv[])
{
FMOD::System
system;
FMOD::Channel* channels[ NUM_SOUNDS ];
FMOD::Sound* sounds[ NUM_SOUNDS ];
unsigned int version, count;
unsigned int m_min_delay;
Uint64P start_time;

const char* fileNames[ NUM_SOUNDS ] =
{
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav",
"../sounds/1-3-kick.wav",
"../sounds/2-3-percussion.wav",
"../sounds/3-3-percussion.wav"
};

// Initialize FMOD

fModErrorCheck( FMOD::System_Create( &system ) );
fModErrorCheck( system->getVersion( &version ) );
fModVersionCheck( version );
fModErrorCheck( system->init( 32, FMOD_INIT_NORMAL, 0 ) );

// Get DSP clock

fModErrorCheck(system->getDSPBufferSize(&m_min_delay, 0));
m_min_delay *= 2;

fModErrorCheck(system->getDSPClock(&start_time.mHi, &start_time.mLo));
start_time += m_min_delay;

// Create sounds

for ( count = 0; count < NUM_SOUNDS; count++ )
{
fModErrorCheck( system->createSound( fileNames[ count ], FMOD_LOOP_NORMAL, 0, &sounds[ count ] ) );
fModErrorCheck( system->playSound( FMOD_CHANNEL_FREE, sounds[ count ], false, &channels[ count ] ) );

fModErrorCheck( channels[count]-&gt;setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, start_time.mHi, start_time.mLo) );

}

// Simple interface

printf("\n");
printf("Press Esc to exit.\n");
printf("\n");

// Main loop

int key;

do {
if ( kbhit() )
{
key = getch();
}
}
while ( key != 27 ); // 27 = Esc

// Shut down application

for ( int count = 0; count < NUM_SOUNDS; count++ )
{
fModErrorCheck( sounds[ count ]->release() );
}

fModErrorCheck( system->close() );
fModErrorCheck( system->release() );
}[/code:3vxdgf2c]

  • You must to post comments
0
0

Never mind. Looks like the original wave file is at 22050 Hz and the default output sampling rate at 48000 Hz.

  • You must to post comments
0
0

You should also be starting those sounds as paused (3rd parameter for playSound should be true). You should be calling system->update in your do loop.

  • You must to post comments
0
0

I suspect that could be related to resampling. If you want to do looping you could just set the looping flag in FMOD. Sequencing via setDelay is done post resamper so it can be slightly less accurate.

  • You must to post comments
0
0

OK that would make sense. However, from looking at the code, it isn’t clear to me where resampling is taking place. I don’t see any mention of sampling rate, or any resampling function anywhere. Could there be a default output sampling rate that differs from the input wave file’s sampling rate? I’m not looking to loop, but I want to understand where artifacts come from. For example, if I were to slice a waveform in small chunks, and then concatenate those in their normal order, would there be any difference with the original waveform? If yes, then why?

  • You must to post comments
0
0

[quote="peter":3aqurp2x]
There is an example on the wiki of how to do to real time sample accurate sequencing using setDelay:
http://52.88.2.202/wiki/index.php5?tit … h_setDelay
[/quote:3aqurp2x]

So I compiled and ran that code and compared its output (even as written to a file) to a realtime looping of the original wav file in a sound editor. There’s a noticeable sound difference! The stitching isn’t as accurate/clean in the FMOD code as it is in the sound editor. Can someone explain what could be going slightly wrong? Bug? Resampling and distortions in other parts of the FMOD chain? I’m confused. Thanks.

  • You must to post comments
0
0

So I won’t have the exact answers you’re looking for but here’s something to chew on in the mean time for your issue #1 — ChannelGroup. Create all of your sound objects and load them into a ChannelGroup. From there you can use the ChannelGroup::setPaused function to start/stop the sounds all together and let FMOD handle starting the sounds at the exact same moment in time. Check out the ChannelGroups example to help you along. This will offload your work out of your For…Loop block of code and put the work on FMOD’s side which is always a better option it seems. Best of luck!

You may also continue to check back to see if one of the FMOD gurus has a better suggestion as mine is just right off the top of my head from your question.

  • You must to post comments
0
0

Hi abeldebeer,

[quote:2vh47xdu]Syncing. This might sound a bit ridiculous, but when I run the playSound function 6 times by using a for-loop, the sounds wouldn’t start playing at exactly the same moment, right?[/quote:2vh47xdu]
Yeah you’re on the right track. When you call playsound or setpaused it is from the main thread. The sounds are getting processed in the mixer thread. They will get out of sync if the mixer thread starts a mix half way through your for loop. A better option would be to schedule the sounds to start at the same time. There is an example on the wiki of how to do to real time sample accurate sequencing using setDelay:
http://52.88.2.202/wiki/index.php5?tit … h_setDelay

-Pete

  • You must to post comments
0
0

Thank you cxvjeff and Peter for your replies!

I have a question about your suggestions.

Check out the [b:3oscz78z]pseudo code[/b:3oscz78z] below:

[code:3oscz78z]createChannelGroup( &channelGroup );
channelGroup->setPaused( true );

for ( count = 0; count < numChannels; count++ )
{
createSound( &sound[ count ] );
playSound( sound[ count ], &channel[ count ] );
channel[ count ]->setChannelGroup( channelGroup );
}

channelGroup->setPaused( false );[/code:3oscz78z]
With the above, when the channelGroup is unpaused, all sounds play at the same time. Could you explain what the big difference is between this approach and the setDelay example code? The setDelay code seems pretty thorough so I reckon that would be the preferred method, but I’d like to understand it a bit more.

Slightly off-topic, I’m developing on Linux (Fedora) and I can’t run the setDelay example because of some missing (Windows?) libraries. Is there a Linux version available as well or could you possibly point me in the right direction to get those libraries?

Thanks again for your time!

  • Abel.
  • You must to post comments
0
0

There is no guarantee that unpausing the channel group will make them synchronised. For performance reasons that function doesn’t lock the mixer so it’s effectively the same as looping though: channel[i]->setPaused(false).

To get the setDelay example running on linux you can use the ‘wincompat.h’ header shipped with the examples instead of windows.h, and try changing __int64 to int64 or ‘singed long long’. Let me know how it goes.

-Pete

  • You must to post comments
0
0

I’ve got the setDelay example working, thanks! :)

Now, since I’m not an experienced C++ programmer, it’s pretty hard for me to filter out the way I can use this for my app. It would be very helpful if you could provide me with a code snippet to show the setDelay function behaviour. I want to start 6 different samples at the same time. They are of the same length.

I hope this is enough info.

Thanks again!

  • You must to post comments
0
0

[quote:2mjrxkcg] I want to start 6 different samples at the same time. They are of the same length. [/quote:2mjrxkcg]

To highlight some important parts of the example:

You need to make sure m_min_delay is set:
[code:2mjrxkcg]ERRCHECK(m_system->getDSPBufferSize(&m_min_delay, 0));
m_min_delay *= 2;[/code:2mjrxkcg]

Scheduling a channel to play immediately:
[code:2mjrxkcg]// the previous channel isn’t playing; schedule ASAP
ERRCHECK(m_system->getDSPClock(&start_time.mHi, &start_time.mLo));
start_time += m_min_delay;
ERRCHECK(m_channels[i]->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START,
start_time.mHi, start_time.mLo));[/code:2mjrxkcg]

-Pete

  • You must to post comments
Showing 12 results
Your Answer

Please first to submit.