Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AudioEngine.iOS.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 #if SILICONSTUDIO_PLATFORM_IOS
4 
5 using System;
6 using MonoTouch.AVFoundation;
7 using MonoTouch.AudioToolbox;
8 using MonoTouch.Foundation;
9 
10 namespace SiliconStudio.Paradox.Audio
11 {
12  public partial class AudioEngine
13  {
14  private AVAudioPlayer audioPlayer;
15  private bool currentMusicDataTypeIsUnsupported;
16 
17  private void AudioEngineImpl(AudioDevice device)
18  {
19  if (nbOfAudioEngineInstances == 0)
20  ActivateAudioSession();
21  }
22 
23  private void ActivateAudioSession()
24  {
25  NSError error;
26  const double preferedAudioLatency = 0.005;
27 
28  // start the AudioSession
29  var audioSession = AVAudioSession.SharedInstance();
30 
31  // set the audio category so that: playback/recording is possible, playback is mixed with background music, music is stopped when screen is locked
32  error = audioSession.SetCategory(AVAudioSessionCategory.SoloAmbient);
33  if (error != null)
34  {
35  Logger.Warning("Failed to set the audio category to 'Ambient'. [Error info: {0}]", error.UserInfo);
36  State = AudioEngineState.Invalidated;
37  return;
38  }
39 
40  // Reduce the buffer size for better latency response..
41  audioSession.SetPreferredIOBufferDuration(preferedAudioLatency, out error);
42  if (error != null)
43  Logger.Warning("Failed to set the audio IO buffer duration to '{1}'. [Error info: {0}]", error.UserInfo,
44  preferedAudioLatency);
45 
46  // set the preferred sampling rate of the application
47  if (AudioSampleRate != 0)
48  {
49  audioSession.SetPreferredSampleRate(AudioSampleRate, out error);
50  Logger.Warning("Failed to set the audio session preferred sampling rate to '{1}'. [Error info: {0}]",
51  error.UserInfo, AudioSampleRate);
52  }
53 
54  // activate the sound for the application
55  error = audioSession.SetActive(true);
56  if (error != null)
57  {
58  Logger.Warning("Failed to activate the audio session. [Error info: {0}]", error.UserInfo);
59  State = AudioEngineState.Invalidated;
60  }
61  }
62 
63  private void DestroyImpl()
64  {
65  ResetMusicPlayer();
66  }
67 
68  private void LoadNewMusic(SoundMusic lastPlayRequestMusicInstance)
69  {
70  if(audioPlayer != null)
71  throw new AudioSystemInternalException("Tried to create a new AudioPlayer but the current instance was not freed.");
72 
73  currentMusic = lastPlayRequestMusicInstance;
74 
75  currentMusicDataTypeIsUnsupported = false;
76 
77  NSError loadError;
78 
79  // TODO: Avoid allocating twice the music size (i.e. by using NSData.FromBytesNoCopy on currentMusic.Stream.GetBuffer())
80  currentMusic.Stream.Position = 0;
81  audioPlayer = AVAudioPlayer.FromData(NSData.FromStream(currentMusic.Stream), out loadError);
82 
83  if (loadError != null)
84  {
85  if (loadError.Code == (int) AudioFileError.UnsupportedFileType || loadError.Code == (int) AudioFileError.UnsupportedDataFormat)
86  {
87  currentMusicDataTypeIsUnsupported = true;
88  musicMediaEvents.Enqueue(new SoundMusicEventNotification(SoundMusicEvent.MetaDataLoaded, null));
89  return;
90  }
91  throw new AudioSystemInternalException("Music loading failed and failure was not handled. [Error="+loadError.LocalizedDescription+"].");
92  }
93 
94  if (audioPlayer == null) // both audioPlayer and loadError are null (happened before when url was not correct)
95  throw new AudioSystemInternalException("Music loading failed and failure was not handled. [Unspecified Error].");
96 
97  audioPlayer.DecoderError += OnAudioPlayerDecoderError;
98  audioPlayer.FinishedPlaying += OnAudioPlayerFinishedPlaying;
99 
100  if (!audioPlayer.PrepareToPlay())
101  {
102  // this happens sometimes when we put the application on background when starting to play.
103  var currentMusicName = currentMusic.Name;
104  currentMusic.SetStateToStopped();
105  ResetMusicPlayer();
106 
107  Logger.Warning("The music '{0}' failed to prepare to play.", currentMusicName);
108  }
109  else
110  {
111  musicMediaEvents.Enqueue(new SoundMusicEventNotification(SoundMusicEvent.MetaDataLoaded, null));
112  musicMediaEvents.Enqueue(new SoundMusicEventNotification(SoundMusicEvent.ReadyToBePlayed, null));
113  }
114  }
115 
116  private void OnAudioPlayerFinishedPlaying(object sender, AVStatusEventArgs e)
117  {
118  if(!e.Status)
119  throw new AudioSystemInternalException("The music play back did not completed successfully.");
120 
121  musicMediaEvents.Enqueue(new SoundMusicEventNotification(SoundMusicEvent.EndOfTrackReached, e));
122  }
123 
124  private void OnAudioPlayerDecoderError(object sender, AVErrorEventArgs e)
125  {
126  musicMediaEvents.Enqueue(new SoundMusicEventNotification(SoundMusicEvent.ErrorOccurred, e));
127  }
128 
129  private void ResetMusicPlayer()
130  {
131  currentMusic = null;
132 
133  if (audioPlayer != null)
134  {
135  audioPlayer.Dispose();
136  audioPlayer = null;
137  }
138  }
139 
140  private void StopCurrentMusic()
141  {
142  if(audioPlayer == null)
143  return;
144 
145  audioPlayer.Stop();
146  audioPlayer.CurrentTime = 0;
147  }
148 
149  private void PauseCurrentMusic()
150  {
151  if (audioPlayer == null)
152  return;
153 
154  audioPlayer.Pause();
155  }
156 
157  private void UpdateMusicVolume()
158  {
159  if (audioPlayer == null)
160  return;
161 
162  audioPlayer.Volume = currentMusic.Volume;
163  }
164 
165  private void StartCurrentMusic()
166  {
167  if (audioPlayer == null)
168  return;
169 
170  if (!audioPlayer.Play())
171  {
172  // this happens sometimes when we put the application on background when starting to play.
173  var currentMusicName = currentMusic.Name;
174  currentMusic.SetStateToStopped();
175  ResetMusicPlayer();
176 
177  Logger.Warning("The music '{0}' failed to start playing.", currentMusicName);
178  }
179  }
180 
181  private void RestartCurrentMusic()
182  {
183  StopCurrentMusic();
184  StartCurrentMusic();
185  }
186 
187  private void PlatformSpecificProcessMusicReady()
188  {
189  // nothing to do here.
190  }
191 
192  private void ProcessMusicError(SoundMusicEventNotification eventNotification)
193  {
194  if (eventNotification.Event == SoundMusicEvent.ErrorOccurred)
195  {
196  var errorEventArgs = (AVErrorEventArgs) eventNotification.EventData;
197  throw new AudioSystemInternalException("An error happened during music play back and was not handled by the AudioSystem. [error:" + errorEventArgs.Error.LocalizedDescription + "].");
198  }
199  }
200 
201  private void ProcessMusicMetaData()
202  {
203  var errorMsg = "Try to play a music with other format than PCM or MP3.";
204  var errorInFormat = currentMusicDataTypeIsUnsupported;
205 
206  if (audioPlayer != null)
207  {
208  var settings = audioPlayer.Settings;
209 
210  if (settings.NumberChannels > 2)
211  {
212  errorInFormat = true;
213  errorMsg = "Try to play a music with more than two audio channels.";
214  }
215  }
216  if (errorInFormat)
217  {
218  ResetMusicPlayer();
219  throw new InvalidOperationException(errorMsg);
220  }
221  }
222 
223  private void ProcessPlayerClosed()
224  {
225  throw new AudioSystemInternalException("Should never arrive here. (Used only by windows implementation.");
226  }
227 
228  private void PauseAudioPlatformSpecific()
229  {
230  // todo: Should we Inactivate the audio session here?
231  }
232 
233  private void ResumeAudioPlatformSpecific()
234  {
235  ActivateAudioSession();
236  }
237  }
238 }
239 #endif