enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / surfaces / tranzport / show.cc
1 /*
2  *   Copyright (C) 2006 Paul Davis
3  *   Copyright (C) 2007 Michael Taht
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  *   */
20
21 #include <iostream>
22 #include <algorithm>
23 #include <cmath>
24
25 #define __STDC_FORMAT_MACROS
26 #include <inttypes.h>
27 #include <float.h>
28 #include <sys/time.h>
29 #include <errno.h>
30
31 #include "pbd/pthread_utils.h"
32
33 #include "ardour/route.h"
34 #include "ardour/audio_track.h"
35 #include "ardour/session.h"
36 #include "ardour/tempo.h"
37 #include "ardour/location.h"
38 #include "ardour/dB.h"
39
40 #include "tranzport_control_protocol.h"
41
42 using namespace ARDOUR;
43 using namespace std;
44 using namespace sigc;
45 using namespace PBD;
46
47 #include "pbd/i18n.h"
48
49 #include "pbd/abstract_ui.cc"
50
51 float
52 log_meter (float db)
53 {
54         float def = 0.0f; /* Meter deflection %age */
55
56         if (db < -70.0f) return 0.0f;
57         if (db > 6.0f) return 1.0f;
58
59         if (db < -60.0f) {
60                 def = (db + 70.0f) * 0.25f;
61         } else if (db < -50.0f) {
62                 def = (db + 60.0f) * 0.5f + 2.5f;
63         } else if (db < -40.0f) {
64                 def = (db + 50.0f) * 0.75f + 7.5f;
65         } else if (db < -30.0f) {
66                 def = (db + 40.0f) * 1.5f + 15.0f;
67         } else if (db < -20.0f) {
68                 def = (db + 30.0f) * 2.0f + 30.0f;
69         } else if (db < 6.0f) {
70                 def = (db + 20.0f) * 2.5f + 50.0f;
71         }
72
73         /* 115 is the deflection %age that would be
74            when db=6.0. this is an arbitrary
75            endpoint for our scaling.
76         */
77
78         return def/115.0f;
79 }
80
81 #define TRANZ_U  0x1 /* upper */
82 #define TRANZ_BL 0x2 /* lower left */
83 #define TRANZ_Q2 0x3 /* 2 quadrant block */
84 #define TRANZ_ULB 0x4 /* Upper + lower left */
85 #define TRANZ_L 0x5  /* lower  */
86 #define TRANZ_UBL 0x6 /* upper left + bottom all */
87 #define TRANZ_Q4 0x7 /* 4 quadrant block */
88 #define TRANZ_UL 0x08 /* upper left */
89
90 // Shift Space - switches your "view"
91 // Currently defined views are:
92 // BigMeter
93 //
94 // Shift Record - SAVE SNAPSHOT
95 // Somewhere I was rewriting this
96 // Other meters
97 // Inverted - show meters "inside out" For example 4 meters covering 2 cells each, and the
98 //
99 // each 4 character cell could be an 8 bar meter = 10 meters!
100 // Dual Meter mode - master and current track
101 // We have 16 rows of pixels so we COULD do a vertical meter
102 // BEAT BLOCKS - For each beat, flash a 8 block (could use the center for vertical meters)
103 // Could have something generic that could handle up to /20 time
104 // Odd times could flash the whole top bar for the first beat
105
106
107 // Vertical Meter _ .colon - + ucolon A P R I H FULLBLACK
108 // MV@$%&*()-
109
110 // 3 char block  rotating beat `\'/
111 // 1 char rotating beat {/\}
112 // 4 char in block rotating beat {/\}
113 //                               {\/)
114
115 void TranzportControlProtocol::show_mini_meter()
116 {
117         // FIXME - show the current marker in passing
118         const int meter_buf_size = 41;
119         static uint32_t last_meter_fill_l = 0;
120         static uint32_t last_meter_fill_r = 0;
121         uint32_t meter_size;
122
123         float speed = fabsf(session->transport_speed());
124         char buf[meter_buf_size];
125
126         if (speed == 1.0)  {
127                 meter_size = 32;
128         }
129
130         if (speed == 0.0) {
131                 meter_size = 20;  // not actually reached
132         }
133
134         if (speed > 0.0 && (speed < 1.0)) {
135                 meter_size = 20; // may shrink more one day
136         }
137
138         if (speed > 1.0 && (speed < 2.0)) {
139                 meter_size = 20;
140         }
141
142         if (speed >= 2.0) {
143                 meter_size = 24;
144         }
145
146
147         // you only seem to get a route_table[0] == 0 on moving forward - bug in next_track?
148
149         if (route_table[0] == 0) {
150                 // Principle of least surprise
151                 print (1, 0, "NoAUDIO  ");
152                 return;
153         }
154
155         float level_l = route_get_peak_input_power (0, 0);
156         float fraction_l = log_meter (level_l);
157
158         // how to figure out if we are mono?
159
160         float level_r = route_get_peak_input_power (0, 1);
161         float fraction_r = log_meter (level_r);
162
163         uint32_t fill_left  = (uint32_t) floor (fraction_l * ((int) meter_size));
164         uint32_t fill_right  = (uint32_t) floor (fraction_r * ((int) meter_size));
165
166         if (fill_left == last_meter_fill_l && fill_right == last_meter_fill_r && !lcd_isdamaged(1,0,meter_size/2)) {
167                 /* nothing to do */
168                 return;
169         }
170
171         last_meter_fill_l = fill_left;  last_meter_fill_r = fill_right;
172
173         // give some feedback when overdriving - override yellow and red lights
174
175         if (fraction_l > 0.96 || fraction_r > 0.96) {
176                 light_on (LightLoop);
177         }
178
179         if (fraction_l == 1.0 || fraction_r == 1.0) {
180                 light_on (LightTrackrec);
181         }
182
183         const uint8_t char_map[16] = { ' ', TRANZ_UL,
184                                        TRANZ_U, TRANZ_U,
185                                        TRANZ_BL, TRANZ_Q2,
186                                        TRANZ_Q2, TRANZ_ULB,
187                                        TRANZ_L, TRANZ_UBL,
188                                        ' ',' ',
189                                        TRANZ_L, TRANZ_UBL,
190                                        TRANZ_Q4,TRANZ_Q4
191         };
192         unsigned int val,j,i;
193
194         for(j = 1, i = 0; i < meter_size/2; i++, j+=2) {
195                 val = (fill_left >= j) | ((fill_left >= j+1) << 1) |
196                         ((fill_right >=j) << 2) | ((fill_right >= j+1) << 3);
197                 buf[i] = char_map[val];
198         }
199
200         /* print() requires this */
201
202         buf[meter_size/2] = '\0';
203
204         print (1, 0, buf);
205
206         /* Add a peak bar, someday do falloff */
207
208         //              char peak[2]; peak[0] = ' '; peak[1] = '\0';
209         //              if(fraction_l == 1.0 || fraction_r == 1.0) peak[0] = 'P';
210         //              print (1,8,peak); // Put a peak meter - P in if we peaked.
211
212 }
213
214 void
215 TranzportControlProtocol::show_meter ()
216 {
217         // you only seem to get a route_table[0] on moving forward - bug elsewhere
218         if (route_table[0] == 0) {
219                 // Principle of least surprise
220                 print (0, 0, "No audio to meter!!!");
221                 print (1, 0, "Select another track");
222                 return;
223         }
224
225         float level = route_get_peak_input_power (0, 0);
226         float fraction = log_meter (level);
227
228         /* Someday add a peak bar*/
229
230         /* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
231            the screen is 20 chars wide, so we can display 40 different levels. compute the level,
232            then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
233         */
234
235         uint32_t fill  = (uint32_t) floor (fraction * 40);
236         char buf[21];
237         uint32_t i;
238
239         if (fill == last_meter_fill) {
240                 /* nothing to do */
241                 return;
242         }
243
244         last_meter_fill = fill;
245
246         bool add_single_level = (fill % 2 != 0);
247         fill /= 2;
248
249         if (fraction > 0.96) {
250                 light_on (LightLoop);
251         }
252
253
254         if (fraction == 1.0) {
255                 light_on (LightTrackrec);
256         }
257
258
259         /* add all full steps */
260
261         for (i = 0; i < fill; ++i) {
262                 buf[i] = 0x07; /* tranzport special code for 4 quadrant LCD block */
263         }
264
265         /* add a possible half-step */
266
267         if (i < 20 && add_single_level) {
268                 buf[i] = 0x03; /* tranzport special code for 2 left quadrant LCD block */
269                 ++i;
270         }
271
272         /* fill rest with space */
273
274         for (; i < 20; ++i) {
275                 buf[i] = ' ';
276         }
277
278         /* print() requires this */
279
280         buf[20] = '\0';
281
282         print (0, 0, buf);
283         print (1, 0, buf);
284 }
285
286 void
287 TranzportControlProtocol::show_bbt (framepos_t where)
288 {
289         if (where != last_where) {
290                 char buf[16];
291                 Timecode::BBT_Time bbt;
292
293                 // When recording or playing back < 1.0 speed do 1 or 2
294                 // FIXME - clean up state machine & break up logic
295                 // this has to co-operate with the mini-meter and
296                 // this is NOT the right way.
297
298                 session->tempo_map().bbt_time (where, bbt);
299                 last_bars = bbt.bars;
300                 last_beats = bbt.beats;
301                 last_ticks = bbt.ticks;
302                 last_where = where;
303
304                 float speed = fabsf(session->transport_speed());
305
306                 if (speed == 1.0)  {
307                         sprintf (buf, "%03" PRIu32 "%1" PRIu32, bbt.bars,bbt.beats); // switch to hex one day
308                         print (1, 16, buf);
309                 }
310
311                 if (speed == 0.0) {
312                         sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
313                         print (1, 10, buf);
314                 }
315
316                 if (speed > 0.0 && (speed < 1.0)) {
317                         sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
318                         print (1, 10, buf);
319                 }
320
321                 if (speed > 1.0 && (speed < 2.0)) {
322                         sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
323                         print (1, 10, buf);
324                 }
325
326                 if (speed >= 2.0) {
327                         sprintf (buf, "%03" PRIu32 "|%1" PRIu32 "|%02" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
328                         print (1, 12, buf);
329                 }
330
331                 TempoMap::Metric m (session->tempo_map().metric_at (where));
332
333                 // the lights stop working well at above 100 bpm so don't bother
334                 if(m.tempo().beats_per_minute() < 101.0 && (speed > 0.0)) {
335
336                         // something else can reset these, so we need to
337
338                         lights_pending[LightRecord] = false;
339                         lights_pending[LightAnysolo] = false;
340                         switch(last_beats) {
341                         case 1: if(last_ticks < 250 || last_ticks >= 0) lights_pending[LightRecord] = true; break;
342                         default: if(last_ticks < 250) lights_pending[LightAnysolo] = true;
343                         }
344                 }
345         }
346 }
347
348 void
349 TranzportControlProtocol::show_transport_time ()
350 {
351         show_bbt (session->transport_frame ());
352 }
353
354 void
355 TranzportControlProtocol::show_timecode (framepos_t where)
356 {
357         if ((where != last_where) || lcd_isdamaged(1,9,10)) {
358
359                 char buf[5];
360                 Timecode::Time timecode;
361
362                 session->timecode_time (where, timecode);
363
364                 if (timecode.negative) {
365                         sprintf (buf, "-%02" PRIu32 ":", timecode.hours);
366                 } else {
367                         sprintf (buf, " %02" PRIu32 ":", timecode.hours);
368                 }
369                 print (1, 8, buf);
370
371                 sprintf (buf, "%02" PRIu32 ":", timecode.minutes);
372                 print (1, 12, buf);
373
374                 sprintf (buf, "%02" PRIu32 ":", timecode.seconds);
375                 print (1, 15, buf);
376
377                 sprintf (buf, "%02" PRIu32, timecode.frames);
378                 print_noretry (1, 18, buf);
379
380                 last_where = where;
381         }
382 }
383
384 void
385 TranzportControlProtocol::show_track_gain ()
386 {
387 // FIXME last_track gain has to become meter/track specific
388         if (route_table[0]) {
389                 gain_t g = route_get_gain (0);
390                 if ((g != last_track_gain) || lcd_isdamaged(0,12,8)) {
391                         char buf[16];
392                         snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
393                         print (0, 12, buf);
394                         last_track_gain = g;
395                 }
396         } else {
397                 print (0, 9, "        ");
398         }
399 }