Skip to content

Commit 4697a60

Browse files
author
Ethan Cheung
committed
fix(Haptics): allow AudioSourceHapticPulser be placed on other object
The pulser uses OnAudioFilterRead, it has to be placed right beneath the audio source. But the way it is presented in the inpsector deceived us to think it can be placed elsewhere. This fix allows it to be placed on other object. Also fixed not getting pulse due to frame lag behind audio thread too much.
1 parent 72d9102 commit 4697a60

1 file changed

Lines changed: 75 additions & 6 deletions

File tree

Runtime/Haptics/AudioSourceHapticPulser.cs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using UnityEngine;
44
using System.Collections;
5+
using Malimbe.MemberClearanceMethod;
56
using Malimbe.PropertySerializationAttribute;
67
using Malimbe.XmlDocumentationAttribute;
78

@@ -17,6 +18,10 @@ public class AudioSourceHapticPulser : RoutineHapticPulser
1718
[field: DocumentedByXml]
1819
public AudioSource AudioSource { get; set; }
1920

21+
/// <summary>
22+
/// Observer added to <see cref="AudioSource"/>.
23+
/// </summary>
24+
protected AudioSourceHapticPulserDataObserver observer;
2025
/// <summary>
2126
/// <see cref="AudioSettings.dspTime"/> of the last <see cref="OnAudioFilterRead"/>.
2227
/// </summary>
@@ -30,6 +35,17 @@ public class AudioSourceHapticPulser : RoutineHapticPulser
3035
/// </summary>
3136
protected int filterReadChannels;
3237

38+
protected virtual void OnEnable()
39+
{
40+
AddDataObserver();
41+
}
42+
43+
protected override void OnDisable()
44+
{
45+
base.OnDisable();
46+
RemoveDataObserver();
47+
}
48+
3349
/// <inheritdoc />
3450
public override bool IsActive()
3551
{
@@ -45,10 +61,11 @@ protected override IEnumerator HapticProcessRoutine()
4561
int outputSampleRate = AudioSettings.outputSampleRate;
4662
while (AudioSource.isPlaying)
4763
{
48-
int sampleIndex = (int)((AudioSettings.dspTime - filterReadDspTime) * outputSampleRate);
4964
float currentSample = 0;
50-
if (filterReadData != null && sampleIndex * filterReadChannels < filterReadData.Length)
65+
if (filterReadData != null)
5166
{
67+
int sampleIndex = (int)((AudioSettings.dspTime - filterReadDspTime) * outputSampleRate) * filterReadChannels;
68+
sampleIndex = Mathf.Min(sampleIndex, filterReadData.Length - filterReadChannels);
5269
for (int i = 0; i < filterReadChannels; ++i)
5370
{
5471
currentSample += filterReadData[sampleIndex + i];
@@ -63,15 +80,67 @@ protected override IEnumerator HapticProcessRoutine()
6380
}
6481

6582
/// <summary>
66-
/// Store currently playing audio data and additional data.
83+
/// Adds a <see cref="AudioSourceHapticPulserDataObserver"/> to the <see cref="AudioSource"/>.
84+
/// </summary>
85+
protected virtual void AddDataObserver()
86+
{
87+
if (AudioSource == null)
88+
{
89+
return;
90+
}
91+
92+
observer = AudioSource.gameObject.AddComponent<AudioSourceHapticPulserDataObserver>();
93+
observer.Source = this;
94+
}
95+
96+
/// <summary>
97+
/// Remove the <see cref="AudioSourceHapticPulserDataObserver"/> from the <see cref="AudioSource"/>.
98+
/// </summary>
99+
protected virtual void RemoveDataObserver()
100+
{
101+
if (observer == null)
102+
{
103+
return;
104+
}
105+
106+
Destroy(observer);
107+
observer = null;
108+
}
109+
110+
/// <summary>
111+
/// Receive audio data from <see cref="AudioSourceHapticPulserDataObserver"/>.
112+
/// </summary>
113+
public void Receive(double dspTime, float[] data, int channels)
114+
{
115+
filterReadDspTime = dspTime;
116+
filterReadData = data;
117+
filterReadChannels = channels;
118+
}
119+
}
120+
121+
/// <summary>
122+
/// Sends currently playing audio data and additional data back to <see cref="AudioSourceHapticPulser"/>
123+
/// </summary>
124+
public class AudioSourceHapticPulserDataObserver : MonoBehaviour
125+
{
126+
/// <summary>
127+
/// The <see cref="AudioSourceHapticPulser"/> that receives audio data.
128+
/// </summary>
129+
[Serialized, Cleared]
130+
[field: DocumentedByXml]
131+
public AudioSourceHapticPulser Source { get; set; }
132+
133+
/// <summary>
134+
/// Sends currently playing audio data and additional data back to <see cref="Source"/>.
67135
/// </summary>
68136
/// <param name="data">An array of floats comprising the audio data.</param>
69137
/// <param name="channels">An int that stores the number of channels of audio data passed to this delegate.</param>
70138
protected virtual void OnAudioFilterRead(float[] data, int channels)
71139
{
72-
filterReadDspTime = AudioSettings.dspTime;
73-
filterReadData = data;
74-
filterReadChannels = channels;
140+
if (Source)
141+
{
142+
Source.Receive(AudioSettings.dspTime, data, channels);
143+
}
75144
}
76145
}
77146
}

0 commit comments

Comments
 (0)