mirror of
https://github.com/RHeavenStudioPlus/HeavenStudioPlus.git
synced 2024-11-22 09:35:11 +00:00
Sound Scheduling Improvements (#491)
* port conductor adjustments * scheduled sounds prebake * allow aiff files to be imported add vbr mp3 warning to readme * improve wording
This commit is contained in:
parent
8ec054749e
commit
dde1a3a287
7 changed files with 80 additions and 31 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -84,3 +84,5 @@ crashlytics-build.properties
|
||||||
/[Aa]ssets/[Ss]treamingAssets/*/*
|
/[Aa]ssets/[Ss]treamingAssets/*/*
|
||||||
/[Aa]ssets/[Ss]treamingAssets/*.manifest
|
/[Aa]ssets/[Ss]treamingAssets/*.manifest
|
||||||
/[Aa]ssets/[Ss]treamingAssets/*.meta
|
/[Aa]ssets/[Ss]treamingAssets/*.meta
|
||||||
|
/[Aa]ssets/[Ss]treamingAssets/[Ss]treamingAssets
|
||||||
|
Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Fallback.asset
|
||||||
|
|
|
@ -89,26 +89,32 @@ namespace HeavenStudio
|
||||||
musicSource.pitch = SongPitch;
|
musicSource.pitch = SongPitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
instance = this;
|
instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBeat(double beat)
|
void Start()
|
||||||
{
|
{
|
||||||
double secFromBeat = GetSongPosFromBeat(beat);
|
musicSource.priority = 0;
|
||||||
|
|
||||||
if (musicSource.clip != null)
|
|
||||||
{
|
|
||||||
if (secFromBeat < musicSource.clip.length)
|
|
||||||
musicSource.time = (float) secFromBeat;
|
|
||||||
else
|
|
||||||
musicSource.time = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GameManager.instance.SetCurrentEventToClosest((float) beat);
|
public void SetBeat(double beat)
|
||||||
songPosBeat = beat;
|
{
|
||||||
|
var chart = GameManager.instance.Beatmap;
|
||||||
|
double offset = chart.data.offset;
|
||||||
|
double startPos = GetSongPosFromBeat(beat);
|
||||||
|
|
||||||
|
double dspTime = AudioSettings.dspTime;
|
||||||
|
|
||||||
|
time = startPos;
|
||||||
|
firstBeatOffset = offset;
|
||||||
|
|
||||||
|
SeekMusicToTime(startPos);
|
||||||
|
|
||||||
|
songPosBeat = GetBeatFromSongPos(time);
|
||||||
|
|
||||||
|
GameManager.instance.SetCurrentEventToClosest(beat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Play(double beat)
|
public void Play(double beat)
|
||||||
|
@ -116,35 +122,35 @@ namespace HeavenStudio
|
||||||
if (isPlaying) return;
|
if (isPlaying) return;
|
||||||
var chart = GameManager.instance.Beatmap;
|
var chart = GameManager.instance.Beatmap;
|
||||||
double offset = chart.data.offset;
|
double offset = chart.data.offset;
|
||||||
bool negativeOffset = offset < 0;
|
|
||||||
double dspTime = AudioSettings.dspTime;
|
double dspTime = AudioSettings.dspTime;
|
||||||
GameManager.instance.SortEventsList();
|
GameManager.instance.SortEventsList();
|
||||||
|
|
||||||
double startPos = GetSongPosFromBeat(beat);
|
double startPos = GetSongPosFromBeat(beat);
|
||||||
|
firstBeatOffset = offset;
|
||||||
time = startPos;
|
time = startPos;
|
||||||
|
|
||||||
if (musicSource.clip != null && startPos < musicSource.clip.length - offset)
|
if (musicSource.clip != null && startPos < musicSource.clip.length - offset)
|
||||||
{
|
{
|
||||||
// https://www.desmos.com/calculator/81ywfok6xk
|
SeekMusicToTime(startPos);
|
||||||
double musicStartDelay = -offset - startPos;
|
double musicStartDelay = -offset - startPos;
|
||||||
if (musicStartDelay > 0)
|
if (musicStartDelay > 0)
|
||||||
{
|
{
|
||||||
musicSource.time = 0;
|
|
||||||
// this can break if the user changes pitch before the audio starts playing
|
|
||||||
musicScheduledTime = dspTime + musicStartDelay / SongPitch;
|
musicScheduledTime = dspTime + musicStartDelay / SongPitch;
|
||||||
musicScheduledPitch = SongPitch;
|
musicScheduledPitch = SongPitch;
|
||||||
musicSource.PlayScheduled(musicScheduledTime);
|
musicSource.PlayScheduled(musicScheduledTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
musicSource.time = (float)-musicStartDelay;
|
musicScheduledTime = dspTime;
|
||||||
musicSource.PlayScheduled(dspTime);
|
musicScheduledPitch = SongPitch;
|
||||||
|
|
||||||
|
musicSource.Play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
songPosBeat = GetBeatFromSongPos(time);
|
songPosBeat = GetBeatFromSongPos(time);
|
||||||
startTime = DateTime.Now;
|
startTime = DateTime.Now;
|
||||||
lastAbsTime = (DateTime.Now - startTime).TotalSeconds;
|
lastAbsTime = 0;
|
||||||
lastDspTime = AudioSettings.dspTime;
|
lastDspTime = AudioSettings.dspTime;
|
||||||
dspStart = dspTime;
|
dspStart = dspTime;
|
||||||
startBeat = songPosBeat;
|
startBeat = songPosBeat;
|
||||||
|
@ -175,6 +181,26 @@ namespace HeavenStudio
|
||||||
musicSource.Stop();
|
musicSource.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SeekMusicToTime(double startPos)
|
||||||
|
{
|
||||||
|
double offset = GameManager.instance.Beatmap.data.offset;
|
||||||
|
if (musicSource.clip != null && startPos < musicSource.clip.length - offset)
|
||||||
|
{
|
||||||
|
// https://www.desmos.com/calculator/81ywfok6xk
|
||||||
|
double musicStartDelay = -offset - startPos;
|
||||||
|
if (musicStartDelay > 0)
|
||||||
|
{
|
||||||
|
musicSource.timeSamples = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int freq = musicSource.clip.frequency;
|
||||||
|
int samples = (int)(freq * (startPos + offset));
|
||||||
|
|
||||||
|
musicSource.timeSamples = samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
double deltaTimeReal { get {
|
double deltaTimeReal { get {
|
||||||
double ret = absTime - lastAbsTime;
|
double ret = absTime - lastAbsTime;
|
||||||
lastAbsTime = absTime;
|
lastAbsTime = absTime;
|
||||||
|
@ -217,7 +243,7 @@ namespace HeavenStudio
|
||||||
time += dt * SongPitch;
|
time += dt * SongPitch;
|
||||||
|
|
||||||
songPos = time;
|
songPos = time;
|
||||||
songPosBeat = GetBeatFromSongPos(songPos - firstBeatOffset);
|
songPosBeat = GetBeatFromSongPos(songPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,7 +375,7 @@ namespace HeavenStudio
|
||||||
public double GetBeatFromSongPos(double seconds)
|
public double GetBeatFromSongPos(double seconds)
|
||||||
{
|
{
|
||||||
double lastTempoChangeBeat = 0f;
|
double lastTempoChangeBeat = 0f;
|
||||||
double counterSeconds = -firstBeatOffset;
|
double counterSeconds = 0;
|
||||||
float lastBpm = 120f;
|
float lastBpm = 120f;
|
||||||
|
|
||||||
foreach (RiqEntity t in GameManager.instance.Beatmap.TempoChanges)
|
foreach (RiqEntity t in GameManager.instance.Beatmap.TempoChanges)
|
||||||
|
|
|
@ -250,7 +250,7 @@ namespace HeavenStudio.Editor
|
||||||
{
|
{
|
||||||
var extensions = new[]
|
var extensions = new[]
|
||||||
{
|
{
|
||||||
new ExtensionFilter("Music Files", "mp3", "ogg", "wav")
|
new ExtensionFilter("Music Files", "mp3", "ogg", "wav", "aiff", "aif", "aifc")
|
||||||
};
|
};
|
||||||
|
|
||||||
#if UNITY_STANDALONE_WINDOWS
|
#if UNITY_STANDALONE_WINDOWS
|
||||||
|
|
|
@ -33,7 +33,9 @@ namespace HeavenStudio.Util
|
||||||
|
|
||||||
bool playInstant = false;
|
bool playInstant = false;
|
||||||
bool played = false;
|
bool played = false;
|
||||||
|
bool queued = false;
|
||||||
|
|
||||||
|
const double PREBAKE_TIME = 0.5;
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
audioSource = GetComponent<AudioSource>();
|
audioSource = GetComponent<AudioSource>();
|
||||||
|
@ -56,18 +58,29 @@ namespace HeavenStudio.Util
|
||||||
playInstant = false;
|
playInstant = false;
|
||||||
scheduledPitch = cnd.SongPitch;
|
scheduledPitch = cnd.SongPitch;
|
||||||
startTime = (AudioSettings.dspTime + (cnd.GetSongPosFromBeat(beat) - cnd.songPositionAsDouble)/(double)scheduledPitch) - offset;
|
startTime = (AudioSettings.dspTime + (cnd.GetSongPosFromBeat(beat) - cnd.songPositionAsDouble)/(double)scheduledPitch) - offset;
|
||||||
|
|
||||||
|
if (scheduledPitch != 0 && AudioSettings.dspTime >= startTime)
|
||||||
|
{
|
||||||
audioSource.PlayScheduled(startTime);
|
audioSource.PlayScheduled(startTime);
|
||||||
|
queued = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
Conductor cnd = Conductor.instance;
|
Conductor cnd = Conductor.instance;
|
||||||
|
double dspTime = AudioSettings.dspTime;
|
||||||
if (!played)
|
if (!played)
|
||||||
{
|
{
|
||||||
if (scheduled)
|
if (scheduled)
|
||||||
{
|
{
|
||||||
if (scheduledPitch != 0 && AudioSettings.dspTime > scheduledTime)
|
if (!queued && dspTime > scheduledTime - PREBAKE_TIME)
|
||||||
|
{
|
||||||
|
audioSource.PlayScheduled(scheduledTime);
|
||||||
|
queued = true;
|
||||||
|
}
|
||||||
|
if (scheduledPitch != 0 && dspTime > scheduledTime)
|
||||||
{
|
{
|
||||||
StartCoroutine(NotRelyOnBeatSound());
|
StartCoroutine(NotRelyOnBeatSound());
|
||||||
played = true;
|
played = true;
|
||||||
|
@ -75,7 +88,13 @@ namespace HeavenStudio.Util
|
||||||
}
|
}
|
||||||
else if (!playInstant)
|
else if (!playInstant)
|
||||||
{
|
{
|
||||||
if (scheduledPitch != 0 && AudioSettings.dspTime > startTime)
|
if (!queued && dspTime > startTime - PREBAKE_TIME)
|
||||||
|
{
|
||||||
|
startTime = (dspTime + (cnd.GetSongPosFromBeat(beat) - cnd.songPositionAsDouble)/(double)scheduledPitch) - offset;
|
||||||
|
audioSource.PlayScheduled(startTime);
|
||||||
|
queued = true;
|
||||||
|
}
|
||||||
|
if (scheduledPitch != 0 && dspTime > startTime)
|
||||||
{
|
{
|
||||||
played = true;
|
played = true;
|
||||||
StartCoroutine(NotRelyOnBeatSound());
|
StartCoroutine(NotRelyOnBeatSound());
|
||||||
|
@ -87,6 +106,7 @@ namespace HeavenStudio.Util
|
||||||
if (cnd.SongPitch == 0)
|
if (cnd.SongPitch == 0)
|
||||||
{
|
{
|
||||||
scheduledPitch = cnd.SongPitch;
|
scheduledPitch = cnd.SongPitch;
|
||||||
|
if (queued)
|
||||||
audioSource.Pause();
|
audioSource.Pause();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -96,7 +116,8 @@ namespace HeavenStudio.Util
|
||||||
audioSource.UnPause();
|
audioSource.UnPause();
|
||||||
}
|
}
|
||||||
scheduledPitch = cnd.SongPitch;
|
scheduledPitch = cnd.SongPitch;
|
||||||
startTime = (AudioSettings.dspTime + (cnd.GetSongPosFromBeat(beat) - cnd.songPositionAsDouble)/(double)scheduledPitch);
|
startTime = (dspTime + (cnd.GetSongPosFromBeat(beat) - cnd.songPositionAsDouble)/(double)scheduledPitch);
|
||||||
|
if (queued)
|
||||||
audioSource.SetScheduledStartTime(startTime);
|
audioSource.SetScheduledStartTime(startTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +142,7 @@ namespace HeavenStudio.Util
|
||||||
{
|
{
|
||||||
if (!looping) // Looping sounds are destroyed manually.
|
if (!looping) // Looping sounds are destroyed manually.
|
||||||
{
|
{
|
||||||
yield return new WaitForSeconds(clip.length / pitch);
|
yield return new WaitUntil(() => !audioSource.isPlaying);
|
||||||
Delete();
|
Delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,7 +235,6 @@ namespace HeavenStudio.Util
|
||||||
|
|
||||||
snd.scheduled = true;
|
snd.scheduled = true;
|
||||||
snd.scheduledTime = targetTime;
|
snd.scheduledTime = targetTime;
|
||||||
audioSource.PlayScheduled(targetTime);
|
|
||||||
|
|
||||||
GameManager.instance.SoundObjects.Add(oneShot);
|
GameManager.instance.SoundObjects.Add(oneShot);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
"com.unity.nuget.newtonsoft-json": "3.2.1",
|
||||||
"jillejr.newtonsoft.json-for-unity.converters": "1.5.1"
|
"jillejr.newtonsoft.json-for-unity.converters": "1.5.1"
|
||||||
},
|
},
|
||||||
"hash": "2fb235a6da261fb1ae60112f9e9ba0d924e96bb8"
|
"hash": "ac56f4d95417af5dbedf20f3b8d79b9f67c72659"
|
||||||
},
|
},
|
||||||
"com.unity.2d.sprite": {
|
"com.unity.2d.sprite": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|
|
@ -39,6 +39,7 @@ These builds include experimental new features that will not be included in Rele
|
||||||
|
|
||||||
|
|
||||||
#### Important Notes:
|
#### Important Notes:
|
||||||
|
- MP3 audio with variable bitrate encoding may [desync when seeking in the editor](https://github.com/RHeavenStudio/HeavenStudio/issues/490). Either use MP3s with constant bitrate encoding or use one of our other supported formats (OGG Vorbis, WAV...)
|
||||||
- On MacOS and Linux builds you may [experience bugs with audio-related tasks](https://github.com/RHeavenStudio/HeavenStudio/issues/72), but in most cases Heaven Studio works perfectly.
|
- On MacOS and Linux builds you may [experience bugs with audio-related tasks](https://github.com/RHeavenStudio/HeavenStudio/issues/72), but in most cases Heaven Studio works perfectly.
|
||||||
- On MacOS you'll need to have Discord open in the background for now, there's a bug that causes the DiscordSDK library to crash when the rich presence is updated while Discord is not open in the background.
|
- On MacOS you'll need to have Discord open in the background for now, there's a bug that causes the DiscordSDK library to crash when the rich presence is updated while Discord is not open in the background.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue