1/*	$NetBSD: psgpam_enc.c,v 1.3 2023/01/15 05:08:33 tsutsui Exp $	*/
2
3/*
4 * Copyright (c) 2018 Yosuke Sugahara. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28/*
29 * PSGPAM, PSGPCM encoders
30 * Function names are PSGPAM, but also used by PSGPCM.
31 *
32 * PSGPAM and PSGPCM internally use the unsigned type auint_t for
33 * intermediate calculations to manage non-linear conversions.
34 */
35
36#include <sys/types.h>
37
38#if defined(_KERNEL)
39#include <sys/device.h>
40#include <sys/audioio.h>
41#include <dev/audio/audio_if.h>
42#include <luna68k/dev/psgpam_enc.h>
43#include <luna68k/dev/psgpam_table.h>
44#else
45#include <stdint.h>
46#include <stdlib.h>
47#include "audio/userland.h"
48#include "psgpam_enc.h"
49#include "psgpam_table.c"
50#include "psgpam_table.h"
51#endif
52
53void
54psgpam_init_context(struct psgpam_codecvar *ctx, u_int sample_rate)
55{
56
57	ctx->offset = 65535;
58	ctx->sample_rate = sample_rate;
59	ctx->expire_initial = sample_rate / 10;
60	ctx->expire = ctx->expire_initial;
61}
62
63static inline auint_t
64dynamic_offset(struct psgpam_codecvar *ctx, auint_t v)
65{
66
67	/*
68	 * if (the passed value cannot be handled by current offset) {
69	 *   update offset to handle the passed value
70	 * } else {
71	 *   increment offset
72	 * }
73	 */
74	if (v <= ctx->offset) {
75		ctx->offset = v;
76	} else {
77		if (--ctx->expire < 0) {
78			ctx->offset += 1;
79			ctx->expire = ctx->expire_initial;
80		}
81	}
82	return v - ctx->offset;
83}
84
85#define BULK(table) *d++ = table[v]
86
87#define W8(table) *d++ = table[v]
88
89#define W16(table) do {							\
90	uint16_t t = (uint16_t)table[v];				\
91	*d++ = ((t & 0xf0) << 4) | (t & 0x0f);				\
92} while (0)
93
94#define W32(table) do {							\
95	uint32_t t = (uint32_t)table[v];				\
96	*d++ = ((t & 0xf000) << 12)					\
97	     | ((t & 0x0f00) <<  8)					\
98	     | ((t & 0x00f0) <<  4)					\
99	     | ((t & 0x000f));						\
100} while (0)
101
102#define SPLIT3(table) do {						\
103	uint16_t t = (uint16_t)table[v];				\
104	*d++ = ((t & 0xf000) >> 12);					\
105	*d++ = ((t & 0x0f00) >>  8);					\
106	*d++ = ((t & 0x000f));						\
107} while (0)
108
109#define ENCODER_DEFINE(enc, TT, table, writer)				\
110void									\
111psgpam_aint_to_##enc(audio_filter_arg_t *arg)				\
112{									\
113	const aint_t *s = arg->src;					\
114	TT *d = arg->dst;						\
115									\
116	for (int i = 0; i < arg->count; i++) {				\
117		auint_t v = (*s++) ^ AINT_T_MIN;			\
118		v >>= (AUDIO_INTERNAL_BITS - table##_BITS);		\
119		writer(table);						\
120	}								\
121}
122
123#define ENCODER_D_DEFINE(enc, TT, table, writer)			\
124void									\
125psgpam_aint_to_##enc##_d(audio_filter_arg_t *arg)			\
126{									\
127	const aint_t *s = arg->src;					\
128	TT *d = arg->dst;						\
129									\
130	for (int i = 0; i < arg->count; i++) {				\
131		auint_t v = (*s++) ^ AINT_T_MIN;			\
132		v >>= (AUDIO_INTERNAL_BITS - table##_BITS);		\
133		v = dynamic_offset(arg->context, v);			\
134		writer(table);						\
135	}								\
136}
137
138ENCODER_DEFINE(pam2a, uint16_t, PAM2A_TABLE, W16)
139ENCODER_DEFINE(pam2b, uint16_t, PAM2B_TABLE, W16)
140ENCODER_DEFINE(pam3a, uint32_t, PAM3A_TABLE, W32)
141ENCODER_DEFINE(pam3b, uint32_t, PAM3B_TABLE, W32)
142ENCODER_DEFINE(pcm1, uint8_t,  PCM1_TABLE, W8)
143ENCODER_DEFINE(pcm2, uint16_t, PCM2_TABLE, W16)
144ENCODER_DEFINE(pcm3, uint8_t,  PCM3_TABLE, SPLIT3)
145
146ENCODER_D_DEFINE(pam2a, uint16_t, PAM2A_TABLE, W16)
147ENCODER_D_DEFINE(pam2b, uint16_t, PAM2B_TABLE, W16)
148ENCODER_D_DEFINE(pam3a, uint32_t, PAM3A_TABLE, W32)
149ENCODER_D_DEFINE(pam3b, uint32_t, PAM3B_TABLE, W32)
150ENCODER_D_DEFINE(pcm1, uint8_t,  PCM1_TABLE, W8)
151ENCODER_D_DEFINE(pcm2, uint16_t, PCM2_TABLE, W16)
152ENCODER_D_DEFINE(pcm3, uint8_t,  PCM3_TABLE, SPLIT3)
153