1 #ifndef AUDIOGRAPHER_TMP_FILE_RT_H
2 #define AUDIOGRAPHER_TMP_FILE_RT_H
8 #include "pbd/gstdio_compat.h"
9 #include "pbd/ringbuffer.h"
11 #include "audiographer/flag_debuggable.h"
12 #include "audiographer/sink.h"
13 #include "sndfile_writer.h"
14 #include "sndfile_reader.h"
17 namespace AudioGrapher
20 static const samplecnt_t rb_chunksize = 8192; // samples
22 /** A temporary file deleted after this class is destructed
23 * with realtime safe background thread writer.
25 template<typename T = DefaultSampleType>
31 /// \a filename_template must match the requirements for mkstemp, i.e. end in "XXXXXX"
32 TmpFileRt (char * filename_template, int format, ChannelCount channels, samplecnt_t samplerate)
33 : SndfileHandle (g_mkstemp(filename_template), true, SndfileBase::ReadWrite, format, channels, samplerate)
34 , filename (filename_template)
35 , _chunksize (rb_chunksize * channels)
36 , _rb (std::max (_chunksize * 16, 5 * samplerate * channels))
41 using SndfileHandle::operator=;
46 /* explicitly close first, some OS (yes I'm looking at you windows)
47 * cannot delete files that are still open
49 if (!filename.empty()) {
51 std::remove(filename.c_str());
53 pthread_mutex_destroy (&_disk_thread_lock);
54 pthread_cond_destroy (&_data_ready);
57 /// Writes data to file
58 void process (ProcessContext<T> const & c)
60 SndfileWriter<T>::check_flags (*this, c);
62 if (SndfileWriter<T>::throw_level (ThrowStrict) && c.channels() != SndfileHandle::channels()) {
63 throw Exception (*this, boost::str (boost::format
64 ("Wrong number of channels given to process(), %1% instead of %2%")
65 % c.channels() % SndfileHandle::channels()));
68 if (SndfileWriter<T>::throw_level (ThrowProcess) && _rb.write_space() < c.samples()) {
69 throw Exception (*this, boost::str (boost::format
70 ("Could not write data to ringbuffer/output file (%1%)")
71 % SndfileHandle::strError()));
74 _rb.write (c.data(), c.samples());
76 if (c.has_flag(ProcessContext<T>::EndOfInput)) {
78 SndfileWriter<T>::FileWritten (filename);
81 if (pthread_mutex_trylock (&_disk_thread_lock) == 0) {
82 pthread_cond_signal (&_data_ready);
83 pthread_mutex_unlock (&_disk_thread_lock);
87 using Sink<T>::process;
91 T *framebuf = (T*) malloc (_chunksize * sizeof (T));
93 pthread_mutex_lock (&_disk_thread_lock);
96 if ((samplecnt_t)_rb.read_space () >= _chunksize) {
97 _rb.read (framebuf, _chunksize);
98 samplecnt_t const written = SndfileBase::write (framebuf, _chunksize);
99 assert (written == _chunksize);
100 SndfileWriter<T>::samples_written += written;
105 pthread_cond_wait (&_data_ready, &_disk_thread_lock);
109 while (_rb.read_space () > 0) {
110 size_t remain = std::min ((samplecnt_t)_rb.read_space (), _chunksize);
111 _rb.read (framebuf, remain);
112 samplecnt_t const written = SndfileBase::write (framebuf, remain);
113 SndfileWriter<T>::samples_written += written;
116 SndfileWriter<T>::writeSync();
117 pthread_mutex_unlock (&_disk_thread_lock);
119 TmpFile<T>::FileFlushed ();
123 std::string filename;
126 samplecnt_t _chunksize;
127 PBD::RingBuffer<T> _rb;
129 pthread_mutex_t _disk_thread_lock;
130 pthread_cond_t _data_ready;
131 pthread_t _thread_id;
133 static void * _disk_thread (void *arg)
135 TmpFileRt *d = static_cast<TmpFileRt *>(arg);
142 pthread_mutex_lock (&_disk_thread_lock);
144 pthread_cond_signal (&_data_ready);
145 pthread_mutex_unlock (&_disk_thread_lock);
146 pthread_join (_thread_id, NULL);
151 SndfileWriter<T>::samples_written = 0;
153 SndfileWriter<T>::add_supported_flag (ProcessContext<T>::EndOfInput);
154 pthread_mutex_init (&_disk_thread_lock, 0);
155 pthread_cond_init (&_data_ready, 0);
157 if (pthread_create (&_thread_id, NULL, _disk_thread, this)) {
159 if (SndfileWriter<T>::throw_level (ThrowStrict)) {
160 throw Exception (*this, "Cannot create export disk writer");
166 TmpFileRt (TmpFileRt const & other) : SndfileHandle (other) {}
171 #endif // AUDIOGRAPHER_TMP_FILE_RT_H