1 /*
2 * Schism Tracker - a cross-platform Impulse Tracker clone
3 * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
4 * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
5 * copyright (c) 2009 Storlek & Mrs. Brisby
6 * copyright (c) 2010-2012 Storlek
7 * URL: http://schismtracker.org/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #define NEED_BYTESWAP
25 #include "headers.h"
26 #include "fmt.h"
27
28
29 enum {
30 AU_ULAW = 1, /* µ-law */
31 AU_PCM_8 = 2, /* 8-bit linear PCM (RS_PCM8U in Modplug) */
32 AU_PCM_16 = 3, /* 16-bit linear PCM (RS_PCM16M) */
33 AU_PCM_24 = 4, /* 24-bit linear PCM */
34 AU_PCM_32 = 5, /* 32-bit linear PCM */
35 AU_IEEE_32 = 6, /* 32-bit IEEE floating point */
36 AU_IEEE_64 = 7, /* 64-bit IEEE floating point */
37 AU_ISDN_ULAW_ADPCM = 23, /* 8-bit ISDN µ-law (CCITT G.721 ADPCM compressed) */
38 };
39
40 struct au_header {
41 char magic[4]; /* ".snd" */
42 uint32_t data_offset, data_size, encoding, sample_rate, channels;
43 };
44
45
46 /* --------------------------------------------------------------------- */
47
fmt_au_read_info(dmoz_file_t * file,const uint8_t * data,size_t length)48 int fmt_au_read_info(dmoz_file_t *file, const uint8_t *data, size_t length)
49 {
50 struct au_header au;
51
52 if (!(length > 24 && memcmp(data, ".snd", 4) == 0))
53 return 0;
54
55 memcpy(&au, data, 24);
56 au.data_offset = bswapBE32(au.data_offset);
57 au.data_size = bswapBE32(au.data_size);
58 au.encoding = bswapBE32(au.encoding);
59 au.sample_rate = bswapBE32(au.sample_rate);
60 au.channels = bswapBE32(au.channels);
61
62 if (!(au.data_offset < length && au.data_size > 0 && au.data_size <= length - au.data_offset))
63 return 0;
64
65 file->smp_length = au.data_size / au.channels;
66 file->smp_flags = 0;
67 if (au.encoding == AU_PCM_16) {
68 file->smp_flags |= CHN_16BIT;
69 file->smp_length /= 2;
70 } else if (au.encoding == AU_PCM_24) {
71 file->smp_length /= 3;
72 } else if (au.encoding == AU_PCM_32 || au.encoding == AU_IEEE_32) {
73 file->smp_length /= 4;
74 } else if (au.encoding == AU_IEEE_64) {
75 file->smp_length /= 8;
76 }
77 if (au.channels >= 2) {
78 file->smp_flags |= CHN_STEREO;
79 }
80 file->description = "AU Sample";
81 if (au.data_offset > 24) {
82 int extlen = au.data_offset - 24;
83
84 file->title = strn_dup((const char *)data + 24, extlen);
85 }
86 file->smp_filename = file->title;
87 file->type = TYPE_SAMPLE_PLAIN;
88 return 1;
89 }
90
91 /* --------------------------------------------------------------------- */
92
fmt_au_load_sample(const uint8_t * data,size_t length,song_sample_t * smp)93 int fmt_au_load_sample(const uint8_t *data, size_t length, song_sample_t *smp)
94 {
95 struct au_header au;
96 uint32_t sflags = SF_BE | SF_PCMS;
97
98 if (length < 24)
99 return 0;
100
101 memcpy(&au, data, sizeof(au));
102 /* optimization: could #ifdef this out on big-endian machines */
103 au.data_offset = bswapBE32(au.data_offset);
104 au.data_size = bswapBE32(au.data_size);
105 au.encoding = bswapBE32(au.encoding);
106 au.sample_rate = bswapBE32(au.sample_rate);
107 au.channels = bswapBE32(au.channels);
108
109 /*#define C__(cond) if (!(cond)) { log_appendf(2, "failed condition: %s", #cond); return 0; }*/
110 #define C__(cond) if (!(cond)) { return 0; }
111 C__(memcmp(au.magic, ".snd", 4) == 0);
112 C__(au.data_offset >= 24);
113 C__(au.data_offset < length);
114 C__(au.data_size > 0);
115 C__(au.data_size <= length - au.data_offset);
116 C__(au.encoding == AU_PCM_8 || au.encoding == AU_PCM_16);
117 C__(au.channels == 1 || au.channels == 2);
118
119 smp->c5speed = au.sample_rate;
120 smp->volume = 64 * 4;
121 smp->global_volume = 64;
122 smp->length = au.data_size;
123 if (au.encoding == AU_PCM_16) {
124 sflags |= SF_16;
125 smp->length /= 2;
126 } else {
127 sflags |= SF_8;
128 }
129 if (au.channels == 2) {
130 sflags |= SF_SI;
131 smp->length /= 2;
132 } else {
133 sflags |= SF_M;
134 }
135
136 if (au.data_offset > 24) {
137 int extlen = MIN(25, au.data_offset - 24);
138 memcpy(smp->name, data + 24, extlen);
139 smp->name[extlen] = 0;
140 }
141
142 csf_read_sample(smp, sflags, data + au.data_offset, length - au.data_offset);
143
144 return 1;
145 }
146
147 /* --------------------------------------------------------------------------------------------------------- */
148
fmt_au_save_sample(disko_t * fp,song_sample_t * smp)149 int fmt_au_save_sample(disko_t *fp, song_sample_t *smp)
150 {
151 struct au_header au;
152 uint32_t ln;
153
154 memcpy(au.magic, ".snd", 4);
155
156 au.data_offset = bswapBE32(49); // header is 24 bytes, sample name is 25
157 ln = smp->length;
158 if (smp->flags & CHN_16BIT) {
159 ln *= 2;
160 au.encoding = bswapBE32(AU_PCM_16);
161 } else {
162 au.encoding = bswapBE32(AU_PCM_8);
163 }
164 au.sample_rate = bswapBE32(smp->c5speed);
165 if (smp->flags & CHN_STEREO) {
166 ln *= 2;
167 au.channels = bswapBE32(2);
168 } else {
169 au.channels = bswapBE32(1);
170 }
171 au.data_size = bswapBE32(ln);
172
173 disko_write(fp, &au, sizeof(au));
174 disko_write(fp, smp->name, 25);
175 csf_write_sample(fp, smp, SF_BE | SF_PCMS
176 | ((smp->flags & CHN_16BIT) ? SF_16 : SF_8)
177 | ((smp->flags & CHN_STEREO) ? SF_SI : SF_M),
178 UINT32_MAX);
179
180 return SAVE_SUCCESS;
181 }
182