4 #if SILICONSTUDIO_PLATFORM_IOS
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Runtime.InteropServices;
9 using MonoTouch.AudioToolbox;
10 using MonoTouch.AudioUnit;
11 using SiliconStudio.Core;
12 using SiliconStudio.Paradox.Audio.Wave;
13 using SiliconStudio.Core.Diagnostics;
15 namespace SiliconStudio.
Paradox.Audio
17 internal unsafe
class AudioVoice : ComponentBase
19 [DllImport(NativeLibrary.LibraryName, CallingConvention = NativeLibrary.CallConvention)]
20 private static extern int SetInputRenderCallbackToChannelMixerDefault(IntPtr inUnit, uint element, IntPtr userData);
22 [DllImport(NativeLibrary.LibraryName, CallingConvention = NativeLibrary.CallConvention)]
23 private static extern int SetInputRenderCallbackTo3DMixerDefault(IntPtr inUnit, uint element, IntPtr userData);
25 [DllImport(NativeLibrary.LibraryName, CallingConvention = NativeLibrary.CallConvention)]
26 private static extern int SetInputRenderCallbackToNull(IntPtr inUnit, uint element);
31 public const int AudioUnitOutputSampleRate = 44100;
33 private readonly AudioEngine audioEngine;
34 private readonly SoundEffectInstance soundEffectInstance;
35 private readonly WaveFormat waveFormat;
36 private const int MaxNumberOfTracks = 16;
38 private static readonly Logger
Log = GlobalLogger.GetLogger(
"AudioVoice");
40 private static int nbOfInstances;
41 private static readonly
object StaticMembersLock =
new object();
43 private static AUGraph audioGraph;
44 private static AudioUnit unitChannelMixer;
45 private static AudioUnit unit3DMixer;
47 private readonly AudioDataRendererInfo* pAudioDataRendererInfo;
52 private static Queue<uint> availableMixerBusIndices;
66 EnableMixerCurrentInput(pAudioDataRendererInfo->IsEnabled2D || pAudioDataRendererInfo->IsEnabled3D);
74 internal uint BusIndexMixer {
get;
private set; }
79 enum _3DMixerParametersIds
89 public bool DidVoicePlaybackEnd()
91 return pAudioDataRendererInfo->PlaybackEnded;
94 private static void CheckUnitStatus(AudioUnitStatus status,
string msg)
96 if (status != AudioUnitStatus.OK)
98 Log.Error(
"Audio Error [{0} / {1}]. Voices: {2}", msg, status, nbOfInstances);
99 throw new AudioSystemInternalException(msg +
" [Error=" + status +
"].");
103 private void EnableMixerCurrentInput(
bool shouldBeEnabled)
105 if(BusIndexMixer == uint.MaxValue)
108 CheckUnitStatus(unitChannelMixer.SetParameter(AudioUnitParameterType.MultiChannelMixerEnable,
109 !Is3D && shouldBeEnabled ? 1f : 0f, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to enable/disable the ChannelMixerInput.");
111 if(waveFormat.Channels == 1)
112 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.Enable,
113 Is3D && shouldBeEnabled ? 1f : 0f, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to enable/disable the 3DMixerInput.");
115 pAudioDataRendererInfo->IsEnabled2D = shouldBeEnabled && !Is3D;
116 pAudioDataRendererInfo->IsEnabled3D = shouldBeEnabled && Is3D;
119 private static void CheckGraphError(AUGraphError error,
string msg)
121 if (error != AUGraphError.OK)
122 throw new AudioSystemInternalException(msg +
" [Error=" + error +
"].");
132 private static AudioStreamBasicDescription CreateLinear16BitsPcm(
int numberOfChannels,
double frameRate,
bool isInterleaved =
true)
134 AudioStreamBasicDescription retFormat;
135 const int wordSize = 2;
137 retFormat.FramesPerPacket = 1;
138 retFormat.Format = AudioFormatType.LinearPCM;
139 retFormat.FormatFlags = AudioFormatFlags.IsPacked | AudioFormatFlags.IsSignedInteger;
140 retFormat.SampleRate = frameRate;
141 retFormat.BitsPerChannel = 8 * wordSize;
142 retFormat.ChannelsPerFrame = numberOfChannels;
143 retFormat.BytesPerFrame = isInterleaved ? numberOfChannels * wordSize : wordSize;
144 retFormat.BytesPerPacket = retFormat.FramesPerPacket * retFormat.BytesPerFrame;
145 retFormat.Reserved = 0;
148 retFormat.FormatFlags |= AudioFormatFlags.IsNonInterleaved;
153 public AudioVoice(AudioEngine engine, SoundEffectInstance effectInstance, WaveFormat desiredFormat)
155 if (engine == null)
throw new ArgumentNullException(
"engine");
156 if (desiredFormat == null)
throw new ArgumentNullException(
"desiredFormat");
158 audioEngine = engine;
159 soundEffectInstance = effectInstance;
160 waveFormat = desiredFormat;
161 BusIndexMixer = uint.MaxValue;
163 if (desiredFormat.BitsPerSample != 16)
164 throw new AudioSystemInternalException(
"Invalid Audio Format. " + desiredFormat.BitsPerSample +
" bits by sample is not supported.");
166 lock (StaticMembersLock)
168 if (nbOfInstances == 0)
171 audioGraph =
new AUGraph();
177 var remoteInOutComponentDesc = AudioComponentDescription.CreateOutput(AudioTypeOutput.Remote);
178 var mixerMultiChannelComponentDesc = AudioComponentDescription.CreateMixer(AudioTypeMixer.MultiChannel);
179 var mixer3DComponentDesc = AudioComponentDescription.CreateMixer(AudioTypeMixer.Spacial);
182 var outputUnitNodeId = audioGraph.AddNode(remoteInOutComponentDesc);
183 var idChannelMixerNode = audioGraph.AddNode(mixerMultiChannelComponentDesc);
184 var id3DMixerNode = audioGraph.AddNode(mixer3DComponentDesc);
187 CheckGraphError(audioGraph.ConnnectNodeInput(idChannelMixerNode, 0, outputUnitNodeId, 0),
"Connection of the graph node failed.");
188 CheckGraphError(audioGraph.ConnnectNodeInput(id3DMixerNode, 0, idChannelMixerNode, MaxNumberOfTracks),
"Connection of the graph node failed.");
191 unitChannelMixer = audioGraph.GetNodeInfo(idChannelMixerNode);
192 unit3DMixer = audioGraph.GetNodeInfo(id3DMixerNode);
195 var desiredSampleRate = (engine.AudioSampleRate != 0) ? engine.AudioSampleRate : AudioUnitOutputSampleRate;
196 unit3DMixer.SetAudioFormat(CreateLinear16BitsPcm(2, desiredSampleRate), AudioUnitScopeType.Output);
197 unitChannelMixer.SetAudioFormat(CreateLinear16BitsPcm(2, desiredSampleRate), AudioUnitScopeType.Output);
200 CheckUnitStatus(unitChannelMixer.SetElementCount(AudioUnitScopeType.Input, MaxNumberOfTracks+1), string.Format(
"Failed to set element count on ChannelMixer [{0}]", MaxNumberOfTracks+1));
201 CheckUnitStatus(unit3DMixer.SetElementCount(AudioUnitScopeType.Input, MaxNumberOfTracks), string.Format(
"Failed to set element count on 3DMixer [{0}]", MaxNumberOfTracks));
204 for (uint i = 0; i < MaxNumberOfTracks; i++)
206 CheckUnitStatus((AudioUnitStatus)SetInputRenderCallbackToNull(unit3DMixer.Handle, i),
"Failed to set the render callback");
207 CheckUnitStatus((AudioUnitStatus)SetInputRenderCallbackToNull(unitChannelMixer.Handle, i),
"Failed to set the render callback");
211 CheckGraphError(audioGraph.Initialize(),
"The audio graph initialization failed.");
214 CheckGraphError(audioGraph.Start(),
"Audio Graph could not start.");
217 for (uint i = 0; i < MaxNumberOfTracks; i++)
219 CheckUnitStatus(unitChannelMixer.SetParameter(AudioUnitParameterType.MultiChannelMixerEnable, 0f, AudioUnitScopeType.Input, i),
"Failed to enable/disable the ChannelMixerInput.");
220 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.Enable, 0f, AudioUnitScopeType.Input, i),
"Failed to enable/disable the 3DMixerInput.");
224 availableMixerBusIndices =
new Queue<uint>();
225 for (uint i = 0; i < MaxNumberOfTracks; i++)
226 availableMixerBusIndices.Enqueue(i);
231 pAudioDataRendererInfo = (AudioDataRendererInfo*)
Utilities.AllocateClearedMemory(
sizeof(AudioDataRendererInfo));
232 pAudioDataRendererInfo->HandleChannelMixer = unitChannelMixer.Handle;
233 pAudioDataRendererInfo->Handle3DMixer = unit3DMixer.Handle;
244 public void SetLoopingPoints(
int startPoint,
int endPoint,
int loopsNumber,
bool loopInfinite)
246 pAudioDataRendererInfo->LoopStartPoint = Math.Max(0, startPoint);
247 pAudioDataRendererInfo->LoopEndPoint = Math.Min(pAudioDataRendererInfo->TotalNumberOfFrames, endPoint);
249 pAudioDataRendererInfo->IsInfiniteLoop = loopInfinite;
250 pAudioDataRendererInfo->NumberOfLoops = Math.Max(0, loopsNumber);
253 protected override void Destroy()
257 Utilities.FreeMemory((IntPtr)pAudioDataRendererInfo);
259 lock (StaticMembersLock)
262 if (nbOfInstances == 0)
264 CheckGraphError(audioGraph.Stop(),
"The audio Graph failed to stop.");
265 audioGraph.Dispose();
272 pAudioDataRendererInfo->PlaybackEnded =
false;
274 EnableMixerCurrentInput(
true);
279 EnableMixerCurrentInput(
false);
284 EnableMixerCurrentInput(
false);
285 pAudioDataRendererInfo->CurrentFrame = 0;
288 if (BusIndexMixer != uint.MaxValue)
291 CheckUnitStatus((AudioUnitStatus)SetInputRenderCallbackToNull(unit3DMixer.Handle, BusIndexMixer),
"Failed to set the render callback");
292 CheckUnitStatus((AudioUnitStatus)SetInputRenderCallbackToNull(unitChannelMixer.Handle, BusIndexMixer),
"Failed to set the render callback");
294 availableMixerBusIndices.Enqueue(BusIndexMixer);
298 public void SetAudioData(SoundEffect soundEffect)
300 BusIndexMixer = uint.MaxValue;
303 if (availableMixerBusIndices.Count > 0)
304 BusIndexMixer = availableMixerBusIndices.Dequeue();
308 audioEngine.ForceSoundEffectInstanceUpdate();
311 if (availableMixerBusIndices.Count > 0)
313 BusIndexMixer = availableMixerBusIndices.Dequeue();
318 var soundEffectToStop = audioEngine.GetLeastSignificativeSoundEffect();
319 if (soundEffectToStop == null)
323 soundEffectToStop.StopAllInstances();
326 if (availableMixerBusIndices.Count > 0)
327 BusIndexMixer = availableMixerBusIndices.Dequeue();
334 unitChannelMixer.SetAudioFormat(CreateLinear16BitsPcm(waveFormat.Channels, waveFormat.SampleRate), AudioUnitScopeType.Input, BusIndexMixer);
337 CheckUnitStatus((AudioUnitStatus)SetInputRenderCallbackToChannelMixerDefault(unitChannelMixer.Handle, BusIndexMixer, (IntPtr)pAudioDataRendererInfo),
"Failed to set the render callback");
339 ResetChannelMixerParameter();
342 if (waveFormat.Channels == 1)
345 unit3DMixer.SetAudioFormat(CreateLinear16BitsPcm(waveFormat.Channels, waveFormat.SampleRate), AudioUnitScopeType.Input, BusIndexMixer);
348 CheckUnitStatus((AudioUnitStatus)SetInputRenderCallbackTo3DMixerDefault(unit3DMixer.Handle, BusIndexMixer, (IntPtr)pAudioDataRendererInfo),
"Failed to set the render callback");
350 Reset3DMixerParameter();
354 EnableMixerCurrentInput(
false);
357 pAudioDataRendererInfo->AudioDataBuffer = soundEffect.WaveDataPtr;
358 pAudioDataRendererInfo->TotalNumberOfFrames = (soundEffect.WaveDataSize / waveFormat.BlockAlign);
359 pAudioDataRendererInfo->NumberOfChannels = waveFormat.Channels;
362 pAudioDataRendererInfo->CurrentFrame = 0;
363 SetLoopingPoints(0,
int.MaxValue, 0, pAudioDataRendererInfo->IsInfiniteLoop);
364 SetVolume(soundEffectInstance.Volume);
365 SetPan(soundEffectInstance.Pan);
368 public void SetVolume(
float volume)
370 if (BusIndexMixer == uint.MaxValue)
375 var gain = Math.Max(-120f, (float) (20*Math.Log10(volume)));
376 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.Gain, gain, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the Gain parameter of the 3D mixer");
380 CheckUnitStatus(unitChannelMixer.SetParameter(AudioUnitParameterType.MultiChannelMixerVolume, volume, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the mixer bus Volume parameter.");
384 public void SetPan(
float pan)
386 if (BusIndexMixer == uint.MaxValue)
390 CheckUnitStatus(unitChannelMixer.SetParameter(AudioUnitParameterType.MultiChannelMixerPan, pan, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the mixer bus Pan parameter.");
393 private void Set3DParameters(
float azimuth,
float elevation,
float distance,
float playRate)
395 if (BusIndexMixer == uint.MaxValue)
398 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.Azimuth, azimuth, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the Azimuth parameter of the 3D mixer");
399 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.Elevation, elevation, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the Elevation parameter of the 3D mixer");
400 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.Distance, distance, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the Distance parameter of the 3D mixer");
401 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.PlaybackRate, playRate, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the PlayRate parameter of the 3D mixer");
404 public void Apply3D(
float azimut,
float elevation,
float distance,
float playRate)
406 Set3DParameters(azimut, elevation, distance, playRate);
410 public void Reset3D()
414 Set3DParameters(0, 0, 0, 1);
419 private void Reset3DMixerParameter()
421 if (BusIndexMixer == uint.MaxValue)
424 Set3DParameters(0, 0, 0, 1);
425 CheckUnitStatus(unit3DMixer.SetParameter((AudioUnitParameterType)_3DMixerParametersIds.Gain, 0, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the Gain parameter of the 3D mixer");
428 private void ResetChannelMixerParameter()
430 if (BusIndexMixer == uint.MaxValue)
433 CheckUnitStatus(unitChannelMixer.SetParameter(AudioUnitParameterType.MultiChannelMixerVolume, 1, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the mixer bus Volume parameter.");
434 CheckUnitStatus(unitChannelMixer.SetParameter(AudioUnitParameterType.MultiChannelMixerPan, 0, AudioUnitScopeType.Input, BusIndexMixer),
"Failed to set the mixer bus Pan parameter.");
437 [DebuggerDisplay(
"AudioDataMixer for input bus {parent.BusIndexChannelMixer}-{parent.BusIndex3DMixer}")]
438 [StructLayout(LayoutKind.Sequential, Pack = 4)]
439 struct AudioDataRendererInfo
441 public int LoopStartPoint;
442 public int LoopEndPoint;
443 public int NumberOfLoops;
444 public bool IsInfiniteLoop;
446 public int CurrentFrame;
447 public int TotalNumberOfFrames;
449 public int NumberOfChannels;
450 public IntPtr AudioDataBuffer;
452 public bool IsEnabled2D;
453 public bool IsEnabled3D;
455 public bool PlaybackEnded;
457 public IntPtr HandleChannelMixer;
458 public IntPtr Handle3DMixer;
ComponentBase.Destroy() event.
SiliconStudio.Core.Utilities Utilities
Output message to log right away.