/* Voice thread entrypoint */
static void NORETURN_ATTR voice_thread(void)
{
    struct voice_thread_data td;
    enum voice_state state = VOICE_STATE_MESSAGE;

    voice_data_init(&td);

    while (1)
    {
        switch (state)
        {
        case VOICE_STATE_MESSAGE:
            state = voice_message(&td);
            break;
        case VOICE_STATE_DECODE:
            state = voice_decode(&td);
            break;
        case VOICE_STATE_BUFFER_INSERT:
            state = voice_buffer_insert(&td);
            break;
        }
    }
}
예제 #2
0
/* Voice thread entrypoint */
static void voice_thread(void)
{
    struct voice_thread_data td;

    voice_data_init(&td);

    /* audio thread will only set this once after it finished the final
     * audio hardware init so this little construct is safe - even
     * cross-core. */
    while (!audio_is_thread_ready())
        sleep(0);

    goto message_wait;

    while (1)
    {
        td.state = TSTATE_DECODE;

        if (!queue_empty(&voice_queue))
        {
        message_wait:
            queue_wait(&voice_queue, &td.ev);

        message_process:
            voice_message(&td);

            /* Branch to initial start point or branch back to previous
             * operation if interrupted by a message */
            switch (td.state)
            {
            case TSTATE_DECODE:        goto voice_decode;
            case TSTATE_BUFFER_INSERT: goto buffer_insert;
            default:                   goto message_wait;
            }
        }

    voice_decode:
        /* Decode the data */
        if (speex_decode_int(td.st, &td.bits, voice_output_buf) < 0)
        {
            /* End of stream or error - get next clip */
            td.vi.size = 0;

            if (td.vi.get_more != NULL)
                td.vi.get_more(&td.vi.start, &td.vi.size);

            if (td.vi.start != NULL && (ssize_t)td.vi.size > 0)
            {
                /* Make bit buffer use our own buffer */
                speex_bits_set_bit_buffer(&td.bits, td.vi.start, td.vi.size);
                /* Don't skip any samples when we're stringing clips together */
                td.lookahead = 0;

                /* Paranoid check - be sure never to somehow get stuck in a
                 * loop without listening to the queue */
                yield();

                if (!queue_empty(&voice_queue))
                    goto message_wait;
                else
                    goto voice_decode;
            }

            /* If all clips are done and not playing, force pcm playback. */
            if (!pcm_is_playing())
                pcmbuf_play_start();

            /* Synthesize a stop request */
            /* NOTE: We have no way to know when the pcm data placed in the
             * buffer is actually consumed and playback has reached the end
             * so until the info is available or inferred somehow, this will
             * not be accurate and the stopped signal will come too soon.
             * ie. You may not hear the "Shutting Down" splash even though
             * it waits for voice to stop. */
            td.ev.id = Q_VOICE_STOP;
            td.ev.data = 0; /* Let PCM drain by itself */
            yield();
            goto message_process;
        }

        yield();

        /* Output the decoded frame */
        td.count = VOICE_FRAME_SIZE - td.lookahead;
        td.src[0] = (const char *)&voice_output_buf[td.lookahead];
        td.src[1] = NULL;
        td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead);

    buffer_insert:
        /* Process the PCM samples in the DSP and send out for mixing */
        td.state = TSTATE_BUFFER_INSERT;

        while (td.count > 0)
        {
            int out_count = dsp_output_count(td.dsp, td.count);
            int inp_count;
            char *dest;

            while (1)
            {
                if (!queue_empty(&voice_queue))
                    goto message_wait;

                if ((dest = pcmbuf_request_voice_buffer(&out_count)) != NULL)
                    break;

                yield();
            }

            /* Get the real input_size for output_size bytes, guarding
             * against resampling buffer overflows. */
            inp_count = dsp_input_count(td.dsp, out_count);

            if (inp_count <= 0)
                break;

            /* Input size has grown, no error, just don't write more than
             * length */
            if (inp_count > td.count)
                inp_count = td.count;

            out_count = dsp_process(td.dsp, dest, td.src, inp_count);

            if (out_count <= 0)
                break;

            pcmbuf_write_voice_complete(out_count);
            td.count -= inp_count;
        }

        yield();
    } /* end while */
} /* voice_thread */