OSDN Git Service

Add support for segment play option (-G, --segment)
authorTAMUKI Shoichi <tamuki@linet.gr.jp>
Sun, 15 May 2016 08:34:21 +0000 (17:34 +0900)
committerTAMUKI Shoichi <tamuki@linet.gr.jp>
Wed, 18 May 2016 20:33:09 +0000 (05:33 +0900)
Play just sub-segment(s) specified by comma separated time segments.
Each time segment defined by dash separated time values of begin-end.
Playing from begin to end.

Signed-off-by: Yotam Medini <yotam.medini@gmail.com>
Signed-off-by: TAMUKI Shoichi <tamuki@linet.gr.jp>
doc/C/timidity.1
doc/ja_JP.eucJP/timidity.1
timidity/mac_qt_a.c
timidity/playmidi.c
timidity/playmidi.h
timidity/readmidi.c
timidity/readmidi.h
timidity/timidity.c

index 7b7488f..d63ac54 100644 (file)
@@ -452,6 +452,30 @@ In the current version of \fBTiMidity++\fP this option is a toggle.
 Toggles fast envelopes.  This option makes \fBTiMidity++\fP faster but
 the release time of the notes are shortened.
 .TP
+.BI "\-G " begin1-end1[,begin2-end2,...]
+.br
+.ns
+.TP
+.BI \-\-segment= begin1-end1[,begin2-end2,...]
+Play just sub-segment(s) specified by comma separated time segments.
+.br
+Each time segment defined by dash separated time values of:
+\fIbegin\fP-\fIend\fP - defaulted to 0-infinity.
+Playing from \fIbegin\fP to \fIend\fP.
+.sp
+Time format: \fI[<minutes>:]<seconds>[.<milliseconds>]\fP
+.sp
+For example:
+.RS
+.TP
+.B \-G12-1:00.500
+Skip 12 seconds and stop at 1 minute and half a second.
+.TP
+.B \-G-5,2:00-
+Play the initial 5 seconds, then skip to 2 minutes from start and play
+till end.
+.RE
+.TP
 .BI "\-g " sec ", \-\-spectrogram=" sec
 Open the Sound\-Spectrogram window.  This option is activated if the
 system has support for the X Window System.
index ca7aaa9..ffad20a 100644 (file)
@@ -468,6 +468,30 @@ C 
 ¡¼¥×¤ÎÊѲ½»þ´Ö¤¬È¾Ê¬¤Ë¤Ê¤ê¹â®¤ËÆ°ºî¤·¤Þ¤¹¡£¤¿¤À¤·¡¤²»¤ÎΩ¤Á¾å¤¬¤ê¤È¥ê
 ¥ê¡¼¥¹»þ´Ö¤¬Ã»¤¯¤Ê¤Ã¤Æ¤·¤Þ¤¤¤Þ¤¹¡£
 .TP
+.BI "\-G " begin1-end1[,begin2-end2,...]
+.br
+.ns
+.TP
+.BI \-\-segment= begin1-end1[,begin2-end2,...]
+¥«¥ó¥Þ¤Ç¶èÀڤä¿»þ´Ö¥»¥°¥á¥ó¥È¤ò»ØÄꤷ¤Æ¡¤Ê£¿ô¤Î¶è´Ö¤ò±éÁÕ¤·¤Þ¤¹¡£
+.br
+¤½¤ì¤¾¤ì¤Î»þ´Ö¥»¥°¥á¥ó¥È¤Ï¡¤\fIbegin\fP-\fIend\fP ¤Î¤è¤¦¤Ë¡¤¥À¥Ã¥·¥å¤Ç
+¶èÀڤä¿»þ´Ö¤Çɽ¤·¤Þ¤¹¡£¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¤0-infinity ¤Ç¤¹¡£
+\fIbegin\fP ¤«¤é \fIend\fP ¤Þ¤Ç¤ò±éÁÕ¤·¤Þ¤¹¡£
+.sp
+»þ´Ö¥Õ¥©¡¼¥Þ¥Ã¥È: \fI[<ʬ>:]<ÉÃ>[.<¥ß¥êÉÃ>]\fP
+.sp
+Îã:
+.RS
+.TP
+.B \-G12-1:00.500
+12 ÉÃ¥¹¥­¥Ã¥×¤·¤Æ±éÁÕ¤ò³«»Ï¤·¡¤1 Ê¬ 00 Éà500 ¤Ç±éÁÕ¤ò½ªÎ»¤·¤Þ¤¹¡£
+.TP
+.B \-G-5,2:00-
+ºÇ½é¤Î 5 Éô֤ò±éÁÕ¤·¤¿¤Î¤Á¡¤³«»Ï¤«¤é 2 Ê¬¥¹¥­¥Ã¥×¤·¤ÆºÇ¸å¤Þ¤Ç±éÁÕ¤·¤Þ
+¤¹¡£
+.RE
+.TP
 .BI "\-g " sec ", \-\-spectrogram=" sec
 ¥µ¥¦¥ó¥É¥¹¥Ú¥¯¥È¥í¥°¥é¥à¤òɽ¼¨¤¹¤ë Window ¤ò³«¤­¤Þ¤¹¡£¤³¤Î¥ª¥×¥·¥ç¥ó¤Ï
 X Window System ¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ë´Ä¶­¤Î¤ß¤ÇÆ°ºî¤·¤Þ¤¹¡£¥µ¥¦¥ó¥É¥¹¥Ú
index aeca714..a2ee43d 100644 (file)
@@ -847,6 +847,8 @@ static void qt_play_event(void *p)
                break;
        case ME_NOTE_STEP:
                break;
+       case ME_CUEPOINT:
+               break;
        case ME_EOT:
                prescan = 0;
                break;
index 8858802..bdca57d 100644 (file)
@@ -285,6 +285,7 @@ static void update_legato_controls(int ch);
 static void set_master_tuning(int);
 static void set_single_note_tuning(int, int, int, int);
 static void set_user_temper_entry(int, int, int);
+static int exec_cue_point(int, int, int);
 void recompute_bank_parameter(int, int);
 
 static void init_voice_filter(int);
@@ -382,6 +383,7 @@ static char *event_name(int type)
        EVENT_NAME(ME_MASTER_VOLUME);
        EVENT_NAME(ME_RESET);
        EVENT_NAME(ME_NOTE_STEP);
+       EVENT_NAME(ME_CUEPOINT);
        EVENT_NAME(ME_TIMESIG);
        EVENT_NAME(ME_KEYSIG);
        EVENT_NAME(ME_TEMPER_KEYSIG);
@@ -8098,6 +8100,9 @@ int play_event(MidiEvent *ev)
                        wrdt->update_events();
                break;
 
+       case ME_CUEPOINT:
+               return exec_cue_point(ch, current_event->a, current_event->b);
+
       case ME_EOT:
        return midi_play_end();
     }
@@ -8243,6 +8248,27 @@ static void set_user_temper_entry(int part, int a, int b)
        }
 }
 
+static int exec_cue_point(int part, int a, int b)
+{
+       static int a0 = 0, b0 = 0;
+       uint32 cur, val;
+       
+       if (part == 0) {
+               a0 = a, b0 = b;
+               return RC_NONE;
+       }
+       if ((cur = current_trace_samples()) == -1)
+               cur = current_sample;
+       aq_flush(1);
+       val = a0 << 24 | b0 << 16 | a << 8 | b;
+       if (val == 0x7fffffff || cur + val >= sample_count)
+               return RC_NEXT;
+       skip_to(cur + val);
+       ctl_updatetime(cur + val);
+       ctl_timestamp();
+       return RC_NONE;
+}
+
 static int play_midi(MidiEvent *eventlist, int32 samples)
 {
     int rc;
index 5e7a5c8..8920ada 100644 (file)
@@ -121,6 +121,7 @@ enum midi_event_t
        ME_MASTER_VOLUME,
        ME_RESET,                               /* Reset and change system mode */
        ME_NOTE_STEP,
+       ME_CUEPOINT,                    /* skip to time segment */
        
        ME_TIMESIG,                             /* Time signature */
        ME_KEYSIG,                              /* Key signature */
index 7aab44a..374b280 100644 (file)
@@ -101,6 +101,7 @@ static char **string_event_table = NULL;
 static int    string_event_table_size = 0;
 int    default_channel_program[256];
 static MidiEvent timesig[256];
+MS_Segment *ms_segments = NULL;
 
 void init_delay_status_gs(void);
 void init_chorus_status_gs(void);
@@ -4339,6 +4340,11 @@ static MidiEvent *groom_list(int32 divisions, int32 *eventsp, int32 *samplesp)
            if(counting_time == 2)
                skip_this_event = 1;
            break;
+
+       case ME_CUEPOINT:
+               if (counting_time == 2)
+                       skip_this_event = 1;
+               break;
         }
 
        /* Recompute time in samples*/
@@ -4595,6 +4601,67 @@ static void insert_note_steps(void)
        }
 }
 
+static int32 compute_smf_at_time(const int32);
+
+static void insert_cue_points(void)
+{
+       MS_Segment *sp;
+       int32 at, t;
+       uint8 a0, b0, a1, b1;
+       
+       for (sp = ms_segments; sp != NULL; sp = sp->next) {
+               if (sp->prev == NULL && sp->begin != 0) {
+                       at = compute_smf_at_time(0);
+                       t = sp->begin * play_mode->rate * midi_time_ratio + 0.5;
+                       a0 = t >> 24, b0 = t >> 16, a1 = t >> 8, b1 = t;
+                       MIDIEVENT(at, ME_CUEPOINT, 0, a0, b0);
+                       MIDIEVENT(at, ME_CUEPOINT, 1, a1, b1);
+               }
+               if (sp->next != NULL) {
+                       at = compute_smf_at_time(sp->end * play_mode->rate);
+                       t = (sp->next->begin - sp->end) * play_mode->rate \
+                                       * midi_time_ratio + 0.5;
+                       a0 = t >> 24, b0 = t >> 16, a1 = t >> 8, b1 = t;
+                       MIDIEVENT(at, ME_CUEPOINT, 0, a0, b0);
+                       MIDIEVENT(at, ME_CUEPOINT, 1, a1, b1);
+               } else if (sp->end != -1) {
+                       at = compute_smf_at_time(sp->end * play_mode->rate);
+                       t = 0x7fffffff;         /* stopper */
+                       a0 = t >> 24, b0 = t >> 16, a1 = t >> 8, b1 = t;
+                       MIDIEVENT(at, ME_CUEPOINT, 0, a0, b0);
+                       MIDIEVENT(at, ME_CUEPOINT, 1, a1, b1);
+               }
+       }
+}
+
+static int32 compute_smf_at_time(const int32 sample)
+{
+       MidiEventList *e;
+       int32 st = 0, tempo = 500000, prev_time = 0;
+       int i;
+       
+       for (i = 0, e = evlist; i < event_count; i++, e = e->next) {
+               st += (double) tempo * play_mode->rate / 1000000 \
+                               / current_file_info->divisions \
+                               * (e->event.time - prev_time) + 0.5;
+               if (st >= sample && e->event.type == ME_NOTE_STEP)
+                       return e->event.time;
+               if (e->event.type == ME_TEMPO)
+                   tempo = e->event.a * 65536 + e->event.b * 256 + e->event.channel;
+               prev_time = e->event.time;
+       }
+       return -1;
+}
+
+void free_ms_segments(void)
+{
+       MS_Segment *sp, *next;
+       
+       for (sp = ms_segments; sp != NULL; sp = next)
+               next = sp->next, free(sp);
+       ms_segments = NULL;
+}
+
 MidiEvent *read_midi_file(struct timidity_file *tf, int32 *count, int32 *sp,
                          char *fn)
 {
@@ -4753,6 +4820,7 @@ MidiEvent *read_midi_file(struct timidity_file *tf, int32 *count, int32 *sp,
 
   grooming:
     insert_note_steps();
+    insert_cue_points();
     ev = groom_list(current_file_info->divisions, count, sp);
     if(ev == NULL)
     {
@@ -6275,6 +6343,7 @@ void remove_channel_layer(int ch)
 void free_readmidi(void)
 {
        reuse_mblock(&mempool);
+       free_ms_segments();
        free_all_midi_file_info();
        free_userdrum();
        free_userinst();
index 73861d4..f88154f 100644 (file)
@@ -113,6 +113,13 @@ struct midi_file_info
     struct timidity_file *pcm_tf;
 };
 
+typedef struct _MS_Segment {
+       FLOAT_T begin;
+       FLOAT_T end;
+       struct _MS_Segment *prev;
+       struct _MS_Segment *next;
+} MS_Segment;
+
 extern int32 readmidi_set_track(int trackno, int rewindp);
 extern void readmidi_add_event(MidiEvent *newev);
 extern void readmidi_add_ctl_event(int32 at, int ch, int control, int val);
@@ -123,6 +130,7 @@ extern int convert_midi_control_change(int chn, int type, int val,
 extern int unconvert_midi_control_change(MidiEvent *ev);
 extern char *readmidi_make_string_event(int type, char *string, MidiEvent *ev,
                                        int cnv);
+extern void free_ms_segments(void);
 extern MidiEvent *read_midi_file(struct timidity_file *mtf,
                                 int32 *count, int32 *sp, char *file_name);
 extern struct midi_file_info *get_midi_file_info(char *filename,int newp);
@@ -140,6 +148,7 @@ extern int dump_current_timesig(MidiEvent *codes, int maxlen);
 
 extern ChannelBitMask quietchannels;
 extern struct midi_file_info *current_file_info;
+extern MS_Segment *ms_segments;
 extern int opt_trace_text_meta_event;
 extern int opt_default_mid;
 extern int opt_system_mid;
index 262ccb1..cf2eeb8 100644 (file)
@@ -151,6 +151,7 @@ enum {
        TIM_OPT_EVIL,
        TIM_OPT_FAST_PAN,
        TIM_OPT_FAST_DECAY,
+       TIM_OPT_SEGMENT,
        TIM_OPT_SPECTROGRAM,
        TIM_OPT_KEYSIG,
        TIM_OPT_HELP,
@@ -219,12 +220,12 @@ const char *optcommands =
 #else
 static const char *optcommands =
 #endif
-               "4A:aB:b:C:c:D:d:E:eFfg:H:hI:i:jK:k:L:M:m:N:"
+               "4A:aB:b:C:c:D:d:E:eFfG:g:H:hI:i:jK:k:L:M:m:N:"
                "O:o:P:p:Q:q:R:S:s:T:t:UV:vW:"
 #ifdef __W32__
                "w:"
 #endif
-               "x:Z:";         /* Only GJlnruXYyz are remain... */
+               "x:Z:";         /* Only JlnruXYyz are remain... */
 #ifdef IA_WINSYN
 const struct option longopts[] = {
 #else
@@ -277,6 +278,7 @@ static const struct option longopts[] = {
        { "fast-panning",           optional_argument, NULL, TIM_OPT_FAST_PAN },
        { "no-fast-decay",          no_argument,       NULL, TIM_OPT_FAST_DECAY },
        { "fast-decay",             optional_argument, NULL, TIM_OPT_FAST_DECAY },
+       { "segment",                required_argument, NULL, TIM_OPT_SEGMENT },
        { "spectrogram",            required_argument, NULL, TIM_OPT_SPECTROGRAM },
        { "force-keysig",           required_argument, NULL, TIM_OPT_KEYSIG },
        { "help",                   optional_argument, NULL, TIM_OPT_HELP },
@@ -433,6 +435,9 @@ static inline int parse_opt_resample(const char *);
 static inline int parse_opt_e(const char *);
 static inline int parse_opt_F(const char *);
 static inline int parse_opt_f(const char *);
+static inline int parse_opt_G(const char *);
+static int parse_segment(MS_Segment *, const char *);
+static int parse_time(FLOAT_T *, const char *);
 static inline int parse_opt_g(const char *);
 static inline int parse_opt_H(const char *);
 __attribute__((noreturn))
@@ -519,8 +524,10 @@ __attribute__((noreturn))
 static inline int parse_opt_fail(const char *);
 static inline int set_value(int *, int, int, int, char *);
 static inline int set_val_i32(int32 *, int32, int32, int32, char *);
-static int parse_val_float_t(FLOAT_T *param, const char *arg, FLOAT_T low, FLOAT_T high, const char *name);
-static inline int set_val_float_t(FLOAT_T *param, FLOAT_T i, FLOAT_T low, FLOAT_T high, const char *name);
+static int parse_val_float_t(FLOAT_T *, const char *, FLOAT_T, FLOAT_T,
+               const char *, int);
+static inline int set_val_float_t(FLOAT_T *, FLOAT_T, FLOAT_T, FLOAT_T,
+               const char *, int);
 static inline int set_channel_flag(ChannelBitMask *, int32, char *);
 static inline int y_or_n_p(const char *);
 static inline int set_flag(int32 *, int32, const char *);
@@ -2617,6 +2624,8 @@ MAIN_INTERFACE int set_tim_opt_short(int c, char *optarg)
        case 'f':
                fast_decay = (fast_decay) ? 0 : 1;
                break;
+       case 'G':
+               return parse_opt_G(optarg);
        case 'g':
                return parse_opt_g(optarg);
        case 'H':
@@ -2790,6 +2799,8 @@ MAIN_INTERFACE int set_tim_opt_long(int c, char *optarg, int index)
                return parse_opt_F(arg);
        case TIM_OPT_FAST_DECAY:
                return parse_opt_f(arg);
+       case TIM_OPT_SEGMENT:
+               return parse_opt_G(arg);
        case TIM_OPT_SPECTROGRAM:
                return parse_opt_g(arg);
        case TIM_OPT_KEYSIG:
@@ -3482,7 +3493,7 @@ static int parse_opt_reverb_freeverb(const char *arg, char type)
        /* scaleroom */
        if (*p && *p != ',') {
                if (parse_val_float_t(&freeverb_scaleroom, p, 0, 10,
-                               "Freeverb scaleroom"))
+                               "Freeverb scaleroom", 1))
                        return 1;
        }
        if ((p = strchr(p, ',')) == NULL)
@@ -3491,7 +3502,7 @@ static int parse_opt_reverb_freeverb(const char *arg, char type)
        /* offsetroom */
        if (*p && *p != ',') {
                if (parse_val_float_t(&freeverb_offsetroom, p, 0, 10,
-                               "Freeverb offsetroom"))
+                               "Freeverb offsetroom", 1))
                        return 1;
        }
        if ((p = strchr(p, ',')) == NULL)
@@ -3602,6 +3613,75 @@ static inline int parse_opt_f(const char *arg)
        return 0;
 }
 
+static inline int parse_opt_G(const char *arg)
+{
+       /* play just sub-segment(s) */
+       MS_Segment *sp;
+       const char *p = arg;
+       
+       ms_segments = (MS_Segment *) safe_malloc(sizeof(MS_Segment));
+       if (parse_segment(ms_segments, p)) {
+               free_ms_segments();
+               return 1;
+       }
+       ms_segments->prev = ms_segments->next = NULL;
+       for (sp = ms_segments; (p = strchr(p, ',')) != NULL; sp = sp->next) {
+               sp->next = (MS_Segment *) safe_malloc(sizeof(MS_Segment));
+               if (parse_segment(sp->next, ++p)) {
+                       free_ms_segments();
+                       return 1;
+               }
+               sp->next->prev = sp, sp->next->next = NULL;
+       }
+       for (sp = ms_segments; sp != NULL; sp = sp->next)
+               if (sp->prev != NULL && sp->begin <= sp->prev->end) {
+                       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Segments must be ordered");
+                       free_ms_segments();
+                       return 1;
+               } else if (sp->end != -1 && sp->begin >= sp->end) {
+                       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Segment time must be ordered");
+                       free_ms_segments();
+                       return 1;
+               }
+       return 0;
+}
+
+static int parse_segment(MS_Segment *seg, const char *p)
+{
+       const char *q;
+       
+       if (*p == '-')
+               seg->begin = 0;
+       else if (parse_time(&seg->begin, p))
+               return 1;
+       p = ((q = strchr(p, '-')) == NULL) ? p + strlen(p) : q + 1;
+       if (*p == ',' || *p == '\0')
+               seg->end = -1;
+       else if (parse_time(&seg->end, p))
+               return 1;
+       return 0;
+}
+
+static int parse_time(FLOAT_T *param, const char *p)
+{
+       const char *p1, *p2;
+       int min;
+       FLOAT_T sec;
+       
+       p1 = ((p1 = strchr(p, ':')) == NULL) ? p + strlen(p) : p1;
+       p2 = ((p2 = strchr(p, '-')) == NULL) ? p + strlen(p) : p2;
+       if (p1 < p2) {
+               if (set_value(&min, atoi(p), 0, 59, "Segment time (min part)"))
+                       return 1;
+               if (parse_val_float_t(&sec, p1 + 1, 0, 59.999,
+                               "Segment time (sec+frac part)", 3))
+                       return 1;
+               *param = min * 60 + sec;
+       } else if (parse_val_float_t(param, p, 0, 3599.999, "Segment time", 3))
+               return 1;
+       return 0;
+}
+
 static inline int parse_opt_g(const char *arg)
 {
 #ifdef SUPPORT_SOUNDSPEC
@@ -3748,6 +3828,12 @@ static int parse_opt_h(const char *arg)
 "Enable "
 #endif
 "fast decay mode (toggle)",
+"  -G         --segment=<begin>-<end>[,<begin2>-<end2>,...]",
+"               Play just sub-segment(s), comma separated segments",
+"               Each segment is dash separated of two time values of:",
+"               <begin>-<end> - defaulted to 0-infinity",
+"               Playing from <begin> to <end>",
+"               Time format: [<minutes>:]<seconds>[.<milliseconds>]",
 #ifdef SUPPORT_SOUNDSPEC
 "  -g sec     --spectrogram=sec",
 "               Open Sound-Spectrogram Window",
@@ -4996,7 +5082,7 @@ static inline int set_val_i32(int32 *param,
 {
        if (i < low || i > high) {
                ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
-                               "%s must be between %ld and %ld", name, low, high);
+                               "%s must be between %d and %d", name, low, high);
                return 1;
        }
        *param = i;
@@ -5004,7 +5090,7 @@ static inline int set_val_i32(int32 *param,
 }
 
 static int parse_val_float_t(FLOAT_T *param,
-               const char *arg, FLOAT_T low, FLOAT_T high, const char *name)
+               const char *arg, FLOAT_T low, FLOAT_T high, const char *name, int f)
 {
        FLOAT_T value;
        char *errp;
@@ -5015,15 +5101,15 @@ static int parse_val_float_t(FLOAT_T *param,
                ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Invalid %s", name);
                return 1;
        }
-       return set_val_float_t(param, value, low, high, name);
+       return set_val_float_t(param, value, low, high, name, f);
 }
 
 static inline int set_val_float_t(FLOAT_T *param,
-               FLOAT_T i, FLOAT_T low, FLOAT_T high, const char *name)
+               FLOAT_T i, FLOAT_T low, FLOAT_T high, const char *name, int f)
 {
        if (i < low || i > high) {
                ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
-                               "%s must be between %.1f and %.1f", name, low, high);
+                               "%s must be between %.*f and %.*f", name, f, low, f, high);
                return 1;
        }
        *param = i;