forked from mia/Aegisub
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:
parent
e36ecbde49
commit
d004fc1856
1 changed files with 23 additions and 27 deletions
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue