forked from cgvr/DeltaVR
microphone recording working with FMOD!!!
This commit is contained in:
122
Assets/_PROJECT/Scripts/ModeGeneration/FMODMicLoopback.cs
Normal file
122
Assets/_PROJECT/Scripts/ModeGeneration/FMODMicLoopback.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
using FMOD;
|
||||
using FMODUnity;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
public class FMODMicLoopback : MonoBehaviour
|
||||
{
|
||||
private uint LATENCY_MS = 50;
|
||||
private uint DRIFT_MS = 1;
|
||||
|
||||
private uint samplesRecorded, samplesPlayed = 0;
|
||||
private int nativeRate, nativeChannels = 0;
|
||||
private uint recSoundLength = 0;
|
||||
uint lastPlayPos = 0;
|
||||
uint lastRecordPos = 0;
|
||||
private uint driftThreshold = 0;
|
||||
private uint desiredLatency = 0;
|
||||
private uint adjustLatency = 0;
|
||||
private int actualLatency = 0;
|
||||
uint minRecordDelta = 0xFFFFFFFF;
|
||||
|
||||
private FMOD.CREATESOUNDEXINFO exInfo = new FMOD.CREATESOUNDEXINFO();
|
||||
|
||||
private FMOD.Sound recSound;
|
||||
private FMOD.Channel channel;
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
/*
|
||||
Determine latency in samples.
|
||||
*/
|
||||
FMODUnity.RuntimeManager.CoreSystem.getRecordDriverInfo(0, out _, 0, out _, out nativeRate, out _, out nativeChannels, out _);
|
||||
|
||||
driftThreshold = (uint)(nativeRate * DRIFT_MS) / 1000;
|
||||
desiredLatency = (uint)(nativeRate * LATENCY_MS) / 1000;
|
||||
adjustLatency = desiredLatency;
|
||||
actualLatency = (int)desiredLatency;
|
||||
|
||||
/*
|
||||
Create user sound to record into, then start recording.
|
||||
*/
|
||||
exInfo.cbsize = Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
|
||||
exInfo.numchannels = nativeChannels;
|
||||
exInfo.format = FMOD.SOUND_FORMAT.PCM16;
|
||||
exInfo.defaultfrequency = nativeRate;
|
||||
exInfo.length = (uint)(nativeRate * sizeof(short) * nativeChannels);
|
||||
|
||||
FMODUnity.RuntimeManager.CoreSystem.createSound("", FMOD.MODE.LOOP_NORMAL | FMOD.MODE.OPENUSER, ref exInfo, out recSound);
|
||||
|
||||
FMODUnity.RuntimeManager.CoreSystem.recordStart(0, recSound, true);
|
||||
|
||||
recSound.getLength(out recSoundLength, FMOD.TIMEUNIT.PCM);
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
/*
|
||||
Determine how much has been recorded since we last checked
|
||||
*/
|
||||
uint recordPos = 0;
|
||||
FMODUnity.RuntimeManager.CoreSystem.getRecordPosition(0, out recordPos);
|
||||
|
||||
uint recordDelta = (recordPos >= lastRecordPos) ? (recordPos - lastRecordPos) : (recordPos + recSoundLength - lastRecordPos);
|
||||
lastRecordPos = recordPos;
|
||||
samplesRecorded += recordDelta;
|
||||
|
||||
if (recordDelta != 0 && (recordDelta < minRecordDelta))
|
||||
{
|
||||
minRecordDelta = recordDelta; // Smallest driver granularity seen so far
|
||||
adjustLatency = (recordDelta <= desiredLatency) ? desiredLatency : recordDelta; // Adjust our latency if driver granularity is high
|
||||
}
|
||||
|
||||
/*
|
||||
Delay playback until our desired latency is reached.
|
||||
*/
|
||||
if (!channel.hasHandle() && samplesRecorded >= adjustLatency)
|
||||
{
|
||||
FMODUnity.RuntimeManager.CoreSystem.getMasterChannelGroup(out FMOD.ChannelGroup mCG);
|
||||
FMODUnity.RuntimeManager.CoreSystem.playSound(recSound, mCG, false, out channel);
|
||||
}
|
||||
|
||||
/*
|
||||
Determine how much has been played since we last checked.
|
||||
*/
|
||||
if (channel.hasHandle())
|
||||
{
|
||||
uint playPos = 0;
|
||||
channel.getPosition(out playPos, FMOD.TIMEUNIT.PCM);
|
||||
|
||||
uint playDelta = (playPos >= lastPlayPos) ? (playPos - lastPlayPos) : (playPos + recSoundLength - lastPlayPos);
|
||||
lastPlayPos = playPos;
|
||||
samplesPlayed += playDelta;
|
||||
|
||||
// Compensate for any drift.
|
||||
int latency = (int)(samplesRecorded - samplesPlayed);
|
||||
actualLatency = (int)((0.97f * actualLatency) + (0.03f * latency));
|
||||
|
||||
int playbackRate = nativeRate;
|
||||
if (actualLatency < (int)(adjustLatency - driftThreshold))
|
||||
{
|
||||
// Playback position is catching up to the record position, slow playback down by 2%
|
||||
playbackRate = nativeRate - (nativeRate / 50);
|
||||
}
|
||||
|
||||
else if (actualLatency > (int)(adjustLatency + driftThreshold))
|
||||
{
|
||||
// Playback is falling behind the record position, speed playback up by 2%
|
||||
playbackRate = nativeRate + (nativeRate / 50);
|
||||
}
|
||||
|
||||
channel.setFrequency((float)playbackRate);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
recSound.release();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user