r665: Merged the official release 2.0.
[cinelerra_cv.git] / cinelerra / indexfile.C
blob7361dfb18d4a9748490988a8ebe97d5967daf4ad
1 #include "asset.h"
2 #include "clip.h"
3 #include "condition.h"
4 #include "edit.h"
5 #include "edl.h"
6 #include "edlsession.h"
7 #include "filexml.h"
8 #include "filesystem.h"
9 #include "errorbox.h"
10 #include "file.h"
11 #include "indexfile.h"
12 #include "indexthread.h"
13 #include "language.h"
14 #include "localsession.h"
15 #include "mainprogress.h"
16 #include "mwindow.h"
17 #include "mwindowgui.h"
18 #include "preferences.h"
19 #include "resourcepixmap.h"
20 #include "theme.h"
21 #include "bctimer.h"
22 #include "trackcanvas.h"
23 #include "tracks.h"
24 #include "vframe.h"
26 // Use native sampling rates for files so the same index can be used in
27 // multiple projects.
29 IndexFile::IndexFile(MWindow *mwindow)
31 //printf("IndexFile::IndexFile 1\n");
32         this->mwindow = mwindow;
33 //printf("IndexFile::IndexFile 2\n");
34         file = 0;
35         interrupt_flag = 0;
36         redraw_timer = new Timer;
39 IndexFile::IndexFile(MWindow *mwindow, Asset *asset)
41 //printf("IndexFile::IndexFile 2\n");
42         file = 0;
43         this->mwindow = mwindow;
44         this->asset = asset;
45         interrupt_flag = 0;
46         redraw_timer = new Timer;
49 IndexFile::~IndexFile()
51 //printf("IndexFile::~IndexFile 1\n");
52         delete redraw_timer;
55 int IndexFile::open_index(Asset *asset)
57 // use buffer if being built
58         this->asset = asset;
59         int result = 0;
61         if(asset->index_status == INDEX_BUILDING)
62         {
63 // use buffer
64                 result = 0;
65         }
66         else
67         if(!(result = open_file()))
68         {
69 // opened existing file
70                 if(read_info())
71                 {
72                         result = 1;
73                         close_index();
74                 }
75                 else
76                 {
77                         asset->index_status = INDEX_READY;
78                 }
79         }
80         else
81         {
82                 result = 1;
83         }
85         return result;
88 int IndexFile::open_index(MWindow *mwindow, Asset *asset)
90         return open_index(asset);
93 void IndexFile::delete_index(Preferences *preferences, Asset *asset)
95         char index_filename[BCTEXTLEN];
96         char source_filename[BCTEXTLEN];
97         get_index_filename(source_filename, 
98                 preferences->index_directory,
99                 index_filename, 
100                 asset->path);
101 //printf("IndexFile::delete_index %s %s\n", source_filename, index_filename);
102         remove(index_filename);
105 int IndexFile::open_file()
107         int result = 0;
108         get_index_filename(source_filename, 
109                 mwindow->preferences->index_directory,
110                 index_filename, 
111                 asset->path);
113         if(file = fopen(index_filename, "rb"))
114         {
115 // Index file already exists.
116 // Get its last size without changing the status.
117                 Asset test_asset;
118                 test_asset = *asset;
119                 read_info(&test_asset);
121                 FileSystem fs;
122                 if(fs.get_date(index_filename) < fs.get_date(test_asset.path))
123                 {
124 // index older than source
125                         result = 2;
126                         fclose(file);
127                 }
128                 else
129                 if(fs.get_size(asset->path) != test_asset.index_bytes)
130                 {
131 // source file is a different size than index source file
132                         result = 2;
133                         fclose(file);   
134                 }
135                 else
136                 {
137                         fseek(file, 0, SEEK_END);
138                         file_length = ftell(file);
139                         fseek(file, 0, SEEK_SET);
140                         result = 0;
141                 }
142         }
143         else
144         {
145 // doesn't exist
146                 result = 1;
147         }
149         return result;
152 int IndexFile::open_source(File *source)
154 //printf("IndexFile::open_source %p %s\n", asset, asset->path);
155         if(source->open_file(mwindow->preferences, 
156                 asset, 
157                 1, 
158                 0, 
159                 0, 
160                 0))
161         {
162                 //printf("IndexFile::open_source() Couldn't open %s.\n", asset->path);
163                 return 1;
164         }
165         else
166         {
167                 FileSystem fs;
168                 asset->index_bytes = fs.get_size(asset->path);
169                 return 0;
170         }
173 int64_t IndexFile::get_required_scale(File *source)
175         int64_t result = 1;
176 // total length of input file
177         int64_t length_source = source->get_audio_length(0);  
179 // get scale of index file
180 //      if(length_source > mwindow->preferences->index_size)
181 //      {
182 // Total peaks which may be stored in buffer
183                 int64_t peak_count = mwindow->preferences->index_size / (2 * sizeof(float) * asset->channels);
184                 for(result = 1; 
185                         length_source / result > peak_count; 
186                         result *= 2)
187                         ;
188 //      }
189 //      else
190 //      {
191 // too small to build an index for
192 //              result = 0;
193 //      }
195 // Takes too long to draw from source on a CDROM.  Make indexes for
196 // everything.
198         return result;
201 int IndexFile::get_index_filename(char *source_filename, 
202         char *index_directory, 
203         char *index_filename, 
204         char *input_filename)
206 // Replace slashes and dots
207         int i, j;
208         int len = strlen(input_filename);
209         for(i = 0, j = 0; i < len; i++)
210         {
211                 if(input_filename[i] != '/' &&
212                         input_filename[i] != '.')
213                         source_filename[j++] = input_filename[i];
214                 else
215                 {
216                         if(i > 0)
217                                 source_filename[j++] = '_';
218                 }
219         }
220         source_filename[j] = 0;
221         FileSystem fs;
222         fs.join_names(index_filename, index_directory, source_filename);
223         strcat(index_filename, ".idx");
224         return 0;
227 int IndexFile::interrupt_index()
229         interrupt_flag = 1;
230         return 0;
233 // Read data into buffers
235 int IndexFile::create_index(Asset *asset, MainProgressBar *progress)
237         int result = 0;
238         this->mwindow = mwindow;
239         this->asset = asset;
240         interrupt_flag = 0;
242 // open the source file
243         File source;
244         if(open_source(&source)) return 1;
247         get_index_filename(source_filename, 
248                 mwindow->preferences->index_directory, 
249                 index_filename, 
250                 asset->path);
253 // Test for index in stream table of contents
254         if(!source.get_index(index_filename))
255         {
256 printf("IndexFile::create_index 1\n");
257                 redraw_edits(1);
258         }
259         else
260 // Build index from scratch
261         {
266                 asset->index_zoom = get_required_scale(&source);
268 // Indexes are now built for everything since it takes too long to draw
269 // from CDROM source.
272 // total length of input file
273                 int64_t length_source = source.get_audio_length(0);  
275 // get amount to read at a time in floats
276                 int64_t buffersize = 65536;
277                 char string[BCTEXTLEN];
278                 sprintf(string, _("Creating %s."), index_filename);
280                 progress->update_title(string);
281                 progress->update_length(length_source);
282                 redraw_timer->update();
284 // thread out index thread
285                 IndexThread *index_thread = new IndexThread(mwindow, 
286                         this, 
287                         asset, 
288                         index_filename, 
289                         buffersize, 
290                         length_source);
291                 index_thread->start_build();
293 // current sample in source file
294                 int64_t position = 0;
295                 int64_t fragment_size = buffersize;
296                 int current_buffer = 0;
299 // pass through file once
300                 while(position < length_source && !result)
301                 {
302                         if(length_source - position < fragment_size && fragment_size == buffersize) fragment_size = length_source - position;
304                         index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 1");
305                         index_thread->input_len[current_buffer] = fragment_size;
307                         int cancelled = progress->update(position);
308                         if(cancelled || 
309                                 index_thread->interrupt_flag || 
310                                 interrupt_flag)
311                         {
312                                 result = 3;
313                         }
315                         for(int channel = 0; !result && channel < asset->channels; channel++)
316                         {
317                                 source.set_audio_position(position, 0);
318                                 source.set_channel(channel);
320 // Read from source file
321                                 if(source.read_samples(index_thread->buffer_in[current_buffer][channel], 
322                                         fragment_size,
323                                         0)) result = 1;
324                         }
326 // Release buffer to thread
327                         if(!result)
328                         {
329                                 index_thread->output_lock[current_buffer]->unlock();
330                                 current_buffer++;
331                                 if(current_buffer >= TOTAL_BUFFERS) current_buffer = 0;
332                                 position += fragment_size;
333                         }
334                         else
335                         {
336                                 index_thread->input_lock[current_buffer]->unlock();
337                         }
338                 }
340 // end thread cleanly
341                 index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 2");
342                 index_thread->last_buffer[current_buffer] = 1;
343                 index_thread->output_lock[current_buffer]->unlock();
345                 index_thread->stop_build();
346                 delete index_thread;
347         }
350         source.close_file();
353         open_index(asset);
354         close_index();
355         mwindow->edl->set_index_file(asset);
356 //printf("IndexFile::create_index 11\n");
357         return 0;
361 int IndexFile::create_index(MWindow *mwindow, 
362                 Asset *asset, 
363                 MainProgressBar *progress)
365         return create_index(asset, progress);
370 int IndexFile::redraw_edits(int force)
372         int64_t difference = redraw_timer->get_scaled_difference(1000);
374         if(difference > 250 || force)
375         {
376                 redraw_timer->update();
377 // Can't lock window here since the window is only redrawn when the pixel
378 // count changes.
379                 mwindow->gui->lock_window("IndexFile::redraw_edits");
380                 mwindow->edl->set_index_file(asset);
381                 mwindow->gui->canvas->draw_indexes(asset);
382                 asset->old_index_end = asset->index_end;
383                 mwindow->gui->unlock_window();
384         }
385         return 0;
391 int IndexFile::draw_index(ResourcePixmap *pixmap, Edit *edit, int x, int w)
393 // check against index_end when being built
394         if(asset->index_zoom == 0)
395         {
396                 printf(_("IndexFile::draw_index: index has 0 zoom\n"));
397                 return 0;
398         }
400 // test channel number
401         if(edit->channel > asset->channels) return 1;
403 // calculate a virtual x where the edit_x should be in floating point
404         double virtual_edit_x = 1.0 * edit->track->from_units(edit->startproject) * 
405                         mwindow->edl->session->sample_rate /
406                         mwindow->edl->local_session->zoom_sample - 
407                         mwindow->edl->local_session->view_start;
409 // samples in segment to draw relative to asset
410         double asset_over_session = (double)edit->asset->sample_rate / 
411                 mwindow->edl->session->sample_rate;
412 //      int64_t startsource = (int64_t)(((pixmap->pixmap_x - pixmap->edit_x + x) * 
413         int64_t startsource = (int64_t)(((pixmap->pixmap_x - virtual_edit_x + x) * 
414                 mwindow->edl->local_session->zoom_sample + 
415                 edit->startsource) * 
416                 asset_over_session);
417 // just in case we get a numerical error 
418         if (startsource < 0) startsource = 0;
419         int64_t length = (int64_t)(w * 
420                 mwindow->edl->local_session->zoom_sample * 
421                 asset_over_session);
423         if(asset->index_status == INDEX_BUILDING)
424         {
425                 if(startsource + length > asset->index_end)
426                         length = asset->index_end - startsource;
427         }
429 // length of index to read in samples * 2
430         int64_t lengthindex = length / asset->index_zoom * 2;
431 // start of data in samples
432         int64_t startindex = startsource / asset->index_zoom * 2;  
433 // Clamp length of index to read by available data
434         if(startindex + lengthindex > asset->get_index_size(edit->channel))
435                 lengthindex = asset->get_index_size(edit->channel) - startindex;
436         if(lengthindex <= 0) return 0;
441 // Actual length read from file in bytes
442         int64_t length_read;   
443 // Start and length of fragment to read from file in bytes.
444         int64_t startfile, lengthfile;
445         float *buffer = 0;
446         int buffer_shared = 0;
447         int i;
448         int center_pixel = mwindow->edl->local_session->zoom_track / 2;
449         if(mwindow->edl->session->show_titles) center_pixel += mwindow->theme->title_bg_data->get_h();
450         int miny = center_pixel - mwindow->edl->local_session->zoom_track / 2;
451         int maxy = center_pixel + mwindow->edl->local_session->zoom_track / 2;
452         int x1 = 0, y1, y2;
453 // get zoom_sample relative to index zoomx
454         double index_frames_per_pixel = mwindow->edl->local_session->zoom_sample / 
455                 asset->index_zoom * 
456                 asset_over_session;
458 // get channel offset
459         startindex += asset->get_index_offset(edit->channel);
462         if(asset->index_status == INDEX_BUILDING)
463         {
464 // index is in RAM, being built
465                 buffer = &(asset->index_buffer[startindex]);
466                 buffer_shared = 1;
467         }
468         else
469         {
470 // index is stored in a file
471                 buffer = new float[lengthindex + 1];
472                 buffer_shared = 0;
473                 startfile = asset->index_start + startindex * sizeof(float);
474                 lengthfile = lengthindex * sizeof(float);
475                 length_read = 0;
477                 if(startfile < file_length)
478                 {
479                         fseek(file, startfile, SEEK_SET);
481                         length_read = lengthfile;
482                         if(startfile + length_read > file_length)
483                                 length_read = file_length - startfile;
485                         fread(buffer, length_read + sizeof(float), 1, file);
486                 }
488                 if(length_read < lengthfile)
489                         for(i = length_read / sizeof(float); 
490                                 i < lengthfile / sizeof(float); 
491                                 i++)
492                                 buffer[i] = 0;
493         }
497         pixmap->canvas->set_color(mwindow->theme->audio_color);
500         double current_frame = 0;
501         float highsample = buffer[0];
502         float lowsample = buffer[1];
503         int prev_y1 = center_pixel;
504         int prev_y2 = center_pixel;
505         int first_frame = 1;
507         for(int bufferposition = 0; 
508                 bufferposition < lengthindex; 
509                 bufferposition += 2)
510         {
511                 if(current_frame >= index_frames_per_pixel)
512                 {
513                         int next_y1 = (int)(center_pixel - highsample * mwindow->edl->local_session->zoom_y / 2);
514                         int next_y2 = (int)(center_pixel - lowsample * mwindow->edl->local_session->zoom_y / 2);
515                         int y1 = next_y1;
516                         int y2 = next_y2;
518 // A different algorithm has to be used if it's 1 sample per pixel and the
519 // index is used.  Now the min and max values are equal so we join the max samples.
520                         if(mwindow->edl->local_session->zoom_sample == 1)
521                         {
522                                 pixmap->canvas->draw_line(x1 + x - 1, prev_y1, x1 + x, y1, pixmap);
523                         }
524                         else
525                         {
526 // Extend line height if it doesn't connect to previous line
527                                 if(!first_frame)
528                                 {
529                                         if(y1 > prev_y2) y1 = prev_y2 + 1;
530                                         if(y2 < prev_y1) y2 = prev_y1 - 1;
531                                 }
532                                 else
533                                 {
534                                         first_frame = 0;
535                                 }
536                                 pixmap->canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap);
537                         }
538                         current_frame -= index_frames_per_pixel;
539                         x1++;
540                         prev_y1 = next_y1;
541                         prev_y2 = next_y2;
542                         highsample = buffer[bufferposition];
543                         lowsample = buffer[bufferposition + 1];
544                 }
546                 current_frame++;
547                 highsample = MAX(highsample, buffer[bufferposition]);
548                 lowsample = MIN(lowsample, buffer[bufferposition + 1]);
549         }
551 // Get last column
552         if(current_frame)
553         {
554                 y1 = (int)(center_pixel - highsample * mwindow->edl->local_session->zoom_y / 2);
555                 y2 = (int)(center_pixel - lowsample * mwindow->edl->local_session->zoom_y / 2);
556                 pixmap->canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap);
557         }
562         if(!buffer_shared) delete [] buffer;
563         return 0;
566 int IndexFile::close_index()
568         if(file)
569         {
570                 fclose(file);
571                 file = 0;
572         }
575 int IndexFile::remove_index()
577         if(asset->index_status == INDEX_READY || asset->index_status == INDEX_NOTTESTED)
578         {
579                 close_index();
580                 remove(index_filename);
581         }
584 int IndexFile::read_info(Asset *test_asset)
586         if(!test_asset) test_asset = asset;
587         if(test_asset->index_status == INDEX_NOTTESTED)
588         {
589 // read start of index data
590                 fread((char*)&(test_asset->index_start), sizeof(int64_t), 1, file);
592 // read test_asset info from index
593                 char *data;
594                 
595                 data = new char[test_asset->index_start];
596                 fread(data, test_asset->index_start - sizeof(int64_t), 1, file);
597                 data[test_asset->index_start - sizeof(int64_t)] = 0;
598                 FileXML xml;
599                 xml.read_from_string(data);
600                 test_asset->read(&xml);
602                 delete [] data;
603                 if(test_asset->format == FILE_UNKNOWN)
604                 {
605                         return 1;
606                 }
607         }
608         return 0;