source: mainline/uspace/lib/trackmod/protracker.c@ 4e2d387

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

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

  • 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
116/** Decode pattern cell.
117 *
118 * @param pattern Pattern
119 * @param row Row number
120 * @param channel Channel number
121 * @param cell Place to store decoded cell
122 */
123static void protracker_decode_cell(uint32_t cdata, trackmod_cell_t *cell)
124{
125 uint32_t code;
126
127 code = uint32_t_be2host(cdata);
128 cell->period = (code >> (4 * 4)) & 0xfff;
129 cell->instr = (((code >> (7 * 4)) & 0xf) << 4) |
130 ((code >> (3 * 4)) & 0xf);
131 cell->effect = code & 0xfff;
132}
133
134/** Load Protracker patterns.
135 *
136 * @param f File to read from
137 * @param module Module being loaded to
138 * @return EOK on success, ENOMEM if out of memory, EIO on I/O error.
139 */
140static errno_t protracker_load_patterns(FILE *f, trackmod_module_t *module)
141{
142 size_t cells;
143 size_t i, j;
144 errno_t rc;
145 size_t nread;
146 uint32_t *buf = NULL;
147
148 cells = module->channels * protracker_pattern_rows;
149 buf = calloc(sizeof(uint32_t), cells);
150
151 if (buf == NULL) {
152 rc = ENOMEM;
153 goto error;
154 }
155
156 for (i = 0; i < module->patterns; i++) {
157 module->pattern[i].rows = protracker_pattern_rows;
158 module->pattern[i].channels = module->channels;
159 module->pattern[i].data = calloc(sizeof(trackmod_cell_t), cells);
160 if (module->pattern[i].data == NULL) {
161 rc = ENOMEM;
162 goto error;
163 }
164
165 nread = fread(buf, sizeof(uint32_t), cells, f);
166 if (nread != cells) {
167 printf("Error reading pattern.\n");
168 rc = EIO;
169 goto error;
170 }
171
172 /* Decode cells */
173 for (j = 0; j < cells; j++) {
174 protracker_decode_cell(buf[j],
175 &module->pattern[i].data[j]);
176 }
177 }
178
179 free(buf);
180 return EOK;
181error:
182 free(buf);
183 return rc;
184}
185
186/** Load protracker samples.
187 *
188 * @param f File being read from
189 * @param sample Sample header
190 * @param module Module being loaded to
191 * @return EOk on success, ENOMEM if out of memory, EIO on I/O error.
192 */
193static errno_t protracker_load_samples(FILE *f, protracker_smp_t *smp,
194 trackmod_module_t *module)
195{
196 errno_t rc;
197 size_t i;
198 uint8_t ftval;
199 size_t nread;
200 trackmod_sample_t *sample;
201
202 for (i = 0; i < module->instrs; i++) {
203 module->instr[i].samples = 1;
204 module->instr[i].sample = calloc(1, sizeof(trackmod_sample_t));
205 if (module->instr[i].sample == NULL) {
206 printf("Error allocating sample.\n");
207 rc = ENOMEM;
208 goto error;
209 }
210
211 sample = &module->instr[i].sample[0];
212 sample->length =
213 uint16_t_be2host(smp[i].length) * 2;
214 sample->bytes_smp = 1;
215 sample->data = calloc(1, sample->length);
216 if (sample->data == NULL) {
217 printf("Error allocating sample.\n");
218 rc = ENOMEM;
219 goto error;
220 }
221
222 nread = fread(sample->data, 1, sample->length, f);
223 if (nread != sample->length) {
224 printf("Error reading sample.\n");
225 rc = EIO;
226 goto error;
227 }
228
229 sample->def_vol = smp[i].def_vol;
230
231 sample->loop_start =
232 uint16_t_be2host(smp[i].loop_start) * 2;
233 sample->loop_len =
234 uint16_t_be2host(smp[i].loop_len) * 2;
235 if (sample->loop_len <= 2)
236 sample->loop_type = tl_no_loop;
237 else
238 sample->loop_type = tl_forward_loop;
239
240 /* Finetune is a 4-bit signed value. */
241 ftval = smp[i].finetune & 0x0f;
242 sample->finetune =
243 (ftval & 0x8) ? (ftval & 0x7) - 8 : ftval;
244 }
245
246 return EOK;
247error:
248 return rc;
249}
250
251/** Load protracker module.
252 *
253 * @param fname File name
254 * @param rmodule Place to store pointer to newly loaded module.
255 * @return EOK on success, ENONEM if out of memory, EIO on I/O error
256 * or if any error is found in the format of the file.
257 */
258errno_t trackmod_protracker_load(char *fname, trackmod_module_t **rmodule)
259{
260 FILE *f = NULL;
261 trackmod_module_t *module = NULL;
262 protracker_31smp_t mod31;
263 protracker_15smp_t mod15;
264 protracker_order_list_t *order_list;
265 protracker_smp_t *sample;
266 size_t samples;
267 size_t channels;
268 size_t patterns;
269 size_t i;
270 size_t nread;
271 errno_t rc;
272
273 f = fopen(fname, "rb");
274 if (f == NULL) {
275 printf("Error opening file.\n");
276 rc = EIO;
277 goto error;
278 }
279
280 nread = fread(&mod31, 1, sizeof(protracker_31smp_t), f);
281 if (nread < sizeof(protracker_15smp_t)) {
282 printf("File too small.\n");
283 rc = EIO;
284 goto error;
285 }
286
287 if (nread == sizeof(protracker_31smp_t)) {
288 /* Could be 31-sample variant */
289 rc = smp_tag_decode(mod31.sample_tag, &channels);
290 if (rc != EOK) {
291 samples = 15;
292 channels = 4;
293 } else {
294 samples = 31;
295 }
296 } else {
297 samples = 15;
298 channels = 4;
299 }
300
301 if (samples == 15) {
302 memcpy(&mod15, &mod31, sizeof(protracker_15smp_t));
303
304 if (fseek(f, sizeof(protracker_15smp_t), SEEK_SET) < 0) {
305 printf("Error seeking.\n");
306 rc = EIO;
307 goto error;
308 }
309
310 order_list = &mod15.order_list;
311 sample = mod15.sample;
312 } else {
313 order_list = &mod31.order_list;
314 sample = mod31.sample;
315 }
316
317 patterns = order_list_get_npatterns(order_list);
318
319 module = trackmod_module_new();
320 if (module == NULL) {
321 printf("Out of memory.\n");
322 rc = ENOMEM;
323 goto error;
324 }
325
326 module->channels = channels;
327
328 module->instrs = samples;
329 module->instr = calloc(sizeof(trackmod_instr_t), samples);
330 if (module->instr == NULL) {
331 printf("Out of memory.\n");
332 rc = ENOMEM;
333 goto error;
334 }
335
336 module->patterns = patterns;
337 module->pattern = calloc(sizeof(trackmod_pattern_t), patterns);
338 if (module->pattern == NULL) {
339 printf("Out of memory.\n");
340 rc = ENOMEM;
341 goto error;
342 }
343
344 /* Order list */
345 module->ord_list_len = order_list->order_list_len;
346 module->ord_list = calloc(sizeof(size_t), module->ord_list_len);
347 if (module->ord_list == NULL) {
348 printf("Out of memory.\n");
349 rc = ENOMEM;
350 goto error;
351 }
352
353 for (i = 0; i < order_list->order_list_len; i++) {
354 module->ord_list[i] = order_list->order_list[i];
355 }
356
357 /* The 'mark' byte may or may not contain a valid restart position */
358 if (order_list->mark < order_list->order_list_len) {
359 module->restart_pos = order_list->mark;
360 }
361
362 /* Load patterns */
363 rc = protracker_load_patterns(f, module);
364 if (rc != EOK)
365 goto error;
366
367 /* Load samples */
368 rc = protracker_load_samples(f, sample, module);
369 if (rc != EOK)
370 goto error;
371
372 (void) fclose(f);
373
374 module->def_bpm = protracker_def_bpm;
375 module->def_tpr = protracker_def_tpr;
376
377 *rmodule = module;
378 return EOK;
379error:
380 if (module != NULL)
381 trackmod_module_destroy(module);
382 if (f != NULL)
383 (void) fclose(f);
384 return rc;
385}
386
387/** @}
388 */
Note: See TracBrowser for help on using the repository browser.