Updated SSM to be able to handle XWB and WAV files.

This commit is contained in:
2018-05-31 14:38:24 -07:00
parent 9fe6a075de
commit 02d3ab39e5
7 changed files with 515 additions and 147 deletions

View File

@ -74,7 +74,11 @@
<ItemGroup>
<Compile Include="SimpleSoundManagerMod.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sound.cs" />
<Compile Include="SoundManager.cs" />
<Compile Include="WavSound.cs" />
<Compile Include="XACTMusicPair.cs" />
<Compile Include="XACTSound.cs" />
</ItemGroup>
<ItemGroup>
<None Include="manifest.json" />

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SimpleSoundManager
{
/// <summary>
/// Interface used for common sound functionality;
/// </summary>
public interface Sound
{
/// <summary>
/// Handles playing a sound.
/// </summary>
void play();
/// <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>
/// <returns></returns>
Sound clone();
}
}

View File

@ -9,200 +9,124 @@ using System.Threading.Tasks;
namespace SimpleSoundManager
{
public class SoundManager
class SoundManager
{
public WaveBank waveBank;
public ISoundBank soundBank;
WaveBank vanillaWaveBank;
ISoundBank vanillaSoundBank;
List<Cue> currentlyPlayingSounds;
public Dictionary<string,Sound> sounds;
public Dictionary<string, XACTMusicPair> musicBanks;
/// <summary>
/// Make a new Sound Manager to play and manage sounds in a modded wave bank.
/// Constructor for this class.
/// </summary>
/// <param name="newWaveBank">The path to the wave bank in the mod's asset folder.</param>
/// <param name="newSoundBank">The path to the sound bank in the mod's asset folder.</param>
public SoundManager(string newWaveBank, string newSoundBank)
public SoundManager()
{
this.waveBank = new WaveBank(Game1.audioEngine, newWaveBank);
this.soundBank = (ISoundBank)new SoundBankWrapper(new SoundBank(Game1.audioEngine, newSoundBank));
this.currentlyPlayingSounds = new List<Cue>();
vanillaWaveBank = Game1.waveBank;
vanillaSoundBank = Game1.soundBank;
this.sounds = new Dictionary<string, Sound>();
this.musicBanks = new Dictionary<string, XACTMusicPair>();
}
/// <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 SoundManager(WaveBank newWaveBank, ISoundBank newSoundBank)
{
this.waveBank = newWaveBank;
this.soundBank = newSoundBank;
this.currentlyPlayingSounds = new List<Cue>();
}
/// <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 playSound(string soundName)
{
Game1.waveBank = this.waveBank;
Game1.soundBank = this.soundBank;
Cue currentCue = this.soundBank.GetCue(soundName);
if (currentCue == null) return;
else
{
currentCue.Play();
currentlyPlayingSounds.Add(currentCue);
}
Game1.waveBank = this.vanillaWaveBank;
Game1.soundBank = this.vanillaSoundBank;
removeAllStopedSounds();
}
/// <summary>
/// Pauses the first instance of this sound.
/// Constructor for wav files.
/// </summary>
/// <param name="soundName"></param>
public void pauseSound(string soundName)
/// <param name="pathToWav"></param>
public void loadWavFile(string soundName,string pathToWav)
{
foreach (var v in currentlyPlayingSounds)
{
if (v.Name == soundName) v.Pause();
break;
}
removeAllStopedSounds();
WavSound wav = new WavSound(pathToWav);
this.sounds.Add(soundName,wav);
}
/// <summary>
/// Pause all sounds that share the sound name in common.
/// Constructor for wav files.
/// </summary>
/// <param name="helper"></param>
/// <param name="soundName"></param>
public void pauseAllSoundsWithThisName(string soundName)
/// <param name="pathToWav"></param>
public void loadWavFile(IModHelper helper,string soundName,string pathToWav)
{
foreach (var v in currentlyPlayingSounds)
{
if (v.Name == soundName) v.Pause();
}
removeAllStopedSounds();
WavSound wav = new WavSound(helper ,pathToWav);
this.sounds.Add(soundName,wav);
}
/// <summary>
/// Pauses all of the sounds that the SoundManager class is keeping track of.
/// Constructor for wav files.
/// </summary>
public void pauseAllSounds()
/// <param name="helper"></param>
/// <param name="songName"></param>
/// <param name="pathToWav"></param>
public void loadWavFile(IModHelper helper,string songName,List<string> pathToWav)
{
foreach(var v in currentlyPlayingSounds)
{
v.Pause();
}
removeAllStopedSounds();
WavSound wav = new WavSound(helper,pathToWav);
this.sounds.Add(songName,wav);
}
/// <summary>
/// Resume the first instance of the sound that has this name.
/// Constructor for XACT files.
/// </summary>
public void resumeSound(string soundName)
/// <param name="waveBank"></param>
/// <param name="soundBank"></param>
/// <param name="songName"></param>
public void loadXACTFile(WaveBank waveBank, ISoundBank soundBank, string songName)
{
foreach(var v in currentlyPlayingSounds)
{
if (v.Name==soundName&&v.IsPaused) v.Resume();
break;
}
removeAllStopedSounds();
XACTSound xactSound = new XACTSound(waveBank, soundBank, songName);
this.sounds.Add(songName, xactSound);
}
/// <summary>
/// Resume all paused sounds that have this name.
/// Constructor for XACT files based on already added music packs.
/// </summary>
/// <param name="soundName"></param>
public void resumeAllSoundsWithThisName(string soundName)
/// <param name="pairName"></param>
/// <param name="songName"></param>
public void loadXACTFile(string pairName, string songName)
{
foreach (var v in currentlyPlayingSounds)
XACTMusicPair musicPair = getMusicPack(pairName);
if (pairName == null)
{
if (v.Name == soundName && v.IsPaused) v.Resume();
return;
}
removeAllStopedSounds();
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>
/// Resumes playing all paused sounds.
/// Gets the music pack pair from the sound pool.
/// </summary>
public void resumeAllSounds()
/// <param name="name"></param>
/// <returns></returns>
public XACTMusicPair getMusicPack(string name)
{
foreach (var v in currentlyPlayingSounds)
foreach(var pack in this.musicBanks)
{
if (v.IsPaused) v.Resume();
if (name == pack.Key) return pack.Value;
}
removeAllStopedSounds();
return null;
}
/// <summary>
/// Stop the first instance of the sound that has this name.
/// Gets a clone of the loaded sound.
/// </summary>
/// <param name="soundName"></param>
public void stopSound(string soundName)
/// <param name="name"></param>
/// <returns></returns>
public Sound getSoundClone(string name)
{
foreach (var v in currentlyPlayingSounds)
foreach(var sound in this.sounds)
{
if (v.Name == soundName)
{
v.Stop(AudioStopOptions.Immediate);
break;
if (sound.Key == name) return sound.Value.clone();
}
}
removeAllStopedSounds();
return null;
}
/// <summary>
/// Stops all of the sounds that share this name.
/// </summary>
/// <param name="soundName"></param>
public void stopAllSoundsWithThisName(string soundName)
{
foreach (var v in currentlyPlayingSounds)
{
if (v.Name == soundName)
{
v.Stop(AudioStopOptions.Immediate);
}
}
removeAllStopedSounds();
}
/// <summary>
/// Stops all of the sounds that the SoundManager is keeping track of.
/// </summary>
public void stopAllSounds()
{
foreach(var v in currentlyPlayingSounds)
{
v.Stop(AudioStopOptions.Immediate);
}
removeAllStopedSounds();
}
/// <summary>
/// Removes all of the sounds that have stoped playing. Used to clean up the list of songs that SoundManager is keeping track of, whether the sound is finished or it manually was stopped.
/// </summary>
public void removeAllStopedSounds()
{
List<Cue> cuesToRemove = new List<Cue>();
foreach (var v in currentlyPlayingSounds)
{
if (v.IsStopped) cuesToRemove.Add(v);
}
foreach (var v in cuesToRemove)
{
currentlyPlayingSounds.Remove(v);
}
cuesToRemove.Clear();
}
}
}

View File

@ -0,0 +1,236 @@
using Microsoft.Xna.Framework.Audio;
using StardewModdingAPI;
using StardewValley;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
/// <summary>
/// ???
/// </summary>
int count;
/// <summary>
/// Used to store the info for the song.
/// </summary>
byte[] byteArray;
public List<string> sounds;
public string path;
/// <summary>
/// Get a raw disk path to the wav file.
/// </summary>
/// <param name="pathToWavFile"></param>
public WavSound(string pathToWavFile)
{
this.path = pathToWavFile;
LoadWavFromFileToStream();
}
/// <summary>
/// A constructor that takes a mod helper and a relative path to a wav file.
/// </summary>
/// <param name="modHelper"></param>
/// <param name="pathInModDirectory"></param>
public WavSound(IModHelper modHelper, string pathInModDirectory)
{
string path = Path.Combine(modHelper.DirectoryPath, pathInModDirectory);
this.path = path;
}
/// <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, List<string> pathPieces)
{
string s = modHelper.DirectoryPath;
foreach(var str in pathPieces)
{
s = Path.Combine(s, str);
}
this.path = s;
}
/// <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;
System.IO.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();
byteArray = reader.ReadBytes(dataSize);
dynamicSound = new DynamicSoundEffectInstance(sampleRate, (AudioChannels)channels);
count = dynamicSound.GetSampleSizeInBytes(TimeSpan.FromMilliseconds(10000));
}
void DynamicSound_BufferNeeded(object sender, EventArgs e)
{
try
{
dynamicSound.SubmitBuffer(byteArray, position, count);
}
catch (Exception err)
{
}
position += count;
if (position + count > byteArray.Length)
{
position = 0;
}
}
/// <summary>
/// Used to pause the current song.
/// </summary>
public void pause()
{
if (dynamicSound != null) dynamicSound.Pause();
}
/// <summary>
/// Used to play a song.
/// </summary>
/// <param name="name"></param>
public void play()
{
if (this.isPlaying() == true) return;
dynamicSound.BufferNeeded += new EventHandler<EventArgs>(DynamicSound_BufferNeeded);
dynamicSound.Play();
}
/// <summary>
/// Used to resume the currently playing song.
/// </summary>
public void resume()
{
if (dynamicSound == null) return;
dynamicSound.Resume();
}
/// <summary>
/// Used to stop the currently playing song.
/// </summary>
public void stop()
{
if (dynamicSound != null)
{
dynamicSound.Stop(true);
dynamicSound.BufferNeeded -= new EventHandler<EventArgs>(DynamicSound_BufferNeeded);
position = 0;
count = 0;
byteArray = new byte[0];
}
}
/// <summary>
/// Used to change from one playing song to another;
/// </summary>
/// <param name="songName"></param>
public void swap(string pathToNewWavFile)
{
this.stop();
this.path = pathToNewWavFile;
this.play();
}
/// <summary>
/// Checks if the song is currently playing.
/// </summary>
/// <returns></returns>
public bool isPlaying()
{
if (this.dynamicSound == null) return false;
if (this.dynamicSound.State == SoundState.Playing) return true;
else return false;
}
/// <summary>
/// Checks if the song is currently paused.
/// </summary>
/// <returns></returns>
public bool isPaused()
{
if (this.dynamicSound == null) return false;
if (this.dynamicSound.State == SoundState.Paused) return true;
else return false;
}
/// <summary>
/// Checks if the song is currently stopped.
/// </summary>
/// <returns></returns>
public bool isStopped()
{
if (this.dynamicSound == null) return false;
if (this.dynamicSound.State == SoundState.Stopped) return true;
else return false;
}
public Sound clone()
{
return new WavSound(this.path);
}
public void restart()
{
this.stop();
this.play();
}
}
}

View File

@ -0,0 +1,134 @@
using Microsoft.Xna.Framework.Audio;
using StardewModdingAPI;
using StardewValley;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SimpleSoundManager
{
public class XACTSound : Sound
{
public WaveBank waveBank;
public ISoundBank soundBank;
public string soundName;
WaveBank vanillaWaveBank;
ISoundBank vanillaSoundBank;
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;
vanillaSoundBank = Game1.soundBank;
vanillaWaveBank = Game1.waveBank;
this.soundName = soundName;
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)
{
if (this.song == null) return;
this.song.Pause();
}
/// <summary>
/// Resume the first instance of the sound that has this name.
/// </summary>
public void resume(string soundName)
{
if (this.song == null) return;
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)
{
if (this.song == null) return;
this.song.Stop(AudioStopOptions.Immediate);
}
/// <summary>
/// Resumes a paused song.
/// </summary>
public void resume()
{
this.resume(soundName);
}
/// <summary>
/// Plays this song.
/// </summary>
public void play()
{
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>
/// <returns></returns>
public Sound clone()
{
return new XACTSound(this.waveBank, this.soundBank, this.soundName);
}
}
}

View File

@ -0,0 +1,34 @@
using Microsoft.Xna.Framework.Audio;
using StardewModdingAPI;
using StardewValley;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SimpleSoundManager
{
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);
waveBank = new WaveBank(Game1.audioEngine, wavBankPath);
soundBank = new SoundBankWrapper(new SoundBank(Game1.audioEngine, soundBankPath));
}
}
}

View File

@ -1,7 +1,7 @@
{
"Name": "Simple Sound Manager",
"Author": "Alpha_Omegasis",
"Version": "1.0.1",
"Version": "2.0.0",
"Description": "A simple framework to play sounds from wave banks.",
"UniqueID": "Omegasis.SimpleSoundManager",
"EntryDll": "SimpleSoundManager.dll",