Beispiel #1
0
int
codec_encode(codec_state *cs,
             coded_unit  *in_native,
             coded_unit  *cu)
{
        uint16_t    ifs, fmt;
        int        success;

        assert(cs        != NULL);
        assert(in_native != NULL);
        assert(cu        != NULL);

        assert(codec_is_native_coding(in_native->id));
        assert (in_native->state == NULL);
#ifdef DEBUG
        {
                const codec_format_t *cf = codec_get_format(cs->id);
                assert (cf->format.bytes_per_block == in_native->data_len);
        }
#endif
        cu->id = cs->id;
        ifs = CODEC_GET_IFS_INDEX(cu->id);
        fmt = CODEC_GET_FMT_INDEX(cu->id);

        xmemchk();
        success = codec_table[ifs].cx_encode(fmt, cs->state, (sample*)in_native->data, cu);
        xmemchk();

        return success;
}
int main() {
	static rtp_packet	*pp;
	static pktbuf_t		*pb;
	int32_t			i, j, n,ts;

	if (pktbuf_create(&pb, PKTBUF_SIZE) == 0) {
		printf("Failed to create buffer\n");
		exit(-1);
	}
	xmemchk();
	for(i = 0; i < 100000; i++) {
		n = lrand48() % 16;
		for(j = 0; j <= n; j++) {
			pp = (rtp_packet*)xmalloc(sizeof(rtp_packet));
			pp->ts = ts ++;
			add_thing(pb, pp);
		}
		n = lrand48() % 16;
		for(j = 0; j < n; j++) {
			remove_thing(pb);
		}
	}
	pktbuf_destroy(&pb);
	xmemdmp();
	printf("Okay\n");
	return 0;
}
Beispiel #3
0
static void
sinc_downsample_mono(struct s_filter_state *fs,
                      sample *src, int src_len,
                      sample *dst, int dst_len)
{
        int32_t *hc, *he, t, work_len;

        sample *work_buf, *ss, *sc, *de, *d;

        work_len = src_len + fs->taps;
        work_buf = (sample*)block_alloc(work_len * sizeof(sample));

        /* Get samples into work_buf */
        memcpy(work_buf, fs->hold_buf, fs->hold_bytes);
        memcpy(work_buf + fs->hold_bytes / sizeof(sample), src, src_len * sizeof(sample));

        /* Save last samples in src into hold_buf for next time */
        if (src_len >= (int)(fs->hold_bytes / sizeof(sample))) {
                memcpy(fs->hold_buf,
                       src + src_len - fs->hold_bytes / sizeof(sample),
                       fs->hold_bytes);
        } else {
                /* incoming chunk was shorter than hold buffer */
                memmove(fs->hold_buf,
                        fs->hold_buf + src_len,
                        fs->hold_bytes - src_len * sizeof(sample));
                memcpy(fs->hold_buf + fs->hold_bytes / sizeof(sample) - src_len,
                       src,
                       src_len * sizeof(sample));
        }

        d  = dst;
        de = dst + dst_len;
        sc = ss = work_buf;
        he      = fs->filter + fs->taps;

        while (d != de) {
                t = 0;
                hc = fs->filter;
                while(hc < he) {
                        t += (*sc) * (*hc);
                        sc++;
                        hc++;
                }
                t = t / SINC_SCALE;
                clip16(t);
                *d = (sample) t;

                d++;
                ss += fs->scale;
                sc = ss;
        }

        assert(d  == dst + dst_len);
        block_free(work_buf, work_len * sizeof(sample));
        xmemchk();
}
static void 
add_thing(pktbuf_t *pb, rtp_packet *pp) {
	pktbuf_enqueue(pb, pp);
	if (buf_est < PKTBUF_SIZE) {
		buf_est++;
	}
	assert(buf_est == pktbuf_get_count(pb));
	xmemchk();
}
Beispiel #5
0
static uint32_t
make_pdu(struct s_pb_iterator *pbi,
         uint32_t               upp,
         codec_id_t            cid,
         channel_data         *out)
{
        struct s_pb_iterator *p;
        uint32_t        i, j, md_len, used;
        u_char        *md_get;
        media_data    *md;
        timestamp_t           playout;
        int            success;

        pb_iterator_dup(&p, pbi);

        used = 0;
        for (i = 0; i < upp; i++) {
                success = pb_iterator_get_at(p, &md_get, &md_len, &playout);
                md = (media_data*)md_get;
                assert(success); /* We could rewind this far so must be able to get something! */

                /* Find first compatible coding */
                for(j = 0; j < md->nrep && md->rep[j]->id != cid; j++);
                if (j == md->nrep) {
                        /* could not find coding */
                        debug_msg("coding not found\n");
                        break;
                }

                if (i == 0 && md->rep[j]->state != NULL) {
                        /* This is first unit in block so we want state */
                        assert(out->elem[used]->data == NULL);
                        out->elem[used]->data     = md->rep[j]->state;
                        out->elem[used]->data_len = md->rep[j]->state_len;
                        md->rep[j]->state     = NULL;
                        md->rep[j]->state_len = 0;
                        used++;
                }
                assert(used < out->nelem);
                assert(out->elem[used]->data == NULL);
                out->elem[used]->data     = md->rep[j]->data;
                out->elem[used]->data_len = md->rep[j]->data_len;
                md->rep[j]->data     = NULL;
                md->rep[j]->data_len = 0;
                md->rep[j]->id       = 0; /* nobble this unit since we have taken it's data */
                used++;
                assert(used <= out->nelem);

                pb_iterator_advance(p);
        }

        pb_iterator_destroy(pb_iterator_get_playout_buffer(pbi), &p);
        xmemchk();

        return used;
}
static void
remove_thing(pktbuf_t *pb) {
	rtp_packet *pp;
	if (pktbuf_dequeue(pb, &pp)) {
		xfree(pp);
		buf_est --;
	}
	assert(buf_est == pktbuf_get_count(pb));
	xmemchk();
}
Beispiel #7
0
void
sinc_shutdown (void)
{
        int i;

        xmemchk();
        for (i = SINC_MIN_CHANGE; i < SINC_MAX_CHANGE; i++) {
                xfree(upfilter[i]);
                xfree(downfilter[i]);
        }
}
Beispiel #8
0
/**
 * xfree:
 * @p: pointer to block to freed.
 * 
 * Free block of memory.  Semantically equivalent to free(), but
 * checks for bounds overruns in @p and tidies up state associated
 * additional functionality.
 *
 * Must be used to free memory allocated with xmalloc(), xrealloc(),
 * and xstrdup().
 **/
void 
xfree(void *p)
{
        alloc_blk  *m;
        chk_header *ch;
        uint32_t size, delta, magic, idx;
        
	if (p == NULL) {
		printf("ERROR: Attempt to free NULL pointer!\n");
		abort();
	}
        ch = ((chk_header*)p) - 1;

        printf("free at %p\n", ch);
        /* Validate entry  */
        if (chk_header_okay(ch) == FALSE) {
                printf("ERROR: Freeing corrupted block\n");
                abort();
        }

        /* Locate in table */
        m = mem_item_find(ch->key);
        if (m == NULL) {
                printf("ERROR: Freeing unallocated or already free'd block\n");
                abort();
        }

        /* Trash memory of allocated block, maybe noticed by apps when    
         * deref'ing free'd */
        size  = ch->size + sizeof(chk_header) + MAGIC_MEMORY_SIZE;
        magic = MAGIC_MEMORY;
        p     = (uint8_t*)ch;
        while (size > 0) {
                delta = min(size, 4);
                memcpy(p, &magic, delta);
                (uint8_t*)p += delta;
                size        -= delta;
        }

        /* Free memory     */
        free(ch);
        free(m->filen);

        /* Remove from table */
        idx = m - mem_item;
        if (naddr - idx > 0) {
                memmove(&mem_item[idx], 
                        &mem_item[idx + 1], 
                        (naddr - idx - 1) * sizeof(alloc_blk));
        }
        naddr--;
        xmemchk();
}
Beispiel #9
0
int
codec_decode(codec_state *cs,
             coded_unit  *in,
             coded_unit  *out)
{
        const codec_format_t *cf;
        codec_id_t           id;
        uint16_t              ifs, fmt, rate, channels;
        int                  success;

        assert(cs  != NULL);
        assert(out != NULL);
        assert(in  != NULL);
        
        id = cs->id;
        assert(in->id == cs->id);
        assert(codec_is_native_coding(in->id) == FALSE);

        ifs = CODEC_GET_IFS_INDEX(id);
        fmt = CODEC_GET_FMT_INDEX(id);

        /* Setup outgoing data block */
        cf = codec_get_format(id);
        assert(out->state == NULL);
        assert(out->data  == NULL);
        rate     = (uint16_t)cf->format.sample_rate;
        channels = (uint16_t)cf->format.channels;
        out->id       = codec_get_native_coding(rate, channels);
        out->data_len = cf->format.bytes_per_block;
        out->data     = (u_char*)block_alloc(out->data_len);

        /* Decode */
        xmemchk();
        success = codec_table[ifs].cx_decode(fmt, cs->state, in, (sample*)out->data);
        xmemchk();

        return success;
}
Beispiel #10
0
void
asarray_destroy(asarray **ppa)
{
    asarray    *pa;
    const char *key;

    pa = *ppa;
    assert(pa != NULL);

    while ((key = asarray_get_key_no(pa, 0)) != NULL) {
        asarray_remove(pa, key);
    }

    xfree(pa);
    *ppa = NULL;
    xmemchk();
}
Beispiel #11
0
static int
audio_device_write(session_t *sp, sample *buf, int dur)
{
        const audio_format *ofmt = audio_get_ofmt(sp->audio_device);
        int len;

        assert(dur >= 0);

        if (sp->out_file) {
                snd_write_audio(&sp->out_file, buf, (uint16_t)(dur * ofmt->channels));
        }

        len =  audio_write(sp->audio_device, buf, dur * ofmt->channels);

        xmemchk();
        
        return len;
}
Beispiel #12
0
static void
oss_pair_devices(void)
{
	/* This function scans through the devices[] array, merging pairs   */
	/* of half-duplex audio devices into a single full duplex device    */
	/* entry (with one device providing half-duplex recording, and the  */
	/* other providing half-duplex playback).                           */
	int	i, j;

	if (num_devices < 2) {
		return;
	}

	for (i = 0; i < num_devices - 1; i++) {
		if ((devices[i].duplex == OSS_DUPLEX_HALF) && (devices[i+1].duplex == OSS_DUPLEX_HALF)) {
			if (oss_test_device_pair(i, i+1)) {
				debug_msg("Combining %s and %s\n", devices[i].audio_rdev, devices[i+1].audio_wdev);
				memcpy(devices[i].audio_wdev, devices[i+1].audio_wdev, 16);
				if (strlen(devices[i+1].mixer_wdev) != 0) {
					debug_msg("Second mixer is valid\n");
					memcpy(devices[i].mixer_wdev, devices[i+1].mixer_wdev, 16);
				} else {
					debug_msg("Second mixer is invalid - assuming first is okay\n");
				}
				devices[i].audio_rfd = -1;
				devices[i].audio_wfd = -1;
				devices[i].mixer_rfd = -1;
				devices[i].mixer_wfd = -1;
				devices[i].duplex    = OSS_DUPLEX_PAIR;
				devices[i].rec_mask  = devices[i+1].rec_mask;
				xfree(devices[i+1].name);
				/* Move the rest of the device table up... */
				for (j = i+1; j < (num_devices - 1); j++) {
					devices[j] = devices[j+1];
				}
				xmemchk();
				num_devices--;
				i--;
			} else {
				debug_msg("Cannot pair %s and %s\n", devices[i].audio_rdev, devices[i+1].audio_wdev);
			}
		}
	}
}
Beispiel #13
0
static void setting_save_int(const char *name, const long val)
{
#ifndef WIN32
        char sval[12];
	sprintf(sval, "%ld", val);
        asarray_add(aa, name, sval);
        xmemchk();
#else
        LONG status;
        char buffer[SETTINGS_BUF_SIZE];

        status = RegSetValueEx(cfgKey, name, 0, REG_DWORD, &(char)val, sizeof(val));
        if (status != ERROR_SUCCESS) {
                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, SETTINGS_BUF_SIZE, NULL);
                debug_msg("Unable to save setting %s: %s\n", name, buffer);
                abort();
        }
#endif
}
Beispiel #14
0
static int
codec_state_store_expand(codec_state_store_t *css)
{
    int i;
    codec_state **buffer;

    /* This should very very rarely get called */

    buffer = (codec_state**)xmalloc((css->allocated + CODEC_STORE_UNIT_SIZE) * sizeof(codec_state*));

    memset(buffer + CODEC_STORE_UNIT_SIZE*sizeof(codec_state*), 0,
           CODEC_STORE_UNIT_SIZE*sizeof(codec_state*));

    for(i = 0; i < css->allocated; i++) {
        buffer[i] = css->buffer[i];
    }
    xmemchk();
    xfree(css->buffer);
    css->buffer     = buffer;
    css->allocated += CODEC_STORE_UNIT_SIZE;
    return TRUE;
}
Beispiel #15
0
/* This function needs to be modified to return some indication of how well
 * or not we are doing.                                                    
 */
int
audio_rw_process(session_t *spi, session_t *spo,  struct s_mixer *ms)
{
        uint32_t cushion_size, read_dur;
        struct s_cushion_struct *c;
	int	trailing_silence, new_cushion, cushion_step, diff;
        const audio_format* ofmt;
	sample	*bufp;

	session_validate(spi);
	session_validate(spo);

        c = spi->cushion;

	if ((read_dur = tx_read_audio(spi->tb)) <= 0) {
		return 0;
	} else {
                if (!spi->audio_device) {
                        /* no device means no cushion */
                        return read_dur;
                }
	}

        xmemchk();

	/* read_dur now reflects the amount of real time it took us to get
	 * through the last cycle of processing. 
         */

	if (spo->lecture == TRUE && spo->auto_lecture == 0) {
                cushion_update(c, read_dur, CUSHION_MODE_LECTURE);
	} else {
                cushion_update(c, read_dur, CUSHION_MODE_CONFERENCE);
	}

	/* Following code will try to achieve new cushion size without
	 * messing up the audio...
	 * First case is when we are in trouble and the output has gone dry.
	 * In this case we write out a complete new cushion with the desired
	 * size. We do not care how much of it is going to be real audio and
	 * how much silence so long as the silence is at the head of the new
	 * cushion. If the silence was at the end we would be creating
	 * another silence gap...
	 */
        cushion_size = cushion_get_size(c);
        ofmt         = audio_get_ofmt(spi->audio_device);

	if (cushion_size < read_dur) {
                debug_msg("catch up! read_dur(%d) > cushion_size(%d)\n",
                        read_dur,
                        cushion_size);
		/* Use a step for the cushion to keep things nicely rounded  */
                /* in the mixing. Round it up.                               */
                new_cushion = cushion_use_estimate(c);
                assert(new_cushion >= 0 && new_cushion < 100000);

                /* The mix routine also needs to know for how long the       */
                /* output went dry so that it can adjust the time.           */
                mix_new_cushion(ms, 
                                cushion_size, 
                                new_cushion, 
                                (read_dur - cushion_size), 
                                &bufp);
                audio_device_write(spo, bufp, new_cushion);
                /* We've blocked for this long for whatever reason           */
                cushion_size    = new_cushion;
        } else {
                trailing_silence = mix_get_audio(ms, read_dur * ofmt->channels, &bufp);
                cushion_step = cushion_get_step(c);
                diff  = 0;
                if (trailing_silence > cushion_step) {
                        /* Check whether we need to adjust the cushion */
                        diff = cushion_diff_estimate_size(c);
                        if (abs(diff) < cushion_step) {
                                diff = 0;
                        }
                } 
                
                /* If diff is less than zero then we must decrease the */
                /* cushion so loose some of the trailing silence.      */
                if (diff < 0 && 
                    mix_active(ms) == FALSE && 
                    source_list_source_count(spi->active_sources) == 0) {
                        /* Only decrease cushion if not playing anything out */
                        uint32_t old_cushion;
                        old_cushion = cushion_get_size(c);
                        if (read_dur > (unsigned)cushion_step) {
                                cushion_step_down(c);
                                if (cushion_get_size(c) != old_cushion) {
                                        debug_msg("Decreasing cushion\n");
                                        read_dur -= cushion_step;
                                }
                        }
                }
                assert(read_dur < 0x7fffffff);
                audio_device_write(spo, bufp, read_dur);
                /*
                 * If diff is greater than zero then we must increase the
                 * cushion so increase the amount of trailing silence.
                 */
                if (diff > 0) {
                        assert(cushion_step > 0);
                        audio_device_write(spo, zero_buf, cushion_step);
                        cushion_step_up(c);
                        debug_msg("Increasing cushion.\n");
                }
        }
        return (read_dur);
}
Beispiel #16
0
static int
layered_decoder_reorganise(channel_data *in, struct s_pb *out, timestamp_t playout)
{
        const codec_format_t *cf;
        codec_id_t            id;
        coded_unit           *cu;
        u_char               *p[LAY_MAX_LAYERS], *end;
        uint32_t               hdr32, data_len;
        uint8_t hdrpt, i;
        uint16_t len[LAY_MAX_LAYERS], mrk[LAY_MAX_LAYERS];
        media_data           *m;
        timestamp_t                  playout_step;

        media_data_create(&m, 1);
        assert(m->nrep == 1);

        if(in->nelem > LAY_MAX_LAYERS) {
                debug_msg("Too many layers to reorganise\n");
		goto done;
        }


       /* Since layer_decoder_peek checks all the headers, we can
        * assume they are OK. We still need to check that they match
        * up, however, i.e. that all the layers are intact, and that
        * they are all using the same codec. Layers need to be sorted
        * into order as well. We use the markers to determine how to
        * join the layers together into one media_data, and then get
        * out of here.
        */

        p[0] = in->elem[0]->data;
        hdr32 = ntohl(*(uint32_t*)p[0]);
        if(hdr32 & LAY_HDR32_PAT) {
                hdrpt = (uint8_t)(LAY_HDR32_GET_PT(hdr32));
                mrk[0] = (uint8_t)(LAY_HDR32_GET_MRK(hdr32));
                len[0] = (uint8_t)(LAY_HDR32_GET_LEN(hdr32));
                p[0] += 4;
        }
        else {
                debug_msg("Invalid layered header\n");
		goto done;
        }

        for(i=1; i<in->nelem; i++) {
                p[i] = in->elem[i]->data;

                hdr32 = ntohl(*(uint32_t*)p[i]);
                if(hdr32 & LAY_HDR32_PAT) {
                        if(hdrpt != (uint8_t)(LAY_HDR32_GET_PT(hdr32))) {
                                debug_msg("layered headers do not match!\n");
                                goto done;
                        }
                        mrk[i] = (uint16_t)(LAY_HDR32_GET_MRK(hdr32));
                        len[i] = (uint16_t)(LAY_HDR32_GET_LEN(hdr32));
                        p[i] += 4;
                }
                else {
                        debug_msg("Invalid layered header\n");
                        goto done;
                }
        }
        end  = in->elem[in->nelem-1]->data + in->elem[in->nelem-1]->data_len;

        /* if layers missing say so */
        if(in->nelem!=LAY_MAX_LAYERS) {
                debug_msg("Not all layers arrived:\n");
                for(i=0; i<in->nelem; i++) {
                        debug_msg("marker[%d] = %d\n", i, mrk[i]);
                }
        }

        /* Everything matches, so we'll use the first layer's details */

        cu = (coded_unit*)block_alloc(sizeof(coded_unit));
        memset(cu, 0, sizeof(coded_unit));

        id = codec_get_by_payload(hdrpt);
        if (codec_id_is_valid(id) == FALSE) {
                debug_msg("Layered channel coder - codec_id not recognised.\n");
                goto fail;
        }
        cf = codec_get_format(id);
        assert(cf != NULL);

       /* Do first unit separately as that may have state */
        if (cf->mean_per_packet_state_size) {
                cu->state_len = cf->mean_per_packet_state_size;
                cu->state     = (u_char*)block_alloc(cu->state_len);
                memcpy(cu->state, p[0], cf->mean_per_packet_state_size);
                for(i=0; i<in->nelem; i++)
                        p[i] += cf->mean_per_packet_state_size;
        }

        data_len = codec_peek_frame_size(id, p[0], (uint16_t)(len[0]));
        m->rep[0]->id = cu->id = id;
        cu->data = (u_char*)block_alloc(data_len);
        cu->data_len = (uint16_t)data_len;
        memset(cu->data, 0, data_len);

        /* join the layers up here */

        for(i=0; i<in->nelem; i++) {
                memcpy(cu->data + mrk[i], p[i], len[i]);
                p[i] += len[i];
        }

        codec_combine_layer(id, cu, m->rep[0], in->nelem, mrk);

        if (cu->state_len) {
                block_free(cu->state, cu->state_len);
                cu->state     = NULL;
                cu->state_len = 0;
        }
        assert(cu->state_len == 0);
        if (cu->data_len) {
                block_free(cu->data, cu->data_len);
                cu->data     = NULL;
                cu->data_len = 0;
        }
        assert(cu->data_len == 0);

        if (pb_add(out, (u_char *)m, sizeof(media_data), playout) == FALSE) {
                debug_msg("layered decode failed\n");
                goto fail;
        }

        /* Now do other units which do not have state*/
        playout_step = ts_map32(cf->format.sample_rate, codec_get_samples_per_frame(id));
        while(p[in->nelem - 1] < end) {
                playout = ts_add(playout, playout_step);
                media_data_create(&m, 1);
                m->rep[0]->id = id;
                assert(m->nrep == 1);

                cu->data            = (u_char*)block_alloc(data_len);
                cu->data_len        = (uint16_t)data_len;
                memset(cu->data, 0, data_len);

                for(i=0; i<in->nelem; i++) {
                        memcpy(cu->data + mrk[i], p[i], len[i]);
                        p[i] += len[i];
                }

                codec_combine_layer(id, cu, m->rep[0], in->nelem, mrk);

                block_free(cu->data, cu->data_len);
                cu->data     = 0;
                cu->data_len = 0;

                if (pb_add(out, (u_char *)m, sizeof(media_data), playout) == FALSE) {
                        debug_msg("layered decode failed\n");
                        goto fail;
                }
        }
        assert(p[in->nelem - 1] == end);

        block_free(cu, sizeof(coded_unit));
	channel_data_destroy(&in, sizeof(channel_data));
        xmemchk();
        return TRUE;

fail:
        if (cu->state) {
                block_free(cu->state, cu->state_len);
                cu->state     = 0;
                cu->state_len = 0;
        }
        assert(cu->state_len == 0);
        if (cu->data) {
                block_free(cu->data, cu->data_len);
                cu->data     = 0;
                cu->data_len = 0;
        }
        assert(cu->data_len == 0);
        block_free(cu, sizeof(coded_unit));
done:
	media_data_destroy(&m, sizeof(media_data));
	channel_data_destroy(&in, sizeof(channel_data));
        xmemchk();
        return FALSE;
}
Beispiel #17
0
int
layered_encoder_encode (u_char      *state,
                        struct s_pb *in,
                        struct s_pb *out,
                        uint32_t      upp)
{
        uint32_t                   m_len;
        timestamp_t                playout;
        struct      s_pb_iterator *pi;
        u_char                    *m_get;
        media_data                *m;
        lay_state                 *le = (lay_state*)state;

        assert(upp != 0 && upp <= MAX_UNITS_PER_PACKET);

        pb_iterator_create(in, &pi);
        pb_iterator_advance(pi); /* Move to first element */

        while(pb_iterator_detach_at(pi, &m_get, &m_len, &playout)) {
                /* Remove element from playout buffer - it belongs to
                 * the layered encoder now.
                 */
                m = (media_data*)m_get;
                assert(m != NULL);

                if (le->nelem == 0) {
                        /* If it's the first unit make a note of it's
                         *  playout */
                        le->playout = playout;
                        if (m->nrep == 0) {
                                /* We have no data ready to go and no data
                                 * came off on incoming queue.
                                 */
                                media_data_destroy(&m, sizeof(media_data));
                                continue;
                        }
                } else {
                        /* Check for early send required:
                         * (a) if this unit has no media respresentations
                         *     e.g. end of talkspurt.
                         * (b) codec type of incoming unit is different
                         *     from what is on queue.
                         */
                        if (m->nrep == 0) {
                                layered_encoder_output(le, out);
                                media_data_destroy(&m, sizeof(media_data));
                                continue;
                        } else if (m->rep[0]->id != le->codec_id) {
                                layered_encoder_output(le, out);
                        }
                }

                assert(m_len == sizeof(media_data));

                le->codec_id = m->rep[0]->id;
                le->elem[le->nelem] = m;
                le->nelem++;

                if (le->nelem >= (uint32_t)upp) {
                        layered_encoder_output(le, out);
                }
        }

        pb_iterator_destroy(in, &pi);

        xmemchk();

        return TRUE;
}
Beispiel #18
0
static void
sinc_downsample_stereo(struct s_filter_state *fs,
                       sample *src, int src_len,
                       sample *dst, int dst_len)
{
        int32_t *hc, *he, t0, t1, work_len;

        sample *work_buf, *ss, *sc, *d, *de;

/*        work_len = src_len + 2 * (fs->taps - fs->scale); */
	work_len = src_len + fs->hold_bytes / sizeof(sample);
        work_buf = (sample*)block_alloc(work_len * sizeof(sample));

        /* Get samples into work_buf */
        memcpy(work_buf, fs->hold_buf, fs->hold_bytes);
	xmemchk();
        memcpy(work_buf + fs->hold_bytes / sizeof(sample), src, src_len * sizeof(sample));
	xmemchk();

        /* Save last samples in src into hold_buf for next time */
        if (src_len >= (int)(fs->hold_bytes / sizeof(sample))) {
                memcpy(fs->hold_buf,
                       src + src_len - fs->hold_bytes / sizeof(sample),
                       fs->hold_bytes);
        } else {
                /* incoming chunk was shorter than hold buffer */
                memmove(fs->hold_buf,
                        fs->hold_buf + src_len,
                        fs->hold_bytes - src_len * sizeof(sample));
                memcpy(fs->hold_buf + fs->hold_bytes / sizeof(sample) - src_len,
                       src,
                       src_len * sizeof(sample));
        }

        d  = dst;
        de = dst + dst_len;
        sc = ss = work_buf;

        he = fs->filter + fs->taps;

        while (d < de) {
                t0 = t1 = 0;
                hc = fs->filter;
                sc = ss;
                while(hc < he) {
                        t0 += (*sc) * (*hc);
                        sc++;
                        t1 += (*sc) * (*hc);
                        sc++;
                        hc++;
                }

                t0 = t0 / SINC_SCALE;
                if (t0 > 32767) {
                        *d = 32767;
                } else if (t0 < -32768) {
                        *d = -32768;
                } else {
                        *d = (sample) t0;
                }
                d++;

                t1 = t1 / SINC_SCALE;
                if (t1 > 32767) {
                        *d = 32767;
                } else if (t1 < -32768) {
                        *d = -32768;
                } else {
                        *d = (sample) t1;
                }
                d++;

                ss += fs->scale * 2;
        }

        assert(d  == dst + dst_len);
        block_free(work_buf, work_len * sizeof(sample));
        xmemchk();
}
Beispiel #19
0
static void
redundancy_decoder_output(channel_unit *chu, struct s_pb *out, timestamp_t playout)
{
        const codec_format_t *cf;
        codec_id_t cid;
        u_char  *hp, *dp, *de, ppt, bpt;
        uint32_t hdr32, blen, boff;
        timestamp_t ts_max_off, ts_blk_off, this_playout;

        hp = dp = chu->data;
        de = chu->data + chu->data_len;

        /* move data pointer past header */
        while (ntohl(*((uint32_t*)dp)) & RED_HDR32_PAT) {
                dp += 4;
        }

        if (dp == hp) {
                debug_msg("Not a redundant block\n");
                return;
        }

        /* At this point dp points to primary payload type.
         * This is a most useful quantity... */
        ppt   = *dp;
        dp += 1;
        assert(dp < de);

        /* Max offset should be in first header.  Want max offset
         * as we nobble timestamps to be:
         *              playout + max_offset - this_offset
         */

        cid   = codec_get_by_payload(ppt);
        if (codec_id_is_valid(cid) == FALSE) {
                debug_msg("Primary not recognized.\n");
                return;
        }

        cf = codec_get_format(cid);
        assert(cf != NULL);

        hdr32 = ntohl(*(uint32_t*)hp);
        ts_max_off = ts_map32(cf->format.sample_rate, RED_HDR32_GET_OFF(hdr32));
	blen = 0;

        while (hdr32 & RED_HDR32_PAT) {
                boff  = RED_HDR32_GET_OFF(hdr32);
                blen  = RED_HDR32_GET_LEN(hdr32);
                bpt   = (u_char)RED_HDR32_GET_PT(hdr32);

                /* Calculate playout point = playout + max_offset - offset */
                ts_blk_off = ts_map32(cf->format.sample_rate, boff);
                this_playout = ts_add(playout, ts_max_off);
                this_playout = ts_sub(this_playout, ts_blk_off);
                hp += 4; /* hdr */
                red_split_unit(ppt, bpt, dp, blen, this_playout, out);
                xmemchk();
                dp += blen;
                hdr32 = ntohl(*(uint32_t*)hp);
        }

        this_playout = ts_add(playout, ts_max_off);
        hp += 1;
        blen = (uint32_t) (de - dp);
        red_split_unit(ppt, ppt, dp, blen, this_playout, out);
        xmemchk();
}
Beispiel #20
0
static channel_data *
redundancy_encoder_output(red_enc_state *re, uint32_t upp)
{
        struct s_pb_iterator *pbm;
        channel_data         *cd_coded[RED_MAX_LAYERS], *cd_out;
        uint32_t               offset ;
        int                   i, j, layers, success = 0,  used = 0;

        pbm = re->media_pos;
        pb_iterator_ffwd(pbm);

        /*** Stage 1: Packing coded audio units ******************************/

        /* Rewind iterator to start of first pdu */
        for(i = 1; (uint32_t)i < upp; i++) {
                success = pb_iterator_retreat(pbm);
                assert(success);
        }

        offset = 0;
        layers = 0;
        for (i = 0; (uint32_t)i < re->n_layers; i++) {
                if (re->units_ready <= re->layer[i].pkts_off * upp) {
                        break;
                }
                /* Move back to start of this layer */
                while (offset < re->layer[i].pkts_off * upp) {
                        success = pb_iterator_retreat(pbm);
                        if (success == FALSE) break;
                        offset++;
                }
                xmemchk();
                /* need upp data elements + 1 for state */
                channel_data_create(&cd_coded[i], upp + 1);
                success = make_pdu(pbm, upp, re->layer[i].cid, cd_coded[i]);
                /* make_pdu may fail because coding not available */
                if (success == FALSE) {
                        channel_data_destroy(&cd_coded[i], sizeof(channel_data));
                        break;
                }
                layers++;
        }

#ifdef DEBUG_REDUNDANCY
        debug_msg("end of data collection\n");
#endif /* DEBUG_REDUNDANCY */
        assert(layers != 0);

        /* Create channel_data unit that will get output */
        channel_data_create(&cd_out, layers * (upp + 1) + re->n_layers);

        /*** Stage 2: Packing redundancy headers *****************************/
        used = 0;
        if ((uint32_t)layers != re->n_layers) {
                /* Add max offset if we didn't make all units */
                add_hdr(cd_out->elem[used],
                        RED_EXTRA,
                        re->layer[re->n_layers - 1].cid,
                        re->layer[re->n_layers - 1].pkts_off * upp,
                        0);
                used++;
        }

        i = layers - 1;
        while (i > 0) {
                add_hdr(cd_out->elem[used],
                        RED_EXTRA,
                        re->layer[re->n_layers - 1].cid,
                        re->layer[re->n_layers - 1].pkts_off * upp,
                        channel_data_bytes(cd_coded[i]));
                used++;
                i--;
        }

        add_hdr(cd_out->elem[used],
                RED_PRIMARY,
                re->layer[0].cid,
                re->layer[0].pkts_off * upp,
                0);
        used++;

        /*** Stage 3: Transfering coded units into output unit ***************/

        for(i = layers - 1; i >= 0; i--) {
                for (j = 0; j < cd_coded[i]->nelem && cd_coded[i]->elem[j]->data != NULL; j++) {
                        cd_out->elem[used]->data       =  cd_coded[i]->elem[j]->data;
                        cd_out->elem[used]->data_len   =  cd_coded[i]->elem[j]->data_len;
                        cd_coded[i]->elem[j]->data     = NULL;
                        cd_coded[i]->elem[j]->data_len = 0;
                        used++;
                        assert(used <= cd_out->nelem);
                }
                assert(used <= cd_out->nelem);
                channel_data_destroy(&cd_coded[i], sizeof(channel_data));
        }

        pb_iterator_audit(pbm, re->history); /* Clear old rubbish */

        return  cd_out;
}
Beispiel #21
0
void settings_load_early(session_t *sp)
{
	/* FIXME: This needs to be updated for the transcoder */
	char				*name, *param, *primary_codec, *port, *silence;
	int				 freq, chan, mute;
        uint32_t                         i, n, success, device_exists;
	const cc_details_t              *ccd;
	const audio_device_details_t    *add = NULL;
        const audio_port_details_t 	*apd = NULL;
        const converter_details_t       *cod = NULL;
        const repair_details_t          *r   = NULL;
        codec_id_t                       cid;

	load_init();		/* Initial settings come from the common prefs file... */
        init_part_two();	/* Switch to pulling settings from the RAT specific prefs file... */

	if (sp->mode == AUDIO_TOOL) {
		name = setting_load_str("audioDevice", "No Audio Device");
	} else {
		name = (char *) xmalloc(20);
		sprintf(name, "Transcoder Port %d", sp->id+1);
	}
        /* User may not have a (valid) audio device entry in the */
        /* settings file, or have "No Audio Device" there.  In   */
        /* either case try to use first available device, if     */
        /* it's in use we'll fallback to dummy device anyway.    */

	device_exists = FALSE;
	n = (int)audio_get_device_count();
	for(i = 0; i < n; i++) {
		add = audio_get_device_details(i);
		if (strcmp(add->name, name) == 0) {
			device_exists = TRUE;
			break;
		}
	}

        if (strcmp(name, "No Audio Device") == 0 || device_exists == FALSE) {
		add = audio_get_device_details(0);
        }

        audio_device_register_change_device(sp, add->descriptor);

	freq = setting_load_int("audioFrequency", 8000);
	chan = setting_load_int("audioChannelsIn", 1);
	primary_codec = setting_load_str("audioPrimary", "GSM");

        cid  = codec_get_matching(primary_codec, (uint16_t)freq, (uint16_t)chan);
        if (codec_id_is_valid(cid) == FALSE) {
                /* Codec name is garbage...should only happen on upgrades */
                cid = codec_get_matching("GSM", (uint16_t)freq, (uint16_t)chan);
        }

        audio_device_register_change_primary(sp, cid);
        audio_device_reconfigure(sp);

        port = setting_load_str("audioOutputPort", "Headphone");
        n    = audio_get_oport_count(sp->audio_device);
        for(i = 0; i < n; i++) {
                apd = audio_get_oport_details(sp->audio_device, i);
                if (!strcasecmp(port, apd->name)) {
                        break;
                }
        }
        audio_set_oport(sp->audio_device, apd->port);

        port = setting_load_str("audioInputPort", "Microphone");
        n    = audio_get_iport_count(sp->audio_device);
        for(i = 0; i < n; i++) {
                apd = audio_get_iport_details(sp->audio_device, i);
                if (!strcasecmp(port, apd->name)) {
                        break;
                }
        }
        audio_set_iport(sp->audio_device, apd->port);

        audio_set_ogain(sp->audio_device, setting_load_int("audioOutputGain", 75));
        audio_set_igain(sp->audio_device, setting_load_int("audioInputGain",  75));
        tx_igain_update(sp->tb);
	name  = setting_load_str("audioChannelCoding", "None");
        param = setting_load_str("audioChannelParameters", "None");

        do {
                n    = channel_get_coder_count();
                for (i = 0; i < n; i++ ) {
                        ccd = channel_get_coder_details(i);
                        if (strcmp(ccd->name, name) == 0) {
                                if (sp->channel_coder) {
                                        channel_encoder_destroy(&sp->channel_coder);
                                }
                                channel_encoder_create(ccd->descriptor, &sp->channel_coder);
                                break;
                        }
                }
                success = channel_encoder_set_parameters(sp->channel_coder, param);
                if (success == 0) {
                        /* Could not set parameters for channel coder, fall back to "None" */
                        name = "None";
                        param = "";
                }
        } while (success == 0);

	channel_encoder_set_units_per_packet(sp->channel_coder, (uint16_t) setting_load_int("audioUnits", 1));

        /* Set default repair to be first available */
        r          = repair_get_details(0);
        sp->repair = r->id;
        name       = setting_load_str("audioRepair", "Pattern-Match");
        n          = (int)repair_get_count();
        for(i = 0; i < n; i++) {
                r = repair_get_details((uint16_t)i);
                if (strcasecmp(r->name, name) == 0) {
                        sp->repair = r->id;
                        break;
                }
        }

        /* Set default converter to be first available */
        cod           = converter_get_details(0);
        sp->converter = cod->id;
        name          = setting_load_str("audioAutoConvert", "High Quality");
        n             = (int)converter_get_count();
        /* If converter setting name matches then override existing choice */
        for(i = 0; i < n; i++) {
                cod = converter_get_details(i);
                if (strcasecmp(cod->name, name) == 0) {
                        sp->converter = cod->id;
                        break;
                }
        }

	silence = setting_load_str("audioSilence", "Automatic");
        sp->silence_detection = sd_name_to_type(silence);
        sp->manual_sd_thresh  = setting_load_int("audioSilenceManualThresh", 100);
        if (sp->manual_sd) {
                manual_sd_set_thresh(sp->manual_sd, sp->manual_sd_thresh);
        }

	sp->limit_playout     = setting_load_int("audioLimitPlayout", 0);
	sp->min_playout       = setting_load_int("audioMinPlayout", 0);
	sp->max_playout       = setting_load_int("audioMaxPlayout", 2000);
	sp->lecture           = setting_load_int("audioLecture", 0);
	sp->agc_on            = setting_load_int("audioAGC", 0);
	sp->loopback_gain     = setting_load_int("audioLoopback", 0);
        audio_loopback(sp->audio_device, sp->loopback_gain);
	sp->echo_suppress     = setting_load_int("audioEchoSuppress", 0);
	sp->meter             = setting_load_int("audioPowermeters", 1);
        sp->rtp_promiscuous_mode   = setting_load_int("rtpPromiscuousMode", 0); 
        sp->rtp_wait_for_rtcp = setting_load_int("rtpWaitForRTCP", 1);

/* Ignore saved render_3d setting.  Break initial device config stuff.  V.fiddly to fix. */
/*	sp->render_3d      = setting_load_int("audio3dRendering", 0);                    */

        mute = setting_load_int("audioInputMute", sp->mode==TRANSCODER?0:1);
        if (mute && tx_is_sending(sp->tb)) {
                tx_stop(sp->tb);
        } else if (mute == 0 && tx_is_sending(sp->tb) == 0) {
                tx_start(sp->tb);
        }

        setting_load_int("audioOutputMute", 1);

        xmemchk();
	load_done();
}
Beispiel #22
0
static void
sinc_upsample_stereo (struct s_filter_state *fs,
                    sample *src, int src_len,
                    sample *dst, int dst_len)
{
        sample *work_buf, *out;
        int     work_len;
        int32_t   tmp[2], si_start, si_end, si, hold_bytes;
        int32_t  *h, hi_start, hi_end, hi;

        hold_bytes = fs->taps / fs->scale * sizeof(sample) * 2;
        work_len   = src_len + hold_bytes / sizeof(sample);
        work_buf   = (sample*)block_alloc(sizeof(sample)*work_len);

        /* Get samples into work_buf */
        memcpy(work_buf, fs->hold_buf, hold_bytes);
        memcpy(work_buf + hold_bytes / sizeof(sample),
               src,
               src_len * sizeof(sample));

        /* Save last samples in src into hold_buf for next time */
        if (src_len >= (int)(hold_bytes / sizeof(sample))) {
                memcpy(fs->hold_buf,
                       src + src_len - hold_bytes / sizeof(sample),
                       hold_bytes);
        } else {
                /* incoming chunk was shorter than hold buffer */
                memmove(fs->hold_buf,
                        fs->hold_buf + src_len,
                        hold_bytes - src_len * sizeof(sample));
                memmove(fs->hold_buf + hold_bytes / sizeof(sample) - src_len,
                        src,
                        src_len * sizeof(sample));
        }

        h      = fs->filter;
        hi_end = fs->taps;

        si_start = 0;
        si_end   = work_len - (fs->taps / fs->scale) * 2;
        out      = dst;

        switch (fs->scale) {
        case 6:
                while (si_start < si_end) {
                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 5;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 4;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 3;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 2;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 1;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 0;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si_start += 2;
                }
                break;
        case 5:
                while (si_start < si_end) {
                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 4;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 3;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 2;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 1;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 0;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si_start += 2;
                }
                break;
        case 4:
                while (si_start < si_end) {
                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 3;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 2;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 1;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 0;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si_start += 2;
                }
                break;
        case 3:
                while (si_start < si_end) {
                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 2;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 1;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 0;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si_start += 2;
                }
                break;
        case 2:
                while (si_start < si_end) {
                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 1;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];

                        si  = si_start;
                        tmp[0] = tmp[1] = 0;
                        hi  = 0;
                        while (hi < hi_end) {
                                tmp[0] += work_buf[si] * h[hi];
                                tmp[1] += work_buf[si + 1] * h[hi];
                                hi  += fs->scale;
                                si  += 2;
                        }
                        tmp[0] /= SINC_SCALE;
                        tmp[1] /= SINC_SCALE;
                        clip16(tmp[0]);
                        clip16(tmp[1]);
                        *out++ = (short)tmp[0];
                        *out++ = (short)tmp[1];
                        si_start += 2;
                }
                break;
        default:
                while (si_start < si_end) {
                        hi_start = fs->scale - 1;
                        while (hi_start >= 0) {
                                tmp[0] = tmp[1] = 0;
                                si  = si_start;
                                hi  = hi_start;
                                while (hi < hi_end) {
                                        tmp[0] += work_buf[si] * h[hi];
                                        tmp[1] += work_buf[si + 1] * h[hi];
                                        hi  += fs->scale;
                                        si  += 2;
                                }
                                tmp[0] /= SINC_SCALE;
                                tmp[1] /= SINC_SCALE;
                                clip16(tmp[0]);
                                clip16(tmp[1]);
                                *out++ = (short)tmp[0];
                                *out++ = (short)tmp[1];
                                hi_start--;
                        }
                        si_start += 2;
                }
        }
        assert(si_start == si_end);
        assert(out == dst + dst_len);

        block_free(work_buf, work_len * sizeof(sample));
        xmemchk();
}