fixed: Stream Flush/Drain rework, housekeeping
[xbmc:xbmc-antiquated.git] / XBMC / xbmc / cores / masteraudio / AudioManager.cpp
1 /*
2  *      Copyright (C) 2009 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "stdafx.h"
23 #include "AudioManager.h"
24
25 // Global (ick) Instance
26 CAudioManager g_AudioLibManager;
27
28 CAudioStream::CAudioStream() :
29   m_pInput(NULL),
30   m_pDSPChain(NULL),
31   m_MixerChannel(0),
32   m_pMixerSink(NULL), // DO NOT delete the sink!
33   m_pInputSourceSlice(NULL),
34   m_pDSPSourceSlice(NULL),
35   m_Open(false)
36 {
37   m_ProcessTimer.reset();
38 }
39
40 CAudioStream::~CAudioStream()
41 {
42     Close();
43 }
44
45 bool CAudioStream::Initialize(CStreamInput* pInput, CDSPChain* pDSPChain, int mixerChannel, IAudioSink* pMixerSink)
46 {
47   // TODO: Move more responsibility from AudioManager::OpenStream
48   m_pInput = pInput;
49   m_pDSPChain = pDSPChain;
50   m_MixerChannel = mixerChannel;
51   m_pMixerSink = pMixerSink;
52
53   m_Open = true;
54   return m_Open;
55 }
56
57 void CAudioStream::Close()
58 {
59   if (!m_Open)
60     return;
61
62   m_Open = false;
63   // Delete any in-process slices (we cannot use them and cannot pass them on)
64   delete m_pInputSourceSlice;
65   m_pInputSourceSlice = NULL;
66   delete m_pDSPSourceSlice;
67   m_pDSPSourceSlice = NULL;
68   CLog::Log(LOGINFO,"MasterAudio:AudioStream: Closing. Average time to process = %0.2fms", m_ProcessTimer.average_time/1000.0f);
69 }
70
71 CStreamInput* CAudioStream::GetInput()
72 {
73   return m_pInput;
74 }
75
76 CDSPChain* CAudioStream::GetDSPChain()
77 {
78   return m_pDSPChain;
79 }
80
81 int CAudioStream::GetMixerChannel()
82 {
83   return m_MixerChannel;
84 }
85
86 // Private Methods
87 ////////////////////////////////////////////////////////////////////////////////
88 bool CAudioStream::ProcessStream()
89 {
90   bool ret = true;
91   MA_RESULT res = MA_SUCCESS;
92   
93   m_ProcessTimer.lap_start();
94
95   // If we don't have one already, pull slice from CStreamInput
96   if (!m_pInputSourceSlice)
97   {
98     if (MA_ERROR == m_pInput->GetSlice(&m_pInputSourceSlice))
99       ret = false;
100   }
101
102   // If we have one to give, pass a slice to CDSPChain sink
103   if(m_pInputSourceSlice)
104   {
105     res = m_pDSPChain->GetSink()->AddSlice(m_pInputSourceSlice);
106     if (MA_ERROR == res)
107       ret = false;
108     else if (MA_SUCCESS == res)
109       m_pInputSourceSlice = NULL; // We are done with this one
110   }
111  
112   // If we don't have one already, pull slice from CDSPChain source
113   if (!m_pDSPSourceSlice)
114   {
115     if (MA_ERROR == m_pDSPChain->GetSource()->GetSlice(&m_pDSPSourceSlice))
116       ret = false;
117   }
118
119   // If we have one to give, pass a slice to the output channel
120   if (m_pDSPSourceSlice)
121   {
122     res = m_pMixerSink->AddSlice(m_pDSPSourceSlice);
123     if (MA_ERROR == res)
124       ret = false;
125     else if (MA_SUCCESS == res)
126       m_pDSPSourceSlice = NULL; // We are done with this one
127   }
128
129   m_ProcessTimer.lap_end();
130
131   return ret;
132 }
133
134 float CAudioStream::GetMaxLatency()
135 {
136   // Latency has 4 parts
137   //  1. Input/Output buffer (TODO: no interface)
138   //  2. DSPFilter
139   //  3. Mixer Channel(renderer)
140   //  4. Cached slices (TODO: currently cannot reliably convert bytes to time)
141
142   // TODO: Periodically cache this value and provide average as opposed to re-calculating each time. It's only
143   //     so accurate anyway, since the latency now may not equal the latency experienced by any given byte
144   return m_pDSPChain->GetMaxLatency() + m_pMixerSink->GetMaxLatency();
145 }
146
147 void CAudioStream::Flush()
148 {
149   // Flush each stream component
150   if (m_pInput)
151     m_pInput->Reset();
152
153   if (m_pDSPChain)
154     m_pDSPChain->Flush();
155
156   if (m_pMixerSink)
157     m_pMixerSink->Flush();
158
159   // Free any in-process slices
160   delete m_pInputSourceSlice;
161   m_pInputSourceSlice = NULL;
162   delete m_pDSPSourceSlice;
163   m_pDSPSourceSlice = NULL;
164   CLog::Log(LOGINFO,"MasterAudio:AudioStream: Flushed stream.");
165 }
166
167 bool CAudioStream::Drain(unsigned int timeout)
168 {
169   // TODO: Make sure this doesn't leave any data behind
170   lap_timer timer;
171   timer.lap_start();
172   while (timer.elapsed_time()/1000 < timeout)
173   {
174     // Move any remaining data along
175     ProcessStream();
176
177     if (!m_pInputSourceSlice && !m_pDSPSourceSlice) // No input left here. Try to empy out the mixer channel
178     {
179       m_pInput->Reset();  // Just in case there is a partial slice left
180       return true;
181     }
182   }
183   
184   Flush();  // Abandon any remaining data
185   return false;
186 }
187
188 ///////////////////////////////////////////////////////////////////////////////
189 // CAudioManager
190 ///////////////////////////////////////////////////////////////////////////////
191 CAudioManager::CAudioManager() :
192   m_pMixer(NULL),
193   m_MaxStreams(MA_MAX_INPUT_STREAMS)
194 {
195
196 }
197
198 CAudioManager::~CAudioManager()
199 {
200   // Clean up any streams that were not closed by a client
201   if (m_StreamList.size())
202   {
203     for (StreamIterator iter = m_StreamList.begin();iter != m_StreamList.end();iter++)
204     {
205       CAudioStream* pStream = (CAudioStream*)iter->second;
206       m_StreamList.erase(iter);
207       CleanupStreamResources(pStream);
208       delete pStream;
209     }
210   }
211   delete m_pMixer;
212 }
213
214 MA_STREAM_ID CAudioManager::OpenStream(CStreamDescriptor* pDesc)
215 {
216   // TODO: reorganize the cleanup/error handling code. There must be a better way to do it.
217
218   // Find out if we can handle an additional input stream, if not, give up
219   if (!pDesc || m_StreamList.size() >= m_MaxStreams)
220     return MA_STREAM_NONE;
221   
222   // Create an input handler for this stream
223   CStreamInput* pInput = new CStreamInput();
224   MA_RESULT res = MA_ERROR;
225   res = pInput->Initialize();
226   if (MA_SUCCESS == res)
227   {
228     res = pInput->SetOutputFormat(pDesc);
229     if (MA_SUCCESS != res)
230     {
231       delete pInput;
232       return MA_STREAM_NONE;
233     }
234   }
235
236   // Create a DSP filter chain for the stream (passthrough detection will be handled by the DSPChain)
237   CDSPChain* pChain = new CDSPChain();
238   if (MA_SUCCESS != pChain->CreateFilterGraph(pDesc))
239   {
240     CLog::Log(LOGERROR,"MasterAudio:AudioManager: Unable to initialize DSPChain.");
241     delete pChain;
242     delete pInput;
243     return MA_STREAM_NONE;
244   }
245
246   // Create a new channel in the mixer to handle the stream
247   if (!m_pMixer)
248     SetMixerType(MA_MIXER_HARDWARE);
249   int mixerChannel = m_pMixer->OpenChannel(pDesc);
250   IAudioSink* pMixerSink = m_pMixer->GetChannelSink(mixerChannel);
251   if (!mixerChannel || !pMixerSink)
252   {
253     m_pMixer->CloseChannel(mixerChannel);
254     delete pInput;
255     delete pChain;
256     return MA_STREAM_NONE;
257   }
258
259   // Wrap everything up in a stream object
260   CAudioStream* pStream = new CAudioStream();
261   pStream->Initialize(pInput, pChain, mixerChannel, pMixerSink);  
262   if (!AddInputStream(pStream))
263   {
264     pStream->Close();
265     delete pStream;
266     delete pInput;
267     delete pChain;
268     return MA_STREAM_NONE;
269   }
270
271   MA_STREAM_ID streamId = GetStreamId(pStream);
272
273   CLog::Log(LOGINFO,"MasterAudio:AudioManager: Opened stream %d (active_stream = %d, max_streams = %d).", streamId, GetOpenStreamCount(), m_MaxStreams);
274
275   return streamId;
276 }
277
278 size_t CAudioManager::AddDataToStream(MA_STREAM_ID streamId, void* pData, size_t len)
279 {
280   size_t bytesAdded = 0;
281
282   CAudioStream* pStream = GetInputStream(streamId);
283   if (!pStream)
284     return 0;
285
286   CStreamInput* pInput = pStream->GetInput();
287   if (!pInput)
288     return 0;
289
290   // Add the data to the stream
291   if(MA_SUCCESS == pInput->AddData(pData,len))
292     bytesAdded = len;
293   else
294     bytesAdded = 0;
295
296   // Push data through the stream
297   pStream->ProcessStream();
298
299   return bytesAdded;
300 }
301
302 bool CAudioManager::ControlStream(MA_STREAM_ID streamId, int controlCode)
303 {
304   CAudioStream* pStream = GetInputStream(streamId);
305   if(!pStream)
306     return false;
307
308   int mixerChannel = pStream->GetMixerChannel();
309   if(!mixerChannel)
310     return false;
311
312   return (MA_SUCCESS == m_pMixer->ControlChannel(mixerChannel, controlCode));
313 }
314
315 bool CAudioManager::SetStreamVolume(MA_STREAM_ID streamId, long vol)
316 {
317   CAudioStream* pStream = GetInputStream(streamId);
318   if(!pStream)
319     return false;
320
321   int mixerChannel = pStream->GetMixerChannel();
322   if(!mixerChannel)
323     return false;
324
325   return (MA_SUCCESS == m_pMixer->SetChannelVolume(mixerChannel, vol));  
326 }
327
328 bool CAudioManager::SetStreamProp(MA_STREAM_ID streamId, int propId, const void* pVal)
329 {
330   // TODO: Implement
331   CAudioStream* pStream = GetInputStream(streamId);
332   if(!pStream)
333     return false;
334
335   return false;
336 }
337
338 bool CAudioManager::GetStreamProp(MA_STREAM_ID streamId, int propId, void* pVal)
339 {
340   // TODO: Implement
341   CAudioStream* pStream = GetInputStream(streamId);
342   if(!pStream)
343     return false;
344
345   return false;
346 }
347
348 float CAudioManager::GetMaxStreamLatency(MA_STREAM_ID streamId)
349 {
350   CAudioStream* pStream = GetInputStream(streamId);
351   if (!pStream)
352     return 0.0f;  // No delay from here to nowhere...
353
354   return pStream->GetMaxLatency();
355 }
356
357 bool CAudioManager::DrainStream(MA_STREAM_ID streamId, unsigned int timeout)
358 {
359   // TODO: There must be a cleaner way to do this (why can't the AudioStream drain the mixer channel?)
360   CAudioStream* pStream = GetInputStream(streamId);
361   if (!pStream)
362     return true; // Nothing to drain
363
364   lap_timer timer;
365   timer.lap_start();
366   if (pStream->Drain(timeout))
367   {
368     unsigned __int64 elapsed = timer.elapsed_time()/1000;
369     if (elapsed < timeout)
370       return m_pMixer->DrainChannel(pStream->GetMixerChannel(), timeout - (unsigned int)elapsed);
371     else
372       m_pMixer->FlushChannel(pStream->GetMixerChannel());
373   }
374   return false; // Out of time
375 }
376
377 void CAudioManager::FlushStream(MA_STREAM_ID streamId)
378 {
379   // TODO: Implement Properly
380   CAudioStream* pStream = GetInputStream(streamId);
381   if (!pStream)
382     return;
383
384   pStream->Flush();
385 }
386
387 void CAudioManager::CloseStream(MA_STREAM_ID streamId)
388 {
389   StreamIterator iter = m_StreamList.find(streamId);
390   if (iter == m_StreamList.end())
391     return;
392   CAudioStream* pStream = iter->second;
393   m_StreamList.erase(iter);
394   CleanupStreamResources(pStream);
395   delete pStream;
396
397   CLog::Log(LOGINFO,"MasterAudio:AudioManager: Closed stream %d (active_stream = %d, max_streams = %d).", streamId, GetOpenStreamCount(), m_MaxStreams);
398 }
399
400 bool CAudioManager::SetMixerType(int mixerType)
401 {
402   if (m_pMixer)
403     delete m_pMixer;
404
405   m_pMixer = NULL;
406
407   switch(mixerType)
408   {
409   case MA_MIXER_HARDWARE:
410     m_pMixer = new CHardwareMixer(m_MaxStreams);
411     return true;
412   case MA_MIXER_SOFTWARE:
413   default:
414     return false;
415   }
416 }
417
418 /////////////////////////////////////////////////////////////////////////////////////
419 // Private Methods
420 /////////////////////////////////////////////////////////////////////////////////////
421 bool CAudioManager::AddInputStream(CAudioStream* pStream)
422 {
423   // Limit the maximum number of open streams
424   if(m_StreamList.size() >= m_MaxStreams)
425     return false;
426
427   MA_STREAM_ID id = GetStreamId(pStream);
428   m_StreamList[id] = pStream;
429   return true;
430 }
431
432 CAudioStream* CAudioManager::GetInputStream(MA_STREAM_ID streamId)
433 {
434   return (CAudioStream*)streamId;
435 }
436
437 MA_STREAM_ID CAudioManager::GetStreamId(CAudioStream* pStream)
438 {
439   return (MA_STREAM_ID)pStream;
440 }
441
442 int CAudioManager::GetOpenStreamCount()
443 {
444   return m_StreamList.size();
445 }
446
447 void CAudioManager::CleanupStreamResources(CAudioStream* pStream)
448 {
449   // Clean up any resources created for the stream
450   delete pStream->GetInput();
451   delete pStream->GetDSPChain();
452   m_pMixer->CloseChannel(pStream->GetMixerChannel());
453   pStream->Close();
454 }