fixed: Stream Flush/Drain rework, housekeeping
[xbmc:xbmc-antiquated.git] / XBMC / xbmc / cores / masteraudio / MasterAudioCore.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 "MasterAudioCore.h"
24
25 // TODO: Move with mixer
26 #include "DirectSoundAdapter.h"
27
28 unsigned __int64 audio_slice_id = 0;
29
30 // CStreamInput
31 //////////////////////////////////////////////////////////////////////////////////////
32 CStreamInput::CStreamInput() : 
33   m_OutputSize(0),
34   m_pBuffer(NULL),
35   m_BufferOffset(0)
36 {
37
38 }
39
40 CStreamInput::~CStreamInput()
41 {
42   delete m_pBuffer;
43 }
44
45 MA_RESULT CStreamInput::Initialize()
46 {
47   // TODO: Should we be setting a default output buffer size?
48   // TODO: What else needs to be done here?
49   if (m_OutputSize)
50     ConfigureBuffer();
51
52   return MA_SUCCESS;
53 }
54
55 // TODO: Consider adding logic to back-down the size of the buffer over time (if it is large).
56 MA_RESULT CStreamInput::AddData(void* pBuffer, size_t bufLen)
57 {
58   // Process a block of data from a client.  We attempt to be as flexible as possible, but a few rules still apply:
59   //  1. We only ever work on one block of data at a time, as long as it is bigger than the output size
60   //  2. The internal buffer will grow to accomodate any input block size, but will not concatenate 2 or more blocks (except remainders)
61
62   // Take a hint from the client about the size of things to come
63   if (!m_OutputSize)
64     m_OutputSize = bufLen;  //TODO: This really should be set externally or put in the descriptor
65
66   if (!m_pBuffer)
67     ConfigureBuffer();
68
69   if (bufLen > m_pBuffer->GetMaxLen()) // Take this as a hint that the client wants to send bigger chunks and increase our buffer size
70   {
71     // Take this as a hint that the client wants to write bigger blocks (we do not want to do this too often)
72     CLog::Log(LOGINFO,"MasterAudio:CStreamInput: Increasing buffer size (current_size = %d, new_size = %d).", m_pBuffer->GetMaxLen(), bufLen);
73     size_t newSize = bufLen;
74     if (m_pBuffer->GetLen() < m_OutputSize)
75       newSize = bufLen * 2; // Misalignment causes us to require more space (TODO: how much space do we really need?) 
76
77     CSimpleBuffer* pNewBuffer = new CSimpleBuffer();
78     pNewBuffer->Initialize(newSize);
79
80     // Copy remaining data from our old buffer
81     pNewBuffer->Write(m_pBuffer->GetData(NULL),m_pBuffer->GetLen());
82
83     // Make the switch
84     delete m_pBuffer;
85     m_pBuffer = pNewBuffer;
86   }
87
88   // See if we already have an output slice in the buffer. If we do, reject the caller this time around.
89   //  We shoud be able to take their data next time around. If we do not have enough already, continue on.
90   if (m_pBuffer->GetLen() >= m_OutputSize)
91     return MA_BUSYORFULL;
92
93   if (!m_pBuffer->Write(pBuffer, bufLen))// Add this input block to the buffer (accumulator)
94     return MA_ERROR;
95
96   return MA_SUCCESS;
97 }
98
99 IAudioSource* CStreamInput::GetSource()
100 {
101   return (IAudioSource*)this;
102 }
103
104 void CStreamInput::SetOutputSize(size_t size)
105 {
106   // TODO: Make sure this doesn't leave us short on input
107   m_OutputSize = size;
108   ConfigureBuffer();
109 }
110
111 void CStreamInput::Reset()
112 {
113   // TODO: is there anything else to be done here? Maybe resize/delete the buffer?
114   m_pBuffer->Empty();
115 }
116
117 // IAudioSource
118 MA_RESULT CStreamInput::TestOutputFormat(CStreamDescriptor* pDesc)
119 {
120   // Currently there is no format we cannot handle. All are treated as bitstreams
121   return MA_SUCCESS;
122 }
123
124 MA_RESULT CStreamInput::SetOutputFormat(CStreamDescriptor* pDesc)
125 {
126   // TODO: Examine the stream descriptor
127   // Currently all formats are treated as bitstreams. There is nothing to be done here.
128   // TODO: Should we clear/reset the buffer on a format change, assuming that the data is not compatible?
129
130   return MA_SUCCESS;
131 }
132
133 MA_RESULT CStreamInput::GetSlice(audio_slice** pSlice)
134 {
135   if (!pSlice || !m_pBuffer)
136     return MA_ERROR;
137
138   audio_slice* pOut = NULL;
139   *pSlice = NULL; // Default return value
140
141   // TODO: Implement slice allocator and reduce number of memcpy's
142
143   size_t bufferLen = m_pBuffer->GetLen();
144   if ((bufferLen - m_BufferOffset) >= m_OutputSize)  // If we have enough data in our buffer, carve off a slice
145   {
146     // Create a new slice object and copy the output data into it
147     pOut = audio_slice::create_slice(m_OutputSize);
148     unsigned char* pBlock = (unsigned char*)m_pBuffer->GetData(NULL);
149     memcpy(pOut->get_data(), &pBlock[m_BufferOffset], m_OutputSize);
150
151     // Update our internal offset. We use this so we don't have to shift the data for each read (performance improvement)
152     m_BufferOffset += m_OutputSize;
153     size_t bytesLeft = bufferLen - m_BufferOffset;
154     if (!bytesLeft) // No more data. Reset the buffer.
155     {
156       // TODO: This would be the best time to shrink the buffer if we wanted to do so.
157       m_pBuffer->Empty();
158       m_BufferOffset = 0;
159     }
160     else if (bytesLeft < m_OutputSize)  // Move any stragglers to the top of the buffer (performance hit)
161     {
162       m_pBuffer->ShiftUp(m_pBuffer->GetLen() - bytesLeft);
163       m_BufferOffset = 0;
164     }
165     *pSlice = pOut; // Must be freed by someone else
166   }
167   else
168     return MA_NEED_DATA;
169
170   return MA_SUCCESS;
171 }
172
173 void CStreamInput::ConfigureBuffer()
174 {
175   if (!m_pBuffer)
176     m_pBuffer = new CSimpleBuffer();
177   // TODO: See if we can recover any data from the buffer. Currently we just wipe the buffer.
178   m_pBuffer->Initialize(m_OutputSize);
179   m_BufferOffset = 0;
180 }
181
182 // CDSPChain
183 //////////////////////////////////////////////////////////////////////////////////////
184 CDSPChain::CDSPChain() :
185   m_pInputSlice(NULL)
186 {
187
188 }
189
190 CDSPChain::~CDSPChain()
191 {
192   // Clean up any remaining filters
193   DisposeGraph();
194   delete m_pInputSlice; // This will not be going to anyone, so it must be cleaned-up
195 }
196
197 IAudioSource* CDSPChain::GetSource()
198 {
199   return (IAudioSource*)this;
200 }
201
202 IAudioSink* CDSPChain::GetSink()
203 {
204   return (IAudioSink*)this;
205 }
206
207 MA_RESULT CDSPChain::CreateFilterGraph(CStreamDescriptor* pDesc)
208 {
209   if (!pDesc)
210     return MA_ERROR;
211
212   // See if stream wants to be passed-through untouched
213   bool passthrough = false;
214   if (MA_SUCCESS == pDesc->GetAttributes()->GetBool(MA_ATT_TYPE_PASSTHROUGH,&passthrough) && passthrough)
215   {
216     CLog::Log(LOGINFO,"MasterAudio:CDSPChain: Detected passthrough stream. No filters will be created or applied.");
217     return MA_SUCCESS;
218   }
219   
220   // TODO: Dynamically create DSP Filter Graph 
221   CLog::Log(LOGINFO,"MasterAudio:CDSPChain: Creating dummy filter graph.");
222   return MA_SUCCESS;
223 }
224
225 // IAudioSource
226 MA_RESULT CDSPChain::TestOutputFormat(CStreamDescriptor* pDesc)
227 {
228   // TODO: Implement Properly
229
230   return MA_SUCCESS;
231 }
232
233 MA_RESULT CDSPChain::SetOutputFormat(CStreamDescriptor* pDesc)
234 {
235   // TODO: Implement Properly
236
237   return MA_SUCCESS;
238 }
239
240 MA_RESULT CDSPChain::GetSlice(audio_slice** pSlice)
241 {
242   // Currently just pass the input to the output
243   // TODO: Push data through the DSP Filter Graph.
244   if (!pSlice)
245     return MA_ERROR;
246
247   if (!m_pInputSlice)
248   {
249     *pSlice = NULL;
250     return MA_NEED_DATA;
251   }
252
253   *pSlice = m_pInputSlice;
254
255   m_pInputSlice = NULL; // Free up the input slot
256
257   return MA_SUCCESS;
258 }
259
260 // IAudioSink
261 MA_RESULT CDSPChain::TestInputFormat(CStreamDescriptor* pDesc)
262 {
263   // TODO: Implement Properly
264
265   return MA_SUCCESS;
266 }
267
268 MA_RESULT CDSPChain::SetInputFormat(CStreamDescriptor* pDesc)
269 {
270   // TODO: Implement Properly
271
272   return MA_SUCCESS;
273 }
274
275 MA_RESULT CDSPChain::AddSlice(audio_slice* pSlice)
276 {
277   if (!pSlice)
278     return MA_ERROR;
279
280   if (m_pInputSlice)
281     return MA_BUSYORFULL; // Only one input slice can be queued at a time
282
283   m_pInputSlice = pSlice;
284   return MA_SUCCESS;
285 }
286
287 float CDSPChain::GetMaxLatency()
288 {
289   // TODO: Calculate and return the actual latency through the filter graph
290   return 0.0f;
291 }
292
293 void CDSPChain::Flush()
294 {
295   // TODO: Flush filter graph
296
297   delete m_pInputSlice;
298   m_pInputSlice = NULL; // We are the last ones with this data. Free it.
299 }
300
301 // Private Methods
302
303 void CDSPChain::DisposeGraph()
304 {
305   // Clean up all filters in the graph
306 }
307
308 // CHardwareMixer
309 //////////////////////////////////////////////////////////////////////////////////////
310
311 CHardwareMixer::CHardwareMixer(int maxChannels) :
312   m_MaxChannels(maxChannels),
313   m_ActiveChannels(0),
314   m_pChannel(NULL)
315 {
316   // Create channel objects
317   // TODO: Use channel factory
318   m_pChannel = new IMixerChannel*[m_MaxChannels];
319   for (int c=0;c<m_MaxChannels;c++)
320     m_pChannel[c] = (IMixerChannel*)new CDirectSoundAdapter();
321 }
322
323 CHardwareMixer::~CHardwareMixer()
324 {
325   // Clean-up channel objects
326   if (m_pChannel)
327   {
328     for (int c=0;c<m_MaxChannels;c++)
329       delete m_pChannel[c];
330     delete[] m_pChannel;
331   }
332 }
333
334 int CHardwareMixer::OpenChannel(CStreamDescriptor* pDesc)
335 {
336   // Make sure we have a channel to open
337   if (m_ActiveChannels >= m_MaxChannels)
338   {
339     CLog::Log(LOGWARNING,"MasterAudio:PassthroughMixer: New channel requested, but none available (active_channels = %d, max_channels = %d).", m_ActiveChannels, m_MaxChannels);
340     return 0;
341   }  
342   int channel = 0;
343
344   // Find a free(idle) channel (there should be one since we are under max channels)
345   for(int c = 0; c < m_MaxChannels; c++)
346   {
347     if (m_pChannel[c]->IsIdle())
348     {
349       channel = c + 1;
350       m_pChannel[c]->SetInputFormat(pDesc);
351       m_ActiveChannels++;
352       break;
353     }
354   }
355   CLog::Log(LOGINFO,"MasterAudio:PassthroughMixer: Opened channel %d (active_channels = %d, max_channels = %d).", channel, m_ActiveChannels, m_MaxChannels);
356   return channel;
357 }
358
359 void CHardwareMixer::CloseChannel(int channel)
360 {
361   if (!channel || !m_ActiveChannels || channel > m_MaxChannels)
362     return;
363
364   CLog::Log(LOGINFO,"MasterAudio:PassthroughMixer: Closing channel %d.", channel);
365
366   m_pChannel[channel - 1]->Close();
367
368   m_ActiveChannels--;
369 }
370
371 MA_RESULT CHardwareMixer::ControlChannel(int channel, int controlCode)
372 {
373   if (!channel || !m_ActiveChannels || channel > m_MaxChannels)
374     return MA_ERROR;
375
376   int channelIndex = channel - 1;
377
378   CLog::Log(LOGDEBUG,"MasterAudio:PassthroughMixer: Control channel %d, code = %d", channel, controlCode);
379
380   switch(controlCode)
381   {
382   case MA_CONTROL_STOP:
383     m_pChannel[channelIndex]->Stop();
384     break;
385   case MA_CONTROL_PLAY:
386     m_pChannel[channelIndex]->Play();
387     break;
388   case MA_CONTROL_PAUSE:
389     m_pChannel[channelIndex]->Pause();
390     break;
391   case MA_CONTROL_RESUME:
392     m_pChannel[channelIndex]->Resume();
393     break;
394   default:
395     return MA_ERROR;
396   }
397   return MA_SUCCESS;
398 }
399
400 // TODO: Check channel state before controlling
401
402 MA_RESULT CHardwareMixer::SetChannelVolume(int channel, long vol)
403 {
404   if (!channel || !m_ActiveChannels || channel > m_MaxChannels)
405     return MA_ERROR;
406   
407   m_pChannel[channel-1]->SetVolume(vol);
408   return MA_SUCCESS;
409 }
410
411 IAudioSink* CHardwareMixer::GetChannelSink(int channel)
412 {
413   if (!channel || channel > m_MaxChannels)
414     return NULL;
415
416   return m_pChannel[channel-1];
417 }
418
419 float CHardwareMixer::GetMaxChannelLatency(int channel)
420 {
421   if (!channel || !m_ActiveChannels || channel > m_MaxChannels)
422     return 0.0f;  // Not going to render anything anyway
423   
424   return m_pChannel[channel-1]->GetMaxLatency();
425 }
426
427 void CHardwareMixer::FlushChannel(int channel)
428 {
429   if (!channel || !m_ActiveChannels || channel > m_MaxChannels)
430     return; // Nothing to flush
431   
432   m_pChannel[channel-1]->Flush();
433 }
434
435 bool CHardwareMixer::DrainChannel(int channel, unsigned int timeout)
436 {
437   if (!channel || !m_ActiveChannels || channel > m_MaxChannels)
438     return true; // Nothing to drain
439   
440   return m_pChannel[channel-1]->Drain(timeout);
441 }
442
443 // CStreamAttributeCollection
444 //////////////////////////////////////////////////////////////////////////////////////
445 CStreamAttributeCollection::CStreamAttributeCollection()
446 {
447
448 }
449
450 CStreamAttributeCollection::~CStreamAttributeCollection()
451 {
452
453 }
454
455 MA_RESULT CStreamAttributeCollection::GetInt(MA_ATTRIB_ID id, int* pVal)
456 {
457   stream_attribute* pAtt = FindAttribute(id);
458   if (!pAtt)
459     return MA_NOTFOUND;
460
461   if (pAtt->type != stream_attribute_int)
462     return MA_TYPE_MISMATCH;
463
464   *pVal = pAtt->intVal;
465   return MA_SUCCESS;
466 }
467
468 MA_RESULT CStreamAttributeCollection::GetFloat(MA_ATTRIB_ID id, float* pVal)
469 {
470   stream_attribute* pAtt = FindAttribute(id);
471   if (!pAtt)
472     return MA_NOTFOUND;
473
474   if (pAtt->type != stream_attribute_float)
475     return MA_TYPE_MISMATCH;
476
477   *pVal = pAtt->floatVal;
478   return MA_SUCCESS;
479 }
480
481 MA_RESULT CStreamAttributeCollection::GetString(MA_ATTRIB_ID id, char** pVal)
482 {
483   stream_attribute* pAtt = FindAttribute(id);
484   if (!pAtt)
485     return MA_NOTFOUND;
486
487   if (pAtt->type != stream_attribute_string)
488     return MA_TYPE_MISMATCH;
489
490   *pVal = pAtt->stringVal;
491   return MA_SUCCESS;
492 }
493
494 MA_RESULT CStreamAttributeCollection::GetPtr(MA_ATTRIB_ID id, void** pVal)
495 {
496   stream_attribute* pAtt = FindAttribute(id);
497   if (!pAtt)
498     return MA_NOTFOUND;
499
500   if (pAtt->type != stream_attribute_ptr)
501     return MA_TYPE_MISMATCH;
502
503   *pVal = pAtt->ptrVal;
504   return MA_SUCCESS;
505 }
506
507 MA_RESULT CStreamAttributeCollection::GetBool(MA_ATTRIB_ID id, bool* pVal)
508 {
509   stream_attribute* pAtt = FindAttribute(id);
510   if (!pAtt)
511     return MA_NOTFOUND;
512
513   if (pAtt->type != stream_attribute_bool)
514     return MA_TYPE_MISMATCH;
515
516   *pVal = pAtt->boolVal;
517   return MA_SUCCESS;
518 }
519
520 MA_RESULT CStreamAttributeCollection::SetInt(MA_ATTRIB_ID id, int val)
521 {
522   stream_attribute att;
523   att.intVal = val;
524   att.type = stream_attribute_int;
525   m_Attributes[id] = att;
526   return MA_SUCCESS;
527 }
528
529 MA_RESULT CStreamAttributeCollection::SetFloat(MA_ATTRIB_ID id, float val)
530 {
531   stream_attribute att;
532   att.floatVal = val;
533   att.type = stream_attribute_float;
534   m_Attributes[id] = att;
535   return MA_SUCCESS;
536 }
537
538 MA_RESULT CStreamAttributeCollection::SetString(MA_ATTRIB_ID id, char* val)
539 {
540   stream_attribute att;
541   att.stringVal = val;
542   att.type = stream_attribute_string;
543   m_Attributes[id] = att;
544   return MA_SUCCESS;
545 }
546
547 MA_RESULT CStreamAttributeCollection::SetPtr(MA_ATTRIB_ID id, void* val)
548 {
549   stream_attribute att;
550   att.ptrVal = val;
551   att.type = stream_attribute_ptr;
552   m_Attributes[id] = att;
553   return MA_SUCCESS;
554 }
555 MA_RESULT CStreamAttributeCollection::SetBool(MA_ATTRIB_ID id, bool val)
556 {
557   stream_attribute att;
558   att.boolVal = val;
559   att.type = stream_attribute_bool;
560   m_Attributes[id] = att;
561   return MA_SUCCESS;
562 }
563
564 stream_attribute* CStreamAttributeCollection::FindAttribute(MA_ATTRIB_ID id)
565 {
566   StreamAttributeIterator iter = m_Attributes.find(id);
567   if (iter != m_Attributes.end())
568     return &iter->second;
569   return NULL;
570 }