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
24 #include "bcsignals.h"
28 #include "condition.h"
31 #include "edlsession.h"
34 #include "filesystem.h"
36 #include "indexable.h"
37 #include "indexfile.h"
38 #include "indexstate.h"
39 #include "indexthread.h"
41 #include "localsession.h"
42 #include "mainprogress.h"
43 #include "mwindowgui.h"
45 #include "preferences.h"
46 #include "removefile.h"
47 #include "renderengine.h"
48 #include "resourcepixmap.h"
51 #include "timelinepane.h"
52 #include "trackcanvas.h"
54 #include "transportque.h"
67 #include <sys/types.h>
69 #include <linux/iso_fs.h>
71 // check for isofs volume_id for dvd/cdrom
73 static int udf_volume_id(const char *path, char *fname)
76 if( stat(path,&st) ) return 1;
77 // search mounted devices
78 FILE *fp = fopen("/proc/mounts","r");
82 while( result && !feof(fp) && !ferror(fp) ) {
83 char devpath[BCTEXTLEN], mpath[BCTEXTLEN];
84 char options[BCTEXTLEN], line[BCTEXTLEN];
85 char fstype[64], zero1[16], zero2[16];
86 if( !fgets(&line[0], sizeof(line)-1, fp) ) break;
87 int n = sscanf(&line[0], "%s %s %s %s %s %s\n",
88 devpath, mpath, fstype, options, zero1, zero2);
89 if( n != 6 ) continue;
90 // check udf filesystems
91 if( strcmp(fstype,"udf") != 0 ) continue;
93 if( stat(devpath,&dst) ) continue;
94 if( st.st_dev != dst.st_rdev ) continue;
95 int fd = open(devpath,O_RDONLY);
96 if( fd < 0 ) continue;
97 struct iso_primary_descriptor id;
98 if( lseek(fd,0x8000,SEEK_SET) == 0x8000 )
99 n = read(fd,&id,sizeof(id));
101 if( n != sizeof(id) ) continue;
102 // look for magic number
103 if( strncmp(ISO_STANDARD_ID,id.id,sizeof(id.id)) ) continue;
104 // look for volume_id
105 if( !isalnum(id.volume_id[0]) ) continue;
106 char *bp = (char*)&id.volume_id[0], *cp = fname;
107 for( int i=0; i<(int)sizeof(id.volume_id); ++i ) *cp++ = *bp++;
108 while( --cp>=fname && *cp==' ' ) *cp = 0;
109 if( !*fname ) continue;
110 // fname = volume_id _ creation_date
111 ++cp; *cp++ = '_'; bp = (char*)&id.creation_date[0];
112 for( int i=0; i<(int)sizeof(id.creation_date)-1; ++i ) {
113 if( !isdigit(*bp) ) break;
117 if( cp-fname > 4 ) result = 0;
124 // Use native sampling rates for files so the same index can be used in
125 // multiple projects.
127 IndexFile::IndexFile(MWindow *mwindow)
129 //printf("IndexFile::IndexFile 1\n");
131 this->mwindow = mwindow;
132 //printf("IndexFile::IndexFile 2\n");
133 redraw_timer = new Timer;
136 IndexFile::IndexFile(MWindow *mwindow,
137 Indexable *indexable)
139 //printf("IndexFile::IndexFile 2\n");
141 this->mwindow = mwindow;
142 this->indexable = indexable;
143 redraw_timer = new Timer;
146 indexable->add_user();
147 source_channels = indexable->get_audio_channels();
148 source_samplerate = indexable->get_sample_rate();
149 source_length = indexable->get_audio_samples();
153 IndexFile::~IndexFile()
155 //printf("IndexFile::~IndexFile 1\n");
157 if(indexable) indexable->remove_user();
161 void IndexFile::reset()
173 IndexState* IndexFile::get_state()
175 IndexState *index_state = 0;
176 if(indexable) index_state = indexable->index_state;
182 int IndexFile::open_index()
184 IndexState *index_state = 0;
187 // use buffer if being built
188 index_state = get_state();
190 if(index_state->index_status == INDEX_BUILDING)
196 if(!(result = open_file()))
198 // opened existing file
206 index_state->index_status = INDEX_READY;
217 void IndexFile::delete_index(Preferences *preferences,
218 Indexable *indexable, const char *suffix)
220 char index_filename[BCTEXTLEN];
221 char source_filename[BCTEXTLEN];
222 const char *path = indexable->path;
224 get_index_filename(source_filename,
225 preferences->index_directory,
226 index_filename, path, suffix);
227 //printf("IndexFile::delete_index %s %s\n", source_filename, index_filename);
228 remove_file(index_filename);
231 int IndexFile::open_file()
235 const char *path = indexable->path;
238 //printf("IndexFile::open_file %f\n", indexable->get_frame_rate());
240 get_index_filename(source_filename,
241 mwindow->preferences->index_directory,
245 if(debug) printf("IndexFile::open_file %d index_filename=%s\n",
248 fd = fopen(index_filename, "rb");
251 // Index file already exists.
252 // Get its last size without changing the real asset status.
253 Indexable *test_indexable = new Indexable(0);
255 test_indexable->copy_indexable(indexable);
256 read_info(test_indexable);
257 IndexState *index_state = test_indexable->index_state;
260 if(fs.get_date(index_filename) < fs.get_date(test_indexable->path))
262 if(debug) printf("IndexFile::open_file %d index_date=%jd source_date=%jd\n",
264 fs.get_date(index_filename),
265 fs.get_date(test_indexable->path));
267 // index older than source
273 if(fs.get_size(test_indexable->path) != index_state->index_bytes)
275 // source file is a different size than index source file
276 if(debug) printf("IndexFile::open_file %d index_size=%jd source_size=%jd\n",
278 index_state->index_bytes,
279 fs.get_size(test_indexable->path));
286 if(debug) printf("IndexFile::open_file %d\n",
288 fseek(fd, 0, SEEK_END);
289 file_length = ftell(fd);
290 fseek(fd, 0, SEEK_SET);
293 test_indexable->Garbage::remove_user();
298 if(debug) printf("IndexFile::open_file %d index_filename=%s doesn't exist\n",
307 int IndexFile::open_source()
309 //printf("IndexFile::open_source %p %s\n", asset, asset->path);
311 if(indexable && indexable->is_asset)
313 if(!source) source = new File;
315 Asset *asset = (Asset*)indexable;
316 if(source->open_file(mwindow->preferences,
319 //printf("IndexFile::open_source() Couldn't open %s.\n", asset->path);
325 asset->index_state->index_bytes = fs.get_size(asset->path);
326 source_length = source->get_audio_length();
331 TransportCommand command;
332 command.command = NORMAL_FWD;
333 command.get_edl()->copy_all((EDL*)indexable);
334 command.change_type = CHANGE_ALL;
335 command.realtime = 0;
336 cache = new CICache(mwindow->preferences);
337 render_engine = new RenderEngine(0,
338 mwindow->preferences, 0, 0);
339 render_engine->set_acache(cache);
340 render_engine->arm_command(&command);
342 indexable->index_state->index_bytes = fs.get_size(indexable->path);
348 void IndexFile::close_source()
353 delete render_engine;
360 int64_t IndexFile::get_required_scale()
365 // get scale of index file
366 // Total peaks which may be stored in buffer
367 int64_t peak_count = mwindow->preferences->index_size /
368 (2 * sizeof(float) * source_channels);
370 source_length / result > peak_count;
374 // Takes too long to draw from source on a CDROM. Make indexes for
380 int IndexFile::get_index_filename(char *source_filename,
381 char *index_directory,
382 char *index_filename,
383 const char *input_filename,
386 const char *input_fn = input_filename;
387 char volume_id[BCTEXTLEN];
388 // Replace mount/directory with volume_id if isofs
389 if( !udf_volume_id(input_filename, volume_id) )
391 char *cp = strrchr((char*)input_filename,'/');
392 if( cp ) input_fn = cp + 1;
393 for( cp=volume_id; *cp; ++cp );
394 *cp++ = '_'; strcpy(cp, input_fn);
395 input_fn = volume_id;
397 // Replace slashes and dots
399 int len = strlen(input_fn);
400 for(i = 0, j = 0; i < len; i++)
402 if(input_fn[i] != '/' &&
404 source_filename[j++] = input_fn[i];
408 source_filename[j++] = '_';
411 source_filename[j] = 0;
413 fs.join_names(index_filename, index_directory, source_filename);
414 strcat(index_filename, suffix ? suffix : ".idx");
418 int IndexFile::interrupt_index()
424 // Read data into buffers
426 int IndexFile::create_index(MainProgressBar *progress)
433 // open the source file
434 if(open_source()) return 1;
435 source_channels = indexable->get_audio_channels();
436 source_samplerate = indexable->get_sample_rate();
437 source_length = indexable->get_audio_samples();
441 get_index_filename(source_filename,
442 mwindow->preferences->index_directory,
448 // Some file formats have their own sample index.
449 // Test for index in stream table of contents
450 if(source && !source->get_index(this, progress))
452 IndexState *index_state = get_state();
453 index_state->index_status = INDEX_READY;
457 // Build index from scratch
461 // Indexes are now built for everything since it takes too long to draw
462 // from CDROM source.
464 // get amount to read at a time in floats
465 int64_t buffersize = 65536;
466 char string[BCTEXTLEN];
467 sprintf(string, _("Creating %s."), index_filename);
469 progress->update_title(string);
470 progress->update_length(source_length);
471 redraw_timer->update();
474 // thread out index thread
475 IndexThread *index_thread = new IndexThread(mwindow,
480 index_thread->start_build();
482 // current sample in source file
483 int64_t position = 0;
484 int64_t fragment_size = buffersize;
485 int current_buffer = 0;
488 // pass through file once
489 // printf("IndexFile::create_index %d source_length=%jd source=%p progress=%p\n",
495 while(position < source_length && !result)
498 if(source_length - position < fragment_size && fragment_size == buffersize) fragment_size = source_length - position;
500 index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 1");
501 index_thread->input_len[current_buffer] = fragment_size;
504 int cancelled = progress->update(position);
505 //printf("IndexFile::create_index cancelled=%d\n", cancelled);
508 index_thread->interrupt_flag ||
516 if(source && !result)
520 !result && channel < source_channels;
523 // Read from source file
524 source->set_audio_position(position);
525 source->set_channel(channel);
527 if(source->read_samples(
528 index_thread->buffer_in[current_buffer][channel],
535 if(render_engine && !result)
538 if(render_engine->arender)
540 result = render_engine->arender->process_buffer(
541 index_thread->buffer_in[current_buffer],
547 for(int i = 0; i < source_channels; i++)
549 bzero(index_thread->buffer_in[current_buffer][i]->get_data(),
550 fragment_size * sizeof(double));
557 // Release buffer to thread
560 index_thread->output_lock[current_buffer]->unlock();
562 if(current_buffer >= TOTAL_INDEX_BUFFERS) current_buffer = 0;
563 position += fragment_size;
567 index_thread->input_lock[current_buffer]->unlock();
573 // end thread cleanly
574 index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 2");
575 index_thread->last_buffer[current_buffer] = 1;
576 index_thread->output_lock[current_buffer]->unlock();
577 index_thread->stop_build();
594 mwindow->edl->set_index_file(indexable);
600 int IndexFile::redraw_edits(int force)
602 int64_t difference = redraw_timer->get_scaled_difference(1000);
604 if(difference > 250 || force)
606 redraw_timer->update();
607 mwindow->gui->lock_window("IndexFile::redraw_edits");
608 mwindow->edl->set_index_file(indexable);
609 mwindow->gui->draw_indexes(indexable);
610 mwindow->gui->unlock_window();
618 int IndexFile::draw_index(
620 ResourcePixmap *pixmap,
626 IndexState *index_state = get_state();
627 int pane_number = canvas->pane->number;
628 //index_state->dump();
631 if(debug) printf("IndexFile::draw_index %d\n", __LINE__);
632 if(index_state->index_zoom == 0)
634 printf(_("IndexFile::draw_index: index has 0 zoom\n"));
637 if(debug) printf("IndexFile::draw_index %d\n", __LINE__);
639 // test channel number
640 if(edit->channel > source_channels) return 1;
641 if(debug) printf("IndexFile::draw_index %d source_samplerate=%d "
642 "w=%d samplerate=%jd zoom_sample=%jd\n",
643 __LINE__, source_samplerate, w,
644 mwindow->edl->session->sample_rate,
645 mwindow->edl->local_session->zoom_sample);
647 // calculate a virtual x where the edit_x should be in floating point
648 double virtual_edit_x = 1.0 *
649 edit->track->from_units(edit->startproject) *
650 mwindow->edl->session->sample_rate /
651 mwindow->edl->local_session->zoom_sample -
652 mwindow->edl->local_session->view_start[pane_number];
654 // samples in segment to draw relative to asset
655 double asset_over_session = (double)source_samplerate /
656 mwindow->edl->session->sample_rate;
657 int64_t startsource = (int64_t)(((pixmap->pixmap_x - virtual_edit_x + x) *
658 mwindow->edl->local_session->zoom_sample +
661 // just in case we get a numerical error
662 if (startsource < 0) startsource = 0;
663 int64_t length = (int64_t)(w *
664 mwindow->edl->local_session->zoom_sample *
666 int64_t lengthindex = length / index_state->index_zoom * 2;
667 int64_t startindex = startsource / index_state->index_zoom * 2;
668 // length of index to read in floats
669 // length of index available in floats
670 int64_t endindex = index_state->index_status == INDEX_BUILDING ?
671 index_state->get_channel_used(edit->channel) * 2 :
672 index_state->get_index_size(edit->channel);
673 // Clamp length of index to read by available data
674 if(startindex + lengthindex >= endindex )
675 lengthindex = endindex - startindex;
676 if( lengthindex <= 0 ) return 0;
678 // Actual length read from file in bytes
680 // Start and length of fragment to read from file in bytes.
681 int64_t startfile, lengthfile;
683 int buffer_shared = 0;
684 int center_pixel = mwindow->edl->local_session->zoom_track / 2;
685 if( mwindow->edl->session->show_titles )
686 center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
687 //int miny = center_pixel - mwindow->edl->local_session->zoom_track / 2;
688 //int maxy = center_pixel + mwindow->edl->local_session->zoom_track / 2;
690 // get zoom_sample relative to index zoomx
691 double index_frames_per_pixel = mwindow->edl->local_session->zoom_sample /
692 index_state->index_zoom *
697 if(index_state->index_status == INDEX_BUILDING)
699 // index is in RAM, being built
700 buffer = index_state->get_channel_buffer(edit->channel);
701 if( !buffer ) return 0;
702 buffer += startindex;
707 // add channel offset
708 startindex += index_state->get_index_offset(edit->channel);
709 // index is stored in a file
710 buffer = new float[lengthindex + 1];
712 startfile = index_state->index_start + startindex * sizeof(float);
713 lengthfile = lengthindex * sizeof(float);
716 if(startfile < file_length)
718 fseek(fd, startfile, SEEK_SET);
720 length_read = lengthfile;
721 if(startfile + length_read > file_length)
722 length_read = file_length - startfile;
724 (void)fread(buffer, length_read + sizeof(float), 1, fd);
727 if(length_read < lengthfile) {
728 int pos = length_read / sizeof(float);
729 int file_length = lengthfile / sizeof(float);
730 while( pos < file_length ) buffer[pos++] = 0;
734 canvas->set_color(mwindow->theme->audio_color);
736 double current_frame = 0;
737 float highsample = buffer[0];
738 float lowsample = buffer[1];
739 int prev_y1 = center_pixel;
740 int prev_y2 = center_pixel;
742 int zoom_y = mwindow->edl->local_session->zoom_y, zoom_y2 = zoom_y / 2;
743 int max_y = center_pixel + zoom_y2 - 1;
746 for(int bufferposition = 0;
747 bufferposition < lengthindex;
750 if(current_frame >= index_frames_per_pixel)
753 int y1 = (int)(center_pixel - highsample * zoom_y2);
754 int y2 = (int)(center_pixel - lowsample * zoom_y2);
755 CLAMP(y1, 0, max_y); int next_y1 = y1;
756 CLAMP(y2, 0, max_y); int next_y2 = y2;
757 //printf("draw_line (%f,%f) = %d,%d, %d,%d\n", lowsample, highsample, x1 + x, y1, x1 + x, y2);
760 // A different algorithm has to be used if it's 1 sample per pixel and the
761 // index is used. Now the min and max values are equal so we join the max samples.
762 if(mwindow->edl->local_session->zoom_sample == 1)
764 canvas->draw_line(x1 + x - 1, prev_y1, x1 + x, y1, pixmap);
768 // Extend line height if it doesn't connect to previous line
771 if(y1 > prev_y2) y1 = prev_y2 + 1;
772 if(y2 < prev_y1) y2 = prev_y1 - 1;
781 canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap);
783 current_frame -= index_frames_per_pixel;
787 highsample = buffer[bufferposition];
788 lowsample = buffer[bufferposition + 1];
792 highsample = MAX(highsample, buffer[bufferposition]);
793 lowsample = MIN(lowsample, buffer[bufferposition + 1]);
800 y1 = (int)(center_pixel - highsample * zoom_y2);
801 y2 = (int)(center_pixel - lowsample * zoom_y2);
802 canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap);
809 if(!buffer_shared) delete [] buffer;
811 if(debug) printf("IndexFile::draw_index %d\n", __LINE__);
815 int IndexFile::close_index()
825 int IndexFile::remove_index()
827 IndexState *index_state = get_state();
828 if(index_state->index_status == INDEX_READY ||
829 index_state->index_status == INDEX_NOTTESTED)
832 remove(index_filename);
837 int IndexFile::read_info(Indexable *test_indexable)
841 // Store format in actual asset.
842 // If it's a nested EDL, we never want the format, just the index info.
843 if(!test_indexable) test_indexable = indexable;
844 if(!test_indexable) return 1;
846 IndexState * index_state = test_indexable->index_state;
847 if(index_state->index_status == INDEX_NOTTESTED)
849 // read start of index data
850 int temp = fread((char*)&(index_state->index_start), sizeof(int64_t), 1, fd);
851 //printf("IndexFile::read_info %d %f\n", __LINE__, test_indexable->get_frame_rate());
854 // read test_indexable info from index
857 data = new char[index_state->index_start];
858 temp = fread(data, index_state->index_start - sizeof(int64_t), 1, fd);
861 data[index_state->index_start - sizeof(int64_t)] = 0;
863 xml.read_from_string(data);
868 // Read the file format & index state.
869 if(test_indexable->is_asset)
871 Asset *test_asset = (Asset *)test_indexable;
872 Asset *asset = new Asset;
875 //printf("IndexFile::read_info %d %f\n", __LINE__, asset->get_frame_rate());
877 if( asset->format == FILE_UNKNOWN ||
878 test_asset->format != asset->format ) {
879 if(debug) printf("IndexFile::read_info %d\n", __LINE__);
882 asset->remove_user();
883 if( ret ) return ret;
887 // Read only the index state for a nested EDL
889 if(debug) printf("IndexFile::read_info %d\n", __LINE__);
892 result = xml.read_tag();
895 if(xml.tag.title_is("INDEX"))
897 index_state->read_xml(&xml, source_channels);
898 if(debug) printf("IndexFile::read_info %d\n", __LINE__);
899 if(debug) index_state->dump();