1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * All rights reserved. 6 * Copyright (c) 2024-2025 The FreeBSD Foundation 7 * 8 * Portions of this software were developed by Christos Margiolis 9 * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * feeder_format: New generation of generic, any-to-any format converter, as 35 * long as the sample values can be read _and_ write. 36 */ 37 38 #ifdef _KERNEL 39 #ifdef HAVE_KERNEL_OPTION_HEADERS 40 #include "opt_snd.h" 41 #endif 42 #include <dev/sound/pcm/sound.h> 43 #include <dev/sound/pcm/pcm.h> 44 #include "feeder_if.h" 45 46 #define SND_USE_FXDIV 47 #include "snd_fxdiv_gen.h" 48 #endif 49 50 #define FEEDFORMAT_RESERVOIR (SND_CHN_MAX * PCM_32_BPS) 51 52 struct feed_format_info { 53 uint32_t ibps, obps; 54 uint32_t ialign, oalign, channels; 55 uint32_t rdfmt, wrfmt; 56 uint8_t reservoir[FEEDFORMAT_RESERVOIR]; 57 }; 58 59 static int 60 feed_format_init(struct pcm_feeder *f) 61 { 62 struct feed_format_info *info; 63 64 if (f->desc->in == f->desc->out || 65 AFMT_CHANNEL(f->desc->in) != AFMT_CHANNEL(f->desc->out)) 66 return (EINVAL); 67 68 info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); 69 if (info == NULL) 70 return (ENOMEM); 71 72 info->channels = AFMT_CHANNEL(f->desc->in); 73 74 info->ibps = AFMT_BPS(f->desc->in); 75 info->ialign = info->ibps * info->channels; 76 info->rdfmt = AFMT_ENCODING(f->desc->in); 77 78 info->obps = AFMT_BPS(f->desc->out); 79 info->oalign = info->obps * info->channels; 80 info->wrfmt = AFMT_ENCODING(f->desc->out); 81 82 f->data = info; 83 84 return (0); 85 } 86 87 static int 88 feed_format_free(struct pcm_feeder *f) 89 { 90 struct feed_format_info *info; 91 92 info = f->data; 93 if (info != NULL) 94 free(info, M_DEVBUF); 95 96 f->data = NULL; 97 98 return (0); 99 } 100 101 static int 102 feed_format_set(struct pcm_feeder *f, int what, int value) 103 { 104 struct feed_format_info *info; 105 106 info = f->data; 107 108 switch (what) { 109 case FEEDFORMAT_CHANNELS: 110 if (value < SND_CHN_MIN || value > SND_CHN_MAX) 111 return (EINVAL); 112 info->channels = (uint32_t)value; 113 info->ialign = info->ibps * info->channels; 114 info->oalign = info->obps * info->channels; 115 break; 116 default: 117 return (EINVAL); 118 break; 119 } 120 121 return (0); 122 } 123 124 static int 125 feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 126 uint32_t count, void *source) 127 { 128 struct feed_format_info *info; 129 intpcm_t v; 130 uint32_t j; 131 uint8_t *src, *dst; 132 133 info = f->data; 134 dst = b; 135 count = SND_FXROUND(count, info->oalign); 136 137 do { 138 if (count < info->oalign) 139 break; 140 141 if (count < info->ialign) { 142 src = info->reservoir; 143 j = info->ialign; 144 } else { 145 if (info->ialign == info->oalign) 146 j = count; 147 else if (info->ialign > info->oalign) 148 j = SND_FXROUND(count, info->ialign); 149 else 150 j = SND_FXDIV(count, info->oalign) * 151 info->ialign; 152 src = dst + count - j; 153 } 154 155 j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source), 156 info->ialign); 157 if (j == 0) 158 break; 159 160 j *= info->channels; 161 count -= j * info->obps; 162 163 do { 164 v = pcm_sample_read_norm(src, info->rdfmt); 165 pcm_sample_write_norm(dst, v, info->wrfmt); 166 dst += info->obps; 167 src += info->ibps; 168 } while (--j != 0); 169 170 } while (count != 0); 171 172 return (dst - b); 173 } 174 175 static struct pcm_feederdesc feeder_format_desc[] = { 176 { FEEDER_FORMAT, 0, 0, 0, 0 }, 177 { 0, 0, 0, 0, 0 } 178 }; 179 180 static kobj_method_t feeder_format_methods[] = { 181 KOBJMETHOD(feeder_init, feed_format_init), 182 KOBJMETHOD(feeder_free, feed_format_free), 183 KOBJMETHOD(feeder_set, feed_format_set), 184 KOBJMETHOD(feeder_feed, feed_format_feed), 185 KOBJMETHOD_END 186 }; 187 188 FEEDER_DECLARE(feeder_format, NULL); 189