fixed: this memcpy in CAudioDecoder should be a memmove
[vuplus_xbmc] / xbmc / cores / paplayer / AudioDecoder.cpp
1 /*
2  *      Copyright (C) 2005-2008 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 "AudioDecoder.h"
23 #include "CodecFactory.h"
24 #include "GUISettings.h"
25 #include "FileItem.h"
26 #include "MusicInfoTag.h"
27 #include "utils/SingleLock.h"
28 #include "utils/log.h"
29 #include <math.h>
30
31 #define INTERNAL_BUFFER_LENGTH  sizeof(float)*2*44100       // float samples, 2 channels, 44100 samples per sec = 1 second
32
33 CAudioDecoder::CAudioDecoder()
34 {
35   m_codec = NULL;
36
37   m_eof = false;
38
39   m_status = STATUS_NO_FILE;
40   m_canPlay = false;
41
42   m_gaplessBufferSize = 0;
43   m_blockSize = 4;
44 }
45
46 CAudioDecoder::~CAudioDecoder()
47 {
48   Destroy();
49 }
50
51 void CAudioDecoder::Destroy()
52 {
53   CSingleLock lock(m_critSection);
54   m_status = STATUS_NO_FILE;
55
56   m_pcmBuffer.Destroy();
57   m_gaplessBufferSize = 0;
58
59   if ( m_codec )
60     delete m_codec;
61   m_codec = NULL;
62
63   m_canPlay = false;
64 }
65
66 bool CAudioDecoder::Create(const CFileItem &file, __int64 seekOffset, unsigned int nBufferSize)
67 {
68   Destroy();
69
70   CSingleLock lock(m_critSection);
71   // create our pcm buffer
72   m_pcmBuffer.Create((int)std::max<unsigned int>(2, nBufferSize) *
73                      INTERNAL_BUFFER_LENGTH);
74
75   // reset our playback timing variables
76   m_eof = false;
77
78   // get correct cache size
79   unsigned int filecache = g_guiSettings.GetInt("cacheaudio.internet");
80   if ( file.IsHD() )
81     filecache = g_guiSettings.GetInt("cache.harddisk");
82   else if ( file.IsOnDVD() )
83     filecache = g_guiSettings.GetInt("cacheaudio.dvdrom");
84   else if ( file.IsOnLAN() )
85     filecache = g_guiSettings.GetInt("cacheaudio.lan");
86
87   // create our codec
88   m_codec=CodecFactory::CreateCodecDemux(file.m_strPath, file.GetMimeType(), filecache * 1024);
89
90   if (!m_codec || !m_codec->Init(file.m_strPath, filecache * 1024))
91   {
92     CLog::Log(LOGERROR, "CAudioDecoder: Unable to Init Codec while loading file %s", file.m_strPath.c_str());
93     Destroy();
94     return false;
95   }
96   m_blockSize = m_codec->m_Channels * m_codec->m_BitsPerSample / 8;
97   
98   // set total time from the given tag
99   if (file.HasMusicInfoTag() && file.GetMusicInfoTag()->GetDuration())
100     m_codec->SetTotalTime(file.GetMusicInfoTag()->GetDuration());
101
102   if (seekOffset)
103     m_codec->Seek(seekOffset);
104
105   m_status = STATUS_QUEUING;
106
107   return true;
108 }
109
110 void CAudioDecoder::GetDataFormat(unsigned int *channels, unsigned int *samplerate, unsigned int *bitspersample)
111 {
112   if (!m_codec)
113     return;
114
115   if (channels) *channels = m_codec->m_Channels;
116   if (samplerate) *samplerate = m_codec->m_SampleRate;
117   if (bitspersample) *bitspersample = m_codec->m_BitsPerSample;
118 }
119
120 __int64 CAudioDecoder::Seek(__int64 time)
121 {
122   m_pcmBuffer.Clear();
123   if (!m_codec)
124     return 0;
125   if (time < 0) time = 0;
126   if (time > m_codec->m_TotalTime) time = m_codec->m_TotalTime;
127   return m_codec->Seek(time);
128 }
129
130 __int64 CAudioDecoder::TotalTime()
131 {
132   if (m_codec)
133     return m_codec->m_TotalTime;
134   return 0;
135 }
136
137 unsigned int CAudioDecoder::GetDataSize()
138 {
139   if (m_status == STATUS_QUEUING || m_status == STATUS_NO_FILE)
140     return 0;
141   // check for end of file and end of buffer
142   if (m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() < PACKET_SIZE)
143     m_status = STATUS_ENDED;
144   return m_pcmBuffer.getMaxReadSize() / sizeof(float);
145 }
146
147 void *CAudioDecoder::GetData(unsigned int size)
148 {
149   if (size > OUTPUT_SAMPLES)
150   {
151     CLog::Log(LOGWARNING, "CAudioDecoder::GetData() more bytes/samples (%i) requested than we have to give (%i)!", size, OUTPUT_SAMPLES);
152     size = OUTPUT_SAMPLES;
153   }
154   // first copy anything from our gapless buffer
155   if (m_gaplessBufferSize > size)
156   {
157     memcpy(m_outputBuffer, m_gaplessBuffer, size*sizeof(float));
158     memmove(m_gaplessBuffer, m_gaplessBuffer + size, (m_gaplessBufferSize - size)*sizeof(float));
159     m_gaplessBufferSize -= size;
160     return m_outputBuffer;
161   }
162   if (m_gaplessBufferSize)
163     memcpy(m_outputBuffer, m_gaplessBuffer, m_gaplessBufferSize*sizeof(float));
164
165   if (m_pcmBuffer.ReadData( (char *)(m_outputBuffer + m_gaplessBufferSize), (size - m_gaplessBufferSize) * sizeof(float)))
166   {
167     m_gaplessBufferSize = 0;
168     // check for end of file + end of buffer
169     if ( m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() < (int) (OUTPUT_SAMPLES * sizeof(float)))
170     {
171       CLog::Log(LOGINFO, "CAudioDecoder::GetData() ending track - only have %lu samples left", (unsigned long)(m_pcmBuffer.getMaxReadSize() / sizeof(float)));
172       m_status = STATUS_ENDED;
173     }
174     return m_outputBuffer;
175   }
176   CLog::Log(LOGERROR, "CAudioDecoder::GetData() ReadBinary failed with %i samples", size - m_gaplessBufferSize);
177   return NULL;
178 }
179
180 void CAudioDecoder::PrefixData(void *data, unsigned int size)
181 {
182   if (!data)
183   {
184     CLog::Log(LOGERROR, "CAudioDecoder::PrefixData() failed - null data pointer");
185     return;
186   }
187   m_gaplessBufferSize = std::min<unsigned int>(PACKET_SIZE, size);
188   memcpy(m_gaplessBuffer, data, m_gaplessBufferSize*sizeof(float));
189   if (m_gaplessBufferSize != size)
190     CLog::Log(LOGWARNING, "CAudioDecoder::PrefixData - losing %i bytes of audio data in track transistion", size - m_gaplessBufferSize);
191 }
192
193 int CAudioDecoder::ReadSamples(int numsamples)
194 {
195   if (m_status == STATUS_NO_FILE || m_status == STATUS_ENDING || m_status == STATUS_ENDED)
196     return RET_SLEEP;             // nothing loaded yet
197
198   // start playing once we're fully queued and we're ready to go
199   if (m_status == STATUS_QUEUED && m_canPlay)
200     m_status = STATUS_PLAYING;
201
202   // grab a lock to ensure the codec is created at this point.
203   CSingleLock lock(m_critSection);
204
205   // Read in more data
206   int maxsize = std::min<int>(INPUT_SAMPLES,
207                   (m_pcmBuffer.getMaxWriteSize() / (int)(sizeof (float))));
208   numsamples = std::min<int>(numsamples, maxsize);
209   numsamples -= (numsamples % m_codec->m_Channels);  // make sure it's divisible by our number of channels
210   if ( numsamples )
211   {
212     int actualsamples = 0;
213     // if our codec sends floating point, then read it
214     int result = READ_ERROR;
215     if (m_codec->HasFloatData())
216       result = m_codec->ReadSamples(m_inputBuffer, numsamples, &actualsamples);
217     else
218       result = ReadPCMSamples(m_inputBuffer, numsamples, &actualsamples);
219
220     if ( result != READ_ERROR && actualsamples )
221     {
222       // do any post processing of the audio (eg replaygain etc.)
223       ProcessAudio(m_inputBuffer, actualsamples);
224
225       // move it into our buffer
226       m_pcmBuffer.WriteData((char *)m_inputBuffer, actualsamples * sizeof(float));
227
228       // update status
229       if (m_status == STATUS_QUEUING && m_pcmBuffer.getMaxReadSize() > m_pcmBuffer.getSize() * 0.9)
230       {
231         CLog::Log(LOGINFO, "AudioDecoder: File is queued");
232         m_status = STATUS_QUEUED;
233       }
234
235       if (result == READ_EOF) // EOF reached
236       {
237         // setup ending if we're within set time of the end (currently just EOF)
238         m_eof = true;
239         if (m_status < STATUS_ENDING)
240           m_status = STATUS_ENDING;
241       }
242
243       return RET_SUCCESS;
244     }
245     if (result == READ_ERROR)
246     {
247       // error decoding, lets finish up and get out
248       CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding %i", result);
249       return RET_ERROR;
250     }
251     if (result == READ_EOF)
252     {
253       m_eof = true;
254       // setup ending if we're within set time of the end (currently just EOF)
255       if (m_status < STATUS_ENDING)
256         m_status = STATUS_ENDING;
257     }
258   }
259   return RET_SLEEP; // nothing to do
260 }
261
262 void CAudioDecoder::ProcessAudio(float *data, int numsamples)
263 {
264   if (g_guiSettings.m_replayGain.iType != REPLAY_GAIN_NONE)
265   {
266     float gainFactor = GetReplayGain();
267     for (int i = 0; i < numsamples; i++)
268     {
269       data[i] *= gainFactor;
270       // check the range (is this needed here?)
271       if (data[i] > 1.0f) data[i] = 1.0f;
272       if (data[i] < -1.0f) data[i] = -1.0f;
273     }
274   }
275 }
276
277 float CAudioDecoder::GetReplayGain()
278 {
279 #define REPLAY_GAIN_DEFAULT_LEVEL 89.0f
280   // Compute amount of gain
281   float replaydB = (float)g_guiSettings.m_replayGain.iNoGainPreAmp;
282   float peak = 0.0f;
283   if (g_guiSettings.m_replayGain.iType == REPLAY_GAIN_ALBUM)
284   {
285     if (m_codec->m_replayGain.iHasGainInfo & REPLAY_GAIN_HAS_ALBUM_INFO)
286     {
287       replaydB = (float)g_guiSettings.m_replayGain.iPreAmp + (float)m_codec->m_replayGain.iAlbumGain / 100.0f;
288       peak = m_codec->m_replayGain.fAlbumPeak;
289     }
290     else if (m_codec->m_replayGain.iHasGainInfo & REPLAY_GAIN_HAS_TRACK_INFO)
291     {
292       replaydB = (float)g_guiSettings.m_replayGain.iPreAmp + (float)m_codec->m_replayGain.iTrackGain / 100.0f;
293       peak = m_codec->m_replayGain.fTrackPeak;
294     }
295   }
296   else if (g_guiSettings.m_replayGain.iType == REPLAY_GAIN_TRACK)
297   {
298     if (m_codec->m_replayGain.iHasGainInfo & REPLAY_GAIN_HAS_TRACK_INFO)
299     {
300       replaydB = (float)g_guiSettings.m_replayGain.iPreAmp + (float)m_codec->m_replayGain.iTrackGain / 100.0f;
301       peak = m_codec->m_replayGain.fTrackPeak;
302     }
303     else if (m_codec->m_replayGain.iHasGainInfo & REPLAY_GAIN_HAS_ALBUM_INFO)
304     {
305       replaydB = (float)g_guiSettings.m_replayGain.iPreAmp + (float)m_codec->m_replayGain.iAlbumGain / 100.0f;
306       peak = m_codec->m_replayGain.fAlbumPeak;
307     }
308   }
309   // convert to a gain type
310   float replaygain = pow(10.0f, (replaydB - REPLAY_GAIN_DEFAULT_LEVEL)* 0.05f);
311   // check peaks
312   if (g_guiSettings.m_replayGain.bAvoidClipping)
313   {
314     if (fabs(peak * replaygain) > 1.0f)
315       replaygain = 1.0f / fabs(peak);
316   }
317   return replaygain;
318 }
319
320 int CAudioDecoder::ReadPCMSamples(float *buffer, int numsamples, int *actualsamples)
321 {
322   // convert samples to bytes
323   numsamples *= (m_codec->m_BitsPerSample / 8);
324
325   // read in our PCM data
326   int result = m_codec->ReadPCM(m_pcmInputBuffer, numsamples, actualsamples);
327
328   // convert to floats (-1 ... 1) range
329   int i;
330   switch (m_codec->m_BitsPerSample)
331   {
332   case 8:
333     for (i = 0; i < *actualsamples; i++)
334       m_inputBuffer[i] = 1.0f / 0x7f * (m_pcmInputBuffer[i] - 128);
335     break;
336   case 16:
337     *actualsamples /= 2;
338     for (i = 0; i < *actualsamples; i++)
339       m_inputBuffer[i] = 1.0f / 0x7fff * ((short *)m_pcmInputBuffer)[i];
340     break;
341   case 24:
342     *actualsamples /= 3;
343     for (i = 0; i < *actualsamples; i++)
344       m_inputBuffer[i] = 1.0f / 0x7fffff * (((int)m_pcmInputBuffer[3*i] << 0) | ((int)m_pcmInputBuffer[3*i+1] << 8) | (((int)((char *)m_pcmInputBuffer)[3*i+2]) << 16));
345     break;
346   }
347   return result;
348 }
349