~ubuntu-branches/ubuntu/hoary/kdemultimedia/hoary

« back to all changes in this revision

Viewing changes to kmidi/TIMIDITY/tk_c.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Schulze
  • Date: 2003-01-22 15:00:51 UTC
  • Revision ID: james.westby@ubuntu.com-20030122150051-uihwkdoxf15mi1tn
Tags: upstream-2.2.2
ImportĀ upstreamĀ versionĀ 2.2.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*================================================================
 
2
 *
 
3
 * The Tcl/Tk interface for Timidity
 
4
 * written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp)
 
5
 *
 
6
 * Most of the following codes are derived from both motif_ctl.c
 
7
 * and motif_pipe.c.  The communication between Tk program and
 
8
 * timidity is established by a pipe stream as in Motif interface.
 
9
 * On the contrast to motif, the stdin and stdout are assigned
 
10
 * as pipe i/o in Tk interface.
 
11
 *
 
12
 *================================================================*/
 
13
#ifdef TCLTK
 
14
 
 
15
#include <stdio.h>
 
16
#include <stdlib.h>
 
17
#include <unistd.h>
 
18
#include <stdarg.h>
 
19
#include <string.h>
 
20
#include <sys/ioctl.h>
 
21
#include <signal.h>
 
22
#include <sys/ioctl.h>
 
23
#include <sys/ipc.h>
 
24
#include <sys/shm.h>
 
25
#include <tcl.h>
 
26
#include <tk.h>
 
27
 
 
28
#include "config.h"
 
29
#include "common.h"
 
30
#include "instrum.h"
 
31
#include "playmidi.h"
 
32
#include "output.h"
 
33
#include "controls.h"
 
34
 
 
35
 
 
36
static void ctl_refresh(void);
 
37
static void ctl_total_time(uint32 tt);
 
38
static void ctl_master_volume(int mv);
 
39
static void ctl_file_name(char *name);
 
40
static void ctl_current_time(uint32 ct);
 
41
static void ctl_note(int v);
 
42
static void ctl_note_display(void);
 
43
static void ctl_program(int ch, int val, const char *name);
 
44
static void ctl_volume(int ch, int val);
 
45
static void ctl_expression(int ch, int val);
 
46
static void ctl_panning(int ch, int val);
 
47
static void ctl_sustain(int ch, int val);
 
48
static void ctl_pitch_bend(int ch, int val);
 
49
static void ctl_reset(void);
 
50
static int ctl_open(int using_stdin, int using_stdout);
 
51
static void ctl_close(void);
 
52
static int ctl_read(int32 *valp);
 
53
static int cmsg(int type, int verbosity_level, const char *fmt, ...);
 
54
static void ctl_pass_playing_list(int number_of_files, const char *list_of_files[]);
 
55
static int ctl_blocking_read(int32 *valp);
 
56
 
 
57
static void pipe_printf(const char *fmt, ...);
 
58
static void pipe_puts(const char *str);
 
59
static int pipe_gets(char *str, int maxlen);
 
60
static void pipe_open();
 
61
static void pipe_error(char *st);
 
62
static int pipe_read_ready();
 
63
 
 
64
static int AppInit(Tcl_Interp *interp);
 
65
static int ExitAll(ClientData clientData, Tcl_Interp *interp,
 
66
                       int argc, char *argv[]);
 
67
static int TraceCreate(ClientData clientData, Tcl_Interp *interp,
 
68
                       int argc, char *argv[]);
 
69
static int TraceUpdate(ClientData clientData, Tcl_Interp *interp,
 
70
                    int argc, char *argv[]);
 
71
static int TraceReset(ClientData clientData, Tcl_Interp *interp,
 
72
                    int argc, char *argv[]);
 
73
static void trace_volume(int ch, int val);
 
74
static void trace_panning(int ch, int val);
 
75
static void trace_prog_init(int ch);
 
76
static void trace_prog(int ch, int val);
 
77
static void trace_bank(int ch, int val);
 
78
static void trace_sustain(int ch, int val);
 
79
 
 
80
static void get_child(int sig);
 
81
static void shm_alloc(void);
 
82
static void shm_free(int sig);
 
83
 
 
84
static void start_panel(void);
 
85
 
 
86
#define MAX_MIDI_CHANNELS       16
 
87
 
 
88
typedef struct {
 
89
        int reset_panel;
 
90
        int multi_part;
 
91
 
 
92
        int32 last_time, cur_time;
 
93
 
 
94
        char v_flags[MAX_MIDI_CHANNELS];
 
95
        int16 cnote[MAX_MIDI_CHANNELS];
 
96
        int16 cvel[MAX_MIDI_CHANNELS];
 
97
        int16 ctotal[MAX_MIDI_CHANNELS];
 
98
 
 
99
        char c_flags[MAX_MIDI_CHANNELS];
 
100
        Channel channel[MAX_MIDI_CHANNELS];
 
101
} PanelInfo;
 
102
 
 
103
extern MidiEvent *current_event;
 
104
 
 
105
typedef struct {
 
106
  uint8 status, channel, note, velocity, voices;
 
107
  uint32 time;
 
108
} OldVoice;
 
109
#define MAX_OLD_NOTES 500
 
110
static OldVoice old_note[MAX_OLD_NOTES];
 
111
static int leading_pointer=0, trailing_pointer=0;
 
112
static uint32 current_centiseconds = 0;
 
113
static int songoffset = 0, current_voices = 0;
 
114
 
 
115
/**********************************************/
 
116
 
 
117
#define ctl tk_control_mode
 
118
 
 
119
ControlMode ctl= 
 
120
{
 
121
  "Tcl/Tk interface", 'k',
 
122
  1,0,0,
 
123
  ctl_open, ctl_pass_playing_list, ctl_close, ctl_read, cmsg,
 
124
  ctl_refresh, ctl_reset, ctl_file_name, ctl_total_time, ctl_current_time, 
 
125
  ctl_note, 
 
126
  ctl_master_volume, ctl_program, ctl_volume, 
 
127
  ctl_expression, ctl_panning, ctl_sustain, ctl_pitch_bend
 
128
};
 
129
 
 
130
#define FLAG_NOTE_OFF   1
 
131
#define FLAG_NOTE_ON    2
 
132
 
 
133
#define FLAG_BANK       1
 
134
#define FLAG_PROG       2
 
135
#define FLAG_PAN        4
 
136
#define FLAG_SUST       8
 
137
 
 
138
static PanelInfo *Panel;
 
139
 
 
140
static int shmid;       /* shared memory id */
 
141
static int child_killed = 0;
 
142
 
 
143
static int pipeAppli[2],pipePanel[2]; /* Pipe for communication with Tcl/Tk process   */
 
144
static int fpip_in, fpip_out;   /* in and out depends in which process we are */
 
145
static int child_pid;                  /* Pid for child process */
 
146
 
 
147
 
 
148
/***********************************************************************/
 
149
/* Put controls on the pipe                                            */
 
150
/***********************************************************************/
 
151
 
 
152
static int cmsg(int type, int verbosity_level, const char *fmt, ...)
 
153
{
 
154
        char local[2048];
 
155
#define TOO_LONG        2000
 
156
 
 
157
        va_list ap;
 
158
        if ((type==CMSG_TEXT || type==CMSG_INFO || type==CMSG_WARNING) &&
 
159
            ctl.verbosity<verbosity_level)
 
160
                return 0;
 
161
 
 
162
        va_start(ap, fmt);
 
163
/*
 
164
        if (strlen(fmt) > TOO_LONG)
 
165
                fmt[TOO_LONG] = 0;
 
166
*/
 
167
        if (!ctl.opened) {
 
168
                vfprintf(stderr, fmt, ap);
 
169
                fprintf(stderr, "\n");
 
170
        } else if (type == CMSG_ERROR) {
 
171
                int rc;
 
172
                int32 val;
 
173
                vsprintf(local, fmt, ap);
 
174
                pipe_printf("CERR %d", type);
 
175
                pipe_puts(local);
 
176
                while ((rc = ctl_blocking_read(&val)) != RC_NEXT)
 
177
                        ;
 
178
        } else {
 
179
                vsprintf(local, fmt, ap);
 
180
                pipe_printf("CMSG %d", type);
 
181
                pipe_puts(local);
 
182
        }
 
183
        va_end(ap);
 
184
        return 0;
 
185
}
 
186
 
 
187
static void ctl_refresh(void)
 
188
{
 
189
        ctl_note_display();
 
190
}
 
191
 
 
192
static void ctl_total_time(uint32 tt)
 
193
{
 
194
        int centisecs=(int)tt/(play_mode->rate/100);
 
195
    ctl_current_time(0);
 
196
    songoffset = 0;
 
197
    current_centiseconds = 0;
 
198
        pipe_printf("TIME %d", centisecs);
 
199
}
 
200
 
 
201
static void ctl_master_volume(int mv)
 
202
{
 
203
        pipe_printf("MVOL %d", mv);
 
204
}
 
205
 
 
206
static void ctl_file_name(char *name)
 
207
{
 
208
        pipe_printf("FILE %s", name);
 
209
}
 
210
 
 
211
static void ctl_current_time(uint32 ct)
 
212
{
 
213
    int centisecs, realct, sec;
 
214
    realct = play_mode->output_count(ct);
 
215
    if (realct < 0) realct = 0;
 
216
    else realct += songoffset;
 
217
    centisecs = realct / (play_mode->rate/100);
 
218
    current_centiseconds = (uint32)centisecs;
 
219
    sec = centisecs / 100;
 
220
 
 
221
/*      Panel->cur_time = (int)ct / (play_mode->rate/100) / 100;*/  /* sec order */
 
222
        Panel->cur_time = sec;
 
223
}
 
224
 
 
225
 
 
226
static void ctl_channel_note(int ch, int note, int vel)
 
227
{
 
228
        ch &= 0x0f;
 
229
        if (vel == 0) {
 
230
                if (note == Panel->cnote[ch])
 
231
                        Panel->v_flags[ch] = FLAG_NOTE_OFF;
 
232
                Panel->cvel[ch] = 0;
 
233
        } else if (vel > Panel->cvel[ch]) {
 
234
                Panel->cvel[ch] = vel;
 
235
                Panel->cnote[ch] = note;
 
236
                Panel->ctotal[ch] = vel * Panel->channel[ch].volume *
 
237
                        Panel->channel[ch].expression / (127*127);
 
238
                Panel->v_flags[ch] = FLAG_NOTE_ON;
 
239
        }
 
240
}
 
241
 
 
242
#if 0
 
243
static void ctl_note(int v)
 
244
{
 
245
        int ch, note, vel;
 
246
 
 
247
        if (!ctl.trace_playing) 
 
248
                return;
 
249
 
 
250
        ch = voice[v].channel;
 
251
        ch &= 0x0f;
 
252
        if (ch < 0 || ch >= MAX_MIDI_CHANNELS) return;
 
253
 
 
254
        note = voice[v].note;
 
255
        if (voice[v].status != VOICE_ON)
 
256
                vel = 0;
 
257
        else
 
258
                vel = voice[v].velocity;
 
259
        ctl_channel_note(ch, note, vel);
 
260
}
 
261
#endif
 
262
 
 
263
static void ctl_tk_note(int status, int ch, int note, int vel)
 
264
{
 
265
 
 
266
        if (ch < 0 || ch >= MAX_MIDI_CHANNELS) return;
 
267
 
 
268
        if (status != VOICE_ON)
 
269
                vel = 0;
 
270
        ctl_channel_note(ch, note, vel);
 
271
}
 
272
 
 
273
static void ctl_note(int v)
 
274
{
 
275
  int i, n;
 
276
  if (!ctl.trace_playing)
 
277
    return;
 
278
  if (voice[v].clone_type != 0) return;
 
279
 
 
280
  old_note[leading_pointer].status = voice[v].status;
 
281
  old_note[leading_pointer].channel = voice[v].channel;
 
282
  old_note[leading_pointer].note = voice[v].note;
 
283
  old_note[leading_pointer].velocity = voice[v].velocity;
 
284
  old_note[leading_pointer].time = current_event->time / (play_mode->rate/100);
 
285
  n=0;
 
286
  i=voices;
 
287
  while (i--)
 
288
    if (voice[i].status!=VOICE_FREE) n++;
 
289
  old_note[leading_pointer].voices = n;
 
290
  leading_pointer++;
 
291
  if (leading_pointer == MAX_OLD_NOTES) leading_pointer = 0;
 
292
 
 
293
}
 
294
 
 
295
static void ctl_note_display(void)
 
296
{
 
297
  int v = trailing_pointer;
 
298
  uint32 then;
 
299
 
 
300
  then = old_note[v].time;
 
301
 
 
302
  while (then <= current_centiseconds && v != leading_pointer)
 
303
    {
 
304
        ctl_tk_note(old_note[v].status, old_note[v].channel, old_note[v].note, old_note[v].velocity);
 
305
        current_voices = old_note[v].voices;
 
306
        v++;
 
307
        if (v == MAX_OLD_NOTES) v = 0;
 
308
        then = old_note[v].time;
 
309
    }
 
310
  trailing_pointer = v;
 
311
}
 
312
 
 
313
static void ctl_program(int ch, int val, const char *name)
 
314
{
 
315
        if (!ctl.trace_playing) 
 
316
                return;
 
317
        ch &= 0x0f;
 
318
        if (ch < 0 || ch >= MAX_MIDI_CHANNELS) return;
 
319
        Panel->channel[ch].program = val;
 
320
        Panel->c_flags[ch] |= FLAG_PROG;
 
321
}
 
322
 
 
323
static void ctl_volume(int ch, int val)
 
324
{
 
325
        if (!ctl.trace_playing)
 
326
                return;
 
327
        ch &= 0x0f;
 
328
        Panel->channel[ch].volume = val;
 
329
        ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]);
 
330
}
 
331
 
 
332
static void ctl_expression(int ch, int val)
 
333
{
 
334
        if (!ctl.trace_playing)
 
335
                return;
 
336
        ch &= 0x0f;
 
337
        Panel->channel[ch].expression = val;
 
338
        ctl_channel_note(ch, Panel->cnote[ch], Panel->cvel[ch]);
 
339
}
 
340
 
 
341
static void ctl_panning(int ch, int val)
 
342
{
 
343
        if (!ctl.trace_playing) 
 
344
                return;
 
345
        ch &= 0x0f;
 
346
        Panel->channel[ch].panning = val;
 
347
        Panel->c_flags[ch] |= FLAG_PAN;
 
348
}
 
349
 
 
350
static void ctl_sustain(int ch, int val)
 
351
{
 
352
        if (!ctl.trace_playing)
 
353
                return;
 
354
        ch &= 0x0f;
 
355
        Panel->channel[ch].sustain = val;
 
356
        Panel->c_flags[ch] |= FLAG_SUST;
 
357
}
 
358
 
 
359
static void ctl_pitch_bend(int ch, int val)
 
360
{
 
361
}
 
362
 
 
363
static void ctl_reset(void)
 
364
{
 
365
        int i;
 
366
 
 
367
        pipe_printf("RSET %d", ctl.trace_playing);
 
368
        if (!ctl.trace_playing)
 
369
                return;
 
370
        for (i = 0; i < MAX_MIDI_CHANNELS; i++) {
 
371
                ctl_program(i, channel[i].program, channel[i].name);
 
372
                ctl_volume(i, channel[i].volume);
 
373
                ctl_expression(i, channel[i].expression);
 
374
                ctl_panning(i, channel[i].panning);
 
375
                ctl_sustain(i, channel[i].sustain);
 
376
                ctl_pitch_bend(i, channel[i].pitchbend);
 
377
                ctl_channel_note(i, Panel->cnote[i], 0);
 
378
        }
 
379
  for (i=0; i<MAX_OLD_NOTES; i++)
 
380
    {
 
381
      old_note[i].time = 0;
 
382
    }
 
383
  leading_pointer = trailing_pointer = current_voices = 0;
 
384
}
 
385
 
 
386
/***********************************************************************/
 
387
/* OPEN THE CONNECTION                                                */
 
388
/***********************************************************************/
 
389
static int ctl_open(int using_stdin, int using_stdout)
 
390
{
 
391
        shm_alloc();
 
392
        pipe_open();
 
393
 
 
394
        if (child_pid == 0)
 
395
                start_panel();
 
396
 
 
397
        signal(SIGCHLD, get_child);
 
398
        signal(SIGTERM, shm_free);
 
399
        signal(SIGINT, shm_free);
 
400
 
 
401
        ctl.opened=1;
 
402
        return 0;
 
403
}
 
404
 
 
405
/* Tells the window to disapear */
 
406
static void ctl_close(void)
 
407
{
 
408
        if (ctl.opened) {
 
409
                kill(child_pid, SIGTERM);
 
410
                shm_free(100);
 
411
                ctl.opened=0;
 
412
        }
 
413
}
 
414
 
 
415
 
 
416
/* 
 
417
 * Read information coming from the window in a BLOCKING way
 
418
 */
 
419
 
 
420
/* commands are: PREV, NEXT, QUIT, STOP, LOAD, JUMP, VOLM */
 
421
 
 
422
static int ctl_blocking_read(int32 *valp)
 
423
{
 
424
        char buf[256], *tok, *arg;
 
425
        int rc;
 
426
        int new_volume;
 
427
        int new_centiseconds;
 
428
 
 
429
        rc = pipe_gets(buf, sizeof(buf)-1);
 
430
        tok = strtok(buf, " ");
 
431
 
 
432
        while (1)/* Loop after pause sleeping to treat other buttons! */
 
433
        {
 
434
                switch (*tok) {
 
435
                case 'V':
 
436
                        if ((arg = strtok(NULL, " ")) != NULL) {
 
437
                                new_volume = atoi(arg);
 
438
                                *valp= new_volume - amplification ;
 
439
                                return RC_CHANGE_VOLUME;
 
440
                        }
 
441
                        return RC_NONE;
 
442
                  
 
443
                case 'J':
 
444
                        if ((arg = strtok(NULL, " ")) != NULL) {
 
445
                                new_centiseconds = atoi(arg);
 
446
                                *valp= new_centiseconds*(play_mode->rate / 100) ;
 
447
                                return RC_JUMP;
 
448
                        }
 
449
                        return RC_NONE;
 
450
                  
 
451
                case 'Q':
 
452
                        return RC_QUIT;
 
453
                
 
454
                case 'L':
 
455
                        return RC_LOAD_FILE;              
 
456
                  
 
457
                case 'N':
 
458
                        return RC_NEXT;
 
459
                  
 
460
                case 'P':
 
461
                        /*return RC_REALLY_PREVIOUS;*/
 
462
                        return RC_PREVIOUS;
 
463
                  
 
464
                case 'R':
 
465
                        return RC_RESTART;
 
466
                  
 
467
                case 'F':
 
468
                        *valp=play_mode->rate;
 
469
                        return RC_FORWARD;
 
470
                  
 
471
                case 'B':
 
472
                        *valp=play_mode->rate;
 
473
                        return RC_BACK;
 
474
                }
 
475
          
 
476
          
 
477
                if (*tok == 'S') {
 
478
                        pipe_gets(buf, sizeof(buf)-1);
 
479
                        tok = strtok(buf, " ");
 
480
                        if (*tok == 'S')
 
481
                                return RC_NONE; /* Resume where we stopped */
 
482
                }
 
483
                else {
 
484
                        fprintf(stderr,"UNKNOWN RC_MESSAGE %s\n", tok);
 
485
                        return RC_NONE;
 
486
                }
 
487
        }
 
488
 
 
489
}
 
490
 
 
491
/* 
 
492
 * Read information coming from the window in a non blocking way
 
493
 */
 
494
static int ctl_read(int32 *valp)
 
495
{
 
496
        int num;
 
497
 
 
498
        if (last_rc_command)
 
499
          {
 
500
                *valp = last_rc_arg;
 
501
                num = last_rc_command;
 
502
                last_rc_command = 0;
 
503
                return num;
 
504
          }
 
505
        /* We don't wan't to lock on reading  */
 
506
        num=pipe_read_ready(); 
 
507
 
 
508
        if (num==0)
 
509
                return RC_NONE;
 
510
  
 
511
        return(ctl_blocking_read(valp));
 
512
}
 
513
 
 
514
static void ctl_pass_playing_list(int number_of_files, const char *list_of_files[])
 
515
{
 
516
        int i=0;
 
517
        char local[1000];
 
518
        int command;
 
519
        int32 val;
 
520
 
 
521
        /* Pass the list to the interface */
 
522
        pipe_printf("LIST %d", number_of_files);
 
523
        for (i=0;i<number_of_files;i++)
 
524
                pipe_puts(list_of_files[i]);
 
525
    
 
526
        /* Ask the interface for a filename to play -> begin to play automatically */
 
527
        /*pipe_puts("NEXT");*/
 
528
        command = ctl_blocking_read(&val);
 
529
 
 
530
        /* Main Loop */
 
531
        for (;;)
 
532
        { 
 
533
                if (command==RC_LOAD_FILE)
 
534
                {
 
535
                        /* Read a LoadFile command */
 
536
                        pipe_gets(local, sizeof(local)-1);
 
537
                        command=play_midi_file(local);
 
538
                }
 
539
                else
 
540
                {
 
541
                        if (command==RC_QUIT) {
 
542
                                /* if really QUIT */
 
543
                                pipe_gets(local, sizeof(local)-1);
 
544
                                if (*local == 'Z')
 
545
                                        return;
 
546
                                /* only stop playing..*/
 
547
                        }
 
548
                        else if (command==RC_ERROR)
 
549
                                command=RC_TUNE_END; /* Launch next file */
 
550
                        else if (command==RC_CHANGE_VOLUME) /* init volume */
 
551
                                amplification += val;
 
552
 
 
553
                        switch(command)
 
554
                        {
 
555
                        case RC_NEXT:
 
556
                                pipe_puts("NEXT");
 
557
                                break;
 
558
                        case RC_REALLY_PREVIOUS:
 
559
                                pipe_puts("PREV");
 
560
                                break;
 
561
                        case RC_TUNE_END:
 
562
                                pipe_puts("TEND");
 
563
                                break;
 
564
                        case RC_RESTART:
 
565
                                pipe_puts("RSTA");
 
566
                                break;
 
567
                        }
 
568
                    
 
569
                        command = ctl_blocking_read(&val);
 
570
                }
 
571
        }
 
572
}
 
573
 
 
574
 
 
575
/* open pipe and fork child process */
 
576
static void pipe_open(void)
 
577
{
 
578
        int res;
 
579
 
 
580
        res = pipe(pipeAppli);
 
581
        if (res!=0) pipe_error("PIPE_APPLI CREATION");
 
582
    
 
583
        res = pipe(pipePanel);
 
584
        if (res!=0) pipe_error("PIPE_PANEL CREATION");
 
585
    
 
586
        if ((child_pid = fork()) == 0) {
 
587
                /*child*/
 
588
                close(pipePanel[1]); 
 
589
                close(pipeAppli[0]);
 
590
            
 
591
                /* redirect to stdin/out */
 
592
                dup2(pipePanel[0], fileno(stdin));
 
593
                close(pipePanel[0]);
 
594
                dup2(pipeAppli[1], fileno(stdout));
 
595
                close(pipeAppli[1]);
 
596
        } else {
 
597
                close(pipePanel[0]);
 
598
                close(pipeAppli[1]);
 
599
    
 
600
                fpip_in= pipeAppli[0];
 
601
                fpip_out= pipePanel[1];
 
602
        }
 
603
}           
 
604
 
 
605
#if defined(sgi)
 
606
#include <sys/time.h>
 
607
#endif
 
608
 
 
609
#if defined(SOLARIS)
 
610
#include <sys/filio.h>
 
611
#endif
 
612
 
 
613
int pipe_read_ready(void)
 
614
 {
 
615
#if defined(sgi)
 
616
    fd_set fds;
 
617
    int cnt;
 
618
    struct timeval timeout;
 
619
 
 
620
    FD_ZERO(&fds);
 
621
    FD_SET(fpip_in, &fds);
 
622
    timeout.tv_sec = timeout.tv_usec = 0;
 
623
    if((cnt = select(fpip_in + 1, &fds, NULL, NULL, &timeout)) < 0)
 
624
    {
 
625
        perror("select");
 
626
        return -1;
 
627
    }
 
628
 
 
629
    return cnt > 0 && FD_ISSET(fpip_in, &fds) != 0;
 
630
#else
 
631
    int num;
 
632
 
 
633
    if(ioctl(fpip_in,FIONREAD,&num) < 0) /* see how many chars in buffer. */
 
634
    {
 
635
        perror("ioctl: FIONREAD");
 
636
        return -1;
 
637
    }
 
638
    return num;
 
639
#endif
 
640
}
 
641
 
 
642
 
 
643
/***********************************************************************/
 
644
/* PIPE COMUNICATION                                                   */
 
645
/***********************************************************************/
 
646
 
 
647
static void pipe_error(char *st)
 
648
{
 
649
    fprintf(stderr,"CONNECTION PROBLEM WITH TCL/TK PROCESS IN %s BECAUSE:%s\n",
 
650
            st,
 
651
            sys_errlist[errno]);
 
652
    exit(1);
 
653
}
 
654
 
 
655
 
 
656
static void pipe_printf(const char *fmt, ...)
 
657
{
 
658
        char buf[256];
 
659
        va_list ap;
 
660
        va_start(ap, fmt);
 
661
        vsprintf(buf, fmt, ap);
 
662
        pipe_puts(buf);
 
663
}
 
664
 
 
665
static void pipe_puts(const char *str)
 
666
{
 
667
        int len;
 
668
        char lf = '\n';
 
669
        len = strlen(str);
 
670
        write(fpip_out, str, len);
 
671
        write(fpip_out, &lf, 1);
 
672
}
 
673
 
 
674
 
 
675
int pipe_gets(char *str, int maxlen)
 
676
{
 
677
/* blocking reading */
 
678
        char *p;
 
679
        int len;
 
680
 
 
681
        /* at least 5 letters (4+\n) command */
 
682
        len = 0;
 
683
        for (p = str; ; p++) {
 
684
                read(fpip_in, p, 1);
 
685
                if (*p == '\n')
 
686
                        break;
 
687
                len++;
 
688
        }
 
689
        *p = 0;
 
690
        return len;
 
691
}
 
692
 
 
693
 
 
694
/*----------------------------------------------------------------
 
695
 * shared memory handling
 
696
 *----------------------------------------------------------------*/
 
697
 
 
698
static void get_child(int sig)
 
699
{
 
700
        child_killed = 1;
 
701
}
 
702
 
 
703
static void shm_alloc(void)
 
704
{
 
705
        shmid = shmget(IPC_PRIVATE, sizeof(PanelInfo),
 
706
                       IPC_CREAT|0600);
 
707
        if (shmid < 0) {
 
708
                fprintf(stderr, "can't allocate shared memory\n");
 
709
                exit(1);
 
710
        }
 
711
        Panel = (PanelInfo *)shmat(shmid, 0, 0);
 
712
        Panel->reset_panel = 0;
 
713
        Panel->multi_part = 0;
 
714
}
 
715
 
 
716
static void shm_free(int sig)
 
717
 
718
        kill(child_pid, SIGTERM);
 
719
        while (!child_killed)
 
720
                ;
 
721
        shmctl(shmid, IPC_RMID,NULL);
 
722
        shmdt((char *)Panel);
 
723
        if (sig != 100)
 
724
                exit(0);
 
725
}
 
726
 
 
727
/*----------------------------------------------------------------
 
728
 * start Tk window panel
 
729
 *----------------------------------------------------------------*/
 
730
 
 
731
static void start_panel(void)
 
732
{
 
733
        char *argv[128];
 
734
        int argc;
 
735
    
 
736
        argc = 0;
 
737
        argv[argc++] = "-f";
 
738
        /* argv[argc++] = TKPROGPATH; */
 
739
        argv[argc++] = TIMID_DIR "/tkmidity.tcl";
 
740
 
 
741
        if (ctl.trace_playing) {
 
742
                argv[argc++] = "-mode";
 
743
                argv[argc++] = "trace";
 
744
        }
 
745
 
 
746
        /* call Tk main routine */
 
747
        Tk_Main(argc, argv, AppInit);
 
748
 
 
749
        exit(0);
 
750
}
 
751
 
 
752
 
 
753
/*----------------------------------------------------------------
 
754
 * initialize Tcl application
 
755
 *----------------------------------------------------------------*/
 
756
 
 
757
static Tcl_Interp *my_interp;
 
758
 
 
759
static int AppInit(Tcl_Interp *interp)
 
760
{
 
761
        my_interp = interp;
 
762
 
 
763
        if (Tcl_Init(interp) == TCL_ERROR) {
 
764
                return TCL_ERROR;
 
765
        }
 
766
        if (Tk_Init(interp) == TCL_ERROR) {
 
767
                return TCL_ERROR;
 
768
        }
 
769
 
 
770
        Tcl_CreateCommand(interp, "TraceCreate", TraceCreate,
 
771
                          (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
 
772
        Tcl_CreateCommand(interp, "TraceUpdate", TraceUpdate,
 
773
                          (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
 
774
        Tcl_CreateCommand(interp, "TraceReset", TraceReset,
 
775
                          (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
 
776
        Tcl_CreateCommand(interp, "ExitAll", ExitAll,
 
777
                          (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
 
778
        Tcl_CreateCommand(interp, "TraceUpdate", TraceUpdate,
 
779
                          (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
 
780
        return TCL_OK;
 
781
}
 
782
 
 
783
static int ExitAll(ClientData clientData, Tcl_Interp *interp,
 
784
                   int argc, char *argv[])
 
785
{
 
786
        /* window is killed; kill the parent process, too */
 
787
        kill(getppid(), SIGTERM);
 
788
        for (;;)
 
789
                sleep(1000);
 
790
}
 
791
 
 
792
/* evaluate Tcl script */
 
793
static char *v_eval(const char *fmt, ...)
 
794
{
 
795
        char buf[256];
 
796
        va_list ap;
 
797
        va_start(ap, fmt);
 
798
        vsprintf(buf, fmt, ap);
 
799
        Tcl_Eval(my_interp, buf);
 
800
        va_end(ap);
 
801
        return my_interp->result;
 
802
}
 
803
 
 
804
static char *v_get2(const char *v1, const char *v2)
 
805
{
 
806
        return Tcl_GetVar2(my_interp, (char *)v1, (char *)v2, 0);
 
807
}
 
808
 
 
809
 
 
810
/*----------------------------------------------------------------
 
811
 * update Tcl timer / trace window
 
812
 *----------------------------------------------------------------*/
 
813
 
 
814
#define FRAME_WIN       ".body.trace"
 
815
#define CANVAS_WIN      FRAME_WIN ".c"
 
816
 
 
817
#define BAR_WID 20
 
818
#define BAR_HGT 130
 
819
#define WIN_WID (BAR_WID * 16)
 
820
#define WIN_HGT (BAR_HGT + 11 + 17)
 
821
#define BAR_HALF_HGT (WIN_HGT / 2 - 11 - 17)
 
822
 
 
823
 
 
824
static int TraceCreate(ClientData clientData, Tcl_Interp *interp,
 
825
                       int argc, char *argv[])
 
826
{
 
827
        int i;
 
828
        v_eval("frame %s -bg black", FRAME_WIN);
 
829
        v_eval("canvas %s -width %d -height %d -bd 0 -bg black "
 
830
               "-highlightthickness 0",
 
831
               CANVAS_WIN, WIN_WID, WIN_HGT);
 
832
        v_eval("pack %s -side top -fill x", CANVAS_WIN);
 
833
        for (i = 0; i < 32; i++) {
 
834
                const char *color;
 
835
                v_eval("%s create text 0 0 -anchor n -fill white -text 00 "
 
836
                       "-tags prog%d", CANVAS_WIN, i);
 
837
                v_eval("%s create poly 0 0 0 0 0 0 -fill yellow -tags pos%d",
 
838
                       CANVAS_WIN, i);
 
839
                color = (i == 9 || i == 25) ? "red" : "green";
 
840
                v_eval("%s create rect 0 0 0 0 -fill %s -tags bar%d "
 
841
                       "-outline \"\"", CANVAS_WIN, color, i);
 
842
        }
 
843
        v_eval("set Stat(TimerId) -1");
 
844
        v_eval("TraceReset");
 
845
        return TCL_OK;
 
846
}
 
847
 
 
848
static void trace_bank(int ch, int val)
 
849
{
 
850
        v_eval("%s itemconfigure bar%d -fill %s",
 
851
               CANVAS_WIN, ch,
 
852
               (val == 128 ? "red" : "green"));
 
853
}
 
854
 
 
855
static void trace_prog(int ch, int val)
 
856
{
 
857
        v_eval("%s itemconfigure prog%d -text %02X",
 
858
               CANVAS_WIN, ch, val);
 
859
}
 
860
 
 
861
static void trace_sustain(int ch, int val)
 
862
{
 
863
        v_eval("%s itemconfigure prog%d -fill %s",
 
864
               CANVAS_WIN, ch,
 
865
               (val == 127 ? "green" : "white"));
 
866
}
 
867
 
 
868
static void trace_prog_init(int ch)
 
869
{
 
870
        int item, yofs, bar, x, y;
 
871
 
 
872
        item = ch;
 
873
        yofs = 0;
 
874
        bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
 
875
        if (ch >= 16) {
 
876
                ch -= 16;
 
877
                yofs = WIN_HGT / 2;
 
878
                if (!Panel->multi_part)
 
879
                        yofs = -500;
 
880
        }
 
881
        x = ch * BAR_WID + BAR_WID/2;
 
882
        y = bar + 11 + yofs;
 
883
        v_eval("%s coords prog%d %d %d", CANVAS_WIN, item, x, y);
 
884
}
 
885
 
 
886
static void trace_volume(int ch, int val)
 
887
{
 
888
        int item, bar, yofs, x1, y1, x2, y2;
 
889
        item = ch;
 
890
        yofs = 0;
 
891
        bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
 
892
        if (ch >= 16) {
 
893
                yofs = WIN_HGT / 2;
 
894
                ch -= 16;
 
895
                if (!Panel->multi_part)
 
896
                        yofs = -500;
 
897
        }
 
898
        x1 = ch * BAR_WID;
 
899
        y1 = bar - 1 + yofs;
 
900
        x2 = x1 + BAR_WID - 1;
 
901
        y2 = y1 - bar * val / 127;
 
902
        v_eval("%s coords bar%d %d %d %d %d", CANVAS_WIN,
 
903
               item, x1, y1, x2, y2);
 
904
}
 
905
 
 
906
static void trace_panning(int ch, int val)
 
907
{
 
908
        int item, bar, yofs;
 
909
        int x, ap, bp;
 
910
        if (val < 0) {
 
911
                v_eval("%s coords pos%d -1 0 -1 0 -1 0", CANVAS_WIN, ch);
 
912
                return;
 
913
        }
 
914
 
 
915
        item = ch;
 
916
        yofs = 0;
 
917
        bar = Panel->multi_part ? BAR_HALF_HGT : BAR_HGT;
 
918
        if (ch >= 16) {
 
919
                yofs = WIN_HGT / 2;
 
920
                ch -= 16;
 
921
                if (!Panel->multi_part)
 
922
                        yofs = -500;
 
923
        }
 
924
 
 
925
        x = BAR_WID * ch;
 
926
        ap = BAR_WID * val / 127;
 
927
        bp = BAR_WID - ap - 1;
 
928
        v_eval("%s coords pos%d %d %d %d %d %d %d", CANVAS_WIN, item,
 
929
               ap + x, bar + 5 + yofs,
 
930
               bp + x, bar + 1 + yofs,
 
931
               bp + x, bar + 9 + yofs);
 
932
}
 
933
 
 
934
static int TraceReset(ClientData clientData, Tcl_Interp *interp,
 
935
                           int argc, char *argv[])
 
936
{
 
937
        int i;
 
938
        for (i = 0; i < 32; i++) {
 
939
                trace_volume(i, 0);
 
940
                trace_panning(i, -1);
 
941
                trace_prog_init(i);
 
942
                trace_prog(i, 0);
 
943
                trace_sustain(i, 0);
 
944
                Panel->ctotal[i] = 0;
 
945
                Panel->cvel[i] = 0;
 
946
                Panel->v_flags[i] = 0;
 
947
                Panel->c_flags[i] = 0;
 
948
        }
 
949
 
 
950
        return TCL_OK;
 
951
}
 
952
 
 
953
 
 
954
 
 
955
#define DELTA_VEL       32
 
956
 
 
957
static void update_notes(void)
 
958
{
 
959
        int i, imax;
 
960
        imax = Panel->multi_part ? 32 : 16;
 
961
        for (i = 0; i < imax; i++) {
 
962
                if (Panel->v_flags[i]) {
 
963
                        if (Panel->v_flags[i] == FLAG_NOTE_OFF) {
 
964
                                Panel->ctotal[i] -= DELTA_VEL;
 
965
                                if (Panel->ctotal[i] <= 0) {
 
966
                                        Panel->ctotal[i] = 0;
 
967
                                        Panel->v_flags[i] = 0;
 
968
                                }
 
969
                        } else {
 
970
                                Panel->v_flags[i] = 0;
 
971
                        }
 
972
                        trace_volume(i, Panel->ctotal[i]);
 
973
                }
 
974
 
 
975
                if (Panel->c_flags[i]) {
 
976
                        if (Panel->c_flags[i] & FLAG_PAN)
 
977
                                trace_panning(i, Panel->channel[i].panning);
 
978
                        if (Panel->c_flags[i] & FLAG_BANK)
 
979
                                trace_bank(i, Panel->channel[i].bank);
 
980
                        if (Panel->c_flags[i] & FLAG_PROG)
 
981
                                trace_prog(i, Panel->channel[i].program);
 
982
                        if (Panel->c_flags[i] & FLAG_SUST)
 
983
                                trace_sustain(i, Panel->channel[i].sustain);
 
984
                        Panel->c_flags[i] = 0;
 
985
                }
 
986
        }
 
987
}
 
988
 
 
989
static int TraceUpdate(ClientData clientData, Tcl_Interp *interp,
 
990
                    int argc, char *argv[])
 
991
{
 
992
        char *playing = v_get2("Stat", "Playing");
 
993
        if (playing && *playing != '0') {
 
994
                if (Panel->reset_panel) {
 
995
                        v_eval("TraceReset");
 
996
                        Panel->reset_panel = 0;
 
997
                }
 
998
                if (Panel->last_time != Panel->cur_time) {
 
999
                        v_eval("SetTime %d", Panel->cur_time);
 
1000
                        Panel->last_time = Panel->cur_time;
 
1001
                }
 
1002
                if (ctl.trace_playing)
 
1003
                        update_notes();
 
1004
        }
 
1005
        v_eval("set Stat(TimerId) [after 50 TraceUpdate]");
 
1006
        return TCL_OK;
 
1007
}
 
1008
 
 
1009
#endif /* TCLTK */