Results 1 to 4 of 4

Thread: ClanLib 1.0: Why the hardcoded 16384?

  1. #1

    Default ClanLib 1.0: Why the hardcoded 16384?

    I made a clanDUMB, to replace clanMIKMOD (That plays music in a really shitty manner... I wonder who decided to implement MIMOD...)

    The thing is: Sometimes the sound still stutter... The solution, usually involves adjusting the buffer size, and also calling rendering more frequently...

    I noticed then that clanDUMB always rendered 16384 samples, exactly. I searched for this number, and found that it is hard-coded inside the generic sound provider.


    So, why that buffer size is hardcoded there? Why 16384?
    Also, how can I call get_data more frequently? (DUMB supports you calling as much as you wish, even without need, then it will render 0 samples and return)
    How I set my own buffer, without hacking clan_lib itself?

  2. #2
    ClanLib Developer
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    554

    Default

    ClanLib's mixer uses a ring buffer for its playback. The buffer is split into N parts, where each part is called a fragment. The sound card plays the buffer in a loop and then notifies the mixer whenever it finished playing one of the fragments.

    The mixer attempts make sure there's always one fragment filled in front of the fragment currently being played by the sound card. The size of the fragment can be modified in ClanLib 2 by adjusting the latency value passed to the CL_SoundOutput constructor. I am not sure if this is possible for ClanLib 1 - it could very well be hard-coded to use fragments of 50 milliseconds of sample data.

    The sound will stutter if the mixer fails to fill a fragment before the sound card starts to play it. In this case the sound card will play what was already in the buffer where the fragment is located and will continue to play 'old' sound until the mixer thread finishes its fragment.

    If the sound card plays the buffer at 44.1 kHz and the fragment size is 2205 samples, then the mixer thread has 50 milliseconds to mix the next fragment. These 50 milliseconds includes that the OS awakens the mixer thread, the get_data() calls for all active soundprovider sessions and the final mixing into the ring buffer.

    The code running in the get_data() function is therefore time critical, so if you are using a 3rd party library that spends considerable time generating sound data, you need to launch a second thread in the session object that prepares data to be ready to be returned from the get_data() function.

    For example, lets say we are coding a sound provider session class that plays mp3 files. Due to the nature of the mp3 format, the library decoding mp3 can only decode a complete 'frame' in a mp3 file at a time. Lets say that such a frame consists of 64000 samples and the library spends 100 milliseconds decoding them. Since a fragment is only 2205 samples, on average it only takes 3.4 milliseconds to decode a fragment.

    Unfortunately every 29th fragment the mp3 decoding library needs to decode 64000 new samples, which then means that every 29th fragment (every 1.4 second) the sound will stutter for 50 ms. For this reason the mp3 decoding function cannot be called from the get_data() function, but must run alongside the mixing in a different thread, spreading the 100 ms over a longer time period.

  3. #3

    Default

    DUMB seemly dislike threads, it was specifically made to be called by a function like get_data or update() or something like that.

    I need to call DUMB more frequently (the issue is that the rendering time varies, usually below the fragment size, but not always), maybe making it fill two or three fragments in advance... even better would be make the fragment size smaller too... (I am kinda mad with that hardcoded buffer, it is too big, any slightly problem cause a huge playback error :/)

    So, clanlib only interfaces with DUMB calling "get_data" ? There are no other sound_session function that I can override to allow me to call DUMB more times without creating a thread?

    I noticed that the way clanlib calls DUMB, no matter how expensive is calculate the music, the processor usage never go above 2%, clearly clanlib is not allowing enough time... :/

    And thank you for replying... :P

  4. #4

    Default

    I am ressurecting this thread here...

    I am trying to figure how to fix the issue of buffer underruns, in my theory, what I should do is make it buffer AHEAD from the cursor (for some reason, it was coded that it wait until the cursor hit the buffer end to call more data... I wonder how it actually don't cause underrun all the time, the logic is escaping me)

Bookmarks

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •