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