📄 sfsave.c
字号:
/*================================================================== * sfsave.c - sound font saving functions * Based on the awesfx utility Copyright (C) 1996-1999 Takashi Iwai * * Smurf Sound Font Editor * Copyright (C) 1999-2001 Josh Green * * 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 or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green <jgreen@users.sourceforge.net> * Smurf homepage: http://smurf.sourceforge.net *==================================================================*/#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <stdarg.h>#include <glib.h>#include "sffile.h"#include "sfont.h"#include "sample.h"#include "util.h"/* functions for saving data to sfont files, with appropriate byte swapping on big endian machines.*/#define WRITECHUNK(id,size,fd) G_STMT_START { \ guint32 _temp; \ if (!safe_fwrite(CHNKIDSTR(id), 4, fd)) \ return(FAIL); \ _temp = GINT32_TO_LE(size); \ if (!safe_fwrite(&_temp, 4, fd)) \ return(FAIL); \} G_STMT_END#define WRITEID(id,fd) G_STMT_START { \ if (!safe_fwrite(CHNKIDSTR(id), 4, fd)) \ return(FAIL); \} G_STMT_END#define WRITESTR(var,fd) G_STMT_START { \ if (!safe_fwrite(var, 20, fd)) \ return(FAIL); \} G_STMT_END#define WRITED(var,fd) G_STMT_START { \ gint32 _temp; \ _temp = GINT32_TO_LE(var); \ if (!safe_fwrite(&_temp, 4, fd)) \ return(FAIL); \} G_STMT_END#define WRITEW(var,fd) G_STMT_START { \ gint16 _temp; \ _temp = GINT16_TO_LE(var); \ if (!safe_fwrite(&_temp, 2, fd)) \ return(FAIL); \} G_STMT_END#define WRITEB(var,fd) G_STMT_START { \ if (!safe_fwrite(&var, 1, fd)) \ return(FAIL); \} G_STMT_END#define WRITEZERO(size, fd) G_STMT_START { \ gint _i; \ for(_i = 0; _i < size; _i++) \ if (fputc('\0', fd) == EOF) \ return(FAIL); \} G_STMT_END#define CHUNKSIZE(size, fd) G_STMT_START { \ guint32 _temp2; \ if (!safe_fseek(fd, (long)(-(size - 4)), SEEK_CUR)) \ return(FAIL); \ _temp2 = size - 8; \ WRITED(_temp2, fd); \ if (!safe_fseek(fd, (long)(size - 8), SEEK_CUR)) \ return(FAIL); \} G_STMT_END/* in case of write error, must restore sambuf samples to original state */typedef struct _UndoSBufSam{ gboolean newinfo; gboolean samfile; SFSample *sam; SFSamDataInfo *datainfo; guint32 posofs;}UndoSBufSam;static guint32 zero_size = 0; /* a 0 value used with WRITECHUNK macro */static SFData *undo_sf; /* the sound font undo info belongs to */static GArray *undo_sbufsams; /* array for storing undo sample info */static gint undo_sampos; /* backup sfont sample data file offset */static gint save_sfbody (SFData * sf, FILE * fd);static gint save_info (SFData * sf, guint32 * size, FILE * fd);static gint save_sdta (SFData * sf, guint32 * size, FILE * fd);static gint process_pdta (SFData * sf, guint32 * size, FILE * fd);static void zero_namestr (gchar *name);static gint save_phdr (SFData * sf, guint32 * size, FILE * fd);static gint save_pbag (SFData * sf, guint32 * size, FILE * fd);static gint save_pmod (SFData * sf, guint32 * size, FILE * fd);static gint save_pgen (SFData * sf, guint32 * size, FILE * fd);static gint save_ihdr (SFData * sf, guint32 * size, FILE * fd);static gint save_ibag (SFData * sf, guint32 * size, FILE * fd);static gint save_imod (SFData * sf, guint32 * size, FILE * fd);static gint save_igen (SFData * sf, guint32 * size, FILE * fd);static gint save_shdr (SFData * sf, guint32 * size, FILE * fd);gintsfsave_file (SFData * sf, FILE * fd){ undo_sbufsams = NULL; if (!save_sfbody (sf, fd)) { /* save the sfont */ sfsave_undo_sample_posofs (); return (FAIL); } return (OK);}/* when a sound font is saved the samples are relocated to the save file, the start position of every sample is changed to reflect the relocation, if however an error occurs during the save process the state of the samples must be restored, this routine restores any relocated samples */voidsfsave_undo_sample_posofs (void){ if (undo_sbufsams) { /* error occured, any samples to restore? */ gint i; UndoSBufSam *ubuf; SFSample *sam; i = undo_sbufsams->len - 1; while (i >= 0) { /* restore state of broken samples */ ubuf = &g_array_index (undo_sbufsams, UndoSBufSam, i); sam = ubuf->sam; if (!ubuf->newinfo) /* new info NOT created? */ { sam->datainfo->samfile = ubuf->samfile; sam->datainfo->start -= ubuf->posofs; } else /* new sample data info was created */ { sam_datainfo_destroy (sam->datainfo); /* destroy sam data info */ sam->datainfo = ubuf->datainfo; /* point to original datainfo */ sam->datainfo->refcount++; } i--; } g_array_free (undo_sbufsams, TRUE); undo_sf->samplepos = undo_sampos; } /* if undo_sbufsams */}voidsfsave_free_sample_posofs (void){ g_array_free (undo_sbufsams, TRUE);}static gintsave_sfbody (SFData * sf, FILE * fd){ guint32 total, size; WRITECHUNK (RIFF_ID, zero_size, fd); /* write RIFF chunk */ WRITEID (SFBK_ID, fd); /* write SFBK ID */ total = 12; /* info chunk */ WRITECHUNK (LIST_ID, zero_size, fd); WRITEID (INFO_ID, fd); if (!save_info (sf, &size, fd)) return (FAIL); size += 12; total += size; CHUNKSIZE (size, fd); /* sample chunk */ WRITECHUNK (LIST_ID, zero_size, fd); WRITEID (SDTA_ID, fd); if (!save_sdta (sf, &size, fd)) return (FAIL); size += 12; total += size; CHUNKSIZE (size, fd); /* pdta monster chunk */ WRITECHUNK (LIST_ID, zero_size, fd); WRITEID (PDTA_ID, fd); if (!process_pdta (sf, &size, fd)) return (FAIL); size += 12; total += size; CHUNKSIZE (size, fd); total -= 8; if (!safe_fseek (fd, 4L, SEEK_SET)) return (FAIL); WRITED (total, fd); fflush (fd); /* flush sfont file buffer (fd may be kept open) */ return (OK);}static gintsave_info (SFData * sf, guint32 * size, FILE * fd){ GSList *nfo; gchar *nfostr; guint32 x; gint id, i; gchar *s, *s2; /* NOTE! : ISFT version used to be saved as "Smurf v" prior to v0.49.8, now SFONT_ISFT_NAME is used from sfont.h */ /* set ISFT info tag, format is "Created by:Most recently modified by" */ if (!(s = sfont_get_info (sf, ISFT_ID)) || !strlen (s)) { s = g_strconcat (":", SFONT_ISFT_NAME VERSION, NULL); } else if (!sf->sffd) { /* if first time save, then "created by" only */ s = g_strdup (s); } else { s2 = strchr (s, ':'); /* locate : Creator/Modifier seperator */ if (s2) i = (gint) (s2 - s); /* if found, copy only Creator */ else i = strlen (s); /* if !found, copy entire string */ /* make sure Creator string isn't too big, including ':' */ if (i > 254 - sizeof (SFONT_ISFT_NAME VERSION)) i = 254 - sizeof (SFONT_ISFT_NAME VERSION); s2 = g_strndup (s, i); s = g_strconcat (s2, ":", SFONT_ISFT_NAME VERSION, NULL); g_free (s2); } sfont_set_info (sf, ISFT_ID, s); g_free (s); x = 4; WRITECHUNK (IFIL_ID, x, fd); /* write sf version info chunk */ WRITEW (sf->version.major, fd); WRITEW (sf->version.minor, fd); *size = 12; nfo = sf->info; while (nfo) { /* loop over infos */ nfostr = (gchar *) (nfo->data) + 1; /* ptr to info string */ x = strlen (nfostr) + 1; /* length of info string + \0 */ x += x % 2; /* make length even */ id = *(guint8 *) (nfo->data); /* if IROM (ROM bank name) is set but IVER (ROM version) isn't, skip */ if (id == IROM_ID && !sf->romver.major && !sf->romver.minor) { nfo = g_slist_next (nfo); continue; } WRITECHUNK (id, x, fd); /* write info chunk */ if (!safe_fwrite (nfostr, x, fd)) /* write info string */ return (FAIL); *size += x + 8; /* size of info string + chunk */ if (id == IROM_ID) { /* if last info chunk was IROM */ x = 4; WRITECHUNK (IVER_ID, x, fd); /* write IVER chunk */ WRITEW (sf->romver.major, fd); WRITEW (sf->romver.minor, fd); *size += x + 8; } nfo = g_slist_next (nfo); } return (OK);}static gintsave_sdta (SFData * sf, guint32 * size, FILE * fd){ GSList *p; SFSample *sam; UndoSBufSam ubuf; /* in case of write error disaster */ gint samsize; gint newsampos; void *buf; *size = 0; /* write sample chunk */ WRITECHUNK (SMPL_ID, zero_size, fd); undo_sbufsams = g_array_new (FALSE, FALSE, sizeof (UndoSBufSam)); undo_sf = sf; undo_sampos = sf->samplepos; /* backup old sample file position */ newsampos = ftell (fd); /* get new sample file position */ p = sf->sample; while (p) { /* loop through samples */ sam = (SFSample *) (p->data); if (sam->sampletype & SF_SAMPLETYPE_ROM) { p = g_slist_next (p); continue; } samsize = (sam->end + 1) * 2; /* load sample data */ if (!(buf = sam_load_sample (sam, sam->end + 1, 0, NULL))) return (FAIL); /* if sample data is in sam buffer, in sound font being saved or has no other sample references, then modify datainfo record */ if (sam->datainfo->samfile || sam->datainfo->sf == sf || (sam->datainfo->refcount == 1 && !sam->datainfo->dorefcount)) { /* save restore info in case of error */ ubuf.newinfo = FALSE; /* using current sam data info record */ ubuf.posofs = *size / 2 - sam->datainfo->start; ubuf.samfile = sam->datainfo->samfile; sam->datainfo->start += ubuf.posofs; /* update start position */ } else /* sample has multiple references and data resides in another saved sound font file, so create new datainfo record */ { /* save restore info in case of error */ ubuf.newinfo = TRUE; /* we are creating a new data info record */ ubuf.datainfo = sam->datainfo; sam->datainfo->refcount--; /* descrement old sam data ref count */ /* create new data info record */ sam->datainfo = sam_datainfo_new (); sam->datainfo->start = *size / 2; sam->datainfo->size = sam->end + 1; sam->datainfo->refcount++; } sam->datainfo->samfile = FALSE; /* sample data will be in sf file */ sam->datainfo->sf = sf; ubuf.sam = sam; g_array_append_val (undo_sbufsams, ubuf); /* append to undo array */ /* save the sample to sound font */ if (!safe_fwrite (buf, samsize, fd)) { free (buf); return (FAIL); } free (buf); /* free the buffer */ WRITEZERO (46 * 2, fd); /* 46 ZERO samples at end */ *size += samsize + 46 * 2; p = g_slist_next (p); } *size += 8; /* for sample chunk id (top of function) */ CHUNKSIZE (*size, fd); /* update chunk size field */ sf->samplepos = newsampos; /* assign new sample data file position */ return (OK);}static gintprocess_pdta (SFData * sf, guint32 * size, FILE * fd){ guint32 x; WRITECHUNK (PHDR_ID, zero_size, fd); if (!save_phdr (sf, &x, fd)) return (FAIL); *size = (x += 8); CHUNKSIZE (x, fd); WRITECHUNK (PBAG_ID, zero_size, fd); if (!save_pbag (sf, &x, fd)) return (FAIL); *size += (x += 8); CHUNKSIZE (x, fd); WRITECHUNK (PMOD_ID, zero_size, fd); if (!save_pmod (sf, &x, fd)) return (FAIL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -