2020-05-31 17:07:22 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using GTAAudioSharp;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using SanAndreasUnity.Audio;
|
2022-01-20 23:31:25 +00:00
|
|
|
|
using SanAndreasUnity.Utilities;
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
|
|
namespace SanAndreasUnity.Behaviours.Audio
|
|
|
|
|
{
|
2022-01-04 15:57:25 +00:00
|
|
|
|
public struct SoundId
|
|
|
|
|
{
|
|
|
|
|
public bool isStream;
|
|
|
|
|
public string fileName;
|
|
|
|
|
public int bankIndex;
|
|
|
|
|
public int audioIndex;
|
2022-01-04 17:52:27 +00:00
|
|
|
|
|
|
|
|
|
public bool IsValid => !string.IsNullOrEmpty(fileName) && bankIndex >= 0 && audioIndex >= 0;
|
2022-01-04 15:57:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-01-20 23:31:25 +00:00
|
|
|
|
public class AudioManager : StartupSingleton<AudioManager>
|
2020-05-31 17:07:22 +00:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
private static GTAAudioFiles s_gtaAudioFiles;
|
|
|
|
|
public static GTAAudioFiles AudioFiles { get { return s_gtaAudioFiles; } }
|
|
|
|
|
|
|
|
|
|
// private Dictionary<string, AudioClip> sfxAudioClips = new Dictionary<string, AudioClip>();
|
|
|
|
|
|
|
|
|
|
// private Dictionary<string, AudioClip> streamsAudioClips = new Dictionary<string, AudioClip>();
|
|
|
|
|
|
|
|
|
|
// private Dictionary<string, AudioStream> streamsAudioStreams = new Dictionary<string, AudioStream>();
|
|
|
|
|
|
|
|
|
|
public bool playStartupSound = true;
|
|
|
|
|
public float startupSoundTimeOffset = 0f;
|
|
|
|
|
static AudioSource s_startupAudioSource;
|
|
|
|
|
|
|
|
|
|
public const int kSfxSampleSize = 2;
|
|
|
|
|
|
2022-01-04 15:57:25 +00:00
|
|
|
|
private static Dictionary<SoundId, AudioClip> _cachedMouthAudioClips = new Dictionary<SoundId, AudioClip>();
|
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
|
|
|
2022-01-23 04:50:32 +00:00
|
|
|
|
static AudioManager()
|
|
|
|
|
{
|
|
|
|
|
Loader.onLoadingFinished -= OnLoadingFinished;
|
|
|
|
|
Loader.onLoadingFinished += OnLoadingFinished;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 23:31:25 +00:00
|
|
|
|
protected override void OnSingletonDisable()
|
2020-05-31 17:07:22 +00:00
|
|
|
|
{
|
|
|
|
|
if (s_gtaAudioFiles != null)
|
|
|
|
|
{
|
|
|
|
|
s_gtaAudioFiles.Dispose ();
|
|
|
|
|
s_gtaAudioFiles = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-23 04:50:32 +00:00
|
|
|
|
static void OnLoadingFinished ()
|
2020-05-31 17:07:22 +00:00
|
|
|
|
{
|
|
|
|
|
if (s_startupAudioSource != null)
|
|
|
|
|
{
|
2022-01-22 00:00:56 +00:00
|
|
|
|
F.DestroyEvenInEditMode(s_startupAudioSource.gameObject);
|
2020-05-31 17:07:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static void InitFromLoader ()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
s_gtaAudioFiles = GTAAudio.OpenRead (Path.Combine (Utilities.Config.GamePath, "audio"));
|
|
|
|
|
|
2022-01-20 23:31:25 +00:00
|
|
|
|
if (Singleton.playStartupSound)
|
2020-05-31 17:07:22 +00:00
|
|
|
|
{
|
|
|
|
|
s_startupAudioSource = PlayStream ("Beats", 1);
|
|
|
|
|
if (s_startupAudioSource != null)
|
2022-01-20 23:31:25 +00:00
|
|
|
|
s_startupAudioSource.time = Singleton.startupSoundTimeOffset;
|
2020-05-31 17:07:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static AudioClip CreateAudioClipFromStream (string streamFileName, int bankIndex)
|
|
|
|
|
{
|
|
|
|
|
AudioStream audio_stream = null;
|
|
|
|
|
System.DateTime startTime = System.DateTime.Now;
|
|
|
|
|
|
|
|
|
|
int streams_bank_index = bankIndex;
|
|
|
|
|
string streams_file_name = streamFileName;
|
|
|
|
|
string key = streams_file_name + "." + streams_bank_index;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((s_gtaAudioFiles != null) && (streams_bank_index >= 0))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Stream stream = s_gtaAudioFiles.OpenStreamsAudioStreamByName(streams_file_name, (uint)streams_bank_index);
|
|
|
|
|
if (stream != null)
|
|
|
|
|
{
|
|
|
|
|
audio_stream = new AudioStream(stream, key, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (audio_stream != null)
|
|
|
|
|
{
|
|
|
|
|
System.TimeSpan time_span = System.DateTime.Now - startTime;
|
|
|
|
|
Debug.Log("\"" + key + "\" took " + time_span.TotalSeconds + " seconds.");
|
|
|
|
|
|
|
|
|
|
return audio_stream.AudioClip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static AudioSource PlayStream (string streamFileName, int bankIndex)
|
|
|
|
|
{
|
|
|
|
|
var clip = CreateAudioClipFromStream (streamFileName, bankIndex);
|
|
|
|
|
if (clip != null)
|
|
|
|
|
{
|
|
|
|
|
AudioSource audioSource = new GameObject (streamFileName + "." + bankIndex).AddComponent<AudioSource> ();
|
|
|
|
|
audioSource.time = 0.0f;
|
|
|
|
|
audioSource.clip = clip;
|
|
|
|
|
audioSource.Play();
|
|
|
|
|
|
|
|
|
|
// destroy game object when sound is finished playing
|
2022-01-22 00:00:56 +00:00
|
|
|
|
if (!F.IsAppInEditTime)
|
|
|
|
|
Destroy( audioSource.gameObject, clip.length );
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
|
|
return audioSource;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:12:34 +00:00
|
|
|
|
public static AudioClip CreateAudioClipFromSfx (string sfxFileName, int bankIndex, int audioIndex)
|
2020-05-31 17:07:22 +00:00
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (null == s_gtaAudioFiles || bankIndex < 0 || audioIndex < 0)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
AudioClip ret = null;
|
|
|
|
|
string clipName = sfxFileName + "." + bankIndex + "." + audioIndex;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using (GTAAudioStream audio_stream = s_gtaAudioFiles.OpenSFXAudioStreamByName(sfxFileName, (uint)bankIndex, (uint)audioIndex))
|
|
|
|
|
{
|
|
|
|
|
if (audio_stream != null)
|
|
|
|
|
{
|
2019-08-28 17:12:34 +00:00
|
|
|
|
ret = CreateAudioClipFromSfx (clipName, audio_stream);
|
2020-05-31 17:07:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:12:34 +00:00
|
|
|
|
static AudioClip CreateAudioClipFromSfx (string clipName, GTAAudioStream audio_stream)
|
2020-05-31 17:07:22 +00:00
|
|
|
|
{
|
|
|
|
|
AudioClip ret = null;
|
|
|
|
|
|
2019-08-28 17:12:34 +00:00
|
|
|
|
byte[] int_pcm = new byte[audio_stream.Length];
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
|
|
if (audio_stream.Read(int_pcm, 0, int_pcm.Length) == int_pcm.Length)
|
|
|
|
|
{
|
|
|
|
|
float[] float_pcm = new float[int_pcm.Length / sizeof(short)];
|
|
|
|
|
for (int i = 0; i < float_pcm.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
float_pcm[i] = Mathf.Clamp(((short)(int_pcm[i * sizeof(short)] | (int_pcm[(i * sizeof(short)) + 1] << 8)) / 32767.5f), -1.0f, 1.0f);
|
|
|
|
|
}
|
2019-08-28 17:12:34 +00:00
|
|
|
|
ret = AudioClip.Create(clipName, float_pcm.Length, 1, audio_stream.SampleRate, false);
|
2020-05-31 17:07:22 +00:00
|
|
|
|
ret.SetData(float_pcm, 0);
|
|
|
|
|
|
2019-08-28 19:49:49 +00:00
|
|
|
|
// Debug.LogFormat("loaded sfx: name {0}, offset {1}, size {2}, length {3}, bitrate Kb/s {4}, stream size {5}, freq {6}",
|
|
|
|
|
// clipName, 0, 0, ret.length,
|
|
|
|
|
// FreqToKbs (audio_stream.SampleRate), audio_stream.Length, audio_stream.SampleRate);
|
2020-05-31 17:07:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-04 15:57:25 +00:00
|
|
|
|
public static AudioClip CreateAudioClip(SoundId soundId)
|
|
|
|
|
{
|
|
|
|
|
return soundId.isStream
|
|
|
|
|
? CreateAudioClipFromStream(soundId.fileName, soundId.bankIndex)
|
|
|
|
|
: CreateAudioClipFromSfx(soundId.fileName, soundId.bankIndex, soundId.audioIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static AudioClip GetAudioClipCached(SoundId soundId)
|
|
|
|
|
{
|
|
|
|
|
if (!_cachedMouthAudioClips.TryGetValue(soundId, out AudioClip audioClip))
|
|
|
|
|
{
|
|
|
|
|
audioClip = AudioManager.CreateAudioClip(soundId);
|
|
|
|
|
_cachedMouthAudioClips.Add(soundId, audioClip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return audioClip;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
|
static float FreqToKbs(int freq)
|
|
|
|
|
{
|
|
|
|
|
return freq * kSfxSampleSize * 8f / 1000f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float KbsToFreq(float kbs)
|
|
|
|
|
{
|
|
|
|
|
return kbs / 8f * 1000f / kSfxSampleSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|