|
@@ -264,11 +264,10 @@ void AudioStreamPlaybackOggVorbis::seek(double p_time) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- vorbis_synthesis_restart(&dsp_state);
|
|
|
-
|
|
|
if (p_time >= vorbis_stream->get_length()) {
|
|
|
p_time = 0;
|
|
|
}
|
|
|
+
|
|
|
frames_mixed = uint32_t(vorbis_data->get_sampling_rate() * p_time);
|
|
|
|
|
|
const int64_t desired_sample = p_time * get_stream_sampling_rate();
|
|
@@ -278,107 +277,81 @@ void AudioStreamPlaybackOggVorbis::seek(double p_time) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- ogg_packet *packet;
|
|
|
- if (!vorbis_data_playback->next_ogg_packet(&packet)) {
|
|
|
- WARN_PRINT_ONCE("seeking beyond limits");
|
|
|
- return;
|
|
|
+ // We want to start decoding before the page that we expect the sample to be in (the sample may
|
|
|
+ // be part of a partial packet across page boundaries). Otherwise, the decoder may not have
|
|
|
+ // synchronized before reaching the sample.
|
|
|
+ int64_t start_page_number = vorbis_data_playback->get_page_number() - 1;
|
|
|
+ if (start_page_number < 0) {
|
|
|
+ start_page_number = 0;
|
|
|
}
|
|
|
|
|
|
- // The granule position of the page we're seeking through.
|
|
|
- int64_t granule_pos = 0;
|
|
|
-
|
|
|
- int headers_remaining = 0;
|
|
|
- int samples_in_page = 0;
|
|
|
- int err;
|
|
|
while (true) {
|
|
|
- if (vorbis_synthesis_idheader(packet)) {
|
|
|
- headers_remaining = 3;
|
|
|
- }
|
|
|
- if (!headers_remaining) {
|
|
|
- err = vorbis_synthesis(&block, packet);
|
|
|
- ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err));
|
|
|
-
|
|
|
- err = vorbis_synthesis_blockin(&dsp_state, &block);
|
|
|
- ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err));
|
|
|
-
|
|
|
- int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
|
|
|
- err = vorbis_synthesis_read(&dsp_state, samples_out);
|
|
|
- ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err));
|
|
|
-
|
|
|
- samples_in_page += samples_out;
|
|
|
-
|
|
|
- } else {
|
|
|
- headers_remaining--;
|
|
|
- }
|
|
|
- if (packet->granulepos != -1 && headers_remaining == 0) {
|
|
|
- // This indicates the end of the page.
|
|
|
- granule_pos = packet->granulepos;
|
|
|
- break;
|
|
|
- }
|
|
|
- if (packet->e_o_s) {
|
|
|
- break;
|
|
|
- }
|
|
|
- if (!vorbis_data_playback->next_ogg_packet(&packet)) {
|
|
|
- // We should get an e_o_s flag before this happens.
|
|
|
- WARN_PRINT("Vorbis file ended without warning.");
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ ogg_packet *packet;
|
|
|
+ int err;
|
|
|
|
|
|
- int64_t samples_to_burn = samples_in_page - (granule_pos - desired_sample);
|
|
|
+ // We start at an unknown granule position.
|
|
|
+ int64_t granule_pos = -1;
|
|
|
|
|
|
- if (samples_to_burn > samples_in_page) {
|
|
|
- WARN_PRINT_ONCE("Burning more samples than we have in this page. Check seek algorithm.");
|
|
|
- } else if (samples_to_burn < 0) {
|
|
|
- WARN_PRINT_ONCE("Burning negative samples doesn't make sense. Check seek algorithm.");
|
|
|
- }
|
|
|
+ // Decode data until we get to the desired sample or notice that we have read past it.
|
|
|
+ vorbis_data_playback->set_page_number(start_page_number);
|
|
|
+ vorbis_synthesis_restart(&dsp_state);
|
|
|
|
|
|
- // Seek again, this time we'll burn a specific number of samples instead of all of them.
|
|
|
- if (!vorbis_data_playback->seek_page(desired_sample)) {
|
|
|
- WARN_PRINT("seek failed");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!vorbis_data_playback->next_ogg_packet(&packet)) {
|
|
|
- WARN_PRINT_ONCE("seeking beyond limits");
|
|
|
- return;
|
|
|
- }
|
|
|
- vorbis_synthesis_restart(&dsp_state);
|
|
|
+ while (true) {
|
|
|
+ if (!vorbis_data_playback->next_ogg_packet(&packet)) {
|
|
|
+ WARN_PRINT_ONCE("Seeking beyond limits");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- while (true) {
|
|
|
- if (vorbis_synthesis_idheader(packet)) {
|
|
|
- headers_remaining = 3;
|
|
|
- }
|
|
|
- if (!headers_remaining) {
|
|
|
err = vorbis_synthesis(&block, packet);
|
|
|
- ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err));
|
|
|
-
|
|
|
- err = vorbis_synthesis_blockin(&dsp_state, &block);
|
|
|
- ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err));
|
|
|
-
|
|
|
- int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
|
|
|
- int read_samples = samples_to_burn > samples_out ? samples_out : samples_to_burn;
|
|
|
- err = vorbis_synthesis_read(&dsp_state, samples_out);
|
|
|
- ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err));
|
|
|
- samples_to_burn -= read_samples;
|
|
|
-
|
|
|
- if (samples_to_burn <= 0) {
|
|
|
- break;
|
|
|
+ if (err != OV_ENOTAUDIO) {
|
|
|
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err) + ".");
|
|
|
+
|
|
|
+ err = vorbis_synthesis_blockin(&dsp_state, &block);
|
|
|
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err) + ".");
|
|
|
+
|
|
|
+ int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
|
|
|
+
|
|
|
+ if (granule_pos < 0) {
|
|
|
+ // We don't know where we are yet, so just keep on decoding.
|
|
|
+ err = vorbis_synthesis_read(&dsp_state, samples_out);
|
|
|
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err) + ".");
|
|
|
+ } else if (granule_pos + samples_out >= desired_sample) {
|
|
|
+ // Our sample is in this block. Skip the beginning of the block up to the sample, then
|
|
|
+ // return.
|
|
|
+ int skip_samples = (int)(desired_sample - granule_pos);
|
|
|
+ err = vorbis_synthesis_read(&dsp_state, skip_samples);
|
|
|
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err) + ".");
|
|
|
+ have_samples_left = skip_samples < samples_out;
|
|
|
+ have_packets_left = !packet->e_o_s;
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ // Our sample is not in this block. Skip it.
|
|
|
+ err = vorbis_synthesis_read(&dsp_state, samples_out);
|
|
|
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err) + ".");
|
|
|
+ granule_pos += samples_out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (packet->granulepos != -1) {
|
|
|
+ // We found an update to our granule position.
|
|
|
+ granule_pos = packet->granulepos;
|
|
|
+ if (granule_pos > desired_sample) {
|
|
|
+ // We've read past our sample. We need to start on an earlier page.
|
|
|
+ if (start_page_number == 0) {
|
|
|
+ // We didn't find the sample even reading from the beginning.
|
|
|
+ have_samples_left = false;
|
|
|
+ have_packets_left = !packet->e_o_s;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ start_page_number--;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (packet->e_o_s) {
|
|
|
+ // We've reached the end of the stream and didn't find our sample.
|
|
|
+ have_samples_left = false;
|
|
|
+ have_packets_left = false;
|
|
|
+ return;
|
|
|
}
|
|
|
- } else {
|
|
|
- headers_remaining--;
|
|
|
- }
|
|
|
- if (packet->granulepos != -1 && headers_remaining == 0) {
|
|
|
- // This indicates the end of the page.
|
|
|
- break;
|
|
|
- }
|
|
|
- if (packet->e_o_s) {
|
|
|
- break;
|
|
|
- }
|
|
|
- if (!vorbis_data_playback->next_ogg_packet(&packet)) {
|
|
|
- // We should get an e_o_s flag before this happens.
|
|
|
- WARN_PRINT("Vorbis file ended without warning.");
|
|
|
- break;
|
|
|
}
|
|
|
}
|
|
|
}
|