Looping mp3/ogg music

[Sorry if this appears on the list twice; I originally sent it on
Saturday but it didn’t seem to make it to the list.]

Hi,

I’m working on a SDL-based solution for looping mp3 or Ogg-Vorbis files.
What I want to do is take an mp3 or ogg file and loop it like
this:

(intro) [(body) (body) …]

Ideally, the intro and body would be in the same mp3/ogg file.

I have an solution for ogg files[1] that seems to work, but I’d like to
hear if others have better ideas. :slight_smile:

I started with the SDL_mixer code, and hacked it to do what I need.
In music_mixer (music.c), I check to see how many bytes OGG_playAudio
copied to the buffer. If it’s less than the buffer size (and looping is
on), I seek back to the start of the “body” section of the ogg file and
call OGG_playAudio again to fill the rest of the buffer. Simple enough.

The problem with this is that the seek is slow, and with
reasonably-sized buffers (<=4096) I get audio buffer underruns (pops).
I have a Celeron running at 525 MHz, with 192 MB of RAM, so I don’t
really consider my machine to be underpowered.

The hack I’ve found to work around this problem is to insert
"SDL_LockAudio(); SDL_UnlockAudio();" in a couple places in my main
event-processing loop. This blocks the main game thread if the audio
thread is in the mixer routine, and so helps to give the audio thread
more CPU time. With this hack I’ve been able to eliminate popping, even
with a relatively small buffer (buffer size 512, 44.1 kHz, 16 bit
stereo).

(This solution can obviously be improved by only blocking the main game
thread when a seek is required – this can be easily done with the
introduction of a new mutex.)

Does anybody have any suggestions on how to improve this? (If people
are interested I can post a patch against SDL_mixer.)

Sam, is this a feature that you think could go into SDL_mixer?

Thanks,
Jasmin

[1] I chose Ogg-Vorbis because it provides routines to seek to any
specified time in an ogg file, while smpeg only allows seeking to a byte
offset in the file. (BTW, does anyone know if that byte offset is a raw
(compressed) offset or an uncompressed offset (which I imagine could be
calculated from a time offset)?)–
Jasmin Patry Lead Programmer, Tux Racer
jfpatry at sunspirestudios.com http://www.tuxracer.com

Does anybody have any suggestions on how to improve this? (If people
are interested I can post a patch against SDL_mixer.)

Sam, is this a feature that you think could go into SDL_mixer?

Probably not… What’s needed at that level is sort of cooperative
multi-tasking. I usually insert a small delay in the main game processing
to allow the kernel to schedule things like audio, if they are necessary.
I am looking forward to common implementations of real-time processing. :slight_smile:

[1] I chose Ogg-Vorbis because it provides routines to seek to any
specified time in an ogg file, while smpeg only allows seeking to a byte
offset in the file. (BTW, does anyone know if that byte offset is a raw
(compressed) offset or an uncompressed offset (which I imagine could be
calculated from a time offset)?)

It’s a compressed offset, so isn’t really useful for more than percentage
seeking within a file.

See ya!
-Sam Lantinga, Lead Programmer, Loki Entertainment Software

Probably not… What’s needed at that level is sort of cooperative
multi-tasking.

… which is what we people who don’t believe in threads are using :slight_smile:

I usually insert a small delay in the main game processing
to allow the kernel to schedule things like audio, if they are necessary.

Since it’s hard to do <10ms sleeps in Linux on x86, can you afford to
wait that long? (I’m not sure how to even get less than 20ms sleeps,
but I presume select() can do it.)

I am looking forward to common implementations of real-time processing. :slight_smile:

If you have problem with the scheduling, try a FIFO or RR scheduling
policy. Unfortunately they require root privs (which is unwise to give
a program), but it can be handled by an external suided program or
daemon.

I think the standard 100Hz clock is silly nowadays; people who have
compiled their kernel with HZ=1000 report very little slowdowns due
to increased timer processing, but a noticeable better interactive
response. And Alpha has had it all along.

Wed, 08 Nov 2000 Mattias Engdeg?rd wrote:

Probably not… What’s needed at that level is sort of cooperative
multi-tasking.

… which is what we people who don’t believe in threads are using :slight_smile:

I usually insert a small delay in the main game processing
to allow the kernel to schedule things like audio, if they are necessary.

Since it’s hard to do <10ms sleeps in Linux on x86, can you afford to
wait that long? (I’m not sure how to even get less than 20ms sleeps,
but I presume select() can do it.)

You can have a thread block for as short time as you like/need, as long as you
have some kernel driver, or other event to wake you up before the next jiffy
calls the scheduler. That is, without a high resolution timer, forget about
sleeping for less than 20 ms. (I don’t remember why, but IIRC, it’s not
possible to wake up in the next jiffy, not even with SCHED_FIFO. This might
be ralated to some special cases, though; not sure.)

Anyway, where to find a hires timer? Well, in some kinds of applications, the
audio card comes in very handy, and this works without kernel modifications.
However, it might not be a good idea to force audio buffering in the ms range
just to get a wake up source, as this will result in audio drop-outs, unless
you’re running on a lowlatency patched kernel… (Then again, this is a
non-issue if you’re using mmap() I/O for the audio, as that decouples the
buffering from the timing.)

Oh, well, short version: It’s possible, but a bit messy, especially on kernels
without high resolution timers.

There is a patch for the “normal” timers, but I actually don’t know if it’s in
2.4.0… checking No, I can’t find it. (It’s quite a messy patch, as I
understand it - it has to mess with the 100 Hz jiffy timer that the kernel
uses for scheduling, wall clock time etc, without breaking those parts of the
system.)

However, the RTC (Real Time Clock) is another usable time base. It’s a timer
that can be set as a 24 hour alarm clock, or to generate interrupts at
power-of-two rates from 2 Hz to 8192 Hz, and you can sleep on /dev/rtc and be
woken up every time an interrupt occurs. It’s available on all PCs, and Alphas.
(Similar chips are probably available on some other architectures as well.)

On Windoze, mmsystem timers with timeBeginPeriod()/timeEndPeriod() should be
usable for this. Music software have used them as a 1 kHz time base for MIDI
timing, calling their sequencer engines at that rate. (Nowadays, there is
finally a usable MIDI API with timestamped event queues, so that this idiotic
approach isn’t required. Idiotic, because the jitter was higher than the
resolution anyway, due to the mediocre RT performance of Windows, and waking
up the application at 1 kHz didn’t improve things…)

I am looking forward to common implementations of real-time processing. :slight_smile:

If you have problem with the scheduling, try a FIFO or RR scheduling
policy. Unfortunately they require root privs (which is unwise to give
a program), but it can be handled by an external suided program or
daemon.

I was just going to write one, but other things got in the way. The reason for
that is that 1) I need it at work, 2) I need it for my audio projects (both of
these requiring SCHED_FIFO on lowlatency kernels) and 3) others in the Linux
audio community want it for their audio hacking on LL kernels.

It’ll be a daemon which can promote non-root applications to SCHED_FIFO at any
priority lower than the max SCHED_FIFO prio (via a library interface). It will
use a thread runnig at the highest prio, and some system load measurement method
to implement watchdog functionality, in order to eliminate another problem with
SCHED_FIFO: System freezes due to bugs or overload in RT threads.

I think the standard 100Hz clock is silly nowadays; people who have
compiled their kernel with HZ=1000 report very little slowdowns due
to increased timer processing, but a noticeable better interactive
response. And Alpha has had it all along.

Well, Alpha used to be a bit more efficient than x86 on interrupt handling,
but these days it’s probably the memory technologies (cache misses) that impact
task switching the most.

OTOH, it’s not good design to “stress” the time sharing scheduling just because
some applications want to do periodic operations at extreme frequencies. If
these can be driven from other timeing sources (any hardware that delivers
interrupts will do), that’s the nice way to do it. As an extra bonus, you get
more accurate timing than you can ever get with a sane HZ value.

//David

…- M u C o S -------------------------. .- David Olofson --------.
| A Free/Open Source | | Audio Hacker |
| Plugin and Integration Standard | | Linux Advocate |
| for | | Open Source Advocate |
| Professional and Consumer | | Singer |
| Multimedia | | Songwriter |
-----> http://www.linuxdj.com/mucos -'—> david at linuxdj.com -’

That is, without a high resolution timer, forget about
sleeping for less than 20 ms.

10 ms is perfectly attainable with select(). 20 ms is what you get when you
call usleep() near the beginning of a tick, since the specifications call
for a delay of at least the requested amount, and this means sleeping for
two jiffies since you might be near the end of one. It is fixable, and
patches for it has been written, but it didn’t matter enough for it to be
integrated as far as I know.

That’s why SDL_Delay() uses select() on Linux.

There is a patch for the “normal” timers, but I actually don’t know if it’s in
2.4.0… checking No, I can’t find it. (It’s quite a messy patch, as I
understand it - it has to mess with the 100 Hz jiffy timer that the kernel
uses for scheduling, wall clock time etc, without breaking those parts of the
system.)

You can use HZ=1000 quite reliably nowadays, since most of the drivers have
been fixed in that regards. You have to rebuild some userland stuff though,
so it is more common to use RTC instead. The drawback is that it requires
root privs and isn’t very portable.

Well, Alpha used to be a bit more efficient than x86 on interrupt handling,
but these days it’s probably the memory technologies (cache misses) that impact
task switching the most.

The 100Hz timer was just seen as needless historical baggage. Personally I
think the cycle counters present in just about all modern cpus should be
used instead, and the fixed-length ticks dropped altogether. The
second-best solution is to increase HZ, or at least the internal kernel
tick frequency.

Besides cache misses (and worse, TLB flushes) are only an issue when
switching tasks. Time slices and scheduling policy are orthogonal to
the tick rate.

Accurate timing has its place in games but I’m not sure that sleeping a
short while each frame just in case some background thread needs the cpu is
the right way to do it

The 100Hz timer was just seen as needless historical baggage. Personally I
think the cycle counters present in just about all modern cpus should be
used instead, and the fixed-length ticks dropped altogether. The

Do modern O/S guarantee that a given process will always execute on
the same CPU? Ie, your RDTSC call may not even execute on the same CPU
every time. There’s also the issue of the accuracy and periodicy of
things like RDTSC–I believe it was added for NT and it only really
guaranteed to be monotonic.

Accurate timing has its place in games but I’m not sure that sleeping a
short while each frame just in case some background thread needs the cpu is
the right way to do it

Yes. There are appropriate reasons to sleep, or select(), in things
like dedicated servers, but explicitly yielding to background threads
sounds like a poor design decision had been made.

m.On Wed, Nov 08, 2000 at 07:59:20PM +0100, Mattias Engdeg?rd wrote:


Programmer “Ha ha.” “Ha ha.” "What are you laughing at?"
Loki Software "Just the horror of being alive."
http://lokigames.com/~briareos/ - Tony Millionaire

Do modern O/S guarantee that a given process will always execute on
the same CPU?

Linux will schedule runnable processes to be run on a free CPU, even
if it’s not the one where it executed last time. CPU-switching
processes are less likely to be selected though:

    /* Give a largish advantage to the same processor...   */
    /* (this is equivalent to penalizing other processors) */
    if (p->processor == this_cpu)
            weight += PROC_CHANGE_PENALTY;

(from kernel/sched.c)

Many other OSes can “lock” a process to a specific CPU if requested,
but that will of course prevent them from running even if other CPUs
are idle.

Ie, your RDTSC call may not even execute on the same CPU
every time. There’s also the issue of the accuracy and periodicy of
things like RDTSC–I believe it was added for NT and it only really
guaranteed to be monotonic.

I believe that most SMP:ish architectures synchronize their cycle
counters, but others may not necessarily do so. It doesn’t matter for
the purposes of fine-grained timed sleeps anyway, since the cycle
counter interrupt would be set to the next event on that CPU, and if a
process is rescheduled to run elsewhere, it’s cycle counter would be
set accordingly. No problem :slight_smile:

10 ms is perfectly attainable with select(). 20 ms is what you get when you
call usleep() near the beginning of a tick, since the specifications call
for a delay of at least the requested amount, and this means sleeping for
two jiffies since you might be near the end of one. It is fixable, and
patches for it has been written, but it didn’t matter enough for it to be
integrated as far as I know.

That’s why SDL_Delay() uses select() on Linux.

Actually, I looked at the glibc 2.1 source code, and usleep() is implemented
using select(). Heheh.

Accurate timing has its place in games but I’m not sure that sleeping a
short while each frame just in case some background thread needs the cpu is
the right way to do it

Absolutely, but it’s an acceptable workaround until a better solution
is widespread.

See ya,
-Sam Lantinga, Lead Programmer, Loki Entertainment Software

Yes. There are appropriate reasons to sleep, or select(), in things
like dedicated servers, but explicitly yielding to background threads
sounds like a poor design decision had been made.

Not necessarily. Any application that has a single CPU-hungry thread
and a separate thread that must perform processing in a narrow time
interval is subject to this problem with the normal kernel schedulers
for both Linux and Win32.

See ya!
-Sam Lantinga, Lead Programmer, Loki Entertainment Software

Actually, I looked at the glibc 2.1 source code, and usleep() is implemented
using select(). Heheh.

I did some experiments before writing that. usleep(10000)
consistently slept for 20 ms. That was with glibc 2.1.3, kernel 2.2.14
(SMP). (Solaris 8 showed the same behaviour, interestingly.)

Of course, if you don’t take the nap in the beginning of a tick (say,
do cpu-intensive work for 9 ms first), you will get different figures.

And even 10 ms is still way too large - almost the vertical refresh
frequency nowadays

Accurate timing has its place in games but I’m not sure that sleeping a
short while each frame just in case some background thread needs the cpu is
the right way to do it

Absolutely, but it’s an acceptable workaround until a better solution
is widespread.

Machintosh use YieldtoThread() or similar name. I know that there is something
similar for Windows, and suppose that it also should exist on Linux.
Anyway I use SDL_Delay in all system expect Mac.

Regards,
Miguel

http://www.arianne.cx