r1009: Move the dependencies to newer package names
[cinelerra_cv/mob.git] / cinelerra / indexfile.C
blobca2f298c00583dfb7f659f3ae6a696116ccf7c32
1 #include "asset.h"
2 #include "bcsignals.h"
3 #include "clip.h"
4 #include "condition.h"
5 #include "edit.h"
6 #include "edl.h"
7 #include "edlsession.h"
8 #include "filexml.h"
9 #include "filesystem.h"
10 #include "errorbox.h"
11 #include "file.h"
12 #include "indexfile.h"
13 #include "indexthread.h"
14 #include "language.h"
15 #include "localsession.h"
16 #include "mainprogress.h"
17 #include "mwindow.h"
18 #include "mwindowgui.h"
19 #include "preferences.h"
20 #include "resourcepixmap.h"
21 #include "theme.h"
22 #include "bctimer.h"
23 #include "trackcanvas.h"
24 #include "tracks.h"
25 #include <unistd.h>
26 #include "vframe.h"
28 // Use native sampling rates for files so the same index can be used in
29 // multiple projects.
31 IndexFile::IndexFile(MWindow *mwindow)
33 //printf("IndexFile::IndexFile 1\n");
34         this->mwindow = mwindow;
35 //printf("IndexFile::IndexFile 2\n");
36         file = 0;
37         interrupt_flag = 0;
38         redraw_timer = new Timer;
41 IndexFile::IndexFile(MWindow *mwindow, Asset *asset)
43 //printf("IndexFile::IndexFile 2\n");
44         file = 0;
45         this->mwindow = mwindow;
46         this->asset = asset;
47         interrupt_flag = 0;
48         redraw_timer = new Timer;
51 IndexFile::~IndexFile()
53 //printf("IndexFile::~IndexFile 1\n");
54         delete redraw_timer;
57 int IndexFile::open_index(Asset *asset)
59 // use buffer if being built
60         this->asset = asset;
61         int result = 0;
63         if(asset->index_status == INDEX_BUILDING)
64         {
65 // use buffer
66                 result = 0;
67         }
68         else
69         if(!(result = open_file()))
70         {
71 // opened existing file
72                 if(read_info())
73                 {
74                         result = 1;
75                         close_index();
76                 }
77                 else
78                 {
79                         asset->index_status = INDEX_READY;
80                 }
81         }
82         else
83         {
84                 result = 1;
85         }
87         return result;
90 int IndexFile::open_index(MWindow *mwindow, Asset *asset)
92         return open_index(asset);
95 void IndexFile::delete_index(Preferences *preferences, Asset *asset)
97         char index_filename[BCTEXTLEN];
98         char source_filename[BCTEXTLEN];
99         get_index_filename(source_filename, 
100                 preferences->index_directory,
101                 index_filename, 
102                 asset->path);
103 //printf("IndexFile::delete_index %s %s\n", source_filename, index_filename);
104         remove(index_filename);
107 int IndexFile::open_file()
109         int result = 0;
110         get_index_filename(source_filename, 
111                 mwindow->preferences->index_directory,
112                 index_filename, 
113                 asset->path);
115         if(file = fopen(index_filename, "rb"))
116         {
117 // Index file already exists.
118 // Get its last size without changing the status.
119                 Asset *test_asset = new Asset;
120                 *test_asset = *asset;
121                 read_info(test_asset);
123                 FileSystem fs;
124                 if(fs.get_date(index_filename) < fs.get_date(test_asset->path))
125                 {
126 // index older than source
127                         result = 2;
128                         fclose(file);
129                         file = 0;
130                 }
131                 else
132                 if(fs.get_size(asset->path) != test_asset->index_bytes)
133                 {
134 // source file is a different size than index source file
135                         result = 2;
136                         fclose(file);   
137                         file = 0;
138                 }
139                 else
140                 {
141                         fseek(file, 0, SEEK_END);
142                         file_length = ftell(file);
143                         fseek(file, 0, SEEK_SET);
144                         result = 0;
145                 }
146                 Garbage::delete_object(test_asset);
147         }
148         else
149         {
150 // doesn't exist
151                 result = 1;
152         }
154         return result;
157 int IndexFile::open_source(File *source)
159 //printf("IndexFile::open_source %p %s\n", asset, asset->path);
160         if(source->open_file(mwindow->preferences, 
161                 asset, 
162                 1, 
163                 0, 
164                 0, 
165                 0))
166         {
167                 //printf("IndexFile::open_source() Couldn't open %s.\n", asset->path);
168                 return 1;
169         }
170         else
171         {
172                 FileSystem fs;
173                 asset->index_bytes = fs.get_size(asset->path);
174                 return 0;
175         }
178 int64_t IndexFile::get_required_scale(File *source)
180         int64_t result = 1;
181 // total length of input file
182         int64_t length_source = source->get_audio_length(0);  
184 // get scale of index file
185 //      if(length_source > mwindow->preferences->index_size)
186 //      {
187 // Total peaks which may be stored in buffer
188                 int64_t peak_count = mwindow->preferences->index_size / (2 * sizeof(float) * asset->channels);
189                 for(result = 1; 
190                         length_source / result > peak_count; 
191                         result *= 2)
192                         ;
193 //      }
194 //      else
195 //      {
196 // too small to build an index for
197 //              result = 0;
198 //      }
200 // Takes too long to draw from source on a CDROM.  Make indexes for
201 // everything.
203         return result;
206 int IndexFile::get_index_filename(char *source_filename, 
207         char *index_directory, 
208         char *index_filename, 
209         char *input_filename)
211 // Replace slashes and dots
212         int i, j;
213         int len = strlen(input_filename);
214         for(i = 0, j = 0; i < len; i++)
215         {
216                 if(input_filename[i] != '/' &&
217                         input_filename[i] != '.')
218                         source_filename[j++] = input_filename[i];
219                 else
220                 {
221                         if(i > 0)
222                                 source_filename[j++] = '_';
223                 }
224         }
225         source_filename[j] = 0;
226         FileSystem fs;
227         fs.join_names(index_filename, index_directory, source_filename);
228         strcat(index_filename, ".idx");
229         return 0;
232 int IndexFile::interrupt_index()
234         interrupt_flag = 1;
235         return 0;
238 // Read data into buffers
240 int IndexFile::create_index(Asset *asset, MainProgressBar *progress)
242         int result = 0;
243         this->mwindow = mwindow;
244         this->asset = asset;
245         interrupt_flag = 0;
247 // open the source file
248         File source;
249         if(open_source(&source)) return 1;
252         get_index_filename(source_filename, 
253                 mwindow->preferences->index_directory, 
254                 index_filename, 
255                 asset->path);
258 // Test for index in stream table of contents
259         if(!source.get_index(index_filename))
260         {
261 printf("IndexFile::create_index 1\n");
262                 redraw_edits(1);
263         }
264         else
265 // Build index from scratch
266         {
271                 asset->index_zoom = get_required_scale(&source);
273 // Indexes are now built for everything since it takes too long to draw
274 // from CDROM source.
277 // total length of input file
278                 int64_t length_source = source.get_audio_length(0);  
280 // get amount to read at a time in floats
281                 int64_t buffersize = 65536;
282                 char string[BCTEXTLEN];
283                 sprintf(string, _("Creating %s."), index_filename);
285                 progress->update_title(string);
286                 progress->update_length(length_source);
287                 redraw_timer->update();
289 // thread out index thread
290                 IndexThread *index_thread = new IndexThread(mwindow, 
291                         this, 
292                         asset, 
293                         index_filename, 
294                         buffersize, 
295                         length_source);
296                 index_thread->start_build();
298 // current sample in source file
299                 int64_t position = 0;
300                 int64_t fragment_size = buffersize;
301                 int current_buffer = 0;
304 // pass through file once
305                 while(position < length_source && !result)
306                 {
307                         if(length_source - position < fragment_size && fragment_size == buffersize) fragment_size = length_source - position;
309                         index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 1");
310                         index_thread->input_len[current_buffer] = fragment_size;
312                         int cancelled = progress->update(position);
313                         if(cancelled || 
314                                 index_thread->interrupt_flag || 
315                                 interrupt_flag)
316                         {
317                                 result = 3;
318                         }
320                         for(int channel = 0; !result && channel < asset->channels; channel++)
321                         {
322                                 source.set_audio_position(position, 0);
323                                 source.set_channel(channel);
325 // Read from source file
326                                 if(source.read_samples(index_thread->buffer_in[current_buffer][channel], 
327                                         fragment_size,
328                                         0)) result = 1;
329                         }
331 // Release buffer to thread
332                         if(!result)
333                         {
334                                 index_thread->output_lock[current_buffer]->unlock();
335                                 current_buffer++;
336                                 if(current_buffer >= TOTAL_BUFFERS) current_buffer = 0;
337                                 position += fragment_size;
338                         }
339                         else
340                         {
341                                 index_thread->input_lock[current_buffer]->unlock();
342                         }
343                 }
345 // end thread cleanly
346                 index_thread->input_lock[current_buffer]->lock("IndexFile::create_index 2");
347                 index_thread->last_buffer[current_buffer] = 1;
348                 index_thread->output_lock[current_buffer]->unlock();
349                 index_thread->stop_build();
351                 delete index_thread;
352         }
355         source.close_file();
359         open_index(asset);
361         close_index();
363         mwindow->edl->set_index_file(asset);
364         return 0;
368 int IndexFile::create_index(MWindow *mwindow, 
369                 Asset *asset, 
370                 MainProgressBar *progress)
372         return create_index(asset, progress);
377 int IndexFile::redraw_edits(int force)
379         int64_t difference = redraw_timer->get_scaled_difference(1000);
381         if(difference > 250 || force)
382         {
383                 redraw_timer->update();
384 // Can't lock window here since the window is only redrawn when the pixel
385 // count changes.
386                 mwindow->gui->lock_window("IndexFile::redraw_edits");
387                 mwindow->edl->set_index_file(asset);
388                 mwindow->gui->canvas->draw_indexes(asset);
389                 asset->old_index_end = asset->index_end;
390                 mwindow->gui->unlock_window();
391         }
392         return 0;
398 int IndexFile::draw_index(ResourcePixmap *pixmap, Edit *edit, int x, int w)
400 // check against index_end when being built
401         if(asset->index_zoom == 0)
402         {
403                 printf(_("IndexFile::draw_index: index has 0 zoom\n"));
404                 return 0;
405         }
407 // test channel number
408         if(edit->channel > asset->channels) return 1;
410 // calculate a virtual x where the edit_x should be in floating point
411         double virtual_edit_x = 1.0 * edit->track->from_units(edit->startproject) * 
412                         mwindow->edl->session->sample_rate /
413                         mwindow->edl->local_session->zoom_sample - 
414                         mwindow->edl->local_session->view_start;
416 // samples in segment to draw relative to asset
417         double asset_over_session = (double)edit->asset->sample_rate / 
418                 mwindow->edl->session->sample_rate;
419 //      int64_t startsource = (int64_t)(((pixmap->pixmap_x - pixmap->edit_x + x) * 
420         int64_t startsource = (int64_t)(((pixmap->pixmap_x - virtual_edit_x + x) * 
421                 mwindow->edl->local_session->zoom_sample + 
422                 edit->startsource) * 
423                 asset_over_session);
424 // just in case we get a numerical error 
425         if (startsource < 0) startsource = 0;
426         int64_t length = (int64_t)(w * 
427                 mwindow->edl->local_session->zoom_sample * 
428                 asset_over_session);
430         if(asset->index_status == INDEX_BUILDING)
431         {
432                 if(startsource + length > asset->index_end)
433                         length = asset->index_end - startsource;
434         }
436 // length of index to read in samples * 2
437         int64_t lengthindex = length / asset->index_zoom * 2;
438 // start of data in samples
439         int64_t startindex = startsource / asset->index_zoom * 2;  
440 // Clamp length of index to read by available data
441         if(startindex + lengthindex > asset->get_index_size(edit->channel))
442                 lengthindex = asset->get_index_size(edit->channel) - startindex;
443         if(lengthindex <= 0) return 0;
448 // Actual length read from file in bytes
449         int64_t length_read;   
450 // Start and length of fragment to read from file in bytes.
451         int64_t startfile, lengthfile;
452         float *buffer = 0;
453         int buffer_shared = 0;
454         int i;
455         int center_pixel = mwindow->edl->local_session->zoom_track / 2;
456         if(mwindow->edl->session->show_titles) center_pixel += mwindow->theme->get_image("title_bg_data")->get_h();
457         int miny = center_pixel - mwindow->edl->local_session->zoom_track / 2;
458         int maxy = center_pixel + mwindow->edl->local_session->zoom_track / 2;
459         int x1 = 0, y1, y2;
460 // get zoom_sample relative to index zoomx
461         double index_frames_per_pixel = mwindow->edl->local_session->zoom_sample / 
462                 asset->index_zoom * 
463                 asset_over_session;
465 // get channel offset
466         startindex += asset->get_index_offset(edit->channel);
469         if(asset->index_status == INDEX_BUILDING)
470         {
471 // index is in RAM, being built
472                 buffer = &(asset->index_buffer[startindex]);
473                 buffer_shared = 1;
474         }
475         else
476         {
477 // index is stored in a file
478                 buffer = new float[lengthindex + 1];
479                 buffer_shared = 0;
480                 startfile = asset->index_start + startindex * sizeof(float);
481                 lengthfile = lengthindex * sizeof(float);
482                 length_read = 0;
484                 if(startfile < file_length)
485                 {
486                         fseek(file, startfile, SEEK_SET);
488                         length_read = lengthfile;
489                         if(startfile + length_read > file_length)
490                                 length_read = file_length - startfile;
492                         fread(buffer, length_read + sizeof(float), 1, file);
493                 }
495                 if(length_read < lengthfile)
496                         for(i = length_read / sizeof(float); 
497                                 i < lengthfile / sizeof(float); 
498                                 i++)
499                                 buffer[i] = 0;
500         }
504         pixmap->canvas->set_color(mwindow->theme->audio_color);
507         double current_frame = 0;
508         float highsample = buffer[0];
509         float lowsample = buffer[1];
510         int prev_y1 = center_pixel;
511         int prev_y2 = center_pixel;
512         int first_frame = 1;
514         for(int bufferposition = 0; 
515                 bufferposition < lengthindex; 
516                 bufferposition += 2)
517         {
518                 if(current_frame >= index_frames_per_pixel)
519                 {
520                         int next_y1 = (int)(center_pixel - highsample * mwindow->edl->local_session->zoom_y / 2);
521                         int next_y2 = (int)(center_pixel - lowsample * mwindow->edl->local_session->zoom_y / 2);
522                         int y1 = next_y1;
523                         int y2 = next_y2;
525 // A different algorithm has to be used if it's 1 sample per pixel and the
526 // index is used.  Now the min and max values are equal so we join the max samples.
527                         if(mwindow->edl->local_session->zoom_sample == 1)
528                         {
529                                 pixmap->canvas->draw_line(x1 + x - 1, prev_y1, x1 + x, y1, pixmap);
530                         }
531                         else
532                         {
533 // Extend line height if it doesn't connect to previous line
534                                 if(!first_frame)
535                                 {
536                                         if(y1 > prev_y2) y1 = prev_y2 + 1;
537                                         if(y2 < prev_y1) y2 = prev_y1 - 1;
538                                 }
539                                 else
540                                 {
541                                         first_frame = 0;
542                                 }
543                                 pixmap->canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap);
544                         }
545                         current_frame -= index_frames_per_pixel;
546                         x1++;
547                         prev_y1 = next_y1;
548                         prev_y2 = next_y2;
549                         highsample = buffer[bufferposition];
550                         lowsample = buffer[bufferposition + 1];
551                 }
553                 current_frame++;
554                 highsample = MAX(highsample, buffer[bufferposition]);
555                 lowsample = MIN(lowsample, buffer[bufferposition + 1]);
556         }
558 // Get last column
559         if(current_frame)
560         {
561                 y1 = (int)(center_pixel - highsample * mwindow->edl->local_session->zoom_y / 2);
562                 y2 = (int)(center_pixel - lowsample * mwindow->edl->local_session->zoom_y / 2);
563                 pixmap->canvas->draw_line(x1 + x, y1, x1 + x, y2, pixmap);
564         }
569         if(!buffer_shared) delete [] buffer;
570         return 0;
573 int IndexFile::close_index()
575         if(file)
576         {
578                 fclose(file);
580                 file = 0;
581         }
584 int IndexFile::remove_index()
586         if(asset->index_status == INDEX_READY || asset->index_status == INDEX_NOTTESTED)
587         {
588                 close_index();
589                 remove(index_filename);
590         }
593 int IndexFile::read_info(Asset *test_asset)
595         if(!test_asset) test_asset = asset;
596         if(test_asset->index_status == INDEX_NOTTESTED)
597         {
598 // read start of index data
599                 fread((char*)&(test_asset->index_start), sizeof(int64_t), 1, file);
601 // read test_asset info from index
602                 char *data;
603                 
604                 data = new char[test_asset->index_start];
605                 fread(data, test_asset->index_start - sizeof(int64_t), 1, file);
606                 data[test_asset->index_start - sizeof(int64_t)] = 0;
607                 FileXML xml;
608                 xml.read_from_string(data);
609                 test_asset->read(&xml);
611                 delete [] data;
612                 if(test_asset->format == FILE_UNKNOWN)
613                 {
614                         return 1;
615                 }
616         }
617         return 0;