remove Track::hidden(); replace with Stripable::is_private_route()
[ardour.git] / libs / ardour / session_click.cc
1 /*
2     Copyright (C) 2002 Paul Davis
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.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <list>
21 #include <cerrno>
22
23 #include "ardour/amp.h"
24 #include "ardour/audio_buffer.h"
25 #include "ardour/buffer_set.h"
26 #include "ardour/click.h"
27 #include "ardour/io.h"
28 #include "ardour/session.h"
29 #include "ardour/tempo.h"
30 #include "ardour/types.h"
31
32 #include <sndfile.h>
33
34 #include "pbd/i18n.h"
35
36 using namespace std;
37 using namespace ARDOUR;
38 using namespace PBD;
39
40 Pool Click::pool ("click", sizeof (Click), 1024);
41
42 void
43 Session::add_click (framepos_t pos, bool emphasis)
44 {
45         if (emphasis) {
46                 if (click_emphasis_data && Config->get_use_click_emphasis () == true) {
47                         clicks.push_back (new Click (pos, click_emphasis_length, click_emphasis_data));
48                 } else if (click_data && Config->get_use_click_emphasis () == false) {
49                         clicks.push_back (new Click (pos, click_length, click_data));
50                 }
51         } else if (click_data) {
52                 clicks.push_back (new Click (pos, click_length, click_data));
53         }
54 }
55
56 void
57 Session::click (framepos_t start, framecnt_t nframes)
58 {
59         vector<TempoMap::BBTPoint> points;
60         framecnt_t click_distance;
61
62         if (_click_io == 0) {
63                 return;
64         }
65
66         Glib::Threads::RWLock::WriterLock clickm (click_lock, Glib::Threads::TRY_LOCK);
67
68         /* how far have we moved since the last time the clicks got cleared
69          */
70
71         click_distance = start - _clicks_cleared;
72
73         if (!clickm.locked() || !_clicking || click_data == 0 || ((click_distance + nframes) < _worst_track_latency)) {
74                 _click_io->silence (nframes);
75                 return;
76         }
77
78         if (_click_rec_only && !actively_recording()) {
79                 return;
80         }
81
82         start -= _worst_track_latency;
83 #ifdef MIXBUS
84         if (_master_out) {
85                 start -= _master_out->signal_latency (); // delay signal by mixbus' internal latency
86         }
87 #endif
88         /* start could be negative at this point */
89         const framepos_t end = start + nframes;
90         /* correct start, potentially */
91         start = max (start, (framepos_t) 0);
92
93         _tempo_map->get_grid (points, start, end);
94
95         if (distance (points.begin(), points.end()) == 0) {
96                 goto run_clicks;
97         }
98
99         for (vector<TempoMap::BBTPoint>::iterator i = points.begin(); i != points.end(); ++i) {
100                 switch ((*i).beat) {
101                 case 1:
102                         add_click ((*i).frame, true);
103                         break;
104                 default:
105                         if (click_emphasis_data == 0 || (Config->get_use_click_emphasis () == false) || (click_emphasis_data && (*i).beat != 1)) { // XXX why is this check needed ??
106                                 add_click ((*i).frame, false);
107                         }
108                         break;
109                 }
110         }
111
112   run_clicks:
113         clickm.release ();
114         run_click (start, nframes);
115 }
116
117 void
118 Session::run_click (framepos_t start, framepos_t nframes)
119 {
120         Glib::Threads::RWLock::ReaderLock clickm (click_lock, Glib::Threads::TRY_LOCK);
121
122         if (!clickm.locked() || click_data == 0) {
123                 _click_io->silence (nframes);
124                 return;
125         }
126
127         Sample *buf;
128         BufferSet& bufs = get_scratch_buffers(ChanCount(DataType::AUDIO, 1));
129         buf = bufs.get_audio(0).data();
130         memset (buf, 0, sizeof (Sample) * nframes);
131
132         for (list<Click*>::iterator i = clicks.begin(); i != clicks.end(); ) {
133
134                 framecnt_t copy;
135                 framecnt_t internal_offset;
136                 Click *clk;
137
138                 clk = *i;
139
140                 if (clk->start < start) {
141                         internal_offset = 0;
142                 } else {
143                         internal_offset = clk->start - start;
144                 }
145
146                 if (nframes < internal_offset) {
147                          /* we've just located or something..
148                             effectively going backwards.
149                             lets get the flock out of here */
150                         break;
151                 }
152
153                 copy = min (clk->duration - clk->offset, nframes - internal_offset);
154
155                 memcpy (buf + internal_offset, &clk->data[clk->offset], copy * sizeof (Sample));
156
157                 clk->offset += copy;
158
159                 if (clk->offset >= clk->duration) {
160                         delete clk;
161                         i = clicks.erase (i);
162                 } else {
163                         ++i;
164                 }
165         }
166
167         _click_gain->run (bufs, 0, 0, 1.0, nframes, false);
168         _click_io->copy_to_outputs (bufs, DataType::AUDIO, nframes, 0);
169 }
170
171 void
172 Session::setup_click_sounds (Sample** data, Sample const * default_data, framecnt_t* length, framecnt_t default_length, string const & path)
173 {
174         if (*data != default_data) {
175                 delete[] *data;
176                 *data = 0;
177         }
178
179         if (path.empty ()) {
180
181                 *data = const_cast<Sample*> (default_data);
182                 *length = default_length;
183
184         } else {
185
186                 SF_INFO info;
187                 SNDFILE* sndfile;
188
189                 info.format = 0;
190                 if ((sndfile = sf_open (path.c_str(), SFM_READ, &info)) == 0) {
191                         char errbuf[256];
192                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
193                         warning << string_compose (_("cannot open click soundfile %1 (%2)"), path, errbuf) << endmsg;
194                         _clicking = false;
195                         return;
196                 }
197
198                 /* read the (possibly multi-channel) click data into a temporary buffer */
199
200                 sf_count_t const samples = info.frames * info.channels;
201
202                 Sample* tmp = new Sample[samples];
203
204                 if (sf_readf_float (sndfile, tmp, info.frames) != info.frames) {
205
206                         warning << _("cannot read data from click soundfile") << endmsg;
207                         *data = 0;
208                         _clicking = false;
209
210                 } else {
211
212                         *data = new Sample[info.frames];
213                         *length = info.frames;
214
215                         /* mix down to mono */
216
217                         for (int i = 0; i < info.frames; ++i) {
218                                 (*data)[i] = 0;
219                                 for (int j = 0; j < info.channels; ++j) {
220                                         (*data)[i] = tmp[i * info.channels + j];
221                                 }
222                                 (*data)[i] /= info.channels;
223                         }
224                 }
225
226                 delete[] tmp;
227                 sf_close (sndfile);
228         }
229 }
230
231 void
232 Session::setup_click_sounds (int which)
233 {
234         clear_clicks ();
235
236         if (which == 0 || which == 1) {
237                 setup_click_sounds (
238                         &click_data,
239                         default_click,
240                         &click_length,
241                         default_click_length,
242                         Config->get_click_sound ()
243                         );
244         }
245
246         if (which == 0 || which == -1) {
247                 setup_click_sounds (
248                         &click_emphasis_data,
249                         default_click_emphasis,
250                         &click_emphasis_length,
251                         default_click_emphasis_length,
252                         Config->get_click_emphasis_sound ()
253                         );
254         }
255 }
256
257 void
258 Session::clear_clicks ()
259 {
260         Glib::Threads::RWLock::WriterLock lm (click_lock);
261
262         for (Clicks::iterator i = clicks.begin(); i != clicks.end(); ++i) {
263                 delete *i;
264         }
265
266         clicks.clear ();
267         _clicks_cleared = _transport_frame;
268 }