Reworking Simple Sound Manager to be something way better.
This commit is contained in:
parent
81c0078b78
commit
ff612c1bb0
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SimpleSoundManager.Framework.Music
|
||||
{
|
||||
/// <summary>
|
||||
/// Config class for the mod.
|
||||
/// </summary>
|
||||
public class Config
|
||||
{
|
||||
public bool EnableDebugLog;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public Config()
|
||||
{
|
||||
EnableDebugLog = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using StardewValley;
|
||||
|
||||
namespace SimpleSoundManager.Framework
|
||||
{
|
||||
/// <summary>Manages all music for the mod.</summary>
|
||||
public class MusicManager
|
||||
{
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The RNG used to select music packs and songs.</summary>
|
||||
private readonly Random Random = new Random();
|
||||
|
||||
/// <summary>The delay timer between songs.</summary>
|
||||
private readonly Timer Timer = new Timer();
|
||||
|
||||
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The loaded music packs.</summary>
|
||||
public IDictionary<string, MusicPack> MusicPacks { get; } = new Dictionary<string, MusicPack>();
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
public MusicManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Adds a valid xwb music pack to the list of music packs available.</summary>
|
||||
/// <param name="musicPack">The music pack to add.</param>
|
||||
/// <param name="displayLogInformation">Whether or not to display the process to the console. Will include information from the pack's metadata. Default:False</param>
|
||||
/// <param name="displaySongs">If displayLogInformation is also true this will display the name of all of the songs in the music pack when it is added in.</param>
|
||||
public void addMusicPack(MusicPack musicPack, bool displayLogInformation = false, bool displaySongs = false)
|
||||
{
|
||||
if (displayLogInformation)
|
||||
{
|
||||
if (ModCore.Config.EnableDebugLog)
|
||||
{
|
||||
ModCore.ModMonitor.Log("Adding music pack:");
|
||||
ModCore.ModMonitor.Log($" Name: {musicPack.Name}");
|
||||
ModCore.ModMonitor.Log($" Author: {musicPack.Manifest.Author}");
|
||||
ModCore.ModMonitor.Log($" Description: {musicPack.Manifest.Description}");
|
||||
ModCore.ModMonitor.Log($" Version Info: {musicPack.Manifest.Version}");
|
||||
}
|
||||
if (displaySongs && ModCore.Config.EnableDebugLog)
|
||||
{
|
||||
ModCore.ModMonitor.Log(" Song List:");
|
||||
foreach (string songName in musicPack.Sounds.Keys)
|
||||
ModCore.ModMonitor.Log($" {songName}");
|
||||
}
|
||||
}
|
||||
|
||||
this.MusicPacks.Add(musicPack.Name, musicPack);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the specified sound from the music pack.
|
||||
/// </summary>
|
||||
/// <param name="packName"></param>
|
||||
/// <param name="soundName"></param>
|
||||
public void playSound(string packName, string soundName)
|
||||
{
|
||||
if (this.MusicPacks.ContainsKey(packName))
|
||||
{
|
||||
this.MusicPacks[packName].PlaySound(soundName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ModCore.DebugLog("No pack with specified key/name: " + packName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops a said sound from the music pack.
|
||||
/// </summary>
|
||||
/// <param name="packName"></param>
|
||||
/// <param name="soundName"></param>
|
||||
public void stopSound(string packName,string soundName)
|
||||
{
|
||||
if (this.MusicPacks.ContainsKey(packName))
|
||||
{
|
||||
this.MusicPacks[packName].StopSound();
|
||||
}
|
||||
else
|
||||
{
|
||||
ModCore.DebugLog("No pack with specified key/name: " + packName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates all music packs every so often.
|
||||
/// </summary>
|
||||
public void update()
|
||||
{
|
||||
foreach(MusicPack pack in this.MusicPacks.Values)
|
||||
{
|
||||
pack.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using NAudio.Vorbis;
|
||||
using NAudio.Wave;
|
||||
using StardewModdingAPI;
|
||||
using StardewValley;
|
||||
|
||||
namespace SimpleSoundManager.Framework
|
||||
{
|
||||
/// <summary>A content pack which can provide music and sounds.</summary>
|
||||
public class MusicPack
|
||||
{
|
||||
/*********
|
||||
** Fields
|
||||
*********/
|
||||
/// <summary>The name of the folder which contains the saved player settings.</summary>
|
||||
private readonly string DataFolderName = "data";
|
||||
|
||||
/// <summary>The name of the folder which contains available music.</summary>
|
||||
private readonly string MusicFolderName = "music";
|
||||
|
||||
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The underlying content pack.</summary>
|
||||
public IContentPack ContentPack { get; }
|
||||
|
||||
/// <summary>The current song name being played, if any.</summary>
|
||||
public string lastPlayedSoundName { get; private set; }
|
||||
|
||||
/// <summary>The currently sound being played, if any.</summary>
|
||||
public SoundEffectInstance lastPlayedSound { get; private set; }
|
||||
|
||||
public Dictionary<string, List<SoundEffectInstance>> playingSounds;
|
||||
|
||||
/// <summary>The manifest info.</summary>
|
||||
public IManifest Manifest => this.ContentPack.Manifest;
|
||||
|
||||
/// <summary>The name of the music pack.</summary>
|
||||
public string Name => this.ContentPack.Manifest.Name;
|
||||
|
||||
/// <summary>The available sounds.</summary>
|
||||
public Dictionary<string, SoundEffectInstance> Sounds { get; } = new Dictionary<string, SoundEffectInstance>();
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="contentPack">The underlying content pack.</param>
|
||||
public MusicPack(IContentPack contentPack)
|
||||
{
|
||||
this.ContentPack = contentPack;
|
||||
this.playingSounds = new Dictionary<string, List<SoundEffectInstance>>();
|
||||
this.LoadMusicFiles();
|
||||
}
|
||||
|
||||
/// <summary>Play a song.</summary>
|
||||
/// <param name="name">The song name to play.</param>
|
||||
public void PlaySound(string name)
|
||||
{
|
||||
// get sound
|
||||
if (!this.Sounds.TryGetValue(name, out SoundEffectInstance sound))
|
||||
{
|
||||
ModCore.ModMonitor.Log("An error occured where we can't find the song anymore. Weird. Please contact Omegasis with a SMAPI Log and describe when/how the event occured.");
|
||||
return;
|
||||
}
|
||||
|
||||
// play sound
|
||||
this.lastPlayedSoundName = name;
|
||||
this.lastPlayedSound = sound;
|
||||
this.lastPlayedSound.Play();
|
||||
|
||||
if (this.playingSounds.ContainsKey(name))
|
||||
{
|
||||
this.playingSounds[name].Add(sound);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.playingSounds.Add(name,new List<SoundEffectInstance>());
|
||||
this.playingSounds[name].Add(sound);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Stop the currently playing song.</summary>
|
||||
public void StopSound()
|
||||
{
|
||||
this.lastPlayedSound?.Stop(true);
|
||||
this.lastPlayedSoundName = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops all of the currently playing sounds.
|
||||
/// </summary>
|
||||
public void stopAllSounds()
|
||||
{
|
||||
foreach (string soundName in playingSounds.Keys)
|
||||
{
|
||||
for (int i = 0; i < playingSounds[soundName].Count; i++)
|
||||
{
|
||||
if (playingSounds[soundName][i].State == SoundState.Stopped)
|
||||
{
|
||||
playingSounds[soundName][i].Stop(true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the music pack to clean up all sounds that have been stopped.
|
||||
/// </summary>
|
||||
public void update()
|
||||
{
|
||||
///Clean up the list.
|
||||
foreach(string soundName in playingSounds.Keys)
|
||||
{
|
||||
for(int i = 0; i < playingSounds[soundName].Count; i++)
|
||||
{
|
||||
if(playingSounds[soundName][i].State== SoundState.Stopped)
|
||||
{
|
||||
playingSounds[soundName].RemoveAt(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Get whether the content pack is currently playing a sound.</summary>
|
||||
public bool IsPlaying()
|
||||
{
|
||||
return this.lastPlayedSound?.State == SoundState.Playing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if there is a sound with said name playing.
|
||||
/// </summary>
|
||||
/// <param name="SoundName"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsPlaying(string SoundName)
|
||||
{
|
||||
return this.playingSounds[SoundName].FindAll(s => s.State== SoundState.Playing).Count>0;
|
||||
}
|
||||
|
||||
/*********
|
||||
** Private methods
|
||||
*********/
|
||||
|
||||
/// <summary>Load in the music files from the pack's respective Directory/Songs folder. Typically Content/Music/Wav/FolderName/Songs</summary>
|
||||
private void LoadMusicFiles()
|
||||
{
|
||||
DateTime startTime = DateTime.Now;
|
||||
|
||||
DirectoryInfo songFolder = new DirectoryInfo(Path.Combine(this.ContentPack.DirectoryPath, this.MusicFolderName));
|
||||
foreach (FileInfo file in songFolder.GetFiles())
|
||||
{
|
||||
// get name
|
||||
string name = Path.GetFileNameWithoutExtension(file.Name);
|
||||
if (this.Sounds.ContainsKey(name))
|
||||
continue;
|
||||
|
||||
// load data
|
||||
SoundEffect effect = null;
|
||||
using (Stream waveFileStream = File.OpenRead(file.FullName))
|
||||
{
|
||||
switch (file.Extension)
|
||||
{
|
||||
case ".wav":
|
||||
effect = SoundEffect.FromStream(waveFileStream);
|
||||
break;
|
||||
|
||||
case ".mp3":
|
||||
using (Mp3FileReader reader = new Mp3FileReader(waveFileStream))
|
||||
using (WaveStream pcmStream = WaveFormatConversionStream.CreatePcmStream(reader))
|
||||
{
|
||||
string tempPath = Path.Combine(songFolder.FullName, $"{name}.wav");
|
||||
ModCore.ModMonitor.Log($"Converting: {tempPath}");
|
||||
|
||||
WaveFileWriter.CreateWaveFile(tempPath, pcmStream);
|
||||
using (Stream tempStream = File.OpenRead(tempPath))
|
||||
effect = SoundEffect.FromStream(tempStream);
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
break;
|
||||
|
||||
case ".ogg":
|
||||
// Credits: https://social.msdn.microsoft.com/Forums/vstudio/en-US/100a97af-2a1c-4b28-b464-d43611b9b5d6/converting-multichannel-ogg-to-stereo-wav-file?forum=csharpgeneral
|
||||
using (VorbisWaveReader vorbisStream = new VorbisWaveReader(file.FullName))
|
||||
{
|
||||
string tempPath = Path.Combine(songFolder.FullName, $"{name}.wav");
|
||||
ModCore.DebugLog($"Converting: {tempPath}");
|
||||
|
||||
WaveFileWriter.CreateWaveFile(tempPath, vorbisStream.ToWaveProvider16());
|
||||
using (Stream tempStream = File.OpenRead(tempPath))
|
||||
effect = SoundEffect.FromStream(tempStream);
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ModCore.ModMonitor.Log($"Unsupported file extension {file.Extension}.", LogLevel.Warn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (effect == null)
|
||||
continue;
|
||||
|
||||
// add sound
|
||||
SoundEffectInstance instance = effect.CreateInstance();
|
||||
this.Sounds.Add(name, instance);
|
||||
}
|
||||
|
||||
// log loading time
|
||||
if (ModCore.Config.EnableDebugLog)
|
||||
ModCore.ModMonitor.Log($"Time to load WAV music pack {this.Name}: {startTime.Subtract(DateTime.Now)}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace SimpleSoundManager.Framework
|
||||
{
|
||||
/// <summary>A class that keeps track of the trigger and the list of songs associated with that trigger.</summary>
|
||||
internal class SongListNode
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The trigger name for the list of songs.</summary>
|
||||
public string Trigger { get; }
|
||||
|
||||
/// <summary>The list of songs associated with a trigger.</summary>
|
||||
public string[] SongList { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="trigger">The trigger name for the list of songs.</param>
|
||||
/// <param name="songList">The list of songs associated with a trigger.</param>
|
||||
public SongListNode(string trigger, IEnumerable<string> songList)
|
||||
{
|
||||
this.Trigger = trigger;
|
||||
this.SongList = songList.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
namespace SimpleSoundManager.Framework
|
||||
{
|
||||
/// <summary>Interface used for common sound functionality;</summary>
|
||||
public interface Sound
|
||||
{
|
||||
/// <summary>Handles playing a sound.</summary>
|
||||
void play();
|
||||
|
||||
void play(float volume);
|
||||
|
||||
/// <summary>Handles pausing a song.</summary>
|
||||
void pause();
|
||||
|
||||
/// <summary>Handles stopping a song.</summary>
|
||||
void stop();
|
||||
|
||||
/// <summary>Handles restarting a song.</summary>
|
||||
void restart();
|
||||
|
||||
/// <summary>Handles getting a clone of the song.</summary>
|
||||
Sound clone();
|
||||
|
||||
string getSoundName();
|
||||
|
||||
bool isStopped();
|
||||
}
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using StardewModdingAPI;
|
||||
using StardewValley;
|
||||
|
||||
namespace SimpleSoundManager.Framework
|
||||
{
|
||||
public class SoundManager
|
||||
{
|
||||
public Dictionary<string, Sound> sounds;
|
||||
public Dictionary<string, XACTMusicPair> musicBanks;
|
||||
|
||||
public float volume;
|
||||
|
||||
public List<Sound> currentlyPlayingSounds = new List<Sound>();
|
||||
|
||||
/// <summary>Constructor for this class.</summary>
|
||||
public SoundManager()
|
||||
{
|
||||
this.sounds = new Dictionary<string, Sound>();
|
||||
this.musicBanks = new Dictionary<string, XACTMusicPair>();
|
||||
this.currentlyPlayingSounds = new List<Sound>();
|
||||
this.volume = 1.0f;
|
||||
}
|
||||
|
||||
/// <summary>Constructor for wav files.</summary>
|
||||
public void loadWavFile(string soundName, string pathToWav)
|
||||
{
|
||||
WavSound wav = new WavSound(soundName, pathToWav);
|
||||
SimpleSoundManagerMod.ModMonitor.Log("Getting sound file:" + soundName);
|
||||
try
|
||||
{
|
||||
this.sounds.Add(soundName, wav);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>Constructor for wav files.</summary>
|
||||
public void loadWavFile(IModHelper helper, string soundName, string relativePath)
|
||||
{
|
||||
WavSound wav = new WavSound(helper, soundName, relativePath);
|
||||
SimpleSoundManagerMod.ModMonitor.Log("Getting sound file:" + soundName);
|
||||
try
|
||||
{
|
||||
this.sounds.Add(soundName, wav);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Sound already added so no need to worry?
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Constructor for wav files.</summary>
|
||||
public void loadWavFile(IModHelper helper, string songName, List<string> pathToWav)
|
||||
{
|
||||
WavSound wav = new WavSound(helper, songName, pathToWav);
|
||||
SimpleSoundManagerMod.ModMonitor.Log("Getting sound file:" + songName);
|
||||
try
|
||||
{
|
||||
this.sounds.Add(songName, wav);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>Constructor for XACT files.</summary>
|
||||
public void loadXACTFile(WaveBank waveBank, ISoundBank soundBank, string songName)
|
||||
{
|
||||
XACTSound xactSound = new XACTSound(waveBank, soundBank, songName);
|
||||
this.sounds.Add(songName, xactSound);
|
||||
}
|
||||
|
||||
/// <summary>Constructor for XACT files based on already added music packs.</summary>
|
||||
public void loadXACTFile(string pairName, string songName)
|
||||
{
|
||||
XACTMusicPair musicPair = this.getMusicPack(pairName);
|
||||
if (pairName == null)
|
||||
return;
|
||||
this.loadXACTFile(musicPair.waveBank, musicPair.soundBank, songName);
|
||||
}
|
||||
|
||||
/// <summary>Creates a music pack pair that holds .xwb and .xsb music files.</summary>
|
||||
/// <param name="helper">The mod's helper that will handle the path of the files.</param>
|
||||
/// <param name="pairName">The name of this music pack pair.</param>
|
||||
/// <param name="wavName">The relative path to the .xwb file</param>
|
||||
/// <param name="soundName">The relative path to the .xsb file</param>
|
||||
public void loadXACTMusicBank(IModHelper helper, string pairName, string wavName, string soundName)
|
||||
{
|
||||
this.musicBanks.Add(pairName, new XACTMusicPair(helper, wavName, soundName));
|
||||
}
|
||||
|
||||
/// <summary>Gets the music pack pair from the sound pool.</summary>
|
||||
public XACTMusicPair getMusicPack(string name)
|
||||
{
|
||||
foreach (var pack in this.musicBanks)
|
||||
{
|
||||
if (name == pack.Key)
|
||||
return pack.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Gets a clone of the loaded sound.</summary>
|
||||
public Sound getSoundClone(string name)
|
||||
{
|
||||
foreach (var sound in this.sounds)
|
||||
{
|
||||
if (sound.Key == name)
|
||||
return sound.Value.clone();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Play the sound with the given name.</summary>
|
||||
public void playSound(string soundName)
|
||||
{
|
||||
SimpleSoundManagerMod.ModMonitor.Log("Trying to play sound: " + soundName);
|
||||
foreach (var sound in this.sounds)
|
||||
{
|
||||
if (sound.Key == soundName)
|
||||
{
|
||||
SimpleSoundManagerMod.ModMonitor.Log("Time to play sound: " + soundName);
|
||||
var s = this.getSoundClone(soundName);
|
||||
s.play(this.volume);
|
||||
this.currentlyPlayingSounds.Add(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Play the sound with the given name and volume.</summary>
|
||||
public void playSound(string soundName, float volume = 1.0f)
|
||||
{
|
||||
SimpleSoundManagerMod.ModMonitor.Log("Trying to play sound: " + soundName);
|
||||
foreach (var sound in this.sounds)
|
||||
{
|
||||
if (sound.Key == soundName)
|
||||
{
|
||||
SimpleSoundManagerMod.ModMonitor.Log("Time to play sound: " + soundName);
|
||||
var s = this.getSoundClone(soundName);
|
||||
s.play(volume);
|
||||
this.currentlyPlayingSounds.Add(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stop the sound that is playing.</summary>
|
||||
public void stopSound(string soundName)
|
||||
{
|
||||
List<Sound> removalList = new List<Sound>();
|
||||
foreach (var sound in this.currentlyPlayingSounds)
|
||||
{
|
||||
if (sound.getSoundName() == soundName)
|
||||
{
|
||||
sound.stop();
|
||||
removalList.Add(sound);
|
||||
}
|
||||
}
|
||||
foreach (var v in removalList)
|
||||
this.currentlyPlayingSounds.Remove(v);
|
||||
}
|
||||
|
||||
/// <summary>Pause the sound with this name?</summary>
|
||||
public void pauseSound(string soundName)
|
||||
{
|
||||
List<Sound> removalList = new List<Sound>();
|
||||
foreach (var sound in this.currentlyPlayingSounds)
|
||||
{
|
||||
if (sound.getSoundName() == soundName)
|
||||
{
|
||||
sound.pause();
|
||||
removalList.Add(sound);
|
||||
}
|
||||
}
|
||||
foreach (var v in removalList)
|
||||
this.currentlyPlayingSounds.Remove(v);
|
||||
}
|
||||
|
||||
public void swapSounds(string newSong)
|
||||
{
|
||||
this.playSound(newSong);
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
List<Sound> removalList = new List<Sound>();
|
||||
foreach (Sound song in this.currentlyPlayingSounds)
|
||||
{
|
||||
if (song.isStopped())
|
||||
removalList.Add(song);
|
||||
}
|
||||
foreach (var v in removalList)
|
||||
this.currentlyPlayingSounds.Remove(v);
|
||||
}
|
||||
|
||||
public void stopAllSounds()
|
||||
{
|
||||
foreach (var v in this.currentlyPlayingSounds)
|
||||
v.stop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,211 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using SimpleSoundManager.Framework;
|
||||
using StardewModdingAPI;
|
||||
|
||||
namespace SimpleSoundManager
|
||||
{
|
||||
class WavSound : Sound
|
||||
{
|
||||
|
||||
/// <summary>Used to actually play the song.</summary>
|
||||
DynamicSoundEffectInstance dynamicSound;
|
||||
|
||||
/// <summary>Used to keep track of where in the song we are.</summary>
|
||||
int position;
|
||||
|
||||
int count;
|
||||
|
||||
/// <summary>Used to store the info for the song.</summary>
|
||||
byte[] byteArray;
|
||||
|
||||
public string path;
|
||||
|
||||
public string soundName;
|
||||
|
||||
public bool loop;
|
||||
|
||||
/// <summary>Get a raw disk path to the wav file.</summary>
|
||||
public WavSound(string name, string pathToWavFile, bool loop = false)
|
||||
{
|
||||
this.path = pathToWavFile;
|
||||
this.LoadWavFromFileToStream();
|
||||
this.soundName = name;
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
/// <summary>A constructor that takes a mod helper and a relative path to a wav file.</summary>
|
||||
public WavSound(IModHelper modHelper, string name, string relativePath, bool loop = false)
|
||||
{
|
||||
string path = Path.Combine(modHelper.DirectoryPath, relativePath);
|
||||
this.path = path;
|
||||
this.soundName = name;
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
/// <summary>Constructor that is more flexible than typing an absolute path.</summary>
|
||||
/// <param name="modHelper">The mod helper for the mod you wish to use to load the music files from.</param>
|
||||
/// <param name="pathPieces">The list of folders and files that make up a complete path.</param>
|
||||
public WavSound(IModHelper modHelper, string soundName, List<string> pathPieces, bool loop = false)
|
||||
{
|
||||
string dirPath = modHelper.DirectoryPath;
|
||||
foreach (string str in pathPieces)
|
||||
dirPath = Path.Combine(dirPath, str);
|
||||
this.path = dirPath;
|
||||
this.soundName = soundName;
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
/// <summary>Loads the .wav file from disk and plays it.</summary>
|
||||
public void LoadWavFromFileToStream()
|
||||
{
|
||||
// Create a new SpriteBatch, which can be used to draw textures.
|
||||
|
||||
string file = this.path;
|
||||
Stream waveFileStream = File.OpenRead(file); //TitleContainer.OpenStream(file);
|
||||
|
||||
BinaryReader reader = new BinaryReader(waveFileStream);
|
||||
|
||||
int chunkID = reader.ReadInt32();
|
||||
int fileSize = reader.ReadInt32();
|
||||
int riffType = reader.ReadInt32();
|
||||
int fmtID = reader.ReadInt32();
|
||||
int fmtSize = reader.ReadInt32();
|
||||
int fmtCode = reader.ReadInt16();
|
||||
int channels = reader.ReadInt16();
|
||||
int sampleRate = reader.ReadInt32();
|
||||
int fmtAvgBPS = reader.ReadInt32();
|
||||
int fmtBlockAlign = reader.ReadInt16();
|
||||
int bitDepth = reader.ReadInt16();
|
||||
|
||||
if (fmtSize == 18)
|
||||
{
|
||||
// Read any extra values
|
||||
int fmtExtraSize = reader.ReadInt16();
|
||||
reader.ReadBytes(fmtExtraSize);
|
||||
}
|
||||
|
||||
int dataID = reader.ReadInt32();
|
||||
int dataSize = reader.ReadInt32();
|
||||
|
||||
this.byteArray = reader.ReadBytes(dataSize);
|
||||
|
||||
|
||||
this.dynamicSound = new DynamicSoundEffectInstance(sampleRate, (AudioChannels)channels);
|
||||
this.count = this.byteArray.Length;//dynamicSound.GetSampleSizeInBytes(TimeSpan.FromMilliseconds(1000));
|
||||
|
||||
this.dynamicSound.BufferNeeded += this.DynamicSound_BufferNeeded;
|
||||
}
|
||||
|
||||
void DynamicSound_BufferNeeded(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.dynamicSound.SubmitBuffer(this.byteArray, this.position, this.count);
|
||||
}
|
||||
catch { }
|
||||
|
||||
this.position += this.count;
|
||||
if (this.position + this.count > this.byteArray.Length)
|
||||
{
|
||||
|
||||
if (this.loop)
|
||||
this.position = 0;
|
||||
//else
|
||||
// this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Used to pause the current song.</summary>
|
||||
public void pause()
|
||||
{
|
||||
this.dynamicSound?.Pause();
|
||||
}
|
||||
|
||||
/// <summary>Used to play a song.</summary>
|
||||
public void play()
|
||||
{
|
||||
if (this.isPlaying())
|
||||
return;
|
||||
|
||||
this.LoadWavFromFileToStream();
|
||||
this.dynamicSound.Play();
|
||||
}
|
||||
|
||||
/// <summary>Used to play a song.</summary>
|
||||
/// <param name="volume">How lound the sound is when playing. 0~1.0f</param>
|
||||
public void play(float volume)
|
||||
{
|
||||
if (this.isPlaying())
|
||||
return;
|
||||
|
||||
this.LoadWavFromFileToStream();
|
||||
this.dynamicSound.Volume = volume;
|
||||
this.dynamicSound.Play();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Used to resume the currently playing song.</summary>
|
||||
public void resume()
|
||||
{
|
||||
this.dynamicSound?.Resume();
|
||||
}
|
||||
|
||||
/// <summary>Used to stop the currently playing song.</summary>
|
||||
public void stop()
|
||||
{
|
||||
if (this.dynamicSound != null)
|
||||
{
|
||||
this.dynamicSound.Stop(true);
|
||||
this.dynamicSound.BufferNeeded -= this.DynamicSound_BufferNeeded;
|
||||
this.position = 0;
|
||||
this.count = 0;
|
||||
this.byteArray = new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Used to change from one playing song to another;</summary>
|
||||
public void swap(string pathToNewWavFile)
|
||||
{
|
||||
this.stop();
|
||||
this.path = pathToNewWavFile;
|
||||
this.play();
|
||||
}
|
||||
|
||||
/// <summary>Checks if the song is currently playing.</summary>
|
||||
public bool isPlaying()
|
||||
{
|
||||
return this.dynamicSound?.State == SoundState.Playing;
|
||||
}
|
||||
|
||||
/// <summary>Checks if the song is currently paused.</summary>
|
||||
public bool isPaused()
|
||||
{
|
||||
return this.dynamicSound?.State == SoundState.Paused;
|
||||
}
|
||||
|
||||
/// <summary>Checks if the song is currently stopped.</summary>
|
||||
public bool isStopped()
|
||||
{
|
||||
return this.dynamicSound?.State == SoundState.Stopped;
|
||||
}
|
||||
|
||||
public Sound clone()
|
||||
{
|
||||
return new WavSound(this.getSoundName(), this.path);
|
||||
}
|
||||
|
||||
public string getSoundName()
|
||||
{
|
||||
return this.soundName;
|
||||
}
|
||||
|
||||
public void restart()
|
||||
{
|
||||
this.stop();
|
||||
this.play();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
using Microsoft.Xna.Framework.Audio;
|
||||
using StardewValley;
|
||||
|
||||
namespace SimpleSoundManager.Framework
|
||||
{
|
||||
public class XACTSound : Sound
|
||||
{
|
||||
public WaveBank waveBank;
|
||||
public ISoundBank soundBank;
|
||||
public string soundName;
|
||||
readonly WaveBank vanillaWaveBank;
|
||||
readonly ISoundBank vanillaSoundBank;
|
||||
readonly Cue song;
|
||||
|
||||
/// <summary>Make a new Sound Manager to play and manage sounds in a modded wave bank.</summary>
|
||||
/// <param name="newWaveBank">The reference to the wave bank in the mod's asset folder.</param>
|
||||
/// <param name="newSoundBank">The reference to the sound bank in the mod's asset folder.</param>
|
||||
public XACTSound(WaveBank newWaveBank, ISoundBank newSoundBank, string soundName)
|
||||
{
|
||||
this.waveBank = newWaveBank;
|
||||
this.soundBank = newSoundBank;
|
||||
|
||||
this.vanillaSoundBank = Game1.soundBank;
|
||||
this.vanillaWaveBank = Game1.waveBank;
|
||||
this.soundName = soundName;
|
||||
this.song = this.soundBank.GetCue(this.soundName);
|
||||
}
|
||||
|
||||
/// <summary>Play a sound from the mod's wave bank.</summary>
|
||||
/// <param name="soundName">The name of the sound in the mod's wave bank. This will fail if the sound doesn't exists. This is also case sensitive.</param>
|
||||
public void play(string soundName)
|
||||
{
|
||||
Game1.waveBank = this.waveBank;
|
||||
Game1.soundBank = this.soundBank;
|
||||
|
||||
if (this.song == null) return;
|
||||
|
||||
this.song.Play();
|
||||
|
||||
Game1.waveBank = this.vanillaWaveBank;
|
||||
Game1.soundBank = this.vanillaSoundBank;
|
||||
}
|
||||
|
||||
/// <summary>Pauses the first instance of this sound.</summary>
|
||||
/// <param name="soundName"></param>
|
||||
public void pause(string soundName)
|
||||
{
|
||||
this.song?.Pause();
|
||||
}
|
||||
|
||||
/// <summary>Resume the first instance of the sound that has this name.</summary>
|
||||
public void resume(string soundName)
|
||||
{
|
||||
this.song?.Resume();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Stop the first instance of the sound that has this name.</summary>
|
||||
/// <param name="soundName"></param>
|
||||
public void stop(string soundName)
|
||||
{
|
||||
this.song?.Stop(AudioStopOptions.Immediate);
|
||||
}
|
||||
|
||||
/// <summary>Resumes a paused song.</summary>
|
||||
public void resume()
|
||||
{
|
||||
this.resume(this.soundName);
|
||||
}
|
||||
|
||||
/// <summary>Plays this song.</summary>
|
||||
public void play()
|
||||
{
|
||||
this.play(this.soundName);
|
||||
}
|
||||
|
||||
/// <summary>Plays this song.</summary>
|
||||
public void play(float volume)
|
||||
{
|
||||
this.play(this.soundName);
|
||||
}
|
||||
|
||||
/// <summary>Pauses this song.</summary>
|
||||
public void pause()
|
||||
{
|
||||
this.pause(this.soundName);
|
||||
}
|
||||
|
||||
/// <summary>Stops this somg.</summary>
|
||||
public void stop()
|
||||
{
|
||||
this.stop(this.soundName);
|
||||
}
|
||||
|
||||
/// <summary>Restarts this song.</summary>
|
||||
public void restart()
|
||||
{
|
||||
this.stop();
|
||||
this.play();
|
||||
}
|
||||
|
||||
/// <summary>Gets a clone of this song.</summary>
|
||||
public Sound clone()
|
||||
{
|
||||
return new XACTSound(this.waveBank, this.soundBank, this.soundName);
|
||||
}
|
||||
|
||||
public string getSoundName()
|
||||
{
|
||||
return this.soundName;
|
||||
}
|
||||
|
||||
public bool isStopped()
|
||||
{
|
||||
return this.song == null || this.song.IsStopped;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
using System.IO;
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using StardewModdingAPI;
|
||||
using StardewValley;
|
||||
|
||||
namespace SimpleSoundManager
|
||||
{
|
||||
public class XACTMusicPair
|
||||
{
|
||||
public WaveBank waveBank;
|
||||
public ISoundBank soundBank;
|
||||
|
||||
/// <summary>Create a xwb and xsb music pack pair.</summary>
|
||||
/// <param name="helper">The mod helper from the mod that will handle loading in the file.</param>
|
||||
/// <param name="wavBankPath">A relative path to the .xwb file in the mod helper's mod directory.</param>
|
||||
/// <param name="soundBankPath">A relative path to the .xsb file in the mod helper's mod directory.</param>
|
||||
public XACTMusicPair(IModHelper helper, string wavBankPath, string soundBankPath)
|
||||
{
|
||||
wavBankPath = Path.Combine(helper.DirectoryPath, wavBankPath);
|
||||
soundBankPath = Path.Combine(helper.DirectoryPath, soundBankPath);
|
||||
|
||||
this.waveBank = new WaveBank(Game1.audioEngine, wavBankPath);
|
||||
this.soundBank = new SoundBankWrapper(new SoundBank(Game1.audioEngine, soundBankPath));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using SimpleSoundManager.Framework;
|
||||
using SimpleSoundManager.Framework.Music;
|
||||
using StardewModdingAPI;
|
||||
|
||||
namespace SimpleSoundManager
|
||||
{
|
||||
public class ModCore : Mod
|
||||
{
|
||||
internal static IModHelper ModHelper;
|
||||
internal static IMonitor ModMonitor;
|
||||
internal static Config Config;
|
||||
public static MusicManager MusicManager;
|
||||
|
||||
/// <summary>The mod entry point, called after the mod is first loaded.</summary>
|
||||
/// <param name="helper">Provides simplified APIs for writing mods.</param>
|
||||
public override void Entry(IModHelper helper)
|
||||
{
|
||||
ModHelper = helper;
|
||||
ModMonitor = this.Monitor;
|
||||
Config = helper.ReadConfig<Config>();
|
||||
this.loadContentPacks();
|
||||
this.Helper.Events.GameLoop.OneSecondUpdateTicked += this.GameLoop_OneSecondUpdateTicked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update all music packs every second.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void GameLoop_OneSecondUpdateTicked(object sender, StardewModdingAPI.Events.OneSecondUpdateTickedEventArgs e)
|
||||
{
|
||||
foreach(MusicPack pack in MusicManager.MusicPacks.Values)
|
||||
{
|
||||
pack.update();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all content packs for SimpleSoundManager
|
||||
/// </summary>
|
||||
private void loadContentPacks()
|
||||
{
|
||||
MusicManager = new MusicManager();
|
||||
foreach (IContentPack contentPack in this.Helper.ContentPacks.GetOwned())
|
||||
{
|
||||
this.Monitor.Log($"Reading content pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} from {contentPack.DirectoryPath}");
|
||||
MusicPack musicPack = new MusicPack(contentPack);
|
||||
MusicManager.addMusicPack(musicPack, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Easy way to display debug logs when allowing for a check to see if they are enabled.
|
||||
/// </summary>
|
||||
/// <param name="s">The message to display.</param>
|
||||
public static void DebugLog(string s)
|
||||
{
|
||||
if (Config.EnableDebugLog)
|
||||
ModMonitor.Log(s);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,6 +66,12 @@
|
|||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NAudio">
|
||||
<Version>1.8.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NAudio.Vorbis">
|
||||
<Version>1.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pathoschild.Stardew.ModBuildConfig" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -73,12 +79,11 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Framework\Sound.cs" />
|
||||
<Compile Include="Framework\SoundManager.cs" />
|
||||
<Compile Include="Framework\WavSound.cs" />
|
||||
<Compile Include="Framework\XactMusicPair.cs" />
|
||||
<Compile Include="Framework\XACTSound.cs" />
|
||||
<Compile Include="SimpleSoundManagerMod.cs" />
|
||||
<Compile Include="Framework\Music\Config.cs" />
|
||||
<Compile Include="Framework\Music\MusicManager.cs" />
|
||||
<Compile Include="Framework\Music\MusicPack.cs" />
|
||||
<Compile Include="Framework\Music\SongListNode.cs" />
|
||||
<Compile Include="ModCore.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
using StardewModdingAPI;
|
||||
|
||||
namespace SimpleSoundManager
|
||||
{
|
||||
public class SimpleSoundManagerMod : Mod
|
||||
{
|
||||
internal static IModHelper ModHelper;
|
||||
internal static IMonitor ModMonitor;
|
||||
|
||||
/// <summary>The mod entry point, called after the mod is first loaded.</summary>
|
||||
/// <param name="helper">Provides simplified APIs for writing mods.</param>
|
||||
public override void Entry(IModHelper helper)
|
||||
{
|
||||
ModHelper = helper;
|
||||
ModMonitor = this.Monitor;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue