/// <param name="synchronise">Start music immediately, don't wait for fade</param> public static void SetGameMusic(SafeSoundEffect music, bool loop = true, bool synchronise = false) { lock (lockObject) { SetMusic(music, 1, loop, synchronise); } }
private static void SetMusic(SafeSoundEffect safeMusic, int priority, bool loop, bool synchronise) { if (!AudioDevice.Available) { return; } SoundEffect music = safeMusic != null ? safeMusic.soundEffect : null; if (activeMusic[priority].music == music) { return; // Already playing this song } // Get rid of music currently set at this level (possibly with a fade-out, if it is still playing) if (activeMusic[priority].music != null) { if (activeMusic[priority].instance != null) { if (activeMusic[priority].fade == 0) { activeMusic[priority].instance.Dispose(); } else { fadingOutMusic.Add(new FadingOutMusic { fade = activeMusic[priority].fade, instance = activeMusic[priority].instance }); } } activeMusic[priority] = default(ActiveMusic); } if (music == null) { return; // Nothing to play } activeMusic[priority].music = music; activeMusic[priority].loop = loop; activeMusic[priority].synchronise = synchronise; if (synchronise && priority >= BestPriority) { DoFastFade(); // Make the most of the synced music opening } if (synchronise) { StartPlaying(priority, priority >= BestPriority ? 1f : 0f); // <- synced music starts immediately (even if it is at zero volume) } else if (CanStartSong(priority, sufficientFadeOutToStart)) { StartPlaying(priority); } }
private static void InternalInitializeMissingSounds() { #if false // TODO: Add the missing sound sounds back in (need to decode through vorbisfile) { string nelsonPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Nelson.ogg"); if (!File.Exists(nelsonPath)) { Debug.WriteLine("Couldn't find Nelson!"); } else { var sound = new SafeSoundEffect(OggVorbis.OggVorbisDecoder.TryDecode(nelsonPath)); if (sound.soundEffect == null) { Debug.WriteLine("Couldn't load Nelson!"); } else { lock (lockObject) missingSoundEffect = sound; } } } { string howardPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Howard.ogg"); if (!File.Exists(howardPath)) { Debug.WriteLine("Couldn't find Howard!"); } else { var sound = new SafeSoundEffect(OggVorbis.OggVorbisDecoder.TryDecode(howardPath)); if (sound.soundEffect == null) { Debug.WriteLine("Couldn't load Howard!"); } else { lock (lockObject) missingMusic = sound; } } } #endif }
private static void AddAndStartPlaying(SafeSoundEffect sound, FadePitchPan fpp, bool loop = false, int queue = -1) { Debug.Assert(playingSoundCount <= playingSounds.Length); if (playingSoundCount == playingSounds.Length) { Array.Resize(ref playingSounds, playingSounds.Length * 2); } Debug.Assert(playingSounds[playingSoundCount].sound == null); // <- got cleared properly Debug.Assert(playingSounds[playingSoundCount].instance == null); // <- got cleared properly Debug.Assert(playingSounds[playingSoundCount].frameCount == 0); // <- got cleared properly // If we are about to play multiple identical sounds at about the same time, stop them from overlapping: float quashFade = 1f; for (int i = playingSoundCount - 1; i >= 0; i--) { if (playingSounds[i].frameCount >= 3) { break; // <- Reaching sounds that are too old } if (ReferenceEquals(playingSounds[i].sound, sound)) { quashFade -= (1f - ((float)playingSounds[i].frameCount / 3f)); } } // TODO: The following is ugly, because it kills sequential sounds (but odds are they would be killed anyway - and because we just use `fpp.fade`, below, they'd get killed anyway) // If a sound would be quashed completely, just don't play it -- this is required because otherwise the quashed sounds would be taking up simulated channels if (quashFade < 0.1f) { while (queue != -1) // Don't leak the queue, if any { queue = FreeQueuedSound(queue); } return; } // TODO: This is ugly because sequential sounds will inherit this fade level fpp.fade *= MathHelper.Clamp(quashFade, 0f, 1f); if (loop) { playingSounds[playingSoundCount].instance = sound.CreateInstance(); } else { playingSounds[playingSoundCount].instance = sound.SoundEffectManager_GetInstance(); } if (playingSounds[playingSoundCount].instance == null) { while (queue != -1) // Don't leak the queue, if any { queue = FreeQueuedSound(queue); } return; // Failed to create sound instance... oh well. } Debug.Assert(playingSounds[playingSoundCount].instance.IsLooped == false); // <- instance was properly cleared if (loop) // <- Cannot set on used instances (even to the same value) { playingSounds[playingSoundCount].instance.IsLooped = true; } playingSounds[playingSoundCount].sound = sound; playingSounds[playingSoundCount].fpp = fpp; playingSounds[playingSoundCount].queue = queue; playingSounds[playingSoundCount].linkToNext = true; // <- all sounds on a given frame get linked! playingSounds[playingSoundCount].fade = 1f; // <- NOTE: assumed by channel ducking code playingSounds[playingSoundCount].frameCount = 0; fpp.ApplyTo(playingSounds[playingSoundCount].instance, SafeSoundEffect.SoundEffectVolume); playingSounds[playingSoundCount].instance.Play(); playingSoundCount++; }
[CustomSerializer] public static void Deserialize(DeserializeContext context, BinaryReader br, SafeSoundEffect value) { throw new InvalidOperationException(); }
// "Ignore" serializer, as per SerializeIgnoreXNA -- we want to be able to store a ref for the definition table, but we can't deserialize a sound effect [CustomSerializer] public static void Serialize(SerializeContext context, BinaryWriter bw, SafeSoundEffect value) { context.VisitObject(value); context.LeaveObject(); }