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
26 #include <boost/atomic.hpp>
27 #include <boost/lockfree/spsc_queue.hpp>
31 #include "nova-tt/semaphore.hpp"
32 #include "utilities/branch_hints.hpp"
34 #include "audio_backend_common.hpp"
40 * audio backend, reading/writing sound files via libsndfile
43 template <typename engine_functor
,
44 typename sample_type
= float,
47 class sndfile_backend
:
48 public detail::audio_delivery_helper
<sample_type
, float, blocking
, false>,
49 public detail::audio_settings_basic
,
50 private engine_functor
52 typedef detail::audio_delivery_helper
<sample_type
, float, blocking
, false> super
;
53 typedef std::size_t size_t;
56 sndfile_backend(void):
57 read_frames(65536), write_frames(65536), running(false), reader_running(false), writer_running(false)
60 size_t get_audio_blocksize(void)
66 void open_client(std::string
const & input_file_name
, std::string
const & output_file_name
,
67 float samplerate
, int format
, uint32_t output_channel_count
)
69 output_channels
= output_channel_count
;
70 samplerate_
= samplerate
= std::floor(samplerate
);
72 if (!input_file_name
.empty()) {
73 input_file
= SndfileHandle(input_file_name
.c_str(), SFM_READ
);
75 throw std::runtime_error("cannot open input file");
77 if (input_file
.samplerate() != samplerate
)
78 throw std::runtime_error("input file: samplerate mismatch");
80 input_channels
= input_file
.channels();
81 super::input_samples
.resize(input_channels
);
87 output_file
= SndfileHandle(output_file_name
.c_str(), SFM_WRITE
, format
, output_channel_count
, samplerate
);
89 throw std::runtime_error("cannot open output file");
91 output_file
.command(SFC_SET_CLIPPING
, NULL
, SF_TRUE
);
93 super::output_samples
.resize(output_channel_count
);
95 temp_buffer
.reset(calloc_aligned
<float>(std::max(input_channels
, output_channels
) * 64));
99 void close_client(void)
101 output_file
.writeSync();
102 input_file
= output_file
= SndfileHandle();
105 bool audio_is_opened(void)
110 bool audio_is_active(void)
112 return running
.load(boost::memory_order_acquire
);
115 void activate_audio(void)
121 reader_running
.store(true);
122 reader_thread
= std::thread(std::bind(&sndfile_backend::sndfile_read_thread
, this));
125 writer_running
.store(true);
126 writer_thread
= std::thread(std::bind(&sndfile_backend::sndfile_write_thread
, this));
129 void deactivate_audio(void)
131 running
.store(false);
135 reader_running
.store(false);
136 reader_thread
.join();
139 writer_running
.store(false);
140 write_semaphore
.post();
141 writer_thread
.join();
145 /* read input fifo from the rt context */
146 void read_input_buffers(size_t frames_per_tick
)
148 if (reader_running
.load(boost::memory_order_acquire
))
150 const size_t total_samples
= input_channels
* frames_per_tick
;
151 size_t remaining
= total_samples
;
153 read_semaphore
.wait();
155 remaining
-= read_frames
.pop(temp_buffer
.get(), remaining
);
157 if (unlikely(read_frames
.empty() &&
158 !reader_running
.load(boost::memory_order_acquire
)))
160 /* at the end, we are not able to read a full sample block, clear the final parts */
161 const size_t last_frame
= (total_samples
- remaining
) / input_channels
;
162 const size_t remaining_per_channel
= remaining
/ input_channels
;
163 assert(remaining
% input_channels
== 0);
164 assert(remaining_per_channel
% input_channels
== 0);
166 for (uint16_t channel
= 0; channel
!= input_channels
; ++channel
)
167 zerovec(super::input_samples
[channel
].get() + last_frame
, remaining_per_channel
);
173 const size_t frames
= (total_samples
- remaining
) / input_channels
;
174 for (size_t frame
= 0; frame
!= frames
; ++frame
)
176 for (uint16_t channel
= 0; channel
!= input_channels
; ++channel
)
177 super::input_samples
[channel
].get()[frame
] = temp_buffer
.get()[frame
* input_channels
+ channel
];
181 super::clear_inputs(frames_per_tick
);
184 void sndfile_read_thread(void)
188 const size_t frames_per_tick
= get_audio_blocksize();
189 sized_array
<sample_type
, aligned_allocator
<sample_type
> > data_to_read(input_channels
* frames_per_tick
, 0.f
);
192 if (unlikely(reader_running
.load(boost::memory_order_acquire
) == false))
195 if (read_position
< (size_t)input_file
.frames())
197 size_t frames
= input_file
.frames() - read_position
;
198 if (frames
> frames_per_tick
)
199 frames
= frames_per_tick
;
201 input_file
.readf(data_to_read
.c_array(), frames
);
202 read_position
+= frames
;
204 const size_t item_to_enqueue
= input_channels
* frames
;
205 size_t remaining
= item_to_enqueue
;
208 remaining
-= read_frames
.push(data_to_read
.c_array(), remaining
);
210 read_semaphore
.post();
213 reader_running
.store(false, boost::memory_order_release
);
217 /* write output fifo from rt context */
218 void write_output_buffers(size_t frames_per_tick
)
220 for (size_t frame
= 0; frame
!= frames_per_tick
; ++frame
) {
221 for (uint16_t channel
= 0; channel
!= output_channels
; ++channel
)
222 temp_buffer
.get()[frame
* output_channels
+ channel
] = super::output_samples
[channel
].get()[frame
];
225 const size_t total_samples
= output_channels
* frames_per_tick
;
226 sample_type
* buffer
= temp_buffer
.get();
228 size_t count
= total_samples
;
230 size_t consumed
= write_frames
.push(buffer
, count
);
233 write_semaphore
.post();
237 void sndfile_write_thread(void)
239 const size_t frames_per_tick
= get_audio_blocksize();
240 sized_array
<sample_type
, aligned_allocator
<sample_type
> > data_to_write(output_channels
* frames_per_tick
, 0.f
);
243 write_semaphore
.wait();
245 size_t dequeued
= write_frames
.pop(data_to_write
.c_array(), data_to_write
.size());
249 output_file
.write(data_to_write
.c_array(), dequeued
);
251 if (unlikely(writer_running
.load(boost::memory_order_acquire
) == false))
258 void audio_fn_noinput(size_t frames_per_tick
)
260 engine_functor::run_tick();
261 write_output_buffers(frames_per_tick
);
264 void audio_fn(size_t frames_per_tick
)
266 super::clear_outputs(frames_per_tick
);
267 read_input_buffers(frames_per_tick
);
268 engine_functor::run_tick();
269 write_output_buffers(frames_per_tick
);
273 SndfileHandle input_file
, output_file
;
274 std::size_t read_position
;
276 aligned_storage_ptr
<sample_type
> temp_buffer
;
278 std::thread reader_thread
, writer_thread
;
279 boost::lockfree::spsc_queue
< sample_type
> read_frames
, write_frames
;
280 nova::semaphore read_semaphore
, write_semaphore
;
281 boost::atomic
<bool> running
, reader_running
, writer_running
;
284 } /* namespace nova */
286 #endif /* AUDIO_BACKEND_SNDFILE_BACKEND_HPP */