0
0

Hello,

I’m creating a program that will process a file containing a sound bank and time offsets to mark when the sound must be played and generate a wave file from it.
So I suppose FMOD_OUTPUTTYPE_WAVWRITER is perfect for the job.

For the sound bank, imagine something like this in a file:

[code:1fs4rrrb]0 kick.wav
1 hit.wav
2 flute.wav[/code:1fs4rrrb]

where the number on the left describes the sound ID of the sound file name on the right,
and the time offsets:

[code:1fs4rrrb]1000 0
2000 1
3000 2[/code:1fs4rrrb]

where the number on the left tells the program when the sound must be played in milliseconds, and number on the right is the sound ID.

Therefore, when I start the program, FMOD will generate a wave file which contains a kick (from kick.wav) in the first second, a hit in the second second, and a flute in the third second, and I will have to wait for at least 3 seconds for the task to complete.

However, if I want to render a longer music, say, 5 minutes, then I must have to wait for at least 5 minutes for the task to complete, since I make it depend on the system timer to play the sound at the specified offset in the file and a while(true) loop to update FMOD::System. I think there must be a way to render faster without waiting for the program to actually render the sound at the specified time, since I saw in a DAW program like, say, Sony ACID, can render the tracks really fast.

Then I take a bit look into the API reference, there’s FMOD_OUTPUTTYPE_WAVWRITER_NRT, then I thought this maybe the solution, so I tried changing the output device right away without modifying anything else, and the generated wave file sounds messed up! I hear many repetitive sound, long delays, etc.

So, how to use the non-realtime version properly? What is the correct way to update the fmod system when using NRT in my case?

Anyway, I use C++ in a Windows environment.

Thanks.

  • You must to post comments
0
0

Anyone?

  • You must to post comments
0
0

I’ve responded to your question on stack overflow, but for anyone else interested here is my response:

The way FMOD_OUTPUTTYPE_WAVEWRITER_NRT works is it generates audio every time System::update is called. The amount of data generated per ‘update’ is governed by the bufferlength parameter of System::setDSPBufferSize.

So the faster you call System::update the faster the data is generated. Now the important part for your job is to convert ‘x’ number of calls to System::update into something you can match to your timeline.

By default FMOD operates at a sample rate of 48000 samples per second (configurable via System::setSoftwareFormat). So if you set ‘bufferlength’ to 1024, each time you call System::update 1024 samples will be generated (written to the wav file). So… 1024 / 48000 = 0.021333 seconds ~ 21.3 milliseconds of data is generated per call. Now you can calculate how many times you need to call System::update to get to the timestamps you have.

  • You must to post comments
0
0

Hi Mathew, Thanks for your reply.

I now understand that a single System::update call generates [bufferlength / sample rate] seconds of data.

So I basically do something like:
[code:186igxik]
float rate = 1024.0f / 48000.0f;
float totalCalls = musicDuration / rate;
float totalTime = 0.0f;
int rowIndex = 0; // row index of the file being rendered
for(int i = 0; i < totalCalls; i++)
{
// say, row is a struct that has the offset (in ms) and sound (FMOD::Sound pointer)
if(row[rowIndex].offset <= totalTime)
{
system->playSound(FMOD_CHANNEL_FREE, row[rowIndex].sound, false, 0);
rowIndex++;
}
system->update();
totalTime += rate * 1000;
}
[/code:186igxik]

Now I can hear the sounds in the right time as specified in the file, but I still hear the sounds (all of them actually) are being played repetitively, like 3 to 4 times, played around the specified offset (for example, a sound which must only be played on 1000th ms, being played more than one time with < 1 second gap between each repetition around 1000th ms (maybe 750th ms, 1600th ms, ..))

I’m pretty sure that I did something wrong with the System::update call.

Also, sounds that acts as a background accompaniment (i.e. a 30 seconds long sound to be played in the first second of the music to fill the background/accompany the beginning of the music) aren’t being played properly. It is like being played intermittently. Maybe the System::update call interrupted it or something, I completely have no idea. :(

Any suggestion? What do you think might be the problem?

Thanks.

  • You must to post comments
0
0

A couple of quick notes, firstly make sure you check every return code from all FMOD functions, secondly do you handle rowIndex going out of bounds?

Other than those things what you are doing seems logical to me, could you perhaps provide a complete example so we can try it out?
Feel free to e-mail support@fmod.org if you don’t wish to post complete source to the forum.

Also you might be interested in Channel::setDelay, you can play a channel a give it a start delay in output samples, i.e. 48000 * time in seconds, this will remove the need for your while loop, you can fire your channels up front.

  • You must to post comments
0
0

Hi Mathew, Thanks for your reply.

After hours of debugging, I found out that the problem was on the sound creation.

The following scenario can reproduce the problem:
[code:1tvk2fnz]
char *data = GetData("test.ogg"); // load and copy the entire file to memory

FMOD_CREATESOUNDEXINFO exinfo;
memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.length = GetFilesize("test.ogg"); // file size of test.ogg, really :D

FMOD::Sound *sound;
// make it stream through data
FMOD_RESULT result = system->createSound(data, FMOD_OPENMEMORY | FMOD_CREATESTREAM, &exinfo, &sound); // result = FMOD_OK

float rate = 1024.0f / 48000.0f;
float totalCalls = musicDuration / rate; // musicDuration in second
float totalTime = 0.0f;
bool played = false;
for(int i = 0; i < totalCalls, i++)
{
if(1000 <= totalTime && !played)
{
// play test.ogg just once.. in the first second..
system->playSound(FMOD_CHANNEL_FREE, sound, false, 0);
played = true;
}

system->update();
totalTime += rate * 1000;
}

delete[] data;
system->close();
system->release();[/code:1tvk2fnz]

So basically, the problem will occur if I make it stream through the data in memory. Instead, if I change the mode to FMOD_CREATESAMPLE, the generated wave sounds fine. :)
Is this some kind of a bug or internal limitation?

Anyway, I do interested in using channels with start delay, but what if I want to reuse the sample? For example, what if I want a "kick" sound to play on every beat of the music?

[code:1tvk2fnz]// sound bank
1 kick.wav

// music to be parsed
// play a kick on every beat of a 120 BPM music
500 1
1000 1
1500 1
2000 1
2500 1
//..and so on[/code:1tvk2fnz]

Thanks.

  • You must to post comments
0
0

You should use FMOD_INIT_STREAM_FROM_UPDATE flag during System::init to ensure streams are updated as fast as the output is operating.

With streams the sound can only be played by one channel at a time, so to play it multiple times you need to create multiple streams. Alternatively use create sample or create compressed sample, the same loaded data can be referenced by multiple channels.

  • You must to post comments
Showing 6 results
Your Answer

Please first to submit.