2018-02-19 21:14:22 +00:00
|
|
|
/*
|
|
|
|
TiMidity++ -- MIDI to WAVE converter and player
|
|
|
|
Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
|
|
|
|
Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* sffile.c
|
|
|
|
* read SoundFont file (SBK/SF2) and store the layer lists
|
|
|
|
*
|
|
|
|
* Copyright (C) 1996,1997 Takashi Iwai
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Modified by Masanao Izumo <mo@goice.co.jp>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "timidity.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "instrum.h"
|
|
|
|
|
|
|
|
namespace TimidityPlus
|
|
|
|
{
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* preset / instrument bag record
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* function prototypes
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#define NEW(type,nums) (type*)safe_malloc(sizeof(type) * (nums))
|
|
|
|
|
|
|
|
static int READCHUNK(SFChunk *vp, struct timidity_file *tf)
|
|
|
|
{
|
|
|
|
if (tf_read(vp, 8, 1, tf) != 1)
|
|
|
|
return -1;
|
|
|
|
vp->size = LE_LONG(vp->size);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int READDW(uint32_t *vp, struct timidity_file *tf)
|
|
|
|
{
|
|
|
|
if (tf_read(vp, 4, 1, tf) != 1)
|
|
|
|
return -1;
|
|
|
|
*vp = LE_LONG(*vp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int READW(uint16_t *vp, struct timidity_file *tf)
|
|
|
|
{
|
|
|
|
if (tf_read(vp, 2, 1, tf) != 1)
|
|
|
|
return -1;
|
|
|
|
*vp = LE_SHORT(*vp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int READSTR(char *str, struct timidity_file *tf)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (tf_read(str, 20, 1, tf) != 1)
|
|
|
|
return -1;
|
|
|
|
str[19] = '\0';
|
|
|
|
n = (int)strlen(str);
|
|
|
|
while (n > 0 && str[n - 1] == ' ')
|
|
|
|
n--;
|
|
|
|
str[n] = '\0';
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define READID(var,tf) tf_read(var, 4, 1, tf)
|
|
|
|
#define READB(var,tf) tf_read(&var, 1, 1, tf)
|
|
|
|
#define SKIPB(tf) skip(tf, 1)
|
|
|
|
#define SKIPW(tf) skip(tf, 2)
|
|
|
|
#define SKIPDW(tf) skip(tf, 4)
|
|
|
|
|
|
|
|
#define FSKIP(size,tf) skip(tf, size)
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* id numbers
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
enum {
|
|
|
|
/* level 0; chunk */
|
|
|
|
UNKN_ID, RIFF_ID, LIST_ID, SFBK_ID,
|
|
|
|
/* level 1; id only */
|
|
|
|
INFO_ID, SDTA_ID, PDTA_ID,
|
|
|
|
/* info stuff; chunk */
|
|
|
|
IFIL_ID, ISNG_ID, IROM_ID, INAM_ID, IVER_ID, IPRD_ID, ICOP_ID,
|
|
|
|
ICRD_ID, IENG_ID, ISFT_ID, ICMT_ID,
|
|
|
|
/* sample data stuff; chunk */
|
|
|
|
SNAM_ID, SMPL_ID,
|
|
|
|
/* preset stuff; chunk */
|
|
|
|
PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID,
|
|
|
|
/* inst stuff; chunk */
|
|
|
|
INST_ID, IBAG_ID, IMOD_ID, IGEN_ID,
|
|
|
|
/* sample header; chunk */
|
|
|
|
SHDR_ID
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* load a soundfont file
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
int Instruments::load_soundfont(SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
SFChunk chunk;
|
|
|
|
|
|
|
|
sf->preset = NULL;
|
|
|
|
sf->sample = NULL;
|
|
|
|
sf->inst = NULL;
|
|
|
|
sf->sf_name = NULL;
|
|
|
|
|
|
|
|
prbags.bag = inbags.bag = NULL;
|
|
|
|
prbags.gen = inbags.gen = NULL;
|
|
|
|
prbags.nbags = inbags.nbags =
|
|
|
|
prbags.ngens = inbags.ngens = 0;
|
|
|
|
|
|
|
|
/* check RIFF file header */
|
|
|
|
READCHUNK(&chunk, fd);
|
|
|
|
if (chunkid(chunk.id) != RIFF_ID) {
|
|
|
|
ctl_cmsg(CMSG_ERROR, VERB_NORMAL,
|
|
|
|
"%s: *** not a RIFF file", fd->filename.c_str());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* check file id */
|
|
|
|
READID(chunk.id, fd);
|
|
|
|
if (chunkid(chunk.id) != SFBK_ID) {
|
|
|
|
ctl_cmsg(CMSG_ERROR, VERB_NORMAL,
|
|
|
|
"%s: *** not a SoundFont file", fd->filename.c_str());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (READCHUNK(&chunk, fd) <= 0)
|
|
|
|
break;
|
|
|
|
else if (chunkid(chunk.id) == LIST_ID) {
|
|
|
|
if (process_list(chunk.size, sf, fd))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
2018-02-21 20:11:31 +00:00
|
|
|
ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "%s: *** illegal id in level 0: %4.4s %4d", fd->filename.c_str(), chunk.id, chunk.size);
|
2018-02-19 21:14:22 +00:00
|
|
|
FSKIP(chunk.size, fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse layer structure */
|
|
|
|
convert_layers(sf);
|
|
|
|
|
|
|
|
/* free private tables */
|
|
|
|
if (prbags.bag) free(prbags.bag);
|
|
|
|
if (prbags.gen) free(prbags.gen);
|
|
|
|
if (inbags.bag) free(inbags.bag);
|
|
|
|
if (inbags.gen) free(inbags.gen);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* free buffer
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
void Instruments::free_soundfont(SFInfo *sf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (sf->preset) {
|
|
|
|
for (i = 0; i < sf->npresets; i++)
|
|
|
|
free_layer(&sf->preset[i].hdr);
|
|
|
|
free(sf->preset);
|
|
|
|
}
|
|
|
|
if (sf->inst) {
|
|
|
|
for (i = 0; i < sf->ninsts; i++)
|
|
|
|
free_layer(&sf->inst[i].hdr);
|
|
|
|
free(sf->inst);
|
|
|
|
}
|
|
|
|
if (sf->sample) free(sf->sample);
|
|
|
|
if (sf->sf_name) free(sf->sf_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* get id value from 4bytes ID string
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
int Instruments::chunkid(char *id)
|
|
|
|
{
|
|
|
|
static struct idstring {
|
|
|
|
const char *str;
|
|
|
|
int id;
|
|
|
|
} idlist[] = {
|
|
|
|
{"RIFF", RIFF_ID},
|
|
|
|
{"LIST", LIST_ID},
|
|
|
|
{"sfbk", SFBK_ID},
|
|
|
|
{"INFO", INFO_ID},
|
|
|
|
{"sdta", SDTA_ID},
|
|
|
|
{"snam", SNAM_ID},
|
|
|
|
{"smpl", SMPL_ID},
|
|
|
|
{"pdta", PDTA_ID},
|
|
|
|
{"phdr", PHDR_ID},
|
|
|
|
{"pbag", PBAG_ID},
|
|
|
|
{"pmod", PMOD_ID},
|
|
|
|
{"pgen", PGEN_ID},
|
|
|
|
{"inst", INST_ID},
|
|
|
|
{"ibag", IBAG_ID},
|
|
|
|
{"imod", IMOD_ID},
|
|
|
|
{"igen", IGEN_ID},
|
|
|
|
{"shdr", SHDR_ID},
|
|
|
|
{"ifil", IFIL_ID},
|
|
|
|
{"isng", ISNG_ID},
|
|
|
|
{"irom", IROM_ID},
|
|
|
|
{"iver", IVER_ID},
|
|
|
|
{"INAM", INAM_ID},
|
|
|
|
{"IPRD", IPRD_ID},
|
|
|
|
{"ICOP", ICOP_ID},
|
|
|
|
{"ICRD", ICRD_ID},
|
|
|
|
{"IENG", IENG_ID},
|
|
|
|
{"ISFT", ISFT_ID},
|
|
|
|
{"ICMT", ICMT_ID},
|
|
|
|
};
|
|
|
|
|
2018-02-23 08:07:42 +00:00
|
|
|
for (unsigned i = 0; i < sizeof(idlist) / sizeof(idlist[0]); i++) {
|
2018-02-19 21:14:22 +00:00
|
|
|
if (strncmp(id, idlist[i].str, 4) == 0)
|
|
|
|
return idlist[i].id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return UNKN_ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* process a list chunk
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
int Instruments::process_list(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
SFChunk chunk;
|
|
|
|
|
|
|
|
/* read the following id string */
|
|
|
|
READID(chunk.id, fd); size -= 4;
|
|
|
|
ctl_cmsg(CMSG_INFO, VERB_DEBUG, "%c%c%c%c:",
|
|
|
|
chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]);
|
|
|
|
switch (chunkid(chunk.id)) {
|
|
|
|
case INFO_ID:
|
|
|
|
return process_info(size, sf, fd);
|
|
|
|
case SDTA_ID:
|
|
|
|
return process_sdta(size, sf, fd);
|
|
|
|
case PDTA_ID:
|
|
|
|
return process_pdta(size, sf, fd);
|
|
|
|
default:
|
2018-02-21 20:11:31 +00:00
|
|
|
ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "%s: *** illegal id in level 1: %4.4s", fd->filename.c_str(), chunk.id);
|
2018-02-19 21:14:22 +00:00
|
|
|
FSKIP(size, fd); /* skip it */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* process info list
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
int Instruments::process_info(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
sf->infopos = tf_tell(fd);
|
|
|
|
sf->infosize = size;
|
|
|
|
|
|
|
|
/* parse the buffer */
|
|
|
|
while (size > 0) {
|
|
|
|
SFChunk chunk;
|
|
|
|
|
|
|
|
/* read a sub chunk */
|
|
|
|
if (READCHUNK(&chunk, fd) <= 0)
|
|
|
|
return -1;
|
|
|
|
size -= 8;
|
|
|
|
|
|
|
|
ctl_cmsg(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:",
|
|
|
|
chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]);
|
|
|
|
switch (chunkid(chunk.id)) {
|
|
|
|
case IFIL_ID:
|
|
|
|
/* soundfont file version */
|
|
|
|
READW(&sf->version, fd);
|
|
|
|
READW(&sf->minorversion, fd);
|
|
|
|
ctl_cmsg(CMSG_INFO, VERB_DEBUG,
|
|
|
|
" version %d, minor %d",
|
|
|
|
sf->version, sf->minorversion);
|
|
|
|
break;
|
|
|
|
case INAM_ID:
|
|
|
|
/* name of the font */
|
|
|
|
sf->sf_name = (char*)safe_malloc(chunk.size + 1);
|
|
|
|
tf_read(sf->sf_name, 1, chunk.size, fd);
|
|
|
|
sf->sf_name[chunk.size] = 0;
|
|
|
|
ctl_cmsg(CMSG_INFO, VERB_DEBUG,
|
|
|
|
" name %s", sf->sf_name);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
size -= chunk.size;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* process sample data list
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
int Instruments::process_sdta(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
while (size > 0) {
|
|
|
|
SFChunk chunk;
|
|
|
|
|
|
|
|
/* read a sub chunk */
|
|
|
|
if (READCHUNK(&chunk, fd) <= 0)
|
|
|
|
return -1;
|
|
|
|
size -= 8;
|
|
|
|
|
|
|
|
ctl_cmsg(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:",
|
|
|
|
chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]);
|
|
|
|
switch (chunkid(chunk.id)) {
|
|
|
|
case SNAM_ID:
|
|
|
|
/* sample name list */
|
|
|
|
load_sample_names(chunk.size, sf, fd);
|
|
|
|
break;
|
|
|
|
case SMPL_ID:
|
|
|
|
/* sample data starts from here */
|
|
|
|
sf->samplepos = tf_tell(fd);
|
|
|
|
sf->samplesize = chunk.size;
|
|
|
|
FSKIP(chunk.size, fd);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FSKIP(chunk.size, fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
size -= chunk.size;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* process preset data list
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
int Instruments::process_pdta(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
while (size > 0) {
|
|
|
|
SFChunk chunk;
|
|
|
|
|
|
|
|
/* read a subchunk */
|
|
|
|
if (READCHUNK(&chunk, fd) <= 0)
|
|
|
|
return -1;
|
|
|
|
size -= 8;
|
|
|
|
|
|
|
|
ctl_cmsg(CMSG_INFO, VERB_DEBUG, " %c%c%c%c:",
|
|
|
|
chunk.id[0], chunk.id[1], chunk.id[2], chunk.id[3]);
|
|
|
|
switch (chunkid(chunk.id)) {
|
|
|
|
case PHDR_ID:
|
|
|
|
load_preset_header(chunk.size, sf, fd);
|
|
|
|
break;
|
|
|
|
case PBAG_ID:
|
|
|
|
load_bag(chunk.size, &prbags, fd);
|
|
|
|
break;
|
|
|
|
case PGEN_ID:
|
|
|
|
load_gen(chunk.size, &prbags, fd);
|
|
|
|
break;
|
|
|
|
case INST_ID:
|
|
|
|
load_inst_header(chunk.size, sf, fd);
|
|
|
|
break;
|
|
|
|
case IBAG_ID:
|
|
|
|
load_bag(chunk.size, &inbags, fd);
|
|
|
|
break;
|
|
|
|
case IGEN_ID:
|
|
|
|
load_gen(chunk.size, &inbags, fd);
|
|
|
|
break;
|
|
|
|
case SHDR_ID:
|
|
|
|
load_sample_info(chunk.size, sf, fd);
|
|
|
|
break;
|
|
|
|
case PMOD_ID: /* ignored */
|
|
|
|
case IMOD_ID: /* ingored */
|
|
|
|
default:
|
|
|
|
FSKIP(chunk.size, fd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
size -= chunk.size;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* store sample name list; sf1 only
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::load_sample_names(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
int i, nsamples;
|
|
|
|
if (sf->version > 1) {
|
2018-02-21 20:11:31 +00:00
|
|
|
ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "%s: *** version 2 has obsolete format??",fd->filename.c_str());
|
2018-02-19 21:14:22 +00:00
|
|
|
FSKIP(size, fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* each sample name has a fixed lentgh (20 bytes) */
|
|
|
|
nsamples = size / 20;
|
|
|
|
if (sf->sample == NULL) {
|
|
|
|
sf->nsamples = nsamples;
|
|
|
|
sf->sample = NEW(SFSampleInfo, sf->nsamples);
|
|
|
|
}
|
|
|
|
else if (sf->nsamples != nsamples) {
|
2018-02-21 20:11:31 +00:00
|
|
|
ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "%s: *** different # of samples ?? (%d : %d)\n",fd->filename.c_str(), sf->nsamples, nsamples);
|
2018-02-19 21:14:22 +00:00
|
|
|
FSKIP(size, fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read each name from file */
|
|
|
|
for (i = 0; i < sf->nsamples; i++) {
|
|
|
|
READSTR(sf->sample[i].name, fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* preset header list
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::load_preset_header(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sf->npresets = size / 38;
|
|
|
|
sf->preset = NEW(SFPresetHdr, sf->npresets);
|
|
|
|
for (i = 0; i < sf->npresets; i++) {
|
|
|
|
READSTR(sf->preset[i].hdr.name, fd);
|
|
|
|
READW(&sf->preset[i].preset, fd);
|
|
|
|
READW(&sf->preset[i].bank, fd);
|
|
|
|
READW(&sf->preset[i].hdr.bagNdx, fd);
|
|
|
|
SKIPDW(fd); /* lib; ignored*/
|
|
|
|
SKIPDW(fd); /* genre; ignored */
|
|
|
|
SKIPDW(fd); /* morph; ignored */
|
|
|
|
/* initialize layer table; it'll be parsed later */
|
|
|
|
sf->preset[i].hdr.nlayers = 0;
|
|
|
|
sf->preset[i].hdr.layer = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* instrument header list
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::load_inst_header(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sf->ninsts = size / 22;
|
|
|
|
sf->inst = NEW(SFInstHdr, sf->ninsts);
|
|
|
|
for (i = 0; i < sf->ninsts; i++) {
|
|
|
|
READSTR(sf->inst[i].hdr.name, fd);
|
|
|
|
READW(&sf->inst[i].hdr.bagNdx, fd);
|
|
|
|
/* iniitialize layer table; it'll be parsed later */
|
|
|
|
sf->inst[i].hdr.nlayers = 0;
|
|
|
|
sf->inst[i].hdr.layer = NULL;
|
|
|
|
|
|
|
|
ctl_cmsg(CMSG_INFO, VERB_DEBUG,
|
|
|
|
" InstHdr %d (%s) bagNdx=%d",
|
|
|
|
i, sf->inst[i].hdr.name, sf->inst[i].hdr.bagNdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* load preset/instrument bag list on the private table
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::load_bag(int size, SFBags *bagp, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
size /= 4;
|
|
|
|
bagp->bag = NEW(uint16_t, size);
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
READW(&bagp->bag[i], fd);
|
|
|
|
SKIPW(fd); /* mod; ignored */
|
|
|
|
}
|
|
|
|
bagp->nbags = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* load preset/instrument generator list on the private table
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::load_gen(int size, SFBags *bagp, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
size /= 4;
|
|
|
|
bagp->gen = NEW(SFGenRec, size);
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
READW((uint16_t *)&bagp->gen[i].oper, fd);
|
|
|
|
READW((uint16_t *)&bagp->gen[i].amount, fd);
|
|
|
|
}
|
|
|
|
bagp->ngens = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* load sample info list
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::load_sample_info(int size, SFInfo *sf, struct timidity_file *fd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int in_rom;
|
|
|
|
|
|
|
|
/* the record size depends on the soundfont version */
|
|
|
|
if (sf->version > 1) {
|
|
|
|
/* SF2 includes sample name and other infos */
|
|
|
|
sf->nsamples = size / 46;
|
|
|
|
sf->sample = NEW(SFSampleInfo, sf->nsamples);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* SBK; sample name may be read already */
|
|
|
|
int nsamples = size / 16;
|
|
|
|
if (sf->sample == NULL) {
|
|
|
|
sf->nsamples = nsamples;
|
|
|
|
sf->sample = NEW(SFSampleInfo, sf->nsamples);
|
|
|
|
}
|
|
|
|
else if (sf->nsamples != nsamples) {
|
|
|
|
/* overwrite it */
|
|
|
|
sf->nsamples = nsamples;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
in_rom = 1; /* data may start from ROM samples */
|
|
|
|
for (i = 0; i < sf->nsamples; i++) {
|
|
|
|
if (sf->version > 1) /* SF2 only */
|
|
|
|
READSTR(sf->sample[i].name, fd);
|
|
|
|
READDW((uint32_t *)&sf->sample[i].startsample, fd);
|
|
|
|
READDW((uint32_t *)&sf->sample[i].endsample, fd);
|
|
|
|
READDW((uint32_t *)&sf->sample[i].startloop, fd);
|
|
|
|
READDW((uint32_t *)&sf->sample[i].endloop, fd);
|
|
|
|
if (sf->version > 1) { /* SF2 only */
|
|
|
|
READDW((uint32_t *)&sf->sample[i].samplerate, fd);
|
|
|
|
READB(sf->sample[i].originalPitch, fd);
|
|
|
|
READB(sf->sample[i].pitchCorrection, fd);
|
|
|
|
READW(&sf->sample[i].samplelink, fd);
|
|
|
|
READW(&sf->sample[i].sampletype, fd);
|
|
|
|
}
|
|
|
|
else { /* for SBK; set missing infos */
|
|
|
|
sf->sample[i].samplerate = 44100;
|
|
|
|
sf->sample[i].originalPitch = 60;
|
|
|
|
sf->sample[i].pitchCorrection = 0;
|
|
|
|
sf->sample[i].samplelink = 0;
|
|
|
|
/* the first RAM data starts from address 0 */
|
|
|
|
if (sf->sample[i].startsample == 0)
|
|
|
|
in_rom = 0;
|
|
|
|
if (in_rom)
|
|
|
|
sf->sample[i].sampletype = 0x8001;
|
|
|
|
else
|
|
|
|
sf->sample[i].sampletype = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*================================================================
|
|
|
|
* convert from bags to layers
|
|
|
|
*================================================================*/
|
|
|
|
|
|
|
|
void Instruments::convert_layers(SFInfo *sf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (prbags.bag == NULL || prbags.gen == NULL ||
|
|
|
|
inbags.bag == NULL || inbags.gen == NULL) {
|
2018-02-21 20:11:31 +00:00
|
|
|
ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "%s: *** illegal bags / gens", sf->sf_name);
|
2018-02-19 21:14:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sf->npresets - 1; i++) {
|
|
|
|
generate_layers(&sf->preset[i].hdr,
|
|
|
|
&sf->preset[i + 1].hdr,
|
|
|
|
&prbags);
|
|
|
|
}
|
|
|
|
for (i = 0; i < sf->ninsts - 1; i++) {
|
|
|
|
generate_layers(&sf->inst[i].hdr,
|
|
|
|
&sf->inst[i + 1].hdr,
|
|
|
|
&inbags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* generate layer lists from stored bags
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::generate_layers(SFHeader *hdr, SFHeader *next, SFBags *bags)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
SFGenLayer *layp;
|
|
|
|
|
|
|
|
hdr->nlayers = next->bagNdx - hdr->bagNdx;
|
|
|
|
if (hdr->nlayers < 0) {
|
2018-02-21 20:11:31 +00:00
|
|
|
ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "%s: illegal layer numbers %d", "", hdr->nlayers);
|
2018-02-19 21:14:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (hdr->nlayers == 0)
|
|
|
|
return;
|
|
|
|
hdr->layer = (SFGenLayer*)safe_malloc(sizeof(SFGenLayer) * hdr->nlayers);
|
|
|
|
layp = hdr->layer;
|
|
|
|
for (layp = hdr->layer, i = hdr->bagNdx; i < next->bagNdx; layp++, i++) {
|
|
|
|
int genNdx = bags->bag[i];
|
|
|
|
layp->nlists = bags->bag[i + 1] - genNdx;
|
|
|
|
if (layp->nlists < 0) {
|
2018-02-21 20:11:31 +00:00
|
|
|
ctl_cmsg(CMSG_WARNING, VERB_NORMAL, "%s: illegal list numbers %d", "", layp->nlists);
|
2018-02-19 21:14:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
layp->list = (SFGenRec*)safe_malloc(sizeof(SFGenRec) * layp->nlists);
|
|
|
|
memcpy(layp->list, &bags->gen[genNdx],
|
|
|
|
sizeof(SFGenRec) * layp->nlists);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------
|
|
|
|
* free a layer
|
|
|
|
*----------------------------------------------------------------*/
|
|
|
|
|
|
|
|
void Instruments::free_layer(SFHeader *hdr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < hdr->nlayers; i++) {
|
|
|
|
SFGenLayer *layp = &hdr->layer[i];
|
|
|
|
if (layp->nlists >= 0)
|
|
|
|
free(layp->list);
|
|
|
|
}
|
|
|
|
if (hdr->nlayers > 0)
|
|
|
|
free(hdr->layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add blank loop for each data */
|
|
|
|
static const int auto_add_blank = 0;
|
|
|
|
void Instruments::correct_samples(SFInfo *sf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
SFSampleInfo *sp;
|
|
|
|
int prev_end;
|
|
|
|
|
|
|
|
prev_end = 0;
|
|
|
|
for (sp = sf->sample, i = 0; i < sf->nsamples; i++, sp++) {
|
|
|
|
/* correct sample positions for SBK file */
|
|
|
|
if (sf->version == 1) {
|
|
|
|
sp->startloop++;
|
|
|
|
sp->endloop += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate sample data size */
|
|
|
|
if (sp->sampletype & 0x8000)
|
|
|
|
sp->size = 0;
|
|
|
|
else if (sp->startsample < prev_end && sp->startsample != 0)
|
|
|
|
sp->size = 0;
|
|
|
|
else {
|
|
|
|
sp->size = -1;
|
|
|
|
if (!auto_add_blank && i != sf->nsamples - 1)
|
|
|
|
sp->size = sp[1].startsample - sp->startsample;
|
|
|
|
if (sp->size < 0)
|
|
|
|
sp->size = sp->endsample - sp->startsample + 48;
|
|
|
|
}
|
|
|
|
prev_end = sp->endsample;
|
|
|
|
|
|
|
|
/* calculate short-shot loop size */
|
|
|
|
if (auto_add_blank || i == sf->nsamples - 1)
|
|
|
|
sp->loopshot = 48;
|
|
|
|
else {
|
|
|
|
sp->loopshot = sp[1].startsample - sp->endsample;
|
|
|
|
if (sp->loopshot < 0 || sp->loopshot > 48)
|
|
|
|
sp->loopshot = 48;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-23 08:07:42 +00:00
|
|
|
}
|