1 // file-based backend (via libsndfile)
2 // Copyright (C) 2010 Tim Blechmann
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.
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
25 #include <boost/atomic.hpp>
26 #include <boost/lockfree/ringbuffer.hpp>
27 #include <boost/thread.hpp>
31 #include "nova-tt/semaphore.hpp"
32 #include "utilities/branch_hints.hpp"
34 #include "audio_backend_common.hpp"
41 * audio backend, reading/writing sound files via libsndfile
44 template <typename engine_functor
,
45 typename sample_type
= float,
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;
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)
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
);
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
);
92 output_file
= SndfileHandle(output_file_name
.c_str(), SFM_WRITE
, format
, output_channel_count
, samplerate
);
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)
112 bool audio_is_active(void)
114 return running
.load(boost::memory_order_acquire
);
117 void activate_audio(void)
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);
137 reader_running
.store(false);
138 reader_thread
.join();
141 writer_running
.store(false);
142 write_semaphore
.post();
143 writer_thread
.join();
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();
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
);
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
];
183 super::clear_inputs(frames_per_tick
);
186 void sndfile_read_thread(void)
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
);
195 if (unlikely(reader_running
.load(boost::memory_order_acquire
) == false))
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
;
211 remaining
-= read_frames
.enqueue(data_to_read
.c_array(), remaining
);
213 read_semaphore
.post();
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
;
234 size_t consumed
= write_frames
.enqueue(buffer
, count
);
237 write_semaphore
.post();
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
);
248 write_semaphore
.wait();
251 size_t dequeued
= write_frames
.dequeue(data_to_write
.c_array(), data_to_write
.size());
255 output_file
.write(data_to_write
.c_array(), dequeued
);
257 if (unlikely(writer_running
.load(boost::memory_order_acquire
) == false))
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
);
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 */