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 free(info, M_DEVBUF); 94 95 f->data = NULL; 96 97 return (0); 98 } 99 100 static int 101 feed_format_set(struct pcm_feeder *f, int what, int value) 102 { 103 struct feed_format_info *info; 104 105 info = f->data; 106 107 switch (what) { 108 case FEEDFORMAT_CHANNELS: 109 if (value < SND_CHN_MIN || value > SND_CHN_MAX) 110 return (EINVAL); 111 info->channels = (uint32_t)value; 112 info->ialign = info->ibps * info->channels; 113 info->oalign = info->obps * info->channels; 114 break; 115 default: 116 return (EINVAL); 117 } 118 119 return (0); 120 } 121 122 static int 123 feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 124 uint32_t count, void *source) 125 { 126 struct feed_format_info *info; 127 intpcm_t v; 128 uint32_t j; 129 uint8_t *src, *dst; 130 131 info = f->data; 132 dst = b; 133 count = SND_FXROUND(count, info->oalign); 134 135 do { 136 if (count < info->oalign) 137 break; 138 139 if (count < info->ialign) { 140 src = info->reservoir; 141 j = info->ialign; 142 } else { 143 if (info->ialign == info->oalign) 144 j = count; 145 else if (info->ialign > info->oalign) 146 j = SND_FXROUND(count, info->ialign); 147 else 148 j = SND_FXDIV(count, info->oalign) * 149 info->ialign; 150 src = dst + count - j; 151 } 152 153 j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source), 154 info->ialign); 155 if (j == 0) 156 break; 157 158 j *= info->channels; 159 count -= j * info->obps; 160 161 do { 162 v = pcm_sample_read_norm(src, info->rdfmt); 163 pcm_sample_write_norm(dst, v, info->wrfmt); 164 dst += info->obps; 165 src += info->ibps; 166 } while (--j != 0); 167 168 } while (count != 0); 169 170 return (dst - b); 171 } 172 173 static kobj_method_t feeder_format_methods[] = { 174 KOBJMETHOD(feeder_init, feed_format_init), 175 KOBJMETHOD(feeder_free, feed_format_free), 176 KOBJMETHOD(feeder_set, feed_format_set), 177 KOBJMETHOD(feeder_feed, feed_format_feed), 178 KOBJMETHOD_END 179 }; 180 181 FEEDER_DECLARE(feeder_format, FEEDER_FORMAT); 182