source: mainline/uspace/lib/trackmod/protracker.c@ 8bf9058

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8bf9058 was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 9.3 KB
Line 
1/*
2 * Copyright (c) 2014 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup trackmod
30 * @{
31 */
32/**
33 * @file Protracker module (.mod).
34 */
35
36#include <errno.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <mem.h>
40
41#include "byteorder.h"
42#include "protracker.h"
43#include "trackmod.h"
44#include "types/protracker.h"
45
46/** Sample tag decoding table entry*/
47typedef struct {
48 /** Tag */
49 const char *tag;
50 /** Number of channels */
51 unsigned channels;
52} smptag_desc_t;
53
54/** Sample tag decoding table */
55static smptag_desc_t smp_tags[] = {
56 { .tag = "M.K.", .channels = 4 },
57 { .tag = "M!K!", .channels = 4 },
58 { .tag = "2CHN", .channels = 2 },
59 { .tag = "6CHN", .channels = 6 },
60 { .tag = "8CHN", .channels = 8 },
61 { .tag = "10CH", .channels = 10 },
62 { .tag = "12CH", .channels = 12 },
63 { .tag = "14CH", .channels = 14 },
64 { .tag = "16CH", .channels = 16 },
65 { .tag = "18CH", .channels = 18 },
66 { .tag = "20CH", .channels = 20 },
67 { .tag = "22CH", .channels = 22 },
68 { .tag = "24CH", .channels = 24 },
69 { .tag = "26CH", .channels = 26 },
70 { .tag = "28CH", .channels = 28 },
71 { .tag = "30CH", .channels = 30 },
72 { .tag = "32CH", .channels = 32 },
73};
74
75/** Decode sample tag.
76 *
77 * @param tag Tag
78 * @param channels Place to store number or channels.
79 * @return EOK on success, EINVAL if tag is not recognized.
80 */
81static errno_t smp_tag_decode(uint8_t *tag, size_t *channels)
82{
83 size_t nentries = sizeof(smp_tags) / sizeof(smptag_desc_t);
84 size_t i;
85
86 for (i = 0; i < nentries; i++) {
87 if (memcmp(tag, smp_tags[i].tag, 4) == 0) {
88 *channels = smp_tags[i].channels;
89 return EOK;
90 }
91 }
92
93 return EINVAL;
94}
95
96/** Get number of patterns stored in file.
97 *
98 * @param olist Order list
99 * @return Number of patterns in file
100 */
101static size_t order_list_get_npatterns(protracker_order_list_t *olist)
102{
103 size_t i;
104 size_t max_pat;
105
106 max_pat = 0;
107 for (i = 0; i < protracker_olist_len; i++) {
108 if (olist->order_list[i] > max_pat)
109 max_pat = olist->order_list[i];
110 }
111
112 return 1 + max_pat;
113}
114
115/** Decode pattern cell.
116 *
117 * @param pattern Pattern
118 * @param row Row number
119 * @param channel Channel number
120 * @param cell Place to store decoded cell
121 */
122static void protracker_decode_cell(uint32_t cdata, trackmod_cell_t *cell)
123{
124 uint32_t code;
125
126 code = uint32_t_be2host(cdata);
127 cell->period = (code >> (4 * 4)) & 0xfff;
128 cell->instr = (((code >> (7 * 4)) & 0xf) << 4) |
129 ((code >> (3 * 4)) & 0xf);
130 cell->effect = code & 0xfff;
131}
132
133/** Load Protracker patterns.
134 *
135 * @param f File to read from
136 * @param module Module being loaded to
137 * @return EOK on success, ENOMEM if out of memory, EIO on I/O error.
138 */
139static errno_t protracker_load_patterns(FILE *f, trackmod_module_t *module)
140{
141 size_t cells;
142 size_t i, j;
143 errno_t rc;
144 size_t nread;
145 uint32_t *buf = NULL;
146
147 cells = module->channels * protracker_pattern_rows;
148 buf = calloc(sizeof(uint32_t), cells);
149
150 if (buf == NULL) {
151 rc = ENOMEM;
152 goto error;
153 }
154
155 for (i = 0; i < module->patterns; i++) {
156 module->pattern[i].rows = protracker_pattern_rows;
157 module->pattern[i].channels = module->channels;
158 module->pattern[i].data = calloc(sizeof(trackmod_cell_t), cells);
159 if (module->pattern[i].data == NULL) {
160 rc = ENOMEM;
161 goto error;
162 }
163
164 nread = fread(buf, sizeof(uint32_t), cells, f);
165 if (nread != cells) {
166 printf("Error reading pattern.\n");
167 rc = EIO;
168 goto error;
169 }
170
171 /* Decode cells */
172 for (j = 0; j < cells; j++) {
173 protracker_decode_cell(buf[j],
174 &module->pattern[i].data[j]);
175 }
176 }
177
178 free(buf);
179 return EOK;
180error:
181 free(buf);
182 return rc;
183}
184
185/** Load protracker samples.
186 *
187 * @param f File being read from
188 * @param sample Sample header
189 * @param module Module being loaded to
190 * @return EOk on success, ENOMEM if out of memory, EIO on I/O error.
191 */
192static errno_t protracker_load_samples(FILE *f, protracker_smp_t *smp,
193 trackmod_module_t *module)
194{
195 errno_t rc;
196 size_t i;
197 uint8_t ftval;
198 size_t nread;
199 trackmod_sample_t *sample;
200
201 for (i = 0; i < module->instrs; i++) {
202 module->instr[i].samples = 1;
203 module->instr[i].sample = calloc(1, sizeof(trackmod_sample_t));
204 if (module->instr[i].sample == NULL) {
205 printf("Error allocating sample.\n");
206 rc = ENOMEM;
207 goto error;
208 }
209
210 sample = &module->instr[i].sample[0];
211 sample->length =
212 uint16_t_be2host(smp[i].length) * 2;
213 sample->bytes_smp = 1;
214 sample->data = calloc(1, sample->length);
215 if (sample->data == NULL) {
216 printf("Error allocating sample.\n");
217 rc = ENOMEM;
218 goto error;
219 }
220
221 nread = fread(sample->data, 1, sample->length, f);
222 if (nread != sample->length) {
223 printf("Error reading sample.\n");
224 rc = EIO;
225 goto error;
226 }
227
228 sample->def_vol = smp[i].def_vol;
229
230 sample->loop_start =
231 uint16_t_be2host(smp[i].loop_start) * 2;
232 sample->loop_len =
233 uint16_t_be2host(smp[i].loop_len) * 2;
234 if (sample->loop_len <= 2)
235 sample->loop_type = tl_no_loop;
236 else
237 sample->loop_type = tl_forward_loop;
238
239 /* Finetune is a 4-bit signed value. */
240 ftval = smp[i].finetune & 0x0f;
241 sample->finetune =
242 (ftval & 0x8) ? (ftval & 0x7) - 8 : ftval;
243 }
244
245 return EOK;
246error:
247 return rc;
248}
249
250/** Load protracker module.
251 *
252 * @param fname File name
253 * @param rmodule Place to store pointer to newly loaded module.
254 * @return EOK on success, ENONEM if out of memory, EIO on I/O error
255 * or if any error is found in the format of the file.
256 */
257errno_t trackmod_protracker_load(char *fname, trackmod_module_t **rmodule)
258{
259 FILE *f = NULL;
260 trackmod_module_t *module = NULL;
261 protracker_31smp_t mod31;
262 protracker_15smp_t mod15;
263 protracker_order_list_t *order_list;
264 protracker_smp_t *sample;
265 size_t samples;
266 size_t channels;
267 size_t patterns;
268 size_t i;
269 size_t nread;
270 errno_t rc;
271
272 f = fopen(fname, "rb");
273 if (f == NULL) {
274 printf("Error opening file.\n");
275 rc = EIO;
276 goto error;
277 }
278
279 nread = fread(&mod31, 1, sizeof(protracker_31smp_t), f);
280 if (nread < sizeof(protracker_15smp_t)) {
281 printf("File too small.\n");
282 rc = EIO;
283 goto error;
284 }
285
286 if (nread == sizeof(protracker_31smp_t)) {
287 /* Could be 31-sample variant */
288 rc = smp_tag_decode(mod31.sample_tag, &channels);
289 if (rc != EOK) {
290 samples = 15;
291 channels = 4;
292 } else {
293 samples = 31;
294 }
295 } else {
296 samples = 15;
297 channels = 4;
298 }
299
300 if (samples == 15) {
301 memcpy(&mod15, &mod31, sizeof(protracker_15smp_t));
302
303 if (fseek(f, sizeof(protracker_15smp_t), SEEK_SET) < 0) {
304 printf("Error seeking.\n");
305 rc = EIO;
306 goto error;
307 }
308
309 order_list = &mod15.order_list;
310 sample = mod15.sample;
311 } else {
312 order_list = &mod31.order_list;
313 sample = mod31.sample;
314 }
315
316 patterns = order_list_get_npatterns(order_list);
317
318 module = trackmod_module_new();
319 if (module == NULL) {
320 printf("Out of memory.\n");
321 rc = ENOMEM;
322 goto error;
323 }
324
325 module->channels = channels;
326
327 module->instrs = samples;
328 module->instr = calloc(sizeof(trackmod_instr_t), samples);
329 if (module->instr == NULL) {
330 printf("Out of memory.\n");
331 rc = ENOMEM;
332 goto error;
333 }
334
335 module->patterns = patterns;
336 module->pattern = calloc(sizeof(trackmod_pattern_t), patterns);
337 if (module->pattern == NULL) {
338 printf("Out of memory.\n");
339 rc = ENOMEM;
340 goto error;
341 }
342
343 /* Order list */
344 module->ord_list_len = order_list->order_list_len;
345 module->ord_list = calloc(sizeof(size_t), module->ord_list_len);
346 if (module->ord_list == NULL) {
347 printf("Out of memory.\n");
348 rc = ENOMEM;
349 goto error;
350 }
351
352 for (i = 0; i < order_list->order_list_len; i++) {
353 module->ord_list[i] = order_list->order_list[i];
354 }
355
356 /* The 'mark' byte may or may not contain a valid restart position */
357 if (order_list->mark < order_list->order_list_len) {
358 module->restart_pos = order_list->mark;
359 }
360
361 /* Load patterns */
362 rc = protracker_load_patterns(f, module);
363 if (rc != EOK)
364 goto error;
365
366 /* Load samples */
367 rc = protracker_load_samples(f, sample, module);
368 if (rc != EOK)
369 goto error;
370
371 (void) fclose(f);
372
373 module->def_bpm = protracker_def_bpm;
374 module->def_tpr = protracker_def_tpr;
375
376 *rmodule = module;
377 return EOK;
378error:
379 if (module != NULL)
380 trackmod_module_destroy(module);
381 if (f != NULL)
382 (void) fclose(f);
383 return rc;
384}
385
386/** @}
387 */
Note: See TracBrowser for help on using the repository browser.