mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-11-10 15:01:40 +00:00
Sample timers and fast MIDI-file rendering (fixes ticket #15)
This commit is contained in:
parent
648a9f155b
commit
7de961292b
15 changed files with 602 additions and 68 deletions
|
@ -70,6 +70,9 @@ Number of audio buffers
|
|||
.B \-d, \-\-dump
|
||||
Dump incoming and outgoing MIDI events to stdout
|
||||
.TP
|
||||
.B \-F, \-\-fast\-render=[file]
|
||||
Render MIDI file to raw audio data and store in [file]
|
||||
.TP
|
||||
.B \-f, \-\-load\-config
|
||||
Load command configuration file (shell commands)
|
||||
.TP
|
||||
|
|
|
@ -61,7 +61,27 @@ FLUIDSYNTH_API fluid_audio_driver_t* new_fluid_audio_driver2(fluid_settings_t* s
|
|||
|
||||
FLUIDSYNTH_API void delete_fluid_audio_driver(fluid_audio_driver_t* driver);
|
||||
|
||||
/**
|
||||
* Create a new file renderer and open the file.
|
||||
* @param synth The synth that creates audio data.
|
||||
* @param filename Output filename
|
||||
* @param period_size Sample count, amount of samples to write to the file at
|
||||
* every call to fluid_file_renderer_process_block().
|
||||
* @return the new object, or NULL on failure
|
||||
*/
|
||||
FLUIDSYNTH_API fluid_file_renderer_t* new_fluid_file_renderer(fluid_synth_t* synth,
|
||||
char* filename, int period_size);
|
||||
|
||||
/**
|
||||
* Write period_size samples to file.
|
||||
* @return FLUID_OK or FLUID_FAILED if an error occurred
|
||||
*/
|
||||
FLUIDSYNTH_API int fluid_file_renderer_process_block(fluid_file_renderer_t* dev);
|
||||
|
||||
/**
|
||||
* Close file and destroy the file renderer object.
|
||||
*/
|
||||
FLUIDSYNTH_API void delete_fluid_file_renderer(fluid_file_renderer_t* dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -101,6 +101,13 @@ FLUIDSYNTH_API void delete_fluid_midi_driver(fluid_midi_driver_t* driver);
|
|||
* The MIDI player allows you to play MIDI files with the FLUID Synth
|
||||
*/
|
||||
|
||||
enum fluid_player_status
|
||||
{
|
||||
FLUID_PLAYER_READY,
|
||||
FLUID_PLAYER_PLAYING,
|
||||
FLUID_PLAYER_DONE
|
||||
};
|
||||
|
||||
FLUIDSYNTH_API fluid_player_t* new_fluid_player(fluid_synth_t* synth);
|
||||
FLUIDSYNTH_API int delete_fluid_player(fluid_player_t* player);
|
||||
FLUIDSYNTH_API int fluid_player_add(fluid_player_t* player, char* midifile);
|
||||
|
@ -110,6 +117,7 @@ FLUIDSYNTH_API int fluid_player_join(fluid_player_t* player);
|
|||
FLUIDSYNTH_API int fluid_player_set_loop(fluid_player_t* player, int loop);
|
||||
FLUIDSYNTH_API int fluid_player_set_midi_tempo(fluid_player_t* player, int tempo);
|
||||
FLUIDSYNTH_API int fluid_player_set_bpm(fluid_player_t* player, int bpm);
|
||||
FLUIDSYNTH_API int fluid_player_get_status(fluid_player_t* player);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ typedef struct _fluid_preset_t fluid_preset_t;
|
|||
typedef struct _fluid_sample_t fluid_sample_t;
|
||||
typedef struct _fluid_mod_t fluid_mod_t;
|
||||
typedef struct _fluid_audio_driver_t fluid_audio_driver_t;
|
||||
typedef struct _fluid_file_renderer_t fluid_file_renderer_t;
|
||||
typedef struct _fluid_player_t fluid_player_t;
|
||||
typedef struct _fluid_midi_event_t fluid_midi_event_t;
|
||||
typedef struct _fluid_midi_driver_t fluid_midi_driver_t;
|
||||
|
|
|
@ -121,6 +121,7 @@ libfluidsynth_la_SOURCES = \
|
|||
fluid_tuning.h \
|
||||
fluid_voice.c \
|
||||
fluid_voice.h \
|
||||
fluid_filerenderer.c \
|
||||
fluid_aufile.c
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/include $(LASH_CFLAGS) $(LADCCA_CFLAGS) \
|
||||
|
|
|
@ -39,14 +39,10 @@ typedef struct {
|
|||
fluid_audio_driver_t driver;
|
||||
fluid_audio_func_t callback;
|
||||
void* data;
|
||||
fluid_file_renderer_t* renderer;
|
||||
int period_size;
|
||||
double sample_rate;
|
||||
FILE* file;
|
||||
fluid_timer_t* timer;
|
||||
float* left;
|
||||
float* right;
|
||||
short* buf;
|
||||
int buf_size;
|
||||
unsigned int samples;
|
||||
} fluid_file_audio_driver_t;
|
||||
|
||||
|
@ -61,7 +57,7 @@ static int fluid_file_audio_run_s16(void* d, unsigned int msec);
|
|||
/**************************************************************
|
||||
*
|
||||
* 'file' audio driver
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
void fluid_file_audio_driver_settings(fluid_settings_t* settings)
|
||||
|
@ -92,22 +88,18 @@ new_fluid_file_audio_driver(fluid_settings_t* settings,
|
|||
dev->data = synth;
|
||||
dev->callback = (fluid_audio_func_t) fluid_synth_process;
|
||||
dev->samples = 0;
|
||||
dev->left = FLUID_ARRAY(float, dev->period_size);
|
||||
dev->right = FLUID_ARRAY(float, dev->period_size);
|
||||
dev->buf = FLUID_ARRAY(short, 2 * dev->period_size);
|
||||
dev->buf_size = 2 * dev->period_size * sizeof(short);
|
||||
|
||||
if (fluid_settings_getstr(settings, "audio.file.name", &filename) == 0) {
|
||||
FLUID_LOG(FLUID_ERR, "No file name specified");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
dev->file = fopen(filename, "wb");
|
||||
if (dev->file == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename);
|
||||
dev->renderer = new_fluid_file_renderer(synth, filename, dev->period_size);
|
||||
if (dev->renderer == NULL) {
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
|
||||
msec = (int) (0.5 + dev->period_size / dev->sample_rate * 1000.0);
|
||||
dev->timer = new_fluid_timer(msec, fluid_file_audio_run_s16, (void*) dev, 1, 0);
|
||||
if (dev->timer == NULL) {
|
||||
|
@ -133,21 +125,9 @@ int delete_fluid_file_audio_driver(fluid_audio_driver_t* p)
|
|||
if (dev->timer != NULL) {
|
||||
delete_fluid_timer(dev->timer);
|
||||
}
|
||||
|
||||
if (dev->file != NULL) {
|
||||
fclose(dev->file);
|
||||
}
|
||||
|
||||
if (dev->left != NULL) {
|
||||
FLUID_FREE(dev->left);
|
||||
}
|
||||
|
||||
if (dev->right != NULL) {
|
||||
FLUID_FREE(dev->right);
|
||||
}
|
||||
|
||||
if (dev->buf != NULL) {
|
||||
FLUID_FREE(dev->buf);
|
||||
|
||||
if (dev->renderer != NULL) {
|
||||
delete_fluid_file_renderer(dev->renderer);
|
||||
}
|
||||
|
||||
FLUID_FREE(dev);
|
||||
|
@ -157,7 +137,6 @@ int delete_fluid_file_audio_driver(fluid_audio_driver_t* p)
|
|||
static int fluid_file_audio_run_s16(void* d, unsigned int clock_time)
|
||||
{
|
||||
fluid_file_audio_driver_t* dev = (fluid_file_audio_driver_t*) d;
|
||||
int n, offset;
|
||||
unsigned int sample_time;
|
||||
|
||||
sample_time = (unsigned int) (dev->samples / dev->sample_rate * 1000.0);
|
||||
|
@ -165,19 +144,7 @@ static int fluid_file_audio_run_s16(void* d, unsigned int clock_time)
|
|||
return 1;
|
||||
}
|
||||
|
||||
fluid_synth_write_s16(dev->data, 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 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev->samples += dev->period_size;
|
||||
|
||||
return 1;
|
||||
return fluid_file_renderer_process_block(dev->renderer) == FLUID_OK ? 1 : 0;
|
||||
}
|
||||
|
|
357
fluidsynth/src/fluid_filerenderer.c
Normal file
357
fluidsynth/src/fluid_filerenderer.c
Normal file
|
@ -0,0 +1,357 @@
|
|||
/* 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"
|
||||
|
||||
struct _fluid_file_renderer_t {
|
||||
FILE* file;
|
||||
fluid_synth_t* synth;
|
||||
short* buf;
|
||||
int period_size;
|
||||
int buf_size;
|
||||
};
|
||||
|
||||
void delete_fluid_file_renderer(fluid_file_renderer_t* dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->file != NULL) {
|
||||
fclose(dev->file);
|
||||
}
|
||||
|
||||
if (dev->buf != NULL) {
|
||||
FLUID_FREE(dev->buf);
|
||||
}
|
||||
|
||||
FLUID_FREE(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a new file renderer object and open the file.
|
||||
*/
|
||||
|
||||
fluid_file_renderer_t* new_fluid_file_renderer(fluid_synth_t* synth, char* filename, int period_size)
|
||||
{
|
||||
fluid_file_renderer_t* dev;
|
||||
|
||||
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;
|
||||
dev->period_size = period_size;
|
||||
dev->buf_size = 2 * dev->period_size * sizeof(short);
|
||||
|
||||
dev->buf = FLUID_ARRAY(short, 2 * dev->period_size);
|
||||
if (dev->buf == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
if (filename == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "No file name specified");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
dev->file = fopen(filename, "wb");
|
||||
if (dev->file == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename);
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
||||
error_recovery:
|
||||
delete_fluid_file_renderer(dev);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Write period_size samples to file.
|
||||
*/
|
||||
int fluid_file_renderer_process_block(fluid_file_renderer_t* dev)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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"
|
||||
|
||||
struct _fluid_file_renderer_t {
|
||||
FILE* file;
|
||||
fluid_synth_t* synth;
|
||||
short* buf;
|
||||
int period_size;
|
||||
int buf_size;
|
||||
};
|
||||
|
||||
void delete_fluid_file_renderer(fluid_file_renderer_t* dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->file != NULL) {
|
||||
fclose(dev->file);
|
||||
}
|
||||
|
||||
if (dev->buf != NULL) {
|
||||
FLUID_FREE(dev->buf);
|
||||
}
|
||||
|
||||
FLUID_FREE(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a new file renderer object and open the file.
|
||||
*/
|
||||
|
||||
fluid_file_renderer_t* new_fluid_file_renderer(fluid_synth_t* synth, char* filename, int period_size)
|
||||
{
|
||||
fluid_file_renderer_t* dev;
|
||||
|
||||
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;
|
||||
dev->period_size = period_size;
|
||||
dev->buf_size = 2 * dev->period_size * sizeof(short);
|
||||
|
||||
dev->buf = FLUID_ARRAY(short, 2 * dev->period_size);
|
||||
if (dev->buf == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
if (filename == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "No file name specified");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
dev->file = fopen(filename, "wb");
|
||||
if (dev->file == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename);
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
||||
error_recovery:
|
||||
delete_fluid_file_renderer(dev);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Write period_size samples to file.
|
||||
*/
|
||||
int fluid_file_renderer_process_block(fluid_file_renderer_t* dev)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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"
|
||||
|
||||
struct _fluid_file_renderer_t {
|
||||
FILE* file;
|
||||
fluid_synth_t* synth;
|
||||
short* buf;
|
||||
int period_size;
|
||||
int buf_size;
|
||||
};
|
||||
|
||||
void delete_fluid_file_renderer(fluid_file_renderer_t* dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->file != NULL) {
|
||||
fclose(dev->file);
|
||||
}
|
||||
|
||||
if (dev->buf != NULL) {
|
||||
FLUID_FREE(dev->buf);
|
||||
}
|
||||
|
||||
FLUID_FREE(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a new file renderer object and open the file.
|
||||
*/
|
||||
|
||||
fluid_file_renderer_t* new_fluid_file_renderer(fluid_synth_t* synth, char* filename, int period_size)
|
||||
{
|
||||
fluid_file_renderer_t* dev;
|
||||
|
||||
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;
|
||||
dev->period_size = period_size;
|
||||
dev->buf_size = 2 * dev->period_size * sizeof(short);
|
||||
|
||||
dev->buf = FLUID_ARRAY(short, 2 * dev->period_size);
|
||||
if (dev->buf == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
if (filename == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "No file name specified");
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
dev->file = fopen(filename, "wb");
|
||||
if (dev->file == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename);
|
||||
goto error_recovery;
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
||||
error_recovery:
|
||||
delete_fluid_file_renderer(dev);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Write period_size samples to file.
|
||||
*/
|
||||
int fluid_file_renderer_process_block(fluid_file_renderer_t* dev)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
@ -1120,6 +1120,7 @@ fluid_track_send_events(fluid_track_t* track,
|
|||
fluid_player_t* new_fluid_player(fluid_synth_t* synth)
|
||||
{
|
||||
int i;
|
||||
char* timing_source;
|
||||
fluid_player_t* player;
|
||||
player = FLUID_NEW(fluid_player_t);
|
||||
if (player == NULL) {
|
||||
|
@ -1133,13 +1134,22 @@ fluid_player_t* new_fluid_player(fluid_synth_t* synth)
|
|||
player->track[i] = NULL;
|
||||
}
|
||||
player->synth = synth;
|
||||
player->timer = NULL;
|
||||
player->system_timer = NULL;
|
||||
player->sample_timer = NULL;
|
||||
player->playlist = NULL;
|
||||
player->current_file = NULL;
|
||||
player->division = 0;
|
||||
player->send_program_change = 1;
|
||||
player->miditempo = 480000;
|
||||
player->deltatime = 4.0;
|
||||
|
||||
player->use_system_timer = 0;
|
||||
if (fluid_settings_getstr(synth->settings, "player.timing-source", &timing_source) != 0) {
|
||||
if (strcmp(timing_source, "system") == 0) {
|
||||
player->use_system_timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
|
@ -1159,6 +1169,17 @@ int delete_fluid_player(fluid_player_t* player)
|
|||
return FLUID_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers settings related to the MIDI player
|
||||
*/
|
||||
void fluid_player_settings(fluid_settings_t* settings)
|
||||
{
|
||||
/* player.timing-source can be either "system" (use system timer)
|
||||
or "sample" (use timer based on number of written samples) */
|
||||
fluid_settings_register_str(settings, "player.timing-source", "sample", 0, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
int fluid_player_reset(fluid_player_t* player)
|
||||
{
|
||||
int i;
|
||||
|
@ -1327,10 +1348,20 @@ int fluid_player_play(fluid_player_t* player)
|
|||
|
||||
player->status = FLUID_PLAYER_PLAYING;
|
||||
|
||||
player->timer = new_fluid_timer((int) player->deltatime, fluid_player_callback,
|
||||
if (player->use_system_timer) {
|
||||
player->system_timer = new_fluid_timer((int) player->deltatime, fluid_player_callback,
|
||||
(void*) player, 1, 0);
|
||||
if (player->timer == NULL) {
|
||||
return FLUID_FAILED;
|
||||
if (player->system_timer == NULL) {
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
} else {
|
||||
player->sample_timer = new_fluid_sample_timer(player->synth, fluid_player_callback,
|
||||
(void*) player);
|
||||
|
||||
if (player->sample_timer == NULL) {
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
fluid_player_callback(player, 0); /* Process the first events before the first block */
|
||||
}
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
@ -1342,14 +1373,24 @@ int fluid_player_play(fluid_player_t* player)
|
|||
*/
|
||||
int fluid_player_stop(fluid_player_t* player)
|
||||
{
|
||||
if (player->timer != NULL) {
|
||||
delete_fluid_timer(player->timer);
|
||||
if (player->system_timer != NULL) {
|
||||
delete_fluid_timer(player->system_timer);
|
||||
}
|
||||
if (player->sample_timer != NULL) {
|
||||
delete_fluid_sample_timer(player->synth, player->sample_timer);
|
||||
}
|
||||
player->status = FLUID_PLAYER_DONE;
|
||||
player->timer = NULL;
|
||||
player->sample_timer = NULL;
|
||||
player->system_timer = NULL;
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
|
||||
int fluid_player_get_status(fluid_player_t* player)
|
||||
{
|
||||
return player->status;
|
||||
}
|
||||
|
||||
/* FIXME - Looping seems to not actually be implemented? */
|
||||
|
||||
/**
|
||||
|
@ -1403,7 +1444,21 @@ int fluid_player_set_bpm(fluid_player_t* player, int bpm)
|
|||
*/
|
||||
int fluid_player_join(fluid_player_t* player)
|
||||
{
|
||||
return player->timer? fluid_timer_join(player->timer) : FLUID_OK;
|
||||
if (player->system_timer) {
|
||||
return fluid_timer_join(player->system_timer);
|
||||
} else if (player->sample_timer) {
|
||||
/* Busy-wait loop, since there's no thread to wait for... */
|
||||
while (player->status == FLUID_PLAYER_PLAYING) {
|
||||
#if defined(WIN32)
|
||||
Sleep(10);
|
||||
#elif defined(MACOS9)
|
||||
/* FIXME: How do we sleep in Macos9? */
|
||||
#else
|
||||
usleep(10000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
|
|
@ -171,13 +171,6 @@ enum midi_meta_event {
|
|||
MIDI_SEQUENCER_EVENT = 0x7f
|
||||
};
|
||||
|
||||
enum fluid_player_status
|
||||
{
|
||||
FLUID_PLAYER_READY,
|
||||
FLUID_PLAYER_PLAYING,
|
||||
FLUID_PLAYER_DONE
|
||||
};
|
||||
|
||||
enum fluid_driver_status
|
||||
{
|
||||
FLUID_MIDI_READY,
|
||||
|
@ -249,10 +242,12 @@ struct _fluid_player_t {
|
|||
int ntracks;
|
||||
fluid_track_t *track[MAX_NUMBER_OF_TRACKS];
|
||||
fluid_synth_t* synth;
|
||||
fluid_timer_t* timer;
|
||||
fluid_timer_t* system_timer;
|
||||
fluid_sample_timer_t* sample_timer;
|
||||
fluid_list_t* playlist;
|
||||
char* current_file;
|
||||
char send_program_change; /* should we ignore the program changes? */
|
||||
char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */
|
||||
int start_ticks; /* the number of tempo ticks passed at the last tempo change */
|
||||
int cur_ticks; /* the number of tempo ticks passed */
|
||||
int begin_msec; /* the time (msec) of the beginning of the file */
|
||||
|
@ -270,6 +265,8 @@ fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i);
|
|||
int fluid_player_reset(fluid_player_t* player);
|
||||
int fluid_player_load(fluid_player_t* player, char *filename);
|
||||
|
||||
void fluid_player_settings(fluid_settings_t* settings);
|
||||
|
||||
|
||||
/*
|
||||
* fluid_midi_file
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "fluid_adriver.h"
|
||||
#include "fluid_mdriver.h"
|
||||
#include "fluid_settings.h"
|
||||
#include "fluid_midi.h"
|
||||
|
||||
/* maximum allowed components of a settings variable (separated by '.') */
|
||||
#define MAX_SETTINGS_TOKENS 8 /* currently only a max of 3 are used */
|
||||
|
@ -195,6 +196,7 @@ void fluid_settings_init(fluid_settings_t* settings)
|
|||
{
|
||||
fluid_synth_settings(settings);
|
||||
fluid_shell_settings(settings);
|
||||
fluid_player_settings(settings);
|
||||
fluid_audio_driver_settings(settings);
|
||||
fluid_midi_driver_settings(settings);
|
||||
}
|
||||
|
|
|
@ -35,18 +35,18 @@ typedef int (*fluid_num_update_t)(void* data, char* name, double value);
|
|||
typedef int (*fluid_str_update_t)(void* data, char* name, char* value);
|
||||
typedef int (*fluid_int_update_t)(void* data, char* name, int value);
|
||||
|
||||
/** returns 0 if the value has been resgister correctly, non-zero
|
||||
/** returns 0 if the value has been registered correctly, non-zero
|
||||
otherwise */
|
||||
int fluid_settings_register_str(fluid_settings_t* settings, char* name, char* def, int hints,
|
||||
fluid_str_update_t fun, void* data);
|
||||
|
||||
/** returns 0 if the value has been resgister correctly, non-zero
|
||||
/** returns 0 if the value has been registered correctly, non-zero
|
||||
otherwise */
|
||||
int fluid_settings_register_num(fluid_settings_t* settings, char* name, double min, double max,
|
||||
double def, int hints, fluid_num_update_t fun, void* data);
|
||||
|
||||
|
||||
/** returns 0 if the value has been resgister correctly, non-zero
|
||||
/** returns 0 if the value has been registered correctly, non-zero
|
||||
otherwise */
|
||||
int fluid_settings_register_int(fluid_settings_t* settings, char* name, int min, int max,
|
||||
int def, int hints, fluid_int_update_t fun, void* data);
|
||||
|
|
|
@ -330,6 +330,73 @@ int fluid_synth_verify_settings(fluid_settings_t *settings)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* FLUID SAMPLE TIMERS
|
||||
* Timers that use written audio data as timing reference
|
||||
*/
|
||||
struct _fluid_sample_timer_t
|
||||
{
|
||||
fluid_sample_timer_t* next; /* Single linked list of timers */
|
||||
unsigned long starttick;
|
||||
fluid_timer_callback_t callback;
|
||||
void* data;
|
||||
int isfinished;
|
||||
};
|
||||
|
||||
/*
|
||||
* fluid_sample_timer_process - called when synth->ticks is updated
|
||||
*/
|
||||
void fluid_sample_timer_process(fluid_synth_t* synth)
|
||||
{
|
||||
fluid_sample_timer_t* st;
|
||||
long msec;
|
||||
int cont;
|
||||
|
||||
for (st=synth->sample_timers; st; st=st->next) {
|
||||
if (st->isfinished) {
|
||||
continue;
|
||||
}
|
||||
|
||||
msec = (long) (1000.0*((double) (synth->ticks - st->starttick))/synth->sample_rate);
|
||||
cont = (*st->callback)(st->data, msec);
|
||||
if (cont == 0) {
|
||||
st->isfinished = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data)
|
||||
{
|
||||
fluid_sample_timer_t* result = FLUID_NEW(fluid_sample_timer_t);
|
||||
if (result == NULL) {
|
||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||
return NULL;
|
||||
}
|
||||
result->starttick = synth->ticks;
|
||||
result->isfinished = 0;
|
||||
result->data = data;
|
||||
result->callback = callback;
|
||||
result->next = synth->sample_timers;
|
||||
synth->sample_timers = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer)
|
||||
{
|
||||
fluid_sample_timer_t** ptr = &synth->sample_timers;
|
||||
while (*ptr) {
|
||||
if (*ptr == timer) {
|
||||
*ptr = timer->next;
|
||||
FLUID_FREE(timer);
|
||||
return FLUID_OK;
|
||||
}
|
||||
ptr = &((*ptr)->next);
|
||||
}
|
||||
FLUID_LOG(FLUID_ERR,"delete_fluid_sample_timer failed, no timer found");
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
* FLUID SYNTH
|
||||
|
@ -2029,6 +2096,10 @@ fluid_synth_one_block(fluid_synth_t* synth, int do_not_mix_fx_to_out)
|
|||
|
||||
/* fluid_mutex_unlock(synth->busy); /\* Allow other threads to touch the synth *\/ */
|
||||
|
||||
fluid_sample_timer_process(synth);
|
||||
|
||||
fluid_check_fpe("fluid_sample_timer_process");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,6 @@ struct _fluid_bank_offset_t {
|
|||
int offset;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* fluid_synth_t
|
||||
*/
|
||||
|
@ -144,6 +143,8 @@ struct _fluid_synth_t
|
|||
* Note: This simple scheme does -not- provide 100 % protection against
|
||||
* thread problems, for example from MIDI thread and shell thread
|
||||
*/
|
||||
fluid_sample_timer_t* sample_timers; /* List of timers triggered after a block has been processed */
|
||||
|
||||
#ifdef LADSPA
|
||||
fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /** Effects unit for LADSPA support */
|
||||
#endif
|
||||
|
@ -208,6 +209,10 @@ void fluid_synth_remove_bank_offset(fluid_synth_t* synth, int sfont_id);
|
|||
void fluid_synth_dither_s16(int *dither_index, int len, float* lin, float* rin,
|
||||
void* lout, int loff, int lincr,
|
||||
void* rout, int roff, int rincr);
|
||||
|
||||
fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data);
|
||||
int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer);
|
||||
|
||||
/*
|
||||
* misc
|
||||
*/
|
||||
|
|
|
@ -176,6 +176,31 @@ settings_foreach_func (void *data, char *name, int type)
|
|||
}
|
||||
}
|
||||
|
||||
void fast_render_loop(fluid_settings_t* settings, fluid_synth_t* synth, fluid_player_t* player)
|
||||
{
|
||||
fluid_file_renderer_t* renderer;
|
||||
char* filename = NULL;
|
||||
int period_size = 0;
|
||||
|
||||
fluid_settings_getint(settings, "audio.period-size", &period_size);
|
||||
fluid_settings_getstr(settings, "audio.file.name", &filename);
|
||||
|
||||
if (filename == NULL || period_size <= 0) {
|
||||
fprintf(stderr, "Failed to fetch parameters for file renderer\n");
|
||||
}
|
||||
|
||||
renderer = new_fluid_file_renderer(synth, filename, period_size);
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) {
|
||||
if (fluid_file_renderer_process_block(renderer) != FLUID_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete_fluid_file_renderer(renderer);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
/*
|
||||
|
@ -212,8 +237,9 @@ int main(int argc, char** argv)
|
|||
int audio_channels = 0;
|
||||
int with_server = 0;
|
||||
int dump = 0;
|
||||
int fast_render = 0;
|
||||
int connect_lash = 1;
|
||||
char *optchars = "a:C:c:df:G:g:hijK:L:lm:no:p:R:r:sVvz:";
|
||||
char *optchars = "a:C:c:df:F:G:g:hijK:L:lm:no:p:R:r:sVvz:";
|
||||
#ifdef LASH_ENABLED
|
||||
int enabled_lash = 0; /* set to TRUE if lash gets enabled */
|
||||
fluid_lash_args_t *lash_args;
|
||||
|
@ -239,6 +265,7 @@ int main(int argc, char** argv)
|
|||
{"connect-jack-outputs", 0, 0, 'j'},
|
||||
{"disable-lash", 0, 0, 'l'},
|
||||
{"dump", 0, 0, 'd'},
|
||||
{"fast-render", 1, 0, 'F'},
|
||||
{"gain", 1, 0, 'g'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"load-config", 1, 0, 'f'},
|
||||
|
@ -322,6 +349,10 @@ int main(int argc, char** argv)
|
|||
case 'f':
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
fluid_settings_setstr(settings, "audio.file.name", optarg);
|
||||
fast_render = 1;
|
||||
break;
|
||||
case 'G':
|
||||
audio_groups = atoi(optarg);
|
||||
break;
|
||||
|
@ -476,11 +507,20 @@ int main(int argc, char** argv)
|
|||
/* signal(SIGINT, handle_signal); */
|
||||
#endif
|
||||
|
||||
if (fast_render) {
|
||||
midi_in = 0;
|
||||
interactive = 0;
|
||||
with_server = 0;
|
||||
fluid_settings_setstr(settings, "player.timing-source", "sample");
|
||||
}
|
||||
|
||||
/* start the synthesis thread */
|
||||
adriver = new_fluid_audio_driver(settings, synth);
|
||||
if (adriver == NULL) {
|
||||
fprintf(stderr, "Failed to create the audio driver\n");
|
||||
goto cleanup;
|
||||
if (!fast_render) {
|
||||
adriver = new_fluid_audio_driver(settings, synth);
|
||||
if (adriver == NULL) {
|
||||
fprintf(stderr, "Failed to create the audio driver\n");
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -569,6 +609,10 @@ int main(int argc, char** argv)
|
|||
fluid_usershell(settings, cmd_handler);
|
||||
}
|
||||
|
||||
if (fast_render) {
|
||||
fast_render_loop(settings, synth, player);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
#if !defined(MACINTOSH) && !defined(WIN32)
|
||||
|
@ -644,7 +688,7 @@ void
|
|||
print_welcome()
|
||||
{
|
||||
printf("FluidSynth version %s\n"
|
||||
"Copyright (C) 2000-2006 Peter Hanappe and others.\n"
|
||||
"Copyright (C) 2000-2009 Peter Hanappe and others.\n"
|
||||
"Distributed under the LGPL license.\n"
|
||||
"SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n",
|
||||
FLUIDSYNTH_VERSION);
|
||||
|
@ -668,6 +712,8 @@ print_help()
|
|||
" Number of audio buffers\n");
|
||||
printf(" -d, --dump\n"
|
||||
" Dump incoming and outgoing MIDI events to stdout\n");
|
||||
printf(" -F, --fast-render=[file]\n"
|
||||
" Render MIDI file to raw audio data and store in [file]\n");
|
||||
printf(" -f, --load-config\n"
|
||||
" Load command configuration file (shell commands)\n");
|
||||
printf(" -G, --audio-groups\n"
|
||||
|
|
|
@ -232,6 +232,7 @@ typedef struct _fluid_tuning_t fluid_tuning_t;
|
|||
typedef struct _fluid_hashtable_t fluid_hashtable_t;
|
||||
typedef struct _fluid_client_t fluid_client_t;
|
||||
typedef struct _fluid_server_socket_t fluid_server_socket_t;
|
||||
typedef struct _fluid_sample_timer_t fluid_sample_timer_t;
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue