forked from YARC-Official/YARG
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cleanup of audio code to make it more manageable (YARC-Official#146)
- Loading branch information
1 parent
7a250dd
commit 85610de
Showing
20 changed files
with
880 additions
and
463 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,4 +27,8 @@ public enum SfxSample { | |
StarPowerRelease, | ||
Clap, | ||
} | ||
|
||
public enum DSPType { | ||
Gain, | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using ManagedBass; | ||
using ManagedBass.DirectX8; | ||
using ManagedBass.Fx; | ||
using UnityEngine; | ||
using UnityEngine.Serialization; | ||
using Debug = UnityEngine.Debug; | ||
|
||
namespace YARG { | ||
public class BassAudioManager : MonoBehaviour, IAudioManager { | ||
public bool UseStarpowerFx { get; set; } | ||
public bool IsChipmunkSpeedup { get; set; } | ||
|
||
public IList<string> SupportedFormats { get; private set; } | ||
|
||
public bool IsAudioLoaded { get; private set; } | ||
public bool IsPlaying { get; private set; } | ||
|
||
public double CurrentPositionD => GetPosition(); | ||
public double AudioLengthD { get; private set; } | ||
|
||
public float CurrentPositionF => (float) GetPosition(); | ||
public float AudioLengthF { get; private set; } | ||
|
||
public double[] stemVolumes; | ||
public double sfxVolume; | ||
|
||
private int opusHandle; | ||
|
||
private IStemMixer _mixer; | ||
|
||
private ISampleChannel[] _sfxSamples; | ||
|
||
private void Awake() { | ||
SupportedFormats = new[] { | ||
".ogg", | ||
".mogg", | ||
".wav", | ||
".mp3", | ||
".aiff", | ||
".opus", | ||
}; | ||
|
||
stemVolumes = new double[AudioHelpers.SupportedStems.Count]; | ||
|
||
_sfxSamples = new ISampleChannel[AudioHelpers.SfxPaths.Count]; | ||
|
||
opusHandle = 0; | ||
} | ||
|
||
public void Initialize() { | ||
Debug.Log("Initializing BASS..."); | ||
string bassPath = GetBassDirectory(); | ||
string opusLibDirectory = Path.Combine(bassPath, "bassopus"); | ||
|
||
opusHandle = Bass.PluginLoad(opusLibDirectory); | ||
Bass.Configure(Configuration.IncludeDefaultDevice, true); | ||
|
||
Bass.UpdatePeriod = 5; | ||
Bass.DeviceBufferLength = 10; | ||
Bass.PlaybackBufferLength = 100; | ||
Bass.DeviceNonStop = true; | ||
|
||
Bass.Configure(Configuration.TruePlayPosition, 0); | ||
Bass.Configure(Configuration.UpdateThreads, 2); | ||
Bass.Configure(Configuration.FloatDSP, true); | ||
|
||
Bass.Configure((Configuration) 68, 1); | ||
Bass.Configure((Configuration) 70, false); | ||
|
||
int deviceCount = Bass.DeviceCount; | ||
Debug.Log($"Devices found: {deviceCount}"); | ||
|
||
if (!Bass.Init(-1, 44100, DeviceInitFlags.Default | DeviceInitFlags.Latency, IntPtr.Zero)) { | ||
Debug.LogError("Failed to initialize BASS"); | ||
Debug.LogError($"Bass Error: {Bass.LastError}"); | ||
return; | ||
} | ||
|
||
LoadSfx(); | ||
|
||
Debug.Log($"BASS Successfully Initialized"); | ||
Debug.Log($"BASS: {Bass.Version}"); | ||
Debug.Log($"BASS.FX: {Bass.Version}"); | ||
Debug.Log($"BASS.Mix: {Bass.Version}"); | ||
|
||
Debug.Log($"Update Period: {Bass.UpdatePeriod}"); | ||
Debug.Log($"Device Buffer Length: {Bass.DeviceBufferLength}"); | ||
Debug.Log($"Playback Buffer Length: {Bass.PlaybackBufferLength}"); | ||
|
||
Debug.Log($"Current Device: {Bass.GetDeviceInfo(Bass.CurrentDevice).Name}"); | ||
} | ||
|
||
public void Unload() { | ||
Debug.Log("Unloading BASS plugins"); | ||
|
||
UnloadSong(); | ||
|
||
Bass.PluginFree(opusHandle); | ||
opusHandle = 0; | ||
|
||
// Free SFX samples | ||
foreach (var sample in _sfxSamples) { | ||
sample.Dispose(); | ||
} | ||
|
||
Bass.Free(); | ||
} | ||
|
||
public void LoadSfx() { | ||
Debug.Log("Loading SFX"); | ||
|
||
_sfxSamples = new ISampleChannel[AudioHelpers.SfxPaths.Count]; | ||
|
||
string sfxFolder = Path.Combine(Application.streamingAssetsPath, "sfx"); | ||
|
||
foreach (string sfxFile in AudioHelpers.SfxPaths) { | ||
string sfxPath = Path.Combine(sfxFolder, sfxFile); | ||
|
||
foreach (string format in SupportedFormats) { | ||
if (!File.Exists($"{sfxPath}{format}")) | ||
continue; | ||
|
||
// Append extension to path (e.g sfx/boop becomes sfx/boop.ogg) | ||
sfxPath += format; | ||
break; | ||
} | ||
|
||
|
||
if (!File.Exists(sfxPath)) { | ||
Debug.LogError($"SFX path does not exist! {sfxPath}"); | ||
continue; | ||
} | ||
var sfxSample = AudioHelpers.GetSfxFromName(sfxFile); | ||
|
||
var sfx = new BassSampleChannel(this, sfxPath, 8, sfxSample); | ||
if (sfx.Load() != 0) { | ||
Debug.LogError($"Failed to load SFX! {sfxPath}"); | ||
Debug.LogError($"Bass Error: {Bass.LastError}"); | ||
continue; | ||
} | ||
|
||
_sfxSamples[(int)sfxSample] = sfx; | ||
Debug.Log($"Loaded {sfxFile}"); | ||
} | ||
|
||
Debug.Log("Finished loading SFX"); | ||
} | ||
|
||
public void LoadSong(IEnumerable<string> stems, bool isSpeedUp) { | ||
Debug.Log("Loading song"); | ||
UnloadSong(); | ||
|
||
_mixer = new BassStemMixer(); | ||
if (!_mixer.Create()) { | ||
throw new Exception("Failed to create mixer"); | ||
} | ||
|
||
foreach (string stemPath in stems) { | ||
// Gets the file name with no extensions (i.e guitar.ogg -> guitar) | ||
string stemName = Path.GetFileNameWithoutExtension(stemPath); | ||
|
||
// Gets the index (SongStem to int) from the name | ||
var songStem = AudioHelpers.GetStemFromName(stemName); | ||
|
||
var stemChannel = new BassStemChannel(this, stemPath, songStem); | ||
if (stemChannel.Load(isSpeedUp, PlayMode.Play.speed) != 0) { | ||
Debug.LogError($"Failed to load stem! {stemPath}"); | ||
Debug.LogError($"Bass Error: {Bass.LastError}"); | ||
continue; | ||
} | ||
|
||
if (_mixer.GetChannel(songStem) != null) { | ||
Debug.LogError($"Stem already loaded! {stemPath}"); | ||
continue; | ||
} | ||
|
||
if (_mixer.AddChannel(stemChannel) != 0) { | ||
Debug.LogError($"Failed to add stem to mixer!"); | ||
Debug.LogError($"Bass Error: {Bass.LastError}"); | ||
} | ||
} | ||
|
||
Debug.Log($"Loaded {_mixer.StemsLoaded} stems"); | ||
|
||
// Setup audio length | ||
AudioLengthD = _mixer.LeadChannel.LengthD; | ||
AudioLengthF = (float) AudioLengthD; | ||
|
||
IsAudioLoaded = true; | ||
} | ||
|
||
public void UnloadSong() { | ||
IsPlaying = false; | ||
IsAudioLoaded = false; | ||
|
||
// Free mixer (and all channels in it) | ||
_mixer?.Dispose(); | ||
} | ||
|
||
public void Play() { | ||
// Don't try to play if there's no audio loaded or if it's already playing | ||
if (!IsAudioLoaded || IsPlaying) { | ||
return; | ||
} | ||
|
||
if (_mixer.Play() != 0) { | ||
Debug.Log($"Play error: {Bass.LastError}"); | ||
} | ||
|
||
IsPlaying = _mixer.IsPlaying; | ||
} | ||
|
||
public void Pause() { | ||
if (!IsAudioLoaded || !IsPlaying) { | ||
return; | ||
} | ||
|
||
if (_mixer.Pause() != 0) { | ||
Debug.Log($"Pause error: {Bass.LastError}"); | ||
} | ||
|
||
IsPlaying = _mixer.IsPlaying; | ||
} | ||
|
||
public void PlaySoundEffect(SfxSample sample) { | ||
var sfx = _sfxSamples[(int) sample]; | ||
|
||
sfx?.Play(); | ||
} | ||
|
||
public void SetStemVolume(SongStem stem, double volume) { | ||
var channel = _mixer.GetChannel(stem); | ||
|
||
// Multiply volume inputted by the stem's volume setting (e.g. 0.5 * 0.5 = 0.25) | ||
channel?.SetVolume(volume * stemVolumes[(int) stem]); | ||
} | ||
|
||
public void UpdateVolumeSetting(SongStem stem, double volume) { | ||
switch (stem) | ||
{ | ||
case SongStem.Master: | ||
Bass.GlobalStreamVolume = (int) (10_000 * volume); | ||
break; | ||
case SongStem.Sfx: | ||
sfxVolume = volume; | ||
break; | ||
default: | ||
stemVolumes[(int) stem] = volume; | ||
break; | ||
} | ||
} | ||
|
||
public void ApplyReverb(SongStem stem, bool reverb) { | ||
_mixer?.GetChannel(stem)?.SetReverb(reverb); | ||
} | ||
|
||
public double GetPosition() { | ||
if (_mixer is null) | ||
return -1; | ||
|
||
return _mixer.GetPosition(); | ||
} | ||
|
||
public void SetPosition(double position) { | ||
throw new NotImplementedException(); | ||
} | ||
|
||
private void OnApplicationQuit() { | ||
Unload(); | ||
} | ||
|
||
private static string GetBassDirectory() { | ||
string pluginDirectory = Path.Combine(Application.dataPath, "Plugins"); | ||
|
||
// Locate windows directory | ||
// Checks if running on 64 bit and sets the path accordingly | ||
#if !UNITY_EDITOR && UNITY_STANDALONE_WIN | ||
#if UNITY_64 | ||
pluginDirectory = Path.Combine(pluginDirectory, "x86_64"); | ||
#else | ||
pluginDirectory = Path.Combine(pluginDirectory, "x86"); | ||
#endif | ||
#endif | ||
|
||
// Unity Editor directory, Assets/Plugins/Bass/ | ||
#if UNITY_EDITOR | ||
pluginDirectory = Path.Combine(pluginDirectory, "BassNative"); | ||
#endif | ||
|
||
// Editor paths differ to standalone paths, as the project contains platform specific folders | ||
#if UNITY_EDITOR_WIN | ||
pluginDirectory = Path.Combine(pluginDirectory, "Windows/x86_64"); | ||
#elif UNITY_EDITOR_OSX | ||
pluginDirectory = Path.Combine(pluginDirectory, "Mac"); | ||
#elif UNITY_EDITOR_LINUX | ||
pluginDirectory = Path.Combine(pluginDirectory, "Linux/x86_64"); | ||
#endif | ||
|
||
return pluginDirectory; | ||
} | ||
} | ||
} |
File renamed without changes.
Oops, something went wrong.