Fix scvim regsitry file for updated filename (thanks Carlo Capocasa)
[supercollider.git] / server / supernova / audio_backend / sndfile_backend.hpp
blob1a16c878bdbf46bd2959a93c3fd0a427d213eb6c
1 // file-based backend (via libsndfile)
2 // Copyright (C) 2010 Tim Blechmann
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; see the file COPYING. If not, write to
16 // the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 // Boston, MA 02111-1307, USA.
19 #ifndef AUDIO_BACKEND_SNDFILE_BACKEND_HPP
20 #define AUDIO_BACKEND_SNDFILE_BACKEND_HPP
22 #include <cmath>
23 #include <string>
25 #include <boost/atomic.hpp>
26 #include <boost/lockfree/ringbuffer.hpp>
27 #include <boost/thread.hpp>
29 #include <sndfile.hh>
31 #include "nova-tt/semaphore.hpp"
32 #include "utilities/branch_hints.hpp"
34 #include "audio_backend_common.hpp"
36 namespace nova
39 /** sndfile backend
41 * audio backend, reading/writing sound files via libsndfile
44 template <typename engine_functor,
45 typename sample_type = float,
46 bool blocking = false
48 class sndfile_backend:
49 public detail::audio_delivery_helper<sample_type, float, blocking, false>,
50 public detail::audio_settings_basic,
51 private engine_functor
53 typedef detail::audio_delivery_helper<sample_type, float, blocking, false> super;
54 typedef std::size_t size_t;
56 public:
57 sndfile_backend(void):
58 read_frames(65536), write_frames(65536), running(false), reader_running(false), writer_running(false)
61 ~sndfile_backend(void)
64 size_t get_audio_blocksize(void)
66 return 64;
69 public:
70 void open_client(std::string const & input_file_name, std::string const & output_file_name,
71 float samplerate, int format, uint32_t output_channel_count)
73 output_channels = output_channel_count;
74 samplerate_ = samplerate = std::floor(samplerate);
76 if (!input_file_name.empty())
78 input_file = SndfileHandle(input_file_name.c_str(), SFM_READ);
79 if (!input_file)
80 throw std::runtime_error("cannot open input file");
82 if (input_file.samplerate() != samplerate)
83 throw std::runtime_error("input file: samplerate mismatch");
85 input_channels = input_file.channels();
86 super::input_samples.resize(input_channels);
88 else
89 input_channels = 0;
90 read_position = 0;
92 output_file = SndfileHandle(output_file_name.c_str(), SFM_WRITE, format, output_channel_count, samplerate);
93 if (!output_file)
94 throw std::runtime_error("cannot open output file");
95 super::output_samples.resize(output_channel_count);
97 temp_buffer.reset(calloc_aligned<float>(std::max(input_channels, output_channels) * 64));
101 void close_client(void)
103 output_file.writeSync();
104 input_file = output_file = SndfileHandle();
107 bool audio_is_opened(void)
109 return output_file;
112 bool audio_is_active(void)
114 return running.load(boost::memory_order_acquire);
117 void activate_audio(void)
119 running.store(true);
121 if (input_file)
123 reader_running.store(true);
124 reader_thread = boost::thread(boost::bind(&sndfile_backend::sndfile_read_thread, this));
127 writer_running.store(true);
128 writer_thread = boost::thread(boost::bind(&sndfile_backend::sndfile_write_thread, this));
131 void deactivate_audio(void)
133 running.store(false);
135 if (input_file)
137 reader_running.store(false);
138 reader_thread.join();
141 writer_running.store(false);
142 write_semaphore.post();
143 writer_thread.join();
146 private:
147 /* read input fifo from the rt context */
148 void read_input_buffers(size_t frames_per_tick)
150 if (reader_running.load(boost::memory_order_acquire))
152 const size_t total_samples = input_channels * frames_per_tick;
153 size_t remaining = total_samples;
155 read_semaphore.wait();
156 do {
157 remaining -= read_frames.dequeue(temp_buffer.get(), remaining);
159 if (unlikely(read_frames.empty() &&
160 !reader_running.load(boost::memory_order_acquire)))
162 /* at the end, we are not able to read a full sample block, clear the final parts */
163 const size_t last_frame = (total_samples - remaining) / input_channels;
164 const size_t remaining_per_channel = remaining / input_channels;
165 assert(remaining % input_channels == 0);
166 assert(remaining_per_channel % input_channels == 0);
168 for (uint16_t channel = 0; channel != input_channels; ++channel)
169 zerovec(super::input_samples[channel].get() + last_frame, remaining_per_channel);
171 break;
173 } while (remaining);
175 const size_t frames = (total_samples - remaining) / input_channels;
176 for (size_t frame = 0; frame != frames; ++frame)
178 for (uint16_t channel = 0; channel != input_channels; ++channel)
179 super::input_samples[channel].get()[frame] = temp_buffer.get()[frame * input_channels + channel];
182 else
183 super::clear_inputs(frames_per_tick);
186 void sndfile_read_thread(void)
188 assert(input_file);
190 const size_t frames_per_tick = get_audio_blocksize();
191 sized_array<sample_type, aligned_allocator<sample_type> > data_to_read(input_channels * frames_per_tick, 0.f);
193 for (;;)
195 if (unlikely(reader_running.load(boost::memory_order_acquire) == false))
196 return;
198 if (read_position < (size_t)input_file.frames())
200 size_t frames = input_file.frames() - read_position;
201 if (frames > frames_per_tick)
202 frames = frames_per_tick;
204 input_file.readf(data_to_read.c_array(), frames);
205 read_position += frames;
207 const size_t item_to_enqueue = input_channels * frames;
208 size_t remaining = item_to_enqueue;
210 do {
211 remaining -= read_frames.enqueue(data_to_read.c_array(), remaining);
212 } while(remaining);
213 read_semaphore.post();
215 else
216 reader_running.store(false, boost::memory_order_release);
220 /* write output fifo from rt context */
221 void write_output_buffers(size_t frames_per_tick)
223 for (size_t frame = 0; frame != frames_per_tick; ++frame)
225 for (uint16_t channel = 0; channel != output_channels; ++channel)
226 temp_buffer.get()[frame * output_channels + channel] = super::output_samples[channel].get()[frame];
229 const size_t total_samples = output_channels * frames_per_tick;
230 sample_type * buffer = temp_buffer.get();
232 size_t count = total_samples;
233 do {
234 size_t consumed = write_frames.enqueue(buffer, count);
235 count -= consumed;
236 buffer += consumed;
237 write_semaphore.post();
238 } while (count);
241 void sndfile_write_thread(void)
243 const size_t frames_per_tick = get_audio_blocksize();
244 sized_array<sample_type, aligned_allocator<sample_type> > data_to_write(output_channels * frames_per_tick, 0.f);
246 for (;;)
248 write_semaphore.wait();
249 for (;;)
251 size_t dequeued = write_frames.dequeue(data_to_write.c_array(), data_to_write.size());
253 if (dequeued == 0)
254 break;
255 output_file.write(data_to_write.c_array(), dequeued);
257 if (unlikely(writer_running.load(boost::memory_order_acquire) == false))
258 return;
263 public:
264 void audio_fn_noinput(size_t frames_per_tick)
266 engine_functor::run_tick();
267 write_output_buffers(frames_per_tick);
270 void audio_fn(size_t frames_per_tick)
272 super::clear_outputs(frames_per_tick);
273 read_input_buffers(frames_per_tick);
274 engine_functor::run_tick();
275 write_output_buffers(frames_per_tick);
278 private:
279 SndfileHandle input_file, output_file;
280 std::size_t read_position;
282 aligned_storage_ptr<sample_type> temp_buffer;
284 boost::thread reader_thread, writer_thread;
285 boost::lockfree::ringbuffer< sample_type, 0 > read_frames, write_frames;
286 nova::semaphore read_semaphore, write_semaphore;
287 boost::atomic<bool> running, reader_running, writer_running;
290 } /* namespace nova */
292 #endif /* AUDIO_BACKEND_SNDFILE_BACKEND_HPP */