Skip to content

Commit

Permalink
fix(Haptics): allow AudioSourceHapticPulser be placed on other object
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Ethan Cheung committed Oct 29, 2019
1 parent 72d9102 commit 4697a60
Showing 1 changed file with 75 additions and 6 deletions.
81 changes: 75 additions & 6 deletions Runtime/Haptics/AudioSourceHapticPulser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using UnityEngine;
using System.Collections;
using Malimbe.MemberClearanceMethod;
using Malimbe.PropertySerializationAttribute;
using Malimbe.XmlDocumentationAttribute;

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

/// <summary>
/// Observer added to <see cref="AudioSource"/>.
/// </summary>
protected AudioSourceHapticPulserDataObserver observer;
/// <summary>
/// <see cref="AudioSettings.dspTime"/> of the last <see cref="OnAudioFilterRead"/>.
/// </summary>
Expand All @@ -30,6 +35,17 @@ public class AudioSourceHapticPulser : RoutineHapticPulser
/// </summary>
protected int filterReadChannels;

protected virtual void OnEnable()
{
AddDataObserver();
}

protected override void OnDisable()
{
base.OnDisable();
RemoveDataObserver();
}

/// <inheritdoc />
public override bool IsActive()
{
Expand All @@ -45,10 +61,11 @@ protected override IEnumerator HapticProcessRoutine()
int outputSampleRate = AudioSettings.outputSampleRate;
while (AudioSource.isPlaying)
{
int sampleIndex = (int)((AudioSettings.dspTime - filterReadDspTime) * outputSampleRate);
float currentSample = 0;
if (filterReadData != null && sampleIndex * filterReadChannels < filterReadData.Length)
if (filterReadData != null)
{
int sampleIndex = (int)((AudioSettings.dspTime - filterReadDspTime) * outputSampleRate) * filterReadChannels;
sampleIndex = Mathf.Min(sampleIndex, filterReadData.Length - filterReadChannels);
for (int i = 0; i < filterReadChannels; ++i)
{
currentSample += filterReadData[sampleIndex + i];
Expand All @@ -63,15 +80,67 @@ protected override IEnumerator HapticProcessRoutine()
}

/// <summary>
/// Store currently playing audio data and additional data.
/// Adds a <see cref="AudioSourceHapticPulserDataObserver"/> to the <see cref="AudioSource"/>.
/// </summary>
protected virtual void AddDataObserver()
{
if (AudioSource == null)
{
return;
}

observer = AudioSource.gameObject.AddComponent<AudioSourceHapticPulserDataObserver>();
observer.Source = this;
}

/// <summary>
/// Remove the <see cref="AudioSourceHapticPulserDataObserver"/> from the <see cref="AudioSource"/>.
/// </summary>
protected virtual void RemoveDataObserver()
{
if (observer == null)
{
return;
}

Destroy(observer);
observer = null;
}

/// <summary>
/// Receive audio data from <see cref="AudioSourceHapticPulserDataObserver"/>.
/// </summary>
public void Receive(double dspTime, float[] data, int channels)
{
filterReadDspTime = dspTime;
filterReadData = data;
filterReadChannels = channels;
}
}

/// <summary>
/// Sends currently playing audio data and additional data back to <see cref="AudioSourceHapticPulser"/>
/// </summary>
public class AudioSourceHapticPulserDataObserver : MonoBehaviour
{
/// <summary>
/// The <see cref="AudioSourceHapticPulser"/> that receives audio data.
/// </summary>
[Serialized, Cleared]
[field: DocumentedByXml]
public AudioSourceHapticPulser Source { get; set; }

/// <summary>
/// Sends currently playing audio data and additional data back to <see cref="Source"/>.
/// </summary>
/// <param name="data">An array of floats comprising the audio data.</param>
/// <param name="channels">An int that stores the number of channels of audio data passed to this delegate.</param>
protected virtual void OnAudioFilterRead(float[] data, int channels)
{
filterReadDspTime = AudioSettings.dspTime;
filterReadData = data;
filterReadChannels = channels;
if (Source)
{
Source.Receive(AudioSettings.dspTime, data, channels);
}
}
}
}

0 comments on commit 4697a60

Please sign in to comment.