source: GPL/lib32/fminstrload.c@ 18

Last change on this file since 18 was 18, checked in by vladest, 20 years ago

initial import

File size: 14.4 KB
Line 
1/*
2 * FM instrument loader
3 *
4 * (C) 2002 InnoTek Systemberatung GmbH
5 * Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <sound/driver.h>
24#include <sound/control.h>
25#include <sound/info.h>
26#include <sound/pcm.h>
27#include <sound/pcm_params.h>
28#include <sound/minors.h>
29#include <sound/asequencer.h>
30#include <sound/asound_fm.h>
31#include <sound/ainstr_fm.h>
32#include <sound/seq_kernel.h>
33#include <sound/instr.h>
34#include <linux/file.h>
35#include <linux/soundcard.h>
36
37#define LINUX
38#include <ossidc32.h>
39#include <stacktoflat.h>
40#include <stdlib.h>
41#include "soundoss.h"
42#include "instrfm.h"
43
44#define assert(a)
45
46typedef struct sbi_header
47{
48 char key[4];
49 char name[32];
50}
51sbi_header_t;
52
53typedef struct sbi_inst
54{
55 sbi_header_t header;
56#define DATA_LEN_2OP 16
57#define DATA_LEN_4OP 24
58 char data[DATA_LEN_4OP];
59}
60sbi_inst_t;
61
62/* offsets for SBI params */
63#define AM_VIB 0
64#define KSL_LEVEL 2
65#define ATTACK_DECAY 4
66#define SUSTAIN_RELEASE 6
67#define WAVE_SELECT 8
68
69/* offset for SBI instrument */
70#define CONNECTION 10
71#define OFFSET_4OP 11
72
73/* offsets in sbi_header.name for SBI extensions */
74#define ECHO_DELAY 25
75#define ECHO_ATTEN 26
76#define CHORUS_SPREAD 27
77#define TRNSPS 28
78#define FIX_DUR 29
79#define MODES 30
80#define FIX_KEY 31
81
82/* Number of elements in an array */
83#define NELEM(a) ( sizeof(a)/sizeof((a)[0]) )
84
85#define ADDR_PARTS 4 /* Number of part in a port description addr 1:2:3:4 */
86#define SEP ", \t" /* Separators for port description */
87
88#define SBI_FILE_TYPE_2OP 0
89#define SBI_FILE_TYPE_4OP 1
90
91static BOOL fInstumentsLoaded = FALSE;
92/* Default file type */
93static int file_type = SBI_FILE_TYPE_2OP;
94
95
96static void load_sb (midihandle *pHandle, char *pFile, int filesize, int bank);
97
98//******************************************************************************
99//******************************************************************************
100OSSRET OSS32_FMMidiLoadInstruments(OSSSTREAMID streamid)
101{
102 midihandle *pHandle = (midihandle *)streamid;
103
104 if(pHandle == NULL || pHandle->magic != MAGIC_MIDI_ALSA32) {
105 DebugInt3();
106 return OSSERR_INVALID_STREAMID;
107 }
108 if(fInstumentsLoaded) return OSSERR_SUCCESS;
109 fInstumentsLoaded = TRUE;
110
111 file_type = SBI_FILE_TYPE_4OP;
112
113 //set operation to non-blocking
114 pHandle->file.f_flags = O_NONBLOCK;
115
116 //load instruments
117 load_sb(pHandle, stdopl3, sizeof(stdopl3), 0);
118
119 //load drums
120 load_sb(pHandle, drumsopl3, sizeof(drumsopl3), 128);
121
122 return OSSERR_SUCCESS;
123}
124//******************************************************************************
125//******************************************************************************
126
127#ifdef DEBUG
128/*
129 * Show instrument FM operators
130 */
131static void
132show_op (fm_instrument_t * fm_instr) {
133 int i = 0;
134
135 do {
136 dprintf( (" OP%d: flags: %s %s %s %s\011OP%d: flags: %s %s %s %s\n",
137 i,
138 fm_instr->op[i].am_vib & (1 << 7) ? "AM" : " ",
139 fm_instr->op[i].am_vib & (1 << 6) ? "VIB" : " ",
140 fm_instr->op[i].am_vib & (1 << 5) ? "EGT" : " ",
141 fm_instr->op[i].am_vib & (1 << 4) ? "KSR" : " ",
142 i + 1,
143 fm_instr->op[i + 1].am_vib & (1 << 7) ? "AM" : " ",
144 fm_instr->op[i + 1].am_vib & (1 << 6) ? "VIB" : " ",
145 fm_instr->op[i + 1].am_vib & (1 << 5) ? "EGT" : " ",
146 fm_instr->op[i + 1].am_vib & (1 << 4) ? "KSR" : ""));
147 dprintf((" OP%d: MULT = 0x%x" "\011\011OP%d: MULT = 0x%x\n",
148 i, fm_instr->op[i].am_vib & 0x0f,
149 i + 1, fm_instr->op[i + 1].am_vib & 0x0f));
150 dprintf((" OP%d: KSL = 0x%x TL = 0x%x\011OP%d: KSL = 0x%x TL = 0x%x\n",
151 i, (fm_instr->op[i].ksl_level >> 6) & 0x03, fm_instr->op[i].ksl_level & 0x3f,
152 i + 1, (fm_instr->op[i + 1].ksl_level >> 6) & 0x03, fm_instr->op[i + 1].ksl_level & 0x3f));
153 dprintf((" OP%d: AR = 0x%x DL = 0x%x\011OP%d: AR = 0x%x DL = 0x%x\n",
154 i, (fm_instr->op[i].attack_decay >> 4) & 0x0f, fm_instr->op[i].attack_decay & 0x0f,
155 i + 1, (fm_instr->op[i + 1].attack_decay >> 4) & 0x0f, fm_instr->op[i + 1].attack_decay & 0x0f));
156 dprintf((" OP%d: SL = 0x%x RR = 0x%x\011OP%d: SL = 0x%x RR = 0x%x\n",
157 i, (fm_instr->op[i].sustain_release >> 4) & 0x0f, fm_instr->op[i].sustain_release & 0x0f,
158 i + 1, (fm_instr->op[i + 1].sustain_release >> 4) & 0x0f, fm_instr->op[i + 1].sustain_release & 0x0f));
159 dprintf((" OP%d: WS = 0x%x\011\011OP%d: WS = 0x%x\n",
160 i, fm_instr->op[i].wave_select & 0x07,
161 i + 1, fm_instr->op[i + 1].wave_select & 0x07));
162 dprintf((" FB = 0x%x, %s\n",
163 (fm_instr->feedback_connection[i / 2] >> 1) & 0x07,
164 fm_instr->feedback_connection[i / 2] & (1 << 0) ? "parallel" : "serial"));
165 i += 2;
166 }
167 while (i == (fm_instr->type == FM_PATCH_OPL3) << 1);
168
169 dprintf((" Extended data:\n"
170 " ED = %d EA = %d CS = %d TR = %d\n"
171 " FD = %d MO = %d FK = %d\n",
172 fm_instr->echo_delay, fm_instr->echo_atten, fm_instr->chorus_spread, fm_instr->trnsps,
173 fm_instr->fix_dur, fm_instr->modes, fm_instr->fix_key));
174}
175#endif
176
177/*
178 * Send patch to destination port
179 */
180static int load_patch (midihandle *pHandle, fm_instrument_t * fm_instr, int bank, int prg, char *name)
181{
182 snd_seq_instr_header_t *put;
183 snd_seq_instr_t id;
184 snd_seq_event_t ev;
185
186 size_t size;
187 int err;
188
189#ifdef DEBUG
190// show_op (fm_instr);
191#endif
192 if ((err = snd_instr_fm_convert_to_stream (fm_instr, name, &put, &size)) < 0) {
193 dprintf(("Unable to convert instrument %.3i to stream", prg));
194 return -1;
195 }
196 memset(&id, 0, sizeof(id));
197 id.std = SND_SEQ_INSTR_TYPE2_OPL2_3;
198 id.prg = prg;
199 id.bank = bank;
200 snd_instr_header_set_id(put, &id);
201
202 /* build event */
203 memset (&ev, 0, sizeof (ev));
204 ev.source.client = pHandle->clientid;
205 ev.source.port = pHandle->clientport;
206 ev.dest.client = pHandle->destclient;
207 ev.dest.port = pHandle->destport;
208
209 ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR;
210 ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
211
212 ev.type = SNDRV_SEQ_EVENT_INSTR_PUT;
213 ev.data.ext.len = size;
214 ev.data.ext.ptr = put;
215
216 err = pHandle->file.f_op->write(&pHandle->file, (char *)__Stack32ToFlat(&ev), sizeof(ev), &pHandle->file.f_pos);
217 if(err < 0) {
218 dprintf(("Unable to write an instrument %.3i put event: %x",prg, err));
219 return -1;
220 }
221 return 0;
222}
223
224/*
225 * Parse standard .sb or .o3 file
226 */
227static void load_sb (midihandle *pHandle, char *pFile, int filesize, int bank)
228{
229 int len, i, offset = 0;
230 int prg;
231
232 sbi_inst_t sbi_instr;
233 fm_instrument_t fm_instr;
234 int fm_instr_type;
235
236 len = (file_type == SBI_FILE_TYPE_4OP) ? DATA_LEN_4OP : DATA_LEN_2OP;
237 for (prg = 0;; prg++) {
238 if(offset + sizeof (sbi_header_t) > filesize) {
239 break; //EOF
240 }
241 memcpy(&sbi_instr.header, pFile+offset, sizeof (sbi_header_t));
242 offset += sizeof (sbi_header_t);
243
244 if (!strncmp (sbi_instr.header.key, "SBI\032", 4) || !strncmp (sbi_instr.header.key, "2OP\032", 4)) {
245 fm_instr_type = FM_PATCH_OPL2;
246 }
247 else
248 if (!strncmp (sbi_instr.header.key, "4OP\032", 4)) {
249 fm_instr_type = FM_PATCH_OPL3;
250 }
251 else {
252 fm_instr_type = 0;
253 }
254 if(offset + len > filesize) {
255 break; //EOF
256 }
257 memcpy(&sbi_instr.data, pFile+offset, len);
258 offset += len;
259
260 if (fm_instr_type == 0)
261 continue;
262
263 memset (&fm_instr, 0, sizeof (fm_instr));
264 fm_instr.type = fm_instr_type;
265
266 for (i = 0; i < 2; i++) {
267 fm_instr.op[i].am_vib = sbi_instr.data[AM_VIB + i];
268 fm_instr.op[i].ksl_level = sbi_instr.data[KSL_LEVEL + i];
269 fm_instr.op[i].attack_decay = sbi_instr.data[ATTACK_DECAY + i];
270 fm_instr.op[i].sustain_release = sbi_instr.data[SUSTAIN_RELEASE + i];
271 fm_instr.op[i].wave_select = sbi_instr.data[WAVE_SELECT + i];
272 }
273 fm_instr.feedback_connection[0] = sbi_instr.data[CONNECTION];
274
275 if (fm_instr_type == FM_PATCH_OPL3) {
276 for (i = 0; i < 2; i++) {
277 fm_instr.op[i + 2].am_vib = sbi_instr.data[OFFSET_4OP + AM_VIB + i];
278 fm_instr.op[i + 2].ksl_level = sbi_instr.data[OFFSET_4OP + KSL_LEVEL + i];
279 fm_instr.op[i + 2].attack_decay = sbi_instr.data[OFFSET_4OP + ATTACK_DECAY + i];
280 fm_instr.op[i + 2].sustain_release = sbi_instr.data[OFFSET_4OP + SUSTAIN_RELEASE + i];
281 fm_instr.op[i + 2].wave_select = sbi_instr.data[OFFSET_4OP + WAVE_SELECT + i];
282 }
283 fm_instr.feedback_connection[1] = sbi_instr.data[OFFSET_4OP + CONNECTION];
284 }
285
286 fm_instr.echo_delay = sbi_instr.header.name[ECHO_DELAY];
287 fm_instr.echo_atten = sbi_instr.header.name[ECHO_ATTEN];
288 fm_instr.chorus_spread = sbi_instr.header.name[CHORUS_SPREAD];
289 fm_instr.trnsps = sbi_instr.header.name[TRNSPS];
290 fm_instr.fix_dur = sbi_instr.header.name[FIX_DUR];
291 fm_instr.modes = sbi_instr.header.name[MODES];
292 fm_instr.fix_key = sbi_instr.header.name[FIX_KEY];
293
294 if (load_patch (pHandle, &fm_instr, bank, prg, sbi_instr.header.name) < 0)
295 break;
296 }
297 return;
298}
299/**
300 * \brief Convert the FM instrument to byte stream
301 * \param fm FM instrument handle
302 * \param name FM instrument name
303 * \param __data Result - allocated byte stream
304 * \param __size Result - size of allocated byte stream
305 * \return 0 on success otherwise a negative error code
306 */
307int snd_instr_fm_convert_to_stream(snd_instr_fm_t *fm,
308 const char *name,
309 snd_instr_header_t **__data,
310 size_t *__size)
311{
312 snd_instr_header_t *put;
313 fm_instrument_t *instr;
314 fm_xinstrument_t *xinstr;
315 int idx;
316
317 if (fm == NULL || __data == NULL)
318 return -EINVAL;
319 instr = (fm_instrument_t *)fm;
320 *__data = NULL;
321 *__size = 0;
322 if (snd_instr_header_malloc(&put, sizeof(fm_xinstrument_t)) < 0)
323 return -ENOMEM;
324 /* build header */
325 if (name)
326 snd_instr_header_set_name(put, name);
327 snd_instr_header_set_type(put, SND_SEQ_INSTR_ATYPE_DATA);
328 snd_instr_header_set_format(put, SND_SEQ_INSTR_ID_OPL2_3);
329 /* build data section */
330 xinstr = (fm_xinstrument_t *)snd_instr_header_get_data(put);
331 xinstr->stype = FM_STRU_INSTR;
332 xinstr->share_id[0] = __cpu_to_le32(instr->share_id[0]);
333 xinstr->share_id[1] = __cpu_to_le32(instr->share_id[1]);
334 xinstr->share_id[2] = __cpu_to_le32(instr->share_id[2]);
335 xinstr->share_id[3] = __cpu_to_le32(instr->share_id[3]);
336 xinstr->type = instr->type;
337 for (idx = 0; idx < 4; idx++) {
338 xinstr->op[idx].am_vib = instr->op[idx].am_vib;
339 xinstr->op[idx].ksl_level = instr->op[idx].ksl_level;
340 xinstr->op[idx].attack_decay = instr->op[idx].attack_decay;
341 xinstr->op[idx].sustain_release = instr->op[idx].sustain_release;
342 xinstr->op[idx].wave_select = instr->op[idx].wave_select;
343 }
344 for (idx = 0; idx < 2; idx++) {
345 xinstr->feedback_connection[idx] = instr->feedback_connection[idx];
346 }
347 xinstr->echo_delay = instr->echo_delay;
348 xinstr->echo_atten = instr->echo_atten;
349 xinstr->chorus_spread = instr->chorus_spread;
350 xinstr->trnsps = instr->trnsps;
351 xinstr->fix_dur = instr->fix_dur;
352 xinstr->modes = instr->modes;
353 xinstr->fix_key = instr->fix_key;
354
355 /* write result */
356 *__data = put;
357 *__size = sizeof(*put) + sizeof(fm_xinstrument_t);
358 return 0;
359}
360/**
361 * \brief allocate an empty #snd_instr_header_t using standard malloc
362 * \param ptr returned pointer
363 * \param len additional data length
364 * \return 0 on success otherwise negative error code
365 */
366int snd_instr_header_malloc(snd_instr_header_t **ptr, size_t len)
367{
368 assert(ptr);
369 *ptr = kmalloc(sizeof(snd_instr_header_t) + len, 0);
370 if (!*ptr)
371 return -ENOMEM;
372
373 memset(*ptr, 0, sizeof(snd_instr_header_t) + len);
374 (*ptr)->len = len;
375
376 return 0;
377}
378
379/**
380 * \brief frees a previously allocated #snd_instr_header_t
381 * \param pointer to object to free
382 */
383void snd_instr_header_free(snd_instr_header_t *obj)
384{
385 kfree(obj);
386}
387/**
388 * \brief Set the data type of an instr_header container
389 * \param info instr_header container
390 * \param type the data type
391 */
392void snd_instr_header_set_type(snd_instr_header_t *info, int type)
393{
394 assert(info);
395 info->data.type = type;
396}
397
398/**
399 * \brief Set the data format of an instr_header container
400 * \param info instr_header container
401 * \param format the data format string
402 */
403void snd_instr_header_set_format(snd_instr_header_t *info, const char *format)
404{
405 assert(info && format);
406 strncpy(info->data.data.format, format, sizeof(info->data.data.format));
407}
408
409/**
410 * \brief Set the instrument id of an instr_header container
411 * \param info instr_header container
412 * \param id instrument id pointer
413 */
414void snd_instr_header_set_id(snd_instr_header_t *info, const snd_seq_instr_t *id)
415{
416 assert(info && id);
417 info->id.instr = *(struct sndrv_seq_instr *)id;
418}
419
420/**
421 * \brief Set the data name of an instr_header container
422 * \param info instr_header container
423 * \param name the name string
424 */
425void snd_instr_header_set_name(snd_instr_header_t *info, const char *name)
426{
427 assert(info && name);
428 strncpy(info->data.name, name, sizeof(info->data.name));
429}
430
431/**
432 * \brief Get the extra data pointer of an instr_header container
433 * \param info instr_header container
434 * \return the extra data pointer
435 */
436void *snd_instr_header_get_data(const snd_instr_header_t *info)
437{
438 assert(info);
439 return (void*)((char*)info + sizeof(*info));
440}
441
442//******************************************************************************
443//******************************************************************************
444
Note: See TracBrowser for help on using the repository browser.