xref: /freebsd/share/examples/sound/simple.c (revision 6a569666868b36f5f436eea9d66789b6df191b8a)
1*6a569666SGoran Mekić /*
2*6a569666SGoran Mekić  * SPDX-License-Identifier: BSD-2-Clause
3*6a569666SGoran Mekić  *
4*6a569666SGoran Mekić  * Copyright (c) 2024 The FreeBSD Foundation
5*6a569666SGoran Mekić  * Copyright (c) 2025 Goran Mekić
6*6a569666SGoran Mekić  *
7*6a569666SGoran Mekić  * Portions of this software were developed by Christos Margiolis
8*6a569666SGoran Mekić  * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
9*6a569666SGoran Mekić  *
10*6a569666SGoran Mekić  * Redistribution and use in source and binary forms, with or without
11*6a569666SGoran Mekić  * modification, are permitted provided that the following conditions
12*6a569666SGoran Mekić  * are met:
13*6a569666SGoran Mekić  * 1. Redistributions of source code must retain the above copyright
14*6a569666SGoran Mekić  *    notice, this list of conditions and the following disclaimer.
15*6a569666SGoran Mekić  * 2. Redistributions in binary form must reproduce the above copyright
16*6a569666SGoran Mekić  *    notice, this list of conditions and the following disclaimer in the
17*6a569666SGoran Mekić  *    documentation and/or other materials provided with the distribution.
18*6a569666SGoran Mekić  *
19*6a569666SGoran Mekić  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20*6a569666SGoran Mekić  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21*6a569666SGoran Mekić  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22*6a569666SGoran Mekić  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23*6a569666SGoran Mekić  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24*6a569666SGoran Mekić  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25*6a569666SGoran Mekić  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*6a569666SGoran Mekić  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27*6a569666SGoran Mekić  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28*6a569666SGoran Mekić  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29*6a569666SGoran Mekić  * SUCH DAMAGE.
30*6a569666SGoran Mekić  */
31*6a569666SGoran Mekić 
32*6a569666SGoran Mekić #include "oss.h"
33*6a569666SGoran Mekić 
34*6a569666SGoran Mekić /*
35*6a569666SGoran Mekić  * Split input buffer into channels. The input buffer is in interleaved format,
36*6a569666SGoran Mekić  * which means if we have 2 channels (L and R), this is what the buffer of 8
37*6a569666SGoran Mekić  * samples would contain: L,R,L,R,L,R,L,R. The result of this function is a
38*6a569666SGoran Mekić  * buffer containing: L,L,L,L,R,R,R,R.
39*6a569666SGoran Mekić  */
40*6a569666SGoran Mekić static void
to_channels(struct config * config,void * output)41*6a569666SGoran Mekić to_channels(struct config *config, void *output)
42*6a569666SGoran Mekić {
43*6a569666SGoran Mekić 	uint8_t *in = config->buf;
44*6a569666SGoran Mekić 	uint8_t *out = output;
45*6a569666SGoran Mekić 	int i, channel, index, offset, byte;
46*6a569666SGoran Mekić 
47*6a569666SGoran Mekić 	/* Iterate over bytes in the input buffer */
48*6a569666SGoran Mekić 	for (byte = 0; byte < config->buffer_info.bytes;
49*6a569666SGoran Mekić 	    byte += config->sample_size) {
50*6a569666SGoran Mekić 		/*
51*6a569666SGoran Mekić 		 * Get index of a sample in the input buffer measured in
52*6a569666SGoran Mekić 		 * samples
53*6a569666SGoran Mekić 		 */
54*6a569666SGoran Mekić 		i = byte / config->sample_size;
55*6a569666SGoran Mekić 
56*6a569666SGoran Mekić 		/* Get which channel is being processed */
57*6a569666SGoran Mekić 		channel = i % config->audio_info.max_channels;
58*6a569666SGoran Mekić 
59*6a569666SGoran Mekić 		/* Get offset of the sample inside a single channel */
60*6a569666SGoran Mekić 		offset = i / config->audio_info.max_channels;
61*6a569666SGoran Mekić 
62*6a569666SGoran Mekić 		/* Get index of a sample in the output buffer */
63*6a569666SGoran Mekić 		index = (channel * config->chsamples + offset) *
64*6a569666SGoran Mekić 		    config->sample_size;
65*6a569666SGoran Mekić 
66*6a569666SGoran Mekić 		/* Copy singe sample from input to output */
67*6a569666SGoran Mekić 		memcpy(out+index, in+byte, config->sample_size);
68*6a569666SGoran Mekić 	}
69*6a569666SGoran Mekić }
70*6a569666SGoran Mekić 
71*6a569666SGoran Mekić /*
72*6a569666SGoran Mekić  * Convert channels into interleaved format and put into output buffer
73*6a569666SGoran Mekić  */
74*6a569666SGoran Mekić static void
to_interleaved(struct config * config,void * input)75*6a569666SGoran Mekić to_interleaved(struct config *config, void *input)
76*6a569666SGoran Mekić {
77*6a569666SGoran Mekić 	uint8_t *out = config->buf;
78*6a569666SGoran Mekić 	uint8_t *in = input;
79*6a569666SGoran Mekić 	int i, index, offset, channel, byte;
80*6a569666SGoran Mekić 
81*6a569666SGoran Mekić 	/* Iterate over bytes in the input buffer */
82*6a569666SGoran Mekić 	for (byte = 0; byte < config->buffer_info.bytes;
83*6a569666SGoran Mekić 	    byte += config->sample_size) {
84*6a569666SGoran Mekić 		/*
85*6a569666SGoran Mekić 		 * Get index of a sample in the input buffer measured in
86*6a569666SGoran Mekić 		 * samples
87*6a569666SGoran Mekić 		 */
88*6a569666SGoran Mekić 		index = byte / config->sample_size;
89*6a569666SGoran Mekić 
90*6a569666SGoran Mekić 		/* Get which channel is being processed */
91*6a569666SGoran Mekić 		channel = index / config->chsamples;
92*6a569666SGoran Mekić 
93*6a569666SGoran Mekić 		/* Get offset of the sample inside a single channel */
94*6a569666SGoran Mekić 		offset = index % config->chsamples;
95*6a569666SGoran Mekić 
96*6a569666SGoran Mekić 		/* Get index of a sample in the output buffer */
97*6a569666SGoran Mekić 		i = (config->audio_info.max_channels * offset + channel) *
98*6a569666SGoran Mekić 		    config->sample_size;
99*6a569666SGoran Mekić 
100*6a569666SGoran Mekić 		/* Copy singe sample from input to output */
101*6a569666SGoran Mekić 		memcpy(out+i, in+byte, config->sample_size);
102*6a569666SGoran Mekić 	}
103*6a569666SGoran Mekić }
104*6a569666SGoran Mekić 
105*6a569666SGoran Mekić int
main(int argc,char * argv[])106*6a569666SGoran Mekić main(int argc, char *argv[])
107*6a569666SGoran Mekić {
108*6a569666SGoran Mekić 	struct config config = {
109*6a569666SGoran Mekić 		.device = "/dev/dsp",
110*6a569666SGoran Mekić 		.mode = O_RDWR,
111*6a569666SGoran Mekić 		.format = AFMT_S32_NE,
112*6a569666SGoran Mekić 		.sample_rate = 48000,
113*6a569666SGoran Mekić 	};
114*6a569666SGoran Mekić 	int32_t *channels;
115*6a569666SGoran Mekić 	int rc, bytes;
116*6a569666SGoran Mekić 
117*6a569666SGoran Mekić 	oss_init(&config);
118*6a569666SGoran Mekić 	bytes = config.buffer_info.bytes;
119*6a569666SGoran Mekić 	channels = malloc(bytes);
120*6a569666SGoran Mekić 
121*6a569666SGoran Mekić 	for (;;) {
122*6a569666SGoran Mekić 		if ((rc = read(config.fd, config.buf, bytes)) < bytes) {
123*6a569666SGoran Mekić 			warn("Requested %d bytes, but read %d!\n", bytes, rc);
124*6a569666SGoran Mekić 			break;
125*6a569666SGoran Mekić 		}
126*6a569666SGoran Mekić 		/*
127*6a569666SGoran Mekić 		 * Strictly speaking, we could omit "channels" and operate only
128*6a569666SGoran Mekić 		 * using config->buf, but this example tries to show the real
129*6a569666SGoran Mekić 		 * world application usage. The problem is that the buffer is
130*6a569666SGoran Mekić 		 * in interleaved format, and if you'd like to do any
131*6a569666SGoran Mekić 		 * processing and/or mixing, it is easier to do that if samples
132*6a569666SGoran Mekić 		 * are grouped per channel.
133*6a569666SGoran Mekić 		 */
134*6a569666SGoran Mekić 		to_channels(&config, channels);
135*6a569666SGoran Mekić 		to_interleaved(&config, channels);
136*6a569666SGoran Mekić 		if ((rc = write(config.fd, config.buf, bytes)) < bytes) {
137*6a569666SGoran Mekić 			warn("Requested %d bytes, but wrote %d!\n", bytes, rc);
138*6a569666SGoran Mekić 			break;
139*6a569666SGoran Mekić 		}
140*6a569666SGoran Mekić 	}
141*6a569666SGoran Mekić 
142*6a569666SGoran Mekić 	free(channels);
143*6a569666SGoran Mekić 	free(config.buf);
144*6a569666SGoran Mekić 	close(config.fd);
145*6a569666SGoran Mekić 
146*6a569666SGoran Mekić 	return (0);
147*6a569666SGoran Mekić }
148