1 | #include <string.h> |
2 | |
3 | #include "wuff_config.h" |
4 | #include "wuff.h" |
5 | #include "wuff_internal.h" |
6 | |
7 | |
8 | wuff_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 | |
37 | wuff_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 | |
50 | wuff_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 | |
74 | wuff_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 | |
84 | wuff_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 | |
99 | wuff_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 | |
118 | wuff_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 | |
194 | void 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 | |