Mittens 2018 will be in Boston this September. There are three spaces left, so check out the thread for details!

Author Topic: How to crossfade music  (Read 463 times)

How to crossfade music
« on: 11 Nov 2017, 20:09 »
Hello!

I need to crossfade between two music tracks. I think this isn't so unusual, so probably someone here has already done it.

an excerpt of my audio player code is below
Code: Adventure Game Studio
  1. AudioChannel *acMusic;
  2. AudioClip * currentMusic;
  3. bool stopped;
  4. int globalMusicVol;
  5. AudioClip * previousmusic ;
  6.  
  7.  
  8. static void MusicPlayer::play(AudioClip * musicClip,  int position){
  9.   stopped = false;
  10.  
  11.   if(previousmusic==musicClip){
  12.     //don't play music if it's already playing
  13.     return;
  14.   }
  15.  
  16.   //stop previous music
  17.   if(previousmusic != null){
  18.     previousmusic.Stop();
  19.   }
  20.  
  21.   previousmusic = musicClip;
  22.  
  23.   currentMusic = musicClip;
  24.   if(position == 0){
  25.     acMusic = musicClip.Play(eAudioPriorityHigh, eRepeat);
  26.   } else {
  27.     acMusic = musicClip.PlayFrom(position,eAudioPriorityHigh, eRepeat);
  28.    
  29.   }
  30.   acMusic.Volume = globalMusicVol;
  31.   updateAllVolumes();
  32. }
  33.  
  34. static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime){
  35.   if(crossfadeTime!= 0.0){
  36.    
  37.   } else {
  38.     int previousPosition = acMusic.Position;
  39.     MusicPlayer.play(musicClip, previousPosition);
  40.   }
  41. }
  42.  

The function I am working is static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime).

I want to enable the possibility to crossfade between two songs that have the exact same length. How to do this? I only have 1 audio channel for music, and I am not using the ambient channel, would be possible to set the audioclip type to whichever isn't the last one?

Cassiebsg told me there is the possibility to use the following function too:
SetGameOption (OPT_CROSSFADEMUSIC, x); // where x= 0 (no), 1 (slow), 2 (slowish), 3 (medium) and 4 (fast)

But... I would like to have control over the crossfade time. Below is my full audio module code, it has dependency on the Tween Module.


MusicPlayer.ash
Add spoiler tag for Hidden:
Code: Adventure Game Studio
  1. // new module header
  2.  
  3. #define MusicPlayer_CROSSFADETIME 0
  4.  
  5.  
  6. struct MusicPlayer
  7. {
  8.   import static void play(AudioClip * musicClip,  int position = 0);
  9.   import static void playFromPreviousTime(AudioClip * musicClip, float crossfadeTime = MusicPlayer_CROSSFADETIME);
  10.   import static AudioClip * getCurrentMusic();
  11.   import static void stop(AudioClip * musicClip);
  12.   import static void setMusicPosition(int x, int y);
  13.   import static void removeMusicPosition();
  14.   import static void tweenMusicPosition(int x, int y,  float timing);
  15.   import static void setGlobalVolume(int musicVolume);
  16.   import static int  getGlobalVolume();
  17.   import static void setVolume(int musicVolume);
  18.   import static void tweenVolume(int musicVolume, float timing);
  19. };
  20.  
  21. struct SFXPlayer
  22. {
  23.   import static void play(AudioClip * soundClip);
  24.   import static void stop(AudioClip * soundClip);
  25.   import static void setGlobalVolume(int soundVolume);
  26.   import static int  getGlobalVolume();
  27.   import static void setVolume(int soundVolume);
  28. };
  29.  
  30. import void SPlayerSetGameVolume(int gameVolume);
  31. import int SPlayerGetGameVolume();
  32. import void SPlayerSetDefaults();
  33.  
  34.  

MusicPlayer.asc
Add spoiler tag for Hidden:
Code: Adventure Game Studio
  1. // MusicPlayer variables
  2. AudioChannel *acMusic;
  3. AudioClip * currentMusic;
  4. bool stopped;
  5. int globalMusicVol;
  6. int currentMusicVolue;
  7.  
  8. // SFXPlayer variables
  9. AudioChannel *acSound;
  10. int gloabalSoundVol;
  11. int orignalMusicX;
  12. int orignalMusicY;
  13.  
  14. function updateAllVolumes(){
  15.   int i = 0;
  16.   AudioChannel *ac;
  17.    
  18.   while (i < System.AudioChannelCount)
  19.   {
  20.     ac = System.AudioChannels[i];
  21.    
  22.     if (ac.PlayingClip != currentMusic)
  23.     {
  24.       ac.Volume = gloabalSoundVol;
  25.     } else {
  26.       ac.Volume = globalMusicVol;
  27.     }
  28.     i++;
  29.   }  
  30. }
  31.  
  32.  
  33. AudioClip * previousmusic ;
  34.  
  35.  
  36. static AudioClip * MusicPlayer::getCurrentMusic(){
  37.   return currentMusic;  
  38. }
  39.  
  40. static void MusicPlayer::play(AudioClip * musicClip,  int position){
  41.   stopped = false;
  42.  
  43.   if(previousmusic==musicClip){
  44.     //don't play music if it's already playing
  45.     return;
  46.   }
  47.  
  48.   //stop previous music
  49.   if(previousmusic != null){
  50.     previousmusic.Stop();
  51.   }
  52.  
  53.   previousmusic = musicClip;
  54.  
  55.   currentMusic = musicClip;
  56.   if(position == 0){
  57.     acMusic = musicClip.Play(eAudioPriorityHigh, eRepeat);
  58.   } else {
  59.     acMusic = musicClip.PlayFrom(position,eAudioPriorityHigh, eRepeat);
  60.    
  61.   }
  62.   acMusic.Volume = globalMusicVol;
  63.   updateAllVolumes();
  64. }
  65.  
  66.  
  67. static void MusicPlayer::playFromPreviousTime(AudioClip * musicClip, float crossfadeTime){
  68.   if(crossfadeTime!= 0.0){
  69.    
  70.   } else {
  71.     int previousPosition = acMusic.Position;
  72.     MusicPlayer.play(musicClip, previousPosition);
  73.   }
  74. }
  75.  
  76. static void MusicPlayer::setMusicPosition(int x, int y){
  77.   acMusic.SetRoomLocation(x, y);
  78.   orignalMusicX = x;
  79.   orignalMusicY = y;
  80. }
  81.  
  82.  
  83. static void MusicPlayer::removeMusicPosition(){
  84.   acMusic.SetRoomLocation(0, 0);
  85.   orignalMusicX = 0;
  86.   orignalMusicY = 0;
  87. }
  88.  
  89. static void MusicPlayer::tweenMusicPosition(int x, int y,  float timing){
  90.   if(x==0 && y==0){
  91.     MusicPlayer.removeMusicPosition();
  92.   }
  93.  
  94.   if(orignalMusicX ==0 && orignalMusicY==0){
  95.     return;  
  96.   }
  97.  
  98.   acMusic.TweenRoomLocation(timing, x, y, orignalMusicX, orignalMusicY);
  99.   orignalMusicX = x;
  100.   orignalMusicY = y;
  101. }
  102.  
  103.  
  104. static void MusicPlayer::stop(AudioClip * musicClip){
  105.   stopped = true;
  106.   previousmusic = null;
  107.   musicClip.Stop();  
  108. }
  109.  
  110. static void MusicPlayer::setVolume(int musicVolume){
  111.   if(acMusic != null){
  112.     currentMusicVolue = musicVolume;
  113.     acMusic.Volume = FloatToInt(IntToFloat(currentMusicVolue) * IntToFloat(globalMusicVol)/100.0);
  114.   }
  115. }
  116.  
  117. static void MusicPlayer::tweenVolume(int musicVolume, float timing){
  118.   if(acMusic != null){
  119.     currentMusicVolue = musicVolume;
  120.     acMusic.TweenVolume(timing, FloatToInt(IntToFloat(musicVolume) * IntToFloat(globalMusicVol)/100.0), eEaseLinearTween, eNoBlockTween);
  121.   }
  122. }
  123.  
  124. static void MusicPlayer::setGlobalVolume(int musicVolume){
  125.   globalMusicVol = musicVolume;
  126.   Game.SetAudioTypeVolume(eAudioTypeAmbientSound, globalMusicVol, eVolExistingAndFuture);
  127.   Game.SetAudioTypeVolume(eAudioTypeMusic, globalMusicVol, eVolExistingAndFuture);
  128. }
  129.  
  130. static int MusicPlayer::getGlobalVolume(){
  131.   return globalMusicVol;  
  132. }
  133.  
  134.  
  135.  
  136. static void SFXPlayer::play(AudioClip * soundClip){
  137.   acSound = soundClip.Play(eAudioPriorityNormal, eOnce);
  138.   acSound.Volume = gloabalSoundVol;
  139.   updateAllVolumes();
  140. }
  141.  
  142. static void SFXPlayer::stop(AudioClip * soundClip){
  143.   soundClip.Stop();  
  144. }
  145.  
  146. static void SFXPlayer::setGlobalVolume(int soundVolume){
  147.   gloabalSoundVol = soundVolume;
  148.   Game.SetAudioTypeVolume(eAudioTypeSound, gloabalSoundVol, eVolExistingAndFuture);
  149. }
  150.  
  151. static void SFXPlayer::setVolume(int soundVolume){
  152.   if(acSound != null){
  153.     acSound.Volume = FloatToInt(IntToFloat(soundVolume) * IntToFloat(gloabalSoundVol)/100.0);
  154.   }
  155. }
  156.  
  157. static int SFXPlayer::getGlobalVolume(){
  158.   return gloabalSoundVol;  
  159. }
  160.  
  161. void  SPlayerSetGameVolume(int gameVolume){
  162.   System.Volume = gameVolume;
  163. }
  164.  
  165. int SPlayerGetGameVolume(){
  166.   return System.Volume;
  167. }
  168.  
  169. void SPlayerSetDefaults(){
  170.   MusicPlayer.setGlobalVolume(70);
  171.   SFXPlayer.setGlobalVolume(70);
  172.   SPlayerSetGameVolume(70);
  173. }
  174.  
  175. function game_start()
  176. {
  177.   SPlayerSetDefaults();
  178.   sldAudio.Value = System.Volume;
  179.   sldTestMainVolume.Value = System.Volume;
  180.   sldTestMusicVolume.Value = globalMusicVol;
  181.   sldTestSoundVolume.Value = gloabalSoundVol;
  182. }
  183.  

selmiak

  • ǝsıɔɹǝxǝ ʞɔǝu puɐ uıɐɹq
    • I can help with play testing
    •  
    • I can help with proof reading
    •  
    • I can help with translating
    •  
    • I can help with web design
    •  
    • selmiak worked on a game that was nominated for an AGS Award!
Re: How to crossfade music
« Reply #1 on: 12 Nov 2017, 12:52 »
old versions of the tween module didn't address the music channel volume, but newer versions of the tween module should help you tweening the music channel volume nonblockingly.

Re: How to crossfade music
« Reply #2 on: 12 Nov 2017, 19:40 »
Hey, using the tween module alone doesn't help me. I would still need to dynamically change the type of song to alocate to a different music channel to do crossfade, but I don't know how.

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
Re: How to crossfade music
« Reply #3 on: 12 Nov 2017, 19:55 »
Hey, using the tween module alone doesn't help me. I would still need to dynamically change the type of song to alocate to a different music channel to do crossfade, but I don't know how.
AGS does not support changing audio types at runtime. You really need 2 music channels to do crossfade, and also to make sure you explicitly stop previous music when you do not want crossfade.

Re: How to crossfade music
« Reply #4 on: 13 Nov 2017, 08:37 »
I looked the code but still couldn't figure out. If I use high priority, will the first music be allocated in the channel for music and the second be allocated in a random channel? I looked the engine crossfade code but it uses a crossfade channel. I am asking because I provide volume controls, so I need to track the channels.

Monsieur OUXX

  • Cavefish
  • Mittens Vassal
  • Mittens Half Initiate
    • I can help with proof reading
    •  
    • I can help with translating
    •  
    • I can help with voice acting
    •  
Re: How to crossfade music
« Reply #5 on: 13 Nov 2017, 12:41 »
If you want a better control over the fading, have a look at how it's done here : http://www.adventuregamestudio.co.uk/forums/index.php?topic=48583.0
 

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
Re: How to crossfade music
« Reply #6 on: 13 Nov 2017, 13:08 »
I looked the code but still couldn't figure out. If I use high priority, will the first music be allocated in the channel for music and the second be allocated in a random channel?

It is not allocated in a random channel, it is allocated on any free channel from the ones you reserved for this type. For that reason you need to reserve at least 2 channels for music type.
BTW, for that reason I am not sure priority is much useful here (unless you have situational music VS background music case).

Normally you start one music and stop the previous one - to prevent them playing at the same time (since you have 2 music channels now). But when you need crossfade, you start another music without stopping previous, the new one will play on the second free channel, and the first channel will eventually be freed when the previous music finished volume decay and stopped.
« Last Edit: 13 Nov 2017, 13:11 by Crimson Wizard »

Re: How to crossfade music
« Reply #7 on: 14 Nov 2017, 01:17 »
Quote
For that reason you need to reserve at least 2 channels for music type.

WHOA!!! THAT'S THE MAGIC!  I didn't knew it was possible. I thought it was hard coded or something.

If someone ever need to do this, here's how: In the AGS Editor, go in Explore Project, go in Audio -> Types -> Music, in the property MaxChannels, write the number you want (in my crossfade case, 2).

About the Crossfade, I still need to code, soon will post here. Thanks Monsieur OUXX, great module! :) (it's kind of magical how there's always a module in the forum that I can't find by searching but someone links in a topic :P)

Thank you Crimson Wizard!!!! :-D
« Last Edit: 14 Nov 2017, 01:27 by eri0o »