4 * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5 * Copyright (C) 2003-2016 Cinelerra CV contributors
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "automation.h"
27 #include "bcsignals.h"
31 #include "condition.h"
34 #include "edlsession.h"
37 #include "filesystem.h"
39 #include "floatauto.h"
40 #include "floatautos.h"
41 #include "indexable.h"
42 #include "indexfile.h"
43 #include "indexstate.h"
44 #include "indexthread.h"
46 #include "localsession.h"
47 #include "mainprogress.h"
48 #include "mwindowgui.h"
50 #include "preferences.h"
51 #include "removefile.h"
52 #include "renderengine.h"
53 #include "resourcepixmap.h"
56 #include "timelinepane.h"
57 #include "trackcanvas.h"
59 #include "transportque.h"
72 #include <sys/types.h>
75 #include <linux/iso_fs.h>
77 #if defined(__FreeBSD__)
78 #include <isofs/cd9660/iso.h>
81 // check for isofs volume_id for dvd/cdrom
83 static int udf_volume_id(const char *path, char *fname)
88 if( stat(path,&st) ) return 1;
89 // search mounted devices
90 FILE *fp = fopen("/proc/mounts","r");
93 while( result && !feof(fp) && !ferror(fp) ) {
94 char devpath[BCTEXTLEN], mpath[BCTEXTLEN];
95 char options[BCTEXTLEN], line[BCTEXTLEN];
96 char fstype[64], zero1[16], zero2[16];
97 if( !fgets(&line[0], sizeof(line)-1, fp) ) break;
98 int n = sscanf(&line[0], "%s %s %s %s %s %s\n",
99 devpath, mpath, fstype, options, zero1, zero2);
100 if( n != 6 ) continue;
101 // check udf filesystems
102 if( strcmp(fstype,"udf") != 0 ) continue;
104 if( stat(devpath,&dst) ) continue;
105 if( st.st_dev != dst.st_rdev ) continue;
106 int fd = open(devpath,O_RDONLY);
107 if( fd < 0 ) continue;
108 struct iso_primary_descriptor id;
109 if( lseek(fd,0x8000,SEEK_SET) == 0x8000 )
110 n = read(fd,&id,sizeof(id));
112 if( n != sizeof(id) ) continue;
113 // look for magic number
114 if( strncmp(ISO_STANDARD_ID,id.id,sizeof(id.id)) ) continue;
115 // look for volume_id
116 if( !isalnum(id.volume_id[0]) ) continue;
117 char *bp = (char*)&id.volume_id[0], *cp = fname;
118 for( int i=0; i<(int)sizeof(id.volume_id); ++i ) *cp++ = *bp++;
119 while( --cp>=fname && *cp==' ' ) *cp = 0;
120 if( !*fname ) continue;
121 // fname = volume_id _ creation_date
122 ++cp; *cp++ = '_'; bp = (char*)&id.creation_date[0];
123 for( int i=0; i<(int)sizeof(id.creation_date)-1; ++i ) {
124 if( !isdigit(*bp) ) break;
128 if( cp-fname > 4 ) result = 0;
136 // Use native sampling rates for files so the same index can be used in
137 // multiple projects.
139 IndexFile::IndexFile(MWindow *mwindow)
141 //printf("IndexFile::IndexFile 1\n");
143 this->mwindow = mwindow;
144 //printf("IndexFile::IndexFile 2\n");
145 redraw_timer = new Timer;
148 IndexFile::IndexFile(MWindow *mwindow,
149 Indexable *indexable)
151 //printf("IndexFile::IndexFile 2\n");
153 this->mwindow = mwindow;
154 this->indexable = indexable;
155 redraw_timer = new Timer;
158 indexable->add_user();
159 source_channels = indexable->get_audio_channels();
160 source_samplerate = indexable->get_sample_rate();
161 source_length = indexable->get_audio_samples();
165 IndexFile::~IndexFile()
167 //printf("IndexFile::~IndexFile 1\n");
169 if(indexable) indexable->remove_user();
173 void IndexFile::reset()
185 IndexState* IndexFile::get_state()
187 IndexState *index_state = 0;
188 if(indexable) index_state = indexable->index_state;
194 int IndexFile::open_index()
196 IndexState *index_state = 0;
199 // use buffer if being built
200 index_state = get_state();
202 if(index_state->index_status == INDEX_BUILDING)
208 if(!(result = open_file()))
210 // opened existing file
218 index_state->index_status = INDEX_READY;
229 void IndexFile::delete_index(Preferences *preferences,
230 Indexable *indexable, const char *suffix)
232 char index_filename[BCTEXTLEN];
233 char source_filename[BCTEXTLEN];
234 const char *path = indexable->path;
236 get_index_filename(source_filename,
237 preferences->index_directory,
238 index_filename, path, suffix);
239 //printf("IndexFile::delete_index %s %s\n", source_filename, index_filename);
240 remove_file(index_filename);
243 void IndexFile::delete_index_files(Preferences *preferences,
244 Indexable *indexable)
246 delete_index(preferences, indexable, ".toc");
247 delete_index(preferences, indexable, ".idx");
248 delete_index(preferences, indexable, ".mkr");
252 int IndexFile::open_file()
256 const char *path = indexable->path;
259 //printf("IndexFile::open_file %f\n", indexable->get_frame_rate());
261 get_index_filename(source_filename,
262 mwindow->preferences->index_directory,
266 if(debug) printf("IndexFile::open_file %d index_filename=%s\n",
269 fd = fopen(index_filename, "rb");
272 // Index file already exists.
273 // Get its last size without changing the real asset status.
274 Indexable *test_indexable = new Indexable(0);
276 test_indexable->copy_indexable(indexable);
277 read_info(test_indexable);
278 IndexState *index_state = test_indexable->index_state;
281 if(fs.get_date(index_filename) < fs.get_date(test_indexable->path))
283 if(debug) printf("IndexFile::open_file %d index_date=%jd source_date=%jd\n",
285 fs.get_date(index_filename),
286 fs.get_date(test_indexable->path));
288 // index older than source
294 if(fs.get_size(test_indexable->path) != index_state->index_bytes)
296 // source file is a different size than index source file
297 if(debug) printf("IndexFile::open_file %d index_size=%jd source_size=%jd\n",
299 index_state->index_bytes,
300 fs.get_size(test_indexable->path));
307 if(debug) printf("IndexFile::open_file %d\n",
309 fseek(fd, 0, SEEK_END);
310 file_length = ftell(fd);
311 fseek(fd, 0, SEEK_SET);
314 test_indexable->Garbage::remove_user();
319 if(debug) printf("IndexFile::open_file %d index_filename=%s doesn't exist\n",
328 int IndexFile::open_source()
330 //printf("IndexFile::open_source %p %s\n", asset, asset->path);
332 if(indexable && indexable->is_asset)
334 if(!source) source = new File;
336 Asset *asset = (Asset*)indexable;
337 if(source->open_file(mwindow->preferences,
340 //printf("IndexFile::open_source() Couldn't open %s.\n", asset->path);
346 asset->index_state->index_bytes = fs.get_size(asset->path);
347 source_length = source->get_audio_length();
348 int proxy_scale = asset->proxy_scale;
349 if( proxy_scale > 0 ) {
350 asset->width = asset->actual_width * proxy_scale;
351 asset->height = asset->actual_height * proxy_scale;
357 TransportCommand command(mwindow->preferences);
358 command.command = NORMAL_FWD;
359 command.get_edl()->copy_all((EDL*)indexable);
360 command.change_type = CHANGE_ALL;
361 command.realtime = 0;
362 cache = new CICache(mwindow->preferences);
363 render_engine = new RenderEngine(0,
364 mwindow->preferences, 0, 0);
365 render_engine->set_acache(cache);
366 render_engine->arm_command(&command);
368 indexable->index_state->index_bytes = fs.get_size(indexable->path);
374 void IndexFile::close_source()
379 delete render_engine;
382 cache->remove_user();
387 int64_t IndexFile::get_required_scale()
392 // get scale of index file
393 // Total peaks which may be stored in buffer
394 int64_t peak_count = mwindow->preferences->index_size /
395 (2 * sizeof(float) * source_channels);
397 source_length / result > peak_count;
401 // Takes too long to draw from source on a CDROM. Make indexes for
407 int IndexFile::get_index_filename(char *source_filename,
408 char *index_directory,
409 char *index_filename,
410 const char *input_filename,
413 const char *input_fn = input_filename;
414 char volume_id[BCTEXTLEN];
415 // Replace mount/directory with volume_id if isofs
416 if( !udf_volume_id(input_filename, volume_id) )
418 char *cp = strrchr((char*)input_filename,'/');
419 if( cp ) input_fn = cp + 1;
420 for( cp=volume_id; *cp; ++cp );
421 *cp++ = '_'; strcpy(cp, input_fn);
422 input_fn = volume_id;
424 // Replace slashes and dots
426 int len = strlen(input_fn);
427 for(i = 0, j = 0; i < len; i++)
429 if(input_fn[i] != '/' &&
431 source_filename[j++] = input_fn[i];
435 source_filename[j++] = '_';
438 source_filename[j] = 0;
440 fs.join_names(index_filename, index_directory, source_filename);
441 strcat(index_filename, suffix ? suffix : ".idx");
445 int IndexFile::interrupt_index()
451 // Read data into buffers
453 int IndexFile::create_index(MainProgressBar *progress)
460 // open the source file
461 if(open_source()) return 1;
462 source_channels = indexable->get_audio_channels();
463 source_samplerate = indexable->get_sample_rate();
464 source_length = indexable->get_audio_samples();
468 get_index_filename(source_filename,
469 mwindow->preferences->index_directory,
475 // Some file formats have their own sample index.
476 // Test for index in stream table of contents
477 if(source && !source->get_index(this, progress))
479 IndexState *index_state = get_state();
480 index_state->index_status = INDEX_READY;
484 // Build index from scratch
488 // Indexes are now built for everything since it takes too long to draw
489 // from CDROM source.
491 // get amount to read at a time in floats
492 int64_t buffersize = 65536;
493 char string[BCTEXTLEN];
494 sprintf(string, _("Creating %s."), index_filename);
496 progress->update_title(string);
497 progress->update_length(source_length);
498 redraw_timer->update();
501 // thread out index thread
502 IndexThread *index_thread = new IndexThread(mwindow,
507 index_thread->start_build();
509 // current sample in source file
510 int64_t position = 0;
511 int64_t fragment_size = buffersize;
512 int current_buffer = 0;
515 // pass through file once
516 // printf("IndexFile::create_index %d source_length=%jd source=%p progress=%p\n",
522 while(position < source_length && !result)
525 if(source_length - position < fragment_size && fragment_size == buffersize) fragment_size = source_length - position;
527 index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 1");
528 index_thread->input_len[current_buffer] = fragment_size;
531 int cancelled = progress->update(position);
532 //printf("IndexFile::create_index cancelled=%d\n", cancelled);
535 index_thread->interrupt_flag ||
543 if(source && !result)
547 !result && channel < source_channels;
550 // Read from source file
551 source->set_audio_position(position);
552 source->set_channel(channel);
554 if(source->read_samples(
555 index_thread->buffer_in[current_buffer][channel],
562 if(render_engine && !result)
565 if(render_engine->arender)
567 result = render_engine->arender->process_buffer(
568 index_thread->buffer_in[current_buffer],
574 for(int i = 0; i < source_channels; i++)
576 bzero(index_thread->buffer_in[current_buffer][i]->get_data(),
577 fragment_size * sizeof(double));
584 // Release buffer to thread
587 index_thread->output_lock[current_buffer]->unlock();
589 if(current_buffer >= TOTAL_INDEX_BUFFERS) current_buffer = 0;
590 position += fragment_size;
594 index_thread->input_lock[current_buffer]->unlock();
600 // end thread cleanly
601 index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 2");
602 index_thread->last_buffer[current_buffer] = 1;
603 index_thread->output_lock[current_buffer]->unlock();
604 index_thread->stop_build();
621 mwindow->edl->set_index_file(indexable);
627 int IndexFile::redraw_edits(int force)
629 int64_t difference = redraw_timer->get_scaled_difference(1000);
631 if(difference > 250 || force)
633 redraw_timer->update();
634 mwindow->gui->lock_window("IndexFile::redraw_edits");
635 mwindow->edl->set_index_file(indexable);
636 mwindow->gui->draw_indexes(indexable);
637 mwindow->gui->unlock_window();
645 int IndexFile::draw_index(
647 ResourcePixmap *pixmap,
653 IndexState *index_state = get_state();
654 int pane_number = canvas->pane->number;
655 //index_state->dump();
658 if(debug) printf("IndexFile::draw_index %d\n", __LINE__);
659 if(index_state->index_zoom == 0)
661 printf(_("IndexFile::draw_index: index has 0 zoom\n"));
664 if(debug) printf("IndexFile::draw_index %d\n", __LINE__);
666 // test channel number
667 if(edit->channel > source_channels) return 1;
668 if(debug) printf("IndexFile::draw_index %d source_samplerate=%d "
669 "w=%d samplerate=%jd zoom_sample=%jd\n",
670 __LINE__, source_samplerate, w,
671 mwindow->edl->session->sample_rate,
672 mwindow->edl->local_session->zoom_sample);
674 // calculate a virtual x where the edit_x should be in floating point
675 double virtual_edit_x = 1.0 *
676 edit->track->from_units(edit->startproject) *
677 mwindow->edl->session->sample_rate /
678 mwindow->edl->local_session->zoom_sample -
679 mwindow->edl->local_session->view_start[pane_number];
681 // samples in segment to draw relative to asset
682 FloatAutos *speed_autos = !edit->track->has_speed() ? 0 :
683 (FloatAutos *)edit->track->automation->autos[AUTOMATION_SPEED];
684 double project_zoom = mwindow->edl->local_session->zoom_sample;
685 int64_t edit_position = (x + pixmap->pixmap_x - virtual_edit_x) * project_zoom;
686 int64_t start_position = edit->startsource;
687 start_position += !speed_autos ? edit_position :
688 speed_autos->automation_integral(edit->startproject, edit_position, PLAY_FORWARD);
689 int64_t end_position = edit->startsource;
690 edit_position = (x + w + pixmap->pixmap_x - virtual_edit_x) * project_zoom;
691 end_position += !speed_autos ? edit_position :
692 speed_autos->automation_integral(edit->startproject, edit_position, PLAY_FORWARD);
693 double session_sample_rate = mwindow->edl->session->sample_rate;
694 double asset_over_session = (double)indexable->get_sample_rate() / session_sample_rate;
695 int64_t start_source = start_position * asset_over_session;
696 if( start_source < 0 ) start_source = 0;
697 int64_t start_index = start_source / index_state->index_zoom;
698 int64_t end_source = end_position * asset_over_session;
699 if( end_source < 0 ) end_source = 0;
700 int64_t end_index = end_source / index_state->index_zoom;
701 // start/length of index to read in floats
702 start_index *= 2; end_index *= 2;
703 // length of index available in floats
704 int64_t size_index = index_state->index_status == INDEX_BUILDING ?
705 index_state->get_channel_used(edit->channel) * 2 :
706 index_state->get_index_size(edit->channel);
707 // Clamp length of index to read by available data
708 if( end_index >= size_index ) end_index = size_index;
709 int64_t length_index = end_index - start_index;
710 if( length_index <= 0 ) return 0;
712 // Start and length of fragment to read from file in bytes.
714 int buffer_shared = 0;
715 int rect_audio = mwindow->preferences->rectify_audio;
716 int data_h = edit->track->data_h;
717 int center_pixel = !rect_audio ? data_h/2 : data_h;
718 if( edit->track->show_titles() )
719 center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
721 if( index_state->index_status == INDEX_BUILDING ) {
722 // index is in RAM, being built
723 buffer = index_state->get_channel_buffer(edit->channel);
724 if( !buffer ) return 0;
725 buffer += start_index;
729 buffer = new float[length_index + 1];
730 int64_t length_buffer = length_index * sizeof(float);
731 // add file/channel offset
732 int64_t index_offset = index_state->get_index_offset(edit->channel);
733 int64_t file_offset = (index_offset + start_index) * sizeof(float);
734 int64_t file_pos = index_state->index_start + file_offset;
735 int64_t read_length = file_length - file_pos;
736 if( read_length > length_buffer )
737 read_length = length_buffer;
738 int64_t length_read = 0;
739 if( read_length > 0 ) {
740 fseek(fd, file_pos, SEEK_SET);
741 length_read = fread(buffer, 1, read_length + sizeof(float), fd);
742 length_read &= ~(sizeof(float)-1);
744 if( (read_length-=length_read) > 0 )
745 memset((char*)buffer + length_read, 0, read_length);
749 canvas->set_color(mwindow->theme->audio_color);
751 int prev_y1 = center_pixel;
752 int prev_y2 = center_pixel;
754 int zoom_y = mwindow->edl->local_session->zoom_y * data_h /
755 mwindow->edl->local_session->zoom_atrack;
756 if( !rect_audio ) zoom_y /= 2;
757 int max_y = center_pixel + zoom_y - 1;
758 edit_position = (x + pixmap->pixmap_x - virtual_edit_x) * project_zoom;
759 int64_t speed_position = edit->startsource;
760 speed_position += !speed_autos ? edit_position :
761 speed_autos->automation_integral(edit->startproject, edit_position, PLAY_FORWARD);
762 int64_t source_position = speed_position * asset_over_session;
763 int64_t index_position = source_position / index_state->index_zoom;
764 int64_t i = 2 * index_position - start_index;
765 CLAMP(i, 0, length_index);
768 for( int64_t x1=0; x1<w && i < length_index; ++x1 ) {
769 float highsample = !rect_audio ? buffer[i] : fabsf(buffer[i]); ++i;
770 float lowsample = !rect_audio ? buffer[i] : fabsf(buffer[i]); ++i;
772 edit_position = (x2 + pixmap->pixmap_x - virtual_edit_x) * project_zoom;
773 int64_t speed_position = edit->startsource;
774 speed_position += !speed_autos ? edit_position :
775 speed_autos->automation_integral(edit->startproject, edit_position, PLAY_FORWARD);
776 source_position = speed_position * asset_over_session;
777 index_position = source_position / index_state->index_zoom;
778 int64_t k = 2 * index_position - start_index;
779 CLAMP(k, 0, length_index);
781 float high = !rect_audio ? buffer[i] : fabsf(buffer[i]);
782 highsample = MAX(highsample, high); ++i;
783 float low = !rect_audio ? buffer[i] : fabsf(buffer[i]);
784 lowsample = MIN(lowsample, low); ++i;
787 int y1 = (int)(center_pixel - highsample * zoom_y);
788 int y2 = (int)(center_pixel - lowsample * zoom_y);
789 CLAMP(y1, 0, max_y); int next_y1 = y1;
790 CLAMP(y2, 0, max_y); int next_y2 = y2;
791 //printf("draw_line (%f,%f) = %d,%d, %d,%d\n", lowsample, highsample, x2, y1, x2, y2);
794 // A different algorithm has to be used if it's 1 sample per pixel and the
795 // index is used. Now the min and max values are equal so we join the max samples.
796 if(mwindow->edl->local_session->zoom_sample == 1) {
797 canvas->draw_bline(x2 - 1, !rect_audio ? prev_y1 : center_pixel, x2, y1, pixmap);
800 // Extend line height if it doesn't connect to previous line
802 if(y1 > prev_y2) y1 = prev_y2 + 1;
803 if(y2 < prev_y1) y2 = prev_y1 - 1;
808 canvas->draw_bline(x2, y1, x2, !rect_audio ? y2 : center_pixel, pixmap);
816 if(!buffer_shared) delete [] buffer;
818 if(debug) printf("IndexFile::draw_index %d\n", __LINE__);
822 int IndexFile::close_index()
832 int IndexFile::remove_index()
834 IndexState *index_state = get_state();
835 if(index_state->index_status == INDEX_READY ||
836 index_state->index_status == INDEX_NOTTESTED)
839 remove(index_filename);
844 int IndexFile::read_info(Indexable *test_indexable)
848 // Store format in actual asset.
849 // If it's a nested EDL, we never want the format, just the index info.
850 if(!test_indexable) test_indexable = indexable;
851 if(!test_indexable) return 1;
853 IndexState * index_state = test_indexable->index_state;
854 if(index_state->index_status == INDEX_NOTTESTED)
856 // read start of index data
857 int temp = fread((char*)&(index_state->index_start), sizeof(int64_t), 1, fd);
858 //printf("IndexFile::read_info %d %f\n", __LINE__, test_indexable->get_frame_rate());
861 // read test_indexable info from index
864 data = new char[index_state->index_start];
865 temp = fread(data, index_state->index_start - sizeof(int64_t), 1, fd);
866 if(!temp) { delete [] data;
869 data[index_state->index_start - sizeof(int64_t)] = 0;
871 xml.read_from_string(data);
876 // Read the file format & index state.
877 if(test_indexable->is_asset)
879 Asset *test_asset = (Asset *)test_indexable;
880 Asset *asset = new Asset;
883 //printf("IndexFile::read_info %d %f\n", __LINE__, asset->get_frame_rate());
885 if( asset->format == FILE_UNKNOWN ||
886 test_asset->format != asset->format ) {
887 if(debug) printf("IndexFile::read_info %d\n", __LINE__);
890 asset->remove_user();
891 if( ret ) return ret;
895 // Read only the index state for a nested EDL
897 if(debug) printf("IndexFile::read_info %d\n", __LINE__);
900 result = xml.read_tag();
903 if(xml.tag.title_is("INDEX"))
905 index_state->read_xml(&xml, source_channels);
906 if(debug) printf("IndexFile::read_info %d\n", __LINE__);
907 if(debug) index_state->dump();