fixed: paplayer bitrate info, iplayer interface was changed but paplayer wasn't updated.
[xbmc:xbmc-antiquated.git] / xbmc / cores / paplayer / paplayer_linux.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 #ifndef __APPLE__
23 #include "../../stdafx.h"
24 #include "paplayer.h"
25 #include "CodecFactory.h"
26 #include "../../utils/GUIInfoManager.h"
27 #include "AudioContext.h"
28 #include "../../FileSystem/FileShoutcast.h"
29 #include "../../Application.h"
30 #ifdef HAS_KARAOKE
31 #include "../../CdgParser.h"
32 #endif
33 #include "FileItem.h"
34 #include "Settings.h"
35 #include "MusicInfoTag.h"
36
37 #ifdef _LINUX
38 #define XBMC_SAMPLE_RATE 44100
39 #else
40 #define XBMC_SAMPLE_RATE 48000
41 #endif
42
43 #define CHECK_ALSA(l,s,e) if ((e)<0) CLog::Log(l,"%s - %s, alsa error: %s",__FUNCTION__,s,snd_strerror(e));
44 #define CHECK_ALSA_RETURN(l,s,e) CHECK_ALSA((l),(s),(e)); if ((e)<0) return false;
45
46 #define VOLUME_FFWD_MUTE 900 // 9dB
47
48 #define FADE_TIME 2 * 2048.0f / XBMC_SAMPLE_RATE.0f      // 2 packets
49
50 #define TIME_TO_CACHE_NEXT_FILE 5000L         // 5 seconds
51 #define TIME_TO_CROSS_FADE      10000L        // 10 seconds
52
53 extern XFILE::CFileShoutcast* m_pShoutCastRipper;
54
55 // PAP: Psycho-acoustic Audio Player
56 // Supporting all open  audio codec standards.
57 // First one being nullsoft's nsv audio decoder format
58
59 PAPlayer::PAPlayer(IPlayerCallback& callback) : IPlayer(callback)
60 {
61   m_bIsPlaying = false;
62   m_bPaused = false;
63   m_cachingNextFile = false;
64   m_currentlyCrossFading = false;
65   m_bQueueFailed = false;
66
67   m_currentDecoder = 0;
68
69   m_iSpeed = 1;
70   m_SeekTime=-1;
71   m_IsFFwdRewding = false;
72   m_timeOffset = 0;
73
74   m_pStream[0] = NULL;
75   m_pStream[1] = NULL;
76
77   // periods will contain the amount of data that can be played with each call to alsa.
78   // the unit is "Frames". for 2 channels 16 bit its 4.
79   // we initialize with packet_size which is probably too big. later the alsa calls will set these
80   // values correctly.
81   m_periods[0] = PACKET_SIZE / 4;
82   m_periods[1] = PACKET_SIZE / 4;
83
84   m_currentStream = 0;
85   m_packet[0][0].packet = NULL;
86   m_packet[1][0].packet = NULL;
87
88   m_bytesSentOut = 0;
89
90   m_BytesPerSecond = 0;
91   m_SampleRate = 0;
92   m_Channels = 0;
93   m_BitsPerSample = 0;
94
95   m_resampleAudio = true;
96
97   m_visBufferLength = 0;
98   m_pCallback = NULL;
99
100   m_forceFadeToNext = false;
101   m_CacheLevel = 0;
102   m_LastCacheLevelCheck = 0;
103
104   m_currentFile = new CFileItem;
105   m_nextFile = new CFileItem;
106 }
107
108 PAPlayer::~PAPlayer()
109 {
110   CloseFileInternal(true);
111   delete m_currentFile;
112   delete m_nextFile;
113 }
114
115
116 void PAPlayer::OnExit()
117 {
118
119 }
120
121 bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
122 {
123   if (m_currentlyCrossFading) CloseFileInternal(false); //user seems to be in a hurry
124
125   m_crossFading = g_guiSettings.GetInt("musicplayer.crossfade");
126   //no crossfading for cdda, cd-reading goes mad and no crossfading for last.fm doesn't like two connections
127   if (file.IsCDDA() || file.IsLastFM()) m_crossFading = 0;
128   if (m_crossFading && IsPlaying())
129   {
130     //do a short crossfade on trackskip
131     //set to max 2 seconds for these prev/next transitions
132     if (m_crossFading > 2) m_crossFading = 2;
133     //queue for crossfading
134     bool result = QueueNextFile(file, false);
135     if (result)
136     {
137       //crossfading value may be update by QueueNextFile when nr of channels changed
138       if (!m_crossFading) // swap to next track
139         m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED);
140       else //force to fade to next track immediately
141         m_forceFadeToNext = true;
142     }
143     return result;
144   }
145
146   // normal opening of file, nothing playing or crossfading not enabled
147   // however no need to return to gui audio device
148   CloseFileInternal(false);
149
150   // always open the file using the current decoder
151   m_currentDecoder = 0;
152
153   if (!m_decoder[m_currentDecoder].Create(file, (__int64)(options.starttime * 1000), m_crossFading))
154     return false;
155
156   m_iSpeed = 1;
157   m_bPaused = false;
158   m_bStopPlaying = false;
159   m_bytesSentOut = 0;
160
161   CLog::Log(LOGINFO, "PAP Player: Playing %s", file.m_strPath.c_str());
162
163   m_timeOffset = (__int64)(options.starttime * 1000);
164
165   m_decoder[m_currentDecoder].GetDataFormat(&m_Channels, &m_SampleRate, &m_BitsPerSample);
166
167   if (!CreateStream(m_currentStream, m_Channels, m_SampleRate, m_BitsPerSample))
168   {
169     m_decoder[m_currentDecoder].Destroy();
170     CLog::Log(LOGERROR, "PAPlayer::Unable to create audio stream");
171   }
172
173   *m_currentFile = file;
174
175   if (ThreadHandle() == NULL)
176     Create();
177
178   m_startEvent.Set();
179
180   m_bIsPlaying = true;
181   m_cachingNextFile = false;
182   m_currentlyCrossFading = false;
183   m_forceFadeToNext = false;
184   m_bQueueFailed = false;
185
186   m_decoder[m_currentDecoder].Start();  // start playback
187
188   if (m_pStream[m_currentStream])
189      snd_pcm_reset(m_pStream[m_currentStream]);
190
191   if (m_pStream[m_currentStream])
192      snd_pcm_pause(m_pStream[m_currentStream], 0);
193
194   return true;
195 }
196
197 void PAPlayer::UpdateCrossFadingTime(const CFileItem& file)
198 {
199   if ((m_crossFading = g_guiSettings.GetInt("musicplayer.crossfade")))
200   {
201     if (
202       m_crossFading &&
203       (
204         file.IsCDDA() ||
205         file.IsLastFM() ||
206         (
207           file.HasMusicInfoTag() && !g_guiSettings.GetBool("musicplayer.crossfadealbumtracks") &&
208           (m_currentFile->GetMusicInfoTag()->GetAlbum() != "") &&
209           (m_currentFile->GetMusicInfoTag()->GetAlbum() == file.GetMusicInfoTag()->GetAlbum()) &&
210           (m_currentFile->GetMusicInfoTag()->GetDiscNumber() == file.GetMusicInfoTag()->GetDiscNumber()) &&
211           (m_currentFile->GetMusicInfoTag()->GetTrackNumber() == file.GetMusicInfoTag()->GetTrackNumber() - 1)
212         )
213       )
214     )
215     {
216       m_crossFading = 0;
217     }
218   }
219 }
220
221 void PAPlayer::OnNothingToQueueNotify()
222 {
223   //nothing to queue, stop playing
224   m_bQueueFailed = true;
225 }
226
227 bool PAPlayer::QueueNextFile(const CFileItem &file)
228 {
229   return QueueNextFile(file, true);
230 }
231
232 bool PAPlayer::QueueNextFile(const CFileItem &file, bool checkCrossFading)
233 {
234   if (IsPaused())
235     Pause();
236
237   if (file.m_strPath == m_currentFile->m_strPath &&
238       file.m_lStartOffset > 0 &&
239       file.m_lStartOffset == m_currentFile->m_lEndOffset)
240   { // continuing on a .cue sheet item - return true to say we'll handle the transistion
241     *m_nextFile = file;
242     return true;
243   }
244
245   // check if we can handle this file at all
246   int decoder = 1 - m_currentDecoder;
247   __int64 seekOffset = (file.m_lStartOffset * 1000) / 75;
248   if (!m_decoder[decoder].Create(file, seekOffset, m_crossFading))
249   {
250     m_bQueueFailed = true;
251     return false;
252   }
253
254   // ok, we're good to go on queuing this one up
255   CLog::Log(LOGINFO, "PAP Player: Queuing next file %s", file.m_strPath.c_str());
256
257   m_bQueueFailed = false;
258   if (checkCrossFading)
259   {
260     UpdateCrossFadingTime(file);
261   }
262
263   unsigned int channels, samplerate, bitspersample;
264   m_decoder[decoder].GetDataFormat(&channels, &samplerate, &bitspersample);
265
266   // check the number of channels isn't changing (else we can't do crossfading)
267   if (m_crossFading && m_decoder[m_currentDecoder].GetChannels() == channels)
268   { // crossfading - need to create a new stream
269     if (!CreateStream(1 - m_currentStream, channels, samplerate, bitspersample))
270     {
271       m_decoder[decoder].Destroy();
272       CLog::Log(LOGERROR, "PAPlayer::Unable to create audio stream");
273     }
274   }
275   else
276   { // no crossfading if nr of channels is not the same
277     m_crossFading = 0;
278   }
279
280   *m_nextFile = file;
281
282   return true;
283 }
284
285
286
287 bool PAPlayer::CloseFileInternal(bool bAudioDevice /*= true*/)
288 {
289   if (IsPaused())
290     Pause();
291
292   m_bStopPlaying = true;
293   m_bStop = true;
294
295   m_visBufferLength = 0;
296   StopThread();
297
298   // kill both our streams if we need to
299   for (int i = 0; i < 2; i++)
300   {
301     m_decoder[i].Destroy();
302     FreeStream(i);
303   }
304
305   m_currentFile->Reset();
306   m_nextFile->Reset();
307
308   if(bAudioDevice)
309     g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE);
310
311   return true;
312 }
313
314 void PAPlayer::FreeStream(int stream)
315 {
316   if (m_pStream[stream])
317   {
318     snd_pcm_drain(m_pStream[stream]);
319     snd_pcm_close(m_pStream[stream]);
320   }
321   m_pStream[stream] = NULL;
322
323   if (m_packet[stream][0].packet)
324     free(m_packet[stream][0].packet);
325
326   for (int i = 0; i < PACKET_COUNT; i++)
327   {
328     m_packet[stream][i].packet = NULL;
329   }
330
331   m_resampler[stream].DeInitialize();
332 }
333
334 bool PAPlayer::CreateStream(int num, int channels, int samplerate, int bitspersample, CStdString codec)
335 {
336   snd_pcm_hw_params_t *hw_params=NULL;
337
338   FreeStream(num);
339
340   m_packet[num][0].packet = (BYTE*)malloc(PACKET_SIZE * PACKET_COUNT);
341   for (int i = 1; i < PACKET_COUNT ; i++)
342     m_packet[num][i].packet = m_packet[num][i - 1].packet + PACKET_SIZE;
343
344   m_SampleRateOutput = channels>2?samplerate:XBMC_SAMPLE_RATE;
345   m_BitsPerSampleOutput = 16;
346
347   m_BytesPerSecond = (m_BitsPerSampleOutput / 8)*m_SampleRateOutput*channels;
348
349   /* Open the device */
350   int nErr = snd_pcm_open(&m_pStream[num], g_guiSettings.GetString("audiooutput.audiodevice"), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
351     CHECK_ALSA_RETURN(LOGERROR,"pcm_open",nErr);
352
353   /* Allocate Hardware Parameters structures and fills it with config space for PCM */
354   snd_pcm_hw_params_alloca(&hw_params);
355
356   nErr = snd_pcm_hw_params_any(m_pStream[num], hw_params);
357     CHECK_ALSA_RETURN(LOGERROR,"hw_params_any",nErr);
358
359   nErr = snd_pcm_hw_params_set_access(m_pStream[num], hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
360     CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_access",nErr);
361
362   // always use 16 bit samples
363 #if defined(_POWERPC) || defined(_POWERPC64)
364   nErr = snd_pcm_hw_params_set_format(m_pStream[num], hw_params, SND_PCM_FORMAT_S16_BE);
365 #else
366   nErr = snd_pcm_hw_params_set_format(m_pStream[num], hw_params, SND_PCM_FORMAT_S16_LE);
367 #endif
368     CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_format",nErr);
369
370   nErr = snd_pcm_hw_params_set_rate_near(m_pStream[num], hw_params, &m_SampleRateOutput, NULL);
371     CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_rate",nErr);
372
373   nErr = snd_pcm_hw_params_set_channels(m_pStream[num], hw_params, channels);
374     CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_channels",nErr);
375
376     m_periods[num] = PACKET_SIZE / 4;
377     nErr = snd_pcm_hw_params_set_period_size_near(m_pStream[num], hw_params, &m_periods[num], 0);
378     CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_period_size",nErr);
379
380   snd_pcm_uframes_t buffer_size = PACKET_SIZE*20; // big enough buffer
381     nErr = snd_pcm_hw_params_set_buffer_size_near(m_pStream[num], hw_params, &buffer_size);
382     CHECK_ALSA_RETURN(LOGERROR,"hw_params_set_buffer_size",nErr);
383
384   unsigned int periodDuration = 0;
385   nErr = snd_pcm_hw_params_get_period_time(hw_params,&periodDuration, 0);
386     CHECK_ALSA(LOGERROR,"hw_params_get_period_time",nErr);
387
388     CLog::Log(LOGDEBUG,"PAPlayer::CreateStream - initialized. "
389          "sample rate: %d, channels: %d, period size: %d, buffer size: %d "
390          "period duration: %d",
391                m_SampleRateOutput,
392          channels,
393          (int) m_periods[num],
394          (int) buffer_size,
395          periodDuration);
396
397
398     /* Assign them to the playback handle and free the parameters structure */
399   nErr = snd_pcm_hw_params(m_pStream[num], hw_params);
400     CHECK_ALSA_RETURN(LOGERROR,"snd_pcm_hw_params",nErr);
401
402   /* disable underrun reporting and play silence */
403   nErr = snd_pcm_prepare (m_pStream[num]);
404     CHECK_ALSA(LOGERROR,"snd_pcm_prepare",nErr);
405
406     // create our resampler  // upsample to XBMC_SAMPLE_RATE, only do this for sources with 1 or 2 channels
407     m_resampler[num].InitConverter(samplerate, bitspersample, channels, m_SampleRateOutput, m_BitsPerSampleOutput, PACKET_SIZE);
408
409     // set initial volume
410     SetStreamVolume(num, g_stSettings.m_nVolumeLevel);
411
412     // TODO: How do we best handle the callback, given that our samplerate etc. may be
413     // changing at this point?
414
415     // fire off our init to our callback
416     if (m_pCallback)
417       m_pCallback->OnInitialize(channels, m_SampleRateOutput, m_BitsPerSampleOutput);
418
419   return true;
420 }
421
422 void PAPlayer::Pause()
423 {
424   CLog::Log(LOGDEBUG,"PAPlayer: pause m_bplaying: %d", m_bIsPlaying);
425   if (!m_bIsPlaying || !m_pStream)
426   return ;
427
428   m_bPaused = !m_bPaused;
429
430   if (m_bPaused)
431   {
432   // pause both streams if we're crossfading
433     if (m_pStream[m_currentStream])
434     snd_pcm_pause(m_pStream[m_currentStream], 1);
435
436   if (m_currentlyCrossFading && m_pStream[1 - m_currentStream])
437     snd_pcm_pause(m_pStream[1 - m_currentStream], 1);
438
439   CLog::Log(LOGDEBUG, "PAP Player: Playback paused");
440   }
441   else
442   {
443     if (m_pStream[m_currentStream]) {
444     snd_pcm_pause(m_pStream[m_currentStream], 0);
445   }
446
447   if (m_currentlyCrossFading && m_pStream[1 - m_currentStream])
448     snd_pcm_pause(m_pStream[1 - m_currentStream], 0);
449
450   FlushStreams();
451
452     CLog::Log(LOGDEBUG, "PAP Player: Playback resumed");
453   }
454 }
455
456 void PAPlayer::SetVolume(long nVolume)
457 {
458    // Todo: Grrrr.... no volume level in alsa. We'll need to do the math ourselves.
459   m_amp[m_currentStream].SetVolume(nVolume);
460
461 }
462
463 void PAPlayer::SetDynamicRangeCompression(long drc)
464 {
465   // TODO: Add volume amplification
466   CLog::Log(LOGDEBUG,"PAPlayer::SetDynamicRangeCompression - drc: %lu", drc);
467 }
468
469 void PAPlayer::Process()
470 {
471   CLog::Log(LOGDEBUG, "PAPlayer: Thread started");
472   if (m_startEvent.WaitMSec(100))
473   {
474     m_startEvent.Reset();
475
476     m_callback.OnPlayBackStarted();
477
478     do
479     {
480       if (!m_bPaused)
481       {
482         if (!ProcessPAP())
483           break;
484       }
485       else
486       {
487         Sleep(100);
488       }
489     }
490     while (!m_bStopPlaying && m_bIsPlaying && !m_bStop);
491
492     CLog::Log(LOGINFO, "PAPlayer: End of playback reached");
493     m_bIsPlaying = false;
494     if (!m_bStopPlaying && !m_bStop)
495     {
496       m_callback.OnPlayBackEnded();
497     }
498   }
499   CLog::Log(LOGDEBUG, "PAPlayer: Thread end");
500 }
501
502 void PAPlayer::ToFFRW(int iSpeed)
503 {
504   m_iSpeed = iSpeed;
505 }
506
507 void PAPlayer::UpdateCacheLevel()
508 {
509   //check cachelevel every .5 seconds
510   if (m_LastCacheLevelCheck + 500 < GetTickCount())
511   {
512     ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
513     if (codec)
514     {
515       m_CacheLevel = codec->GetCacheLevel();
516       m_LastCacheLevelCheck = GetTickCount();
517       //CLog::Log(LOGDEBUG,"Cachelevel: %i%%", m_CacheLevel);
518     }
519   }
520 }
521
522 bool PAPlayer::ProcessPAP()
523 {
524   /*
525    * Here's what we should be doing in each player loop:
526    *
527    * 1.  Run DoWork() on our audio device to actually output audio.
528    *
529    * 2.  Pass our current buffer to the audio device to see if it wants anything,
530    *     and if so, reduce our buffer size accordingly.
531    *
532    * 3.  Check whether we have space in our buffer for more data, and if so,
533    *     read some more in.
534    *
535    * 4.  Check for end of file and return false if we reach it.
536    *
537    * 5.  Perform any seeking and ffwd/rewding as necessary.
538    *
539    * 6.  If we don't do anything in 2...5, we can take a breather and break out for sleeping.
540    */
541   while (true)
542   {
543     if (m_bStop) return false;
544
545     // Check for .cue sheet item end
546     if (m_currentFile->m_lEndOffset && GetTime() >= GetTotalTime64())
547     {
548       CLog::Log(LOGINFO, "PAPlayer: Passed end of track in a .cue sheet item");
549       m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED);
550     }
551
552     // check whether we need to send off our callbacks etc.
553     int status = m_decoder[m_currentDecoder].GetStatus();
554     if (status == STATUS_NO_FILE)
555       return false;
556
557     UpdateCacheLevel();
558
559     // check whether we should queue the next file up
560     if ((GetTotalTime64() > 0) && GetTotalTime64() - GetTime() < TIME_TO_CACHE_NEXT_FILE + m_crossFading * 1000L && !m_cachingNextFile)
561     { // request the next file from our application
562       m_callback.OnQueueNextItem();
563       m_cachingNextFile = true;
564     }
565
566     if (m_crossFading && m_decoder[0].GetChannels() == m_decoder[1].GetChannels())
567     {
568       if (((GetTotalTime64() - GetTime() < m_crossFading * 1000L) || (m_forceFadeToNext)) && !m_currentlyCrossFading)
569       { // request the next file from our application
570         if (m_decoder[1 - m_currentDecoder].GetStatus() == STATUS_QUEUED && m_pStream[1 - m_currentStream])
571         {
572           m_currentlyCrossFading = true;
573           if (m_forceFadeToNext)
574           {
575             m_forceFadeToNext = false;
576             m_crossFadeLength = m_crossFading * 1000L;
577           }
578           else
579           {
580             m_crossFadeLength = GetTotalTime64() - GetTime();
581           }
582           m_currentDecoder = 1 - m_currentDecoder;
583           m_decoder[m_currentDecoder].Start();
584           m_currentStream = 1 - m_currentStream;
585           CLog::Log(LOGDEBUG, "Starting Crossfade - resuming stream %i", m_currentStream);
586
587           snd_pcm_pause(m_pStream[m_currentStream], 0);
588
589           m_callback.OnPlayBackStarted();
590           m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75;
591           m_bytesSentOut = 0;
592           *m_currentFile = *m_nextFile;
593           m_nextFile->Reset();
594           m_cachingNextFile = false;
595         }
596       }
597     }
598
599     // Check for EOF and queue the next track if applicable
600     if (m_decoder[m_currentDecoder].GetStatus() == STATUS_ENDED)
601     { // time to swap tracks
602       if (m_nextFile->m_strPath != m_currentFile->m_strPath ||
603           !m_nextFile->m_lStartOffset ||
604           m_nextFile->m_lStartOffset != m_currentFile->m_lEndOffset)
605       { // don't have a .cue sheet item
606         int nextstatus = m_decoder[1 - m_currentDecoder].GetStatus();
607         if (nextstatus == STATUS_QUEUED || nextstatus == STATUS_QUEUING || nextstatus == STATUS_PLAYING)
608         { // swap streams
609           CLog::Log(LOGDEBUG, "PAPlayer: Swapping tracks %i to %i", m_currentDecoder, 1-m_currentDecoder);
610           if (!m_crossFading || m_decoder[0].GetChannels() != m_decoder[1].GetChannels())
611           { // playing gapless (we use only the 1 output stream in this case)
612             int prefixAmount = m_decoder[m_currentDecoder].GetDataSize();
613             CLog::Log(LOGDEBUG, "PAPlayer::Prefixing %i samples of old data to new track for gapless playback", prefixAmount);
614             m_decoder[1 - m_currentDecoder].PrefixData(m_decoder[m_currentDecoder].GetData(prefixAmount), prefixAmount);
615             // check if we need to change the resampler (due to format change)
616             unsigned int channels, samplerate, bitspersample;
617             m_decoder[m_currentDecoder].GetDataFormat(&channels, &samplerate, &bitspersample);
618             unsigned int channels2, samplerate2, bitspersample2;
619             m_decoder[1 - m_currentDecoder].GetDataFormat(&channels2, &samplerate2, &bitspersample2);
620             // change of channels - reinitialize our speaker configuration
621             if (channels != channels2)
622             {
623               CLog::Log(LOGWARNING, "PAPlayer: Channel number has changed - restarting direct sound");
624               FreeStream(m_currentStream);
625               if (!CreateStream(m_currentStream, channels2, samplerate2, bitspersample2))
626               {
627                 CLog::Log(LOGERROR, "PAPlayer: Error creating stream!");
628                 return false;
629               }
630               snd_pcm_pause(m_pStream[m_currentStream], 0);
631             }
632             else if (samplerate != samplerate2 || bitspersample != bitspersample2)
633             {
634               CLog::Log(LOGINFO, "PAPlayer: Restarting resampler due to a change in data format");
635               m_resampler[m_currentStream].DeInitialize();
636               if (!m_resampler[m_currentStream].InitConverter(samplerate2, bitspersample2, channels2, XBMC_SAMPLE_RATE, 16, PACKET_SIZE))
637               {
638                 CLog::Log(LOGERROR, "PAPlayer: Error initializing resampler!");
639                 return false;
640               }
641             }
642             CLog::Log(LOGINFO, "PAPlayer: Starting new track");
643
644             m_decoder[m_currentDecoder].Destroy();
645             m_decoder[1 - m_currentDecoder].Start();
646             m_callback.OnPlayBackStarted();
647             m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75;
648             m_bytesSentOut = 0;
649             *m_currentFile = *m_nextFile;
650             m_nextFile->Reset();
651             m_cachingNextFile = false;
652             m_currentDecoder = 1 - m_currentDecoder;
653           }
654           else
655           { // cross fading - shouldn't ever get here - if we do, return false
656             if (!m_currentlyCrossFading)
657             {
658               CLog::Log(LOGERROR, "End of file Reached before crossfading kicked in!");
659               return false;
660             }
661             else
662             {
663               CLog::Log(LOGINFO, "End of file reached before crossfading finished!");
664               return false;
665             }
666           }
667         }
668         else
669         {
670           if (GetTotalTime64() <= 0 && !m_bQueueFailed)
671           { //we did not know the duration so didn't queue the next song, try queueing it now
672             if (!m_cachingNextFile)
673             {// request the next file from our application
674               m_callback.OnQueueNextItem();
675               m_cachingNextFile = true;
676             }
677           }
678           else
679           {
680             // no track queued - return and get another one once we are finished
681             // with the current stream
682             WaitForStream();
683             return false;
684           }
685         }
686       }
687       else
688       {
689         // set the next track playing (.cue sheet)
690         m_decoder[m_currentDecoder].SetStatus(STATUS_PLAYING);
691         m_callback.OnPlayBackStarted();
692         m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75;
693         m_bytesSentOut = 0;
694         *m_currentFile = *m_nextFile;
695         m_nextFile->Reset();
696         m_cachingNextFile = false;
697       }
698     }
699
700     // handle seeking and ffwd/rewding.
701     HandleSeeking();
702     if (!HandleFFwdRewd())
703     {
704       // need to skip to the next track - let's see if we already have another one
705       m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED);
706       continue; // loop around to start the next track
707     }
708
709     if (!m_bPaused) {
710
711     // Let our decoding stream(s) do their thing
712     int retVal = m_decoder[m_currentDecoder].ReadSamples(PACKET_SIZE);
713     if (retVal == RET_ERROR)
714     {
715       m_decoder[m_currentDecoder].Destroy();
716       return false;
717     }
718
719     int retVal2 = m_decoder[1 - m_currentDecoder].ReadSamples(PACKET_SIZE);
720     if (retVal2 == RET_ERROR)
721     {
722       m_decoder[1 - m_currentDecoder].Destroy();
723     }
724
725     // if we're cross-fading, then we do this for both streams, otherwise
726     // we do it just for the one stream.
727     if (m_currentlyCrossFading)
728     {
729       if (GetTime() >= m_crossFadeLength)  // finished
730       {
731         CLog::Log(LOGDEBUG, "Finished Crossfading");
732         m_currentlyCrossFading = false;
733         SetStreamVolume(m_currentStream, g_stSettings.m_nVolumeLevel);
734         FreeStream(1 - m_currentStream);
735         m_decoder[1 - m_currentDecoder].Destroy();
736       }
737       else
738       {
739         float fraction = (float)(m_crossFadeLength - GetTime()) / (float)m_crossFadeLength - 0.5f;
740         // make sure we can take valid logs.
741         if (fraction > 0.499f) fraction = 0.499f;
742         if (fraction < -0.499f) fraction = -0.499f;
743         float volumeCurrent = 2000.0f * log10(0.5f - fraction);
744         float volumeNext = 2000.0f * log10(0.5f + fraction);
745         SetStreamVolume(m_currentStream, g_stSettings.m_nVolumeLevel + (int)volumeCurrent);
746         SetStreamVolume(1 - m_currentStream, g_stSettings.m_nVolumeLevel + (int)volumeNext);
747         if (AddPacketsToStream(1 - m_currentStream, m_decoder[1 - m_currentDecoder]))
748           retVal2 = RET_SUCCESS;
749       }
750     }
751
752        // add packets as necessary
753        if (AddPacketsToStream(m_currentStream, m_decoder[m_currentDecoder]))
754          retVal = RET_SUCCESS;
755
756        if (retVal == RET_SLEEP && retVal2 == RET_SLEEP)
757          Sleep(1);
758     }
759     else
760         Sleep(100);
761   }
762   return true;
763 }
764
765 void PAPlayer::ResetTime()
766 {
767   m_bytesSentOut = 0;
768 }
769
770 __int64 PAPlayer::GetTime()
771 {
772   __int64  timeplus = m_BytesPerSecond ? (__int64)(((float) m_bytesSentOut / (float)m_BytesPerSecond ) * 1000.0) : 0;
773   return m_timeOffset + timeplus - m_currentFile->m_lStartOffset * 1000 / 75;
774 }
775
776 __int64 PAPlayer::GetTotalTime64()
777 {
778   __int64 total = m_decoder[m_currentDecoder].TotalTime();
779   if (m_currentFile->m_lEndOffset)
780     total = m_currentFile->m_lEndOffset * 1000 / 75;
781   if (m_currentFile->m_lStartOffset)
782     total -= m_currentFile->m_lStartOffset * 1000 / 75;
783   return total;
784 }
785
786 int PAPlayer::GetTotalTime()
787 {
788   return (int)(GetTotalTime64()/1000);
789 }
790
791 int PAPlayer::GetCacheLevel() const
792 {
793   const ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
794   if (codec)
795     return codec->GetCacheLevel();
796
797   return -1;
798 }
799
800 int PAPlayer::GetChannels()
801 {
802   ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
803   if (codec)
804     return codec->m_Channels;
805   return 0;
806 }
807
808 int PAPlayer::GetBitsPerSample()
809 {
810   ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
811   if (codec)
812     return codec->m_BitsPerSample;
813   return 0;
814 }
815
816 int PAPlayer::GetSampleRate()
817 {
818   ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
819   if (codec)
820     return (int)((codec->m_SampleRate / 1000) + 0.5);
821   return 0;
822 }
823
824 CStdString PAPlayer::GetCodecName()
825 {
826   ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
827   if (codec)
828     return codec->m_CodecName;
829   return "";
830 }
831
832 int PAPlayer::GetAudioBitrate()
833 {
834   ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
835   if (codec)
836     return (int)((codec->m_Bitrate / 1000) + 0.5); // in kbits/s, rounded to the nearest int
837   return 0;
838 }
839
840 bool PAPlayer::CanSeek()
841 {
842   return ((m_decoder[m_currentDecoder].TotalTime() > 0) && m_decoder[m_currentDecoder].CanSeek());
843 }
844
845 void PAPlayer::SeekTime(__int64 iTime /*=0*/)
846 {
847   if (!CanSeek()) return;
848   if (m_currentFile->m_lStartOffset)
849     iTime += m_currentFile->m_lStartOffset * 1000 / 75;
850   m_SeekTime = iTime;
851   CLog::Log(LOGDEBUG, "PAPlayer::Seeking to time %f", 0.001f * m_SeekTime);
852 }
853
854 void PAPlayer::SeekPercentage(float fPercent /*=0*/)
855 {
856   if (fPercent < 0.0f) fPercent = 0.0f;
857   if (fPercent > 100.0f) fPercent = 100.0f;
858   SeekTime((__int64)(fPercent * 0.01f * (float)GetTotalTime64()));
859 }
860
861 float PAPlayer::GetPercentage()
862 {
863   float percent = (float)GetTime() * 100.0f / GetTotalTime64();
864   return percent;
865 }
866
867 void PAPlayer::HandleSeeking()
868 {
869   if (m_SeekTime != -1)
870   {
871     DWORD time = timeGetTime();
872     m_timeOffset = m_decoder[m_currentDecoder].Seek(m_SeekTime);
873     CLog::Log(LOGDEBUG, "Seek to time %f took %u ms",
874               0.001f * m_SeekTime, timeGetTime() - time);
875     FlushStreams();
876     m_bytesSentOut = 0;
877     m_SeekTime = -1;
878   }
879   g_infoManager.m_performingSeek = false;
880 }
881
882 void PAPlayer::FlushStreams()
883 {
884   for (int stream = 0; stream < 2; stream++)
885   {
886     if (m_pStream[stream] && m_packet[stream])
887     {
888       int nErr = snd_pcm_drain(m_pStream[stream]);
889       CHECK_ALSA(LOGERROR,"flush-drain",nErr);
890       nErr = snd_pcm_prepare(m_pStream[stream]);
891       CHECK_ALSA(LOGERROR,"flush-prepare",nErr);
892       nErr = snd_pcm_start(m_pStream[stream]);
893       CHECK_ALSA(LOGERROR,"flush-start",nErr);
894     }
895   }
896 }
897
898 bool PAPlayer::HandleFFwdRewd()
899 {
900   if (!m_IsFFwdRewding && m_iSpeed == 1)
901     return true;  // nothing to do
902   if (m_IsFFwdRewding && m_iSpeed == 1)
903   { // stop ffwd/rewd
904     m_IsFFwdRewding = false;
905     SetVolume(g_stSettings.m_nVolumeLevel);
906     m_bytesSentOut = 0;
907     FlushStreams();
908     return true;
909   }
910   // we're definitely fastforwarding or rewinding
911   int snippet = m_BytesPerSecond / 2;
912   if ( m_bytesSentOut >= snippet )
913   {
914     // Calculate offset to seek if we do FF/RW
915     __int64 time = GetTime();
916     if (m_IsFFwdRewding) snippet = (int)m_bytesSentOut;
917     time += (__int64)((double)snippet * (m_iSpeed - 1.0) / m_BytesPerSecond * 1000.0);
918
919     // Is our offset inside the track range?
920     if (time >= 0 && time <= m_decoder[m_currentDecoder].TotalTime())
921     { // just set next position to read
922       m_IsFFwdRewding = true;
923       time += m_currentFile->m_lStartOffset * 1000 / 75;
924       m_timeOffset = m_decoder[m_currentDecoder].Seek(time);
925       m_bytesSentOut = 0;
926       FlushStreams();
927       SetVolume(g_stSettings.m_nVolumeLevel - VOLUME_FFWD_MUTE); // override xbmc mute
928     }
929     else if (time < 0)
930     { // ...disable seeking and start the track again
931       time = m_currentFile->m_lStartOffset * 1000 / 75;
932       m_timeOffset = m_decoder[m_currentDecoder].Seek(time);
933       m_bytesSentOut = 0;
934       FlushStreams();
935       m_iSpeed = 1;
936       SetVolume(g_stSettings.m_nVolumeLevel); // override xbmc mute
937     } // is our next position greater then the end sector...
938     else //if (time > m_codec->m_TotalTime)
939     {
940       // restore volume level so the next track isn't muted
941       SetVolume(g_stSettings.m_nVolumeLevel);
942       CLog::Log(LOGDEBUG, "PAP Player: End of track reached while seeking");
943       return false;
944     }
945   }
946   return true;
947 }
948
949 void PAPlayer::SetStreamVolume(int stream, long nVolume)
950 {
951   m_amp[stream].SetVolume(nVolume);
952 }
953
954 bool PAPlayer::AddPacketsToStream(int stream, CAudioDecoder &dec)
955 {
956
957   if (!m_pStream[stream] || dec.GetStatus() == STATUS_NO_FILE)
958     return false;
959
960     bool ret = false;
961
962     int nAvail = snd_pcm_frames_to_bytes(m_pStream[stream], snd_pcm_avail_update(m_pStream[stream]));
963     if (nAvail < PACKET_SIZE) {
964         return false;
965     }
966
967     if (m_resampler[stream].GetData(m_packet[stream][0].packet))
968     {
969         // got some data from our resampler - construct audio packet
970         m_packet[stream][0].length = PACKET_SIZE;
971         m_packet[stream][0].stream = stream;
972
973   unsigned char *pcmPtr = m_packet[stream][0].packet;
974
975   // handle volume de-amp
976   m_amp[stream].DeAmplify((short *)pcmPtr, m_packet[stream][0].length / 2);
977
978     StreamCallback(&m_packet[stream][0]);
979
980   while ( pcmPtr < m_packet[stream][0].packet + m_packet[stream][0].length) {
981     int nPeriodSize = snd_pcm_frames_to_bytes(m_pStream[stream],m_periods[stream]);
982     if ( pcmPtr + nPeriodSize >  m_packet[stream][0].packet + m_packet[stream][0].length) {
983       nPeriodSize = m_packet[stream][0].packet + m_packet[stream][0].length - pcmPtr;
984     }
985
986     int framesToWrite = snd_pcm_bytes_to_frames(m_pStream[stream],nPeriodSize);
987     int writeResult = snd_pcm_writei(m_pStream[stream], pcmPtr, framesToWrite);
988     if (  writeResult == -EPIPE  ) {
989       CLog::Log(LOGDEBUG, "PAPlayer::AddPacketsToStream - buffer underun (tried to write %d frames)",
990       framesToWrite);
991       int err = snd_pcm_prepare(m_pStream[stream]);
992         CHECK_ALSA(LOGERROR,"prepare after EPIPE", err);
993     }
994     else if (writeResult != framesToWrite) {
995       CLog::Log(LOGERROR, "PAPlayer::AddPacketsToStream - failed to write %d frames. "
996       "bad write (err: %d) - %s",
997         framesToWrite, writeResult, snd_strerror(writeResult));
998       break;
999     }
1000     //else
1001         //    m_bytesSentOut += nPeriodSize;
1002
1003     pcmPtr += nPeriodSize;
1004   }
1005
1006       // something done
1007       ret = true;
1008     }
1009     else
1010     { // resampler wants more data - let's feed it
1011       int amount = m_resampler[stream].GetInputSamples();
1012       if (amount > 0 && amount <= (int)dec.GetDataSize())
1013       {
1014         // needs some data - let's feed it
1015         m_resampler[stream].PutFloatData((float *)dec.GetData(amount), amount);
1016         ret = true;
1017       }
1018     }
1019
1020   return ret;
1021 }
1022
1023 bool PAPlayer::FindFreePacket( int stream, DWORD* pdwPacket )
1024 {
1025   return true;
1026 }
1027
1028 void PAPlayer::RegisterAudioCallback(IAudioCallback *pCallback)
1029 {
1030   m_pCallback = pCallback;
1031   if (m_pCallback)
1032     m_pCallback->OnInitialize(m_Channels, m_SampleRateOutput, m_BitsPerSampleOutput);
1033 }
1034
1035 void PAPlayer::UnRegisterAudioCallback()
1036 {
1037   m_pCallback = NULL;
1038 }
1039
1040 void PAPlayer::DoAudioWork()
1041 {
1042   if (m_pCallback && m_visBufferLength)
1043   {
1044     m_pCallback->OnAudioData((BYTE*)m_visBuffer, m_visBufferLength);
1045     m_visBufferLength = 0;
1046   }
1047 }
1048
1049 void PAPlayer::StreamCallback( LPVOID pPacketContext )
1050 {
1051   AudioPacket *pkt = (AudioPacket *)pPacketContext;
1052
1053
1054   // only process from the current stream (if we're crossfading for instance)
1055   if (pkt->stream != m_currentStream)
1056     return;
1057
1058   m_bytesSentOut += pkt->length;
1059
1060   if (m_pCallback)
1061   { // copy into our visualisation buffer.
1062     // can't use a memcpy() here due to the context (will crash otherwise)
1063     memcpy((short*)m_visBuffer, pkt->packet, pkt->length);
1064     m_visBufferLength = pkt->length;
1065   }
1066 }
1067
1068 void CALLBACK StaticStreamCallback( VOID* pStreamContext, VOID* pPacketContext, DWORD dwStatus )
1069 {
1070   PAPlayer* pPlayer = (PAPlayer*)pStreamContext;
1071   pPlayer->StreamCallback(pPacketContext);
1072 }
1073
1074 bool PAPlayer::HandlesType(const CStdString &type)
1075 {
1076   ICodec* codec=CodecFactory::CreateCodec(type);
1077
1078   if (codec && codec->CanInit())
1079   {
1080     delete codec;
1081     return true;
1082   }
1083   if (codec)
1084     delete codec;
1085
1086   return false;
1087 }
1088
1089 // Skip to next track/item inside the current media (if supported).
1090 bool PAPlayer::SkipNext()
1091 {
1092   if (m_decoder[m_currentDecoder].GetCodec() && m_decoder[m_currentDecoder].GetCodec()->SkipNext())
1093   {
1094     return true;
1095   }
1096   return false;
1097 }
1098
1099 bool PAPlayer::CanRecord()
1100 {
1101   if (!m_pShoutCastRipper) return false;
1102   return m_pShoutCastRipper->CanRecord();
1103 }
1104
1105 bool PAPlayer::IsRecording()
1106 {
1107   if (!m_pShoutCastRipper) return false;
1108   return m_pShoutCastRipper->IsRecording();
1109 }
1110
1111 bool PAPlayer::Record(bool bOnOff)
1112 {
1113   if (!m_pShoutCastRipper) return false;
1114   if (bOnOff && IsRecording()) return true;
1115   if (bOnOff == false && IsRecording() == false) return true;
1116   if (bOnOff)
1117     return m_pShoutCastRipper->Record();
1118
1119   m_pShoutCastRipper->StopRecording();
1120   return true;
1121 }
1122
1123 void PAPlayer::WaitForStream()
1124 {
1125   // should we wait for our other stream as well?
1126   // currently we don't.
1127   if (!m_pStream[m_currentStream])
1128   {
1129     snd_pcm_wait(m_pStream[m_currentStream], -1);
1130   }
1131 }
1132 #endif