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 27#include <dev/sound/pcm/sound.h> 28#include <dev/sound/pcm/vchan.h> 29#include "feeder_if.h" 30
|
32 33struct vchinfo { 34 u_int32_t spd, fmt, blksz, bps, run; 35 struct pcm_channel *channel, *parent; 36 struct pcmchan_caps caps; 37}; 38 39static u_int32_t vchan_fmt[] = { 40 AFMT_STEREO | AFMT_S16_LE, 41 0 42}; 43 44static int 45vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count) 46{ 47 /* 48 * to is the output buffer, tmp is the input buffer 49 * count is the number of 16bit samples to mix 50 */ 51 int i; 52 int x; 53 54 for(i = 0; i < count; i++) { 55 x = to[i]; 56 x += tmp[i]; 57 if (x < -32768) { 58 /* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */ 59 x = -32768; 60 } 61 if (x > 32767) { 62 /* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */ 63 x = 32767; 64 } 65 to[i] = x & 0x0000ffff; 66 } 67 return 0; 68} 69 70static int 71feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) 72{ 73 /* we're going to abuse things a bit */ 74 struct snd_dbuf *src = source; 75 struct pcmchan_children *cce; 76 struct pcm_channel *ch; 77 int16_t *tmp, *dst; 78 unsigned int cnt; 79 80 KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize")); 81 count &= ~1; 82 bzero(b, count); 83 84 /* 85 * we are going to use our source as a temporary buffer since it's 86 * got no other purpose. we obtain our data by traversing the channel 87 * list of children and calling vchan_mix_* to mix count bytes from each 88 * into our destination buffer, b 89 */ 90 dst = (int16_t *)b; 91 tmp = (int16_t *)sndbuf_getbuf(src); 92 bzero(tmp, count); 93 SLIST_FOREACH(cce, &c->children, link) { 94 ch = cce->channel; 95 if (ch->flags & CHN_F_TRIGGERED) { 96 if (ch->flags & CHN_F_MAPPED) 97 sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); 98 cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); 99 vchan_mix_s16(dst, tmp, cnt / 2); 100 } 101 } 102 103 return count; 104} 105 106static struct pcm_feederdesc feeder_vchan_s16_desc[] = { 107 {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 108 {0}, 109}; 110static kobj_method_t feeder_vchan_s16_methods[] = { 111 KOBJMETHOD(feeder_feed, feed_vchan_s16), 112 { 0, 0 } 113}; 114FEEDER_DECLARE(feeder_vchan_s16, 2, NULL); 115 116/************************************************************/ 117 118static void * 119vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 120{ 121 struct vchinfo *ch; 122 struct pcm_channel *parent = devinfo; 123 124 KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); 125 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 126 ch->parent = parent; 127 ch->channel = c; 128 ch->fmt = AFMT_U8; 129 ch->spd = DSP_DEFAULT_SPEED; 130 ch->blksz = 2048; 131 132 c->flags |= CHN_F_VIRTUAL; 133 134 return ch; 135} 136 137static int 138vchan_free(kobj_t obj, void *data) 139{ 140 return 0; 141} 142 143static int 144vchan_setformat(kobj_t obj, void *data, u_int32_t format) 145{ 146 struct vchinfo *ch = data; 147 struct pcm_channel *parent = ch->parent; 148 149 ch->fmt = format; 150 ch->bps = 1; 151 ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; 152 ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0; 153 ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0; 154 chn_notify(parent, CHN_N_FORMAT); 155 return 0; 156} 157 158static int 159vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 160{ 161 struct vchinfo *ch = data; 162 struct pcm_channel *parent = ch->parent; 163 164 ch->spd = speed; 165 chn_notify(parent, CHN_N_RATE); 166 return speed; 167} 168 169static int 170vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 171{ 172 struct vchinfo *ch = data; 173 struct pcm_channel *parent = ch->parent; 174 int prate, crate; 175 176 ch->blksz = blocksize; 177 chn_notify(parent, CHN_N_BLOCKSIZE); 178 179 crate = ch->spd * ch->bps; 180 prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard); 181 blocksize = sndbuf_getblksz(parent->bufhard); 182 blocksize *= prate; 183 blocksize /= crate; 184 185 return blocksize; 186} 187 188static int 189vchan_trigger(kobj_t obj, void *data, int go) 190{ 191 struct vchinfo *ch = data; 192 struct pcm_channel *parent = ch->parent; 193 194 if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 195 return 0; 196 197 ch->run = (go == PCMTRIG_START)? 1 : 0; 198 chn_notify(parent, CHN_N_TRIGGER); 199 200 return 0; 201} 202 203static struct pcmchan_caps * 204vchan_getcaps(kobj_t obj, void *data) 205{ 206 struct vchinfo *ch = data; 207 208 ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard); 209 ch->caps.maxspeed = ch->caps.minspeed; 210 ch->caps.fmtlist = vchan_fmt; 211 ch->caps.caps = 0; 212 213 return &ch->caps; 214} 215 216static kobj_method_t vchan_methods[] = { 217 KOBJMETHOD(channel_init, vchan_init), 218 KOBJMETHOD(channel_free, vchan_free), 219 KOBJMETHOD(channel_setformat, vchan_setformat), 220 KOBJMETHOD(channel_setspeed, vchan_setspeed), 221 KOBJMETHOD(channel_setblocksize, vchan_setblocksize), 222 KOBJMETHOD(channel_trigger, vchan_trigger), 223 KOBJMETHOD(channel_getcaps, vchan_getcaps), 224 { 0, 0 } 225}; 226CHANNEL_DECLARE(vchan); 227 228/* virtual channel interface */ 229 230int 231vchan_create(struct pcm_channel *parent) 232{ 233 struct snddev_info *d = parent->parentsnddev; 234 struct pcmchan_children *pce; 235 struct pcm_channel *child; 236 int err, first; 237 238 pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); 239 if (!pce) { 240 return ENOMEM; 241 } 242 243 /* create a new playback channel */ 244 child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); 245 if (!child) { 246 free(pce, M_DEVBUF); 247 return ENODEV; 248 } 249 250 CHN_LOCK(parent); 251 if (!(parent->flags & CHN_F_BUSY)) { 252 CHN_UNLOCK(parent); 253 return EBUSY; 254 } 255 256 first = SLIST_EMPTY(&parent->children); 257 /* add us to our parent channel's children */ 258 pce->channel = child; 259 SLIST_INSERT_HEAD(&parent->children, pce, link); 260 CHN_UNLOCK(parent); 261 262 /* add us to our grandparent's channel list */ 263 err = pcm_chn_add(d, child, !first); 264 if (err) { 265 pcm_chn_destroy(child); 266 free(pce, M_DEVBUF); 267 } 268 269 /* XXX gross ugly hack, murder death kill */ 270 if (first && !err) { 271 err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); 272 if (err) 273 printf("chn_reset: %d\n", err); 274 err = chn_setspeed(parent, 44100); 275 if (err) 276 printf("chn_setspeed: %d\n", err); 277 } 278 279 return err; 280} 281 282int 283vchan_destroy(struct pcm_channel *c) 284{ 285 struct pcm_channel *parent = c->parentchannel; 286 struct snddev_info *d = parent->parentsnddev; 287 struct pcmchan_children *pce; 288 int err, last; 289 290 CHN_LOCK(parent); 291 if (!(parent->flags & CHN_F_BUSY)) { 292 CHN_UNLOCK(parent); 293 return EBUSY; 294 } 295 if (SLIST_EMPTY(&parent->children)) { 296 CHN_UNLOCK(parent); 297 return EINVAL; 298 } 299 300 /* remove us from our parent's children list */ 301 SLIST_FOREACH(pce, &parent->children, link) { 302 if (pce->channel == c) 303 goto gotch; 304 } 305 CHN_UNLOCK(parent); 306 return EINVAL; 307gotch: 308 SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); 309 free(pce, M_DEVBUF); 310 311 last = SLIST_EMPTY(&parent->children); 312 if (last) 313 parent->flags &= ~CHN_F_BUSY; 314 315 /* remove us from our grantparent's channel list */ 316 err = pcm_chn_remove(d, c, !last); 317 if (err) 318 return err; 319 320 CHN_UNLOCK(parent); 321 /* destroy ourselves */ 322 err = pcm_chn_destroy(c); 323 324 return err; 325} 326 327int 328vchan_initsys(device_t dev) 329{ 330#ifdef SND_DYNSYSCTL 331 struct snddev_info *d; 332 333 d = device_get_softc(dev); 334 SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 335 OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 336 sysctl_hw_snd_vchans, "I", ""); 337#endif 338 339 return 0; 340} 341 342
|