Jump to content
  • Advertisement
Sign in to follow this  
beepmaster

Crossfading music ( OGG, MP3)

This topic is 3996 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, What are solution to crossfade 2 music during a game ? No problem to mix sound but lot of librairies like SDL seems not to be able to mix 2 music together : case it allow only 1 channel for music: so i can fade the first one then start the other but no scross-fadin ;( - I want to list what kind of solution I can use : directX, allegor ??? etc... - And what librairies, tools do you suggest for crossfading ? Is it any problem with game performance to crossfade two "ogg" music together ? ( like too much cpu ) Thanks ;) Cedric ;)

Share this post


Link to post
Share on other sites
Advertisement
SDL_Mixer may not be able to stream two MP3s or Oggs simultaneously, but that's not an inherent limitation of SDL. You just need to write your own mixer code to feed multiple streams into SDL's audio buffer.

There's a theoretical risk that you'll run into complications when reading two or more files simultaneously (ie. rapidly alternating between them), but modern operating systems should tolerate that just fine because of caching. The most I've tried was three Oggs playing simultaneously using my own mixing code, and it didn't cause any problems whatsoever. Decoding an Ogg file should take less than 3% of your CPU time on a reasonably new computer.

This page includes a simple custom mixer for SDL that I learned from when I wrote my own. Basically, you'll set up a callback mixer function that uses libvorbis functions to load a chunk of music from each stream, each time SDL calls it, and mixes them together.

Share this post


Link to post
Share on other sites
Hello, me again :)

I read a lot of articles about mixing ogg :

- OPENAL : Free but need charge in use with few soundcards, and XBOX
- FMOD : Only Free for personnal use..
- DIRECTX/DIRECTSOUND : Lot of channel but not under open licence...
- SDL : Open, free, but need special development to mix ogg together...

I'm not used to develop mixing audio data programm from low level code, i would like to learn more about mixing ogg with SDL (from Fingers_ REPLY) :

I Start to download, compile and run the mixing example (With SDL without SDL_MIXER) from http://olofson.net/examples.html
and to download, compile and run example from
http://xiph.org/vorbis/
that contains example of a filtrer (if I understand well )to decompress from stdin ogg data to stdout uncompressed

I understand the theory :

First of all I need to read OGG from basic SDL like the example (without SDL_MIXER)

1- I get piece of sound from a long OGG music
2- I decompress that piece to a memory block
3- I tell SDL to read that memory block
4- During this time I decompress a second block
5- If the sound read reach the end, I switch to the second undecompressed data block...


In a second time i will be able to reach my need :

- Mixing ogg together

After that, If I reach this point, creating a cross fading effect
(Volume go down at the end of the first music, and the second music volume go up) will be easy.

I'm not used to manipulate audio data, I programm with Bloodshed Dev-C++ under windows and I need help about that, I want to lean more by using SDLand building a mixer with theses example : can you help me ?



Share this post


Link to post
Share on other sites
The simultaneity of decompressing and playback is sort of automatically taken care of by SDL. I think the mixer callback function is basically always called one buffer length in advance. So SDL starts playing the contents of buffer A, and at the same time tells you to fill up buffer B with the next batch of data.

A simple two-stream callback would work like this:

1. Read from ogg stream 1 into buffer A
2. Read from ogg stream 2 into buffer B
3. Mix these into the output buffer (provided by SDL)
This is just a for loop where you set for each sample:
Output[s] = A[s]*Avolume + B[s]*Bvolume

This is the same as the sm_mixer function in simplemixer, except you get the data from the streams instead of previously loaded Wav files.

The guts for steps 1 and 2 can be adapted from the Vorbisfile example. The "pcmout" buffer represents the buffers A and B.

I'll cut'n'paste together some code from these examples to illustrate. It won't be pretty, but a lot simpler than dissecting my fully featured sound system...

When initializing, you need to set up two vorbisfiles:

FILE *ogg1, *ogg2;
OggVorbis_File vf1, vf2;

// I'm skipping error checks just for brevity's sake
ogg1 = fopen("music1.ogg", "rb");
ov_open_callbacks(ogg1, &vf1, NULL, 0, OV_CALLBACKS_DEFAULT);
ogg2 = fopen("music2.ogg", "rb");
ov_open_callbacks(ogg2, &vf2, NULL, 0, OV_CALLBACKS_DEFAULT);


In the callback (sm_mixer), you'll do something like this for each open vorbisfile. I changed it to load the length of the mix buffer at a time (instead of the whole file), and to loop the music if you reach the end of file.

char buffer1[4096]; // this must be the same size as the buffer you set for SDL
// instead of 4096, use whatever you get in the "len" parameter
char *buf = buffer1;
int end=0;
int room=4096;

while(!end){
long ret=ov_read(&vf1,buf,room,0,2,1,¤t_section);
if (ret == 0) { // end of read
end = 1;
if (room > 0) // end of file, rewind to beginning
ov_time_seek(&vf1, 0);
} else if (ret < 0) {
} else {
room -= ret;
buf += ret;
}
}
// this should have loaded 4096 bytes of data from vorbisfile vf1 into buffer1
// do the same thing for vf2 / buffer2


And finally, mix it all together

Sint16 *buffer1s16 = (Sint16 *)buffer1;
Sint16 *buffer2s16 = (Sint16 *)buffer2;
Sint16 *buf = (Sint16 *)stream;
int len = 4096 / 2; // divide buffer length
int s;

for (s = 0; s < len; s++)
{
buf[s] = ((buffer1s16[s] * volume1) >> 8) +
((buffer2s16[s] * volume2) >> 8);
}

Share this post


Link to post
Share on other sites
Thanks you a lot, I feel free to try something with all of that.
I say it again : im not used to programm audio data...

I'll go back here with my result :)

Thanks you again :)

Share this post


Link to post
Share on other sites
My conclusion : So the callback is called like an event each time SDL can use audiodata, the stream variable is the wave buffer that SDL send to soundcard.

//I tryed that :

from the main function :

ogg1 = fopen("music.ogg", "rb");

if(!ogg1)
fprintf(stderr,"Erreur d'ouverture du fichier OGG\n");
//in english : data file error...

if( ov_open(ogg1, &vf1, NULL, 0 )<0)
fprintf(stderr,"Fichier musical invalide\n");
//in english : music file error...

//and the callback function :

void mixaudio(void *unused, Uint8 *stream, int len)
{
//this callback function is really called

fprintf(stdout,"DEBUT MIX \n");
int current_section;

char bufferA[len]; //juste a buffer for performing a mix between two ogg later
char *buffer=bufferA; //pointer to this buffer

//I read data of length "len from ogg file to the temp buffer
long ret=ov_read(&vf1,buffer,len,0,2,1,¤t_section);
//I supose that from the next call of this function, the starting reading position will be from "len" until the ogg music is fully read.

//I have to control the "ret" variable here later...

//just debugging data : in french debut = begin ;)
fprintf(stdout,"DEBUT MIX OGG LEN:%d RET:%d\n",len,ret);

//debug data
fprintf(stdout,"END MIX OGG\n");

//end ogg convert

//so here mysterious conversion from last example for our temp buffer
Sint16 *buffer1s16 = (Sint16 *)buffer;
//we got a pointer to the stream variable with same format
Sint16 *buf = (Sint16 *)stream;
//length is divided... mmm ... dont really know why... may be a 16 to 8 bits conversion
int length = len / 2; // divide buffer length
//s variable for current position in the buffer to mix
int s;


for (s = 0; s < length; s++)

{
//a false mixing, just à try with only one buffer...i'll add a second buffer later
buf[s] = ((buffer1s16[s] * 1) >> 8)+0 ;//add a second music channel here by replacing the 0
}

}//end of call back


//result : nothing, not any sound...

The callback function is really called, if I cut n' past from an example data of sdl callback : that ok I Hear sound from wav files, but nothing with the ogg file... First of all, I want to know why... what is the mistake ?


Thanks,

Cedric.

Share this post


Link to post
Share on other sites
Please forget my last reply, may be a little pointer problem.

I just rewrite a simple ogg decompressor on the fly using the SDL callback function : it's fine now, I understand the basis.

Next step is to mix ;)

Share this post


Link to post
Share on other sites
The volume numbers are assumed to go from zero to 256 like they are in the simplemixer code, so using volume of 1 will cause it to be very, very quiet. The bit-shift (>>8) equals a division by 256, so if you have (buffer[s] * 256) >> 8 then the end result is the same as buffer[s] ie. it plays at full volume. You can actually replace the shift with a division for improved clarity, and let the compiler optimize it.

When mixing the two Oggs, you should make sure the sum of their volumes doesn't exceed 256. For example, halfway through a crossfade volume1 and volume2 can both be 128 but no higher than that. Otherwise, unpleasant noise artifacts may appear.

The division of the buffer length is indeed because the length given by SDL in the "len" parameter is in bytes but the mixer deals in Sint16's. I forgot to complete the comment, sorry about that.

Share this post


Link to post
Share on other sites
Okayyyyyyy :)

Thanks you :)

>> = Shift, i never used that :) That a long time ago I didn't write a C++ programm too :) I remember that in assembler 110 (bin) = 6 (dec) 11 (bin) = 3 (decimal)

8 bits ( char* ) -> 16bits so I have to divide len by two OK :)

16 bits = 512 values ... but if I add two music I have to divide each music so here for two music : 256 per sound.

I can change volume of my ogg music now :) very cool, I just have to to that again with another file and it's will be fine :)


About the function below :

The function ov_read(&vf1,buffer,len,0,2,1,¤t_section);
can return length of data > of len ??? I hope No...

Thanks again ;)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!