示例#1
0
void mi6k_frame_new(pa_memchunk *out,
		    unsigned char **chars_out,
		    struct mi6k_dim_command **dim_command_out)
{
    unsigned char *clear_cmd;
    unsigned char *chars;
    struct mi6k_dim_command *dim_command;

    out->memblock = pa_memblock_new(mi6k.pool, FRAME_SIZE);
    assert(out->memblock);

    out->index = 0;
    out->length = out->memblock->length;

    clear_cmd = out->memblock->data;
    dim_command = (void*) (clear_cmd + 1);
    chars = (void*) (dim_command + 1);

    *clear_cmd = MI6K_HOME;
    dim_command->cmd[0] = MI6K_SET_DIM_1;
    dim_command->cmd[1] = MI6K_SET_DIM_2;
    dim_command->column = 0xFF;
    dim_command->level = MI6K_BRIGHTNESS_MIN;
    memset(chars, ' ', MI6K_WIDTH * MI6K_HEIGHT);

    if (chars_out) {
	*chars_out = chars;
    }
    if (dim_command_out) {
	*dim_command_out = dim_command;
    }
}
示例#2
0
pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
    pa_memblock *n;
    size_t l;
    void *tdata, *sdata;

    pa_assert(c);
    pa_assert(c->memblock);

    if (pa_memblock_ref_is_one(c->memblock) &&
        !pa_memblock_is_read_only(c->memblock) &&
        pa_memblock_get_length(c->memblock) >= c->index+min)
        return c;

    l = PA_MAX(c->length, min);

    n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l);

    sdata = pa_memblock_acquire(c->memblock);
    tdata = pa_memblock_acquire(n);

    memcpy(tdata, (uint8_t*) sdata + c->index, c->length);

    pa_memblock_release(c->memblock);
    pa_memblock_release(n);

    pa_memblock_unref(c->memblock);

    c->memblock = n;
    c->index = 0;

    return c;
}
static int process_source_data(struct userdata *u)
{
    ssize_t l;
    void *p;
    if (!u->memchunk_source.memblock) {
        u->memchunk_source.memblock = pa_memblock_new(u->core->mempool, 16*1024); // at least vchan buffer size
        u->memchunk_source.index = u->memchunk_source.length = 0;
    }

    pa_assert(pa_memblock_get_length(u->memchunk_source.memblock) > u->memchunk_source.index);

    p = pa_memblock_acquire(u->memchunk_source.memblock);
    l = libvchan_read(u->rec_ctrl, p + u->memchunk_source.index, pa_memblock_get_length(u->memchunk_source.memblock) - u->memchunk_source.index);
    pa_memblock_release(u->memchunk_source.memblock);
    pa_log_debug("process_source_data %lu", l);

    if (l <= 0) {
        /* vchan disconnected/error */
        pa_log("Failed to read data from vchan");
        return -1;
    } else {

        u->memchunk_source.length = (size_t) l;
        pa_source_post(u->source, &u->memchunk_source);
        u->memchunk_source.index += (size_t) l;

        if (u->memchunk_source.index >= pa_memblock_get_length(u->memchunk_source.memblock)) {
            pa_memblock_unref(u->memchunk_source.memblock);
            pa_memchunk_reset(&u->memchunk_source);
        }
    }
    return 0;
}
pa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
    pa_memchunk *packet = NULL;
    size_t i;

    pa_assert(pb);
    pa_assert(pb->packets);

    if (seq == 0) {
        /* 0 means seq reached UINT16_MAX and has been wrapped... */
        pa_assert(pb->seq == UINT16_MAX);
        pb->seq = 0;
    } else {
        /* ...otherwise, seq MUST have be increased! */
        pa_assert(seq == pb->seq + 1);
        pb->seq++;
    }

    i = (pb->pos + 1) % pb->size;

    if (pb->packets[i].memblock)
        pa_memblock_unref(pb->packets[i].memblock);
    pa_memchunk_reset(&pb->packets[i]);

    pb->packets[i].memblock = pa_memblock_new(pb->mempool, size);
    pb->packets[i].length = size;
    pb->packets[i].index = 0;

    packet = &pb->packets[i];

    if (pb->count < pb->size)
        pb->count++;
    pb->pos = i;

    return packet;
}
示例#5
0
static int thread_read(struct userdata *u) {
    void *p;
    ssize_t readd;
    pa_memchunk chunk;

    chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size);

    p = pa_memblock_acquire(chunk.memblock);
    readd = u->stream->read(u->stream, (uint8_t*) p, pa_memblock_get_length(chunk.memblock));
    pa_memblock_release(chunk.memblock);

    if (readd < 0) {
        pa_log("Failed to read from stream. (err %i)", readd);
        goto end;
    }

    u->timestamp += pa_bytes_to_usec(readd, &u->source->sample_spec);

    chunk.index = 0;
    chunk.length = readd;

    if (chunk.length > 0)
        pa_source_post(u->source, &chunk);

end:
    pa_memblock_unref(chunk.memblock);

    return 0;
}
static int do_read(connection *c) {
    pa_memchunk chunk;
    ssize_t r;
    size_t l;
    void *p;
    size_t space = 0;

    connection_assert_ref(c);

    if (!c->sink_input || (l = (size_t) pa_atomic_load(&c->playback.missing)) <= 0)
        return 0;

    if (c->playback.current_memblock) {

        space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;

        if (space <= 0) {
            pa_memblock_unref(c->playback.current_memblock);
            c->playback.current_memblock = NULL;
        }
    }

    if (!c->playback.current_memblock) {
        pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
        c->playback.memblock_index = 0;

        space = pa_memblock_get_length(c->playback.current_memblock);
    }

    if (l > space)
        l = space;

    p = pa_memblock_acquire(c->playback.current_memblock);
    r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l);
    pa_memblock_release(c->playback.current_memblock);

    if (r <= 0) {

        if (r < 0 && (errno == EINTR || errno == EAGAIN))
            return 0;

        pa_log_debug("read(): %s", r == 0 ? "EOF" : pa_cstrerror(errno));
        return -1;
    }

    chunk.memblock = c->playback.current_memblock;
    chunk.index = c->playback.memblock_index;
    chunk.length = (size_t) r;

    c->playback.memblock_index += (size_t) r;

    pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
    pa_atomic_sub(&c->playback.missing, (int) r);

    return 0;
}
示例#7
0
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
    struct userdata *u;
    float *src, *dst;
    size_t fs;
    unsigned n, h, c;
    pa_memchunk tchunk;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    pa_assert_se(u = i->userdata);

    /* Hmm, process any rewind request that might be queued up */
    pa_sink_process_rewind(u->sink, 0);

    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
        pa_memchunk nchunk;

        pa_sink_render(u->sink, nbytes, &nchunk);
        pa_memblockq_push(u->memblockq, &nchunk);
        pa_memblock_unref(nchunk.memblock);
    }

    tchunk.length = PA_MIN(nbytes, tchunk.length);
    pa_assert(tchunk.length > 0);

    fs = pa_frame_size(&i->sample_spec);
    n = (unsigned) (PA_MIN(tchunk.length, u->block_size) / fs);

    pa_assert(n > 0);

    chunk->index = 0;
    chunk->length = n*fs;
    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);

    pa_memblockq_drop(u->memblockq, chunk->length);

    src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
    dst = (float*) pa_memblock_acquire(chunk->memblock);

    for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) {
        for (c = 0; c < u->input_count; c++)
            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c], sizeof(float), src+ h*u->max_ladspaport_count + c, u->channels*sizeof(float), n);
        u->descriptor->run(u->handle[h], n);
        for (c = 0; c < u->output_count; c++)
            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + h*u->max_ladspaport_count + c, u->channels*sizeof(float), u->output[c], sizeof(float), n);
    }

    pa_memblock_release(tchunk.memblock);
    pa_memblock_release(chunk->memblock);

    pa_memblock_unref(tchunk.memblock);

    return 0;
}
示例#8
0
static void do_read(struct userdata *u) {
    uint32_t free_frags;
    pa_memchunk memchunk;
    WAVEHDR *hdr;
    MMRESULT res;
    void *p;

    if (!u->source)
        return;

    if (!PA_SOURCE_IS_LINKED(u->source->state))
        return;

    EnterCriticalSection(&u->crit);
    free_frags = u->free_ifrags;
    u->free_ifrags = 0;
    LeaveCriticalSection(&u->crit);

    if (free_frags == u->fragments)
        pa_log_debug("WaveIn overflow!");

    while (free_frags) {
        hdr = &u->ihdrs[u->cur_ihdr];
        if (hdr->dwFlags & WHDR_PREPARED)
            waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR));

        if (hdr->dwBytesRecorded) {
            memchunk.memblock = pa_memblock_new(u->core->mempool, hdr->dwBytesRecorded);
            pa_assert(memchunk.memblock);

            p = pa_memblock_acquire(memchunk.memblock);
            memcpy((char*) p, hdr->lpData, hdr->dwBytesRecorded);
            pa_memblock_release(memchunk.memblock);

            memchunk.length = hdr->dwBytesRecorded;
            memchunk.index = 0;

            pa_source_post(u->source, &memchunk);
            pa_memblock_unref(memchunk.memblock);
        }

        res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
            pa_log_error("Unable to prepare waveIn block: %d", res);

        res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR));
        if (res != MMSYSERR_NOERROR)
            pa_log_error("Unable to add waveIn block: %d", res);

        free_frags--;
        u->cur_ihdr++;
        u->cur_ihdr %= u->fragments;
    }
}
static void thread_func(void *userdata) {
    struct userdata *u = userdata;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    u->timestamp = pa_rtclock_now();

    for (;;) {
        int ret;

        /* Generate some null data */
        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
            pa_usec_t now;
            pa_memchunk chunk;

            now = pa_rtclock_now();

            if ((chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) {

                chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); /* or chunk.length? */
                chunk.index = 0;
                pa_source_post(u->source, &chunk);
                pa_memblock_unref(chunk.memblock);

                u->timestamp = now;
            }

            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->latency_time * PA_USEC_PER_MSEC);
        } else
            pa_rtpoll_set_timer_disabled(u->rtpoll);

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#10
0
static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
    pa_memblock *b;
    size_t length;
    void *data;

    pa_assert(pool);

    length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);

    b = pa_memblock_new(pool, length);

    data = pa_memblock_acquire(b);
    memset(data, c, length);
    pa_memblock_release(b);

    pa_memblock_set_is_silence(b, true);

    return b;
}
示例#11
0
void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq) {
    size_t l;
    unsigned gcd, n;
    void *p;

    pa_memchunk_reset(c);

    gcd = pa_gcd(rate, freq);
    n = rate / gcd;

    l = pa_mempool_block_size_max(pool) / sizeof(float);

    l /= n;
    if (l <= 0) l = 1;
    l *= n;

    c->length = l * sizeof(float);
    c->memblock = pa_memblock_new(pool, c->length);

    p = pa_memblock_acquire(c->memblock);
    calc_sine(p, c->length, freq * l / rate);
    pa_memblock_release(c->memblock);
}
示例#12
0
int pa_sound_file_load(
        pa_mempool *pool,
        const char *fname,
        pa_sample_spec *ss,
        pa_channel_map *map,
        pa_memchunk *chunk,
        pa_proplist *p) {

    SNDFILE *sf = NULL;
    SF_INFO sfi;
    int ret = -1;
    size_t l;
    sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames) = NULL;
    void *ptr = NULL;
    int fd;

    pa_assert(fname);
    pa_assert(ss);
    pa_assert(chunk);

    pa_memchunk_reset(chunk);

    if ((fd = pa_open_cloexec(fname, O_RDONLY, 0)) < 0) {
        pa_log("Failed to open file %s: %s", fname, pa_cstrerror(errno));
        goto finish;
    }

#ifdef HAVE_POSIX_FADVISE
    if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL) < 0) {
        pa_log_warn("POSIX_FADV_SEQUENTIAL failed: %s", pa_cstrerror(errno));
        goto finish;
    } else
        pa_log_debug("POSIX_FADV_SEQUENTIAL succeeded.");
#endif

    pa_zero(sfi);
    if (!(sf = sf_open_fd(fd, SFM_READ, &sfi, 1))) {
        pa_log("Failed to open file %s", fname);
        goto finish;
    }

    fd = -1;

    if (pa_sndfile_read_sample_spec(sf, ss) < 0) {
        pa_log("Failed to determine file sample format.");
        goto finish;
    }

    if ((map && pa_sndfile_read_channel_map(sf, map) < 0)) {
        if (ss->channels > 2)
            pa_log("Failed to determine file channel map, synthesizing one.");
        pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_DEFAULT);
    }

    if (p)
        pa_sndfile_init_proplist(sf, p);

    if ((l = pa_frame_size(ss) * (size_t) sfi.frames) > PA_SCACHE_ENTRY_SIZE_MAX) {
        pa_log("File too large");
        goto finish;
    }

    chunk->memblock = pa_memblock_new(pool, l);
    chunk->index = 0;
    chunk->length = l;

    readf_function = pa_sndfile_readf_function(ss);

    ptr = pa_memblock_acquire(chunk->memblock);

    if ((readf_function && readf_function(sf, ptr, sfi.frames) != sfi.frames) ||
        (!readf_function && sf_read_raw(sf, ptr, (sf_count_t) l) != (sf_count_t) l)) {
        pa_log("Premature file end");
        goto finish;
    }

    ret = 0;

finish:

    if (sf)
        sf_close(sf);

    if (ptr)
        pa_memblock_release(chunk->memblock);

    if (ret != 0 && chunk->memblock)
        pa_memblock_unref(chunk->memblock);

    if (fd >= 0)
        pa_close(fd);

    return ret;
}
示例#13
0
static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
    pa_memblock *r;
    void *d;
    unsigned i;

    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
    d = pa_memblock_acquire(r);

    switch (ss->format) {

    case PA_SAMPLE_U8:
    case PA_SAMPLE_ULAW:
    case PA_SAMPLE_ALAW: {
        static const uint8_t u8_samples[] = {
            0x00, 0xFF, 0x7F, 0x80, 0x9f,
            0x3f, 0x01, 0xF0, 0x20, 0x21
        };

        memcpy(d, u8_samples, sizeof(u8_samples));
        break;
    }

    case PA_SAMPLE_S16NE:
    case PA_SAMPLE_S16RE: {
        static const uint16_t u16_samples[] = {
            0x0000, 0xFFFF, 0x7FFF, 0x8000, 0x9fff,
            0x3fff, 0x0001, 0xF000, 0x0020, 0x0021
        };

        memcpy(d, u16_samples, sizeof(u16_samples));
        break;
    }

    case PA_SAMPLE_S24_32NE:
    case PA_SAMPLE_S24_32RE:
    case PA_SAMPLE_S32NE:
    case PA_SAMPLE_S32RE: {
        static const uint32_t u32_samples[] = {
            0x00000001, 0xFFFF0002, 0x7FFF0003, 0x80000004, 0x9fff0005,
            0x3fff0006, 0x00010007, 0xF0000008, 0x00200009, 0x0021000A
        };

        memcpy(d, u32_samples, sizeof(u32_samples));
        break;
    }

    case PA_SAMPLE_S24NE:
    case PA_SAMPLE_S24RE: {
        /* Need to be on a byte array because they are not aligned */
        static const uint8_t u24_samples[] = {
            0x00, 0x00, 0x01,
            0xFF, 0xFF, 0x02,
            0x7F, 0xFF, 0x03,
            0x80, 0x00, 0x04,
            0x9f, 0xff, 0x05,
            0x3f, 0xff, 0x06,
            0x01, 0x00, 0x07,
            0xF0, 0x00, 0x08,
            0x20, 0x00, 0x09,
            0x21, 0x00, 0x0A
        };

        memcpy(d, u24_samples, sizeof(u24_samples));
        break;
    }

    case PA_SAMPLE_FLOAT32NE:
    case PA_SAMPLE_FLOAT32RE: {
        float *u = d;
        static const float float_samples[] = {
            0.0f, -1.0f, 1.0f, 4711.0f, 0.222f,
            0.33f, -.3f, 99.0f, -0.555f, -.123f
        };

        if (ss->format == PA_SAMPLE_FLOAT32RE) {
            for (i = 0; i < 10; i++)
                u[i] = PA_FLOAT32_SWAP(float_samples[i]);
        } else
            memcpy(d, float_samples, sizeof(float_samples));

        break;
    }

    default:
        pa_assert_not_reached();
    }

    pa_memblock_release(r);

    return r;
}
示例#14
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int write_type = 0;
    pa_memchunk silence;
    uint32_t silence_overhead = 0;
    double silence_ratio = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());

    /* Create a chunk of memory that is our encoded silence sample. */
    pa_memchunk_reset(&silence);

    for (;;) {
        int ret;

        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
            pa_sink_process_rewind(u->sink, 0);

        if (u->rtpoll_item) {
            struct pollfd *pollfd;
            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            /* Render some data and write it to the fifo */
            if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
                pa_usec_t usec;
                int64_t n;
                void *p;

                if (!silence.memblock) {
                    pa_memchunk silence_tmp;

                    pa_memchunk_reset(&silence_tmp);
                    silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096);
                    silence_tmp.length = 4096;
                    p = pa_memblock_acquire(silence_tmp.memblock);
                    memset(p, 0, 4096);
                    pa_memblock_release(silence_tmp.memblock);
                    pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence);
                    pa_assert(0 == silence_tmp.length);
                    silence_overhead = silence_tmp.length - 4096;
                    silence_ratio = silence_tmp.length / 4096;
                    pa_memblock_unref(silence_tmp.memblock);
                }

                for (;;) {
                    ssize_t l;

                    if (u->encoded_memchunk.length <= 0) {
                        if (u->encoded_memchunk.memblock)
                            pa_memblock_unref(u->encoded_memchunk.memblock);
                        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
                            size_t rl;

                            /* We render real data */
                            if (u->raw_memchunk.length <= 0) {
                                if (u->raw_memchunk.memblock)
                                    pa_memblock_unref(u->raw_memchunk.memblock);
                                pa_memchunk_reset(&u->raw_memchunk);

                                /* Grab unencoded data */
                                pa_sink_render(u->sink, u->block_size, &u->raw_memchunk);
                            }
                            pa_assert(u->raw_memchunk.length > 0);

                            /* Encode it */
                            rl = u->raw_memchunk.length;
                            u->encoding_overhead += u->next_encoding_overhead;
                            pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk);
                            u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length));
                            u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length);
                        } else {
                            /* We render some silence into our memchunk */
                            memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk));
                            pa_memblock_ref(silence.memblock);

                            /* Calculate/store some values to be used with the smoother */
                            u->next_encoding_overhead = silence_overhead;
                            u->encoding_ratio = silence_ratio;
                        }
                    }
                    pa_assert(u->encoded_memchunk.length > 0);

                    p = pa_memblock_acquire(u->encoded_memchunk.memblock);
                    l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type);
                    pa_memblock_release(u->encoded_memchunk.memblock);

                    pa_assert(l != 0);

                    if (l < 0) {

                        if (errno == EINTR)
                            continue;
                        else if (errno == EAGAIN) {

                            /* OK, we filled all socket buffers up
                             * now. */
                            goto filled_up;

                        } else {
                            pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {
                        u->offset += l;

                        u->encoded_memchunk.index += l;
                        u->encoded_memchunk.length -= l;

                        pollfd->revents = 0;

                        if (u->encoded_memchunk.length > 0) {
                            /* we've completely written the encoded data, so update our overhead */
                            u->encoding_overhead += u->next_encoding_overhead;

                            /* OK, we wrote less that we asked for,
                             * hence we can assume that the socket
                             * buffers are full now */
                            goto filled_up;
                        }
                    }
                }

filled_up:

                /* At this spot we know that the socket buffers are
                 * fully filled up. This is the best time to estimate
                 * the playback position of the server */

                n = u->offset - u->encoding_overhead;

#ifdef SIOCOUTQ
                {
                    int l;
                    if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
                        n -= (l / u->encoding_ratio);
                }
#endif

                usec = pa_bytes_to_usec(n, &u->sink->sample_spec);

                if (usec > u->latency)
                    usec -= u->latency;
                else
                    usec = 0;

                pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
            }

            /* Hmm, nothing to do. Let's sleep */
            pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state)  ? POLLOUT : 0;*/
        }

        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->rtpoll_item) {
            struct pollfd* pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~POLLOUT) {
                if (u->sink->thread_info.state != PA_SINK_SUSPENDED) {
                    pa_log("FIFO shutdown.");
                    goto fail;
                }

                /* We expect this to happen on occasion if we are not sending data.
                   It's perfectly natural and normal and natural */
                if (u->rtpoll_item)
                    pa_rtpoll_item_free(u->rtpoll_item);
                u->rtpoll_item = NULL;
            }
        }
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    if (silence.memblock)
        pa_memblock_unref(silence.memblock);
    pa_log_debug("Thread shutting down");
}
示例#15
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    bool timer_elapsed = false;
    size_t max_block_size;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    if (u->core->realtime_scheduling)
        pa_thread_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    max_block_size = pa_frame_align(pa_mempool_block_size_max(u->core->mempool), &u->source->sample_spec);
    u->timestamp = pa_rtclock_now();

    for (;;) {
        int ret;

        /* Generate some null data */
        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
            pa_usec_t now;
            pa_memchunk chunk;

            now = pa_rtclock_now();

            if (timer_elapsed && (chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) {

                chunk.length = PA_MIN(max_block_size, chunk.length);

                chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
                chunk.index = 0;
                pa_silence_memchunk(&chunk, &u->source->sample_spec);
                pa_source_post(u->source, &chunk);
                pa_memblock_unref(chunk.memblock);

                u->timestamp += pa_bytes_to_usec(chunk.length, &u->source->sample_spec);
            }

            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->block_usec);
        } else
            pa_rtpoll_set_timer_disabled(u->rtpoll);

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
            goto fail;

        timer_elapsed = pa_rtpoll_timer_elapsed(u->rtpoll);

        if (ret == 0)
            goto finish;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#16
0
int main(int argc, char *argv[]) {
    pa_mempool *pool_a, *pool_b, *pool_c;
    unsigned id_a, id_b, id_c;
    pa_memexport *export_a, *export_b;
    pa_memimport *import_b, *import_c;
    pa_memblock *mb_a, *mb_b, *mb_c;
    int r, i;
    pa_memblock* blocks[5];
    uint32_t id, shm_id;
    size_t offset, size;
    char *x;

    const char txt[] = "This is a test!";

    pool_a = pa_mempool_new(1);
    pool_b = pa_mempool_new(1);
    pool_c = pa_mempool_new(1);

    pa_mempool_get_shm_id(pool_a, &id_a);
    pa_mempool_get_shm_id(pool_b, &id_b);
    pa_mempool_get_shm_id(pool_c, &id_c);

    pa_assert(pool_a && pool_b && pool_c);

    blocks[0] = pa_memblock_new_fixed(pool_a, (void*) txt, sizeof(txt), 1);

    blocks[1] = pa_memblock_new(pool_a, sizeof(txt));
    x = pa_memblock_acquire(blocks[1]);
    snprintf(x, pa_memblock_get_length(blocks[1]), "%s", txt);
    pa_memblock_release(blocks[1]);

    blocks[2] = pa_memblock_new_pool(pool_a, sizeof(txt));
    x = pa_memblock_acquire(blocks[2]);
    snprintf(x, pa_memblock_get_length(blocks[2]), "%s", txt);
    pa_memblock_release(blocks[2]);

    blocks[3] = pa_memblock_new_malloced(pool_a, pa_xstrdup(txt), sizeof(txt));
    blocks[4] = NULL;

    for (i = 0; blocks[i]; i++) {
        printf("Memory block %u\n", i);

        mb_a = blocks[i];
        pa_assert(mb_a);

        export_a = pa_memexport_new(pool_a, revoke_cb, (void*) "A");
        export_b = pa_memexport_new(pool_b, revoke_cb, (void*) "B");

        pa_assert(export_a && export_b);

        import_b = pa_memimport_new(pool_b, release_cb, (void*) "B");
        import_c = pa_memimport_new(pool_c, release_cb, (void*) "C");

        pa_assert(import_b && import_c);

        r = pa_memexport_put(export_a, mb_a, &id, &shm_id, &offset, &size);
        pa_assert(r >= 0);
        pa_assert(shm_id == id_a);

        printf("A: Memory block exported as %u\n", id);

        mb_b = pa_memimport_get(import_b, id, shm_id, offset, size);
        pa_assert(mb_b);
        r = pa_memexport_put(export_b, mb_b, &id, &shm_id, &offset, &size);
        pa_assert(r >= 0);
        pa_assert(shm_id == id_a || shm_id == id_b);
        pa_memblock_unref(mb_b);

        printf("B: Memory block exported as %u\n", id);

        mb_c = pa_memimport_get(import_c, id, shm_id, offset, size);
        pa_assert(mb_c);
        x = pa_memblock_acquire(mb_c);
        printf("1 data=%s\n", x);
        pa_memblock_release(mb_c);

        print_stats(pool_a, "A");
        print_stats(pool_b, "B");
        print_stats(pool_c, "C");

        pa_memexport_free(export_b);
        x = pa_memblock_acquire(mb_c);
        printf("2 data=%s\n", x);
        pa_memblock_release(mb_c);
        pa_memblock_unref(mb_c);

        pa_memimport_free(import_b);

        pa_memblock_unref(mb_a);

        pa_memimport_free(import_c);
        pa_memexport_free(export_a);
    }

    printf("vaccuuming...\n");

    pa_mempool_vacuum(pool_a);
    pa_mempool_vacuum(pool_b);
    pa_mempool_vacuum(pool_c);

    printf("vaccuuming done...\n");

    pa_mempool_free(pool_a);
    pa_mempool_free(pool_b);
    pa_mempool_free(pool_c);

    return 0;
}
示例#17
0
static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
    pa_memblock *r;
    void *d;
    unsigned i;

    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
    d = pa_memblock_acquire(r);

    switch (ss->format) {

        case PA_SAMPLE_U8:
        case PA_SAMPLE_ULAW:
        case PA_SAMPLE_ALAW: {
            memcpy(d, u8_result[0], sizeof(u8_result[0]));
            break;
        }

        case PA_SAMPLE_S16NE:
        case PA_SAMPLE_S16RE: {
            if (ss->format == PA_SAMPLE_S16RE) {
                uint16_t *u = d;
                for (i = 0; i < 10; i++)
                    u[i] = PA_UINT16_SWAP(s16ne_result[0][i]);
            } else
                memcpy(d, s16ne_result[0], sizeof(s16ne_result[0]));
            break;
        }

        case PA_SAMPLE_S24_32NE:
        case PA_SAMPLE_S24_32RE:
        case PA_SAMPLE_S32NE:
        case PA_SAMPLE_S32RE: {
            if (ss->format == PA_SAMPLE_S24_32RE || ss->format == PA_SAMPLE_S32RE) {
                uint32_t *u = d;
                for (i = 0; i < 10; i++)
                    u[i] = PA_UINT32_SWAP(s32ne_result[0][i]);
            } else
                memcpy(d, s32ne_result[0], sizeof(s32ne_result[0]));
            break;
        }

        case PA_SAMPLE_S24NE:
        case PA_SAMPLE_S24RE:
            if (ss->format == PA_SAMPLE_S24LE) {
                uint8_t *u = d;
                for (i = 0; i < 30; i += 3)
                    PA_WRITE24LE(&u[i], PA_READ24BE(&s24be_result[0][i]));
            } else
                memcpy(d, s24be_result[0], sizeof(s24be_result[0]));
            break;

        case PA_SAMPLE_FLOAT32NE:
        case PA_SAMPLE_FLOAT32RE: {
            if (ss->format == PA_SAMPLE_FLOAT32RE) {
                float *u = d;
                for (i = 0; i < 10; i++)
                    PA_WRITE_FLOAT32RE(&u[i], float32ne_result[0][i]);
            } else
                memcpy(d, float32ne_result[0], sizeof(float32ne_result[0]));

            break;
        }

        default:
            pa_assert_not_reached();
    }

    pa_memblock_release(r);

    return r;
}
示例#18
0
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
    struct userdata *u;
    float *src, *dst;
    size_t fs;
    unsigned n, c;
    pa_memchunk tchunk;
    pa_usec_t current_latency PA_GCC_UNUSED;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    pa_assert_se(u = i->userdata);

    /* Hmm, process any rewind request that might be queued up */
    pa_sink_process_rewind(u->sink, 0);

    /* (1) IF YOU NEED A FIXED BLOCK SIZE USE
     * pa_memblockq_peek_fixed_size() HERE INSTEAD. NOTE THAT FILTERS
     * WHICH CAN DEAL WITH DYNAMIC BLOCK SIZES ARE HIGHLY
     * PREFERRED. */
    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
        pa_memchunk nchunk;

        pa_sink_render(u->sink, nbytes, &nchunk);
        pa_memblockq_push(u->memblockq, &nchunk);
        pa_memblock_unref(nchunk.memblock);
    }

    /* (2) IF YOU NEED A FIXED BLOCK SIZE, THIS NEXT LINE IS NOT
     * NECESSARY */
    tchunk.length = PA_MIN(nbytes, tchunk.length);
    pa_assert(tchunk.length > 0);

    fs = pa_frame_size(&i->sample_spec);
    n = (unsigned) (tchunk.length / fs);

    pa_assert(n > 0);

    chunk->index = 0;
    chunk->length = n*fs;
    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);

    pa_memblockq_drop(u->memblockq, chunk->length);

    src = pa_memblock_acquire_chunk(&tchunk);
    dst = pa_memblock_acquire(chunk->memblock);

    /* (3) PUT YOUR CODE HERE TO DO SOMETHING WITH THE DATA */

    /* As an example, copy input to output */
    for (c = 0; c < u->channels; c++) {
        pa_sample_clamp(PA_SAMPLE_FLOAT32NE,
                        dst+c, u->channels * sizeof(float),
                        src+c, u->channels * sizeof(float),
                        n);
    }

    pa_memblock_release(tchunk.memblock);
    pa_memblock_release(chunk->memblock);

    pa_memblock_unref(tchunk.memblock);

    /* (4) IF YOU NEED THE LATENCY FOR SOMETHING ACQUIRE IT LIKE THIS: */
    current_latency =
        /* Get the latency of the master sink */
        pa_sink_get_latency_within_thread(i->sink) +

        /* Add the latency internal to our sink input on top */
        pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);

    return 0;
}
示例#19
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    unsigned short revents = 0;
    int ret, err;
    audio_info_t info;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());

    for (;;) {
        /* Render some data and write it to the dsp */

        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
            process_rewind(u);

        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
            pa_usec_t xtime0, ysleep_interval, xsleep_interval;
            uint64_t buffered_bytes;

            err = ioctl(u->fd, AUDIO_GETINFO, &info);
            if (err < 0) {
                pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno));
                goto fail;
            }

            if (info.play.error) {
                pa_log_debug("buffer under-run!");

                AUDIO_INITINFO(&info);
                info.play.error = 0;
                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));

                pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
            }

            for (;;) {
                void *p;
                ssize_t w;
                size_t len;
                int write_type = 1;

                /*
                 * Since we cannot modify the size of the output buffer we fake it
                 * by not filling it more than u->buffer_size.
                 */
                xtime0 = pa_rtclock_now();
                buffered_bytes = get_playback_buffered_bytes(u);
                if (buffered_bytes >= (uint64_t)u->buffer_size)
                    break;

                len = u->buffer_size - buffered_bytes;
                len -= len % u->frame_size;

                if (len < (size_t) u->minimum_request)
                    break;

                if (!u->memchunk.length)
                    pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk);

                len = PA_MIN(u->memchunk.length, len);

                p = pa_memblock_acquire(u->memchunk.memblock);
                w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type);
                pa_memblock_release(u->memchunk.memblock);

                if (w <= 0) {
                    if (errno == EINTR) {
                        continue;
                    } else if (errno == EAGAIN) {
                        /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */
                        pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes);
                        break;
                    } else {
                        pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
                        goto fail;
                    }
                } else {
                    pa_assert(w % u->frame_size == 0);

                    u->written_bytes += w;
                    u->memchunk.index += w;
                    u->memchunk.length -= w;
                    if (u->memchunk.length <= 0) {
                        pa_memblock_unref(u->memchunk.memblock);
                        pa_memchunk_reset(&u->memchunk);
                    }
                }
            }

            ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec);
            xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval);
            pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval));
        } else
            pa_rtpoll_set_timer_disabled(u->rtpoll);

        /* Try to read some data and pass it on to the source driver */

        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && (revents & POLLIN)) {
            pa_memchunk memchunk;
            void *p;
            ssize_t r;
            size_t len;

            err = ioctl(u->fd, AUDIO_GETINFO, &info);
            pa_assert(err >= 0);

            if (info.record.error) {
                pa_log_debug("buffer overflow!");

                AUDIO_INITINFO(&info);
                info.record.error = 0;
                if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
                    pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
            }

            err = ioctl(u->fd, I_NREAD, &len);
            pa_assert(err >= 0);

            if (len > 0) {
                memchunk.memblock = pa_memblock_new(u->core->mempool, len);
                pa_assert(memchunk.memblock);

                p = pa_memblock_acquire(memchunk.memblock);
                r = pa_read(u->fd, p, len, NULL);
                pa_memblock_release(memchunk.memblock);

                if (r < 0) {
                    pa_memblock_unref(memchunk.memblock);
                    if (errno == EAGAIN)
                        break;
                    else {
                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
                        goto fail;
                    }
                } else {
                    u->read_bytes += r;

                    memchunk.index = 0;
                    memchunk.length = r;

                    pa_source_post(u->source, &memchunk);
                    pa_memblock_unref(memchunk.memblock);

                    revents &= ~POLLIN;
                }
            }
        }

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pa_assert(u->fd >= 0);

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
            pollfd->events = (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0;
        }

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
                pa_log("DSP shutdown.");
                goto fail;
            }

            revents = pollfd->revents;
        } else
            revents = 0;
    }

fail:
    /* We have to continue processing messages until we receive the
     * SHUTDOWN message */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#20
0
static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
    pa_memblock *r;
    void *d;
    unsigned i;

    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * 10));
    d = pa_memblock_acquire(r);

    switch (ss->format) {

        case PA_SAMPLE_U8:
        case PA_SAMPLE_ULAW:
        case PA_SAMPLE_ALAW: {
            uint8_t *u = d;

            u[0] = 0x00;
            u[1] = 0xFF;
            u[2] = 0x7F;
            u[3] = 0x80;
            u[4] = 0x9f;
            u[5] = 0x3f;
            u[6] = 0x1;
            u[7] = 0xF0;
            u[8] = 0x20;
            u[9] = 0x21;
            break;
        }

        case PA_SAMPLE_S16NE:
        case PA_SAMPLE_S16RE: {
            uint16_t *u = d;

            u[0] = 0x0000;
            u[1] = 0xFFFF;
            u[2] = 0x7FFF;
            u[3] = 0x8000;
            u[4] = 0x9fff;
            u[5] = 0x3fff;
            u[6] = 0x1;
            u[7] = 0xF000;
            u[8] = 0x20;
            u[9] = 0x21;
            break;
        }

        case PA_SAMPLE_S32NE:
        case PA_SAMPLE_S32RE: {
            uint32_t *u = d;

            u[0] = 0x00000001;
            u[1] = 0xFFFF0002;
            u[2] = 0x7FFF0003;
            u[3] = 0x80000004;
            u[4] = 0x9fff0005;
            u[5] = 0x3fff0006;
            u[6] =    0x10007;
            u[7] = 0xF0000008;
            u[8] =   0x200009;
            u[9] =   0x21000A;
            break;
        }

        case PA_SAMPLE_S24_32NE:
        case PA_SAMPLE_S24_32RE: {
            uint32_t *u = d;

            u[0] = 0x000001;
            u[1] = 0xFF0002;
            u[2] = 0x7F0003;
            u[3] = 0x800004;
            u[4] = 0x9f0005;
            u[5] = 0x3f0006;
            u[6] =    0x107;
            u[7] = 0xF00008;
            u[8] =   0x2009;
            u[9] =   0x210A;
            break;
        }

        case PA_SAMPLE_FLOAT32NE:
        case PA_SAMPLE_FLOAT32RE: {
            float *u = d;

            u[0] = 0.0f;
            u[1] = -1.0f;
            u[2] = 1.0f;
            u[3] = 4711.0f;
            u[4] = 0.222f;
            u[5] = 0.33f;
            u[6] = -.3f;
            u[7] = 99.0f;
            u[8] = -0.555f;
            u[9] = -.123f;

            if (ss->format == PA_SAMPLE_FLOAT32RE)
                for (i = 0; i < 10; i++)
                    u[i] = PA_FLOAT32_SWAP(u[i]);

            break;
        }

        case PA_SAMPLE_S24NE:
        case PA_SAMPLE_S24RE: {
            uint8_t *u = d;

            PA_WRITE24NE(u,    0x000001);
            PA_WRITE24NE(u+3,  0xFF0002);
            PA_WRITE24NE(u+6,  0x7F0003);
            PA_WRITE24NE(u+9,  0x800004);
            PA_WRITE24NE(u+12, 0x9f0005);
            PA_WRITE24NE(u+15, 0x3f0006);
            PA_WRITE24NE(u+18,    0x107);
            PA_WRITE24NE(u+21, 0xF00008);
            PA_WRITE24NE(u+24,   0x2009);
            PA_WRITE24NE(u+27,   0x210A);
            break;
        }

        default:
            pa_assert_not_reached();
    }

    pa_memblock_release(r);

    return r;
}
示例#21
0
/* Called from IO thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
    file_stream *u;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    u = FILE_STREAM(i->userdata);
    file_stream_assert_ref(u);

    if (!u->memblockq)
        return -1;

    for (;;) {
        pa_memchunk tchunk;
        size_t fs;
        void *p;
        sf_count_t n;

        if (pa_memblockq_peek(u->memblockq, chunk) >= 0) {
            chunk->length = PA_MIN(chunk->length, length);
            pa_memblockq_drop(u->memblockq, chunk->length);
            return 0;
        }

        if (!u->sndfile)
            break;

        tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length);
        tchunk.index = 0;

        p = pa_memblock_acquire(tchunk.memblock);

        if (u->readf_function) {
            fs = pa_frame_size(&i->sample_spec);
            n = u->readf_function(u->sndfile, p, (sf_count_t) (length/fs));
        } else {
            fs = 1;
            n = sf_read_raw(u->sndfile, p, (sf_count_t) length);
        }

        pa_memblock_release(tchunk.memblock);

        if (n <= 0) {
            pa_memblock_unref(tchunk.memblock);

            sf_close(u->sndfile);
            u->sndfile = NULL;
            break;
        }

        tchunk.length = (size_t) n * fs;

        pa_memblockq_push(u->memblockq, &tchunk);
        pa_memblock_unref(tchunk.memblock);
    }

    if (pa_sink_input_safe_to_remove(i)) {
        pa_memblockq_free(u->memblockq);
        u->memblockq = NULL;

        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
    }

    return -1;
}
示例#22
0
int main(int argc, char *argv[]) {
    pa_mempool *pool = NULL;
    pa_sample_spec a, b;
    int ret = 1, c;
    bool all_formats = true;
    pa_resample_method_t method;
    int seconds;
    unsigned crossover_freq = 120;

    static const struct option long_options[] = {
        {"help",                  0, NULL, 'h'},
        {"verbose",               0, NULL, 'v'},
        {"version",               0, NULL, ARG_VERSION},
        {"from-rate",             1, NULL, ARG_FROM_SAMPLERATE},
        {"from-format",           1, NULL, ARG_FROM_SAMPLEFORMAT},
        {"from-channels",         1, NULL, ARG_FROM_CHANNELS},
        {"to-rate",               1, NULL, ARG_TO_SAMPLERATE},
        {"to-format",             1, NULL, ARG_TO_SAMPLEFORMAT},
        {"to-channels",           1, NULL, ARG_TO_CHANNELS},
        {"seconds",               1, NULL, ARG_SECONDS},
        {"resample-method",       1, NULL, ARG_RESAMPLE_METHOD},
        {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS},
        {NULL,                    0, NULL, 0}
    };

    setlocale(LC_ALL, "");
#ifdef ENABLE_NLS
    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
#endif

    pa_log_set_level(PA_LOG_WARN);
    if (!getenv("MAKE_CHECK"))
        pa_log_set_level(PA_LOG_INFO);

    pa_assert_se(pool = pa_mempool_new(false, 0));

    a.channels = b.channels = 1;
    a.rate = b.rate = 44100;
    a.format = b.format = PA_SAMPLE_S16LE;

    method = PA_RESAMPLER_AUTO;
    seconds = 60;

    while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) {

        switch (c) {
            case 'h' :
                help(argv[0]);
                ret = 0;
                goto quit;

            case 'v':
                pa_log_set_level(PA_LOG_DEBUG);
                break;

            case ARG_VERSION:
                printf(_("%s %s\n"), argv[0], PACKAGE_VERSION);
                ret = 0;
                goto quit;

            case ARG_DUMP_RESAMPLE_METHODS:
                dump_resample_methods();
                ret = 0;
                goto quit;

            case ARG_FROM_CHANNELS:
                a.channels = (uint8_t) atoi(optarg);
                break;

            case ARG_FROM_SAMPLEFORMAT:
                a.format = pa_parse_sample_format(optarg);
                all_formats = false;
                break;

            case ARG_FROM_SAMPLERATE:
                a.rate = (uint32_t) atoi(optarg);
                break;

            case ARG_TO_CHANNELS:
                b.channels = (uint8_t) atoi(optarg);
                break;

            case ARG_TO_SAMPLEFORMAT:
                b.format = pa_parse_sample_format(optarg);
                all_formats = false;
                break;

            case ARG_TO_SAMPLERATE:
                b.rate = (uint32_t) atoi(optarg);
                break;

            case ARG_SECONDS:
                seconds = atoi(optarg);
                break;

            case ARG_RESAMPLE_METHOD:
                if (*optarg == '\0' || pa_streq(optarg, "help")) {
                    dump_resample_methods();
                    ret = 0;
                    goto quit;
                }
                method = pa_parse_resample_method(optarg);
                break;

            default:
                goto quit;
        }
    }

    ret = 0;
    pa_assert_se(pool = pa_mempool_new(false, 0));

    if (!all_formats) {

        pa_resampler *resampler;
        pa_memchunk i, j;
        pa_usec_t ts;

        pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
        pa_log_debug("=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)", seconds,
                   a.rate, a.channels, pa_sample_format_to_string(a.format),
                   b.rate, b.channels, pa_sample_format_to_string(b.format));

        ts = pa_rtclock_now();
        pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
        pa_log_info("init: %llu", (long long unsigned)(pa_rtclock_now() - ts));

        i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a));

        ts = pa_rtclock_now();
        i.length = pa_memblock_get_length(i.memblock);
        i.index = 0;
        while (seconds--) {
            pa_resampler_run(resampler, &i, &j);
            if (j.memblock)
                pa_memblock_unref(j.memblock);
        }
        pa_log_info("resampling: %llu", (long long unsigned)(pa_rtclock_now() - ts));
        pa_memblock_unref(i.memblock);

        pa_resampler_free(resampler);

        goto quit;
    }

    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
        for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) {
            pa_resampler *forth, *back;
            pa_memchunk i, j, k;

            pa_log_debug("=== %s -> %s -> %s -> /2",
                       pa_sample_format_to_string(a.format),
                       pa_sample_format_to_string(b.format),
                       pa_sample_format_to_string(a.format));

            pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
            pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, crossover_freq, method, 0));

            i.memblock = generate_block(pool, &a);
            i.length = pa_memblock_get_length(i.memblock);
            i.index = 0;
            pa_resampler_run(forth, &i, &j);
            pa_resampler_run(back, &j, &k);

            dump_block("before", &a, &i);
            dump_block("after", &b, &j);
            dump_block("reverse", &a, &k);

            pa_memblock_unref(i.memblock);
            pa_memblock_unref(j.memblock);
            pa_memblock_unref(k.memblock);

            pa_resampler_free(forth);
            pa_resampler_free(back);
        }
    }

 quit:
    if (pool)
        pa_mempool_free(pool);

    return ret;
}
示例#23
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    unsigned short revents = 0;
    int ret;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    if (u->core->high_priority)
        pa_make_realtime();

    pa_thread_mq_install(&u->thread_mq);
    pa_rtpoll_install(u->rtpoll);

    for (;;) {
        /* Render some data and write it to the dsp */

        if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) {
            audio_info_t info;
            int err;
            size_t len;

            err = ioctl(u->fd, AUDIO_GETINFO, &info);
            pa_assert(err >= 0);

            /*
             * Since we cannot modify the size of the output buffer we fake it
             * by not filling it more than u->buffer_size.
             */
            len = u->buffer_size;
            len -= u->written_bytes - (info.play.samples * u->frame_size);

            /* The sample counter can sometimes go backwards :( */
            if (len > u->buffer_size)
                len = 0;

            if (info.play.error) {
                pa_log_debug("Solaris buffer underflow!");
                clear_underflow(u);
            }

            len -= len % u->frame_size;

            while (len) {
                void *p;
                ssize_t r;

                if (!u->memchunk.length)
                    pa_sink_render(u->sink, len, &u->memchunk);

                pa_assert(u->memchunk.length);

                p = pa_memblock_acquire(u->memchunk.memblock);
                r = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, NULL);
                pa_memblock_release(u->memchunk.memblock);

                if (r < 0) {
                    if (errno == EINTR)
                        continue;
                    else if (errno != EAGAIN) {
                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
                        goto fail;
                    }
                } else {
                    pa_assert(r % u->frame_size == 0);

                    u->memchunk.index += r;
                    u->memchunk.length -= r;

                    if (u->memchunk.length <= 0) {
                        pa_memblock_unref(u->memchunk.memblock);
                        pa_memchunk_reset(&u->memchunk);
                    }

                    len -= r;
                    u->written_bytes += r;
                }
            }
        }

        /* Try to read some data and pass it on to the source driver */

        if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN))) {
            pa_memchunk memchunk;
            int err;
            size_t l;
            void *p;
            ssize_t r;
            audio_info_t info;

            err = ioctl(u->fd, AUDIO_GETINFO, &info);
            pa_assert(err >= 0);

            if (info.record.error) {
                pa_log_debug("Solaris buffer overflow!");
                clear_overflow(u);
            }

            err = ioctl(u->fd, I_NREAD, &l);
            pa_assert(err >= 0);

            if (l > 0) {
                /* This is to make sure it fits in the memory pool. Also, a page
                   should be the most efficient transfer size. */
                if (l > u->page_size)
                    l = u->page_size;

                memchunk.memblock = pa_memblock_new(u->core->mempool, l);
                pa_assert(memchunk.memblock);

                p = pa_memblock_acquire(memchunk.memblock);
                r = pa_read(u->fd, p, l, NULL);
                pa_memblock_release(memchunk.memblock);

                if (r < 0) {
                    pa_memblock_unref(memchunk.memblock);
                    if (errno != EAGAIN) {
                        pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
                        goto fail;
                    }
                } else {
                    memchunk.index = 0;
                    memchunk.length = r;

                    pa_source_post(u->source, &memchunk);
                    pa_memblock_unref(memchunk.memblock);

                    u->read_bytes += r;

                    revents &= ~POLLIN;
                }
            }
        }

        if (u->fd >= 0) {
            struct pollfd *pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
            pollfd->events =
                ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0);
        }

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->fd >= 0) {
            struct pollfd *pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
                pa_log("DSP shutdown.");
                goto fail;
            }

            revents = pollfd->revents;
        } else
            revents = 0;
    }

fail:
    /* We have to continue processing messages until we receive the
     * SHUTDOWN message */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#24
0
文件: module-oss.c 项目: Thread974/pa
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int write_type = 0, read_type = 0;
    short revents = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    for (;;) {
        int ret;

/*        pa_log("loop");    */

        if (PA_UNLIKELY(u->sink && u->sink->thread_info.rewind_requested))
            pa_sink_process_rewind(u->sink, 0);

        /* Render some data and write it to the dsp */

        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {

            if (u->use_mmap) {

                if ((ret = mmap_write(u)) < 0)
                    goto fail;

                revents &= ~POLLOUT;

                if (ret > 0)
                    continue;

            } else {
                ssize_t l;
                pa_bool_t loop = FALSE, work_done = FALSE;

                l = (ssize_t) u->out_fragment_size;

                if (u->use_getospace) {
                    audio_buf_info info;

                    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
                        pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
                        u->use_getospace = FALSE;
                    } else {
                        l = info.bytes;

                        /* We loop only if GETOSPACE worked and we
                         * actually *know* that we can write more than
                         * one fragment at a time */
                        loop = TRUE;
                    }
                }

                /* Round down to multiples of the fragment size,
                 * because OSS needs that (at least some versions
                 * do) */
                l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size;

                /* Hmm, so poll() signalled us that we can read
                 * something, but GETOSPACE told us there was nothing?
                 * Hmm, make the best of it, try to read some data, to
                 * avoid spinning forever. */
                if (l <= 0 && (revents & POLLOUT)) {
                    l = (ssize_t) u->out_fragment_size;
                    loop = FALSE;
                }

                while (l > 0) {
                    void *p;
                    ssize_t t;

                    if (u->memchunk.length <= 0)
                        pa_sink_render(u->sink, (size_t) l, &u->memchunk);

                    pa_assert(u->memchunk.length > 0);

                    p = pa_memblock_acquire(u->memchunk.memblock);
                    t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
                    pa_memblock_release(u->memchunk.memblock);

/*                     pa_log("wrote %i bytes of %u", t, l); */

                    pa_assert(t != 0);

                    if (t < 0) {

                        if (errno == EINTR)
                            continue;

                        else if (errno == EAGAIN) {
                            pa_log_debug("EAGAIN");

                            revents &= ~POLLOUT;
                            break;

                        } else {
                            pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {

                        u->memchunk.index += (size_t) t;
                        u->memchunk.length -= (size_t) t;

                        if (u->memchunk.length <= 0) {
                            pa_memblock_unref(u->memchunk.memblock);
                            pa_memchunk_reset(&u->memchunk);
                        }

                        l -= t;

                        revents &= ~POLLOUT;
                        work_done = TRUE;
                    }

                    if (!loop)
                        break;
                }

                if (work_done)
                    continue;
            }
        }

        /* Try to read some data and pass it on to the source driver. */

        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {

            if (u->use_mmap) {

                if ((ret = mmap_read(u)) < 0)
                    goto fail;

                revents &= ~POLLIN;

                if (ret > 0)
                    continue;

            } else {

                void *p;
                ssize_t l;
                pa_memchunk memchunk;
                pa_bool_t loop = FALSE, work_done = FALSE;

                l = (ssize_t) u->in_fragment_size;

                if (u->use_getispace) {
                    audio_buf_info info;

                    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
                        pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
                        u->use_getispace = FALSE;
                    } else {
                        l = info.bytes;
                        loop = TRUE;
                    }
                }

                l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size;

                if (l <= 0 && (revents & POLLIN)) {
                    l = (ssize_t) u->in_fragment_size;
                    loop = FALSE;
                }

                while (l > 0) {
                    ssize_t t;
                    size_t k;

                    pa_assert(l > 0);

                    memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);

                    k = pa_memblock_get_length(memchunk.memblock);

                    if (k > (size_t) l)
                        k = (size_t) l;

                    k = (k/u->frame_size)*u->frame_size;

                    p = pa_memblock_acquire(memchunk.memblock);
                    t = pa_read(u->fd, p, k, &read_type);
                    pa_memblock_release(memchunk.memblock);

                    pa_assert(t != 0); /* EOF cannot happen */

/*                     pa_log("read %i bytes of %u", t, l); */

                    if (t < 0) {
                        pa_memblock_unref(memchunk.memblock);

                        if (errno == EINTR)
                            continue;

                        else if (errno == EAGAIN) {
                            pa_log_debug("EAGAIN");

                            revents &= ~POLLIN;
                            break;

                        } else {
                            pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {
                        memchunk.index = 0;
                        memchunk.length = (size_t) t;

                        pa_source_post(u->source, &memchunk);
                        pa_memblock_unref(memchunk.memblock);

                        l -= t;

                        revents &= ~POLLIN;
                        work_done = TRUE;
                    }

                    if (!loop)
                        break;
                }

                if (work_done)
                    continue;
            }
        }

/*         pa_log("loop2 revents=%i", revents); */

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pa_assert(u->fd >= 0);

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
            pollfd->events = (short)
                (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
                 ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0));
        }

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
                pa_log("DSP shutdown.");
                goto fail;
            }

            revents = pollfd->revents;
        } else
            revents = 0;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
示例#25
0
/* Called from input thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
    struct userdata *u;

    pa_source_output_assert_ref(o);
    pa_source_output_assert_io_context(o);
    pa_assert_se(u = o->userdata);

    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
        pa_log("push when no link?");
        return;
    }

    /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */

    /* if uplink sink exists, pull data from there; simplify by using
       same length as chunk provided by source */
    if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
        pa_memchunk tchunk;
        size_t nbytes = chunk->length;
        pa_mix_info streams[2];
        pa_memchunk target_chunk;
        void *target;
        int ch;

        /* Hmm, process any rewind request that might be queued up */
        pa_sink_process_rewind(u->sink, 0);

        /* get data from the sink */
        while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) {
            pa_memchunk nchunk;

            /* make sure we get nbytes from the sink with render_full,
               otherwise we cannot mix with the uplink */
            pa_sink_render_full(u->sink, nbytes, &nchunk);
            pa_memblockq_push(u->sink_memblockq, &nchunk);
            pa_memblock_unref(nchunk.memblock);
        }
        pa_assert(tchunk.length == chunk->length);

        /* move the read pointer for sink memblockq */
        pa_memblockq_drop(u->sink_memblockq, tchunk.length);

        /* allocate target chunk */
        /* this could probably be done in-place, but having chunk as both
           the input and output creates issues with reference counts */
        target_chunk.index = 0;
        target_chunk.length = chunk->length;
        pa_assert(target_chunk.length == chunk->length);

        target_chunk.memblock = pa_memblock_new(o->source->core->mempool,
                                                target_chunk.length);
        pa_assert( target_chunk.memblock );

        /* get target pointer */
        target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock)
                         + target_chunk.index);

        /* set-up mixing structure
           volume was taken care of in sink and source already */
        streams[0].chunk = *chunk;
        for(ch=0;ch<o->sample_spec.channels;ch++)
            streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
        streams[0].volume.channels = o->sample_spec.channels;

        streams[1].chunk = tchunk;
        for(ch=0;ch<o->sample_spec.channels;ch++)
            streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
        streams[1].volume.channels = o->sample_spec.channels;

        /* do mixing */
        pa_mix(streams,                /* 2 streams to be mixed */
               2,
               target,                 /* put result in target chunk */
               chunk->length,          /* same length as input */
               (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */
               NULL,                   /* no volume information */
               FALSE);                 /* no mute */

        pa_memblock_release(target_chunk.memblock);
        pa_memblock_unref(tchunk.memblock); /* clean-up */

        /* forward the data to the virtual source */
        pa_source_post(u->source, &target_chunk);

        pa_memblock_unref(target_chunk.memblock); /* clean-up */

    } else {
        /* forward the data to the virtual source */
        pa_source_post(u->source, chunk);
    }


}
示例#26
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int read_type = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    for (;;) {
        int ret;
        struct pollfd *pollfd;

        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

        /* Try to read some data and pass it on to the source driver */
        if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
            ssize_t l;
            void *p;

            if (!u->memchunk.memblock) {
                u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd));
                u->memchunk.index = u->memchunk.length = 0;
            }

            pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);

            p = pa_memblock_acquire(u->memchunk.memblock);
            l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
            pa_memblock_release(u->memchunk.memblock);

            pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */

            if (l < 0) {

                if (errno == EINTR)
                    continue;
                else if (errno != EAGAIN) {
                    pa_log("Failed to read data from FIFO: %s", pa_cstrerror(errno));
                    goto fail;
                }

            } else {

                u->memchunk.length = (size_t) l;
                pa_source_post(u->source, &u->memchunk);
                u->memchunk.index += (size_t) l;

                if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
                    pa_memblock_unref(u->memchunk.memblock);
                    pa_memchunk_reset(&u->memchunk);
                }

                pollfd->revents = 0;
            }
        }

        /* Hmm, nothing to do. Let's sleep */
        pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0);

        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

        if (pollfd->revents & ~POLLIN) {
            pa_log("FIFO shutdown.");
            goto fail;
        }
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
/* Called from I/O thread context */
static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
    struct userdata *u;
    float *src, *dst;
    unsigned n;
    pa_memchunk tchunk;

    unsigned j, k, l;
    float sum_right, sum_left;
    float current_sample;

    pa_sink_input_assert_ref(i);
    pa_assert(chunk);
    pa_assert_se(u = i->userdata);

    /* Hmm, process any rewind request that might be queued up */
    pa_sink_process_rewind(u->sink, 0);

    while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
        pa_memchunk nchunk;

        pa_sink_render(u->sink, nbytes * u->sink_fs / u->fs, &nchunk);
        pa_memblockq_push(u->memblockq, &nchunk);
        pa_memblock_unref(nchunk.memblock);
    }

    tchunk.length = PA_MIN(nbytes * u->sink_fs / u->fs, tchunk.length);
    pa_assert(tchunk.length > 0);

    n = (unsigned) (tchunk.length / u->sink_fs);

    pa_assert(n > 0);

    chunk->index = 0;
    chunk->length = n * u->fs;
    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);

    pa_memblockq_drop(u->memblockq, n * u->sink_fs);

    src = pa_memblock_acquire_chunk(&tchunk);
    dst = pa_memblock_acquire(chunk->memblock);

    for (l = 0; l < n; l++) {
        memcpy(((char*) u->input_buffer) + u->input_buffer_offset * u->sink_fs, ((char *) src) + l * u->sink_fs, u->sink_fs);

        sum_right = 0;
        sum_left = 0;

        /* fold the input buffer with the impulse response */
        for (j = 0; j < u->hrir_samples; j++) {
            for (k = 0; k < u->channels; k++) {
                current_sample = u->input_buffer[((u->input_buffer_offset + j) % u->hrir_samples) * u->channels + k];

                sum_left += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_left[k]];
                sum_right += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_right[k]];
            }
        }

        dst[2 * l] = PA_CLAMP_UNLIKELY(sum_left, -1.0f, 1.0f);
        dst[2 * l + 1] = PA_CLAMP_UNLIKELY(sum_right, -1.0f, 1.0f);

        u->input_buffer_offset--;
        if (u->input_buffer_offset < 0)
            u->input_buffer_offset += u->hrir_samples;
    }

    pa_memblock_release(tchunk.memblock);
    pa_memblock_release(chunk->memblock);

    pa_memblock_unref(tchunk.memblock);

    return 0;
}