2 * Copyright (C) 2006 Paul Davis
3 * Copyright (C) 2007 Michael Taht
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.
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.
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.
25 #define __STDC_FORMAT_MACROS
31 #include "pbd/pthread_utils.h"
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"
40 #include "tranzport_control_protocol.h"
42 using namespace ARDOUR
;
49 #include "pbd/abstract_ui.cc"
54 float def
= 0.0f
; /* Meter deflection %age */
56 if (db
< -70.0f
) return 0.0f
;
57 if (db
> 6.0f
) return 1.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
;
73 /* 115 is the deflection %age that would be
74 when db=6.0. this is an arbitrary
75 endpoint for our scaling.
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 */
90 // Shift Space - switches your "view"
91 // Currently defined views are:
94 // Shift Record - SAVE SNAPSHOT
95 // Somewhere I was rewriting this
97 // Inverted - show meters "inside out" For example 4 meters covering 2 cells each, and the
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
107 // Vertical Meter _ .colon - + ucolon A P R I H FULLBLACK
110 // 3 char block rotating beat `\'/
111 // 1 char rotating beat {/\}
112 // 4 char in block rotating beat {/\}
115 void TranzportControlProtocol::show_mini_meter()
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;
123 float speed
= fabsf(session
->transport_speed());
124 char buf
[meter_buf_size
];
131 meter_size
= 20; // not actually reached
134 if (speed
> 0.0 && (speed
< 1.0)) {
135 meter_size
= 20; // may shrink more one day
138 if (speed
> 1.0 && (speed
< 2.0)) {
147 // you only seem to get a route_table[0] == 0 on moving forward - bug in next_track?
149 if (route_table
[0] == 0) {
150 // Principle of least surprise
151 print (1, 0, "NoAUDIO ");
155 float level_l
= route_get_peak_input_power (0, 0);
156 float fraction_l
= log_meter (level_l
);
158 // how to figure out if we are mono?
160 float level_r
= route_get_peak_input_power (0, 1);
161 float fraction_r
= log_meter (level_r
);
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
));
166 if (fill_left
== last_meter_fill_l
&& fill_right
== last_meter_fill_r
&& !lcd_isdamaged(1,0,meter_size
/2)) {
171 last_meter_fill_l
= fill_left
; last_meter_fill_r
= fill_right
;
173 // give some feedback when overdriving - override yellow and red lights
175 if (fraction_l
> 0.96 || fraction_r
> 0.96) {
176 light_on (LightLoop
);
179 if (fraction_l
== 1.0 || fraction_r
== 1.0) {
180 light_on (LightTrackrec
);
183 const uint8_t char_map
[16] = { ' ', TRANZ_UL
,
192 unsigned int val
,j
,i
;
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
];
200 /* print() requires this */
202 buf
[meter_size
/2] = '\0';
206 /* Add a peak bar, someday do falloff */
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.
215 TranzportControlProtocol::show_meter ()
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");
225 float level
= route_get_peak_input_power (0, 0);
226 float fraction
= log_meter (level
);
228 /* Someday add a peak bar*/
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 ":"
235 uint32_t fill
= (uint32_t) floor (fraction
* 40);
239 if (fill
== last_meter_fill
) {
244 last_meter_fill
= fill
;
246 bool add_single_level
= (fill
% 2 != 0);
249 if (fraction
> 0.96) {
250 light_on (LightLoop
);
254 if (fraction
== 1.0) {
255 light_on (LightTrackrec
);
259 /* add all full steps */
261 for (i
= 0; i
< fill
; ++i
) {
262 buf
[i
] = 0x07; /* tranzport special code for 4 quadrant LCD block */
265 /* add a possible half-step */
267 if (i
< 20 && add_single_level
) {
268 buf
[i
] = 0x03; /* tranzport special code for 2 left quadrant LCD block */
272 /* fill rest with space */
274 for (; i
< 20; ++i
) {
278 /* print() requires this */
287 TranzportControlProtocol::show_bbt (nframes_t where
)
289 if (where
!= last_where
) {
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.
298 session
->tempo_map().bbt_time (where
, bbt
);
299 last_bars
= bbt
.bars
;
300 last_beats
= bbt
.beats
;
301 last_ticks
= bbt
.ticks
;
304 float speed
= fabsf(session
->transport_speed());
307 sprintf (buf
, "%03" PRIu32
"%1" PRIu32
, bbt
.bars
,bbt
.beats
); // switch to hex one day
312 sprintf (buf
, "%03" PRIu32
"|%1" PRIu32
"|%04" PRIu32
, bbt
.bars
,bbt
.beats
,bbt
.ticks
);
316 if (speed
> 0.0 && (speed
< 1.0)) {
317 sprintf (buf
, "%03" PRIu32
"|%1" PRIu32
"|%04" PRIu32
, bbt
.bars
,bbt
.beats
,bbt
.ticks
);
321 if (speed
> 1.0 && (speed
< 2.0)) {
322 sprintf (buf
, "%03" PRIu32
"|%1" PRIu32
"|%04" PRIu32
, bbt
.bars
,bbt
.beats
,bbt
.ticks
);
327 sprintf (buf
, "%03" PRIu32
"|%1" PRIu32
"|%02" PRIu32
, bbt
.bars
,bbt
.beats
,bbt
.ticks
);
331 TempoMap::Metric
m (session
->tempo_map().metric_at (where
));
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)) {
336 // something else can reset these, so we need to
338 lights_pending
[LightRecord
] = false;
339 lights_pending
[LightAnysolo
] = false;
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;
349 TranzportControlProtocol::show_transport_time ()
351 nframes_t where
= session
->transport_frame();
356 TranzportControlProtocol::show_timecode (nframes_t where
)
358 if ((where
!= last_where
) || lcd_isdamaged(1,9,10)) {
361 Timecode::Time timecode
;
363 session
->timecode_time (where
, timecode
);
365 if (timecode
.negative
) {
366 sprintf (buf
, "-%02" PRIu32
":", timecode
.hours
);
368 sprintf (buf
, " %02" PRIu32
":", timecode
.hours
);
372 sprintf (buf
, "%02" PRIu32
":", timecode
.minutes
);
375 sprintf (buf
, "%02" PRIu32
":", timecode
.seconds
);
378 sprintf (buf
, "%02" PRIu32
, timecode
.frames
);
379 print_noretry (1, 18, buf
);
386 TranzportControlProtocol::show_track_gain ()
388 // FIXME last_track gain has to become meter/track specific
389 if (route_table
[0]) {
390 gain_t g
= route_get_gain (0);
391 if ((g
!= last_track_gain
) || lcd_isdamaged(0,12,8)) {
393 snprintf (buf
, sizeof (buf
), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));