1 /* libSoX file format: WavPack (c) 2008 robs@users.sourceforge.net
2 *
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #include "sox_i.h"
19
20 #ifdef HAVE_WAVPACK_H
21 #define HAVE_WAVPACK 1
22 #endif
23
24 #ifdef HAVE_WAVPACK
25
26 #ifdef HAVE_WAVPACK_H
27 #include <wavpack.h>
28 #else
29 #include <wavpack/wavpack.h>
30 #endif
31
32 typedef struct {
33 WavpackContext * codec;
34 size_t first_block_size;
35 } priv_t;
36
ft_read_b_buf(void * ft,void * buf,int32_t len)37 static int32_t ft_read_b_buf(void * ft, void * buf, int32_t len) {
38 return (int32_t)lsx_read_b_buf((sox_format_t *)ft, buf, (size_t)len);}
ft_tell(void * ft)39 static uint32_t ft_tell(void * ft) {
40 return lsx_tell((sox_format_t *)ft);}
ft_seek_abs(void * ft,uint32_t offset)41 static int ft_seek_abs(void * ft, uint32_t offset) {
42 return lsx_seeki((sox_format_t *)ft, (off_t)offset, SEEK_SET);}
ft_seek_rel(void * ft,int32_t offset,int mode)43 static int ft_seek_rel(void * ft, int32_t offset, int mode) {
44 return lsx_seeki((sox_format_t *)ft, (off_t)offset, mode);}
ft_unreadb(void * ft,int b)45 static int ft_unreadb(void * ft, int b) {
46 return lsx_unreadb((sox_format_t *)ft, (unsigned)b);}
ft_filelength(void * ft)47 static uint32_t ft_filelength(void * ft) {
48 return (uint32_t)lsx_filelength((sox_format_t *)ft);}
ft_is_seekable(void * ft)49 static int ft_is_seekable(void *ft) {
50 return ((sox_format_t *)ft)->seekable;}
ft_write_b_buf(void * ft,void * buf,int32_t len)51 static int32_t ft_write_b_buf(void * ft, void * buf, int32_t len) {
52 priv_t * p = (priv_t *)((sox_format_t *)ft)->priv;
53 if (!p->first_block_size)
54 p->first_block_size = len;
55 return (int32_t)lsx_write_b_buf((sox_format_t *)ft, buf, (size_t)len);}
56
57 static WavpackStreamReader io_fns = {
58 ft_read_b_buf, ft_tell, ft_seek_abs, ft_seek_rel,
59 ft_unreadb, ft_filelength, ft_is_seekable, ft_write_b_buf
60 };
61
start_read(sox_format_t * ft)62 static int start_read(sox_format_t * ft)
63 {
64 priv_t * p = (priv_t *)ft->priv;
65 char msg[80];
66
67 p->codec = WavpackOpenFileInputEx(&io_fns, ft, NULL, msg, OPEN_NORMALIZE, 0);
68 if (!p->codec) {
69 lsx_fail_errno(ft, SOX_EHDR, "%s", msg);
70 return SOX_EOF;
71 }
72 ft->encoding.bits_per_sample = WavpackGetBytesPerSample(p->codec) << 3;
73 ft->signal.channels = WavpackGetNumChannels(p->codec);
74 if (WavpackGetSampleRate(p->codec) && ft->signal.rate && ft->signal.rate != WavpackGetSampleRate(p->codec))
75 lsx_warn("`%s': overriding sample rate", ft->filename);
76 else ft->signal.rate = WavpackGetSampleRate(p->codec);
77
78 ft->signal.length = (uint64_t)WavpackGetNumSamples(p->codec) * ft->signal.channels;
79 ft->encoding.encoding = (WavpackGetMode(p->codec) & MODE_FLOAT)?
80 SOX_ENCODING_WAVPACKF : SOX_ENCODING_WAVPACK;
81 return SOX_SUCCESS;
82 }
83
read_samples(sox_format_t * ft,sox_sample_t * buf,size_t len)84 static size_t read_samples(sox_format_t * ft, sox_sample_t * buf, size_t len)
85 {
86 priv_t * p = (priv_t *)ft->priv;
87 size_t i, actual = WavpackUnpackSamples(p->codec, buf, (uint32_t) len / ft->signal.channels) * ft->signal.channels;
88 for (i = 0; i < actual; ++i) switch (ft->encoding.bits_per_sample) {
89 SOX_SAMPLE_LOCALS;
90 case 8: buf[i] = SOX_SIGNED_8BIT_TO_SAMPLE(buf[i],); break;
91 case 16: buf[i] = SOX_SIGNED_16BIT_TO_SAMPLE(buf[i],); break;
92 case 24: buf[i] = SOX_SIGNED_24BIT_TO_SAMPLE(buf[i],); break;
93 case 32: buf[i] = ft->encoding.encoding == SOX_ENCODING_WAVPACKF?
94 SOX_FLOAT_32BIT_TO_SAMPLE(*(float *)&buf[i], ft->clips) :
95 SOX_SIGNED_32BIT_TO_SAMPLE(buf[i],);
96 break;
97 }
98 return actual;
99 }
100
stop_read(sox_format_t * ft)101 static int stop_read(sox_format_t * ft)
102 {
103 priv_t * p = (priv_t *)ft->priv;
104 WavpackCloseFile(p->codec);
105 return SOX_SUCCESS;
106 }
107
start_write(sox_format_t * ft)108 static int start_write(sox_format_t * ft)
109 {
110 priv_t * p = (priv_t *)ft->priv;
111 WavpackConfig config;
112 uint64_t size64;
113
114 p->codec = WavpackOpenFileOutput(ft_write_b_buf, ft, NULL);
115 if (!p->codec) {
116 lsx_fail_errno(ft, SOX_ENOMEM, "WavPack error creating output instance");
117 return SOX_EOF;
118 }
119 memset(&config, 0, sizeof(config));
120 config.bytes_per_sample = ft->encoding.bits_per_sample >> 3;
121 config.bits_per_sample = ft->encoding.bits_per_sample;
122 config.channel_mask = ft->signal.channels == 1? 4 :
123 ft->signal.channels == 2? 3 : (1 << ft->signal.channels) - 1;
124 config.num_channels = ft->signal.channels;
125 config.sample_rate = (int32_t)(ft->signal.rate + .5);
126 config.flags = CONFIG_VERY_HIGH_FLAG;
127 size64 = ft->signal.length / ft->signal.channels;
128 if (!WavpackSetConfiguration(p->codec, &config, size64 && size64 <= UINT_MAX ? (uint32_t)size64 : (uint32_t)-1)) {
129 lsx_fail_errno(ft, SOX_EHDR, "%s", WavpackGetErrorMessage(p->codec));
130 return SOX_EOF;
131 }
132 WavpackPackInit(p->codec);
133 return SOX_SUCCESS;
134 }
135
write_samples(sox_format_t * ft,const sox_sample_t * buf,size_t len)136 static size_t write_samples(sox_format_t * ft, const sox_sample_t * buf, size_t len)
137 {
138 priv_t * p = (priv_t *)ft->priv;
139 size_t i;
140 int32_t * obuf = lsx_malloc(len * sizeof(*obuf));
141 int result;
142
143 for (i = 0; i < len; ++i) switch (ft->encoding.bits_per_sample) {
144 SOX_SAMPLE_LOCALS;
145 case 8: obuf[i] = SOX_SAMPLE_TO_SIGNED_8BIT(buf[i], ft->clips); break;
146 case 16: obuf[i] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[i], ft->clips); break;
147 case 24: obuf[i] = SOX_SAMPLE_TO_SIGNED_24BIT(buf[i], ft->clips) << 8;
148 obuf[i] >>= 8; break;
149 case 32: obuf[i] = ft->encoding.encoding == SOX_ENCODING_WAVPACKF?
150 SOX_SAMPLE_TO_SIGNED_24BIT(*(float *)&buf[i], ft->clips) :
151 SOX_SAMPLE_TO_SIGNED_32BIT(buf[i], ft->clips);
152 break;
153 }
154 result = WavpackPackSamples(p->codec, obuf, (uint32_t) len / ft->signal.channels);
155 free(obuf);
156 return result? len : 0;
157 }
158
stop_write(sox_format_t * ft)159 static int stop_write(sox_format_t * ft)
160 {
161 priv_t * p = (priv_t *)ft->priv;
162 WavpackFlushSamples(p->codec);
163 if (!WavpackFlushSamples(p->codec)) {
164 lsx_fail_errno(ft, SOX_EINVAL, "%s", WavpackGetErrorMessage(p->codec));
165 return SOX_EOF;
166 }
167 if (ft->seekable && WavpackGetNumSamples(p->codec) != WavpackGetSampleIndex(p->codec) && p->first_block_size >= 4) {
168 char * buf = lsx_malloc(p->first_block_size);
169 lsx_rewind(ft);
170 lsx_readchars(ft, buf, p->first_block_size);
171 if (!memcmp(buf, "wvpk", (size_t)4)) {
172 WavpackUpdateNumSamples(p->codec, buf);
173 lsx_rewind(ft);
174 lsx_writebuf(ft, buf, p->first_block_size);
175 }
176 free(buf);
177 }
178 p->codec = WavpackCloseFile(p->codec);
179 return SOX_SUCCESS;
180 }
181
seek(sox_format_t * ft,uint64_t offset)182 static int seek(sox_format_t * ft, uint64_t offset)
183 {
184 priv_t * p = (priv_t *)ft->priv;
185
186 return WavpackSeekSample(p->codec, (uint32_t) (offset / ft->signal.channels))? SOX_SUCCESS : SOX_EOF;
187 }
188
LSX_FORMAT_HANDLER(wavpack)189 LSX_FORMAT_HANDLER(wavpack)
190 {
191 static char const * const names[] = {"wv", NULL};
192 static unsigned const write_encodings[] = {
193 SOX_ENCODING_WAVPACK, 8, 16, 24, 32, 0,
194 SOX_ENCODING_WAVPACKF, 32, 0,
195 0};
196 static sox_format_handler_t handler = {SOX_LIB_VERSION_CODE,
197 "Lossless, lossy, and hybrid audio compression",
198 names, 0,
199 start_read, read_samples, stop_read,
200 start_write, write_samples, stop_write,
201 seek, write_encodings, NULL, sizeof(priv_t)
202 };
203 return &handler;
204 }
205
206 #endif /* HAVE_WAVPACK */
207