1/* FluidSynth - A Software Synthesizer
2 *
3 * Copyright (C) 2003  Peter Hanappe and others.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public License
7 * as published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 * 02111-1307, USA
19 */
20
21/* fluid_aufile.c
22 *
23 * Audio driver, outputs the audio to a file (non real-time)
24 *
25 */
26
27#include "fluid_adriver.h"
28#include "fluid_settings.h"
29#include "fluid_sys.h"
30#include <stdio.h>
31
32
33/** fluid_file_audio_driver_t
34 *
35 * This structure should not be accessed directly. Use audio port
36 * functions instead.
37 */
38typedef struct {
39	fluid_audio_driver_t driver;
40	fluid_audio_func_t callback;
41	void* data;
42	int period_size;
43	double sample_rate;
44	FILE* file;
45	fluid_timer_t* timer;
46	float* left;
47	float* right;
48	short* buf;
49	int buf_size;
50	unsigned int samples;
51} fluid_file_audio_driver_t;
52
53
54fluid_audio_driver_t* new_fluid_file_audio_driver(fluid_settings_t* settings,
55						  fluid_synth_t* synth);
56
57int delete_fluid_file_audio_driver(fluid_audio_driver_t* p);
58void fluid_file_audio_driver_settings(fluid_settings_t* settings);
59static int fluid_file_audio_run_s16(void* d, unsigned int msec);
60
61/**************************************************************
62 *
63 *        'file' audio driver
64 *
65 */
66
67void fluid_file_audio_driver_settings(fluid_settings_t* settings)
68{
69	fluid_settings_register_str(settings, "audio.file.name", "fluidsynth.raw", 0, NULL, NULL);
70}
71
72
73fluid_audio_driver_t*
74new_fluid_file_audio_driver(fluid_settings_t* settings,
75			    fluid_synth_t* synth)
76{
77	fluid_file_audio_driver_t* dev;
78	int err;
79	char* filename;
80	int msec;
81
82	dev = FLUID_NEW(fluid_file_audio_driver_t);
83	if (dev == NULL) {
84		FLUID_LOG(FLUID_ERR, "Out of memory");
85		return NULL;
86	}
87	FLUID_MEMSET(dev, 0, sizeof(fluid_file_audio_driver_t));
88
89	fluid_settings_getint(settings, "audio.period-size", &dev->period_size);
90	fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate);
91
92	dev->data = synth;
93	dev->callback = (fluid_audio_func_t) fluid_synth_process;
94	dev->samples = 0;
95	dev->left = FLUID_ARRAY(float, dev->period_size);
96	dev->right = FLUID_ARRAY(float, dev->period_size);
97	dev->buf = FLUID_ARRAY(short, 2 * dev->period_size);
98	dev->buf_size = 2 * dev->period_size * sizeof(short);
99
100	if (fluid_settings_getstr(settings, "audio.file.name", &filename) == 0) {
101		FLUID_LOG(FLUID_ERR, "No file name specified");
102		goto error_recovery;
103	}
104
105	dev->file = fopen(filename, "wb");
106	if (dev->file == NULL) {
107		FLUID_LOG(FLUID_ERR, "Failed to open the file '%s'", filename);
108		goto error_recovery;
109	}
110
111	msec = (int) (0.5 + dev->period_size / dev->sample_rate * 1000.0);
112	dev->timer = new_fluid_timer(msec, fluid_file_audio_run_s16, (void*) dev, 1, 0);
113	if (dev->timer == NULL) {
114		FLUID_LOG(FLUID_PANIC, "Couldn't create the audio thread.");
115		goto error_recovery;
116	}
117
118	return (fluid_audio_driver_t*) dev;
119
120 error_recovery:
121	delete_fluid_file_audio_driver((fluid_audio_driver_t*) dev);
122	return NULL;
123}
124
125int delete_fluid_file_audio_driver(fluid_audio_driver_t* p)
126{
127	fluid_file_audio_driver_t* dev = (fluid_file_audio_driver_t*) p;
128
129	if (dev == NULL) {
130		return FLUID_OK;
131	}
132
133	if (dev->timer != NULL) {
134		delete_fluid_timer(dev->timer);
135	}
136
137	if (dev->file != NULL) {
138		fclose(dev->file);
139	}
140
141	if (dev->left != NULL) {
142		FLUID_FREE(dev->left);
143	}
144
145	if (dev->right != NULL) {
146		FLUID_FREE(dev->right);
147	}
148
149	if (dev->buf != NULL) {
150		FLUID_FREE(dev->buf);
151	}
152
153	FLUID_FREE(dev);
154	return FLUID_OK;
155}
156
157static int fluid_file_audio_run_s16(void* d, unsigned int clock_time)
158{
159	fluid_file_audio_driver_t* dev = (fluid_file_audio_driver_t*) d;
160	int n, offset;
161	unsigned int sample_time;
162
163	sample_time = (unsigned int) (dev->samples / dev->sample_rate * 1000.0);
164	if (sample_time > clock_time) {
165		return 1;
166	}
167
168	fluid_synth_write_s16(dev->data, dev->period_size, dev->buf, 0, 2, dev->buf, 1, 2);
169
170	for (offset = 0; offset < dev->buf_size; offset += n) {
171
172		n = fwrite((char*) dev->buf + offset, 1, dev->buf_size - offset, dev->file);
173		if (n < 0) {
174			FLUID_LOG(FLUID_ERR, "Audio output file write error: %s",
175				  strerror (errno));
176			return 0;
177		}
178	}
179
180	dev->samples += dev->period_size;
181
182	return 1;
183}
184