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
feed_format_init(struct pcm_feeder * f)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
feed_format_free(struct pcm_feeder * f)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
feed_format_set(struct pcm_feeder * f,int what,int value)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
feed_format_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)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