Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DynamicSoundEffectInstance.Android.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_ANDROID
4 
5 using System;
6 using System.Collections.Generic;
7 using System.Threading;
8 using Android.Media;
9 
10 using SiliconStudio.Paradox.Audio.Wave;
11 
12 using SiliconStudio.Core.Extensions;
13 
14 namespace SiliconStudio.Paradox.Audio
15 {
16  partial class DynamicSoundEffectInstance
17  {
18  /// <summary>
19  /// The number of sub-division of the audioTrack buffer.
20  /// </summary>
21  private const int NbOfSubBuffers = 3;
22 
23  /// <summary>
24  /// The handles to the data buffers in use in the current subBuffer.
25  /// </summary>
26  private SubBufferDataHandles currentSubBufferHandles;
27 
28  /// <summary>
29  /// The empty space remainning in the current subBuffer
30  /// </summary>
31  private int dataNeededToFillCurSubBuffer ;
32 
33  /// <summary>
34  /// The size in byte of a subBuffer of the audio track (size of a sub-part of the audioTrack buffer).
35  /// </summary>
36  internal int SubBufferSize;
37 
38  /// <summary>
39  /// The total number of audio frames submitted for playback.
40  /// </summary>
41  private int nbOfAudioFrameAvailable;
42 
43  /// <summary>
44  /// Lock to protect all internal data from concurrency introduced by audio callbacks.
45  /// </summary>
46  private readonly object internalLock = new object();
47 
48  /// <summary>
49  /// A buffer provided by the user that is partially written on the AudioTrack buffer.
50  /// </summary>
51  private class UserBuffer
52  {
53  public int Offset;
54  public int ByteCount;
55  public readonly byte[] Buffer;
56 
57  public UserBuffer(int offset, int byteCount, byte[] buffer)
58  {
59  Offset = offset;
60  ByteCount = byteCount;
61  Buffer = buffer;
62  }
63  }
64 
65  /// <summary>
66  /// The list of the user provided buffers that are waiting to be submitted to the audioTrack.
67  /// </summary>
68  private readonly Queue<UserBuffer> pendingUserBuffers = new Queue<UserBuffer>();
69 
70  private void InitializeDynamicSound()
71  {
72  // nothing to do here.
73  }
74 
75  /// <summary>
76  /// Method called when a subbuffer of the audio track has finished to be played.
77  /// </summary>
78  /// <param name="sender"></param>
79  /// <param name="e"></param>
80  private void OnSubBufferConsumed(object sender, EventArgs e)
81  {
82  //OnBufferEndCommon();
83  //
84  //// write next data on the audio track if it exist
85  //lock (internalLock)
86  //{
87  // if (AudioTrack == null)
88  // return;
89  //
90  // WriteUserBuffersToAudioTrack();
91  //}
92  }
93 
94  /// <summary>
95  /// Method called when all the data for playback has been consumed.
96  /// </summary>
97  /// <param name="sender"></param>
98  /// <param name="e"></param>
99  private void OnAllDataConsumed(object sender, EventArgs e)
100  {
101  //lock (internalLock)
102  //{
103  // pendingBufferCount -= currentSubBufferHandles.HandleCount;
104  // currentSubBufferHandles.FreeHandles();
105  //
106  // // bug of the library: if playback arrives at the end of audio data,
107  // // future submitted buffers are never played unless all the buffer gets full.
108  // // the solve this problem we destroy and recreate the voice
109  // RebuildVoice();
110  // AudioTrack.Play();
111  //}
112  }
113 
114  internal void RebuildVoice()
115  {
116  DestroyVoice();
117  CreateVoice(WaveFormat);
118 
119  UpdateLooping();
120  UpdatePitch();
121  UpdateStereoVolumes();
122  }
123 
124  private void SubmitBufferImpl(byte[] buffer, int offset, int byteCount)
125  {
126  //lock (internalLock)
127  //{
128  // if(AudioTrack == null)
129  // return;
130  //
131  // // set the marker to the end of the audioTrack to trigger OnAllDataConsumed
132  // nbOfAudioFrameAvailable += byteCount / WaveFormat.BlockAlign;
133  // var status = AudioTrack.SetNotificationMarkerPosition(nbOfAudioFrameAvailable);
134  // if (status != TrackStatus.Success)
135  // throw new AudioSystemInternalException("AudioTrack.SetNotificationMarkerPosition failed and failure was not handled. [error=" + status + "].");
136  //
137  // Interlocked.Increment(ref pendingBufferCount);
138  //
139  // // add the user buffer to the pending list waiting to be processed
140  // var userBuffer = new UserBuffer(offset, byteCount, buffer);
141  // pendingUserBuffers.Enqueue(userBuffer);
142  //
143  // // write next data on the audio track if it possible
144  // WriteUserBuffersToAudioTrack();
145  //}
146  }
147 
148  private void AudioTrackWrite(byte[] buffer, int offset, int byteCount)
149  {
150  //if (AudioTrack == null) // the voice has been destroyed or creation failed
151  // return;
152  //
153  //// monodroid make a copy of the ENTIRE buffer when passing it to the java API
154  //// to avoid useless copy of big buffer written in slices (and consequent garbage collection)
155  //// we create a subbuffer of just the required size (then copy happens only on the needed data)
156  //
157  //int writtenLength;
158  //if (buffer.Length > NbOfSubBuffers*SubBufferSize)
159  //{
160  // writtenLength = AudioTrack.Write(buffer.SubArray(offset, byteCount), 0, byteCount);
161  //}
162  //else
163  //{
164  // writtenLength = AudioTrack.Write(buffer, offset, byteCount);
165  //}
166  //
167  ////if (writtenLength != byteCount) -> sometimes failure happens without reason but does not disturb next write calls, so we skip the check
168  //// throw new AudioSystemInternalException("AudioTrack.Write failed to write the provided data and failure was not handled. [error=" + writtenLength + "]");
169  }
170 
171  private void WriteUserBuffersToAudioTrack()
172  {
173  // Here we need to fill the subBuffers of size 'subBufferSize' with the buffers provided by the user.
174  // There are three cases:
175  // (1) the provided buffer is smaller than our subBuffer size. In this case we need to concat the buffers.
176  // (2) the provided buffer is bigger than our subBuffer size. In this case we need to cut the buffer.
177  // (3) the buffer has exaclty the size of our subBuffer size. In this case we submit the buffer as is.
178 
179  while (internalPendingBufferCount < NbOfSubBuffers)
180  {
181  if (pendingUserBuffers.Count == 0) // no user buffers available anymore
182  return;
183 
184  var userBuffer = pendingUserBuffers.Peek();
185 
186  if (userBuffer.ByteCount > dataNeededToFillCurSubBuffer) // (case 2: cut the buffers)
187  {
188  AudioTrackWrite(userBuffer.Buffer, userBuffer.Offset, dataNeededToFillCurSubBuffer);
189 
190  Interlocked.Increment(ref internalPendingBufferCount);
191  submittedBufferHandles.Enqueue(currentSubBufferHandles); // submit the handles (note: current buffer handle is added only in case (1))
192 
193  userBuffer.Offset += dataNeededToFillCurSubBuffer;
194  userBuffer.ByteCount -= dataNeededToFillCurSubBuffer;
195  dataNeededToFillCurSubBuffer = SubBufferSize;
196  currentSubBufferHandles = new SubBufferDataHandles();
197  }
198  else
199  {
200  // case 1: concat the buffers
201  AudioTrackWrite(userBuffer.Buffer, userBuffer.Offset, userBuffer.ByteCount);
202 
203  currentSubBufferHandles.AddHandle();
204  dataNeededToFillCurSubBuffer -= userBuffer.ByteCount;
205 
206  if (dataNeededToFillCurSubBuffer == 0) // case 3: the user buffer fit perfectly the size of our subbuffer
207  {
208  Interlocked.Increment(ref internalPendingBufferCount);
209  submittedBufferHandles.Enqueue(currentSubBufferHandles); // submit the handles (note: current buffer handle is added only in case (1))
210 
211  dataNeededToFillCurSubBuffer = SubBufferSize;
212  currentSubBufferHandles = new SubBufferDataHandles();
213  }
214 
215  // remove this buffer from the pending list because consumed
216  pendingUserBuffers.Dequeue();
217  }
218  }
219  }
220 
221  internal override void StopImpl()
222  {
223  // For the DynamicSoundEffectInstance stop does not need to destroy/reconstruct the voice
224  // It is done in above ClearBuffersImpl function. Moreover destruction/contruction need to be lock protected.
225  }
226 
227  private void ClearBuffersImpl()
228  {
229  lock (internalLock)
230  {
231  // we need to destroy/reconstruct the voice here to be sure to clean all written data in the voice (flush is not working).
232  // destroy/construct the voice need to be lock protected in order to avoid the callback to work with invalid voice.
233  RebuildVoice();
234 
235  // remove all the user pending buffers.
236  pendingUserBuffers.Clear();
237 
238  // free current buffer handles
239  currentSubBufferHandles.FreeHandles();
240 
241  ResetBufferInfo();
242  }
243  }
244 
245  internal override void CreateVoice(WaveFormat format)
246  {
247  }
248 
249  private void ResetBufferInfo()
250  {
251  currentSubBufferHandles = new SubBufferDataHandles();
252  dataNeededToFillCurSubBuffer = SubBufferSize;
253  nbOfAudioFrameAvailable = 0;
254  }
255 
256  internal override void PlatformSpecificDisposeImpl()
257  {
258  lock (internalLock)
259  {
260  DestroyVoice();
261  }
262  }
263 
264 
265  /// <summary>
266  /// A list of all the data handles lock for one subBuffer.
267  /// </summary>
268  private class SubBufferDataHandles
269  {
270  public void FreeHandles()
271  {
272  HandleCount = 0;
273  }
274 
275  public int HandleCount { get; private set; }
276 
277  public void AddHandle()
278  {
279  HandleCount = HandleCount + 1;
280  }
281  };
282  }
283 }
284 
285 #endif
SiliconStudio.Paradox.Graphics.Buffer Buffer
Definition: BasicEffect.cs:15
_In_ size_t _In_ size_t _In_ DXGI_FORMAT format
Definition: DirectXTexP.h:175