fixed: Stream Flush/Drain rework, housekeeping
[xbmc:xbmc-antiquated.git] / XBMC / xbmc / cores / masteraudio / DirectSoundAdapter.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 "DirectSoundAdapter.h"
24 // TODO: Remove WaveOut test code
25 #include "Util/WaveFileRenderer.h"
26 #include "Settings.h"
27
28 CWaveFileRenderer waveOut;
29 bool writeWave = false;
30
31 CDirectSoundAdapter::CDirectSoundAdapter() : 
32   m_pRenderer(NULL),
33   m_TotalBytesReceived(0),
34   m_pInputSlice(NULL)
35 {
36
37 }
38
39 CDirectSoundAdapter::~CDirectSoundAdapter()
40 {
41   Close();
42 }
43
44 // IAudioSink
45 MA_RESULT CDirectSoundAdapter::TestInputFormat(CStreamDescriptor* pDesc)
46 {
47   // TODO: Implement properly
48
49   return MA_SUCCESS;
50 }
51
52 MA_RESULT CDirectSoundAdapter::SetInputFormat(CStreamDescriptor* pDesc)
53 {
54   if (m_pRenderer)
55     Close();
56
57   if (!pDesc)  // NULL indicates 'Clear'
58     return MA_SUCCESS;
59
60   CStreamAttributeCollection* pAtts = pDesc->GetAttributes();
61   if (!pAtts)
62     return MA_ERROR;
63
64   unsigned int format = 0;
65   unsigned int channels = 0;
66   unsigned int bitsPerSample = 0;
67   unsigned int samplesPerSecond = 0;
68   unsigned int encoding = 0;
69  
70   // TODO: Write helper method to fetch attributes
71   if (MA_SUCCESS != pAtts->GetInt(MA_ATT_TYPE_STREAM_FORMAT,(int*)&format))
72     return MA_ERROR;
73
74   if (writeWave)
75     waveOut.Open(g_stSettings.m_logFolder + "\\waveout.wav",m_pRenderer->GetChunkLen(),samplesPerSecond);
76
77   // TODO: Find a more elegant way to configure the renderer
78   m_pRenderer = NULL;
79   switch (format)
80   {
81   case MA_STREAM_FORMAT_PCM:
82     if (MA_SUCCESS != pAtts->GetInt(MA_ATT_TYPE_CHANNELS,(int*)&channels))
83       return MA_ERROR;
84     if (MA_SUCCESS != pAtts->GetInt(MA_ATT_TYPE_BITDEPTH,(int*)&bitsPerSample))
85       return MA_ERROR;
86     if (MA_SUCCESS != pAtts->GetInt(MA_ATT_TYPE_SAMPLESPERSEC,(int*)&samplesPerSecond))
87       return MA_ERROR;
88     m_pRenderer = CAudioRendererFactory::Create(NULL,channels, samplesPerSecond, bitsPerSample, false,"",false,false);
89     break;
90   case MA_STREAM_FORMAT_ENCODED:
91     if ((MA_SUCCESS == pAtts->GetInt(MA_ATT_TYPE_ENCODING,(int*)&encoding)) && (encoding == MA_STREAM_ENCODING_AC3 || encoding == MA_STREAM_ENCODING_DTS))
92     {
93       m_pRenderer = CAudioRendererFactory::Create(NULL, 2, 48000, 16, false, "", false, true);
94       break;
95     }
96   default:
97     break;  // Unsupported format
98   }
99   if (!m_pRenderer)
100     return MA_ERROR;
101
102   return m_OutputBuffer.Initialize(m_pRenderer->GetChunkLen()*2) ? MA_SUCCESS : MA_ERROR; // TODO: This is wasteful, but currently required for variable-length writes.
103 }
104
105 MA_RESULT CDirectSoundAdapter::AddSlice(audio_slice* pSlice)
106 {
107   // Get some sizes to prevent duplicate calls
108   size_t newLen = pSlice->header.data_len;
109   size_t bufferLen = m_OutputBuffer.GetLen();
110   size_t chunkLen = m_pRenderer->GetChunkLen();
111
112   // See if we can send some data to the renderer
113   if (bufferLen > chunkLen)
114   {
115     size_t bytesWritten = m_pRenderer->AddPackets((unsigned char*)m_OutputBuffer.GetData(NULL), bufferLen);
116     size_t bytesLeft = bufferLen - bytesWritten;
117     if (bytesWritten) // The renderer took some data
118     {
119       if (writeWave)
120       {
121         waveOut.WriteData((short*)m_OutputBuffer.GetData(NULL), bufferLen);
122         waveOut.Save();
123       }
124
125       if (bytesLeft)
126         m_OutputBuffer.ShiftUp(bytesWritten);
127       else
128         m_OutputBuffer.Empty();
129       bufferLen = bytesLeft; 
130     }
131     else  // The renderer was already full and we have some data to give. No need to accept more data.
132       return MA_BUSYORFULL;
133   }
134
135   // If we still have at least one chunk, don't accept any more data
136   if (bufferLen > chunkLen)
137     return MA_BUSYORFULL;
138
139   // See if we have space to store the incoming data
140   if (bufferLen + newLen > m_OutputBuffer.GetMaxLen())  // TODO: Should we dynamically grow the buffer? Split it up and make multiple calls?
141     return MA_ERROR;  // This will likely cause us to clog up and hang...
142
143   // Save the new slice data
144   m_OutputBuffer.Write(pSlice->get_data(), newLen);
145
146   m_TotalBytesReceived += pSlice->header.data_len;
147
148   delete pSlice;  // We are the end of the road for this slice
149
150   return MA_SUCCESS;
151 }
152
153 float CDirectSoundAdapter::GetMaxLatency()
154 {
155   if (!m_pRenderer)
156     return 0;
157
158   return m_pRenderer->GetDelay();
159 }
160
161 void CDirectSoundAdapter::Flush()
162 {
163   if (m_pRenderer)
164     m_pRenderer->Stop();
165 }
166
167 bool CDirectSoundAdapter::Drain(unsigned int timeout)
168 {
169   // TODO: Find a way to honor the timeout
170
171   if (m_pRenderer)
172     m_pRenderer->WaitCompletion();
173
174   return true;
175 }
176
177 // IRenderingControl
178 void CDirectSoundAdapter::Play()
179 {
180   if (m_pRenderer)
181     m_pRenderer->Resume();
182 }
183
184 void CDirectSoundAdapter::Stop()
185 {
186   if (writeWave)
187     waveOut.Close(true);
188
189   if (m_pRenderer)
190     m_pRenderer->Stop();
191
192   CLog::Log(LOGINFO, "MasterAudio:DirectSoundAdapter: Stopped - Total Bytes Received = %I64d",m_TotalBytesReceived);
193 }
194
195 void CDirectSoundAdapter::Pause()
196 {
197   if (m_pRenderer)
198     m_pRenderer->Pause();
199 }
200
201 void CDirectSoundAdapter::Resume()
202 {
203   if (m_pRenderer)
204     m_pRenderer->Resume();
205 }
206
207 void CDirectSoundAdapter::SetVolume(long vol)
208 {
209   if (m_pRenderer)
210     m_pRenderer->SetCurrentVolume(vol);
211 }
212
213 // IMixerChannel
214 void CDirectSoundAdapter::Close()
215 {
216   // Reset the state of the channel
217
218   delete m_pInputSlice; // We don't need it and can't give it away
219
220   if (m_pRenderer)
221     m_pRenderer->Deinitialize();
222
223   delete m_pRenderer;
224   m_pRenderer = NULL;
225
226   m_OutputBuffer.Empty();
227 }
228
229 bool CDirectSoundAdapter::IsIdle()
230 {
231   return (m_pRenderer == NULL);
232 }