Improve ALSA playback position reporting

Use std::chrono since it's a nicer API. Use a separate lock for playback
position so that the GUI thread isn't blocked for hundreds of ms while
snd_pcm_drain is waiting, and update the playback position after
decoding audio rather than before to avoid it being significantly wrong
when not using a cache.
This commit is contained in:
Thomas Goyne 2014-05-23 11:37:51 -07:00
parent e36ecbde49
commit d004fc1856
1 changed files with 23 additions and 27 deletions

View File

@ -65,6 +65,8 @@ enum class Message {
Close Close
}; };
using clock = std::chrono::steady_clock;
class AlsaPlayer final : public AudioPlayer { class AlsaPlayer final : public AudioPlayer {
std::mutex mutex; std::mutex mutex;
std::condition_variable cond; std::condition_variable cond;
@ -78,8 +80,9 @@ class AlsaPlayer final : public AudioPlayer {
int64_t start_position = 0; int64_t start_position = 0;
std::atomic<int64_t> end_position{0}; std::atomic<int64_t> end_position{0};
std::mutex position_mutex;
int64_t last_position = 0; int64_t last_position = 0;
timespec last_position_time = {0, 0}; clock::time_point last_position_time;
std::vector<char> decode_buffer; std::vector<char> decode_buffer;
@ -87,6 +90,17 @@ class AlsaPlayer final : public AudioPlayer {
void PlaybackThread(); void PlaybackThread();
void UpdatePlaybackPosition(snd_pcm_t *pcm, int64_t position)
{
snd_pcm_sframes_t delay;
if (snd_pcm_delay(pcm, &delay) == 0)
{
std::unique_lock<std::mutex> playback_lock;
last_position = position - delay;
last_position_time = clock::now();
}
}
public: public:
AlsaPlayer(AudioProvider *provider); AlsaPlayer(AudioProvider *provider);
~AlsaPlayer(); ~AlsaPlayer();
@ -157,15 +171,12 @@ do_setup:
LOG_D("audio/player/alsa") << "starting playback"; LOG_D("audio/player/alsa") << "starting playback";
int64_t position = start_position; int64_t position = start_position;
// Playback position
last_position = position;
clock_gettime(CLOCK_REALTIME, &last_position_time);
// Initial buffer-fill // Initial buffer-fill
{ {
auto avail = std::min(snd_pcm_avail(pcm), (snd_pcm_sframes_t)(end_position-position)); auto avail = std::min(snd_pcm_avail(pcm), (snd_pcm_sframes_t)(end_position-position));
decode_buffer.resize(avail * framesize); decode_buffer.resize(avail * framesize);
provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume); provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume);
snd_pcm_sframes_t written = 0; snd_pcm_sframes_t written = 0;
while (written <= 0) while (written <= 0)
{ {
@ -185,6 +196,7 @@ do_setup:
LOG_D("audio/player/alsa") << "initial buffer filled, hitting start"; LOG_D("audio/player/alsa") << "initial buffer filled, hitting start";
snd_pcm_start(pcm); snd_pcm_start(pcm);
UpdatePlaybackPosition(pcm, position);
playing = true; playing = true;
BOOST_SCOPE_EXIT_ALL(&) { playing = false; }; BOOST_SCOPE_EXIT_ALL(&) { playing = false; };
while (true) while (true)
@ -206,14 +218,6 @@ do_setup:
break; break;
} }
// Playback position
snd_pcm_sframes_t delay;
if (snd_pcm_delay(pcm, &delay) == 0)
{
last_position = position - delay;
clock_gettime(CLOCK_REALTIME, &last_position_time);
}
// Fill buffer // Fill buffer
snd_pcm_sframes_t tmp_pcm_avail = snd_pcm_avail(pcm); snd_pcm_sframes_t tmp_pcm_avail = snd_pcm_avail(pcm);
if (tmp_pcm_avail == -EPIPE) if (tmp_pcm_avail == -EPIPE)
@ -249,6 +253,8 @@ do_setup:
position += written; position += written;
} }
UpdatePlaybackPosition(pcm, position);
// Check for end of playback // Check for end of playback
if (position >= end_position) if (position >= end_position)
{ {
@ -328,26 +334,16 @@ void AlsaPlayer::SetEndPosition(int64_t pos)
int64_t AlsaPlayer::GetCurrentPosition() int64_t AlsaPlayer::GetCurrentPosition()
{ {
int64_t lastpos; int64_t lastpos;
timespec lasttime; clock::time_point lasttime;
int64_t samplerate;
{ {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> playback_lock;
lastpos = last_position; lastpos = last_position;
lasttime = last_position_time; lasttime = last_position_time;
samplerate = provider->GetSampleRate();
} }
timespec now; auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - lasttime).count();
clock_gettime(CLOCK_REALTIME, &now); return lastpos + ms * provider->GetSampleRate() / 1000;
const double NANO = 1000000000; // nano- is 10^-9
double now_sec = now.tv_sec + now.tv_nsec/NANO;
double last_sec = lasttime.tv_sec + lasttime.tv_nsec/NANO;
double diff_sec = now_sec - last_sec;
return lastpos + (int64_t)(diff_sec * samplerate);
} }
} }