edl plugin names eng, fix segv for opengl brender, renderfarm rework strategy, perf...
[goodguy/history.git] / cinelerra-5.1 / cinelerra / formattools.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2010-2013 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 "bcsignals.h"
24 #include "clip.h"
25 #include "guicast.h"
26 #include "file.h"
27 #include "filesystem.h"
28 #include "formattools.h"
29 #include "language.h"
30 #include "libdv.h"
31 #include "libmjpeg.h"
32 #include "maxchannels.h"
33 #include "mwindow.h"
34 #include "preferences.h"
35 #include "theme.h"
36 #include "videodevice.inc"
37 #include <string.h>
38 #include <unistd.h>
39 #include <ctype.h>
40
41
42 FormatTools::FormatTools(MWindow *mwindow,
43                                 BC_WindowBase *window,
44                                 Asset *asset)
45 {
46         this->mwindow = mwindow;
47         this->window = window;
48         this->asset = asset;
49         this->plugindb = mwindow->plugindb;
50
51         aparams_button = 0;
52         vparams_button = 0;
53         aparams_thread = 0;
54         vparams_thread = 0;
55         channels_tumbler = 0;
56         audio_switch = 0;
57         video_switch = 0;
58         path_textbox = 0;
59         path_button = 0;
60         path_recent = 0;
61         format_title = 0;
62         format_button = 0;
63         format_text = 0;
64         audio_title = 0;
65         video_title = 0;
66         channels_title = 0;
67         channels_button = 0;
68         multiple_files = 0;
69         w = window->get_w();
70
71         recording = 0;
72         use_brender = 0;
73         do_audio = 0;
74         do_video = 0;
75         prompt_audio = 0;
76         prompt_audio_channels = 0;
77         prompt_video = 0;
78         prompt_video_compression = 0;
79         file_per_label = 0;
80         locked_compressor = 0;
81         video_driver = 0;
82 }
83
84 FormatTools::~FormatTools()
85 {
86         delete path_recent;
87 SET_TRACE
88         delete path_button;
89 SET_TRACE
90         delete path_textbox;
91 SET_TRACE
92         delete format_button;
93 SET_TRACE
94
95         if(aparams_button) delete aparams_button;
96 SET_TRACE
97         if(vparams_button) delete vparams_button;
98 SET_TRACE
99         if(aparams_thread) delete aparams_thread;
100 SET_TRACE
101         if(vparams_thread) delete vparams_thread;
102 SET_TRACE
103         if(channels_tumbler) delete channels_tumbler;
104 }
105
106 void FormatTools::create_objects(
107                 int &init_x, int &init_y,
108                 int do_audio, int do_video,   // Include support for audio, video
109                 int prompt_audio, int prompt_video, // Include checkbox for audio, video
110                 int prompt_audio_channels,
111                 int prompt_video_compression,
112                 const char *locked_compressor,
113                 int recording,
114                 int *file_per_label,
115                 int brender,
116                 int horizontal_layout)
117 {
118         int x = init_x;
119         int y = init_y;
120         int ylev = init_y;
121         int margin = mwindow->theme->widget_border;
122
123         this->locked_compressor = locked_compressor;
124         this->recording = recording;
125         this->use_brender = brender;
126         this->do_audio = do_audio;
127         this->do_video = do_video;
128         this->prompt_audio = prompt_audio;
129         this->prompt_audio_channels = prompt_audio_channels;
130         this->prompt_video = prompt_video;
131         this->prompt_video_compression = prompt_video_compression;
132         this->file_per_label = file_per_label;
133
134 //printf("FormatTools::create_objects 1\n");
135
136         if(!recording)
137         {
138                 int px = x;
139                 window->add_subwindow(path_textbox = new FormatPathText(px, y, this));
140                 px += path_textbox->get_w() + 5;
141                 path_recent = new BC_RecentList("PATH", mwindow->defaults,
142                                         path_textbox, 10, px, y, 300, 100);
143                 window->add_subwindow(path_recent);
144                 path_recent->load_items(File::formattostr(asset->format));
145                 px += path_recent->get_w();
146                 window->add_subwindow(path_button = new BrowseButton(
147                         mwindow->theme, window, path_textbox, px, y, asset->path,
148                         _("Output to file"), _("Select a file to write to:"), 0));
149
150 // Set w for user.
151                 w = MAX(w, 305);
152                 y += path_textbox->get_h() + 10;
153         }
154         else
155         {
156 //              w = x + 305;
157                 w = 305;
158         }
159
160         x = init_x;
161         window->add_subwindow(format_title = new BC_Title(x, y, _("File Format:")));
162         x += format_title->get_w() + margin;
163         window->add_subwindow(format_text = new BC_TextBox(x, y, 160, 1,
164                 File::formattostr(asset->format)));
165         x += format_text->get_w() + margin;
166 //printf("FormatTools::create_objects %d %p\n", __LINE__, window);
167         window->add_subwindow(format_button = new FormatFormat(x, y, this));
168         format_button->create_objects();
169         x += format_button->get_w() + 5;
170         window->add_subwindow(ffmpeg_type = new FFMpegType(x, y, 70, 1, asset->fformat));
171         FFMPEG::set_asset_format(asset, asset->fformat);
172         x += ffmpeg_type->get_w();
173         window->add_subwindow(format_ffmpeg = new FormatFFMPEG(x, y, this));
174         format_ffmpeg->create_objects();
175         x = init_x;
176         y += format_button->get_h() + 10;
177         if( do_audio ) {
178                 window->add_subwindow(audio_title = new BC_Title(x, y, _("Audio:"), LARGEFONT,
179                         BC_WindowBase::get_resources()->audiovideo_color));
180                 x += audio_title->get_w() + margin;
181                 window->add_subwindow(aparams_button = new FormatAParams(mwindow, this, x, y));
182                 x += aparams_button->get_w() + margin;
183                 if(prompt_audio) {
184                         window->add_subwindow(audio_switch = new FormatAudio(x, y, this, asset->audio_data));
185                 }
186                 x = init_x;
187                 ylev = y;
188                 y += aparams_button->get_h() + 10;
189
190 // Audio channels only used for recording.
191 //              if(prompt_audio_channels)
192 //              {
193 //                      window->add_subwindow(channels_title = new BC_Title(x, y, _("Number of audio channels to record:")));
194 //                      x += 260;
195 //                      window->add_subwindow(channels_button = new FormatChannels(x, y, this));
196 //                      x += channels_button->get_w() + 5;
197 //                      window->add_subwindow(channels_tumbler = new BC_ITumbler(channels_button, 1, MAXCHANNELS, x, y));
198 //                      y += channels_button->get_h() + 20;
199 //                      x = init_x;
200 //              }
201
202 //printf("FormatTools::create_objects 6\n");
203                 aparams_thread = new FormatAThread(this);
204         }
205
206 //printf("FormatTools::create_objects 7\n");
207         if( do_video ) {
208                 if( horizontal_layout && do_audio ) {
209                         x += 370;
210                         y = ylev;
211                 }
212
213 //printf("FormatTools::create_objects 8\n");
214                 window->add_subwindow(video_title = new BC_Title(x, y, _("Video:"), LARGEFONT,
215                         BC_WindowBase::get_resources()->audiovideo_color));
216                 x += video_title->get_w() + margin;
217                 if(prompt_video_compression) {
218                         window->add_subwindow(vparams_button = new FormatVParams(mwindow, this, x, y));
219                         x += vparams_button->get_w() + margin;
220                 }
221
222 //printf("FormatTools::create_objects 9\n");
223                 if(prompt_video) {
224                         window->add_subwindow(video_switch = new FormatVideo(x, y, this, asset->video_data));
225                         y += video_switch->get_h();
226                 }
227                 else {
228                         y += vparams_button->get_h();
229                 }
230
231 //printf("FormatTools::create_objects 10\n");
232                 y += 10;
233                 vparams_thread = new FormatVThread(this);
234         }
235
236 //printf("FormatTools::create_objects 11\n");
237
238         x = init_x;
239         if( file_per_label ) {
240                 multiple_files = new FormatMultiple(mwindow, x, y, file_per_label);
241                 window->add_subwindow(multiple_files);
242                 y += multiple_files->get_h() + 10;
243         }
244
245 //printf("FormatTools::create_objects 12\n");
246
247         init_y = y;
248         update_format();
249 }
250
251 void FormatTools::update_driver(int driver)
252 {
253         this->video_driver = driver;
254
255         locked_compressor = 0;
256         switch(driver)
257         {
258                 case CAPTURE_DVB:
259                 case VIDEO4LINUX2MPEG:
260 // Just give the user information about how the stream is going to be
261 // stored but don't change the asset.
262 // Want to be able to revert to user settings.
263                         if(asset->format == FILE_MPEG) break;
264                         asset->format = FILE_MPEG;
265                         format_text->update(File::formattostr(asset->format));
266                         asset->audio_data = 1;
267                         asset->video_data = 1;
268                         audio_switch->update(1);
269                         video_switch->update(1);
270                         break;
271
272                 case CAPTURE_IEC61883:
273                 case CAPTURE_FIREWIRE:
274                 case VIDEO4LINUX2JPEG:
275                 case CAPTURE_JPEG_WEBCAM:
276                         asset->format = FILE_FFMPEG;
277                         format_text->update(File::formattostr(asset->format));
278
279                         switch(driver) {
280                         case CAPTURE_IEC61883:
281                         case CAPTURE_FIREWIRE:
282                                 locked_compressor = (char*)CODEC_TAG_DVSD;
283                                 break;
284
285                         case VIDEO4LINUX2JPEG:
286                                 locked_compressor = (char*)CODEC_TAG_MJPEG;
287                                 break;
288
289                         case CAPTURE_JPEG_WEBCAM:
290                                 locked_compressor = (char*)CODEC_TAG_JPEG;
291                                 break;
292                         }
293                         if( locked_compressor )
294                                 strcpy(asset->vcodec, locked_compressor);
295
296                         audio_switch->update(asset->audio_data);
297                         video_switch->update(asset->video_data);
298                         break;
299
300                 default:
301                         format_text->update(File::formattostr(asset->format));
302                         audio_switch->update(asset->audio_data);
303                         video_switch->update(asset->video_data);
304                         break;
305         }
306         close_format_windows();
307         update_format();
308 }
309
310 void FormatTools::update_format()
311 {
312         if( do_audio && prompt_audio && audio_switch ) {
313                 audio_switch->update(asset->audio_data);
314                 if( File::renders_audio(asset) )
315                         audio_switch->enable();
316                 else
317                         audio_switch->disable();
318         }
319         if( do_video && prompt_video && video_switch ) {
320                 video_switch->update(asset->video_data);
321                 if( File::renders_video(asset) )
322                         video_switch->enable();
323                 else
324                         video_switch->disable();
325         }
326         if( asset->format == FILE_FFMPEG ) {
327                 ffmpeg_type->show();
328                 format_ffmpeg->show();
329         }
330         else {
331                 ffmpeg_type->hide();
332                 format_ffmpeg->hide();
333         }
334 }
335
336 int FormatTools::handle_event()
337 {
338         return 0;
339 }
340
341 Asset* FormatTools::get_asset()
342 {
343         return asset;
344 }
345
346 void FormatTools::update_extension()
347 {
348         const char *extension = File::get_tag(asset->format);
349 // split multiple extensions
350         ArrayList<const char*> extensions;
351         int len = !extension ? -1 : strlen(extension);
352         const char *extension_ptr = extension;
353         for(int i = 0; i <= len; i++)
354         {
355                 if(extension[i] == '/' || extension[i] == 0)
356                 {
357                         extensions.append(extension_ptr);
358                         extension_ptr = extension + i + 1;
359                 }
360         }
361
362         if(extensions.size())
363         {
364                 char *ptr = strrchr(asset->path, '.');
365                 if(!ptr)
366                 {
367                         ptr = asset->path + strlen(asset->path);
368                         *ptr = '.';
369                 }
370                 ptr++;
371
372 // test for equivalent extension
373                 int need_extension = 1;
374                 //int extension_len = 0;
375                 for(int i = 0; i < extensions.size() && need_extension; i++)
376                 {
377                         char *ptr1 = ptr;
378                         extension_ptr = extensions.get(i);
379 // test an extension
380                         need_extension = 0;
381                         while(*ptr1 != 0 && *extension_ptr != 0 && *extension_ptr != '/')
382                         {
383                                 if(tolower(*ptr1) != tolower(*extension_ptr))
384                                 {
385                                         need_extension = 1;
386                                         break;
387                                 }
388                                 ptr1++;
389                                 extension_ptr++;
390                         }
391
392                         if(*ptr1 == 0 &&
393                                 *extension_ptr != 0 &&
394                                 *extension_ptr != '/')
395                                 need_extension = 1;
396                 }
397
398 //printf("FormatTools::update_extension %d %d\n", __LINE__, need_extension);
399 // copy extension
400                 if(need_extension)
401                 {
402                         char *ptr1 = ptr;
403                         extension_ptr = asset->format != FILE_FFMPEG ?
404                                 extensions.get(0) : asset->fformat;
405                         while(*extension_ptr != 0 && *extension_ptr != '/')
406                                 *ptr1++ = *extension_ptr++;
407                         *ptr1 = 0;
408                 }
409
410                 int character1 = ptr - asset->path;
411                 int character2 = strlen(asset->path);
412 //              *(asset->path + character2) = 0;
413                 if(path_textbox)
414                 {
415                         path_textbox->update(asset->path);
416                         path_textbox->set_selection(character1, character2, character2);
417                 }
418         }
419 }
420
421 void FormatTools::update(Asset *asset, int *file_per_label)
422 {
423         this->asset = asset;
424         this->file_per_label = file_per_label;
425         if( file_per_label ) multiple_files->update(file_per_label);
426         if( path_textbox ) path_textbox->update(asset->path);
427         format_text->update(File::formattostr(asset->format));
428         update_format();
429         close_format_windows();
430 }
431
432 void FormatTools::close_format_windows()
433 {
434 // This is done in ~file
435         if( aparams_thread ) {
436                 if( aparams_thread->running() )
437                         aparams_thread->file->close_window();
438                 aparams_thread->join();
439         }
440         if( vparams_thread ) {
441                 if( vparams_thread->running() )
442                         vparams_thread->file->close_window();
443                 vparams_thread->join();
444         }
445 }
446
447 int FormatTools::get_w()
448 {
449         return asset->format != FILE_FFMPEG ? w :
450                 format_ffmpeg->get_x() + format_ffmpeg->get_w();
451 }
452
453 void FormatTools::set_w(int w)
454 {
455         this->w = w;
456 }
457
458 void FormatTools::reposition_window(int &init_x, int &init_y)
459 {
460         int x = init_x;
461         int y = init_y;
462
463         if(path_textbox)
464         {
465                 int px = x;
466                 path_textbox->reposition_window(px, y);
467                 px += path_textbox->get_w() + 5;
468                 path_recent->reposition_window(px, y);
469                 px += path_recent->get_w() + 8;
470                 path_button->reposition_window(px, y);
471                 y += path_textbox->get_h() + 10;
472         }
473
474         format_title->reposition_window(x, y);
475         x += 90;
476         format_text->reposition_window(x, y);
477         x += format_text->get_w();
478         format_button->reposition_window(x, y);
479
480         x = init_x;
481         y += format_button->get_h() + 10;
482
483         if(do_audio)
484         {
485                 audio_title->reposition_window(x, y);
486                 x += 80;
487                 aparams_button->reposition_window(x, y);
488                 x += aparams_button->get_w() + 10;
489                 if(prompt_audio) audio_switch->reposition_window(x, y);
490
491                 x = init_x;
492                 y += aparams_button->get_h() + 10;
493                 if(prompt_audio_channels)
494                 {
495                         channels_title->reposition_window(x, y);
496                         x += 260;
497                         channels_button->reposition_window(x, y);
498                         x += channels_button->get_w() + 5;
499                         channels_tumbler->reposition_window(x, y);
500                         y += channels_button->get_h() + 20;
501                         x = init_x;
502                 }
503         }
504
505
506         if(do_video)
507         {
508                 video_title->reposition_window(x, y);
509                 x += 80;
510                 if(prompt_video_compression)
511                 {
512                         vparams_button->reposition_window(x, y);
513                         x += vparams_button->get_w() + 10;
514                 }
515
516                 if(prompt_video)
517                 {
518                         video_switch->reposition_window(x, y);
519                         y += video_switch->get_h();
520                 }
521                 else
522                 {
523                         y += vparams_button->get_h();
524                 }
525
526                 y += 10;
527                 x = init_x;
528         }
529
530         if( file_per_label ) {
531                 multiple_files->reposition_window(x, y);
532                 y += multiple_files->get_h() + 10;
533         }
534
535         init_y = y;
536 }
537
538
539 int FormatTools::set_audio_options()
540 {
541 //      if(video_driver == CAPTURE_DVB)
542 //      {
543 //              return 0;
544 //      }
545
546         if(!aparams_thread->running())
547         {
548                 aparams_thread->start();
549         }
550         else
551         {
552                 aparams_thread->file->raise_window();
553         }
554         return 0;
555 }
556
557 int FormatTools::set_video_options()
558 {
559 //      if(video_driver == CAPTURE_DVB)
560 //      {
561 //              return 0;
562 //      }
563
564         if(!vparams_thread->running())
565         {
566                 vparams_thread->start();
567         }
568         else
569         {
570                 vparams_thread->file->raise_window();
571         }
572
573         return 0;
574 }
575
576
577
578
579
580 FormatAParams::FormatAParams(MWindow *mwindow, FormatTools *format, int x, int y)
581  : BC_Button(x, y, mwindow->theme->get_image_set("wrench"))
582 {
583         this->format = format;
584         set_tooltip(_("Configure audio compression"));
585 }
586
587 FormatAParams::~FormatAParams()
588 {
589 }
590
591 int FormatAParams::handle_event()
592 {
593         format->set_audio_options();
594         return 1;
595 }
596
597
598
599
600
601 FormatVParams::FormatVParams(MWindow *mwindow, FormatTools *format, int x, int y)
602  : BC_Button(x, y, mwindow->theme->get_image_set("wrench"))
603 {
604         this->format = format;
605         set_tooltip(_("Configure video compression"));
606 }
607
608 FormatVParams::~FormatVParams()
609 {
610 }
611
612 int FormatVParams::handle_event()
613 {
614         format->set_video_options();
615         return 1;
616 }
617
618
619
620
621
622 FormatAThread::FormatAThread(FormatTools *format)
623  : Thread(1, 0, 0)
624 {
625         this->format = format;
626         file = new File;
627         joined = 1;
628 }
629
630 FormatAThread::~FormatAThread()
631 {
632         delete file;  file = 0;
633         join();
634 }
635
636 void FormatAThread::start()
637 {
638         join();
639         joined = 0;
640         Thread::start();
641 }
642
643
644 void FormatAThread::run()
645 {
646         file->get_options(format, 1, 0);
647 }
648
649
650
651
652 FormatVThread::FormatVThread(FormatTools *format)
653  : Thread(1, 0, 0)
654 {
655         this->format = format;
656         file = new File;
657         joined = 1;
658 }
659
660 FormatVThread::~FormatVThread()
661 {
662         delete file;  file = 0;
663         join();
664 }
665
666 void FormatVThread::start()
667 {
668         join();
669         joined = 0;
670         Thread::start();
671 }
672
673 void FormatVThread::run()
674 {
675         file->get_options(format, 0, 1);
676 }
677
678
679
680
681
682 FormatPathText::FormatPathText(int x, int y, FormatTools *format)
683  : BC_TextBox(x, y, format->w - x -
684                 2*format->mwindow->theme->get_image_set("wrench")[0]->get_w() - 20, 1,
685         format->asset->path)
686 {
687         this->format = format;
688 }
689
690 FormatPathText::~FormatPathText()
691 {
692 }
693 int FormatPathText::handle_event()
694 {
695         calculate_suggestions();
696         strcpy(format->asset->path, get_text());
697         format->handle_event();
698         return 1;
699 }
700
701
702
703
704 FormatAudio::FormatAudio(int x, int y, FormatTools *format, int default_)
705  : BC_CheckBox(x,
706         y,
707         default_,
708         (char*)(format->recording ? _("Record audio tracks") : _("Render audio tracks")))
709 {
710         this->format = format;
711 }
712
713 FormatAudio::~FormatAudio() {}
714 int FormatAudio::handle_event()
715 {
716         format->asset->audio_data = get_value();
717         return 1;
718 }
719
720
721 FormatVideo::FormatVideo(int x, int y, FormatTools *format, int default_)
722  : BC_CheckBox(x,
723         y,
724         default_,
725         (char*)(format->recording ? _("Record video tracks") : _("Render video tracks")))
726 {
727 this->format = format;
728 }
729
730 FormatVideo::~FormatVideo() {}
731 int FormatVideo::handle_event()
732 {
733         format->asset->video_data = get_value();
734         return 1;
735 }
736
737
738
739
740 FormatFormat::FormatFormat(int x, int y, FormatTools *format)
741  : FormatPopup(x, y, format->do_audio, format->do_video, format->use_brender)
742 {
743         this->format = format;
744 }
745
746 FormatFormat::~FormatFormat()
747 {
748 }
749
750 int FormatFormat::handle_event()
751 {
752         BC_ListBoxItem *selection = get_selection(0, 0);
753         if( selection ) {
754                 int new_format = File::strtoformat(get_selection(0, 0)->get_text());
755 //              if(new_format != format->asset->format)
756                 {
757                         Asset *asset = format->asset;
758                         asset->format = new_format;
759                         asset->audio_data = File::renders_audio(asset);
760                         asset->video_data = File::renders_video(asset);
761                         asset->ff_audio_options[0] = 0;
762                         asset->ff_video_options[0] = 0;
763                         format->format_text->update(selection->get_text());
764                         if( !format->use_brender )
765                                 format->update_extension();
766                         format->close_format_windows();
767                         if (format->path_recent) format->path_recent->
768                                 load_items(File::formattostr(format->asset->format));
769                         format->update_format();
770                 }
771         }
772         return 1;
773 }
774
775
776 FormatFFMPEG::FormatFFMPEG(int x, int y, FormatTools *format)
777  : FFMPEGPopup(format->plugindb, x, y)
778 {
779         this->format = format;
780 }
781
782 FormatFFMPEG::~FormatFFMPEG()
783 {
784 }
785
786 int FormatFFMPEG::handle_event()
787 {
788         BC_ListBoxItem *selection = get_selection(0, 0);
789         if( selection ) {
790                 char *text = get_selection(0, 0)->get_text();
791                 format->ffmpeg_type->update(text);
792                 format->asset->ff_audio_options[0] = 0;
793                 format->asset->ff_video_options[0] = 0;
794                 FFMPEG::set_asset_format(format->asset, text);
795                 format->update_extension();
796                 format->close_format_windows();
797                 format->update_format();
798         }
799         return 1;
800 }
801
802
803
804
805 FormatChannels::FormatChannels(int x, int y, FormatTools *format)
806  : BC_TextBox(x, y, 100, 1, format->asset->channels)
807 {
808         this->format = format;
809 }
810
811 FormatChannels::~FormatChannels()
812 {
813 }
814
815 int FormatChannels::handle_event()
816 {
817         format->asset->channels = atol(get_text());
818         return 1;
819 }
820
821
822 FormatToTracks::FormatToTracks(int x, int y, int *output)
823  : BC_CheckBox(x, y, *output, _("Overwrite project with output"))
824 {
825         this->output = output;
826 }
827
828 FormatToTracks::~FormatToTracks()
829 {
830 }
831
832 int FormatToTracks::handle_event()
833 {
834         *output = get_value();
835         return 1;
836 }
837
838
839 FormatMultiple::FormatMultiple(MWindow *mwindow, int x, int y, int *output)
840  : BC_CheckBox(x, y, *output, _("Create new file at each label"))
841 {
842         this->output = output;
843         this->mwindow = mwindow;
844 }
845
846 FormatMultiple::~FormatMultiple()
847 {
848 }
849
850 int FormatMultiple::handle_event()
851 {
852         *output = get_value();
853         return 1;
854 }
855
856 void FormatMultiple::update(int *output)
857 {
858         this->output = output;
859         set_value(*output ? 1 : 0);
860 }
861
862