Jump to content

  • Log In with Google      Sign In   
  • Create Account


ultramailman

Member Since 13 Oct 2012
Online Last Active Today, 08:07 PM

Topics I've Started

SDL audio callback buffer size

28 April 2014 - 12:23 AM

Hello all.

 

This is my first time going into the sound module of SDL, and I was able to get it to play a sound whenever the spaceship shoots a laser, or whenever it gets hit. However, the sound comes out too slow and isn't in sync with the rapid firing laser.

 

The problem is the audio callback function is called only around once every second, while the space ship can shoot 10 lasers per second. So I added some print statements to show what the buffer size is, and found out that the buffer size is 22056 (close to half of the frequency, 44100). So I tried changing the buffer size through SDL_AudioSpec.samples, but it is still 22056. Then I tried changing SDL_AudioSpec.freq to something lower, and that was able to change the buffer size.

 

So now I can change the buffer size, but only if also change the frequency. But that makes the sounds sound slow-motion.

 

Is there a way to customize the buffer size without changing the frequency? Or can it be a hardware limitation?

 

Here's code showing what I mean

void SoundSystem::init()
{
    SDL_AudioSpec want;
    Sound::getSpec("sound/checkspec.wav", &want); // use a dummy file to get the spec, this dummy file has same format as other sound files
    want.userdata = this;
    want.callback = SoundSystem::callback;
    //want.freq = 1024; // this changes buffer size to 1024, but makes sounds sound very slow and deep
    //want.samples = 512; // this is what I should use to change buffer size to 512, but it doesn't do anything
    good = (0 == SDL_OpenAudio(&want, &spec));
    if(good)
        SDL_PauseAudio(0);
}
 
void SoundSystem::callback(void *userdata, Uint8 *stream, int len)
{
    auto sys = (SoundSystem *) userdata;
    SDL_memset(stream, 0, len);
    std::cout << len << '\n';    //printing this shows the buffer size
    sys->lock(); // sdl spinlock
    for(Playback & p : sys->playbacks) // mix all sounds in a list
    {
        if(p.buf == nullptr)
            continue;
        unsigned left = p.pos + len > p.len ?  p.len - p.pos : len;
        SDL_MixAudioFormat(stream, p.buf + p.pos, sys->spec.format, left, SDL_MIX_MAXVOLUME);
        
        p.pos += left;
        if(p.pos == p.len)
            p = Playback{nullptr, 0, 0};
    }
    sys->unlock();
}
bool Sound::getSpec(std::string path, SDL_AudioSpec * spec)
{
    unsigned len;
    Uint8 * buf;
    bool good = SDL_LoadWAV(path.c_str(), spec, &buf, &len);
    if(good)
        SDL_FreeWAV(buf);
    return good;
}

random generators

15 March 2014 - 01:37 PM

Hello.

 

I've just switched from int rand(void) to c++11's random library, with all the engines and the distributions.

 

So It's pretty convenient to be able to have the distribution objects to generate uniform floats or integers, but I wonder if there is the need to have multiple engine objects in one program.

 

Let's say I use randomness in three different ways, in the same game, in no particular order:

1. generate a uniform x position from 0.0 to 100.0 (enemy spawn location)

2. generate a uniform fraction [0.0, 1.0) (enemy spawn chance)

3. generate a uniform integer from 1 to 3 (randomly choose a power-up)

 

Is it "more correct" to use one generator with its own seed for each purpose, or it is fine to just use the same generator with different distributions?


sdl2 window flashes

15 February 2014 - 03:01 PM

Hello.

 

Anyone notice how when a window is created using sdl2, it immediately closes and a new one is created again? It doesn't seem to have any negative effects, but it does bother me a little. What's going on here?

 

From what I can gather, sdl2 will try to use hardware rendering if possible, so my guess is the first time it makes a window for software rendering, then detects my computer can use hardware rendering, so it closes the window and creates a hardware rendering window. But that happens even if I specify the opengl flag.

 

Any ideas?

 

edit:

the tool chain I use is mingw.

the way I set them up goes like this:

SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER);
screen = SDL_CreateWindow("shooter game", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_width, window_height, 0);
renderer = SDL_CreateRenderer(screen, -1, 0);

atomic write

11 December 2013 - 05:52 PM

Hello. A while ago I was curious about threading, particularly about synchronization without mutexes.

 

From what I understand, a full memory barrier makes sure writes are done before the barrier, and writes and reads after the barrier are done after the barrier.

 

So I wrote two helpful functions, atomic read and atomic write.

static inline unsigned atomic_read(atomic_t * ptr)
{
    __sync_synchronize(); // makes sure all the writes to this variable are done first
    return ptr->val;
}
static inline void atomic_write(atomic_t * ptr, unsigned val)
{
    __sync_synchronize(); // makes sure writes to this variable happens after the code before this
    ptr->val = val;
}

 

Then I thought I'd look at how other people implement it, and what I found was that everyone else's implementation of atomic write is like this:

static inline void atomic_write(atomic_t * ptr, unsigned val)
{
    ptr->val = val;
    __sync_synchronize(); // notice how the barrier is after the write, as opposed to before
}

 

I'm guessing putting the barrier after the write makes sure the write is visible as soon as possible. But since atomic read already has a barrier before the read, isn't the barrier after the write redundant?

 

For example, using my ordering, an atomic write followed by a read would become:

barrier

write

barrier

read

 

and the reverse would be:

barrier

read

barrier

write

 

(both looks ok to me, every read and write sandwiched in barriers)

 

Using the more ordering I observed in other implementations, it would be:

write

barrier

barrier

read

 

(this looks ok, just the redundant barrier there)

 

and the reverse would be:

barrier

read

write

barrier

 

(here it looks like the read and write can be done out of order, because there isn't a barrier between read and write)

 

So my question is this: which one is a more correct placement of the full memory barrier, if it matters at all?


Is this thread safe?

22 November 2013 - 07:08 PM

Hello. I am curious if I can have two threads process a shared array in a pipelined manner, and still be thread-safe without using mutex locks:

thread1 advances in the array, doing its work.

thread2 also advances, but stops to wait if the next element is being processed by thread1.

 

To test that, I wrote a small program.

A writer thread writes into a shared array.

A reader thread reads from the shared array, then prints it in the terminal.

This repeats until 80000 elements have been printed.

 

I repeated this program many times, and one time the printing paused indefinitely. I suspect there was a deadlock, but I can't reproduce it again.

 

My question is: can the two threads in this program reach a state of deadlock? If so, can someone explain how it can happen?

 

edit: I thought this should be fine was because a while ago, I read an article, and it says it is fine as long as each piece of data is written to by only one thread. I kept that in mind and these two threads seem to do just that (at least I think they do).

#include <pthread.h>
#include <stdio.h>
//#include <windows.h>
#include <time.h>
#define DATA_SIZE (80)

// used to stop threads
volatile int running = 1;

// both threads use this share array of data
volatile unsigned data[DATA_SIZE] = {0};

// numbers to show which element a thread is using
volatile unsigned writer_seq = 1, reader_seq = 0;

// numbers to show how many times the threads waited for each other
volatile unsigned long long writer_waits = 0, reader_waits = 0;

// number of iterations the reader thread will run
volatile unsigned reads_left = DATA_SIZE * 1000;
void * writer_thread(void * args)
{
    unsigned count = 0;
    while(running){
        if(writer_seq == 0)
            count = (count + 1) % 10;
        data[writer_seq] = count;
        unsigned next = (writer_seq + 1) % DATA_SIZE;
        while(next == reader_seq)
        {
            writer_waits++;
            //Sleep(1);
        }
        writer_seq = next;
    }
    return NULL;
}
void * reader_thread(void *args)
{
    while(running){
        printf("%d", data[reader_seq]);
        unsigned next = (reader_seq + 1) % DATA_SIZE;
        while(next == writer_seq)
        {
            reader_waits ++;
            //Sleep(1);
        }
        reader_seq = next;
        
        reads_left--;
        if(reads_left == 0)
            running = 0;
    }
    return NULL;
}
int main(void)
{
    clock_t t1 = clock();
    pthread_t writer, reader;
    pthread_create(&writer, NULL, &writer_thread, NULL);
    pthread_create(&reader, NULL, &reader_thread, NULL);
    void * ret = NULL;
    pthread_join(writer, &ret);
    pthread_join(reader, &ret);
    clock_t t2 = clock();
    printf("reader waits:%llu, writer waits:%llu, time:%f\n", reader_waits, writer_waits, (float)(t2 - t1) / CLOCKS_PER_SEC);
}

PARTNERS