summaryrefslogtreecommitdiff
path: root/apps/recorder/recording.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/recorder/recording.c')
-rw-r--r--apps/recorder/recording.c250
1 files changed, 140 insertions, 110 deletions
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 0a21d96566..6a053cd12e 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -30,8 +30,10 @@
30#include "mpeg.h" 30#include "mpeg.h"
31#include "audio.h" 31#include "audio.h"
32#if CONFIG_CODEC == SWCODEC 32#if CONFIG_CODEC == SWCODEC
33#include "pcm_record.h" 33#include "thread.h"
34#include "pcm_playback.h"
34#include "playback.h" 35#include "playback.h"
36#include "enc_config.h"
35#endif 37#endif
36#ifdef HAVE_UDA1380 38#ifdef HAVE_UDA1380
37#include "uda1380.h" 39#include "uda1380.h"
@@ -73,36 +75,40 @@
73 75
74#define PM_HEIGHT ((LCD_HEIGHT >= 72) ? 2 : 1) 76#define PM_HEIGHT ((LCD_HEIGHT >= 72) ? 2 : 1)
75 77
78#if CONFIG_KEYPAD == RECORDER_PAD
76bool f2_rec_screen(void); 79bool f2_rec_screen(void);
77bool f3_rec_screen(void); 80bool f3_rec_screen(void);
81#endif
78 82
79#define MAX_FILE_SIZE 0x7F800000 /* 2 GB - 4 MB */ 83#define MAX_FILE_SIZE 0x7F800000 /* 2 GB - 4 MB */
80 84
81int screen_update = NB_SCREENS; 85int screen_update = NB_SCREENS;
82bool remote_display_on = true; 86bool remote_display_on = true;
83const char* const freq_str[6] =
84{
85 "44.1kHz",
86 "48kHz",
87 "32kHz",
88 "22.05kHz",
89 "24kHz",
90 "16kHz"
91};
92 87
88/** File name creation **/
93#if CONFIG_CODEC == SWCODEC 89#if CONFIG_CODEC == SWCODEC
94#define REC_ENCODER_ID(q) \ 90
95 rec_quality_info_afmt[q] 91#ifdef IF_CNFN_NUM
96#define REC_QUALITY_LABEL(q) \ 92/* current file number to assist in creating unique numbered filenames
97 (audio_formats[REC_ENCODER_ID(q)].label) 93 without actually having to create the file on disk */
98#define REC_FILE_ENDING(q) \ 94static int file_number = -1;
99 (audio_formats[REC_ENCODER_ID(q)].ext) 95#endif /* IF_CNFN_NUM */
100#else 96
97#define REC_FILE_ENDING(rec_format) \
98 (audio_formats[rec_format_afmt[rec_format]].ext_list)
99
100#else /* CONFIG_CODEC != SWCODEC */
101
101/* default record file extension for HWCODEC */ 102/* default record file extension for HWCODEC */
102#define REC_QUALITY_LABEL(q) "MP3" 103#define REC_FILE_ENDING(rec_format) \
103#define REC_FILE_ENDING(q) ".mp3" 104 (audio_formats[AFMT_MPA_L3].ext_list)
104#endif
105 105
106#endif /* CONFIG_CODEC == SWCODEC */
107
108/* path for current file */
109static char path_buffer[MAX_PATH];
110
111/** Automatic Gain Control (AGC) **/
106#ifdef HAVE_AGC 112#ifdef HAVE_AGC
107/* Timing counters: 113/* Timing counters:
108 * peak_time is incremented every 0.2s, every 2nd run of record screen loop. 114 * peak_time is incremented every 0.2s, every 2nd run of record screen loop.
@@ -496,20 +502,24 @@ void adjust_cursor(void)
496 502
497char *rec_create_filename(char *buffer) 503char *rec_create_filename(char *buffer)
498{ 504{
505 char ext[16];
506
499 if(global_settings.rec_directory) 507 if(global_settings.rec_directory)
500 getcwd(buffer, MAX_PATH); 508 getcwd(buffer, MAX_PATH);
501 else 509 else
502 strncpy(buffer, rec_base_directory, MAX_PATH); 510 strncpy(buffer, rec_base_directory, MAX_PATH);
503 511
512 snprintf(ext, sizeof(ext), ".%s",
513 REC_FILE_ENDING(global_settings.rec_format));
504 514
505#ifdef CONFIG_RTC 515#ifdef CONFIG_RTC
506 create_datetime_filename(buffer, buffer, "R", 516 /* We'll wait at least up to the start of the next second so no duplicate
507 REC_FILE_ENDING(global_settings.rec_quality)); 517 names are created */
518 return create_datetime_filename(buffer, buffer, "R", ext, true);
508#else 519#else
509 create_numbered_filename(buffer, buffer, "rec_", 520 return create_numbered_filename(buffer, buffer, "rec_", ext, 4
510 REC_FILE_ENDING(global_settings.rec_quality), 4); 521 IF_CNFN_NUM_(, &file_number));
511#endif 522#endif
512 return buffer;
513} 523}
514 524
515int rec_create_directory(void) 525int rec_create_directory(void)
@@ -557,9 +567,15 @@ static void rec_boost(bool state)
557 567
558/** 568/**
559 * Selects an audio source for recording or playback 569 * Selects an audio source for recording or playback
560 * powers/unpowers related devices. 570 * powers/unpowers related devices and sets up monitoring.
561 * Here because it calls app code and used only for HAVE_RECORDING atm. 571 * Here because it calls app code and used only for HAVE_RECORDING atm.
562 * Would like it in pcm_record.c. 572 * Would like it in pcm_record.c.
573 *
574 * Behaves like a firmware function in that it does not use global settings
575 * to determine the state.
576 *
577 * The order of setting monitoring may need tweaking dependent upon the
578 * selected source to get the smoothest transition.
563 */ 579 */
564#if defined(HAVE_UDA1380) 580#if defined(HAVE_UDA1380)
565#define ac_disable_recording uda1380_disable_recording 581#define ac_disable_recording uda1380_disable_recording
@@ -571,7 +587,13 @@ static void rec_boost(bool state)
571#define ac_set_monitor tlv320_set_monitor 587#define ac_set_monitor tlv320_set_monitor
572#endif 588#endif
573 589
574void rec_set_source(int source, int flags) 590#ifdef HAVE_SPDIF_IN
591#define rec_spdif_set_monitor(m) audio_spdif_set_monitor(m)
592#else
593#define rec_spdif_set_monitor(m)
594#endif
595
596void rec_set_source(int source, unsigned flags)
575{ 597{
576 /* Prevent pops from unneeded switching */ 598 /* Prevent pops from unneeded switching */
577 static int last_source = AUDIO_SRC_PLAYBACK; 599 static int last_source = AUDIO_SRC_PLAYBACK;
@@ -586,7 +608,9 @@ void rec_set_source(int source, int flags)
586 608
587 /** Do power up/down of associated device(s) **/ 609 /** Do power up/down of associated device(s) **/
588 610
611 /** SPDIF **/
589#ifdef HAVE_SPDIF_IN 612#ifdef HAVE_SPDIF_IN
613 /* Always boost for SPDIF */
590 if ((source == AUDIO_SRC_SPDIF) != (source == last_source)) 614 if ((source == AUDIO_SRC_SPDIF) != (source == last_source))
591 rec_boost(source == AUDIO_SRC_SPDIF); 615 rec_boost(source == AUDIO_SRC_SPDIF);
592 616
@@ -595,10 +619,11 @@ void rec_set_source(int source, int flags)
595 both optical in and out is controlled by the same power source, which is 619 both optical in and out is controlled by the same power source, which is
596 the case on H1x0. */ 620 the case on H1x0. */
597 spdif_power_enable((source == AUDIO_SRC_SPDIF) || 621 spdif_power_enable((source == AUDIO_SRC_SPDIF) ||
598 global_settings.spdif_enable); 622 audio_get_spdif_power_setting());
599#endif 623#endif
600#endif 624#endif
601 625
626 /** Tuner **/
602#ifdef CONFIG_TUNER 627#ifdef CONFIG_TUNER
603 /* Switch radio off or on per source and flags. */ 628 /* Switch radio off or on per source and flags. */
604 if (source != AUDIO_SRC_FMRADIO) 629 if (source != AUDIO_SRC_FMRADIO)
@@ -612,12 +637,15 @@ void rec_set_source(int source, int flags)
612 switch (source) 637 switch (source)
613 { 638 {
614 default: /* playback - no recording */ 639 default: /* playback - no recording */
640 source = AUDIO_SRC_PLAYBACK;
641 case AUDIO_SRC_PLAYBACK:
615 pm_playback = true; 642 pm_playback = true;
616 if (source == last_source) 643 if (source == last_source)
617 break; 644 break;
618 ac_disable_recording(); 645 ac_disable_recording();
619 ac_set_monitor(false); 646 ac_set_monitor(false);
620 pcm_rec_mux(0); /* line in */ 647 pcm_rec_mux(0); /* line in */
648 rec_spdif_set_monitor(-1); /* silence it */
621 break; 649 break;
622 650
623 case AUDIO_SRC_MIC: /* recording only */ 651 case AUDIO_SRC_MIC: /* recording only */
@@ -625,6 +653,7 @@ void rec_set_source(int source, int flags)
625 break; 653 break;
626 ac_enable_recording(true); /* source mic */ 654 ac_enable_recording(true); /* source mic */
627 pcm_rec_mux(0); /* line in */ 655 pcm_rec_mux(0); /* line in */
656 rec_spdif_set_monitor(0);
628 break; 657 break;
629 658
630 case AUDIO_SRC_LINEIN: /* recording only */ 659 case AUDIO_SRC_LINEIN: /* recording only */
@@ -632,29 +661,20 @@ void rec_set_source(int source, int flags)
632 break; 661 break;
633 pcm_rec_mux(0); /* line in */ 662 pcm_rec_mux(0); /* line in */
634 ac_enable_recording(false); /* source line */ 663 ac_enable_recording(false); /* source line */
664 rec_spdif_set_monitor(0);
635 break; 665 break;
636 666
637#ifdef HAVE_SPDIF_IN 667#ifdef HAVE_SPDIF_IN
638 case AUDIO_SRC_SPDIF: /* recording only */ 668 case AUDIO_SRC_SPDIF: /* recording only */
639 if (recording) 669 if (source == last_source)
640 { 670 break;
641 /* This was originally done in audio_set_recording_options only */ 671 ac_disable_recording();
642#ifdef HAVE_SPDIF_POWER 672 audio_spdif_set_monitor(1);
643 EBU1CONFIG = global_settings.spdif_enable ? (1 << 2) : 0;
644 /* Input source is EBUin1, Feed-through monitoring if desired */
645#else
646 EBU1CONFIG = (1 << 2);
647 /* Input source is EBUin1, Feed-through monitoring */
648#endif
649 }
650
651 if (source != last_source)
652 uda1380_disable_recording();
653 break; 673 break;
654#endif /* HAVE_SPDIF_IN */ 674#endif /* HAVE_SPDIF_IN */
655 675
656#ifdef HAVE_FMRADIO_IN 676#ifdef HAVE_FMRADIO_IN
657 case AUDIO_SRC_FMRADIO: 677 case AUDIO_SRC_FMRADIO: /* recording and playback */
658 if (!recording) 678 if (!recording)
659 { 679 {
660 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN), 680 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
@@ -687,6 +707,8 @@ void rec_set_source(int source, int flags)
687 tlv320_set_monitor(true); /* analog bypass */ 707 tlv320_set_monitor(true); /* analog bypass */
688 } 708 }
689#endif 709#endif
710
711 rec_spdif_set_monitor(0);
690 break; 712 break;
691/* #elif defined(CONFIG_TUNER) */ 713/* #elif defined(CONFIG_TUNER) */
692/* Have radio but cannot record it */ 714/* Have radio but cannot record it */
@@ -702,33 +724,50 @@ void rec_set_source(int source, int flags)
702} /* rec_set_source */ 724} /* rec_set_source */
703#endif /* CONFIG_CODEC == SWCODEC && !defined(SIMULATOR) */ 725#endif /* CONFIG_CODEC == SWCODEC && !defined(SIMULATOR) */
704 726
705/* steal the mp3 buffer then actually set options */ 727void rec_init_recording_options(struct audio_recording_options *options)
706void rec_set_recording_options(int frequency, int quality, 728{
707 int source, int source_flags, 729 options->rec_source = global_settings.rec_source;
708 int channel_mode, bool editable, 730 options->rec_frequency = global_settings.rec_frequency;
709 int prerecord_time) 731 options->rec_channels = global_settings.rec_channels;
732 options->rec_prerecord_time = global_settings.rec_prerecord_time;
733#if CONFIG_CODEC == SWCODEC
734 options->rec_source_flags = 0;
735 options->enc_config.rec_format = global_settings.rec_format;
736 global_to_encoder_config(&options->enc_config);
737#else
738 options->rec_quality = global_settings.rec_quality;
739 options->rec_editable = global_settings.rec_editable;
740#endif
741}
742
743void rec_set_recording_options(struct audio_recording_options *options)
710{ 744{
711#if CONFIG_CODEC != SWCODEC 745#if CONFIG_CODEC != SWCODEC
712 if (global_settings.rec_prerecord_time) 746 if (global_settings.rec_prerecord_time)
713#endif
714 talk_buffer_steal(); /* will use the mp3 buffer */ 747 talk_buffer_steal(); /* will use the mp3 buffer */
748#endif
749
750#ifdef HAVE_SPDIF_IN
751#ifdef HAVE_SPDIF_POWER
752 audio_set_spdif_power_setting(global_settings.spdif_enable);
753#endif
754#endif
715 755
716#if CONFIG_CODEC == SWCODEC 756#if CONFIG_CODEC == SWCODEC
717 rec_set_source(source, source_flags | SRCF_RECORDING); 757 rec_set_source(options->rec_source,
718#else 758 options->rec_source_flags | SRCF_RECORDING);
719 (void)source_flags;
720#endif 759#endif
721 760
722 audio_set_recording_options(frequency, quality, source, 761 audio_set_recording_options(options);
723 channel_mode, editable, prerecord_time);
724} 762}
725 763
726static char path_buffer[MAX_PATH];
727
728/* steals mp3 buffer, creates unique filename and starts recording */ 764/* steals mp3 buffer, creates unique filename and starts recording */
729void rec_record(void) 765void rec_record(void)
730{ 766{
767#if CONFIG_CODEC != SWCODEC
731 talk_buffer_steal(); /* we use the mp3 buffer */ 768 talk_buffer_steal(); /* we use the mp3 buffer */
769#endif
770 IF_CNFN_NUM_(file_number = -1;) /* Hit disk for number */
732 audio_record(rec_create_filename(path_buffer)); 771 audio_record(rec_create_filename(path_buffer));
733} 772}
734 773
@@ -753,7 +792,6 @@ static void trigger_listener(int trigger_status)
753 case TRIG_GO: 792 case TRIG_GO:
754 if((audio_status() & AUDIO_STATUS_RECORD) != AUDIO_STATUS_RECORD) 793 if((audio_status() & AUDIO_STATUS_RECORD) != AUDIO_STATUS_RECORD)
755 { 794 {
756 talk_buffer_steal(); /* we use the mp3 buffer */
757 rec_record(); 795 rec_record();
758 /* give control to mpeg thread so that it can start 796 /* give control to mpeg thread so that it can start
759 recording */ 797 recording */
@@ -831,6 +869,8 @@ bool recording_screen(bool no_source)
831 ID2P(LANG_GIGABYTE) 869 ID2P(LANG_GIGABYTE)
832 }; 870 };
833 871
872 struct audio_recording_options rec_options;
873
834 global_settings.recscreen_on = true; 874 global_settings.recscreen_on = true;
835 cursor = 0; 875 cursor = 0;
836#if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR) 876#if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR)
@@ -838,35 +878,26 @@ bool recording_screen(bool no_source)
838#endif 878#endif
839 879
840#if CONFIG_CODEC == SWCODEC 880#if CONFIG_CODEC == SWCODEC
841 audio_stop();
842 voice_stop();
843 /* recording_menu gets messed up: so reset talk_menu */ 881 /* recording_menu gets messed up: so reset talk_menu */
844 talk_menu = global_settings.talk_menu; 882 talk_menu = global_settings.talk_menu;
845 global_settings.talk_menu = 0; 883 global_settings.talk_menu = 0;
884 /* audio_init_recording stops anything playing when it takes the audio
885 buffer */
846#else 886#else
847 /* Yes, we use the D/A for monitoring */ 887 /* Yes, we use the D/A for monitoring */
848 peak_meter_enabled = true; 888 peak_meter_enabled = true;
849 peak_meter_playback(true); 889 peak_meter_playback(true);
850#endif 890#endif
851 891
852#if CONFIG_CODEC == SWCODEC
853 audio_init_recording(talk_get_bufsize());
854#else
855 audio_init_recording(0); 892 audio_init_recording(0);
856#endif
857 sound_set_volume(global_settings.volume); 893 sound_set_volume(global_settings.volume);
858 894
859#ifdef HAVE_AGC 895#ifdef HAVE_AGC
860 peak_meter_get_peakhold(&peak_l, &peak_r); 896 peak_meter_get_peakhold(&peak_l, &peak_r);
861#endif 897#endif
862 898
863 rec_set_recording_options(global_settings.rec_frequency, 899 rec_init_recording_options(&rec_options);
864 global_settings.rec_quality, 900 rec_set_recording_options(&rec_options);
865 global_settings.rec_source,
866 0,
867 global_settings.rec_channels,
868 global_settings.rec_editable,
869 global_settings.rec_prerecord_time);
870 901
871 set_gain(); 902 set_gain();
872 settings_apply_trigger(); 903 settings_apply_trigger();
@@ -1025,7 +1056,6 @@ bool recording_screen(bool no_source)
1025 { 1056 {
1026 /* manual recording */ 1057 /* manual recording */
1027 have_recorded = true; 1058 have_recorded = true;
1028 talk_buffer_steal(); /* we use the mp3 buffer */
1029 rec_record(); 1059 rec_record();
1030 last_seconds = 0; 1060 last_seconds = 0;
1031 if (talk_menu) 1061 if (talk_menu)
@@ -1253,16 +1283,10 @@ bool recording_screen(bool no_source)
1253#if CONFIG_CODEC == SWCODEC 1283#if CONFIG_CODEC == SWCODEC
1254 /* reinit after submenu exit */ 1284 /* reinit after submenu exit */
1255 audio_close_recording(); 1285 audio_close_recording();
1256 audio_init_recording(talk_get_bufsize()); 1286 audio_init_recording(0);
1257#endif 1287#endif
1258 rec_set_recording_options( 1288 rec_init_recording_options(&rec_options);
1259 global_settings.rec_frequency, 1289 rec_set_recording_options(&rec_options);
1260 global_settings.rec_quality,
1261 global_settings.rec_source,
1262 0,
1263 global_settings.rec_channels,
1264 global_settings.rec_editable,
1265 global_settings.rec_prerecord_time);
1266 1290
1267 if(rec_create_directory() > 0) 1291 if(rec_create_directory() > 0)
1268 have_recorded = true; 1292 have_recorded = true;
@@ -1739,11 +1763,7 @@ bool recording_screen(bool no_source)
1739 } 1763 }
1740 } /* end while(!done) */ 1764 } /* end while(!done) */
1741 1765
1742#if CONFIG_CODEC == SWCODEC
1743 audio_stat = pcm_rec_status();
1744#else
1745 audio_stat = audio_status(); 1766 audio_stat = audio_status();
1746#endif
1747 if (audio_stat & AUDIO_STATUS_ERROR) 1767 if (audio_stat & AUDIO_STATUS_ERROR)
1748 { 1768 {
1749 gui_syncsplash(0, true, str(LANG_SYSFONT_DISK_FULL)); 1769 gui_syncsplash(0, true, str(LANG_SYSFONT_DISK_FULL));
@@ -1804,11 +1824,22 @@ bool recording_screen(bool no_source)
1804#if CONFIG_KEYPAD == RECORDER_PAD 1824#if CONFIG_KEYPAD == RECORDER_PAD
1805bool f2_rec_screen(void) 1825bool f2_rec_screen(void)
1806{ 1826{
1827 static const char* const freq_str[6] =
1828 {
1829 "44.1kHz",
1830 "48kHz",
1831 "32kHz",
1832 "22.05kHz",
1833 "24kHz",
1834 "16kHz"
1835 };
1836
1807 bool exit = false; 1837 bool exit = false;
1808 bool used = false; 1838 bool used = false;
1809 int w, h, i; 1839 int w, h, i;
1810 char buf[32]; 1840 char buf[32];
1811 int button; 1841 int button;
1842 struct audio_recording_options rec_options;
1812 1843
1813 FOR_NB_SCREENS(i) 1844 FOR_NB_SCREENS(i)
1814 { 1845 {
@@ -1919,13 +1950,8 @@ bool f2_rec_screen(void)
1919 } 1950 }
1920 } 1951 }
1921 1952
1922 rec_set_recording_options(global_settings.rec_frequency, 1953 rec_init_recording_options(&rec_options);
1923 global_settings.rec_quality, 1954 rec_set_recording_options(&rec_options);
1924 global_settings.rec_source,
1925 0,
1926 global_settings.rec_channels,
1927 global_settings.rec_editable,
1928 global_settings.rec_prerecord_time);
1929 1955
1930 set_gain(); 1956 set_gain();
1931 1957
@@ -1948,6 +1974,8 @@ bool f3_rec_screen(void)
1948 str(LANG_SYSFONT_RECORDING_SRC_LINE), 1974 str(LANG_SYSFONT_RECORDING_SRC_LINE),
1949 str(LANG_SYSFONT_RECORDING_SRC_DIGITAL) 1975 str(LANG_SYSFONT_RECORDING_SRC_DIGITAL)
1950 }; 1976 };
1977 struct audio_recording_options rec_options;
1978
1951 FOR_NB_SCREENS(i) 1979 FOR_NB_SCREENS(i)
1952 { 1980 {
1953 screens[i].setfont(FONT_SYSFIXED); 1981 screens[i].setfont(FONT_SYSFIXED);
@@ -2019,13 +2047,8 @@ bool f3_rec_screen(void)
2019 } 2047 }
2020 } 2048 }
2021 2049
2022 rec_set_recording_options(global_settings.rec_frequency, 2050 rec_init_recording_options(&rec_options);
2023 global_settings.rec_quality, 2051 rec_set_recording_options(&rec_options);
2024 global_settings.rec_source,
2025 0,
2026 global_settings.rec_channels,
2027 global_settings.rec_editable,
2028 global_settings.rec_prerecord_time);
2029 2052
2030 set_gain(); 2053 set_gain();
2031 2054
@@ -2066,23 +2089,30 @@ unsigned long audio_num_recorded_bytes(void)
2066} 2089}
2067 2090
2068#if CONFIG_CODEC == SWCODEC 2091#if CONFIG_CODEC == SWCODEC
2069void rec_set_source(int source, int flags) 2092void rec_set_source(int source, unsigned flags)
2070{ 2093{
2071 source = source; 2094 source = source;
2072 flags = flags; 2095 flags = flags;
2073} 2096}
2074#endif
2075 2097
2076void audio_set_recording_options(int frequency, int quality, 2098#ifdef HAVE_SPDIF_IN
2077 int source, int channel_mode, 2099#ifdef HAVE_SPDIF_POWER
2078 bool editable, int prerecord_time) 2100void audio_set_spdif_power_setting(bool on)
2079{ 2101{
2080 frequency = frequency; 2102 on = on;
2081 quality = quality; 2103}
2082 source = source; 2104
2083 channel_mode = channel_mode; 2105bool audio_get_spdif_power_setting(void)
2084 editable = editable; 2106{
2085 prerecord_time = prerecord_time; 2107 return true;
2108}
2109#endif /* HAVE_SPDIF_POWER */
2110#endif /* HAVE_SPDIF_IN */
2111#endif /* CONFIG_CODEC == SWCODEC */
2112
2113void audio_set_recording_options(struct audio_recording_options *options)
2114{
2115 options = options;
2086} 2116}
2087 2117
2088void audio_set_recording_gain(int left, int right, int type) 2118void audio_set_recording_gain(int left, int right, int type)
@@ -2104,7 +2134,7 @@ void audio_resume_recording(void)
2104{ 2134{
2105} 2135}
2106 2136
2107void pcm_rec_get_peaks(int *left, int *right) 2137void pcm_calculate_rec_peaks(int *left, int *right)
2108{ 2138{
2109 if (left) 2139 if (left)
2110 *left = 0; 2140 *left = 0;