Make sure all voices are released when sampler is destroyed.
[calfbox.git] / streamrec.c
blob5c169e6fabecddfbbe81b48f828d736f0f63c8ff
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2011 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "engine.h"
20 #include "errors.h"
21 #include "recsrc.h"
22 #include "rt.h"
23 #include <assert.h>
24 #include <glib.h>
25 #include <jack/ringbuffer.h>
26 #include <malloc.h>
27 #include <pthread.h>
28 #include <semaphore.h>
29 #include <sndfile.h>
30 #include <string.h>
31 #include <unistd.h>
33 // XXXKF the syncing model here is flawed in several ways:
34 // - it's not possible to do block-accurate syncing
35 // - it's not possible to flush the output buffer and stop recording
36 // - rb_for_writing is being written from two threads (audio and UI),
37 // which is not guaranteed to work
38 // -
40 // 1/8s for 44.1kHz stereo float
41 #define STREAM_BUFFER_SIZE 16384
42 #define STREAM_BUFFER_COUNT 8
44 #define STREAM_CMD_QUIT (-1)
45 #define STREAM_CMD_SYNC (-2)
47 struct recording_buffer
49 float data[STREAM_BUFFER_SIZE];
50 uint32_t write_ptr;
53 struct stream_recorder
55 struct cbox_recorder iface;
56 struct recording_buffer buffers[STREAM_BUFFER_COUNT];
58 struct cbox_rt *rt;
59 struct cbox_engine *engine;
60 gchar *filename;
61 SNDFILE *volatile sndfile;
62 SF_INFO info;
63 pthread_t thr_writeout;
64 sem_t sem_sync_completed;
66 struct recording_buffer *cur_buffer;
67 uint32_t write_ptr;
69 jack_ringbuffer_t *rb_for_writing, *rb_just_written;
72 static void *stream_recorder_thread(void *user_data)
74 struct stream_recorder *self = user_data;
76 do {
77 int8_t buf_idx;
78 if (!jack_ringbuffer_read(self->rb_for_writing, (char *)&buf_idx, 1))
80 usleep(10000);
81 continue;
83 if (buf_idx == STREAM_CMD_QUIT)
84 break;
85 if (buf_idx == STREAM_CMD_SYNC)
87 // this assumes that the recorder is already detached from any source
88 if (self->cur_buffer && self->cur_buffer->write_ptr)
89 sf_write_float(self->sndfile, self->cur_buffer->data, self->cur_buffer->write_ptr);
91 sf_command(self->sndfile, SFC_UPDATE_HEADER_NOW, NULL, 0);
92 sf_write_sync(self->sndfile);
93 sem_post(&self->sem_sync_completed);
94 continue;
96 else
98 sf_write_float(self->sndfile, self->buffers[buf_idx].data, self->buffers[buf_idx].write_ptr);
99 self->buffers[buf_idx].write_ptr = 0;
100 jack_ringbuffer_write(self->rb_just_written, (char *)&buf_idx, 1);
101 sf_command(self->sndfile, SFC_UPDATE_HEADER_NOW, NULL, 0);
103 } while(1);
104 return NULL;
107 static gboolean stream_recorder_attach(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error)
109 struct stream_recorder *self = handler->user_data;
111 if (self->sndfile)
113 if (error)
114 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Recorder already attached to a different source");
115 return FALSE;
118 memset(&self->info, 0, sizeof(self->info));
119 self->info.frames = 0;
120 self->info.samplerate = self->engine->io_env.srate;
121 self->info.channels = src->channels;
122 self->info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; // XXXKF make format configurable on instantiation
123 self->info.sections = 0;
124 self->info.seekable = 0;
126 self->sndfile = sf_open(self->filename, SFM_WRITE, &self->info);
127 if (!self->sndfile)
129 if (error)
130 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot open sound file '%s': %s", self->filename, sf_strerror(NULL));
131 return FALSE;
134 pthread_create(&self->thr_writeout, NULL, stream_recorder_thread, self);
135 return TRUE;
138 void stream_recorder_record_block(struct cbox_recorder *handler, const float **buffers, uint32_t numsamples)
140 struct stream_recorder *self = handler->user_data;
142 if (!self->sndfile)
143 return;
145 if (self->cur_buffer && (self->cur_buffer->write_ptr + numsamples * self->info.channels) * sizeof(float) >= STREAM_BUFFER_SIZE)
147 int8_t idx = self->cur_buffer - self->buffers;
148 jack_ringbuffer_write(self->rb_for_writing, (char *)&idx, 1);
149 self->cur_buffer = NULL;
151 if (!self->cur_buffer)
153 int8_t buf_idx = -1;
154 if (!jack_ringbuffer_read(self->rb_just_written, (char *)&buf_idx, 1)) // underrun
155 return;
156 self->cur_buffer = &self->buffers[buf_idx];
159 unsigned int nc = self->info.channels;
161 float *wbuf = self->cur_buffer->data + self->cur_buffer->write_ptr;
162 for (unsigned int c = 0; c < nc; c++)
163 for (int i = 0; i < numsamples; i++)
164 wbuf[c + i * nc] = buffers[c][i];
165 self->cur_buffer->write_ptr += nc * numsamples;
168 gboolean stream_recorder_detach(struct cbox_recorder *handler, GError **error)
170 struct stream_recorder *self = handler->user_data;
172 if (!self->sndfile)
174 if (error)
175 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No sound file associated with stream recorder");
176 return FALSE;
179 int8_t cmd = STREAM_CMD_SYNC;
180 jack_ringbuffer_write(self->rb_for_writing, (char *)&cmd, 1);
181 sem_wait(&self->sem_sync_completed);
182 return TRUE;
185 void stream_recorder_destroy(struct cbox_recorder *handler)
187 struct stream_recorder *self = handler->user_data;
189 if (self->sndfile)
191 int8_t cmd = STREAM_CMD_QUIT;
192 jack_ringbuffer_write(self->rb_for_writing, (char *)&cmd, 1);
193 pthread_join(self->thr_writeout, NULL);
196 free(self);
200 static gboolean stream_recorder_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
202 struct stream_recorder *rec = ct->user_data;
203 if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
205 if (!cbox_check_fb_channel(fb, cmd->command, error))
206 return FALSE;
208 if (!cbox_execute_on(fb, NULL, "/filename", "s", error, rec->filename))
209 return FALSE;
210 return CBOX_OBJECT_DEFAULT_STATUS(&rec->iface, fb, error);
212 return cbox_object_default_process_cmd(ct, fb, cmd, error);
215 struct cbox_recorder *cbox_recorder_new_stream(struct cbox_engine *engine, struct cbox_rt *rt, const char *filename)
217 struct stream_recorder *self = malloc(sizeof(struct stream_recorder));
218 self->rt = rt;
219 self->engine = engine;
220 CBOX_OBJECT_HEADER_INIT(&self->iface, cbox_recorder, CBOX_GET_DOCUMENT(engine));
221 cbox_command_target_init(&self->iface.cmd_target, stream_recorder_process_cmd, self);
223 self->iface.user_data = self;
224 self->iface.attach = stream_recorder_attach;
225 self->iface.record_block = stream_recorder_record_block;
226 self->iface.detach = stream_recorder_detach;
227 self->iface.destroy = stream_recorder_destroy;
229 self->sndfile = NULL;
230 self->filename = g_strdup(filename);
231 self->cur_buffer = NULL;
233 self->rb_for_writing = jack_ringbuffer_create(STREAM_BUFFER_COUNT + 1);
234 self->rb_just_written = jack_ringbuffer_create(STREAM_BUFFER_COUNT + 1);
235 sem_init(&self->sem_sync_completed, 0, 0);
237 CBOX_OBJECT_REGISTER(&self->iface);
239 for (uint8_t i = 0; i < STREAM_BUFFER_COUNT; i++)
240 jack_ringbuffer_write(self->rb_just_written, (char *)&i, 1);
242 return &self->iface;