4 * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "automation.h"
30 #include "bcsignals.h"
33 #include "bccmodels.h"
38 #include "edlsession.h"
40 #include "filesystem.h"
41 #include "floatauto.h"
42 #include "floatautos.h"
43 #include "framecache.h"
44 #include "indexfile.h"
46 #include "localsession.h"
48 #include "mwindowgui.h"
49 #include "renderengine.h"
50 #include "resourcethread.h"
51 #include "resourcepixmap.h"
54 #include "timelinepane.h"
56 #include "trackcanvas.h"
57 #include "transportque.h"
60 #include "wavecache.h"
63 ResourcePixmap::ResourcePixmap(MWindow *mwindow,
69 : BC_Pixmap(gui, w, h)
73 this->mwindow = mwindow;
75 this->pane_number = pane_number;
76 startsource = edit->startsource;
77 data_type = edit->track->data_type;
79 source_framerate = edit->asset->frame_rate;
80 source_samplerate = edit->asset->sample_rate;
83 if( edit->nested_edl ) {
84 source_framerate = edit->nested_edl->session->frame_rate;
85 source_samplerate = edit->nested_edl->session->sample_rate;
88 project_framerate = edit->edl->session->frame_rate;
89 project_samplerate = edit->edl->session->sample_rate;
90 edit_id = edit->id; pixmap_w = w; pixmap_h = h;
93 ResourcePixmap::~ResourcePixmap()
98 void ResourcePixmap::reset()
110 void ResourcePixmap::resize(int w, int h)
112 int new_w = (w > get_w()) ? w : get_w();
113 int new_h = (h > get_h()) ? h : get_h();
115 BC_Pixmap::resize(new_w, new_h);
119 void ResourcePixmap::draw_data(TrackCanvas *canvas,
120 Edit *edit, int64_t edit_x, int64_t edit_w,
121 int64_t pixmap_x, int64_t pixmap_w, int64_t pixmap_h,
122 int mode, int indexes_only)
124 // Get new areas to fill in relative to pixmap
125 // Area to redraw relative to pixmap
129 // Ignore if called by resourcethread.
130 // if( mode == IGNORE_THREAD ) return;
133 if( edit->track->show_titles() )
134 y += mwindow->theme->get_image("title_bg_data")->get_h();
137 // If want indexes only & index can't be drawn, don't do anything.
139 int64_t index_zoom = 0;
140 Indexable *indexable = 0;
141 if( edit->asset ) indexable = edit->asset;
142 if( edit->nested_edl ) indexable = edit->nested_edl;
143 if( indexable && indexes_only ) {
144 IndexFile indexfile(mwindow, indexable);
145 if( !indexfile.open_index() ) {
146 index_zoom = indexable->index_state->index_zoom;
147 indexfile.close_index();
151 if( data_type == TRACK_AUDIO ) {
152 double asset_over_session = (double)indexable->get_sample_rate() /
153 mwindow->edl->session->sample_rate;
154 if( index_zoom <= mwindow->edl->local_session->zoom_sample *
166 /* Incremental drawing is not possible with resource thread */
168 // edit->startsource != this->startsource ||
169 // mwindow->edl->session->sample_rate != project_samplerate ||
170 // !EQUIV(mwindow->edl->session->frame_rate, project_framerate) ||
171 // mwindow->edl->local_session->zoom_sample != zoom_sample ||
172 // mwindow->edl->local_session->zoom_track != zoom_track ||
173 // this->pixmap_h != pixmap_h ||
174 // (data_type == TRACK_AUDIO &&
175 // mwindow->edl->local_session->zoom_y != zoom_y) ||
179 // Redraw the whole thing.
181 refresh_w = pixmap_w;
184 // Start translated right
185 if( pixmap_w == this->pixmap_w && edit_x < this->edit_x && edit_w != pixmap_w ) {
186 refresh_w = this->edit_x - edit_x;
187 refresh_x = this->pixmap_w - refresh_w;
189 // Moved completely off the pixmap
190 if( refresh_w > this->pixmap_w ) {
191 refresh_w = this->pixmap_w;
198 mwindow->edl->local_session->zoom_track,
204 // Start translated left
205 if( pixmap_w == this->pixmap_w && edit_x > this->edit_x && edit_w != pixmap_w ) {
207 refresh_w = edit_x - this->edit_x;
209 // Moved completely off the pixmap
210 if( refresh_w > this->pixmap_w ) {
211 refresh_w = this->pixmap_w;
216 this->pixmap_w - refresh_w,
217 mwindow->edl->local_session->zoom_track,
223 // Start translated right and pixmap came off of right side
224 if( pixmap_w < this->pixmap_w && edit_x < this->edit_x &&
225 this->edit_x + edit_w > this->pixmap_x + this->pixmap_w ) {
226 refresh_w = (this->edit_x + edit_w) - (this->pixmap_x + this->pixmap_w);
227 refresh_x = pixmap_w - refresh_w;
229 if( refresh_w >= pixmap_w ) {
231 refresh_w = pixmap_w;
234 copy_area(this->edit_x - edit_x,
236 pixmap_w - refresh_w,
237 mwindow->edl->local_session->zoom_track,
243 // Start translated right and reduced in size on the right.
244 if( pixmap_w < this->pixmap_w && edit_x < this->edit_x ) {
248 copy_area(this->pixmap_w - pixmap_w,
251 mwindow->edl->local_session->zoom_track,
256 // Start translated left and pixmap came off left side
257 if( edit_x >= 0 && this->edit_x < 0 ) {
259 refresh_w = -this->edit_x;
261 if( refresh_w > pixmap_w ) {
262 refresh_w = pixmap_w;
268 mwindow->edl->local_session->zoom_track,
274 // Start translated left and reduced in size on the right
275 if( pixmap_w < this->pixmap_w && edit_x > this->edit_x ) {
280 // Start translated right and left went into left side.
281 if( pixmap_w > this->pixmap_w && edit_x < 0 && this->edit_x > 0 ) {
282 refresh_w = pixmap_w - (edit_x + this->pixmap_w);
283 refresh_x = pixmap_w - refresh_w;
285 // Moved completely off new pixmap
286 if( refresh_w > pixmap_w ) {
287 refresh_w = pixmap_w;
294 mwindow->edl->local_session->zoom_track,
300 // Start translated right and increased in size on the right
301 if( pixmap_w > this->pixmap_w && edit_x <= this->edit_x ) {
302 refresh_w = pixmap_w - this->pixmap_w;
303 refresh_x = pixmap_w - refresh_w;
306 // Start translated left and increased in size on the right
307 if( pixmap_w > this->pixmap_w && edit_x > this->edit_x ) {
309 refresh_w = edit_x - this->edit_x;
311 // Moved completely off new pixmap
312 if( refresh_w > this->pixmap_w ) {
313 refresh_w = pixmap_w;
321 mwindow->edl->local_session->zoom_track,
328 // Update pixmap settings
329 this->edit_id = edit->id;
330 this->startsource = edit->startsource;
333 this->source_framerate = edit->asset->frame_rate;
335 if( edit->nested_edl )
336 this->source_framerate = edit->nested_edl->session->frame_rate;
339 this->source_samplerate = edit->asset->sample_rate;
341 if( edit->nested_edl )
342 this->source_samplerate = edit->nested_edl->session->sample_rate;
344 this->project_framerate = edit->edl->session->frame_rate;
345 this->project_samplerate = edit->edl->session->sample_rate;
346 this->edit_x = edit_x;
347 this->pixmap_x = pixmap_x;
348 this->pixmap_w = pixmap_w;
349 this->pixmap_h = pixmap_h;
350 this->zoom_sample = mwindow->edl->local_session->zoom_sample;
351 this->zoom_track = mwindow->edl->local_session->zoom_track;
352 this->zoom_y = mwindow->edl->local_session->zoom_y;
356 // Draw background image
358 mwindow->theme->draw_resource_bg(canvas,
365 refresh_x + refresh_w,
366 mwindow->edl->local_session->zoom_track + y);
367 //printf("ResourcePixmap::draw_data 70\n");
370 // Draw media which already exists
371 Track *track = edit->track;
373 switch( track->data_type )
376 draw_audio_resource(canvas,
383 draw_video_resource(canvas,
395 draw_subttl_resource(canvas,
405 void ResourcePixmap::draw_title(TrackCanvas *canvas,
406 Edit *edit, int64_t edit_x, int64_t edit_w,
407 int64_t pixmap_x, int64_t pixmap_w)
409 // coords relative to pixmap
410 int64_t total_x = edit_x - pixmap_x, total_w = edit_w;
411 int64_t x = total_x, w = total_w;
412 int left_margin = 10;
414 if( x < 0 ) { w -= -x; x = 0; }
415 if( w > pixmap_w ) w -= w - pixmap_w;
417 canvas->draw_3segmenth(x, 0, w, total_x, total_w,
418 mwindow->theme->get_image("title_bg_data"), this);
420 // if( total_x > -BC_INFINITY ) {
421 char title[BCTEXTLEN];
422 edit->get_title(title);
423 canvas->set_color(mwindow->theme->title_color);
424 canvas->set_font(mwindow->theme->title_font);
426 // Justify the text on the left boundary of the edit if it is visible.
427 // Otherwise justify it on the left side of the screen.
428 int text_x = total_x + left_margin;
429 text_x = MAX(left_margin, text_x);
430 //printf("ResourcePixmap::draw_title 1 %d\n", text_x);
431 canvas->draw_text(text_x,
432 canvas->get_text_ascent(mwindow->theme->title_font) + 2,
433 title, strlen(title), this);
438 // Need to draw one more x
439 void ResourcePixmap::draw_audio_resource(TrackCanvas *canvas, Edit *edit, int x, int w)
442 if( !edit->asset && !edit->nested_edl ) return;
443 Indexable *indexable = 0;
444 if( edit->asset ) indexable = edit->asset;
445 if( edit->nested_edl ) indexable = edit->nested_edl;
446 // printf("ResourcePixmap::draw_audio_resource %d x=%d w=%d\n", __LINE__, x, w);
449 IndexState *index_state = indexable->index_state;
450 double asset_over_session = (double)indexable->get_sample_rate() /
451 mwindow->edl->session->sample_rate;
453 // Develop strategy for drawing
454 // printf("ResourcePixmap::draw_audio_resource %d %p %d\n",
457 // index_state->index_status);
458 switch( index_state->index_status )
460 case INDEX_NOTTESTED:
463 // Disabled. All files have an index.
464 // case INDEX_TOOSMALL:
465 // draw_audio_source(canvas, edit, x, w);
470 IndexFile indexfile(mwindow, indexable);
471 if( !indexfile.open_index() ) {
472 if( index_state->index_zoom >
473 mwindow->edl->local_session->zoom_sample *
474 asset_over_session ) {
475 //printf("ResourcePixmap::draw_audio_resource %d\n", __LINE__);
477 draw_audio_source(canvas, edit, x, w);
480 //printf("ResourcePixmap::draw_audio_resource %d\n", __LINE__);
481 indexfile.draw_index(canvas,
489 indexfile.close_index();
514 void ResourcePixmap::draw_audio_source(TrackCanvas *canvas, Edit *edit, int x, int w)
517 Indexable *indexable = edit->get_source();
518 int center_pixel = mwindow->edl->local_session->zoom_track / 2;
519 if( edit->track->show_titles() )
520 center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
521 int64_t scale_y = mwindow->edl->local_session->zoom_y;
522 int y_max = center_pixel + scale_y / 2 - 1;
524 double project_zoom = mwindow->edl->local_session->zoom_sample;
525 FloatAutos *speed_autos = !edit->track->has_speed() ? 0 :
526 (FloatAutos *)edit->track->automation->autos[AUTOMATION_SPEED];
527 int64_t edit_position = (x + pixmap_x - edit_x) * project_zoom;
528 int64_t start_position = edit->startsource;
529 start_position += !speed_autos ? edit_position :
530 speed_autos->automation_integral(edit->startproject, edit_position, PLAY_FORWARD);
531 int64_t end_position = edit->startsource;
532 edit_position = (x + w + pixmap_x - edit_x) * project_zoom;
533 end_position += !speed_autos ? edit_position :
534 speed_autos->automation_integral(edit->startproject, edit_position, PLAY_FORWARD);
536 double session_sample_rate = mwindow->edl->session->sample_rate;
537 double asset_over_session = (double)indexable->get_sample_rate() / session_sample_rate;
538 start_position *= asset_over_session;
539 end_position *= asset_over_session;
540 int sample_size = end_position - start_position;
541 if( sample_size < 0 ) sample_size = 0;
542 int source_samples = sample_size + 1;
544 // Single sample zoom
545 if( mwindow->edl->local_session->zoom_sample == 1 ) {
548 canvas->set_color(mwindow->theme->audio_color);
550 if( indexable->is_asset ) {
551 mwindow->gui->unlock_window();
552 File *source = mwindow->audio_cache->check_out(edit->asset, mwindow->edl);
553 mwindow->gui->lock_window("draw_audio_source");
556 printf(_("ResourcePixmap::draw_audio_source: failed to check out %s for drawing.\n"), edit->asset->path);
560 source->set_audio_position(start_position);
561 source->set_channel(edit->channel);
562 buffer = new Samples(source_samples);
563 result = source->read_samples(buffer, source_samples);
564 mwindow->audio_cache->check_in(edit->asset);
567 if( mwindow->gui->render_engine &&
568 mwindow->gui->render_engine_id != indexable->id ) {
569 delete mwindow->gui->render_engine;
570 mwindow->gui->render_engine = 0;
573 if( !mwindow->gui->render_engine ) {
574 TransportCommand command;
575 command.command = NORMAL_FWD;
576 command.get_edl()->copy_all(edit->nested_edl);
577 command.change_type = CHANGE_ALL;
578 command.realtime = 0;
579 mwindow->gui->render_engine = new RenderEngine(0,
580 mwindow->preferences, 0, 0);
581 mwindow->gui->render_engine_id = edit->nested_edl->id;
582 mwindow->gui->render_engine->set_acache(mwindow->audio_cache);
583 mwindow->gui->render_engine->arm_command(&command);
586 if( mwindow->gui->render_engine->arender ) {
587 Samples *buffers[MAX_CHANNELS];
588 memset(buffers, 0, sizeof(buffers));
589 int nch = indexable->get_audio_channels(), ch = edit->channel;
590 for( int i=0; i<nch; ++i )
591 buffers[i] = new Samples(source_samples);
592 mwindow->gui->render_engine->arender->process_buffer(
593 buffers, source_samples, start_position);
594 for( int i=0; i<nch; ++i )
595 if( i != ch ) delete buffers[i];
596 buffer = buffers[ch];
601 double *samples = buffer->get_data();
602 int y1 = center_pixel - samples[0] * scale_y / 2;
603 int y2 = CLIP(y1, 0, y_max);
605 for( int x0=0; x0<w; ++x0 ) {
606 int x1 = x0 + x, x2 = x1 + 1;
607 edit_position = (x1 + pixmap_x - edit_x) * project_zoom;
608 int64_t speed_position = edit->startsource;
609 speed_position += !speed_autos ? edit_position :
610 speed_autos->automation_integral(
611 edit->startproject, edit_position, PLAY_FORWARD);
612 int j = speed_position * asset_over_session - start_position;
613 CLAMP(j, 0, sample_size);
615 y1 = center_pixel - samples[j] * scale_y / 2;
616 y2 = CLIP(y1, 0, y_max);
617 //printf("ResourcePixmap::draw_audio_source %d %d %d\n", __LINE__, y1, y2);
618 canvas->draw_line(x0, y0, x2, y2, this);
625 edit_position = (x + pixmap_x - edit_x) * project_zoom;
626 int64_t speed_position = edit->startsource;
627 speed_position += !speed_autos ? edit_position :
628 speed_autos->automation_integral(
629 edit->startproject, edit_position, PLAY_FORWARD);
630 int64_t next_position = asset_over_session * speed_position;
631 // Multiple sample zoom
632 int first_pixel = 1, prev_y1 = -1, prev_y2 = y_max;
633 canvas->set_color(mwindow->theme->audio_color);
636 // Draw each pixel from the cache
637 //printf("ResourcePixmap::draw_audio_source %d x=%d w=%d\n", __LINE__, x, w);
638 for( int x2=x+w; x<x2; ++x ) {
639 int64_t prev_position = next_position;
640 edit_position = (x + pixmap_x - edit_x) * project_zoom;
641 speed_position = edit->startsource;
642 speed_position += !speed_autos ? edit_position :
643 speed_autos->automation_integral(
644 edit->startproject, edit_position, PLAY_FORWARD);
645 next_position = speed_position * asset_over_session;
646 // Starting sample of pixel relative to asset rate.
647 WaveCacheItem *item = mwindow->wave_cache->get_wave(indexable->id,
648 edit->channel, prev_position, next_position);
650 //printf("ResourcePixmap::draw_audio_source %d\n", __LINE__);
651 int y_lo = (int)(center_pixel - item->low * scale_y / 2);
652 int y1 = CLIP(y_lo, 0, y_max);
653 int y_hi = (int)(center_pixel - item->high * scale_y / 2);
654 int y2 = CLIP(y_hi, 0, y_max);
656 y_lo = MIN(y1,prev_y2);
657 y_hi = MAX(y2,prev_y1);
661 y_lo = y1; y_hi = y2;
663 prev_y1 = y1; prev_y2 = y2;
664 canvas->draw_line(x, y_lo, x, y_hi, this);
665 //printf("ResourcePixmap::draw_audio_source %d %d %d %d\n", __LINE__, x, y1, y2);
666 mwindow->wave_cache->unlock();
669 //printf("ResourcePixmap::draw_audio_source %d\n", __LINE__);
670 gui->resource_thread->add_wave(this,
671 canvas->pane->number, indexable, x,
672 edit->channel, prev_position, next_position);
673 first_pixel = 1; prev_y1 = -1; prev_y2 = y_max;
678 canvas->test_timer();
681 void ResourcePixmap::draw_wave(TrackCanvas *canvas,
682 int x, double high, double low)
685 if( mwindow->edl->session->show_titles )
686 top_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
687 int center_pixel = mwindow->edl->local_session->zoom_track / 2 + top_pixel;
688 int bottom_pixel = top_pixel + mwindow->edl->local_session->zoom_track;
689 int y1 = (int)(center_pixel -
690 low * mwindow->edl->local_session->zoom_y / 2);
691 int y2 = (int)(center_pixel -
692 high * mwindow->edl->local_session->zoom_y / 2);
693 CLAMP(y1, top_pixel, bottom_pixel);
694 CLAMP(y2, top_pixel, bottom_pixel);
695 canvas->set_color(mwindow->theme->audio_color);
696 canvas->draw_line(x, y1, x, y2, this);
700 void ResourcePixmap::draw_video_resource(TrackCanvas *canvas,
701 Edit *edit, int64_t edit_x, int64_t edit_w, int64_t pixmap_x, int64_t pixmap_w,
702 int refresh_x, int refresh_w, int mode)
705 //BC_Signals::dump_stack();
707 // pixels spanned by a picon
708 int64_t picon_w = Units::round(edit->picon_w());
709 int64_t picon_h = edit->picon_h();
710 // if( picon_w <= 0 || picon_w > edit_w ) return;
711 // Don't draw video if picon is empty, or edit only hairline
712 if( picon_w < 1 || edit_w < 2 ) return;
713 // or bigger than edit and fills at less than 1.5 percent timeline
714 if( picon_w > edit_w && edit_w < canvas->get_w()/64 ) return;
716 // Frames spanned by a picon
717 double frame_w = edit->frame_w();
718 // pixels spanned by a frame
719 if( frame_w < picon_w ) frame_w = picon_w;
720 // Current pixel relative to pixmap
722 if( edit->track->show_titles() )
723 y += mwindow->theme->get_image("title_bg_data")->get_h();
725 // Frame in project touched by current pixel
726 FloatAutos *speed_autos = !edit->track->has_speed() ? 0 :
727 (FloatAutos *)edit->track->automation->autos[AUTOMATION_SPEED];
728 Indexable *indexable = edit->get_source();
729 double session_sample_rate = mwindow->edl->session->sample_rate;
730 double project_zoom = mwindow->edl->local_session->zoom_sample / session_sample_rate;
731 int skip_frames = Units::to_int64(((int64_t)refresh_x + pixmap_x - edit_x) / frame_w);
732 int x = Units::to_int64(skip_frames * frame_w) + edit_x - pixmap_x;
734 // Draw only cached frames
735 while( x < refresh_x + refresh_w ) {
736 int64_t edit_position =
737 edit->track->to_units((x + pixmap_x - edit_x) * project_zoom, 0);
738 int64_t speed_position = edit->startsource;
739 speed_position += !speed_autos ? edit_position :
740 speed_autos->automation_integral(
741 edit->startproject, edit_position, PLAY_FORWARD);
742 VFrame *picon_frame = indexable->id < 0 ? 0 :
743 mwindow->frame_cache->get_frame_ptr(speed_position, edit->channel,
744 mwindow->edl->session->frame_rate, BC_RGB888,
745 picon_w, picon_h, indexable->id);
747 draw_vframe(picon_frame, x, y, picon_w, picon_h, 0, 0);
748 mwindow->frame_cache->unlock();
750 else if( mode != IGNORE_THREAD ) {
751 // Set picon thread to draw in background
752 // printf("ResourcePixmap::draw_video_resource %d %d %lld\n",
753 // __LINE__, mwindow->frame_cache->total(), source_frame);
754 gui->resource_thread->add_picon(this, canvas->pane->number, x, y,
755 picon_w, picon_h, mwindow->edl->session->frame_rate,
756 speed_position, edit->channel, indexable);
759 canvas->test_timer();
765 void ResourcePixmap::draw_subttl_resource(TrackCanvas *canvas, Edit *edit, int x, int w)
767 SEdit *sedit = (SEdit *)edit;
768 char *text = sedit->get_text();
769 if( !*text || w < 10 ) return;
770 int center_pixel = canvas->resource_h() / 2;
771 if( edit->track->show_titles() )
772 center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
773 int64_t scale_y = mwindow->edl->local_session->zoom_y;
775 if( x0 < 0 ) x0 = -x0;
776 int x1 = (int)(pixmap_x - x0 + x);
777 int y_max = center_pixel + scale_y / 2 - 1;
778 int font = MEDIUMFONT, color = WHITE;
779 canvas->set_font(font);
780 canvas->set_color(color);
781 int ch = canvas->get_text_height(font);
782 int hh = canvas->get_text_height(font,text) + ch/2;
783 int y1 = y_max - hh - 10;
785 canvas->draw_text(x1, y1, text, -1, this);
788 void ResourcePixmap::dump()
790 printf("ResourcePixmap %p\n", this);
791 printf(" edit %jx edit_x %jd pixmap_x %jd pixmap_w %jd visible %d\n",
792 edit_id, edit_x, pixmap_x, pixmap_w, visible);