Question

I wrote a small sound playing library with PortAudio on Linux. It's for a small game, so there are lots of little sounds when various things happen. I open up a stream for each wav file to play by calling Pa_OpenStream(). On linux this call takes on average around 10ms. However on Windows this typically takes 40 to 70ms. And worse, the first call takes 1.3 seconds. Then after that occasionally it will again take 1.3 seconds. I haven't been able to find anything consistent about why it hangs, except that it happens every first call. The windows build actually runs fine on Wine.

I assume this has to do with differences in the underlying sound API in use in different systems. But oddly enough I haven't found any information anywhere, despite extensive searching.

Here's my play function:

int play(const char * sN)
{
    float threshold = .01f;

    char * soundName = (char*)sN;
    float g = glfwGetTime();
    updatePlayer();
    float g2 = glfwGetTime();
    if (g2-g > threshold) printf("updatePlayer: %f/", g2 - g);
    if (!paused && (int)streams.size() < maxStreams && !mute)
    {
        streamStr * ss = new streamStr;

        g = glfwGetTime();
        if (g-g2 > threshold) printf("new stream: %f/", g - g2);
        PaError err;

        sfData * sdata = getData(soundName);
        ss->sfd = sdata;

        g2 = glfwGetTime();
        if (g2-g > threshold)printf("getData: %f/", g2 - g);
        err = Pa_OpenStream(&(ss->stream), 0, &sdata->outputParameters, sdata->sfInfo.samplerate, paFramesPerBufferUnspecified, paNoFlag, PaCallback, ss);
        if (err)
        {
            printf("PortAudio error opening output: %s\n", Pa_GetErrorText(err));
            delete ss;
            return 1;
        }

    g = glfwGetTime();
    if (g-g2 > threshold)
    printf("Pa_OpenStream: %f/", g - g2);

        Pa_StartStream(ss->stream);

    g2 = glfwGetTime();
    if (g2-g > threshold)printf("Pa_StartStream: %f/", g2 - g);
        addStreams(ss);

    g = glfwGetTime();
    if (g-g2 > threshold)printf("addStreams: %f", g - g2);
        //Pa_SetStreamFinishedCallback(ss, finishedCallback);
        printf("\n");
   }
    return 0;
}
Was it helpful?

Solution

IDK why it's taking that long (because I don't know windows), but I can say you are going about this the wrong way. Specifically, you shouldn't make any timing expectations about opening a new stream. For example, I would expect similar issues (albeit to a much lesser degree) on OS X.

The correct implementation would be to always have a stream open, playing silence. Then, when you need to play a sound, you can play it right away. For best latency, you should pre-load the first few buffers from the file so you don't need to access the disk when playback starts. I don't know what the exact overhead is on windows for opening a stream (I'm sure it depends on the API), but on some versions of OS X, it's huge (the entire kernel switches into preemptive mode if no audio was running before).

That said, 1.3 seconds is insane. I recommend asking on the mailing list. Be sure to say what host-API you are using because you didn't say that here, and, for Windows, it matters. Also, what version of windows.

OTHER TIPS

To minimise startup latency for this use-case (i.e. expecting StartStream() to give minimum startup latency) you should use the paPrimeOutputBuffersUsingStreamCallback stream flag. Otherwise the initial buffers will be zero and the time it takes for the sound to hit the DACs will include playing out the buffer length of zeros (which would be around 80ms on Windows WMME or DirectSound with the default PA settings).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top