Assists music production by grouping standalone programs into sessions. Community version of "Non Session Manager".
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1549 lines
36KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2008 Jonathan Moore Liles */
  3. /* */
  4. /* This program is free software; you can redistribute it and/or modify it */
  5. /* under the terms of the GNU General Public License as published by the */
  6. /* Free Software Foundation; either version 2 of the License, or (at your */
  7. /* option) any later version. */
  8. /* */
  9. /* This program is distributed in the hope that it will be useful, but WITHOUT */
  10. /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
  11. /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
  12. /* more details. */
  13. /* */
  14. /* You should have received a copy of the GNU General Public License along */
  15. /* with This program; see the file COPYING. If not,write to the Free Software */
  16. /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /*******************************************************************************/
  18. /* This is the Timeline widget, which contains all the tracks and
  19. * provides cursor overlays, scrolling, zooming, measure lines, tempo
  20. * map and just about everything else. */
  21. #include <FL/Fl_Scroll.H>
  22. #include <FL/Fl_Pack.H>
  23. #include <FL/Fl_Scrollbar.H>
  24. #include <FL/Fl_Widget.H>
  25. #include <FL/fl_draw.H>
  26. #include <FL/Fl_Menu_Button.H>
  27. #include "Timeline.H"
  28. #include "Tempo_Sequence.H"
  29. #include "Time_Sequence.H"
  30. #include "Audio_Sequence.H"
  31. #include "Control_Sequence.H"
  32. #include "Scalebar.H"
  33. #include "Sequence.H"
  34. #include "Annotation_Sequence.H"
  35. #include "Track.H"
  36. #include "Transport.H"
  37. #include "Engine/Engine.H" // for lock()
  38. #include "FL/menu_popup.H"
  39. #include "const.h"
  40. #include "debug.h"
  41. /* these headers are just for the NSM support */
  42. #include "Project.H"
  43. #include "TLE.H"
  44. /* */
  45. #include "NSM.H"
  46. extern NSM_Client *nsm;
  47. #ifdef USE_WIDGET_FOR_TIMELINE
  48. #define BASE Fl_Group
  49. #define redraw_overlay()
  50. #define BX x()
  51. #define BY y()
  52. #else
  53. #ifdef USE_SINGLEBUFFERED_TIMELINE
  54. #warning Using singlebuffered timeline window. This may cause flicker and makes the cursors invisible.
  55. #define BASE Fl_Single_Window
  56. #define redraw_overlay()
  57. #else
  58. #define BASE Fl_Overlay_Window
  59. #endif
  60. #define BX 0
  61. #define BY 0
  62. #endif
  63. bool Timeline::draw_with_measure_lines = true;
  64. Timeline::snap_e Timeline::snap_to = Bars;
  65. bool Timeline::snapping_on_hold = false;
  66. bool Timeline::snap_magnetic = true;
  67. bool Timeline::follow_playhead = true;
  68. bool Timeline::center_playhead = true;
  69. const float UPDATE_FREQ = 0.02f;
  70. extern const char *instance_name;
  71. extern TLE *tle;
  72. /** return the combined height of all visible children of (veritcal)
  73. pack, /p/. This is necessary because pack sizes are adjusted only
  74. when the relevant areas are exposes. */
  75. static int
  76. pack_visible_height ( const Fl_Pack *p )
  77. {
  78. int th = 0;
  79. const Fl_Widget* const *w = p->array();
  80. for ( int i = p->children(); i--; ++w )
  81. if ( (*w)->visible() )
  82. th += (*w)->h() + p->spacing();
  83. return th;
  84. }
  85. #define BP fl_begin_polygon()
  86. #define EP fl_end_polygon()
  87. #define vv(x,y) fl_vertex( x, y )
  88. #define BL fl_begin_line()
  89. #define EL fl_end_line()
  90. static void
  91. draw_full_arrow_symbol ( Fl_Color color )
  92. {
  93. /* draw cap */
  94. fl_color( color );
  95. BP;
  96. vv( -1, -1 );
  97. vv( 0, 1 );
  98. vv( 1, -1 );
  99. EP;
  100. /* draw cap outline */
  101. fl_color( FL_BLACK );
  102. BL;
  103. vv( -1, -1 );
  104. vv( 0, 1 );
  105. vv( 1, -1 );
  106. EL;
  107. }
  108. /** callback used by Loggable class to create a snapshot of system
  109. * state. */
  110. void
  111. Timeline::snapshot ( void )
  112. {
  113. tempo_track->log_children();
  114. time_track->log_children();
  115. for ( int i = 0; i < tracks->children(); ++i )
  116. {
  117. ((Track*)tracks->child( i ))->log_children();
  118. }
  119. }
  120. /** recalculate the size of vertical scrolling area and inform scrollbar */
  121. void
  122. Timeline::adjust_vscroll ( void )
  123. {
  124. vscroll->value( _yposition, h() - rulers->h() - hscroll->h(), 0, pack_visible_height( tracks ) );
  125. }
  126. /** recalculate the size of horizontal scrolling area and inform scrollbar */
  127. void
  128. Timeline::adjust_hscroll ( void )
  129. {
  130. hscroll->value( ts_to_x( xoffset ), tracks->w() - Track::width(), 0, ts_to_x( length() ) );
  131. }
  132. void
  133. Timeline::cb_scroll ( Fl_Widget *w, void *v )
  134. {
  135. ((Timeline*)v)->cb_scroll( w );
  136. }
  137. void
  138. Timeline::cb_scroll ( Fl_Widget *w )
  139. {
  140. if ( w == vscroll )
  141. {
  142. tracks->position( tracks->x(), (rulers->y() + rulers->h()) - vscroll->value() );
  143. yposition( vscroll->value() );
  144. adjust_vscroll();
  145. }
  146. else
  147. {
  148. if ( hscroll->zoom_changed() )
  149. {
  150. nframes_t under_mouse = x_to_offset( Fl::event_x() );
  151. _fpp = hscroll->zoom();
  152. const int tw = tracks->w() - Track::width();
  153. // hscroll->value( ts_to_x( xoffset ), tw, 0, ts_to_x( length() ) );
  154. hscroll->value( max( 0, ts_to_x( under_mouse ) - ( Fl::event_x() - tracks->x() - Track::width() ) ),
  155. tw, 0, ts_to_x( length() ) );
  156. redraw();
  157. }
  158. xposition( hscroll->value() );
  159. }
  160. }
  161. void
  162. Timeline::menu_cb ( Fl_Widget *w, void *v )
  163. {
  164. ((Timeline*)v)->menu_cb( (Fl_Menu_*)w );
  165. }
  166. /** ensure that p1 is less than p2 */
  167. void
  168. Timeline::fix_range ( void )
  169. {
  170. if ( p1 > p2 )
  171. {
  172. nframes_t t = p2;
  173. p2 = p1;
  174. p1 = t;
  175. }
  176. }
  177. /** set the range to /start/ + /length/ */
  178. void
  179. Timeline::range ( nframes_t start, nframes_t length )
  180. {
  181. p1 = start;
  182. p2 = start + length;
  183. redraw();
  184. }
  185. void
  186. Timeline::menu_cb ( Fl_Menu_ *m )
  187. {
  188. if ( ! active_r() )
  189. return;
  190. const char *picked = m->mvalue()->label();
  191. /* m->item_pathname( picked, sizeof( picked ) ); */
  192. DMESSAGE( "%s", picked );
  193. if ( ! strcmp( picked, "Add Audio Track" ) )
  194. {
  195. /* FIXME: prompt for I/O config? */
  196. Loggable::block_start();
  197. /* add audio track */
  198. char *name = get_unique_track_name( "Audio" );
  199. Track *t = new Track( name );
  200. Audio_Sequence *o = new Audio_Sequence( t );
  201. add_track( t );
  202. t->sequence( o );
  203. t->take_focus();
  204. Loggable::block_end();
  205. }
  206. else if ( ! strcmp( picked, "Tempo from range (beat)" ) )
  207. {
  208. if ( p1 != p2 )
  209. {
  210. fix_range();
  211. beats_per_minute( p1, sample_rate() * 60 / (float)( p2 - p1 ) );
  212. p2 = p1;
  213. }
  214. }
  215. else if ( ! strcmp( picked, "Tempo from range (bar)" ) )
  216. {
  217. if ( p1 != p2 )
  218. {
  219. fix_range();
  220. position_info pi = solve_tempomap( p1 );
  221. beats_per_minute( p1, sample_rate() * 60 / (float)( ( p2 - p1 ) / pi.beats_per_bar ) );
  222. p2 = p1;
  223. }
  224. }
  225. else if ( ! strcmp( picked, "Playhead to mouse" ) )
  226. {
  227. int X = Fl::event_x() - Track::width();
  228. if ( X > 0 )
  229. {
  230. transport->locate( xoffset + x_to_ts( X ) );
  231. }
  232. }
  233. else if ( ! strcmp( picked, "P1 to mouse" ) )
  234. {
  235. int X = Fl::event_x() - Track::width();
  236. if ( X > 0 )
  237. {
  238. p1 = xoffset + x_to_ts( X );
  239. }
  240. /* FIXME: only needs to damage the location of the old cursor! */
  241. redraw();
  242. }
  243. else if ( ! strcmp( picked, "P2 to mouse" ) )
  244. {
  245. int X = Fl::event_x() - Track::width();
  246. if ( X > 0 )
  247. {
  248. p2 = xoffset + x_to_ts( X );
  249. }
  250. /* FIXME: only needs to damage the location of the old cursor! */
  251. redraw();
  252. }
  253. else if ( ! strcmp( picked, "Playhead left beat" ) )
  254. {
  255. nframes_t f = transport->frame;
  256. if ( prev_line( &f ) )
  257. transport->locate( f );
  258. }
  259. else if ( ! strcmp( picked, "Playhead right beat" ) )
  260. {
  261. nframes_t f = transport->frame;
  262. if ( next_line( &f ) )
  263. transport->locate( f );
  264. }
  265. else if ( ! strcmp( picked, "Playhead left bar" ) )
  266. {
  267. nframes_t f = transport->frame;
  268. if ( prev_line( &f, true ) )
  269. transport->locate( f );
  270. }
  271. else if ( ! strcmp( picked, "Playhead right bar" ) )
  272. {
  273. nframes_t f = transport->frame;
  274. if ( next_line( &f, true ) )
  275. transport->locate( f );
  276. }
  277. else if ( ! strcmp( picked, "Swap P1 and playhead" ) )
  278. {
  279. nframes_t t = transport->frame;
  280. transport->locate( p1 );
  281. p1 = t;
  282. redraw();
  283. }
  284. else if ( ! strcmp( picked, "Swap P2 and playhead" ) )
  285. {
  286. nframes_t t = transport->frame;
  287. transport->locate( p2 );
  288. p2 = t;
  289. redraw();
  290. }
  291. else if ( ! strcmp( picked, "P1 to playhead" ) )
  292. {
  293. p1 = transport->frame;
  294. redraw();
  295. }
  296. else if ( ! strcmp( picked, "P2 to playhead" ) )
  297. {
  298. p2 = transport->frame;
  299. redraw();
  300. }
  301. else
  302. WARNING( "programming error: Unknown menu item" );
  303. }
  304. int
  305. Timeline::ntracks ( void ) const
  306. {
  307. return tracks->children();
  308. }
  309. Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : BASE( X, Y, W, H, L )
  310. {
  311. Loggable::snapshot_callback( &Timeline::snapshot, this );
  312. _sample_rate = 0;
  313. box( FL_FLAT_BOX );
  314. xoffset = 0;
  315. _yposition = 0;
  316. _old_yposition = 0;
  317. _old_xposition = 0;
  318. #ifndef USE_WIDGET_FOR_TIMELINE
  319. X = Y = 0;
  320. #endif
  321. p1 = p2 = 0;
  322. menu = new Fl_Menu_Button( 0, 0, 0, 0, "Timeline" );
  323. /* menu->add( "Add Track", 0, 0, 0 ); */
  324. menu->add( "Add Audio Track", 'a', 0, 0 );
  325. menu->add( "Tempo from range (beat)", 't', 0, 0 );
  326. menu->add( "Tempo from range (bar)", FL_CTRL + 't', 0, 0 );
  327. menu->add( "Playhead to mouse", 'p', 0, 0 );
  328. menu->add( "P1 to mouse", '[', 0, 0 );
  329. menu->add( "P2 to mouse", ']', 0, 0 );
  330. menu->add( "Playhead left beat", FL_SHIFT + FL_Left, 0, 0 );
  331. menu->add( "Playhead right beat", FL_SHIFT + FL_Right, 0, 0 );
  332. menu->add( "Playhead left bar", FL_CTRL + FL_SHIFT + FL_Left, 0, 0 );
  333. menu->add( "Playhead right bar", FL_CTRL + FL_SHIFT + FL_Right, 0, 0 );
  334. menu->add( "Swap P1 and playhead", FL_CTRL + FL_SHIFT + '[', 0, 0 );
  335. menu->add( "Swap P2 and playhead", FL_CTRL + FL_SHIFT + ']', 0, 0 );
  336. menu->add( "P1 to playhead", FL_CTRL + '[', 0, 0 );
  337. menu->add( "P2 to playhead", FL_CTRL + ']', 0, 0 );
  338. menu_set_callback( const_cast<Fl_Menu_Item*>(menu->menu()), &Timeline::menu_cb, (void*)this );
  339. {
  340. Scalebar *o = new Scalebar( X, Y + H - 18, W - 18, 18 );
  341. o->range( 0, 48000 * 300 );
  342. // o->zoom_range( 1, 16384 );
  343. // o->zoom_range( 1, 65536 << 4 );
  344. o->zoom_range( 1, 20 );
  345. o->zoom( 8 );
  346. o->type( FL_HORIZONTAL );
  347. o->callback( cb_scroll, this );
  348. hscroll = o;
  349. }
  350. {
  351. Fl_Scrollbar *o = new Fl_Scrollbar( X + W - 18, Y, 18, H - 18 );
  352. o->type( FL_VERTICAL );
  353. o->callback( cb_scroll, this );
  354. vscroll = o;
  355. }
  356. {
  357. Fl_Pack *o = new Fl_Pack( X + Track::width(), Y, (W - Track::width()) - vscroll->w(), H - hscroll->h(), "rulers" );
  358. o->type( Fl_Pack::VERTICAL );
  359. {
  360. Tempo_Sequence *o = new Tempo_Sequence( 0, 0, 800, 24 );
  361. o->color( fl_gray_ramp( 18 ) );
  362. o->label( "Tempo" );
  363. o->align( FL_ALIGN_LEFT );
  364. tempo_track = o;
  365. }
  366. {
  367. Time_Sequence *o = new Time_Sequence( 0, 24, 800, 24 );
  368. o->color( fl_gray_ramp( 16 ) );
  369. o->label( "Time" );
  370. o->align( FL_ALIGN_LEFT );
  371. time_track = o;
  372. }
  373. /* { */
  374. /* Annotation_Sequence *o = new Annotation_Sequence( 0, 24, 800, 24 ); */
  375. /* o->color( fl_gray_ramp( 'F' ) ); */
  376. /* o->label( "Ruler" ); */
  377. /* o->align( FL_ALIGN_LEFT ); */
  378. /* ruler_track = o; */
  379. /* } */
  380. o->size( o->w(), o->child( 0 )->h() * o->children() );
  381. rulers = o;
  382. o->end();
  383. }
  384. {
  385. // sample_rate() = engine->sample_rate();
  386. _fpp = 8;
  387. // length() = sample_rate() * 60 * 2;
  388. /* FIXME: hack */
  389. // length() = x_to_ts( W );
  390. {
  391. Fl_Pack *o = new Fl_Pack( X, rulers->y() + rulers->h(), W - vscroll->w(), 1 );
  392. o->type( Fl_Pack::VERTICAL );
  393. o->spacing( 1 );
  394. tracks = o;
  395. o->end();
  396. resizable( o );
  397. }
  398. }
  399. /* rulers go above tracks... */
  400. add( rulers );
  401. /* make sure scrollbars are on top */
  402. add( vscroll );
  403. add( hscroll );
  404. vscroll->range( 0, tracks->h() );
  405. redraw();
  406. end();
  407. Fl::add_timeout( UPDATE_FREQ, update_cb, this );
  408. }
  409. void
  410. Timeline::beats_per_minute ( nframes_t when, float bpm )
  411. {
  412. tempo_track->add( new Tempo_Point( when, bpm ) );
  413. }
  414. void
  415. Timeline::time ( nframes_t when, int bpb, int note_type )
  416. {
  417. time_track->add( new Time_Point( when, bpb, note_type ) );
  418. }
  419. /************/
  420. /* Snapping */
  421. /************/
  422. struct nearest_line_arg
  423. {
  424. nframes_t original;
  425. nframes_t closest;
  426. bool bar;
  427. };
  428. const int snap_pixel = 10;
  429. static nframes_t
  430. abs_diff ( nframes_t n1, nframes_t n2 )
  431. {
  432. return n1 > n2 ? n1 - n2 : n2 - n1;
  433. }
  434. static void
  435. nearest_line_snap_cb ( nframes_t frame, const BBT &bbt, void *arg )
  436. {
  437. nearest_line_arg *n = (nearest_line_arg *)arg;
  438. if ( n->bar && bbt.beat )
  439. return;
  440. if ( Timeline::snap_magnetic &&
  441. abs_diff( frame, n->original ) > timeline->x_to_ts( snap_pixel ) )
  442. return;
  443. if ( abs_diff( frame, n->original ) < abs_diff( n->original, n->closest ) )
  444. n->closest = frame;
  445. }
  446. static void
  447. nearest_line_cb ( nframes_t frame, const BBT &bbt, void *arg )
  448. {
  449. nearest_line_arg *n = (nearest_line_arg *)arg;
  450. if ( n->bar && bbt.beat )
  451. return;
  452. if ( abs_diff( frame, n->original ) < abs_diff( n->original, n->closest ) )
  453. n->closest = frame;
  454. }
  455. static void
  456. prev_next_line_cb ( nframes_t frame, const BBT &bbt, void *arg )
  457. {
  458. nearest_line_arg *n = (nearest_line_arg *)arg;
  459. if ( n->bar && bbt.beat )
  460. return;
  461. if ( abs_diff( frame, n->original ) < abs_diff( n->original, n->closest ) )
  462. n->closest = frame;
  463. }
  464. /** Set the value pointed to by /frame/ to the frame number of the of
  465. the nearest measure line to /when/. Returns true if the new value of
  466. *frame is valid, false otherwise. */
  467. bool
  468. Timeline::nearest_line ( nframes_t *frame, bool snap ) const
  469. {
  470. if ( snap && ( snapping_on_hold || None == Timeline::snap_to ) )
  471. return false;
  472. nframes_t when = *frame;
  473. nearest_line_arg n = { when, -1, snap && Timeline::Bars == Timeline::snap_to };
  474. render_tempomap( when > x_to_ts( w() >> 1 ) ? when - x_to_ts( w() >> 1 ) : 0,
  475. when + x_to_ts( w() >> 1 ), snap ? nearest_line_snap_cb : nearest_line_cb, &n );
  476. if ( n.closest == (nframes_t)-1 )
  477. return false;
  478. else
  479. {
  480. *frame = n.closest;
  481. return true;
  482. }
  483. }
  484. /** Set the value pointed to by /frame/ to the frame number of the of
  485. the nearest measure line to *greater than* /when/. Returns true if
  486. the new value of *frame is valid, false otherwise. */
  487. bool
  488. Timeline::next_line ( nframes_t *frame, bool bar ) const
  489. {
  490. nframes_t when = *frame + 1;
  491. nearest_line_arg n = { when, -1, bar };
  492. render_tempomap( when, x_to_ts( w() ), prev_next_line_cb, &n );
  493. if ( n.closest == (nframes_t)-1 )
  494. return false;
  495. else
  496. {
  497. *frame = n.closest;
  498. return true;
  499. }
  500. }
  501. /** Set the value pointed to by /frame/ to the frame number of the of
  502. the nearest measure line to *less than* /when/. Returns true if
  503. the new value of *frame is valid, false otherwise. */
  504. bool
  505. Timeline::prev_line ( nframes_t *frame, bool bar ) const
  506. {
  507. nframes_t when = *frame - 1;
  508. nearest_line_arg n = { when, -1, bar };
  509. render_tempomap( xoffset, when - xoffset, prev_next_line_cb, &n );
  510. if ( n.closest == (nframes_t)-1 )
  511. return false;
  512. else
  513. {
  514. *frame = n.closest;
  515. return true;
  516. }
  517. }
  518. /** given screen pixel coordinate /x/ return frame offset into
  519. * timeline, taking into account the current scroll position, widget
  520. * layout, etc. */
  521. nframes_t
  522. Timeline::x_to_offset ( int x ) const
  523. {
  524. return x_to_ts( max( 0, x - Track::width() ) ) + xoffset;
  525. }
  526. /** draws a single measure line */
  527. static void
  528. draw_measure_cb ( nframes_t frame, const BBT &bbt, void *arg )
  529. {
  530. Fl_Color *color = (Fl_Color*)arg;
  531. fl_color( FL_BLACK );
  532. fl_line_style( FL_DASH, 0 );
  533. if ( bbt.beat )
  534. ++color;
  535. fl_color( *color );
  536. const int x = timeline->ts_to_x( frame - timeline->xoffset ) + Track::width();
  537. fl_line( x, 0, x, 5000 );
  538. fl_line_style( FL_SOLID, 0 );
  539. }
  540. /* FIXME: wrong place for this */
  541. const float ticks_per_beat = 1920.0;
  542. /** re-render the unified tempomap based on the current contents of the Time and Tempo sequences */
  543. void
  544. Timeline::update_tempomap ( void )
  545. {
  546. /* FIXME: we need some type of locking! */
  547. _tempomap.clear();
  548. for ( list <Sequence_Widget *>::const_iterator i = time_track->_widgets.begin();
  549. i != time_track->_widgets.end(); ++i )
  550. _tempomap.push_back( *i );
  551. for ( list <Sequence_Widget *>::const_iterator i = tempo_track->_widgets.begin();
  552. i != tempo_track->_widgets.end(); ++i )
  553. _tempomap.push_back( *i );
  554. _tempomap.sort( Sequence_Widget::sort_func );
  555. }
  556. /** return a stucture containing the BBT info which applies at /frame/ */
  557. position_info
  558. Timeline::solve_tempomap ( nframes_t frame ) const
  559. {
  560. return render_tempomap( frame, 0, 0, 0 );
  561. }
  562. /* THREAD: UI and RT */
  563. /** draw appropriate measure lines inside the given bounding box */
  564. position_info
  565. Timeline::render_tempomap( nframes_t start, nframes_t length, measure_line_callback * cb, void *arg ) const
  566. {
  567. const nframes_t end = start + length;
  568. position_info pos;
  569. memset( &pos, 0, sizeof( pos ) );
  570. BBT &bbt = pos.bbt;
  571. /* default values */
  572. pos.beat_type = 4;
  573. pos.beats_per_bar = 4;
  574. pos.tempo = 120.0;
  575. const nframes_t samples_per_minute = sample_rate() * 60;
  576. float bpm = 120.0f;
  577. time_sig sig;
  578. sig.beats_per_bar = 4;
  579. sig.beat_type = 4;
  580. nframes_t f = 0;
  581. nframes_t next = 0;
  582. nframes_t frames_per_beat = samples_per_minute / bpm;
  583. if ( ! _tempomap.size() )
  584. return pos;
  585. for ( list <const Sequence_Widget *>::const_iterator i = _tempomap.begin();
  586. i != _tempomap.end(); ++i )
  587. {
  588. if ( ! strcmp( (*i)->class_name(), "Tempo_Point" ) )
  589. {
  590. const Tempo_Point *p = (Tempo_Point*)(*i);
  591. bpm = p->tempo();
  592. frames_per_beat = samples_per_minute / bpm;
  593. }
  594. else
  595. {
  596. const Time_Point *p = (Time_Point*)(*i);
  597. sig = p->time();
  598. /* Time point resets beat */
  599. bbt.beat = 0;
  600. }
  601. {
  602. list <const Sequence_Widget *>::const_iterator n = i;
  603. ++n;
  604. if ( n == _tempomap.end() )
  605. next = end;
  606. else
  607. // next = min( (*n)->start(), end );
  608. /* points may not always be aligned with beat boundaries, so we must align here */
  609. next = (*n)->start() - ( ( (*n)->start() - (*i)->start() ) % frames_per_beat );
  610. }
  611. for ( ; f < next; ++bbt.beat, f += frames_per_beat )
  612. {
  613. if ( bbt.beat == sig.beats_per_bar )
  614. {
  615. bbt.beat = 0;
  616. ++bbt.bar;
  617. }
  618. if ( f >= start )
  619. {
  620. /* in the zone */
  621. if ( cb )
  622. cb( f, bbt, arg );
  623. }
  624. /* ugliness to avoid failing out at -1 */
  625. if ( end >= frames_per_beat )
  626. {
  627. if ( f >= end - frames_per_beat )
  628. goto done;
  629. }
  630. else if ( f + frames_per_beat >= end )
  631. goto done;
  632. }
  633. }
  634. done:
  635. pos.frame = f;
  636. pos.tempo = bpm;
  637. pos.beats_per_bar = sig.beats_per_bar;
  638. pos.beat_type = sig.beat_type;
  639. assert( f <= end );
  640. assert( end - f <= frames_per_beat );
  641. /* FIXME: this this right? */
  642. const double frames_per_tick = frames_per_beat / ticks_per_beat;
  643. bbt.tick = ( end - f ) / frames_per_tick;
  644. return pos;
  645. }
  646. /** maybe draw appropriate measure lines in rectangle defined by X, Y, W, and H, using color /color/ as a base */
  647. void
  648. Timeline::draw_measure_lines ( int X, int Y, int W, int H, Fl_Color color )
  649. {
  650. if ( ! draw_with_measure_lines )
  651. return;
  652. Fl_Color colors[2];
  653. colors[1] = fl_color_average( FL_BLACK, color, 0.65f );
  654. colors[0] = fl_color_average( FL_RED, colors[1], 0.65f );
  655. const nframes_t start = x_to_offset( X );
  656. const nframes_t length = x_to_ts( W );
  657. fl_push_clip( X, Y, W, H );
  658. render_tempomap( start, length, draw_measure_cb, &colors );
  659. fl_pop_clip();
  660. }
  661. void
  662. Timeline::draw_clip ( void * v, int X, int Y, int W, int H )
  663. {
  664. Timeline *tl = (Timeline *)v;
  665. fl_push_clip( X, Y, W, H );
  666. /* fl_color( rand() ); */
  667. /* fl_rectf( X, Y, X + W, Y + H ); */
  668. tl->draw_box();
  669. tl->draw_child( *tl->rulers );
  670. fl_push_clip( tl->tracks->x(), tl->rulers->y() + tl->rulers->h(), tl->tracks->w(), tl->h() - tl->rulers->h() - tl->hscroll->h() );
  671. tl->draw_child( *tl->tracks );
  672. fl_pop_clip();
  673. fl_pop_clip();
  674. }
  675. /** handle resize event */
  676. void
  677. Timeline::resize ( int X, int Y, int W, int H )
  678. {
  679. BASE::resize( X, Y, W, H );
  680. /* why is this necessary? */
  681. rulers->resize( BX + Track::width(), BY, W - Track::width() - vscroll->w(), rulers->h() );
  682. /* why is THIS necessary? */
  683. hscroll->resize( BX, BY + H - 18, hscroll->w(), 18 );
  684. vscroll->size( vscroll->w(), H - 18 );
  685. tracks->resize( BX, BY + rulers->h(), W - vscroll->w(), H - vscroll->h() );
  686. }
  687. /** draw ancillary cursors (not necessarily in the overlay plane) */
  688. void
  689. Timeline::draw_cursors ( void ) const
  690. {
  691. if ( p1 != p2 )
  692. {
  693. draw_cursor( p1, FL_BLUE, draw_full_arrow_symbol );
  694. draw_cursor( p2, FL_GREEN, draw_full_arrow_symbol );
  695. }
  696. }
  697. void
  698. Timeline::draw ( void )
  699. {
  700. int X, Y, W, H;
  701. int bdx = 0;
  702. int bdw = 0;
  703. X = tracks->x() + bdx + 1;
  704. Y = tracks->y();
  705. W = tracks->w() - bdw - 1;
  706. H = tracks->h();
  707. adjust_vscroll();
  708. #ifndef USE_UNOPTIMIZED_DRAWING
  709. if ( ( damage() & FL_DAMAGE_ALL ) || ( damage() & FL_DAMAGE_EXPOSE ) )
  710. #else
  711. #warning Optimized drawing of timeline disabled. This will waste your CPU.
  712. #endif
  713. {
  714. DMESSAGE( "complete redraw" );
  715. draw_box( box(), BX, BY, w(), h(), color() );
  716. fl_push_clip( BX, rulers->y(), w(), rulers->h() );
  717. draw_child( *rulers );
  718. fl_pop_clip();
  719. fl_push_clip( tracks->x(), rulers->y() + rulers->h(), tracks->w(), hscroll->y() - (rulers->y() + rulers->h()) );
  720. draw_child( *tracks );
  721. fl_pop_clip();
  722. draw_child( *hscroll );
  723. draw_child( *vscroll );
  724. draw_cursors();
  725. redraw_overlay();
  726. goto done;
  727. }
  728. if ( damage() & FL_DAMAGE_SCROLL )
  729. {
  730. int dx = ts_to_x( _old_xposition ) - ts_to_x( xoffset );
  731. int dy = _old_yposition - _yposition;
  732. /* draw_child( *rulers ); */
  733. if ( ! dy )
  734. fl_scroll( rulers->x(), rulers->y(), rulers->w(), rulers->h(), dx, 0, draw_clip, this );
  735. Y = rulers->y() + rulers->h();
  736. H = h() - rulers->h() - hscroll->h();
  737. if ( dy == 0 )
  738. fl_scroll( X + Track::width(), Y, W - Track::width(), H, dx, dy, draw_clip, this );
  739. else
  740. fl_scroll( X, Y, W, H, dx, dy, draw_clip, this );
  741. }
  742. if ( damage() & FL_DAMAGE_CHILD )
  743. {
  744. fl_push_clip( rulers->x(), rulers->y(), rulers->w(), rulers->h() );
  745. update_child( *rulers );
  746. fl_pop_clip();
  747. fl_push_clip( tracks->x(), rulers->y() + rulers->h(), tracks->w(), h() - rulers->h() - hscroll->h() );
  748. update_child( *tracks );
  749. fl_pop_clip();
  750. update_child( *hscroll );
  751. update_child( *vscroll );
  752. draw_cursors();
  753. }
  754. done:
  755. _old_xposition = xoffset;
  756. _old_yposition = _yposition;
  757. }
  758. /** draw a single cursor line at /frame/ with color /color/ using symbol routine /symbol/ for the cap */
  759. void
  760. Timeline::draw_cursor ( nframes_t frame, Fl_Color color, void (*symbol)(Fl_Color) ) const
  761. {
  762. // int x = ( ts_to_x( frame ) - ts_to_x( xoffset ) ) + tracks->x() + Track::width();
  763. if ( frame < xoffset )
  764. return;
  765. const int x = ts_to_x( frame - xoffset ) + tracks->x() + Track::width();
  766. if ( x > tracks->x() + tracks->w() )
  767. return;
  768. fl_color( color );
  769. const int y = rulers->y() + rulers->h();
  770. const int h = this->h() - hscroll->h() - 1;
  771. fl_push_clip( tracks->x() + Track::width(), y, tracks->w(), h );
  772. fl_line( x, y, x, h );
  773. fl_color( fl_darker( color ) );
  774. fl_line( x - 1, y, x - 1, h );
  775. fl_color( FL_BLACK );
  776. fl_line( x + 1, y, x + 1, h );
  777. fl_push_matrix();
  778. fl_translate( x, y );
  779. fl_scale( 16, 8 );
  780. symbol( color );
  781. fl_pop_matrix();
  782. fl_pop_clip();
  783. }
  784. void
  785. Timeline::draw_playhead ( void )
  786. {
  787. draw_cursor( transport->frame, FL_RED, draw_full_arrow_symbol );
  788. // draw_cursor( length(), FL_BLACK, draw_full_arrow_symbol );
  789. }
  790. void
  791. Timeline::redraw_playhead ( void )
  792. {
  793. static nframes_t last_playhead = -1;
  794. if ( last_playhead != transport->frame )
  795. {
  796. redraw_overlay();
  797. last_playhead = transport->frame;
  798. if ( follow_playhead )
  799. {
  800. if ( center_playhead && active() )
  801. xposition( max( 0, ts_to_x( transport->frame ) - ( ( tracks->w() - Track::width() ) >> 1 ) ) );
  802. else if ( ts_to_x( transport->frame ) > ts_to_x( xoffset ) + ( tracks->w() - Track::width() ) )
  803. xposition( ts_to_x( transport->frame ) );
  804. }
  805. }
  806. }
  807. /** called so many times a second to redraw the playhead etc. */
  808. void
  809. Timeline::update_cb ( void *arg )
  810. {
  811. Fl::repeat_timeout( UPDATE_FREQ, update_cb, arg );
  812. Timeline *tl = (Timeline *)arg;
  813. tl->redraw_playhead();
  814. }
  815. /** draw cursors in overlay plane */
  816. void
  817. Timeline::draw_overlay ( void )
  818. {
  819. draw_playhead();
  820. if ( ! ( _selection.w && _selection.h ) )
  821. return;
  822. fl_push_clip( tracks->x() + Track::width(), rulers->y() + rulers->h(), tracks->w() - Track::width(), h() - rulers->h() - hscroll->h() );
  823. const Rectangle &r = _selection;
  824. fl_color( FL_BLACK );
  825. fl_line_style( FL_SOLID, 2 );
  826. fl_rect( r.x + 2, r.y + 2, r.w, r.h );
  827. fl_color( FL_MAGENTA );
  828. fl_line_style( FL_DASH, 2 );
  829. fl_rect( r.x, r.y, r.w, r.h );
  830. fl_line( r.x, r.y, r.x + r.w, r.y + r.h );
  831. fl_line( r.x + r.w, r.y, r.x, r.y + r.h );
  832. fl_line_style( FL_SOLID, 0 );
  833. fl_pop_clip();
  834. }
  835. /** select sequence widgets within rectangle /r/ */
  836. void
  837. Timeline::select ( const Rectangle &r )
  838. {
  839. const int Y = r.y;
  840. for ( int i = tracks->children(); i-- ; )
  841. {
  842. Track *t = (Track*)tracks->child( i );
  843. if ( ! ( t->y() > Y + r.h || t->y() + t->h() < Y ) )
  844. t->select( r.x, r.y, r.w, r.h, true, true );
  845. }
  846. }
  847. /** delete all selected sequence widgets */
  848. void
  849. Timeline::delete_selected ( void )
  850. {
  851. Sequence_Widget::delete_selected();
  852. }
  853. /** clear the selection of seqeunce widgets */
  854. void
  855. Timeline::select_none ( void )
  856. {
  857. Sequence_Widget::select_none();
  858. }
  859. int
  860. Timeline::nselected ( void ) const
  861. {
  862. return Sequence_Widget::nselected();
  863. }
  864. /** An unfortunate necessity for implementing our own DND aside from
  865. * the (bogus) native FLTK system */
  866. Track *
  867. Timeline::track_under ( int Y )
  868. {
  869. for ( int i = tracks->children(); i-- ; )
  870. {
  871. Track *t = (Track*)tracks->child( i );
  872. if ( ! ( t->y() > Y || t->y() + t->h() < Y ) )
  873. return t;
  874. }
  875. return NULL;
  876. }
  877. #include "FL/event_name.H"
  878. #include "FL/test_press.H"
  879. /** a bit of a hack to keep FLTK's focus navigation stuff from
  880. * stealing the arrow keys from us */
  881. int
  882. Timeline::handle_scroll ( int m )
  883. {
  884. if ( m == FL_KEYBOARD &&
  885. Fl::event_key() != FL_Home &&
  886. Fl::event_key() != FL_End )
  887. return menu->test_shortcut() || hscroll->handle( m ) || vscroll->handle( m );
  888. else
  889. return 0;
  890. }
  891. int
  892. Timeline::handle ( int m )
  893. {
  894. static Drag *drag = NULL;
  895. static bool range = false;
  896. /* if ( m != FL_NO_EVENT ) */
  897. /* DMESSAGE( "%s", event_name( m ) ); */
  898. /* int r = BASE::handle( m ); */
  899. switch ( m )
  900. {
  901. case FL_ENTER:
  902. return 1;
  903. case FL_LEAVE:
  904. return 1;
  905. case FL_KEYDOWN:
  906. if ( Fl::event_state() & ( FL_ALT | FL_CTRL | FL_SHIFT ) )
  907. /* we don't want any keys with modifiers... */
  908. return 0;
  909. if ( Fl::event_key() == 'r' )
  910. {
  911. range = true;
  912. return 1;
  913. }
  914. else if ( Fl::event_key() == 's' )
  915. {
  916. snapping_on_hold = true;
  917. return 1;
  918. }
  919. return 0;
  920. case FL_KEYUP:
  921. if ( Fl::event_state() & ( FL_ALT | FL_CTRL | FL_SHIFT ) )
  922. /* we don't want any keys with modifiers... */
  923. return 0;
  924. if ( Fl::event_key() == 'r' )
  925. {
  926. range = false;
  927. return 1;
  928. }
  929. else if ( Fl::event_key() == 's' )
  930. {
  931. snapping_on_hold = false;
  932. return 1;
  933. }
  934. return 0;
  935. // case FL_KEYBOARD:
  936. case FL_SHORTCUT:
  937. {
  938. if ( Fl::event_state() & ( FL_ALT | FL_CTRL | FL_SHIFT ) )
  939. /* we don't want any keys with modifiers... */
  940. return 0;
  941. switch ( Fl::event_key() )
  942. {
  943. case FL_Delete:
  944. case FL_Home:
  945. case FL_End:
  946. /* keep scrollbar from eating these. */
  947. return 0;
  948. default:
  949. return BASE::handle( m );
  950. }
  951. return 0;
  952. }
  953. default:
  954. {
  955. int r = BASE::handle( m );
  956. if ( m != FL_RELEASE && r )
  957. return r;
  958. const int X = Fl::event_x();
  959. const int Y = Fl::event_y();
  960. switch ( m )
  961. {
  962. case FL_PUSH:
  963. {
  964. if ( test_press( FL_BUTTON1 ) || test_press( FL_BUTTON1 + FL_CTRL ) )
  965. {
  966. assert( ! drag );
  967. drag = new Drag( X, Y );
  968. _selection.x = X;
  969. _selection.y = Y;
  970. if ( ! Fl::event_ctrl() )
  971. select_none();
  972. return 1;
  973. }
  974. else if ( test_press( FL_BUTTON3 ) )
  975. {
  976. menu_popup( menu );
  977. return 1;
  978. }
  979. return 0;
  980. }
  981. case FL_DRAG:
  982. {
  983. int ox = X - drag->x;
  984. int oy = Y - drag->y;
  985. if ( ox < 0 )
  986. _selection.x = X;
  987. if ( oy < 0 )
  988. _selection.y = Y;
  989. _selection.w = abs( ox );
  990. _selection.h = abs( oy );
  991. if ( range )
  992. {
  993. p1 = x_to_offset( _selection.x );
  994. p2 = x_to_offset( _selection.x + _selection.w );
  995. redraw();
  996. }
  997. redraw_overlay();
  998. return 1;
  999. break;
  1000. }
  1001. case FL_RELEASE:
  1002. {
  1003. delete drag;
  1004. drag = NULL;
  1005. if ( range )
  1006. {
  1007. p1 = x_to_offset( _selection.x );
  1008. p2 = x_to_offset( _selection.x + _selection.w );
  1009. redraw();
  1010. }
  1011. else
  1012. select( _selection );
  1013. _selection.x = _selection.y =_selection.w = _selection.h = 0;
  1014. redraw_overlay();
  1015. return 1;
  1016. }
  1017. default:
  1018. return 0;
  1019. break;
  1020. }
  1021. return 0;
  1022. }
  1023. }
  1024. }
  1025. /** retrun a pointer to the track named /name/, or NULL if no track is named /name/ */
  1026. Track *
  1027. Timeline::track_by_name ( const char *name )
  1028. {
  1029. for ( int i = tracks->children(); i-- ; )
  1030. {
  1031. Track *t = (Track*)tracks->child( i );
  1032. if ( ! strcmp( name, t->name() ) )
  1033. return t;
  1034. }
  1035. return NULL;
  1036. }
  1037. /** return a malloc'd string representing a unique name for a new track */
  1038. char *
  1039. Timeline::get_unique_track_name ( const char *name )
  1040. {
  1041. char pat[256];
  1042. strcpy( pat, name );
  1043. for ( int i = 1; track_by_name( pat ); ++i )
  1044. snprintf( pat, sizeof( pat ), "%s.%d", name, i );
  1045. return strdup( pat );
  1046. }
  1047. /**********/
  1048. /* Public */
  1049. /**********/
  1050. /** return the current length of the timeline, which is arrived at by
  1051. * calculating the end frame of the rightmost audio region on an
  1052. * active audio sequence. Control_Points, etc. do not factor into this
  1053. * calcaulation. */
  1054. nframes_t
  1055. Timeline::length ( void ) const
  1056. {
  1057. nframes_t l = 0;
  1058. for ( int i = tracks->children(); i--; )
  1059. l = max( l, ((Track*)tracks->child( i ))->sequence()->length() );
  1060. // adjust_hscroll();
  1061. return l;
  1062. }
  1063. /** set horizontal scroll postion to absolute pixel coordinate /X/ */
  1064. void
  1065. Timeline::xposition ( int X )
  1066. {
  1067. xoffset = x_to_ts( X );
  1068. damage( FL_DAMAGE_SCROLL );
  1069. }
  1070. /** set vertical scroll position to absolute pixel coordinate /Y/ */
  1071. void
  1072. Timeline::yposition ( int Y )
  1073. {
  1074. _yposition = Y;
  1075. damage( FL_DAMAGE_SCROLL );
  1076. }
  1077. /** zoom in by one zoom step */
  1078. void
  1079. Timeline::zoom_in ( void )
  1080. {
  1081. hscroll->zoom_in();
  1082. }
  1083. /** zoom out by one zoom step */
  1084. void
  1085. Timeline::zoom_out ( void )
  1086. {
  1087. hscroll->zoom_out();
  1088. }
  1089. /** zoom the display to show /secs/ seconds per screen */
  1090. void
  1091. Timeline::zoom ( float secs )
  1092. {
  1093. const int sw = tracks->w() - Track::width();
  1094. int fpp = (int)((secs * sample_rate()) / sw);
  1095. int p = 0;
  1096. while ( 1 << p < fpp ) p++;
  1097. hscroll->zoom( p );
  1098. redraw();
  1099. }
  1100. /** fit the zoom to the current length of the timeline (subject to nearest power of two) */
  1101. void
  1102. Timeline::zoom_fit ( void )
  1103. {
  1104. xposition( 0 );
  1105. if ( length() )
  1106. zoom( length() / (float)sample_rate() );
  1107. else
  1108. zoom( 60 );
  1109. }
  1110. /** add /track/ to the timeline */
  1111. void
  1112. Timeline::add_track ( Track *track )
  1113. {
  1114. DMESSAGE( "added new track to the timeline" );
  1115. engine->lock();
  1116. tracks->add( track );
  1117. engine->unlock();
  1118. /* FIXME: why is this necessary? doesn't the above add do DAMAGE_CHILD? */
  1119. redraw();
  1120. }
  1121. /** remove /track/ from the timeline */
  1122. void
  1123. Timeline::remove_track ( Track *track )
  1124. {
  1125. DMESSAGE( "removed track from the timeline" );
  1126. engine->lock();
  1127. /* FIXME: what to do about track contents? */
  1128. tracks->remove( track );
  1129. engine->unlock();
  1130. /* FIXME: why is this necessary? doesn't the above add do DAMAGE_CHILD? */
  1131. redraw();
  1132. }
  1133. /************/
  1134. /* Commands */
  1135. /************/
  1136. void
  1137. Timeline::command_quit ( )
  1138. {
  1139. Project::close();
  1140. command_save();
  1141. while ( Fl::first_window() ) Fl::first_window()->hide();
  1142. }
  1143. bool
  1144. Timeline::command_load ( const char *name, const char *display_name )
  1145. {
  1146. if ( ! name )
  1147. return false;
  1148. int r = Project::open( name );
  1149. if ( r < 0 )
  1150. {
  1151. const char *s = Project::errstr( r );
  1152. fl_alert( "Could not open project \"%s\":\n\n\t%s", name, s );
  1153. return false;
  1154. }
  1155. Project::set_name ( display_name ? display_name : name );
  1156. return true;
  1157. }
  1158. bool
  1159. Timeline::command_save ( )
  1160. {
  1161. tle->save_options();
  1162. return true;
  1163. }
  1164. bool
  1165. Timeline::command_new ( const char *name, const char *display_name )
  1166. {
  1167. return Project::create( name, NULL );
  1168. Project::set_name ( display_name );
  1169. /* FIXME: there's other stuff that needs to be done here! */
  1170. /* tle->update_menu(); */
  1171. /* tle->main_window->redraw(); */
  1172. }
  1173. const char *
  1174. Timeline::session_manager_name ( void )
  1175. {
  1176. return nsm->session_manager_name();
  1177. }