fluid_filerenderer.c
上传用户:tjmskj2
上传日期:2020-08-17
资源大小:577k
文件大小:14k
- /* FluidSynth - A Software Synthesizer
- *
- * Copyright (C) 2003 Peter Hanappe and others.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public License
- * as published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA
- */
- /*
- * Low-level routines for file output.
- */
- #include <stdio.h>
- #include "fluidsynth_priv.h"
- #include "fluid_synth.h"
- #include "fluid_sys.h"
- #include "fluid_settings.h"
- #if LIBSNDFILE_SUPPORT
- #include <sndfile.h>
- #endif
- struct _fluid_file_renderer_t {
- fluid_synth_t* synth;
- #if LIBSNDFILE_SUPPORT
- SNDFILE* sndfile;
- float* buf;
- #else
- FILE* file;
- short* buf;
- #endif
- int period_size;
- int buf_size;
- };
- #if LIBSNDFILE_SUPPORT
- /* Default file type used, if none specified and auto extension search fails */
- #define FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE SF_FORMAT_WAV
- /* File audio format names.
- * !! Keep in sync with format_ids[] */
- const char *format_names[] = {
- "s8",
- "s16",
- "s24",
- "s32",
- "u8",
- "float",
- "double",
- NULL /* Terminator */
- };
-
- /* File audio format IDs.
- * !! Keep in sync with format_names[] */
- const int format_ids[] = {
- SF_FORMAT_PCM_S8,
- SF_FORMAT_PCM_16,
- SF_FORMAT_PCM_24,
- SF_FORMAT_PCM_32,
- SF_FORMAT_PCM_U8,
- SF_FORMAT_FLOAT,
- SF_FORMAT_DOUBLE
- };
- /* File endian byte order names.
- * !! Keep in sync with endian_ids[] */
- const char *endian_names[] = {
- "auto",
- "little",
- "big",
- "cpu",
- NULL
- };
- /* File endian byte order ids.
- * !! Keep in sync with endian_names[] */
- const int endian_ids[] = {
- SF_ENDIAN_FILE,
- SF_ENDIAN_LITTLE,
- SF_ENDIAN_BIG,
- SF_ENDIAN_CPU
- };
- static int fluid_file_renderer_parse_options (char *filetype, char *format,
- char *endian, char *filename, SF_INFO *info);
- static int fluid_file_renderer_find_file_type (char *extension, int *type);
- static int fluid_file_renderer_find_valid_format (SF_INFO *info);
- #else /* No libsndfile support */
- /* File type names. */
- const char *type_names[] = {
- "raw",
- NULL /* Terminator */
- };
- /* File audio format names. */
- const char *format_names[] = {
- "s16",
- NULL /* Terminator */
- };
- /* File endian byte order names. */
- const char *endian_names[] = {
- "cpu",
- NULL
- };
- #endif
- void
- fluid_file_renderer_settings (fluid_settings_t* settings)
- {
- #if LIBSNDFILE_SUPPORT
- SF_FORMAT_INFO finfo, cmpinfo;
- int major_count;
- int i, i2;
- const char **np;
- fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.wav",
- FLUID_HINT_FILENAME, NULL, NULL);
- fluid_settings_register_str(settings, "audio.file.type", "auto", 0, NULL, NULL);
- fluid_settings_register_str(settings, "audio.file.format", "s16", 0, NULL, NULL);
- fluid_settings_register_str(settings, "audio.file.endian", "auto", 0, NULL, NULL);
- fluid_settings_add_option (settings, "audio.file.type", "auto");
- sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int));
- for (i = 0; i < major_count; i++)
- {
- finfo.format = i;
- sf_command (NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof (finfo));
- /* Check for duplicates */
- for (i2 = 0; i2 < i; i2++)
- {
- cmpinfo.format = i2;
- sf_command (NULL, SFC_GET_FORMAT_MAJOR, &cmpinfo, sizeof (cmpinfo));
- if (FLUID_STRCMP (cmpinfo.extension, finfo.extension) == 0)
- break;
- }
- if (i2 == i)
- fluid_settings_add_option (settings, "audio.file.type", finfo.extension);
- }
- for (np = format_names; *np; np++)
- fluid_settings_add_option (settings, "audio.file.format", *np);
- for (np = endian_names; *np; np++)
- fluid_settings_add_option (settings, "audio.file.endian", *np);
- #else
- fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.raw", 0, NULL, NULL);
- fluid_settings_register_str(settings, "audio.file.type", "raw", 0, NULL, NULL);
- fluid_settings_add_option (settings, "audio.file.type", "raw");
- fluid_settings_register_str(settings, "audio.file.format", "s16", 0, NULL, NULL);
- fluid_settings_add_option (settings, "audio.file.format", "s16");
- fluid_settings_register_str(settings, "audio.file.endian", "cpu", 0, NULL, NULL);
- fluid_settings_add_option (settings, "audio.file.endian", "cpu");
- #endif
- }
- /**
- * Create a new file renderer and open the file.
- * @param synth The synth that creates audio data.
- * @return the new object, or NULL on failure
- * @since 1.1.0
- *
- * NOTE: Available file types and formats depends on if libfluidsynth was
- * built with libsndfile support or not. If not then only RAW 16 bit output is
- * supported.
- *
- * Uses the following settings from the synth object:
- * - audio.file.name: Output filename
- * - audio.file.type: File type, "auto" tries to determine type from filename
- * extension with fallback to "wav".
- * - audio.file.format: Audio format
- * - audio.file.endian: Endian byte order, "auto" for file type's default byte order
- * - audio.period-size: Size of audio blocks to process
- * - synth.sample-rate: Sample rate to use
- */
- fluid_file_renderer_t *
- new_fluid_file_renderer(fluid_synth_t* synth)
- {
- #if LIBSNDFILE_SUPPORT
- char *type, *format, *endian;
- SF_INFO info;
- double samplerate;
- int retval;
- #endif
- char *filename = NULL;
- fluid_file_renderer_t* dev;
- fluid_return_val_if_fail (synth != NULL, NULL);
- fluid_return_val_if_fail (synth->settings != NULL, NULL);
- dev = FLUID_NEW(fluid_file_renderer_t);
- if (dev == NULL) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- return NULL;
- }
- FLUID_MEMSET(dev, 0, sizeof(fluid_file_renderer_t));
- dev->synth = synth;
- fluid_settings_getint (synth->settings, "audio.period-size", &dev->period_size);
- #if LIBSNDFILE_SUPPORT
- dev->buf_size = 2 * dev->period_size * sizeof (float);
- dev->buf = FLUID_ARRAY(float, 2 * dev->period_size);
- #else
- dev->buf_size = 2 * dev->period_size * sizeof (short);
- dev->buf = FLUID_ARRAY(short, 2 * dev->period_size);
- #endif
- if (dev->buf == NULL) {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- goto error_recovery;
- }
- fluid_settings_dupstr (synth->settings, "audio.file.name", &filename);
- if (filename == NULL) {
- FLUID_LOG(FLUID_ERR, "No file name specified");
- goto error_recovery;
- }
- #if LIBSNDFILE_SUPPORT
- memset (&info, 0, sizeof (info));
- info.format = FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE | SF_FORMAT_PCM_16;
- fluid_settings_dupstr (synth->settings, "audio.file.type", &type);
- fluid_settings_dupstr (synth->settings, "audio.file.format", &format);
- fluid_settings_dupstr (synth->settings, "audio.file.endian", &endian);
- retval = fluid_file_renderer_parse_options (type, format, endian, filename, &info);
- if (type) FLUID_FREE (type);
- if (format) FLUID_FREE (format);
- if (endian) FLUID_FREE (endian);
- if (!retval) goto error_recovery;
- fluid_settings_getnum (synth->settings, "synth.sample-rate", &samplerate);
- info.samplerate = samplerate + 0.5;
- info.channels = 2;
- /* Search for valid format for given file type, if invalid and no format was specified.
- * To handle Ogg/Vorbis and possibly future file types with new formats.
- * Checking if format is SF_FORMAT_PCM_16 isn't a fool proof way to check if
- * format was specified or not (if user specifies "s16" itself), but should suffice. */
- if (!sf_format_check (&info)
- && ((info.format & SF_FORMAT_SUBMASK) != SF_FORMAT_PCM_16
- || !fluid_file_renderer_find_valid_format (&info)))
- {
- FLUID_LOG(FLUID_ERR, "Invalid or unsupported audio file format settings");
- goto error_recovery;
- }
- dev->sndfile = sf_open (filename, SFM_WRITE, &info);
- if (!dev->sndfile)
- {
- FLUID_LOG(FLUID_ERR, "Failed to open audio file '%s' for writing", filename);
- goto error_recovery;
- }
- /* Turn on clipping and normalization of floats (-1.0 - 1.0) */
- sf_command (dev->sndfile, SFC_SET_CLIPPING, NULL, SF_TRUE);
- sf_command (dev->sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE);
- #else
- dev->file = fopen(filename, "wb");
- if (dev->file == NULL) {
- FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename);
- goto error_recovery;
- }
- #endif
- return dev;
- error_recovery:
- if (filename) FLUID_FREE (filename);
- delete_fluid_file_renderer(dev);
- return NULL;
- }
- /**
- * Close file and destroy a file renderer object.
- * @param dev File renderer object.
- * @since 1.1.0
- */
- void delete_fluid_file_renderer(fluid_file_renderer_t* dev)
- {
- if (dev == NULL) {
- return;
- }
- #if LIBSNDFILE_SUPPORT
- if (dev->sndfile != NULL) {
- int retval = sf_close (dev->sndfile);
- if (retval != 0) FLUID_LOG (FLUID_WARN, "Error closing audio file: %s",
- sf_error_number (retval));
- }
- #else
- if (dev->file != NULL) {
- fclose(dev->file);
- }
- #endif
- if (dev->buf != NULL) {
- FLUID_FREE(dev->buf);
- }
- FLUID_FREE(dev);
- return;
- }
- /**
- * Write period_size samples to file.
- * @param dev File renderer instance
- * @return #FLUID_OK or #FLUID_FAILED if an error occurred
- * @since 1.1.0
- */
- int
- fluid_file_renderer_process_block(fluid_file_renderer_t* dev)
- {
- #if LIBSNDFILE_SUPPORT
- int n;
- fluid_synth_write_float(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2);
- n = sf_writef_float (dev->sndfile, dev->buf, dev->period_size);
- if (n != dev->period_size) {
- FLUID_LOG (FLUID_ERR, "Audio file write error: %s",
- sf_strerror (dev->sndfile));
- return FLUID_FAILED;
- }
- return FLUID_OK;
- #else /* No libsndfile support */
- int n, offset;
- fluid_synth_write_s16(dev->synth, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2);
- for (offset = 0; offset < dev->buf_size; offset += n) {
- n = fwrite((char*) dev->buf + offset, 1, dev->buf_size - offset, dev->file);
- if (n < 0) {
- FLUID_LOG(FLUID_ERR, "Audio output file write error: %s",
- strerror (errno));
- return FLUID_FAILED;
- }
- }
- return FLUID_OK;
- #endif
- }
- #if LIBSNDFILE_SUPPORT
- /**
- * Parse a colon separated format string and configure an SF_INFO structure accordingly.
- * @param filetype File type string (NULL or "auto" to attempt to identify format
- * by filename extension, with fallback to "wav")
- * @param format File audio format string or NULL to use "s16"
- * @param endian File endian string or NULL to use "auto" which uses the file type's
- * default endian byte order.
- * @param filename File name (used by "auto" type to determine type, based on extension)
- * @param info Audio file info structure to configure
- * @return TRUE on success, FALSE otherwise
- */
- static int
- fluid_file_renderer_parse_options (char *filetype, char *format, char *endian,
- char *filename, SF_INFO *info)
- {
- int type = -1; /* -1 indicates "auto" type */
- char *s;
- int i;
- /* If "auto" type, then use extension to search for a match */
- if (!filetype || FLUID_STRCMP (filetype, "auto") == 0)
- {
- type = FLUID_FILE_RENDERER_DEFAULT_FILE_TYPE;
- s = FLUID_STRRCHR (filename, '.');
- if (s && s[1] != ' ')
- {
- if (!fluid_file_renderer_find_file_type (s + 1, &type))
- FLUID_LOG (FLUID_WARN, "Failed to determine audio file type from filename, defaulting to WAV");
- }
- }
- else if (!fluid_file_renderer_find_file_type (filetype, &type))
- {
- FLUID_LOG(FLUID_ERR, "Invalid or unsupported audio file type '%s'", filetype);
- return FALSE;
- }
- info->format = (info->format & ~SF_FORMAT_TYPEMASK) | type;
- /* Look for subtype */
- if (format)
- {
- for (i = 0; format_names[i]; i++)
- if (FLUID_STRCMP (format, format_names[i]) == 0)
- break;
- if (!format_names[i])
- {
- FLUID_LOG (FLUID_ERR, "Invalid or unsupported file audio format '%s'", format);
- return FALSE;
- }
- info->format = (info->format & ~SF_FORMAT_SUBMASK) | format_ids[i];
- }
- #if LIBSNDFILE_HASVORBIS
- /* Force subformat to vorbis as nothing else would make sense currently */
- if ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
- info->format = (info->format & ~SF_FORMAT_SUBMASK) | SF_FORMAT_VORBIS;
- }
- #endif
- /* Look for endian */
- if (endian)
- {
- for (i = 0; endian_names[i]; i++)
- if (FLUID_STRCMP (endian, endian_names[i]) == 0)
- break;
- if (!endian_names[i])
- {
- FLUID_LOG (FLUID_ERR, "Invalid or unsupported endian byte order '%s'", endian);
- return FALSE;
- }
- info->format = (info->format & ~SF_FORMAT_ENDMASK) | endian_ids[i];
- }
- return TRUE;
- }
- /**
- * Searches for a supported libsndfile file type by extension.
- * @param extension The extension string
- * @param ext_len Length of the extension string
- * @param type Location to store the type (unmodified if not found)
- * @return TRUE if found, FALSE otherwise
- */
- static int
- fluid_file_renderer_find_file_type (char *extension, int *type)
- {
- SF_FORMAT_INFO finfo;
- int major_count;
- int i;
- sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int));
- for (i = 0; i < major_count; i++)
- {
- finfo.format = i;
- sf_command (NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof (finfo));
- if (FLUID_STRCMP (extension, finfo.extension) == 0)
- break;
- }
- if (i < major_count)
- {
- *type = finfo.format;
- return TRUE;
- }
- return FALSE;
- }
- /* Search for a valid audio format for a given file type */
- static int
- fluid_file_renderer_find_valid_format (SF_INFO *info)
- {
- SF_FORMAT_INFO format_info;
- int count, i;
- sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int));
- for (i = 0; i < count; i++)
- {
- format_info.format = i;
- sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info));
- info->format = (info->format & ~SF_FORMAT_SUBMASK) | format_info.format;
- if (sf_format_check (info)) return TRUE;
- }
- return FALSE;
- }
- #endif