add missing GPL information in guicast program files
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / edit.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "asset.h"
23 #include "assets.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "edit.h"
27 #include "edits.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "filexml.h"
31 #include "filesystem.h"
32 #include "labels.h"
33 #include "localsession.h"
34 #include "plugin.h"
35 #include "mainsession.h"
36 #include "strack.h"
37 #include "trackcanvas.h"
38 #include "tracks.h"
39 #include "transition.h"
40 #include <string.h>
41
42
43 Edit::Edit()
44 {
45         reset();
46 }
47
48 Edit::Edit(EDL *edl, Track *track)
49 {
50         reset();
51         this->edl = edl;
52         this->track = track;
53         if(track) this->edits = track->edits;
54         id = EDL::next_id();
55         orig_id = id;
56 }
57
58 Edit::Edit(EDL *edl, Edits *edits)
59 {
60         reset();
61         this->edl = edl;
62         this->edits = edits;
63         if(edits) this->track = edits->track;
64         id = EDL::next_id();
65         orig_id = id;
66 }
67
68 Edit::~Edit()
69 {
70 //printf("Edit::~Edit 1\n");
71         if(transition) delete transition;
72 //printf("Edit::~Edit 2\n");
73 }
74
75 void Edit::reset()
76 {
77         edl = 0;
78         track = 0;
79         edits = 0;
80         startsource = 0;
81         startproject = 0;
82         length = 0;
83         asset = 0;
84         transition = 0;
85         channel = 0;
86         user_title[0] = 0;
87         nested_edl = 0;
88         is_selected = 0;
89         hard_left = 0;
90         hard_right = 0;
91         color = 0;
92         group_id = 0;
93 }
94
95 Indexable* Edit::get_source()
96 {
97         if(asset) return asset;
98         if(nested_edl) return nested_edl;
99         return 0;
100 }
101
102 int Edit::copy(int64_t start,
103         int64_t end,
104         FileXML *file,
105         const char *output_path)
106 {
107 // variables
108 //printf("Edit::copy 1\n");
109
110         int64_t endproject = startproject + length;
111         int result;
112
113         if((startproject >= start && startproject <= end) ||  // startproject in range
114                  (endproject <= end && endproject >= start) ||     // endproject in range
115                  (startproject <= start && endproject >= end))    // range in project
116         {
117 // edit is in range
118                 int64_t startproject_in_selection = startproject; // start of edit in selection in project
119                 int64_t startsource_in_selection = startsource; // start of source in selection in source
120                 //int64_t endsource_in_selection = startsource + length; // end of source in selection
121                 int64_t length_in_selection = length;             // length of edit in selection
122 //printf("Edit::copy 2\n");
123
124                 if(startproject < start)
125                 {         // start is after start of edit in project
126                         int64_t length_difference = start - startproject;
127
128                         startsource_in_selection += length_difference;
129                         startproject_in_selection += length_difference;
130                         length_in_selection -= length_difference;
131                 }
132 //printf("Edit::copy 3\n");
133
134                 if(endproject > end)
135                 {         // end is before end of edit in project
136                         length_in_selection = end - startproject_in_selection;
137                 }
138
139 //printf("Edit::copy 4\n");
140                 if(file)    // only if not counting
141                 {
142                         file->tag.set_title("EDIT");
143                         file->tag.set_property("STARTSOURCE", startsource_in_selection);
144                         file->tag.set_property("CHANNEL", (int64_t)channel);
145                         file->tag.set_property("LENGTH", length_in_selection);
146                         file->tag.set_property("HARD_LEFT", hard_left);
147                         file->tag.set_property("HARD_RIGHT", hard_right);
148                         file->tag.set_property("COLOR", color);
149                         file->tag.set_property("GROUP_ID", group_id);
150                         if(user_title[0]) file->tag.set_property("USER_TITLE", user_title);
151 //printf("Edit::copy 5\n");
152
153                         copy_properties_derived(file, length_in_selection);
154
155                         file->append_tag();
156 //                      file->append_newline();
157 //printf("Edit::copy 6\n");
158
159                         if(nested_edl)
160                         {
161                                 file->tag.set_title("NESTED_EDL");
162                                 file->tag.set_property("SRC", nested_edl->path);
163                                 file->append_tag();
164                                 file->tag.set_title("/NESTED_EDL");
165                                 file->append_tag();
166                                 file->append_newline();
167                         }
168
169                         if(asset)
170                         {
171 //printf("Edit::copy 6 %s\n", asset->path);
172                                 char stored_path[BCTEXTLEN];
173                                 char asset_directory[BCTEXTLEN];
174                                 char output_directory[BCTEXTLEN];
175                                 FileSystem fs;
176
177 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
178                                 fs.extract_dir(asset_directory, asset->path);
179 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
180
181                                 if(output_path)
182                                         fs.extract_dir(output_directory, output_path);
183                                 else
184                                         output_directory[0] = 0;
185 //printf("Edit::copy %s, %s %s, %s\n", asset->path, asset_directory, output_path, output_directory);
186
187                                 if(output_path && !strcmp(asset_directory, output_directory))
188                                         fs.extract_name(stored_path, asset->path);
189                                 else
190                                         strcpy(stored_path, asset->path);
191
192                                 file->tag.set_title("FILE");
193                                 file->tag.set_property("SRC", stored_path);
194                                 file->append_tag();
195                                 file->tag.set_title("/FILE");
196                                 file->append_tag();
197                         }
198
199                         if(transition && startsource_in_selection == startsource)
200                         {
201                                 transition->save_xml(file);
202                         }
203
204 //printf("Edit::copy 7\n");
205                         file->tag.set_title("/EDIT");
206                         file->append_tag();
207                         file->append_newline();
208 //printf("Edit::copy 8\n");
209                 }
210 //printf("Edit::copy 9\n");
211                 result = 1;
212         }
213         else
214         {
215                 result = 0;
216         }
217 //printf("Edit::copy 10\n");
218         return result;
219 }
220
221
222 int64_t Edit::get_source_end(int64_t default_)
223 {
224         return default_;
225 }
226
227 void Edit::insert_transition(char *title)
228 {
229 //printf("Edit::insert_transition this=%p title=%p title=%s\n", this, title, title);
230         delete transition;
231         transition = new Transition(edl, this, title,
232                 track->to_units(edl->session->default_transition_length, 1));
233 }
234
235 void Edit::detach_transition()
236 {
237         delete transition;
238         transition = 0;
239         if( edl->local_session->gang_tracks == GANG_NONE ) return;
240         double pos = track->from_units(startproject);
241         Track *current = edl->tracks->first;
242         for( ; current; current=current->next ) {
243                 if( current == track ) continue;
244                 if( current->data_type != track->data_type ) continue;
245                 if( !current->armed_gang(track) ) continue;
246                 int64_t track_pos = current->to_units(pos, 1);
247                 Edit *edit = current->edits->editof(track_pos, PLAY_FORWARD, 0);
248                 if( !edit ) continue;
249                 double edit_pos = track->from_units(edit->startproject);
250                 if( !edl->equivalent(pos, edit_pos) ) continue;
251                 delete edit->transition;
252                 edit->transition = 0;
253         }
254 }
255
256 int Edit::silence()
257 {
258         return (track->data_type != TRACK_SUBTITLE ?
259                 asset || nested_edl :
260                 *((SEdit *)this)->get_text()) ? 0 : 1;
261 }
262
263 void Edit::set_selected(int v)
264 {
265         if( !group_id ) {
266                 if( v < 0 ) v = !is_selected ? 1 : 0;
267                 int gang = edl->local_session->gang_tracks != GANG_NONE ? 1 : 0;
268                 select_affected_edits(v, gang);
269         }
270         else
271                 edl->tracks->set_group_selected(group_id, v);
272 }
273
274 // gang<0: rest of tracks, gang==0: this track, gang>0: to next master
275 void Edit::select_affected_edits(int v, int gang)
276 {
277         is_selected = v;
278         if( !gang ) return;
279         double position = track->from_units(startproject);
280         for( Track *current=track->next; current; current=current->next ) {
281                 if( gang > 0 && current->master ) break;
282                 if( !current->is_armed() ) continue;
283                 for( Edit *edit=current->edits->first; edit; edit=edit->next ) {
284                         if( edit->silence() ) continue;
285                         double start = current->from_units(edit->startproject);
286                         if( edl->equivalent(start, position) ) {
287                                 edit->is_selected = v;
288                                 break;
289                         }
290                 }
291         }
292 }
293
294
295 void Edit::copy_from(Edit *edit)
296 {
297         this->orig_id = edit->orig_id;
298         this->nested_edl = edl->nested_edls.get_nested(edit->nested_edl);
299         this->asset = edl->assets->update(edit->asset);
300         this->startsource = edit->startsource;
301         this->startproject = edit->startproject;
302         this->length = edit->length;
303         this->hard_left = edit->hard_left;
304         this->hard_right = edit->hard_right;
305         this->color = edit->color;
306         this->group_id = edit->group_id;
307         strcpy (this->user_title, edit->user_title);
308
309         if(edit->transition)
310         {
311                 if(!transition) transition = new Transition(edl,
312                         this,
313                         edit->transition->title,
314                         edit->transition->length);
315                 *this->transition = *edit->transition;
316         }
317         this->channel = edit->channel;
318 }
319
320 void Edit::clone_from(Edit *edit)
321 {
322         copy_from(edit);
323         edit->orig_id = edit->id;
324 }
325
326 void Edit::equivalent_output(Edit *edit, int64_t *result)
327 {
328 // End of edit changed
329         if(startproject + length != edit->startproject + edit->length)
330         {
331                 int64_t new_length = MIN(startproject + length,
332                         edit->startproject + edit->length);
333                 if(*result < 0 || new_length < *result)
334                         *result = new_length;
335         }
336
337         if(
338 // Different nested EDLs
339                 (edit->nested_edl && !nested_edl) ||
340                 (!edit->nested_edl && nested_edl) ||
341 // Different assets
342                 (edit->asset == 0 && asset != 0) ||
343                 (edit->asset != 0 && asset == 0) ||
344 // different transitions
345                 (edit->transition == 0 && transition != 0) ||
346                 (edit->transition != 0 && transition == 0) ||
347 // Position changed
348                 (startproject != edit->startproject) ||
349                 (startsource != edit->startsource) ||
350 // Transition changed
351                 (transition && edit->transition &&
352                         !transition->identical(edit->transition)) ||
353 // Asset changed
354                 (asset && edit->asset &&
355                         !asset->equivalent(*edit->asset, 1, 1, edl)) ||
356 // Nested EDL changed
357                 (nested_edl && edit->nested_edl &&
358                         strcmp(nested_edl->path, edit->nested_edl->path))
359                 )
360         {
361 // Start of edit changed
362                 if(*result < 0 || startproject < *result) *result = startproject;
363         }
364 }
365
366
367 Edit& Edit::operator=(Edit& edit)
368 {
369 //printf("Edit::operator= called\n");
370         copy_from(&edit);
371         return *this;
372 }
373
374 void Edit::synchronize_params(Edit *edit)
375 {
376         copy_from(edit);
377 }
378
379
380 // Comparison for ResourcePixmap drawing
381 int Edit::identical(Edit &edit)
382 {
383         int result = (this->nested_edl == edit.nested_edl &&
384                 this->asset == edit.asset &&
385                 this->startsource == edit.startsource &&
386                 this->startproject == edit.startproject &&
387                 this->length == edit.length &&
388                 this->transition == edit.transition &&
389                 this->channel == edit.channel);
390         return result;
391 }
392
393 int Edit::operator==(Edit &edit)
394 {
395         return identical(edit);
396 }
397
398 double Edit::frames_per_picon()
399 {
400         return Units::round(picon_w()) / frame_w();
401 }
402
403 double Edit::frame_w()
404 {
405         return track->from_units(1) *
406                 edl->session->sample_rate /
407                 edl->local_session->zoom_sample;
408 }
409
410 double Edit::picon_w()
411 {
412         int w = 0, h = 0;
413         if(asset) {
414                 w = asset->width;
415                 h = asset->height;
416         }
417         else if(nested_edl) {
418                 w = nested_edl->session->output_w;
419                 h = nested_edl->session->output_h;
420         }
421         return w>0 && h>0 ? ((double)track->data_h*w)/h : 0;
422 }
423
424 int Edit::picon_h()
425 {
426         return track->data_h;
427 }
428
429
430 int Edit::dump(FILE *fp)
431 {
432         fprintf(fp,"     EDIT %p\n", this); fflush(fp);
433         fprintf(fp,"      id %d, orig_id %d, nested_edl=%p %s asset=%p %s\n",
434                 id, orig_id, nested_edl, nested_edl ? nested_edl->path : "",
435                 asset, asset ? asset->path : "");
436         fflush(fp);
437         fprintf(fp,"      channel %d, color %08x, hard lt/rt %d/%d"
438                 " group_id %d, is_selected %d\n",
439                 channel, color, hard_left, hard_right, group_id, is_selected);
440         if( transition ) {
441                 fprintf(fp,"      TRANSITION %p\n", transition);
442                 transition->dump(fp);
443         }
444         fprintf(fp,"      startsource %jd startproject %jd length %jd\n",
445                 startsource, startproject, length);
446         fflush(fp);
447         return 0;
448 }
449
450 int Edit::load_properties(FileXML *file, int64_t &startproject)
451 {
452         startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
453         length = file->tag.get_property("LENGTH", (int64_t)0);
454         hard_left = file->tag.get_property("HARD_LEFT", (int64_t)0);
455         hard_right = file->tag.get_property("HARD_RIGHT", (int64_t)0);
456         color = file->tag.get_property("COLOR", 0);
457         group_id = file->tag.get_property("GROUP_ID", group_id);
458         user_title[0] = 0;
459         file->tag.get_property("USER_TITLE", user_title);
460         this->startproject = startproject;
461         load_properties_derived(file);
462         return 0;
463 }
464
465 void Edit::shift(int64_t difference)
466 {
467         startproject += difference;
468 }
469
470 void Edit::trim(int64_t difference)
471 {
472         length += difference;
473         if( startproject < 0 ) {
474                 if( (startsource+=startproject) < 0 ) startsource = 0;
475                 if( (length+=startproject) < 0 ) length = 0;
476                 startproject = 0;
477         }
478         if( startsource < 0 )
479                 startsource = 0;
480         int64_t src_len = get_source_end(INT64_MAX);
481         if( startsource + length > src_len ) {
482                 length = src_len - startsource;
483                 if( length < 0 ) length = 0;
484         }
485         if( length < 0 )
486                 length = 0;
487 }
488
489 int Edit::shift_start(int edit_mode, int64_t newposition, int64_t oldposition,
490         int edit_labels, int edit_autos, int edit_plugins, Edits *trim_edits)
491 {
492         int64_t cut_length = newposition - oldposition;
493         int rest_moved = edit_mode == MOVE_RIPPLE || edit_mode == MOVE_EDGE ? 1 : 0;
494         if( cut_length > length )
495                 cut_length = length;
496         else if( cut_length < -length )
497                 cut_length = -length;
498
499         int64_t start = startproject, end = start + length;
500         Edit *prev = this->previous, *next = this->next;
501         int edits_moved = 0;
502
503         switch( edit_mode ) {
504         case MOVE_RIPPLE:
505                 edits_moved = 1;
506                 startsource += cut_length;
507                 cut_length = -cut_length;
508                 length += cut_length;
509                 for( Edit *edit=next; edit; edit=edit->next )
510                         edit->startproject += cut_length;
511                 break;
512         case MOVE_ROLL:
513                 if( prev && prev->length + cut_length < 0 )
514                         cut_length = -prev->length;
515                 if( prev ) prev->trim(cut_length);
516                 startproject += cut_length;
517                 startsource += cut_length;
518                 length -= cut_length;
519                 break;
520         case MOVE_SLIP:
521                 edits_moved = 1;
522                 startsource -= cut_length;
523                 break;
524         case MOVE_SLIDE:
525                 edits_moved = 1;
526                 if( prev && prev->length + cut_length < 0 )
527                         cut_length = -prev->length;
528                 if( next && next->length - cut_length < 0 )
529                         cut_length = next->length;
530                 if( prev ) prev->trim(cut_length);
531                 startproject += cut_length;
532                 if( next ) {
533                         next->startproject += cut_length;
534                         next->startsource += cut_length;
535                         next->trim(-cut_length);
536                 }
537                 break;
538         case MOVE_EDGE:
539                 edits_moved = 1;
540                 startsource -= cut_length;
541                 length += cut_length;
542                 for( Edit *edit=next; edit; edit=edit->next )
543                         edit->startproject += cut_length;
544                 break;
545         }
546         trim(0);
547         return follow_edits(start, end, cut_length, edits_moved, rest_moved,
548                 edit_labels, edit_autos, edit_plugins, trim_edits);
549 }
550
551 int Edit::shift_end(int edit_mode, int64_t newposition, int64_t oldposition,
552         int edit_labels, int edit_autos, int edit_plugins, Edits *trim_edits)
553 {
554         int64_t cut_length = newposition - oldposition;
555         int rest_moved = edit_mode == MOVE_RIPPLE || edit_mode == MOVE_EDGE ? 1 : 0;
556         if( cut_length > length ) {
557                  if( !rest_moved ) cut_length = length;
558         }
559         else if( cut_length < -length )
560                 cut_length = -length;
561         int64_t start = startproject, end = start + length;
562         Edit *prev = this->previous, *next = this->next;
563         int edits_moved = 0;
564
565         switch( edit_mode ) {
566         case MOVE_RIPPLE:
567         case MOVE_EDGE:
568                 length += cut_length;
569                 for( Edit *edit=next; edit; edit=edit->next )
570                         edit->startproject += cut_length;
571                 break;
572         case MOVE_ROLL:
573                 if( next && next->length - cut_length < 0 )
574                         cut_length = next->length;
575                 length += cut_length;
576                 if( next ) {
577                         next->startproject += cut_length;
578                         next->startsource += cut_length;
579                         next->trim(-cut_length);
580                 }
581                 break;
582         case MOVE_SLIP:
583                 edits_moved = 1;
584                 startsource -= cut_length;
585                 break;
586         case MOVE_SLIDE:
587                 edits_moved = 1;
588                 if( prev && prev->length + cut_length < 0 )
589                         cut_length = -prev->length;
590                 if( next && next->length - cut_length < 0 )
591                         cut_length = next->length;
592                 if( prev ) prev->trim(cut_length);
593                 startproject += cut_length;
594                 if( next ) {
595                         next->startproject += cut_length;
596                         next->startsource += cut_length;
597                         next->trim(-cut_length);
598                 }
599                 break;
600         }
601         trim(0);
602         return follow_edits(start, end, cut_length, edits_moved, rest_moved,
603                 edit_labels, edit_autos, edit_plugins, trim_edits);
604 }
605
606 int Edit::follow_edits(int64_t start, int64_t end, int64_t cut_length,
607                 int edits_moved, int rest_moved, int edit_labels, int edit_autos,
608                 int edit_plugins, Edits *trim_edits)
609 {
610         if( edits_moved && rest_moved ) {
611                 if( edit_labels ) {
612                         double cut_len = track->from_units(cut_length);
613                         double start_pos = edits->track->from_units(start);
614                         if( cut_len < 0 )
615                                 edits->edl->labels->clear(start_pos + cut_len, start_pos, 1);
616                         else if( cut_len > 0 )
617                                 edits->edl->labels->insert(start_pos, cut_len);
618                 }
619                 if( cut_length < 0 )
620                         track->clear(start + cut_length, start,
621                                 0, 0, edit_autos, edit_plugins, trim_edits);
622                 else if( cut_length > 0 ) {
623                         if( edit_autos )
624                                 track->shift_keyframes(start, cut_length);
625                         if( edit_plugins )
626                                 track->shift_effects(start, cut_length, 1, trim_edits);
627                 }
628         }
629         else if( edits_moved ) {
630                 if( edit_labels ) {
631                         double cut_len = track->from_units(cut_length);
632                         double start_pos = edits->track->from_units(start);
633                         edits->edl->labels->insert(start_pos, cut_len);
634                         double end_pos = edits->track->from_units(end);
635                         edits->edl->labels->insert(end_pos + cut_len, -cut_len);
636                 }
637                 if( edit_autos ) {
638                         if( cut_length > 0 ) {
639                                 track->clear(end, end+cut_length, 0, 0, 0, 1, 0);
640                                 track->shift_keyframes(start, cut_length);
641                         }
642                         else if( cut_length < 0 ) {
643                                 track->clear(start+cut_length, start, 0, 0, 0, 1, 0);
644                                 track->shift_keyframes(end+cut_length, -cut_length);
645                         }
646                 }
647                 if( edit_plugins ) {
648                         if( cut_length > 0 ) {
649                                 track->clear(end, end+cut_length, 0, 0, -1, 0, 0);
650                                 track->shift_effects(start, cut_length, 1, 0);
651                         }
652                         else if( cut_length < 0 ) {
653                                 track->clear(start+cut_length, start, 0, 0, -1, 0, 0);
654                                 track->shift_effects(end+cut_length, -cut_length, 1, 0);
655                         }
656                 }
657         }
658         else if( rest_moved ) {
659                 if( edit_labels ) {
660                         double cut_len = track->from_units(cut_length);
661                         double end_pos = edits->track->from_units(end);
662                         if( cut_len < 0 )
663                                 edits->edl->labels->clear(end_pos + cut_len, end_pos, 1);
664                         else if( cut_len > 0 )
665                                 edits->edl->labels->insert(end_pos, cut_len);
666                 }
667                 if( cut_length < 0 )
668                         track->clear(end + cut_length, end,
669                                 0, 0, edit_autos, edit_plugins, trim_edits);
670                 else if( cut_length > 0 ) {
671                         if( edit_autos )
672                                 track->shift_keyframes(end, cut_length);
673                         if( edit_plugins )
674                                 track->shift_effects(end, cut_length, 1, trim_edits);
675                 }
676         }
677         return 0;
678 }
679
680
681
682 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
683 {
684         int64_t left, right, left_unit, right_unit;
685         if(!transition) return 0;
686         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
687
688         if(cursor_x > left && cursor_x < right)
689         {
690 //              transition->popup_transition(cursor_x, cursor_y);
691                 return 1;
692         }
693         return 0;
694 }
695
696 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
697 {
698         int64_t left, right, left_unit, right_unit;
699         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
700
701         int64_t pixel1, pixel2;
702         pixel1 = left;
703         pixel2 = pixel1 + xS(10);
704
705 // test left edit
706 // cursor_x is faked in acanvas
707         if(cursor_x >= pixel1 && cursor_x <= pixel2)
708         {
709                 selection = left_unit;
710                 return 1;     // left handle
711         }
712
713         //int64_t endproject = startproject + length;
714         pixel2 = right;
715         pixel1 = pixel2 - xS(10);
716
717 // test right edit
718         if(cursor_x >= pixel1 && cursor_x <= pixel2)
719         {
720                 selection = right_unit;
721                 return 2;     // right handle
722         }
723         return 0;
724 }
725
726 void Edit::get_title(char *title)
727 {
728         if( user_title[0] ) {
729                 strcpy(title, user_title);
730                 return;
731         }
732         Indexable *idxbl = asset ? (Indexable*)asset : (Indexable*)nested_edl;
733         if( !idxbl ) {
734                 title[0] = 0;
735                 return;
736         }
737         FileSystem fs;
738         fs.extract_name(title, idxbl->path);
739         if( asset || track->data_type == TRACK_AUDIO ) {
740                 char number[BCSTRLEN];
741                 sprintf(number, " #%d", channel + 1);
742                 strcat(title, number);
743         }
744 }
745