feeder_rate.c revision 75320
1/* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/dev/sound/pcm/feeder_rate.c 75320 2001-04-08 20:26:22Z cg $ 27 */ 28 29#include <dev/sound/pcm/sound.h> 30 31#include "feeder_if.h" 32 33MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); 34 35#define FEEDBUFSZ 8192 36#undef FEEDER_DEBUG 37 38struct feed_rate_info { 39 u_int32_t src, dst; 40 int srcpos, srcinc; 41 int16_t *buffer; 42 u_int16_t alpha; 43}; 44 45static int 46feed_rate_setup(struct pcm_feeder *f) 47{ 48 struct feed_rate_info *info = f->data; 49 50 info->srcinc = (info->src << 16) / info->dst; 51 /* srcinc is 16.16 fixed point increment for srcpos for each dstpos */ 52 info->srcpos = 0; 53 return 0; 54} 55 56static int 57feed_rate_set(struct pcm_feeder *f, int what, int value) 58{ 59 struct feed_rate_info *info = f->data; 60 61 switch(what) { 62 case FEEDRATE_SRC: 63 info->src = value; 64 break; 65 case FEEDRATE_DST: 66 info->dst = value; 67 break; 68 default: 69 return -1; 70 } 71 return feed_rate_setup(f); 72} 73 74static int 75feed_rate_init(struct pcm_feeder *f) 76{ 77 struct feed_rate_info *info; 78 79 info = malloc(sizeof(*info), M_RATEFEEDER, M_WAITOK | M_ZERO); 80 if (info == NULL) 81 return ENOMEM; 82 info->buffer = malloc(FEEDBUFSZ, M_RATEFEEDER, M_WAITOK | M_ZERO); 83 if (info->buffer == NULL) { 84 free(info, M_RATEFEEDER); 85 return ENOMEM; 86 } 87 info->src = DSP_DEFAULT_SPEED; 88 info->dst = DSP_DEFAULT_SPEED; 89 info->alpha = 0; 90 f->data = info; 91 return feed_rate_setup(f); 92} 93 94static int 95feed_rate_free(struct pcm_feeder *f) 96{ 97 struct feed_rate_info *info = f->data; 98 99 if (info) { 100 if (info->buffer) 101 free(info->buffer, M_RATEFEEDER); 102 free(info, M_RATEFEEDER); 103 } 104 f->data = NULL; 105 return 0; 106} 107 108static int 109feed_rate(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 110{ 111 struct feed_rate_info *info = f->data; 112 int16_t *destbuf = (int16_t *)b; 113 int fetch, v, alpha, hidelta, spos, dpos; 114 115 /* 116 * at this point: 117 * info->srcpos is 24.8 fixed offset into the fetchbuffer. 0 <= srcpos <= 0xff 118 * 119 * our input and output are always AFMT_S16LE stereo. this simplifies things. 120 */ 121 122 /* 123 * we start by fetching enough source data into our buffer to generate 124 * about as much as was requested. we put it at offset 2 in the 125 * buffer so that we can interpolate from the last samples in the 126 * previous iteration- when we finish we will move our last samples 127 * to the start of the buffer. 128 */ 129 spos = 0; 130 dpos = 0; 131 132 /* fetch is in bytes */ 133 fetch = (count * info->srcinc) >> 16; 134 fetch = min(fetch, FEEDBUFSZ - 4) & ~3; 135 if (fetch == 0) 136 return 0; 137 fetch = FEEDER_FEED(f->source, c, ((u_int8_t *)info->buffer) + 4, fetch, source); 138 fetch /= 2; 139 140 alpha = info->alpha; 141 hidelta = min(info->srcinc >> 16, 1) * 2; 142 while ((spos + hidelta + 1) < fetch) { 143 v = (info->buffer[spos] * (0xffff - alpha)) + (info->buffer[spos + hidelta] * alpha); 144 destbuf[dpos++] = v >> 16; 145 146 v = (info->buffer[spos + 1] * (0xffff - alpha)) + (info->buffer[spos + hidelta + 1] * alpha); 147 destbuf[dpos++] = v >> 16; 148 149 alpha += info->srcinc; 150 spos += (alpha >> 16) * 2; 151 alpha &= 0xffff; 152 153 } 154 info->alpha = alpha & 0xffff; 155 info->buffer[0] = info->buffer[spos - hidelta]; 156 info->buffer[1] = info->buffer[spos - hidelta + 1]; 157 158 count = dpos * 2; 159 return count; 160} 161 162static struct pcm_feederdesc feeder_rate_desc[] = { 163 {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 164 {0}, 165}; 166static kobj_method_t feeder_rate_methods[] = { 167 KOBJMETHOD(feeder_init, feed_rate_init), 168 KOBJMETHOD(feeder_free, feed_rate_free), 169 KOBJMETHOD(feeder_set, feed_rate_set), 170 KOBJMETHOD(feeder_feed, feed_rate), 171 { 0, 0 } 172}; 173FEEDER_DECLARE(feeder_rate, 2, NULL); 174 175 176