Skip to content

Commit 42fcda3

Browse files
committed
Add support for 24-bit and 32-bit audio output.
Drivers with 24-bit and 32-bit audio support: - [x] ALSA - [ ] BSD - [ ] CoreAudio - [ ] NetBSD - [ ] OSS - [x] PulseAudio - [ ] sndio - [x] Raw file - [x] WAV file Drivers that do not support 24-bit or 32-bit: - [ ] Sound Blaster Drivers to verify: - [ ] AHI - [ ] AIFF file - [ ] AIX - [ ] ALSA 0.5 - [ ] BeOS - [ ] DART - [ ] HP-UX - [ ] QNX - [ ] SGI - [ ] Solaris
1 parent 34154d1 commit 42fcda3

27 files changed

+303
-59
lines changed

Changelog

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@ Stable versions
33

44
4.2.1 (?):
55
Changes by Alice Rowan
6+
- Support 24-bit and 32-bit output for the PulseAudio, ALSA,
7+
WAV, file drivers (requires libxmp 4.7.0+) (FIXME).
68
- Report pan value "---" for instruments/samples without
79
a default panning value (sub->pan < 0).
810
- Don't unmute muted IT channels unless explicitly unmuted by
911
the -S/--solo option.
12+
- wav: fix incorrect RIFF length in output.
13+
- wav: add terminal pad byte after odd length data chunks.
14+
- file: fix incorrect handling of -Dendian ("big" would output
15+
little endian, anything else would output big endian).
16+
- pulseaudio: support 8-bit output.
1017

1118
4.2.0 (20230615):
1219
Changes by Özkan Sezer:

README

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ directly to cmatsuoka@gmail.com.
5959
LICENSE
6060

6161
Extended Module Player
62-
Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
62+
Copyright (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr
6363

6464
This program is free software; you can redistribute it and/or modify it under
6565
the terms of the GNU General Public License as published by the Free Software

src/common.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222

2323
#include <xmp.h>
2424

25+
/* Allow building with <4.7.0 for now... */
26+
#if XMP_VERCODE < 0x040700
27+
#define XMP_FORMAT_32BIT (1 << 3)
28+
#endif
29+
2530
struct player_mode {
2631
const char *name;
2732
const char *desc;
@@ -33,6 +38,7 @@ struct options {
3338
int amplify; /* amplification factor */
3439
int rate; /* sampling rate */
3540
int format; /* sample format */
41+
int format_downmix; /* XMP_FORMAT_32BIT may require downmix to 24 */
3642
int max_time; /* max. replay time */
3743
int mix; /* channel separation */
3844
int defpan; /* default pan */

src/main.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Extended Module Player
2-
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
2+
* Copyright (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr
33
*
44
* This file is part of the Extended Module Player and is distributed
55
* under the terms of the GNU General Public License. See the COPYING
@@ -335,13 +335,21 @@ int main(int argc, char **argv)
335335
}
336336

337337
if (opt.verbose > 0) {
338+
int format_bits;
339+
if (opt.format & XMP_FORMAT_32BIT) {
340+
format_bits = opt.format_downmix == 24 ? 24 : 32;
341+
} else if (~opt.format & XMP_FORMAT_8BIT) {
342+
format_bits = 16;
343+
} else {
344+
format_bits = 8;
345+
}
346+
338347
report("Extended Module Player " VERSION "\n"
339-
"Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr\n");
348+
"Copyright (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr\n");
340349

341350
report("Using %s\n", sound->description());
342351

343-
report("Mixer set to %d Hz, %dbit, %s%s%s\n", opt.rate,
344-
opt.format & XMP_FORMAT_8BIT ? 8 : 16,
352+
report("Mixer set to %d Hz, %dbit, %s%s%s\n", opt.rate, format_bits,
345353
opt.interp == XMP_INTERP_LINEAR ? "linear interpolated " :
346354
opt.interp == XMP_INTERP_SPLINE ? "cubic spline interpolated " : "",
347355
opt.format & XMP_FORMAT_MONO ? "mono" : "stereo",

src/options.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Extended Module Player
2-
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
2+
* Copyright (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr
33
*
44
* This file is part of the Extended Module Player and is distributed
55
* under the terms of the GNU General Public License. See the COPYING
@@ -96,7 +96,7 @@ static void usage(char *s, struct options *options)
9696
"\nMixer options:\n"
9797
" -A --amiga Use Paula simulation mixer in Amiga formats\n"
9898
" -a --amplify {0|1|2|3} Amplification factor: 0=Normal, 1=x2, 2=x4, 3=x8\n"
99-
" -b --bits {8|16} Software mixer resolution (8 or 16 bits)\n"
99+
" -b --bits {8|16|24|32} Software mixer resolution (8, 16, 24, or 32 bits)\n"
100100
" -c --stdout Mix the module to stdout\n"
101101
" -f --frequency rate Sampling rate in hertz (default 44100)\n"
102102
" -i --interpolation {nearest|linear|spline}\n"
@@ -190,8 +190,19 @@ void get_options(int argc, char **argv, struct options *options)
190190
options->amplify = atoi(optarg);
191191
break;
192192
case 'b':
193-
if (atoi(optarg) == 8) {
193+
options->format &= ~(XMP_FORMAT_8BIT | XMP_FORMAT_32BIT);
194+
options->format_downmix = 0;
195+
switch (atoi(optarg)) {
196+
case 8:
194197
options->format |= XMP_FORMAT_8BIT;
198+
break;
199+
case 24:
200+
options->format |= XMP_FORMAT_32BIT;
201+
options->format_downmix = 24;
202+
break;
203+
case 32:
204+
options->format |= XMP_FORMAT_32BIT;
205+
break;
195206
}
196207
break;
197208
case 'C':
@@ -385,6 +396,11 @@ void get_options(int argc, char **argv, struct options *options)
385396
if (options->rate > 48000)
386397
options->rate = 48000; /* Max. rate 48 kHz */
387398

399+
if (xmp_vercode < 0x040700) {
400+
options->format &= ~XMP_FORMAT_32BIT;
401+
options->format_downmix = 0;
402+
}
403+
388404
/* apply guess if no driver selected */
389405
if (!options->driver_id)
390406
options->driver_id = driver_guess;

src/sound.c

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Extended Module Player
2-
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
2+
* Copyright (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr
33
*
44
* This file is part of the Extended Module Player and is distributed
55
* under the terms of the GNU General Public License. See the COPYING
@@ -101,16 +101,103 @@ const struct sound_driver *select_sound_driver(struct options *options)
101101
return NULL;
102102
}
103103

104-
/* Convert little-endian 16 bit samples to big-endian */
105-
void convert_endian(unsigned char *p, int l)
104+
/* Downmix 32-bit to 24-bit aligned (in-place) */
105+
void downmix_32_to_24_aligned(unsigned char *buffer, int buffer_bytes)
106+
{
107+
unsigned *buf32 = (unsigned *)buffer;
108+
int i;
109+
110+
for (i = 0; i < buffer_bytes; i += 4) {
111+
*buf32 >>= 8;
112+
buf32++;
113+
}
114+
}
115+
116+
/* Downmix 32-bit to 24-bit unaligned (in-place).
117+
* Returns the new number of useful bytes in the buffer. */
118+
int downmix_32_to_24_unaligned(unsigned char *buffer, int buffer_bytes)
119+
{
120+
unsigned char *out = buffer;
121+
int buffer_samples = buffer_bytes >> 2;
122+
int i;
123+
124+
/* Big endian 32-bit (22 11 00 XX) -> 24-bit (22 11 00)
125+
* Little endian 32-bit (XX 00 11 22) -> 24-bit (00 11 22)
126+
* Skip the first byte for little endian to allow reusing the same loop.
127+
*/
128+
if (!is_big_endian()) {
129+
buffer++;
130+
}
131+
132+
for (i = 0; i < buffer_samples; i++) {
133+
out[0] = buffer[0];
134+
out[1] = buffer[1];
135+
out[2] = buffer[2];
136+
out += 3;
137+
buffer += 4;
138+
}
139+
140+
return buffer_samples * 3;
141+
}
142+
143+
144+
/* Convert native endian 16-bit samples for file IO */
145+
void convert_endian_16bit(unsigned char *buffer, int buffer_bytes)
106146
{
107147
unsigned char b;
108148
int i;
109149

110-
for (i = 0; i < l; i++) {
111-
b = p[0];
112-
p[0] = p[1];
113-
p[1] = b;
114-
p += 2;
150+
for (i = 0; i < buffer_bytes; i += 2) {
151+
b = buffer[0];
152+
buffer[0] = buffer[1];
153+
buffer[1] = b;
154+
buffer += 2;
155+
}
156+
}
157+
158+
/* Convert native endian 24-bit unaligned samples for file IO */
159+
void convert_endian_24bit(unsigned char *buffer, int buffer_bytes)
160+
{
161+
unsigned char b;
162+
int i;
163+
164+
for (i = 0; i < buffer_bytes; i += 3) {
165+
b = buffer[0];
166+
buffer[0] = buffer[2];
167+
buffer[2] = b;
168+
buffer += 3;
169+
}
170+
}
171+
172+
/* Convert native endian 32-bit samples for file IO */
173+
void convert_endian_32bit(unsigned char *buffer, int buffer_bytes)
174+
{
175+
unsigned char a, b;
176+
int i;
177+
178+
for (i = 0; i < buffer_bytes; i+= 4) {
179+
a = buffer[0];
180+
b = buffer[1];
181+
buffer[0] = buffer[3];
182+
buffer[1] = buffer[2];
183+
buffer[2] = b;
184+
buffer[3] = a;
185+
buffer += 4;
186+
}
187+
}
188+
189+
/* Convert native endian 16-bit, 24-bit, or 32-bit samples for file IO */
190+
void convert_endian(unsigned char *buffer, int buffer_bytes, int bits)
191+
{
192+
switch (bits) {
193+
case 16:
194+
convert_endian_16bit(buffer, buffer_bytes);
195+
break;
196+
case 24:
197+
convert_endian_24bit(buffer, buffer_bytes);
198+
break;
199+
case 32:
200+
convert_endian_32bit(buffer, buffer_bytes);
201+
break;
115202
}
116203
}

src/sound.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,19 @@ extern const struct sound_driver *const sound_driver_list[];
5858
#define chkparm2(x,y,z,w) { if (!strcmp(s, x)) { \
5959
if (2 > sscanf(token, y, z, w)) parm_error(); } }
6060

61-
static inline int is_big_endian(void) {
61+
static inline int is_big_endian(void)
62+
{
6263
unsigned short w = 0x00ff;
6364
return (*(char *)&w == 0x00);
6465
}
6566

6667
void init_sound_drivers(void);
6768
const struct sound_driver *select_sound_driver(struct options *);
68-
void convert_endian(unsigned char *, int);
69+
void downmix_32_to_24_aligned(unsigned char *, int);
70+
int downmix_32_to_24_unaligned(unsigned char *, int);
71+
void convert_endian_16bit(unsigned char *, int);
72+
void convert_endian_24bit(unsigned char *, int);
73+
void convert_endian_32bit(unsigned char *, int);
74+
void convert_endian(unsigned char *, int, int);
6975

7076
#endif

src/sound_ahi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static int init(struct options *options) {
9292
if (AHIBuf[1]) {
9393
/* driver is initialized before calling libxmp, so this is OK : */
9494
options->format &= ~XMP_FORMAT_UNSIGNED;/* no unsigned with AHI */
95+
options->format &= ~XMP_FORMAT_32BIT;
9596
return 0;
9697
}
9798
}

src/sound_aiff.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ static int init(struct options *options)
9999
} else {
100100
fd = stdout;
101101
}
102+
options->format &= ~XMP_FORMAT_32BIT;
102103

103104
fwrite(hed, 1, 54, fd);
104105

@@ -107,8 +108,8 @@ static int init(struct options *options)
107108

108109
static void play(void *b, int len)
109110
{
110-
if (swap_endian && bits == 16) {
111-
convert_endian((unsigned char *)b, len);
111+
if (swap_endian) {
112+
convert_endian((unsigned char *)b, len, bits);
112113
}
113114
fwrite(b, 1, len, fd);
114115
size += len;

src/sound_aix.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Extended Module Player
2-
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
2+
* Copyright (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr
33
*
44
* This file is part of the Extended Module Player and is distributed
55
* under the terms of the GNU General Public License. See the COPYING
@@ -83,6 +83,8 @@ static int init(struct options *options)
8383
close(audio_fd);
8484
return -1;
8585
}
86+
options->format &= ~XMP_FORMAT_32BIT;
87+
8688
return 0;
8789
}
8890

0 commit comments

Comments
 (0)