VoipAudioSourceHiLevel.cs
3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
namespace Oculus.Platform
{
using UnityEngine;
using System;
using System.Collections.Generic;
public class VoipAudioSourceHiLevel : MonoBehaviour
{
// This is a delegate that exists as a surface for OnAudioFilterRead
// It will be callled on unity's audio thread
public class FilterReadDelegate : MonoBehaviour
{
public VoipAudioSourceHiLevel parent;
float[] scratchBuffer;
void Awake()
{
int bufferSizeElements = (int)CAPI.ovr_Voip_GetOutputBufferMaxSize();
scratchBuffer = new float[bufferSizeElements];
}
void OnAudioFilterRead(float[] data, int channels)
{
int sizeToFetch = data.Length / channels;
int sourceBufferSize = sizeToFetch;
if (sourceBufferSize > scratchBuffer.Length)
{
Array.Clear(data, 0, data.Length);
throw new Exception(string.Format("Audio system tried to pull {0} bytes, max voip internal ring buffer size {1}", sizeToFetch, scratchBuffer.Length));
}
int available = parent.pcmSource.PeekSizeElements();
if (available < sourceBufferSize)
{
if (verboseLogging)
{
Debug.LogFormat(
"Voip starved! Want {0}, but only have {1} available",
sourceBufferSize,
available);
}
return;
}
int copied = parent.pcmSource.GetPCM(scratchBuffer, sourceBufferSize);
if (copied < sourceBufferSize)
{
Debug.LogWarningFormat(
"GetPCM() returned {0} samples, expected {1}",
copied,
sourceBufferSize);
return;
}
int dest = 0;
float tmpPeakAmp = -1;
for (int i = 0; i < sizeToFetch; i++)
{
float val = scratchBuffer[i];
for (int j = 0; j < channels; j++)
{
data[dest++] = val;
if (val > tmpPeakAmp)
{
tmpPeakAmp = val;
}
}
}
parent.peakAmplitude = tmpPeakAmp;
}
}
int initialPlaybackDelayMS;
public UInt64 senderID
{
set
{
pcmSource.SetSenderID(value);
}
}
public AudioSource audioSource;
public float peakAmplitude;
protected IVoipPCMSource pcmSource;
static int audioSystemPlaybackFrequency;
static bool verboseLogging = false;
protected void Stop() {}
VoipSampleRate SampleRateToEnum(int rate) {
switch(rate) {
case 48000:
return VoipSampleRate.HZ48000;
case 44100:
return VoipSampleRate.HZ44100;
case 24000:
return VoipSampleRate.HZ24000;
default:
return VoipSampleRate.Unknown;
}
}
protected void Awake()
{
CreatePCMSource();
if(audioSource == null) {
audioSource = gameObject.AddComponent<AudioSource>();
}
audioSource.gameObject.AddComponent<FilterReadDelegate>();
var filterDelegate = audioSource.gameObject.GetComponent<FilterReadDelegate>();
filterDelegate.parent = this;
initialPlaybackDelayMS = 40;
audioSystemPlaybackFrequency = AudioSettings.outputSampleRate;
CAPI.ovr_Voip_SetOutputSampleRate(SampleRateToEnum(audioSystemPlaybackFrequency));
if(verboseLogging) {
Debug.LogFormat("freq {0}", audioSystemPlaybackFrequency);
}
}
void Start() {
audioSource.Stop();
}
protected virtual void CreatePCMSource()
{
pcmSource = new VoipPCMSourceNative();
}
protected static int MSToElements(int ms) {
return ms * audioSystemPlaybackFrequency / 1000;
}
void Update()
{
pcmSource.Update();
if (!audioSource.isPlaying && pcmSource.PeekSizeElements() >= MSToElements(initialPlaybackDelayMS)) {
if(verboseLogging) {
Debug.LogFormat("buffered {0} elements, starting playback", pcmSource.PeekSizeElements());
}
audioSource.Play();
}
}
}
}