Crossfading music ( OGG, MP3)

Started by
9 comments, last by Fingers_ 16 years, 5 months ago
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 ;)
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.
Thansk for your answer,

What other solution can we use in a game to mix music (music and not small wav files ) ?
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 ?



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 = A*Avolume + B*Bvolume<br><br>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.<br><br>The guts for steps 1 and 2 can be adapted from the <a href="http://xiph.org/vorbis/doc/vorbisfile/example.html">Vorbisfile example</a>. The "pcmout" buffer represents the buffers A and B. <br><br>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…<br><br>When initializing, you need to set up two vorbisfiles:<br><pre><br> FILE *ogg1, *ogg2;<br> OggVorbis_File vf1, vf2;<br><br>// I'm skipping error checks just for brevity's sake<br> ogg1 = fopen("music1.ogg", "rb");<br> ov_open_callbacks(ogg1, &vf1, NULL, 0, OV_CALLBACKS_DEFAULT);<br> ogg2 = fopen("music2.ogg", "rb");<br> ov_open_callbacks(ogg2, &vf2, NULL, 0, OV_CALLBACKS_DEFAULT);<br></pre><br><br>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.<br><pre><br> char buffer1[4096]; // this must be the same size as the buffer you set for SDL<br> // instead of 4096, use whatever you get in the "len" parameter<br> char *buf = buffer1;<br> int end=0;<br> int room=4096;<br><br> while(!end){<br> long ret=ov_read(&vf1,buf,room,0,2,1,&current_section);<br> if (ret == 0) { // end of read<br> end = 1;<br> if (room &gt; 0) // end of file, rewind to beginning<br> ov_time_seek(&vf1, 0);<br> } else if (ret &lt; 0) {<br> } else {<br> room -= ret;<br> buf += ret;<br> }<br> }<br>// this should have loaded 4096 bytes of data from vorbisfile vf1 into buffer1<br>// do the same thing for vf2 / buffer2<br></pre><br><br>And finally, mix it all together<br><pre><br> Sint16 *buffer1s16 = (Sint16 *)buffer1;<br> Sint16 *buffer2s16 = (Sint16 *)buffer2;<br> Sint16 *buf = (Sint16 *)stream;<br> int len = 4096 / 2; // divide buffer length<br> int s;<br><br> for (s = 0; s &lt; len; s++)<br> {<br> buf = ((buffer1s16 * volume1) &gt;&gt; 8) +<br> ((buffer2s16 * volume2) &gt;&gt; 8);<br> }<br><br></pre>
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 :)
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,&current_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 = ((buffer1s16 * 1) &gt;&gt; 8)+0 ;//add a second music channel here by replacing the 0<br> }<br><br>}//end of call back<br><br><br>//result : nothing, not any sound…<br><br>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 ?<br><br><br>Thanks,<br><br>Cedric.
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 ;)
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 * 256) &gt;&gt; 8 then the end result is the same as buffer ie. it plays at full volume. You can actually replace the shift with a division for improved clarity, and let the compiler optimize it.<br><br>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.<br><br>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.
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,&current_section);
can return length of data > of len ??? I hope No...

Thanks again ;)

This topic is closed to new replies.

Advertisement