ogg seeking in the interface

This commit is contained in:
Adam D. Ruppe 2022-11-20 18:01:04 -05:00
parent 7d475b1480
commit 1f6ead0a17
2 changed files with 68 additions and 5 deletions

View File

@ -227,6 +227,17 @@ interface SampleController {
+/
bool paused();
/++
Seeks to a point in the sample, if possible. If impossible, this function does nothing.
Params:
where = point to seek to, in seconds
History:
Added November 20, 2022 (dub v10.10)
+/
void seek(float where);
/++
Sets a delegate that will be called on the audio thread when the sample is finished
playing; immediately after [finished] becomes `true`.
@ -247,6 +258,8 @@ private class DummySample : SampleController {
float position() { return float.init; }
bool finished() { return true; }
bool paused() { return true; }
void seek(float where) {}
}
private class SampleControlFlags : SampleController {
@ -262,7 +275,10 @@ private class SampleControlFlags : SampleController {
bool finished() { return finished_; }
bool paused() { return paused_; }
void seek(float where) { synchronized(this) {requestedSeek = where;} }
float currentPosition = 0.0;
float requestedSeek = float.init;
}
/++
@ -909,6 +925,15 @@ final class AudioPcmOutThreadImplementation : Thread {
if(cast(int) buffer.length != buffer.length)
throw new Exception("eeeek");
synchronized(scf)
if(scf.requestedSeek !is float.init) {
if(v.seek(cast(uint) (scf.requestedSeek * v.sampleRate))) {
scf.currentPosition = scf.requestedSeek;
}
scf.requestedSeek = float.init;
}
plain:
auto got = v.getSamplesShortInterleaved(2, buffer.ptr, cast(int) buffer.length);
if(got == 0) {
@ -940,6 +965,15 @@ final class AudioPcmOutThreadImplementation : Thread {
tmp[0] = buffersIn[0].ptr;
tmp[1] = buffersIn[1].ptr;
synchronized(scf)
if(scf.requestedSeek !is float.init) {
if(v.seekFrame(cast(uint) (scf.requestedSeek * v.sampleRate))) {
scf.currentPosition = scf.requestedSeek;
}
scf.requestedSeek = float.init;
}
loop:
auto actuallyGot = v.getSamplesFloat(v.chans, tmp.ptr, cast(int) buffersIn[0].length);
if(actuallyGot == 0 && loop) {
@ -970,6 +1004,9 @@ final class AudioPcmOutThreadImplementation : Thread {
Please note that the static type may change in the future. It will always be a subtype of [SampleController], but it may be more specialized as I add more features and this will not necessarily match its sister functions, [playOgg] and [playWav], though all three will share an ancestor in [SampleController]. Therefore, if you use `auto`, there's no guarantee the static type won't change in future versions and I will NOT consider that a breaking change since the base interface will remain compatible.
Bugs:
Mp3s cannot be seeked or looped in the current implementation.
History:
Automatic resampling support added Nov 7, 2020.
@ -1030,6 +1067,15 @@ final class AudioPcmOutThreadImplementation : Thread {
if(cast(int) buffer.length != buffer.length)
throw new Exception("eeeek");
synchronized(scf)
if(scf.requestedSeek !is float.init) {
if(mp3.seek(cast(uint) (scf.requestedSeek * v.sampleRate))) {
scf.currentPosition = scf.requestedSeek;
}
scf.requestedSeek = float.init;
}
more:
if(next.length >= buffer.length) {
buffer[] = next[0 .. buffer.length];
@ -1057,8 +1103,9 @@ final class AudioPcmOutThreadImplementation : Thread {
}
}
if(scf.stopped)
if(scf.stopped) {
scf.finished_ = true;
}
return !scf.stopped;
}
);

View File

@ -588,6 +588,7 @@ struct ProbedPage {
private int error (VorbisDecoder f, STBVorbisError e) {
f.error = e;
if (!f.eof && e != STBVorbisError.need_more_data) {
// import std.stdio; debug writeln(e);
f.error = e; // breakpoint for debugging
}
return 0;
@ -1030,7 +1031,7 @@ private bool getn (VorbisDecoder f, void* data, int n) {
}
private void skip (VorbisDecoder f, int n) {
if (f.eof || n <= 0) return;
if (f.eof || n == 0) return;
f.rawSkip(n);
}
@ -1125,6 +1126,7 @@ private int maybe_start_packet (VorbisDecoder f) {
if (f.next_seg == -1) {
auto x = get8(f);
if (f.eof) return false; // EOF at page boundary is not an error!
// import std.stdio; debug writefln("CAPTURE %x %x", x, f.stpos);
if (0x4f != x ) return error(f, STBVorbisError.missing_capture_pattern);
if (0x67 != get8(f)) return error(f, STBVorbisError.missing_capture_pattern);
if (0x67 != get8(f)) return error(f, STBVorbisError.missing_capture_pattern);
@ -4097,7 +4099,15 @@ private:
}
return 0;
}
void rawSkip (int n) { static if (__VERSION__ > 2067) pragma(inline, true); if (isOpened && n > 0) { if ((stpos += n) > stend) stpos = stend; } }
void rawSkip (int n) { static if (__VERSION__ > 2067) pragma(inline, true);
if (isOpened) {
stpos += n;
if(stpos < stst)
stpos = stst;
else if(stpos > stend)
stpos = stend;
}
}
void rawSeek (int n) { static if (__VERSION__ > 2067) pragma(inline, true); if (isOpened) { stpos = stst+(n < 0 ? 0 : n); if (stpos > stend) stpos = stend; } }
void rawClose () { static if (__VERSION__ > 2067) pragma(inline, true); if (isOpened) { isOpened = false; stmread(null, 0, this); } }
@ -4121,14 +4131,17 @@ private:
static int stflRead (void[] buf, uint ofs, VorbisDecoder vb) {
if (buf !is null) {
//{ import core.stdc.stdio; printf("stflRead: ofs=%u; len=%u\n", ofs, cast(uint)buf.length); }
if (vb.stlastofs != ofs) {
// { import core.stdc.stdio; printf("stflRead: ofs=%u; len=%u\n", ofs, cast(uint)buf.length); }
import core.stdc.stdio : fseek, SEEK_SET;
vb.stlastofs = ofs;
fseek(vb.stfl, ofs, SEEK_SET);
}
import core.stdc.stdio : fread;
return cast(int)fread(buf.ptr, 1, buf.length, vb.stfl);
auto rd = cast(int)fread(buf.ptr, 1, buf.length, vb.stfl);
if(rd > 0)
vb.stlastofs += rd;
return rd;
} else {
if (vb.stclose) {
import core.stdc.stdio : fclose;
@ -4492,6 +4505,8 @@ public:
assert(this.current_loc_valid);
assert(this.current_loc <= sample_number);
import std.stdio;
// linear search for the relevant packet
max_frame_samples = (this.blocksize_1*3-this.blocksize_0)>>2;
while (this.current_loc < sample_number) {
@ -4514,6 +4529,7 @@ public:
}
// the next frame will start with the sample
assert(this.current_loc == sample_number);
return 1;
}