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 free(info, M_DEVBUF);
94
95 f->data = NULL;
96
97 return (0);
98 }
99
100 static int
feed_format_set(struct pcm_feeder * f,int what,int value)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
feed_format_feed(struct pcm_feeder * f,struct pcm_channel * c,uint8_t * b,uint32_t count,void * source)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