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.

974 lines
26KB

  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. #include <FL/fl_draw.H>
  19. #include <FL/Fl.H>
  20. #include <FL/Fl_Widget.H>
  21. #include <FL/Fl_Menu_Item.H>
  22. #include <FL/fl_show_colormap.H>
  23. #include "Sequence.H"
  24. #include "Audio_Region.H"
  25. #include "Timeline.H"
  26. #include "Waveform.H"
  27. #include "Audio_Sequence.H"
  28. #include "Track.H"
  29. #include "Engine/Audio_File.H"
  30. #include "Transport.H"
  31. #include "const.h"
  32. #include "debug.h"
  33. #include <FL/focus_frame.H>
  34. #include <algorithm>
  35. using std::min;
  36. using std::max;
  37. /* defined in timeline.C */
  38. extern void draw_full_arrow_symbol ( Fl_Color color );
  39. extern Timeline *timeline;
  40. extern Transport *transport;
  41. bool Audio_Region::inherit_track_color = true;
  42. bool Audio_Region::show_box = true;
  43. Fl_Boxtype Audio_Region::_box = FL_BORDER_BOX;
  44. Fl_Color Audio_Region::_selection_color = FL_MAGENTA;
  45. /* static Fl_Color fl_invert_color ( Fl_Color c ) */
  46. /* { */
  47. /* unsigned char r, g, b; */
  48. /* Fl::get_color( c, r, g, b ); */
  49. /* return fl_rgb_color( 255 - r, 255 - g, 255 - b ); */
  50. /* } */
  51. void
  52. Audio_Region::get ( Log_Entry &e ) const
  53. {
  54. e.add( ":source", _clip ? _clip->name() : "" );
  55. e.add( ":gain", _scale );
  56. e.add( ":fade-in-type", _fade_in.type );
  57. e.add( ":fade-in-duration", _fade_in.length );
  58. e.add( ":fade-out-type", _fade_out.type );
  59. e.add( ":fade-out-duration", _fade_out.length );
  60. Sequence_Region::get( e );
  61. e.add( ":offset", _r->offset );
  62. e.add( ":loop", _loop );
  63. }
  64. void
  65. Audio_Region::set ( Log_Entry &e )
  66. {
  67. for ( int i = 0; i < e.size(); ++i )
  68. {
  69. const char *s, *v;
  70. e.get( i, &s, &v );
  71. if ( ! strcmp( s, ":gain" ) )
  72. _scale = atof( v );
  73. else if ( ! strcmp( s, ":fade-in-type" ) )
  74. _fade_in.type = (Fade::fade_type_e)atoi( v );
  75. else if ( ! strcmp( s, ":fade-in-duration" ) )
  76. _fade_in.length = atoll( v );
  77. else if ( ! strcmp( s, ":fade-out-type" ) )
  78. _fade_out.type = (Fade::fade_type_e)atoi( v );
  79. else if ( ! strcmp( s, ":fade-out-duration" ) )
  80. _fade_out.length = atoll( v );
  81. else if ( ! strcmp( s, ":offset" ) )
  82. _r->offset = atoll( v );
  83. else if ( ! strcmp( s, ":loop" ) )
  84. _loop = atoll( v );
  85. else if ( ! strcmp( s, ":source" ) )
  86. {
  87. if ( ! ( _clip = Audio_File::from_file( v ) ) )
  88. {
  89. printf( "Grave error: could not open source \"%s\"\n", v );
  90. }
  91. }
  92. }
  93. Sequence_Region::set( e );
  94. }
  95. void
  96. Audio_Region::init ( void )
  97. {
  98. _adjusting_gain = 0;
  99. _loop = 0;
  100. _sequence = NULL;
  101. _scale = 1.0f;
  102. _clip = NULL;
  103. _color = FL_FOREGROUND_COLOR;
  104. _box_color = FL_GRAY;
  105. /* FIXME: shouldn't this be a fraction of the sample rate? */
  106. _fade_in.length = 256;
  107. _fade_in.type = Fade::Sigmoid;
  108. _fade_out = _fade_in;
  109. }
  110. /* copy constructor */
  111. Audio_Region::Audio_Region ( const Audio_Region & rhs ) : Sequence_Region( rhs )
  112. {
  113. // *((Sequence_Region*)this) = (Sequence_Region &)rhs;
  114. _clip = rhs._clip->duplicate();
  115. _scale = rhs._scale;
  116. _fade_in = rhs._fade_in;
  117. _fade_out = rhs._fade_out;
  118. _loop = rhs._loop;
  119. _box_color = rhs._box_color;
  120. _color = rhs._color;
  121. _adjusting_gain = 0.0f;
  122. log_create();
  123. }
  124. /* */
  125. Audio_Region::Audio_Region ( Audio_File *c )
  126. {
  127. init();
  128. _clip = c;
  129. _r->length = _clip->length();
  130. log_create();
  131. }
  132. /* used when DND importing and when recording. must not invoke log_create() */
  133. Audio_Region::Audio_Region ( Audio_File *c, Sequence *t, nframes_t o )
  134. {
  135. init();
  136. _clip = c;
  137. _r->offset = 0;
  138. _r->start = o;
  139. _r->length = _clip->length();
  140. int sum = 0;
  141. const char *s = rindex( _clip->name(), '/' );
  142. if ( ! s )
  143. s = _clip->name();
  144. for ( int i = strlen( s ); i--; )
  145. sum += s[ i ];
  146. while ( sum >> 8 )
  147. sum = (sum & 0xFF) + (sum >> 8);
  148. _box_color = (Fl_Color)sum;
  149. t->add( this );
  150. }
  151. Audio_Region::~Audio_Region ( )
  152. {
  153. log_destroy();
  154. _clip->release();
  155. }
  156. void
  157. Audio_Region::menu_cb ( Fl_Widget *w, void *v )
  158. {
  159. ((Audio_Region*)v)->menu_cb( (Fl_Menu_*) w );
  160. }
  161. void
  162. Audio_Region::menu_cb ( const Fl_Menu_ *m )
  163. {
  164. char picked[256];
  165. m->item_pathname( picked, sizeof( picked ) );
  166. if ( ! strcmp( picked, "Fade/In/Linear" ) )
  167. _fade_in.type = Fade::Linear;
  168. else if ( ! strcmp( picked, "Fade/In/Sigmoid" ) )
  169. _fade_in.type = Fade::Sigmoid;
  170. else if ( ! strcmp( picked, "Fade/In/Logarithmic" ) )
  171. _fade_in.type = Fade::Logarithmic;
  172. else if ( ! strcmp( picked, "Fade/In/Parabolic" ) )
  173. _fade_in.type = Fade::Parabolic;
  174. else if ( ! strcmp( picked, "Fade/Out/Linear" ) )
  175. _fade_out.type = Fade::Linear;
  176. else if ( ! strcmp( picked, "Fade/Out/Sigmoid" ) )
  177. _fade_out.type = Fade::Sigmoid;
  178. else if ( ! strcmp( picked, "Fade/Out/Logarithmic" ) )
  179. _fade_out.type = Fade::Logarithmic;
  180. else if ( ! strcmp( picked, "Fade/Out/Parabolic" ) )
  181. _fade_out.type = Fade::Parabolic;
  182. else if ( ! strcmp( picked, "/Color" ) )
  183. box_color( fl_show_colormap( box_color() ) );
  184. else if ( ! strcmp( picked, "/Split at mouse" ) )
  185. {
  186. split( timeline->x_to_offset( Fl::event_x() ) );
  187. }
  188. else if ( ! strcmp( picked, "/Crop to range" ) )
  189. {
  190. trim_left( timeline->range_start() );
  191. trim_right( timeline->range_end() );
  192. }
  193. else if ( ! strcmp( picked, "/Fade in to mouse" ) )
  194. {
  195. nframes_t offset = x_to_offset( Fl::event_x() );
  196. if ( offset < length() )
  197. _fade_in.length = offset;
  198. DMESSAGE( "set fade in duration" );
  199. }
  200. else if ( ! strcmp( picked, "/Fade out to mouse" ) )
  201. {
  202. long offset = length() - x_to_offset( Fl::event_x() );
  203. if ( offset > 0 )
  204. _fade_out.length = offset;
  205. }
  206. else if ( ! strcmp( picked, "/Gain with mouse vertical drag" ) )
  207. {
  208. /* float g = h() / (y() - Fl::event_y() ); */
  209. /* _scale = g; */
  210. }
  211. else if ( ! strcmp( picked, "/Loop point to mouse" ) )
  212. {
  213. nframes_t offset = x_to_offset( Fl::event_x() );
  214. if ( offset > 0 )
  215. {
  216. nframes_t f = offset + _r->start;
  217. if ( timeline->nearest_line( &f, false ) )
  218. _loop = f - _r->start;
  219. else
  220. _loop = offset;
  221. }
  222. }
  223. else if ( ! strcmp( picked, "/Clear loop point" ) )
  224. _loop = 0;
  225. else if ( ! strcmp( picked, "/Normalize" ) )
  226. normalize();
  227. else if ( ! strcmp( picked, "/Denormalize" ) )
  228. _scale = 1.0;
  229. else if ( ! strcmp( picked, "/Range from" ) )
  230. timeline->range( start(), length() );
  231. else if ( ! strcmp( picked, "/Trim left to playhead" ) )
  232. {
  233. redraw();
  234. trim_left( transport->frame );
  235. }
  236. else if ( ! strcmp( picked, "/Trim right to playhead" ) )
  237. {
  238. redraw();
  239. trim_right( transport->frame );
  240. }
  241. else if ( ! strcmp( picked, "/Split at playhead" ) )
  242. {
  243. redraw();
  244. split( transport->frame );
  245. }
  246. else if ( ! strcmp( picked, "/Loop point at playhead" ) )
  247. {
  248. nframes_t f = transport->frame;
  249. _loop = f - _r->start;
  250. }
  251. else if ( ! strcmp( picked, "/Fade in to playhead" ) )
  252. {
  253. nframes_t offset = transport->frame - _r->start;
  254. if ( offset < length() )
  255. _fade_in.length = offset;
  256. }
  257. else if ( ! strcmp( picked, "/Fade out to playhead" ) )
  258. {
  259. nframes_t offset = _r->start + _r->length - transport->frame;
  260. if ( offset > 0 )
  261. _fade_out.length = offset;
  262. }
  263. else if ( ! strcmp( picked, "/Remove" ) )
  264. remove();
  265. else
  266. FATAL( "Unknown menu choice \"%s\"", picked );
  267. redraw();
  268. }
  269. #include "FL/test_press.H"
  270. #include "FL/menu_popup.H"
  271. /** build the context menu for this region */
  272. Fl_Menu_Button &
  273. Audio_Region::menu ( void )
  274. {
  275. static Fl_Menu_Button m( 0, 0, 0, 0, "Region" );
  276. Fade::fade_type_e it = _fade_in.type;
  277. Fade::fade_type_e ot = _fade_out.type;
  278. Fl_Menu_Item items[] =
  279. {
  280. { "Fade", 0, 0, 0, FL_SUBMENU },
  281. { "In", 0, 0, 0, FL_SUBMENU },
  282. { "Linear", 0, 0, 0, FL_MENU_RADIO | ( it == Fade::Linear ? FL_MENU_VALUE : 0 ) },
  283. { "Sigmoid", 0, 0, 0, FL_MENU_RADIO | ( it == Fade::Sigmoid ? FL_MENU_VALUE : 0 ) },
  284. { "Logarithmic", 0, 0, 0, FL_MENU_RADIO | ( it == Fade::Logarithmic ? FL_MENU_VALUE : 0 ) },
  285. { "Parabolic", 0, 0, 0, FL_MENU_RADIO | ( it == Fade::Parabolic ? FL_MENU_VALUE : 0 ) },
  286. { 0 },
  287. { "Out", 0, 0, 0, FL_SUBMENU },
  288. { "Linear", 0, 0, 0, FL_MENU_RADIO | ( ot == Fade::Linear ? FL_MENU_VALUE : 0 ) },
  289. { "Sigmoid", 0, 0, 0, FL_MENU_RADIO | ( ot == Fade::Sigmoid ? FL_MENU_VALUE : 0 ) },
  290. { "Logarithmic", 0, 0, 0, FL_MENU_RADIO | ( ot == Fade::Logarithmic ? FL_MENU_VALUE : 0 ) },
  291. { "Parabolic", 0, 0, 0, FL_MENU_RADIO | ( ot == Fade::Parabolic ? FL_MENU_VALUE : 0 ) },
  292. { 0 },
  293. { 0 },
  294. { "Color", 0, 0, 0, inherit_track_color ? FL_MENU_INACTIVE : 0 },
  295. { "Crop to range", 'c', 0, 0, FL_MENU_DIVIDER },
  296. { "Split at mouse", 's', 0, 0 },
  297. { "Gain with mouse vertical drag", 'g', 0, 0 },
  298. { "Fade in to mouse", FL_F + 3, 0, 0 },
  299. { "Fade out to mouse", FL_F + 4, 0, 0 },
  300. { "Loop point to mouse", 'l', 0, 0 },
  301. { "Clear loop point", 0, 0, 0, 0 == _loop ? FL_MENU_INACTIVE : 0 },
  302. { "Normalize", 'n', 0, 0 },
  303. { "Denormalize", FL_SHIFT + 'n', 0, 0, 1.0 == _scale ? FL_MENU_INACTIVE : 0 },
  304. { "Range from", FL_CTRL + 'r', 0, 0, FL_MENU_DIVIDER },
  305. { "Trim left to playhead", '{', 0, 0 },
  306. { "Trim right to playhead", '}', 0, 0 },
  307. { "Split at playhead", FL_SHIFT + 's', 0, 0 },
  308. { "Loop point at playhead", FL_SHIFT + 'l', 0, 0 },
  309. { "Fade in to playhead", FL_F + 3 + FL_SHIFT, 0, 0 },
  310. { "Fade out to playhead", FL_F + 4 + FL_SHIFT, 0, 0 },
  311. { "Remove", 0, 0, 0 },
  312. { 0 },
  313. };
  314. menu_set_callback( items, &Audio_Region::menu_cb, (void*)this );
  315. m.copy( items, (void*)this );
  316. return m;
  317. }
  318. /** Draws the curve for a single fade. /X/ and /W/ repersent the
  319. portion of the region covered by this draw, which may or may not
  320. cover the fade in question. */
  321. void
  322. Audio_Region::draw_fade ( const Fade &fade, Fade::fade_dir_e dir, bool line, int X, int W )
  323. {
  324. const int dy = y() + Fl::box_dy( box() );
  325. const int dh = h() - Fl::box_dh( box() );
  326. const int height = dh;
  327. const int width = timeline->ts_to_x( fade.length );
  328. if ( width < 4 )
  329. /* too small to draw */
  330. return;
  331. int fx;
  332. if ( dir == Fade::In )
  333. {
  334. fx = curve_x();
  335. if ( fx + width < X ||
  336. fx > X + W )
  337. /* clipped */
  338. return;
  339. }
  340. else
  341. {
  342. fx = curve_x() + abs_w();
  343. if ( fx - width > X + W ||
  344. fx < X )
  345. /* clipped */
  346. return;
  347. }
  348. if ( line )
  349. fl_begin_line();
  350. else
  351. fl_begin_polygon();
  352. fl_vertex( fx, dy );
  353. fl_vertex( fx, dy + height );
  354. {
  355. const double ti = 1.0 / (double)width;
  356. double ts = 0.0;
  357. const int xi = dir == Fade::In ? 1 : -1;
  358. for ( int i = 0; i < width; i++, ts += ti, fx += xi )
  359. fl_vertex( fx, dy + height - ( height * fade.gain( ts )));
  360. }
  361. fl_vertex( fx, dy );
  362. if ( line )
  363. fl_end_line();
  364. else
  365. fl_end_polygon();
  366. }
  367. Fl_Color
  368. Audio_Region::actual_box_color ( void ) const
  369. {
  370. return Audio_Region::inherit_track_color ? sequence()->track()->color() : _box_color;
  371. }
  372. void
  373. Audio_Region::draw_box( void )
  374. {
  375. // fl_push_clip( x(), y(), w(), h() );
  376. Fl_Color selection_color = _selection_color;
  377. Fl_Color color = actual_box_color();
  378. color = fl_color_average( color, sequence()->color(), 0.75f );
  379. if ( recording() )
  380. {
  381. color = FL_RED;
  382. }
  383. else if ( ! active_r() )
  384. {
  385. color = fl_inactive( color );
  386. selection_color = fl_inactive( selection_color );
  387. }
  388. Fl_Boxtype b;
  389. Fl_Color c = selected() ? fl_color_average( color, fl_rgb_color(10,10,10), 0.4f ) : color;
  390. if ( Audio_Region::show_box )
  391. {
  392. b = box();
  393. }
  394. else
  395. {
  396. b = FL_DOWN_FRAME;
  397. }
  398. fl_draw_box( b, x(), y(), w(), h(), c );
  399. // fl_pop_clip();
  400. }
  401. void
  402. Audio_Region::peaks_ready_callback ( void *v )
  403. {
  404. /* this is called from the peak builder thread */
  405. DMESSAGE("Damaging region from peaks ready callback");
  406. Fl::lock();
  407. ((Audio_Region*)v)->redraw();
  408. Fl::unlock();
  409. Fl::awake();
  410. }
  411. bool
  412. Audio_Region::recording ( void ) const
  413. {
  414. return this == sequence()->track()->capture_region();
  415. }
  416. /** Draw (part of) region. X, Y, W and H are the rectangle we're clipped to. */
  417. void
  418. Audio_Region::draw ( void )
  419. {
  420. /* intersect clip with region */
  421. int X, Y, W, H;
  422. fl_clip_box( x(), y(), w(), h(), X, Y, W, H );
  423. if ( ! ( W > 0 && H > 0 ) )
  424. /* no coverage */
  425. return;
  426. if ( start() > timeline->xoffset + timeline->x_to_ts( sequence()->drawable_w() ) ||
  427. start() + length() < timeline->xoffset )
  428. /* not in viewport */
  429. return;
  430. fl_push_clip( X, Y, W, H );
  431. /* overdraw a little to avoid artifacts when scrolling */
  432. W += 2;
  433. // Fl_Color c = selected() ? fl_invert_color( _color ) : _color;
  434. if ( sequence()->damage() & FL_DAMAGE_USER1 &&
  435. recording() )
  436. {
  437. /* TODO: limit drawing. */
  438. }
  439. /* calculate waveform offset due to scrolling */
  440. /* offset is the number of frames into the waveform the value of X translates to */
  441. /* this is the timestamp at where we'll actually be drawing. */
  442. nframes_t x_frame = timeline->x_to_ts(
  443. timeline->ts_to_x( timeline->xoffset ) + ( X - _sequence->drawable_x() ) );
  444. nframes_t offset = 0;
  445. if ( x_frame > start() )
  446. offset = x_frame - start();
  447. nframes_t fo = 0;
  448. nframes_t ostart = 0, oend = 0;
  449. const int total_peaks_needed = W;
  450. nframes_t total_frames_needed = timeline->x_to_ts( total_peaks_needed );
  451. {
  452. /* Fl_Color c = fl_color_average( FL_DARK1, */
  453. /* Audio_Region::inherit_track_color ? sequence()->track()->color() : _box_color, */
  454. /* 0.75f ); */
  455. fl_color( fl_color_add_alpha( FL_DARK1, 127 ) );
  456. draw_fade( _fade_in, Fade::In, false, X, W );
  457. draw_fade( _fade_out, Fade::Out, false, X, W );
  458. }
  459. int channels = 0;
  460. int peaks = 0;
  461. Peak *pbuf = NULL;
  462. Fl_Color fg_color = FL_FOREGROUND_COLOR;
  463. Fl_Color bg_color = FL_BACKGROUND_COLOR;
  464. if ( !active_r() )
  465. {
  466. fg_color = fl_inactive(fg_color);
  467. bg_color = fl_inactive(bg_color);
  468. }
  469. do {
  470. nframes_t loop_frames_needed = _loop ? _loop : total_frames_needed;
  471. int loop_peaks_needed = timeline->ts_to_x( loop_frames_needed );
  472. nframes_t start = _r->offset;
  473. if ( ! fo ) /* first loop... */
  474. {
  475. if ( _loop )
  476. {
  477. // start += offset;
  478. start += offset % _loop;
  479. loop_frames_needed -= offset % loop_frames_needed;
  480. loop_peaks_needed = timeline->ts_to_x( loop_frames_needed );
  481. }
  482. else
  483. start += offset;
  484. assert( loop_peaks_needed >= 0 );
  485. }
  486. if ( fo + loop_frames_needed > total_frames_needed )
  487. {
  488. loop_frames_needed -= ( fo + loop_frames_needed ) - total_frames_needed;
  489. loop_peaks_needed = timeline->ts_to_x( loop_frames_needed );
  490. }
  491. if ( !loop_peaks_needed )
  492. break;
  493. const nframes_t end = start + loop_frames_needed;
  494. if ( start != ostart || end != oend )
  495. {
  496. _clip->peaks()->peakfile_ready();
  497. if ( _clip->read_peaks( timeline->fpp(),
  498. start,
  499. end,
  500. &peaks, &pbuf, &channels ) )
  501. {
  502. Waveform::scale( pbuf, peaks * channels, _scale );
  503. ostart = start;
  504. oend = end;
  505. }
  506. if ( _clip->peaks()->needs_more_peaks() && ! transport->rolling )
  507. {
  508. /* maybe create a thread to make the peaks */
  509. /* this function will just return if there's nothing to do. */
  510. _clip->peaks()->make_peaks_asynchronously( Audio_Region::peaks_ready_callback, this );
  511. }
  512. }
  513. else
  514. {
  515. // DMESSAGE( "using cached peaks" );
  516. }
  517. if ( peaks && pbuf )
  518. {
  519. int ch = (h() - Fl::box_dh( box() )) / channels;
  520. int xo = timeline->ts_to_x( fo );
  521. for ( int i = 0; i < channels; ++i )
  522. {
  523. Waveform::draw( X + xo,
  524. (y() + Fl::box_dy( box() )) + (i * ch),
  525. loop_peaks_needed,
  526. ch,
  527. pbuf + i, peaks, channels,
  528. fg_color, bg_color );
  529. }
  530. }
  531. else
  532. ;
  533. // WARNING( "Pbuf == %p, peaks = %lu", pbuf, (unsigned long)peaks );
  534. if ( _loop )
  535. {
  536. const int lx = sequence()->drawable_x() + timeline->ts_to_x( ( this->start() + _loop ) - timeline->xoffset );
  537. if ( lx < X + W )
  538. {
  539. fl_color( fl_darker( FL_CYAN ) );
  540. fl_line( lx, y(), lx, y() + h() );
  541. fl_line( lx - 3, y(), lx + 3, y() );
  542. fl_line( lx - 3, y() + h() - 1, lx + 3, y() + h() - 1 );
  543. }
  544. }
  545. if ( peaks < loop_peaks_needed )
  546. {
  547. // DMESSAGE( "Peak read came up %lu peaks short", (unsigned long)loop_peaks_needed - peaks );
  548. }
  549. fo += loop_frames_needed;
  550. }
  551. while ( _loop && fo < total_frames_needed );
  552. if ( _adjusting_gain > 0.0f )
  553. {
  554. fl_color( fl_color_add_alpha( FL_DARK1, 127 ) );
  555. fl_rectf( X, ( y() + h() ) - ( h() * ( _scale * 0.25 ) ), X + W, y() + h() );
  556. fl_line_style( FL_DASH, 1 );
  557. fl_color( fl_color_add_alpha( FL_GREEN, 200 ) );
  558. float j = 5;
  559. for ( int i = y() + h(); i > y(); i -= j, j *= 1.2 )
  560. {
  561. fl_line( X, i, X + W, i );
  562. }
  563. fl_line_style( FL_SOLID, 0 );
  564. }
  565. if ( selected() )
  566. draw_selection_frame( line_x() + Fl::box_dx(box()),
  567. y() + Fl::box_dy(box()),
  568. abs_w() - Fl::box_dw(box()),
  569. h() - Fl::box_dh(box()),
  570. selection_color() );
  571. /* if ( current() ) */
  572. /* { */
  573. /* /\* draw length bubble *\/ */
  574. /* char pat[40]; */
  575. /* snprintf( pat, sizeof( pat ), "%dm:%.1fs", (int)(length() / timeline->sample_rate()) / 60, (double)length() / timeline->sample_rate() ); */
  576. /* draw_label( pat, (Fl_Align)(FL_ALIGN_INSIDE | FL_ALIGN_CENTER), FL_GREEN ); */
  577. /* } */
  578. fl_pop_clip();
  579. }
  580. void
  581. Audio_Region::draw_label ( void )
  582. {
  583. if ( _clip->dummy() )
  584. {
  585. char pat[256];
  586. snprintf( pat, sizeof( pat ), "Missing Source!: %s", _clip->name() );
  587. draw_label( pat, align() );
  588. }
  589. else
  590. draw_label( _clip->name(), align() );
  591. }
  592. /** split region at absolute frame /where/ */
  593. void
  594. Audio_Region::split ( nframes_t where )
  595. {
  596. block_start();
  597. nframes_t old_fade_in = _fade_in.length;
  598. _fade_in.length = 256;
  599. Audio_Region *copy = new Audio_Region( *this );
  600. {
  601. Logger _log( copy );
  602. _fade_in.length = old_fade_in;
  603. _fade_out.length = 256;
  604. Sequence_Region::split( copy, where );
  605. }
  606. log_end();
  607. block_end();
  608. log_start();
  609. }
  610. int
  611. Audio_Region::handle ( int m )
  612. {
  613. static int ox;
  614. static bool copied = false;
  615. static nframes_t os;
  616. int X = Fl::event_x();
  617. int Y = Fl::event_y();
  618. Logger _log( this );
  619. if ( ! Fl::pushed() )
  620. // selecting an item from the context menu can leave this value set.
  621. Sequence_Widget::pushed( NULL );
  622. switch ( m )
  623. {
  624. case FL_FOCUS:
  625. case FL_UNFOCUS:
  626. return 1;
  627. case FL_KEYUP:
  628. if ( _adjusting_gain > 0 )
  629. {
  630. _adjusting_gain = 0;
  631. redraw();
  632. return 1;
  633. }
  634. break;
  635. case FL_KEYBOARD:
  636. if ( Fl::event_key() == 'g' )
  637. {
  638. if ( _adjusting_gain <= 0 )
  639. {
  640. _adjusting_gain = _scale;
  641. redraw();
  642. }
  643. return 1;
  644. }
  645. return menu().test_shortcut() != 0;
  646. case FL_ENTER:
  647. return Sequence_Region::handle( m );
  648. case FL_LEAVE:
  649. if ( _adjusting_gain > 0 )
  650. {
  651. _adjusting_gain = 0;
  652. redraw();
  653. }
  654. return Sequence_Region::handle( m );
  655. case FL_PUSH:
  656. {
  657. if ( _adjusting_gain > 0.0f )
  658. {
  659. _adjusting_gain = _scale;
  660. return 1;
  661. }
  662. /* splitting */
  663. if ( test_press( FL_BUTTON2 | FL_SHIFT ) )
  664. {
  665. /* split */
  666. if ( ! copied )
  667. {
  668. split( timeline->x_to_offset( X ) );
  669. }
  670. return 0;
  671. }
  672. else
  673. {
  674. ox = x() - X;
  675. /* for panning */
  676. os = _r->offset;
  677. if ( test_press( FL_BUTTON2 | FL_CTRL ) )
  678. {
  679. normalize();
  680. return 1;
  681. }
  682. else if ( test_press( FL_BUTTON3 ) )
  683. {
  684. /* context menu */
  685. menu_popup( &menu() );
  686. return 1;
  687. }
  688. else
  689. return Sequence_Region::handle( m );
  690. }
  691. break;
  692. }
  693. case FL_RELEASE:
  694. {
  695. Sequence_Region::handle( m );
  696. copied = false;
  697. return 1;
  698. }
  699. case FL_DRAG:
  700. if ( Fl::event_is_click() )
  701. return 1;
  702. if ( ! _drag )
  703. {
  704. begin_drag( Drag( X, Y, x_to_offset( X ) ) );
  705. _log.hold();
  706. }
  707. if ( _adjusting_gain )
  708. {
  709. int d = _drag->y - Y;
  710. _scale = _adjusting_gain + ( 0.01f * d );
  711. if ( _scale < 0.01f )
  712. _scale = 0.01f;
  713. redraw();
  714. return 1;
  715. }
  716. if ( test_press( FL_BUTTON1 | FL_SHIFT | FL_CTRL ) )
  717. {
  718. /* panning */
  719. int d = (ox + X) - x();
  720. if ( d < 0 )
  721. _r->offset = os + timeline->x_to_ts( 0 - d );
  722. else
  723. {
  724. if ( os < timeline->x_to_ts( d ) )
  725. _r->offset = 0;
  726. else
  727. _r->offset = os - timeline->x_to_ts( d );
  728. }
  729. redraw();
  730. return 1;
  731. }
  732. return Sequence_Region::handle( m );
  733. default:
  734. return Sequence_Region::handle( m );
  735. break;
  736. }
  737. return 0;
  738. }
  739. /**********/
  740. /* Public */
  741. /**********/
  742. /** return the name of the audio source this region represents */
  743. const char *
  744. Audio_Region::source_name ( void ) const
  745. {
  746. return _clip->name();
  747. }
  748. /** set the amplitude scaling for this region from the normalization
  749. * factor for the range of samples represented by this region */
  750. void
  751. Audio_Region::normalize ( void )
  752. {
  753. int peaks, channels;
  754. Peak *pbuf;
  755. const nframes_t npeaks = _loop ? _loop : length();
  756. if ( _clip->read_peaks( npeaks, offset(), offset() + npeaks, &peaks, &pbuf, &channels ) &&
  757. peaks )
  758. {
  759. _scale = 1000.0f;
  760. for ( int i = 0; i < channels; i++ )
  761. {
  762. float f = (pbuf + i)->normalization_factor();
  763. if ( f < _scale )
  764. _scale = f;
  765. }
  766. }
  767. /* FIXME: wrong place for this? */
  768. sequence()->handle_widget_change( start(), length() );
  769. redraw();
  770. }