1#include <string.h>
2
3#include "wuff_config.h"
4#include "wuff.h"
5#include "wuff_internal.h"
6
7
8wuff_sint32 wuff_open(struct wuff_handle ** handle_pointer, struct wuff_callback * callback, void * userdata)
9{
10 struct wuff_handle * handle;
11 wuff_sint32 wuff_status;
12
13 if (handle_pointer == NULL || callback == NULL)
14 return WUFF_INVALID_PARAM;
15
16 handle = wuff_alloc(sizeof(struct wuff_handle));
17 if (handle == NULL)
18 return WUFF_MEMALLOC_ERROR;
19
20 memset(handle, 0, sizeof(struct wuff_handle));
21 handle->buffer.data = NULL;
22 handle->callback = callback;
23 handle->userdata = userdata;
24
25 wuff_status = wuff_setup(handle);
26 if (wuff_status < 0)
27 {
28 wuff_cleanup(handle);
29 return wuff_status;
30 }
31
32 *handle_pointer = handle;
33
34 return WUFF_SUCCESS;
35}
36
37wuff_sint32 wuff_close(struct wuff_handle * handle)
38{
39 wuff_sint32 wuff_status;
40
41 if (handle == NULL)
42 return WUFF_INVALID_PARAM;
43
44 wuff_status = wuff_cleanup(handle);
45 WUFF_STATUS_BAIL()
46
47 return WUFF_SUCCESS;
48}
49
50wuff_sint32 wuff_seek(struct wuff_handle * handle, wuff_uint64 offset)
51{
52 wuff_sint32 wuff_status;
53 wuff_uint64 seek_offset;
54
55 if (handle == NULL)
56 return WUFF_INVALID_PARAM;
57
58 /* Clamp offset to stream length. */
59 offset = offset <= handle->stream.length ? offset : handle->stream.length;
60 seek_offset = offset * handle->stream.header.block_size;
61 wuff_status = handle->callback->seek(handle->userdata, handle->stream.data.offset + seek_offset);
62 WUFF_STATUS_BAIL()
63
64 handle->stream.position = offset;
65 handle->output.block_offset = 0;
66
67 /* A new position requires an empty buffer. */
68 wuff_status = wuff_buffer_clear(handle);
69 WUFF_STATUS_BAIL()
70
71 return WUFF_SUCCESS;
72}
73
74wuff_sint32 wuff_tell(struct wuff_handle * handle, wuff_uint64 * offset)
75{
76 if (handle == NULL)
77 return WUFF_INVALID_PARAM;
78
79 *offset = handle->stream.position;
80
81 return WUFF_SUCCESS;
82}
83
84wuff_sint32 wuff_stream_info(struct wuff_handle * handle, struct wuff_info * info)
85{
86 if (handle == NULL || info == NULL)
87 return WUFF_INVALID_PARAM;
88
89 info->format = handle->stream.format;
90 info->channels = handle->stream.header.channels;
91 info->sample_rate = handle->stream.header.sample_rate;
92 info->bits_per_sample = handle->stream.header.bits_per_sample;
93 info->length = handle->stream.length;
94 /* Think about adding channel mapping and perhaps other things. */
95
96 return WUFF_SUCCESS;
97}
98
99wuff_sint32 wuff_format(struct wuff_handle * handle, wuff_uint16 format)
100{
101 wuff_sint32 wuff_status;
102
103 if (handle == NULL)
104 return WUFF_INVALID_PARAM;
105 else if (format >= WUFF_FORMAT_MAX)
106 return WUFF_FORMAT_UNSUPPORTED;
107
108 /* A format change resets the position to the start of the block. */
109 wuff_status = wuff_seek(handle, handle->stream.position);
110 WUFF_STATUS_BAIL()
111
112 wuff_status = wuff_set_output_format(handle, format);
113 WUFF_STATUS_BAIL()
114
115 return WUFF_SUCCESS;
116}
117
118wuff_sint32 wuff_read(struct wuff_handle * handle, wuff_uint8 * out_buffer, size_t * out_size)
119{
120 size_t current_offset;
121 size_t r_samples, num_samples;
122 wuff_uint8 head_offset, head, tail, sample_size;
123 wuff_uint8 * in_buffer;
124 wuff_sint32 wuff_status;
125
126 if (handle == NULL || out_buffer == NULL || out_size == NULL)
127 return WUFF_INVALID_PARAM;
128
129 if (*out_size == 0)
130 return WUFF_SUCCESS;
131
132 sample_size = (wuff_uint8)handle->output.bytes_per_sample;
133
134 /* Calculating the number of samples that fit into the application buffer. */
135 /* The first and last sample may be truncated. */
136 current_offset = handle->output.block_offset;
137 head_offset = current_offset % sample_size;
138 head = head_offset == 0 ? 0 : sample_size - head_offset;
139 num_samples = wuff_calculate_samples(*out_size, sample_size, &head, &tail);
140
141 /* Requesting the number of samples from the buffer. */
142 /* Calculate the new sample count if necessary and write the output. */
143 r_samples = num_samples;
144 wuff_status = wuff_buffer_request(handle, &in_buffer, &r_samples);
145 WUFF_STATUS_BAIL()
146 else if (r_samples == 0)
147 {
148 /* Possible EOF. */
149 *out_size = 0;
150 }
151 else
152 {
153 if (r_samples == 1 && head != 0)
154 {
155 /* Only the first truncated sample fits. */
156 /* I really hope nobody will use small buffers like this. */
157 num_samples = 0;
158 tail = 0;
159 }
160 else
161 {
162 /* At this point the first (possibly truncated) sample will be fully written. */
163 /* Subtract the first and last sample from the count if they're truncated. */
164 if (r_samples < num_samples)
165 tail = 0;
166 num_samples = r_samples - !!head - !!tail;
167 }
168
169 handle->output.function(out_buffer, in_buffer, num_samples, head_offset, head, tail);
170
171 /* Report the number of bytes written. */
172 *out_size = num_samples * sample_size + head + tail;
173
174 /* Adjust the block offset and sample position. */
175 current_offset += *out_size;
176 if (current_offset >= handle->output.block_size)
177 {
178 handle->stream.position += current_offset / handle->output.block_size;
179 handle->output.block_offset = current_offset % handle->output.block_size;
180 }
181 else
182 {
183 handle->output.block_offset = current_offset;
184 }
185
186 /* Release the fully processed samples from the buffer. */
187 wuff_status = wuff_buffer_release(handle, head_offset + head == sample_size ? num_samples + 1 : num_samples);
188 WUFF_STATUS_BAIL()
189 }
190
191 return WUFF_SUCCESS;
192}
193
194void wuff_version(struct wuff_version * version)
195{
196 if (version == NULL)
197 return;
198
199 version->major = WUFF_VERSION_MAJOR;
200 version->minor = WUFF_VERSION_MINOR;
201 version->build = WUFF_VERSION_BUILD;
202 version->revision = WUFF_VERSION_REVISION;
203}
204