home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume37 / tclm / part01 < prev    next >
Encoding:
Text File  |  1993-05-15  |  58.8 KB  |  2,240 lines

  1. Newsgroups: comp.sources.misc
  2. From: durian@advtech.uswest.com (Mike Durian)
  3. Subject: v37i043:  tclm - TCL extensions for MIDI file manipulation, Part01/05
  4. Message-ID: <csm-v37i043=tclm.165023@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: ba4df218ca79503f7d60bdf8e6123339
  6. Date: Mon, 10 May 1993 21:50:43 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: durian@advtech.uswest.com (Mike Durian)
  10. Posting-number: Volume 37, Issue 43
  11. Archive-name: tclm/part01
  12. Environment: BSD/386, Esix SV4, SunOS, TCL 6.x
  13.  
  14. Tclm is an extended version of John Ousterhout's tcl (Tool Command
  15. Language) package.  The extensions are designed to allow easy
  16. manipulation of Standard MIDI Files.  The combination of the easy
  17. to use tcl interpreted language and the MIDI extensions makes it
  18. very simple to write you own MIDI applications.
  19.  
  20. If you've played with tclm-0.1, you'll find 1.0 a lot different.
  21. I think it is much easier to use and a lot more powerful, but then
  22. my opinion might be a bit biased.
  23.  
  24. Included with tclm are a few scripts that use tclm.  These include
  25. scripts to play and record record MIDI files as well as a simple
  26. text based sequencer.  There is also a pair of scripts that convert
  27. a MIDI file into human readable form and back again.
  28.  
  29. Mike Durian
  30. durian@advtech.uswest.com
  31. ------------------------
  32. #! /bin/sh
  33. # This is a shell archive.  Remove anything before this line, then feed it
  34. # into a shell via "sh file" or similar.  To overwrite existing files,
  35. # type "sh file -c".
  36. # Contents:  tclm-1.0 tclm-1.0/doc tclm-1.0/mlib tclm-1.0/patchlevel.h
  37. #   tclm-1.0/tclmCmd.c
  38. # Wrapped by kent@sparky on Mon May 10 09:43:32 1993
  39. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  40. echo If this archive is complete, you will see the following message:
  41. echo '          "shar: End of archive 1 (of 5)."'
  42. if test ! -d 'tclm-1.0' ; then
  43.     echo shar: Creating directory \"'tclm-1.0'\"
  44.     mkdir 'tclm-1.0'
  45. fi
  46. if test ! -d 'tclm-1.0/doc' ; then
  47.     echo shar: Creating directory \"'tclm-1.0/doc'\"
  48.     mkdir 'tclm-1.0/doc'
  49. fi
  50. if test ! -d 'tclm-1.0/mlib' ; then
  51.     echo shar: Creating directory \"'tclm-1.0/mlib'\"
  52.     mkdir 'tclm-1.0/mlib'
  53. fi
  54. if test -f 'tclm-1.0/patchlevel.h' -a "${1}" != "-c" ; then 
  55.   echo shar: Will not clobber existing file \"'tclm-1.0/patchlevel.h'\"
  56. else
  57.   echo shar: Extracting \"'tclm-1.0/patchlevel.h'\" \(93 characters\)
  58.   sed "s/^X//" >'tclm-1.0/patchlevel.h' <<'END_OF_FILE'
  59. X/*
  60. X * patchlevel.h,v 1.3 1993/05/06 02:51:11 durian Exp
  61. X */
  62. X
  63. X#define TCLM_PATCHLEVEL "0.9.5"
  64. END_OF_FILE
  65.   if test 93 -ne `wc -c <'tclm-1.0/patchlevel.h'`; then
  66.     echo shar: \"'tclm-1.0/patchlevel.h'\" unpacked with wrong size!
  67.   fi
  68.   # end of 'tclm-1.0/patchlevel.h'
  69. fi
  70. if test -f 'tclm-1.0/tclmCmd.c' -a "${1}" != "-c" ; then 
  71.   echo shar: Will not clobber existing file \"'tclm-1.0/tclmCmd.c'\"
  72. else
  73.   echo shar: Extracting \"'tclm-1.0/tclmCmd.c'\" \(54562 characters\)
  74.   sed "s/^X//" >'tclm-1.0/tclmCmd.c' <<'END_OF_FILE'
  75. X/*-
  76. X * Copyright (c) 1993 Michael B. Durian.  All rights reserved.
  77. X *
  78. X * Redistribution and use in source and binary forms, with or without
  79. X * modification, are permitted provided that the following conditions
  80. X * are met:
  81. X * 1. Redistributions of source code must retain the above copyright
  82. X *    notice, this list of conditions and the following disclaimer.
  83. X * 2. Redistributions in binary form must reproduce the above copyright
  84. X *    notice, this list of conditions and the following disclaimer in the
  85. X *    documentation and/or other materials provided with the distribution.
  86. X * 3. All advertising materials mentioning features or use of this software
  87. X *    must display the following acknowledgement:
  88. X *    This product includes software developed by Michael B. Durian.
  89. X * 4. The name of the the Author may be used to endorse or promote 
  90. X *    products derived from this software without specific prior written 
  91. X *    permission.
  92. X *
  93. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
  94. X * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  95. X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
  96. X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  97. X * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  98. X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  99. X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  100. X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  101. X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  102. X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  103. X * SUCH DAMAGE.
  104. X */
  105. X/*
  106. X * tclmCmd.c,v 1.14 1993/05/07 17:45:07 durian Exp
  107. X */
  108. X
  109. Xstatic char cvsid[] = "tclmCmd.c,v 1.14 1993/05/07 17:45:07 durian Exp";
  110. X
  111. X#include "tclInt.h"
  112. X#include "tclUnix.h"
  113. X#include "patchlevel.h"
  114. X#include "mutil.h"
  115. X#include "tclm.h"
  116. X#ifdef MIDIPLAY
  117. X#include "tclmPlay.h"
  118. X#endif
  119. X
  120. X
  121. XTcl_HashTable MidiFileHash;
  122. Xstatic int mfileId = 0;
  123. X
  124. Xstatic char *key_strings[] = {"C flat", "G flat", "D flat", "A flat",
  125. X    "E flat", "B flat", "F", "C", "G", "D", "A", "E", "B", "F sharp",
  126. X    "C sharp"};
  127. Xstatic char *event_list = "channelpressure keypressure \"a meta event\" \
  128. Xnoteoff noteon parameter pitchwheel program sysex";
  129. Xstatic char *meta_events = "metachanprefix metacpy metacue metaeot \
  130. Xmetainstname metakey metalyric metamarker metaseqname metaseqnum metaseqspec \
  131. Xmetasmpte metatempo metatext metatime";
  132. X
  133. Xstatic int Tclm_ConvertMeta _ANSI_ARGS_((Tcl_Interp *, int, char **,
  134. X    unsigned char *, int *));
  135. Xstatic int Tclm_ConvertTiming _ANSI_ARGS_((Tcl_Interp *, char *,
  136. X    unsigned char *, int *));
  137. Xstatic int Tclm_ConvertBytes _ANSI_ARGS_((Tcl_Interp *, char *,
  138. X    unsigned char *, int *));
  139. Xstatic int Tclm_AddMetaBytes _ANSI_ARGS_((Tcl_Interp *, unsigned char *, int *,
  140. X    char *));
  141. Xstatic void Tclm_AddMetaString _ANSI_ARGS_((unsigned char *, int *, char *));
  142. Xstatic void Tclm_MakeMetaText _ANSI_ARGS_((Tcl_Interp *, unsigned char *));
  143. X
  144. Xvoid
  145. XTclm_InitMidi(interp)
  146. X    Tcl_Interp *interp;
  147. X{
  148. X
  149. X    Tcl_CreateCommand(interp, "midiconfig", Tclm_MidiConfig, NULL, NULL);
  150. X    Tcl_CreateCommand(interp, "midiread", Tclm_MidiRead, NULL, NULL);
  151. X    Tcl_CreateCommand(interp, "midiwrite", Tclm_MidiWrite, NULL, NULL);
  152. X    Tcl_CreateCommand(interp, "midimerge", Tclm_MidiMerge, NULL, NULL);
  153. X    Tcl_CreateCommand(interp, "midimake", Tclm_MidiMake, NULL, NULL);
  154. X    Tcl_CreateCommand(interp, "midifree", Tclm_MidiFree, NULL, NULL);
  155. X    Tcl_CreateCommand(interp, "midirewind", Tclm_MidiRewind, NULL, NULL);
  156. X
  157. X    Tcl_CreateCommand(interp, "midifixtovar", Tclm_MidiFixToVar, NULL,
  158. X        NULL);
  159. X    Tcl_CreateCommand(interp, "midivartofix", Tclm_MidiVarToFix, NULL,
  160. X        NULL);
  161. X    Tcl_CreateCommand(interp, "midiget", Tclm_MidiGet, NULL, NULL);
  162. X    Tcl_CreateCommand(interp, "midiput", Tclm_MidiPut, NULL, NULL);
  163. X    Tcl_CreateCommand(interp, "miditiming", Tclm_MidiTiming, NULL, NULL);
  164. X    Tcl_CreateCommand(interp, "midiplayable", Tclm_MidiPlayable, NULL,
  165. X        NULL);
  166. X    Tcl_CreateCommand(interp, "tclmversion", Tclm_TclmVersion, NULL, NULL);
  167. X    Tcl_InitHashTable(&MidiFileHash, TCL_ONE_WORD_KEYS);
  168. X#ifdef MIDIPLAY
  169. X    Tclm_InitPlay(interp);
  170. X#endif
  171. X}
  172. X
  173. X
  174. Xint
  175. XTclm_MidiConfig(dummy, interp, argc, argv)
  176. X    ClientData dummy;
  177. X    Tcl_Interp *interp;
  178. X    int argc;
  179. X    char **argv;
  180. X{
  181. X    int length;
  182. X    int result;
  183. X
  184. X    /*
  185. X     * argv[0] - midiconfig
  186. X     * argv[1] - mfileID
  187. X     * argv[2] - format | division | tracks
  188. X     * argv[3] - optional arg
  189. X     */
  190. X    result = TCL_OK;
  191. X    if (argc < 3 || argc > 4) {
  192. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  193. X            argv[0], "mfileId {format | division | tracks} ?arg?\"",
  194. X            (char *)NULL);
  195. X        return (TCL_ERROR);
  196. X    }
  197. X
  198. X    length = strlen(argv[2]);
  199. X    switch(argv[2][0]) {
  200. X    case 'd':
  201. X        if (strncmp(argv[2], "division", length) == 0)
  202. X            result = Tclm_Division(interp, argc, argv);
  203. X        else {
  204. X            Tcl_AppendResult(interp, "bad option, ", argv[2],
  205. X                ", must be one of format, division or tracks",
  206. X                (char *)NULL);
  207. X            return (TCL_ERROR);
  208. X        }
  209. X        break;
  210. X    case 'f':
  211. X        if (strncmp(argv[2], "format", length) == 0)
  212. X            result = Tclm_Format(interp, argc, argv);
  213. X        else {
  214. X            Tcl_AppendResult(interp, "bad option, ", argv[2],
  215. X                ", must be one of format, division or tracks",
  216. X                (char *)NULL);
  217. X            return (TCL_ERROR);
  218. X        }
  219. X        break;
  220. X    case 't':
  221. X        if (strncmp(argv[2], "tracks", length) == 0)
  222. X            result = Tclm_NumTracks(interp, argc, argv);
  223. X        else {
  224. X            Tcl_AppendResult(interp, "bad option, ", argv[2],
  225. X                ", must be one of format, division or tracks",
  226. X                (char *)NULL);
  227. X            return (TCL_ERROR);
  228. X        }
  229. X        break;
  230. X    default:
  231. X        Tcl_AppendResult(interp, "bad option, ", argv[2],
  232. X            ", must be one of format, division or tracks",
  233. X            (char *)NULL);
  234. X        return (TCL_ERROR);
  235. X    }
  236. X
  237. X    return (result);
  238. X}
  239. X
  240. Xint
  241. XTclm_MidiMake(dummy, interp, argc, argv)
  242. X    ClientData dummy;
  243. X    Tcl_Interp *interp;
  244. X    int argc;
  245. X    char **argv;
  246. X{
  247. X    MIDI_FILE *mfile;
  248. X    Tcl_HashEntry *hash_entry;
  249. X    int created_hash;
  250. X
  251. X    /*
  252. X     * argv[0] - midimake
  253. X     */
  254. X    if (argc != 1) {
  255. X        Tcl_AppendResult(interp, "bad # args: should be \"",
  256. X            argv[0], "\"", (char *)NULL);
  257. X        return (TCL_ERROR);
  258. X    }
  259. X    if ((mfile = (MIDI_FILE *)malloc(sizeof(MIDI_FILE))) == NULL) {
  260. X        Tcl_AppendResult(interp, "Not enough memory for MIDI file",
  261. X            (char *)NULL);
  262. X        return (TCL_ERROR);
  263. X    }
  264. X    strncpy(mfile->hchunk.str, "MThd", 4);
  265. X    mfile->hchunk.length = 6;
  266. X    mfile->hchunk.format = 1;
  267. X    mfile->hchunk.division = 120;
  268. X    mfile->hchunk.num_trks = 0;
  269. X    mfile->tchunks = NULL;
  270. X
  271. X    hash_entry = Tcl_CreateHashEntry(&MidiFileHash, (char *)mfileId,
  272. X        &created_hash);
  273. X    if (!created_hash) {
  274. X        Tcl_AppendResult(interp, "Hash bucket for file alread ",
  275. X            "exists", (char *)NULL);
  276. X        return (TCL_ERROR);
  277. X    }
  278. X    Tcl_SetHashValue(hash_entry, mfile);
  279. X
  280. X    sprintf(interp->result, "mfile%d", mfileId++);
  281. X    return (TCL_OK);
  282. X}
  283. X
  284. Xint
  285. XTclm_MidiRead(dummy, interp, argc, argv)
  286. X    ClientData dummy;
  287. X    Tcl_Interp *interp;
  288. X    int argc;
  289. X    char **argv;
  290. X{
  291. X    MIDI_FILE *mfile;
  292. X    OpenFile *filePtr;
  293. X    Tcl_HashEntry *hash_entry;
  294. X    int created_hash;
  295. X    int fd;
  296. X    int i;
  297. X    int result;
  298. X    char num_str[20];
  299. X
  300. X    /*
  301. X     * argv[0] - midiread
  302. X     * argv[1] - open file descriptor
  303. X     */
  304. X    if (argc != 2) {
  305. X        Tcl_AppendResult(interp, "bad # args: should be \"",
  306. X            argv[0], " fileId\"", (char *)NULL);
  307. X        return (TCL_ERROR);
  308. X    }
  309. X    if ((result = TclGetOpenFile(interp, argv[1], &filePtr)) != TCL_OK)
  310. X        return (result);
  311. X
  312. X    fd = fileno(filePtr->f);
  313. X    if ((mfile = (MIDI_FILE *)malloc(sizeof(MIDI_FILE))) == NULL) {
  314. X        Tcl_AppendResult(interp, "Not enough memory for MIDI file",
  315. X            (char *)NULL);
  316. X        return (TCL_ERROR);
  317. X    }
  318. X    if (!read_header_chunk(fd, &mfile->hchunk)) {
  319. X        if (MidiEof)
  320. X            Tcl_AppendResult(interp, "EOF");
  321. X        else
  322. X            Tcl_AppendResult(interp,
  323. X                "Couldn't read header chunk\n", MidiError,
  324. X                (char *)NULL);
  325. X        return (TCL_ERROR);
  326. X    }
  327. X    if ((mfile->tchunks = (TCHUNK *)malloc(mfile->hchunk.num_trks *
  328. X        sizeof(TCHUNK))) == NULL) {
  329. X        Tcl_AppendResult(interp, "Not enough memory for track ",
  330. X            "chunks", (char *)NULL);
  331. X        return (TCL_ERROR);
  332. X    }
  333. X
  334. X    for (i = 0;  i < mfile->hchunk.num_trks; i++) {
  335. X        if (!read_track_chunk(fd, &(mfile->tchunks[i]))) {
  336. X            sprintf(num_str, "%d", i);
  337. X            Tcl_AppendResult(interp, "Couldn't read track ",
  338. X                "number ",  num_str, "\n", MidiError,
  339. X                (char *)NULL);
  340. X            return (TCL_ERROR);
  341. X        }
  342. X    }
  343. X    hash_entry = Tcl_CreateHashEntry(&MidiFileHash, (char *)mfileId,
  344. X        &created_hash);
  345. X    if (!created_hash) {
  346. X        Tcl_AppendResult(interp, "Hash bucket for file alread ",
  347. X            "exists", (char *)NULL);
  348. X        return (TCL_ERROR);
  349. X    }
  350. X    Tcl_SetHashValue(hash_entry, mfile);
  351. X
  352. X    sprintf(interp->result, "mfile%d", mfileId++);
  353. X    return (TCL_OK);
  354. X}
  355. X
  356. Xint
  357. XTclm_MidiWrite(dummy, interp, argc, argv)
  358. X    ClientData dummy;
  359. X    Tcl_Interp *interp;
  360. X    int argc;
  361. X    char **argv;
  362. X{
  363. X    MIDI_FILE *mfile;
  364. X    OpenFile *filePtr;
  365. X    int fd;
  366. X    int i;
  367. X    int result;
  368. X
  369. X    /*
  370. X     * argv[0] - midiwrite
  371. X     * argv[1] - mfileId
  372. X     * argv[2] - fileId
  373. X     */
  374. X    if (argc != 3) {
  375. X        Tcl_AppendResult(interp, "bad # args: shoudl be \"",
  376. X            argv[0], " mfileId fileId\"", (char *)NULL);
  377. X        return (TCL_ERROR);
  378. X    }
  379. X    if ((result = TclGetOpenFile(interp, argv[2], &filePtr)) != TCL_OK)
  380. X        return (result);
  381. X
  382. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  383. X        return (result);
  384. X
  385. X    fd = fileno(filePtr->f);
  386. X
  387. X    if (!write_header_chunk(fd, &mfile->hchunk)) {
  388. X        Tcl_AppendResult(interp, "Couldn't write header chunk\n",
  389. X            MidiError, (char *)NULL);
  390. X        return (TCL_ERROR);
  391. X    }
  392. X    for (i = 0; i < mfile->hchunk.num_trks; i++) {
  393. X        if (!write_track_chunk(fd, &(mfile->tchunks[i]))) {
  394. X            sprintf(interp->result,
  395. X                "Coudln't write track chunk %d\n%s", i,
  396. X                MidiError);
  397. X            return (TCL_ERROR);
  398. X        }
  399. X    }
  400. X    return (TCL_OK);
  401. X}
  402. X
  403. Xint
  404. XTclm_MidiMerge(dummy, interp, argc, argv)
  405. X    ClientData dummy;
  406. X    Tcl_Interp *interp;
  407. X    int argc;
  408. X    char **argv;
  409. X{
  410. X    char **strs;
  411. X    char **substrs;
  412. X    MIDI_FILE *outmfile;
  413. X    MIDI_FILE **inmfile;
  414. X    TCHUNK **intrack;
  415. X    TCHUNK *outtrack;
  416. X    int *tscalar;
  417. X    char *chk_ptr;
  418. X    int delta;
  419. X    int endtime;
  420. X    int i;
  421. X    int ind;
  422. X    int numin;
  423. X    int num_strs;
  424. X    int num_substrs;
  425. X    int result;
  426. X
  427. X    /*
  428. X     * argv[0] - midimerge
  429. X     * argv[1] - {outmfile outtrack}
  430. X     * argv[2] - {{inmfile intrack tscalar} {inmfile intrack tscalar} ...}
  431. X     * argv[3] - delta
  432. X     */
  433. X    if (argc != 4) {
  434. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  435. X            argv[0], " {outmfile outtrack} {{inmfile intrack} ",
  436. X            "{inmfile intrack} ...} delta", (char *)NULL);
  437. X        return (TCL_ERROR);
  438. X    }
  439. X
  440. X    /* parse output fields */
  441. X    if ((result = Tcl_SplitList(interp, argv[1], &num_strs, &strs)) !=
  442. X        TCL_OK)
  443. X        return (result);
  444. X
  445. X    if (num_strs != 2) {
  446. X        Tcl_AppendResult(interp, "bad track designation: ",
  447. X            argv[1], (char *)NULL);
  448. X        return (TCL_ERROR);
  449. X    }
  450. X
  451. X    if ((result = Tclm_GetMFile(interp, strs[0], &outmfile)) != TCL_OK)
  452. X        return (result);
  453. X
  454. X    ind = (int)strtol(strs[1], &chk_ptr, 0);
  455. X    if (chk_ptr == strs[1] || ind < 0 || ind > outmfile->hchunk.num_trks) {
  456. X        Tcl_AppendResult(interp, "bad outtrack value: ", strs[1],
  457. X            (char *)NULL);
  458. X        return (TCL_ERROR);
  459. X    }
  460. X    free((char *)strs);
  461. X
  462. X    outtrack = &outmfile->tchunks[ind];
  463. X
  464. X    /* now parse input strs */
  465. X    if ((result = Tcl_SplitList(interp, argv[2], &num_strs, &strs)) !=
  466. X        TCL_OK)
  467. X        return (result);
  468. X
  469. X    numin = num_strs;
  470. X    if ((inmfile = (MIDI_FILE **)malloc(sizeof(MIDI_FILE *) * numin))
  471. X        == NULL) {
  472. X        Tcl_AppendResult(interp, "Not enough memory for infiles",
  473. X            (char *)NULL);
  474. X        free((char *)strs);
  475. X        return (TCL_ERROR);
  476. X    }
  477. X    if ((tscalar = (int *)malloc(sizeof(int) * numin)) == NULL) {
  478. X        Tcl_AppendResult(interp, "Not enough memory for tscalars",
  479. X            (char *)NULL);
  480. X        free((char *)strs);
  481. X        free((char *)inmfile);
  482. X        return (TCL_ERROR);
  483. X    }
  484. X    if ((intrack = (TCHUNK **)malloc(sizeof(TCHUNK *) * numin)) == NULL) {
  485. X        Tcl_AppendResult(interp, "Not enough memory for intracks",
  486. X            (char *)NULL);
  487. X        free((char *)strs);
  488. X        free((char *)inmfile);
  489. X        free((char *)tscalar);
  490. X        return (TCL_ERROR);
  491. X    }
  492. X
  493. X    for (i = 0; i < numin; i++) {
  494. X        /* parse each input pair */
  495. X        if ((result = Tcl_SplitList(interp, strs[i], &num_substrs,
  496. X            &substrs)) != TCL_OK) {
  497. X            free((char *)strs);
  498. X            free((char *)inmfile);
  499. X            free((char *)tscalar);
  500. X            free((char *)intrack);
  501. X            return (result);
  502. X        }
  503. X        if (num_substrs != 3) {
  504. X            Tcl_AppendResult(interp, "bad track designation: ",
  505. X                strs[i], (char *)NULL);
  506. X            free((char *)strs);
  507. X            free((char *)inmfile);
  508. X            free((char *)tscalar);
  509. X            free((char *)intrack);
  510. X            return (TCL_ERROR);
  511. X        }
  512. X        if ((result = Tclm_GetMFile(interp, substrs[0], &inmfile[i]))
  513. X            != TCL_OK) {
  514. X            free((char *)strs);
  515. X            free((char *)inmfile);
  516. X            free((char *)tscalar);
  517. X            free((char *)intrack);
  518. X            return (result);
  519. X        }
  520. X        ind = (int)strtol(substrs[1], &chk_ptr, 0);
  521. X        if (chk_ptr == substrs[1] || ind < 0 ||
  522. X            ind > inmfile[i]->hchunk.num_trks) {
  523. X            Tcl_AppendResult(interp, "bad outtrack value: ",
  524. X                substrs[1], (char *)NULL);
  525. X            free((char *)strs);
  526. X            free((char *)inmfile);
  527. X            free((char *)tscalar);
  528. X            free((char *)intrack);
  529. X            free((char *)substrs);
  530. X            return (TCL_ERROR);
  531. X        }
  532. X        intrack[i] = &inmfile[i]->tchunks[ind];
  533. X
  534. X        tscalar[i] = (int)strtol(substrs[2], &chk_ptr, 0);
  535. X        if (chk_ptr == substrs[2]) {
  536. X            Tcl_AppendResult(interp, "bad tscalar value: ",
  537. X                substrs[2], (char *)NULL);
  538. X            free((char *)strs);
  539. X            free((char *)inmfile);
  540. X            free((char *)tscalar);
  541. X            free((char *)intrack);
  542. X            free((char *)substrs);
  543. X            return (TCL_ERROR);
  544. X        }
  545. X
  546. X        free((char *)substrs);
  547. X    }
  548. X    free((char *)strs);
  549. X
  550. X    delta = (int)strtol(argv[3], &chk_ptr, 0);
  551. X    if (chk_ptr == argv[3]) {
  552. X        Tcl_AppendResult(interp, "bad delta value: ", argv[3],
  553. X            (char *)NULL);
  554. X        free((char *)inmfile);
  555. X        free((char *)tscalar);
  556. X        free((char *)intrack);
  557. X        return (TCL_ERROR);
  558. X    }
  559. X
  560. X    if ((endtime = merge_tracks(outtrack, intrack, tscalar, numin, delta))
  561. X        == -1) {
  562. X        Tcl_AppendResult(interp, "Couldn't merge files\n",
  563. X            MidiError, (char *)NULL);
  564. X        free((char *)inmfile);
  565. X        free((char *)tscalar);
  566. X        free((char *)intrack);
  567. X        return (TCL_ERROR);
  568. X    }
  569. X
  570. X    sprintf(interp->result, "%d", endtime);
  571. X    free((char *)inmfile);
  572. X    free((char *)tscalar);
  573. X    free((char *)intrack);
  574. X    return (TCL_OK);
  575. X}
  576. X
  577. Xint
  578. XTclm_MidiFree(dummy, interp, argc, argv)
  579. X    ClientData dummy;
  580. X    Tcl_Interp *interp;
  581. X    int argc;
  582. X    char **argv;
  583. X{
  584. X    MIDI_FILE *mfile;
  585. X    int mfileId;
  586. X    int result;
  587. X
  588. X    /*
  589. X     * argv[0] - midifree
  590. X     * argv[1] - mfileId
  591. X     */
  592. X    if (argc != 2) {
  593. X        Tcl_AppendResult(interp, "bad # args: should be \"",
  594. X            argv[0], " mfileId\"", (char *)NULL);
  595. X        return (TCL_ERROR);
  596. X    }
  597. X
  598. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  599. X        return (result);
  600. X
  601. X    mfileId = (int)strtol(argv[1] + 5, NULL, 0);
  602. X    Tcl_DeleteHashEntry(Tcl_FindHashEntry(&MidiFileHash, (char *)mfileId));
  603. X
  604. X    free(mfile->tchunks);
  605. X    free(mfile);
  606. X    return (TCL_OK);
  607. X}
  608. X
  609. Xint
  610. XTclm_GetMFile(interp, FileId, mfile)
  611. X    Tcl_Interp *interp;
  612. X    char *FileId;
  613. X    MIDI_FILE **mfile;
  614. X{
  615. X    Tcl_HashEntry *hash_entry;
  616. X    char *chk_ptr;
  617. X    int mfileId;
  618. X
  619. X    if (strncmp(FileId, "mfile", 5) != 0) {
  620. X        Tcl_AppendResult(interp, "Bad MIDI file identifier \"",
  621. X            FileId, "\"", (char *)NULL);
  622. X        return (TCL_ERROR);
  623. X    }
  624. X
  625. X    mfileId = (int)strtol(FileId + 5, &chk_ptr, 0);
  626. X    if (chk_ptr == FileId + 5) {
  627. X        Tcl_AppendResult(interp, "Bad MIDI file identifier \"",
  628. X            FileId, "\"", (char *)NULL);
  629. X        return (TCL_ERROR);
  630. X    }
  631. X    if ((hash_entry = Tcl_FindHashEntry(&MidiFileHash, (char *)mfileId))
  632. X        == NULL) {
  633. X        Tcl_AppendResult(interp, FileId, " doesn't exist",
  634. X            (char *)NULL);
  635. X        return (TCL_ERROR);
  636. X    }
  637. X    *mfile = (MIDI_FILE *)Tcl_GetHashValue(hash_entry);
  638. X    return (TCL_OK);
  639. X}
  640. X
  641. Xint
  642. XTclm_SetMFile(interp, FileId, mfile)
  643. X    Tcl_Interp *interp;
  644. X    char *FileId;
  645. X    MIDI_FILE *mfile;
  646. X{
  647. X    Tcl_HashEntry *hash_entry;
  648. X    char *chk_ptr;
  649. X    int mfileId;
  650. X
  651. X    if (strncmp(FileId, "mfile", 5) != 0) {
  652. X        Tcl_AppendResult(interp, "Bad MIDI file identifier \"",
  653. X            FileId, "\"", (char *)NULL);
  654. X        return (TCL_ERROR);
  655. X    }
  656. X
  657. X    mfileId = (int)strtol(FileId + 5, &chk_ptr, 0);
  658. X    if (chk_ptr == FileId + 5) {
  659. X        Tcl_AppendResult(interp, "Bad MIDI file identifier \"",
  660. X            FileId, "\"", (char *)NULL);
  661. X        return (TCL_ERROR);
  662. X    }
  663. X    if ((hash_entry = Tcl_FindHashEntry(&MidiFileHash, (char *)mfileId))
  664. X        == NULL) {
  665. X        Tcl_AppendResult(interp, FileId, " doesn't exist",
  666. X            (char *)NULL);
  667. X        return (TCL_ERROR);
  668. X    }
  669. X    Tcl_SetHashValue(hash_entry, (char *)mfile);
  670. X    return (TCL_OK);
  671. X}
  672. X
  673. Xint
  674. XTclm_NumTracks(interp, argc, argv)
  675. X    Tcl_Interp *interp;
  676. X    int argc;
  677. X    char **argv;
  678. X{
  679. X    MIDI_FILE *mfile;
  680. X    char *chk_ptr;
  681. X    int i;
  682. X    int result;
  683. X    int num_trks;
  684. X
  685. X    /*
  686. X     * argv[0] - midiconfig
  687. X     * argv[1] - mfileId
  688. X     * argv[2] - tracks
  689. X     * argv[3] - optional number of tracks
  690. X     */
  691. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  692. X        return (result);
  693. X
  694. X    if (argc == 3)
  695. X        sprintf(interp->result, "%d", mfile->hchunk.num_trks);
  696. X    else {
  697. X        num_trks = (int)strtol(argv[3], &chk_ptr, 0);
  698. X        if (chk_ptr == argv[3]) {
  699. X            Tcl_AppendResult(interp, "Bad number of tracks ",
  700. X                argv[3], (char *)NULL);
  701. X            return (TCL_ERROR);
  702. X        }
  703. X        if (mfile->hchunk.format == 0 && num_trks > 1) {
  704. X            Tcl_AppendResult(interp, "Format 0 files can only ",
  705. X                "have zero or one tracks, not ", argv[3],
  706. X                (char *)NULL);
  707. X            return (TCL_ERROR);
  708. X        }
  709. X        if (mfile->tchunks == NULL) {
  710. X            if (num_trks != 0) {
  711. X                if ((mfile->tchunks = (TCHUNK *)malloc(
  712. X                    sizeof(TCHUNK) * num_trks)) == NULL) {
  713. X                    Tcl_AppendResult(interp,
  714. X                        "Not enough memory for ", argv[3],
  715. X                        " tracks", (char *)NULL);
  716. X                }
  717. X            }
  718. X        } else {
  719. X            if (num_trks == 0) {
  720. X                free((char *)mfile->tchunks);
  721. X                mfile->tchunks = NULL;
  722. X            } else {
  723. X                if ((mfile->tchunks = (TCHUNK *)realloc(
  724. X                    mfile->tchunks, sizeof(TCHUNK) * num_trks))
  725. X                    == NULL) {
  726. X                    Tcl_AppendResult(interp,
  727. X                        "Not enough memory for ", argv[3],
  728. X                        " tracks", (char *)NULL);
  729. X                }
  730. X            }
  731. X        }
  732. X
  733. X        for (i = mfile->hchunk.num_trks; i < num_trks; i++)
  734. X            init_track(&mfile->tchunks[i]);
  735. X
  736. X        mfile->hchunk.num_trks = num_trks;
  737. X        if ((result = Tclm_SetMFile(interp, argv[1], mfile)) !=
  738. X            TCL_OK)
  739. X            return (result);
  740. X    }
  741. X    return (TCL_OK);
  742. X}
  743. X
  744. Xint
  745. XTclm_Format(interp, argc, argv)
  746. X    Tcl_Interp *interp;
  747. X    int argc;
  748. X    char **argv;
  749. X{
  750. X    MIDI_FILE *mfile;
  751. X    char *chk_ptr;
  752. X    int result;
  753. X    int format;
  754. X
  755. X    /*
  756. X     * argv[0] - midiconfig
  757. X     * argv[1] - mfileId
  758. X     * argv[2] - format
  759. X     * argv[3] - optional arg
  760. X     */
  761. X
  762. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  763. X        return (result);
  764. X
  765. X    if (argc == 3)
  766. X        sprintf(interp->result, "%d", mfile->hchunk.format);
  767. X    else {
  768. X        format = (int)strtol(argv[3], &chk_ptr, 0);
  769. X        if (chk_ptr == argv[3] || format < 0 || format > 2) {
  770. X            Tcl_AppendResult(interp, "Bad format",
  771. X                argv[2], (char *)NULL);
  772. X            return (TCL_ERROR);
  773. X        }
  774. X        if (format == 0 && mfile->hchunk.num_trks > 1) {
  775. X            Tcl_AppendResult(interp, argv[1], " has too ",
  776. X                "many tracks to be format 0", (char *)NULL);
  777. X            return (TCL_ERROR);
  778. X        }
  779. X        mfile->hchunk.format = format;
  780. X        if ((result = Tclm_SetMFile(interp, argv[1], mfile)) !=
  781. X            TCL_OK)
  782. X            return (result);
  783. X    }
  784. X    return (TCL_OK);
  785. X}
  786. X
  787. Xint
  788. XTclm_Division(interp, argc, argv)
  789. X    Tcl_Interp *interp;
  790. X    int argc;
  791. X    char **argv;
  792. X{
  793. X    MIDI_FILE *mfile;
  794. X    char *chk_ptr;
  795. X    int division;
  796. X    int result;
  797. X
  798. X    /*
  799. X     * argv[0] - midiconfig
  800. X     * argv[1] - mfileId
  801. X     * argv[2] - division
  802. X     * argv[3] - optional arg
  803. X     */
  804. X
  805. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  806. X        return (result);
  807. X
  808. X    if (argc == 3)
  809. X        sprintf(interp->result, "%d", mfile->hchunk.division);
  810. X    else {
  811. X        division = (int)strtol(argv[3], &chk_ptr, 0);
  812. X        if (chk_ptr == argv[3]) {
  813. X            Tcl_AppendResult(interp, "bad division value ",
  814. X                argv[3], (char *)NULL);
  815. X            return (TCL_ERROR);
  816. X        }
  817. X        mfile->hchunk.division = division;
  818. X        if ((result = Tclm_SetMFile(interp, argv[1], mfile)) !=
  819. X            TCL_OK)
  820. X            return (result);
  821. X    }
  822. X    return (TCL_OK);
  823. X}
  824. X
  825. Xint
  826. XTclm_MidiGet(foo, interp, argc, argv)
  827. X    ClientData foo;
  828. X    Tcl_Interp *interp;
  829. X    int argc;
  830. X    char **argv;
  831. X{
  832. X    long timing;
  833. X    char *chk_ptr;
  834. X    unsigned char *event_ptr;
  835. X    MIDI_FILE *mfile;
  836. X    Tcl_Interp *temp_interp;
  837. X    int channel;
  838. X    int delta;
  839. X    int denom;
  840. X    int data_length;
  841. X    int event_size;
  842. X    int i;
  843. X    int normal_type;
  844. X    int result;
  845. X    int track_num;
  846. X    EVENT_TYPE event_type;
  847. X    char dummy[MAX_EVENT_SIZE];
  848. X    unsigned char event[MAX_EVENT_SIZE];
  849. X    unsigned char running_state;
  850. X
  851. X    /*
  852. X     * argv[0] - midiget
  853. X     * argv[1] - mfileId
  854. X     * argv[2] - track number
  855. X     */
  856. X
  857. X    if (argc != 3) {
  858. X        Tcl_AppendResult(interp, "bad # args: should be \"",
  859. X            argv[0], " mfileId track_num\"", (char *)NULL);
  860. X        return (TCL_ERROR);
  861. X    }
  862. X
  863. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  864. X        return (result);
  865. X
  866. X    track_num = (int)strtol(argv[2], &chk_ptr, 0);
  867. X    if (chk_ptr == argv[2] || track_num < 0 ||
  868. X        track_num > mfile->hchunk.num_trks - 1) {
  869. X        Tcl_AppendResult(interp, "Bad track number ", argv[2],
  870. X            (char *)NULL);
  871. X        return (TCL_ERROR);
  872. X    }
  873. X    if ((event_size = get_smf_event(&(mfile->tchunks[track_num]), event,
  874. X        &event_type)) == -1) {
  875. X        Tcl_AppendResult(interp, "Couldn't get event from ", argv[1],
  876. X            " track ", argv[2], "\n", MidiError, (char *)NULL);
  877. X        return (TCL_ERROR);
  878. X    }
  879. X    if (event_size == 0) {
  880. X        Tcl_AppendResult(interp, "EOT", (char *)NULL);
  881. X        return (TCL_OK);
  882. X    }
  883. X
  884. X    /* get timing and skip over it */
  885. X    event_ptr = event;
  886. X    timing = var2fix(event_ptr, &delta);
  887. X    sprintf(dummy, "%ld ", timing);
  888. X    Tcl_AppendResult(interp, dummy, (char *)NULL);
  889. X    event_ptr += delta;
  890. X    event_size -= delta;
  891. X
  892. X    switch(event_type) {
  893. X    case NORMAL:
  894. X        if (event_ptr[0] & 0x80) {
  895. X            running_state = event_ptr[0];
  896. X            event_ptr++;
  897. X            event_size--;
  898. X        } else {
  899. X            running_state =
  900. X                get_running_state(&mfile->tchunks[track_num]);
  901. X        }
  902. X        normal_type = running_state & 0xf0;
  903. X        channel = running_state & 0x0f;
  904. X        switch(normal_type) {
  905. X        case 0x80:
  906. X            sprintf(dummy, "noteoff %d 0x%02x 0x%02x",
  907. X                channel, event_ptr[0], event_ptr[1]);
  908. X            Tcl_AppendResult(interp, dummy, (char *)NULL);
  909. X            break;
  910. X        case 0x90:
  911. X            sprintf(dummy, "noteon %d 0x%02x 0x%02x",
  912. X                channel, event_ptr[0], event_ptr[1]);
  913. X            Tcl_AppendResult(interp, dummy, (char *)NULL);
  914. X            break;
  915. X        case 0xa0:
  916. X            sprintf(dummy, "keypressure %d 0x%02x 0x%02x",
  917. X                channel, event_ptr[0], event_ptr[1]);
  918. X            Tcl_AppendResult(interp, dummy, (char *)NULL);
  919. X            break;
  920. X        case 0xb0:
  921. X            sprintf(dummy, "parameter %d 0x%02x 0x%02x",
  922. X                channel, event_ptr[0], event_ptr[1]);
  923. X            Tcl_AppendResult(interp, dummy, (char *)NULL);
  924. X            break;
  925. X        case 0xc0:
  926. X            sprintf(dummy, "program %d 0x%02x",
  927. X                channel, event_ptr[0]);
  928. X            Tcl_AppendResult(interp, dummy, (char *)NULL);
  929. X            break;
  930. X        case 0xd0:
  931. X            sprintf(dummy, "channelpressure %d 0x%02x",
  932. X                channel, event_ptr[0]);
  933. X            Tcl_AppendResult(interp, dummy, (char *)NULL);
  934. X            break;
  935. X        case 0xe0:
  936. X            sprintf(dummy, "pitchwheel %d 0x%04x",
  937. X                channel, ((event_ptr[1] << 7) & 0x3f80) |
  938. X                event_ptr[0]);
  939. X            Tcl_AppendResult(interp, dummy, (char *)NULL);
  940. X            break;
  941. X        }
  942. X        break;
  943. X    case SYSEX:
  944. X        Tcl_AppendResult(interp, "sysex ", (char *)NULL);
  945. X        if (*event_ptr == 0xf7)
  946. X            Tcl_AppendResult(interp, "cont ", (char *)NULL);
  947. X        event_ptr++;
  948. X        event_size--;
  949. X        temp_interp = Tcl_CreateInterp();
  950. X        data_length = var2fix(event_ptr, &delta);
  951. X        for (i = 0; i < data_length; i++) {
  952. X            sprintf(dummy, "0x%02x", event_ptr[delta + i]);
  953. X            Tcl_AppendElement(temp_interp, dummy, 0);
  954. X        }
  955. X        Tcl_AppendElement(interp, temp_interp->result, 0);
  956. X        Tcl_DeleteInterp(temp_interp);
  957. X        break;
  958. X    case METASEQNUM:
  959. X        sprintf(dummy, "metaseqnum %d",
  960. X            ((event_ptr[3] << 8) & 0xff00) | (event_ptr[4] & 0xff));
  961. X        Tcl_AppendResult(interp, dummy, (char *)NULL);
  962. X        break;
  963. X    case METATEXT:
  964. X        Tcl_AppendResult(interp, "metatext ", (char *)NULL);
  965. X        Tclm_MakeMetaText(interp, &event_ptr[2]);
  966. X        break;
  967. X    case METACPY:
  968. X        Tcl_AppendResult(interp, "metacpy ", (char *)NULL);
  969. X        Tclm_MakeMetaText(interp, &event_ptr[2]);
  970. X        break;
  971. X    case METASEQNAME:
  972. X        Tcl_AppendResult(interp, "metaseqname ", (char *)NULL);
  973. X        Tclm_MakeMetaText(interp, &event_ptr[2]);
  974. X        break;
  975. X    case METAINSTNAME:
  976. X        Tcl_AppendResult(interp, "metainstname ", (char *)NULL);
  977. X        Tclm_MakeMetaText(interp, &event_ptr[2]);
  978. X        break;
  979. X    case METALYRIC:
  980. X        Tcl_AppendResult(interp, "metalyric ", (char *)NULL);
  981. X        Tclm_MakeMetaText(interp, &event_ptr[2]);
  982. X        break;
  983. X    case METAMARKER:
  984. X        Tcl_AppendResult(interp, "metamarker ", (char *)NULL);
  985. X        Tclm_MakeMetaText(interp, &event_ptr[2]);
  986. X        break;
  987. X    case METACUE:
  988. X        Tcl_AppendResult(interp, "metacue ", (char *)NULL);
  989. X        Tclm_MakeMetaText(interp, &event_ptr[2]);
  990. X        break;
  991. X    case METACHANPREFIX:
  992. X        temp_interp = Tcl_CreateInterp();
  993. X        data_length = var2fix(&event_ptr[2], &delta);
  994. X        for (i = 0; i < data_length; i++) {
  995. X            sprintf(dummy, "0x%02x", event_ptr[2 + delta + i]);
  996. X            Tcl_AppendElement(temp_interp, dummy, 0);
  997. X        }
  998. X        Tcl_AppendResult(interp, "metachanprefix {",
  999. X            temp_interp->result, "}", (char *)NULL);
  1000. X        Tcl_DeleteInterp(temp_interp);
  1001. X        break;
  1002. X    case METAEOT:
  1003. X        Tcl_AppendResult(interp, "metaeot", (char *)NULL);
  1004. X        break;
  1005. X    case METATEMPO:
  1006. X        sprintf(dummy, "metatempo %d", 60000000 /
  1007. X            (event_ptr[3] * 0x10000 + event_ptr[4] * 0x100 +
  1008. X            event_ptr[5]));
  1009. X        Tcl_AppendResult(interp, dummy, (char *)NULL);
  1010. X        break;
  1011. X    case METASMPTE:
  1012. X        sprintf(dummy, "metasmpte %d %d %d %d %d", event_ptr[3],
  1013. X            event_ptr[4], event_ptr[5], event_ptr[6], event_ptr[7]);
  1014. X        Tcl_AppendResult(interp, dummy, (char *)NULL);
  1015. X        break;
  1016. X    case METATIME:
  1017. X        denom = 1;
  1018. X        for (i = 0; i < event_ptr[4]; i++)
  1019. X            denom *= 2;
  1020. X        sprintf(dummy, "metatime %d %d %d %d", event_ptr[3], denom,
  1021. X            event_ptr[5], event_ptr[6]);
  1022. X        Tcl_AppendResult(interp, dummy, (char *)NULL);
  1023. X        break;
  1024. X    case METAKEY:
  1025. X        Tcl_AppendResult(interp, "metakey \"",
  1026. X            key_strings[(int)event_ptr[3] + 7], "\" ",
  1027. X            (char *)NULL);
  1028. X        if (event_ptr[4] == 0)
  1029. X            Tcl_AppendResult(interp, "major", (char *)NULL);
  1030. X        else
  1031. X            Tcl_AppendResult(interp, "minor", (char *)NULL);
  1032. X        break;
  1033. X    case METASEQSPEC:
  1034. X        Tcl_AppendResult(interp, "metaseqspec", (char *)NULL);
  1035. X        break;
  1036. X    }
  1037. X
  1038. X    return (TCL_OK);
  1039. X}
  1040. X
  1041. Xstatic void
  1042. XTclm_MakeMetaText(interp, event)
  1043. X    Tcl_Interp *interp;
  1044. X    unsigned char *event;
  1045. X{
  1046. X    int data_length;
  1047. X    int delta;
  1048. X    int i;
  1049. X    char dummy[MAX_EVENT_SIZE];
  1050. X
  1051. X    data_length = var2fix(event, &delta);
  1052. X    for (i = 0; i < data_length; i++)
  1053. X        dummy[i] = event[delta + i];
  1054. X    dummy[i] = '\0';
  1055. X    Tcl_AppendResult(interp, "\"", dummy, "\"", (char *)NULL);
  1056. X}
  1057. X
  1058. Xstatic int
  1059. XTclm_ConvertTiming(interp, str, timing, timing_length)
  1060. X    Tcl_Interp *interp;
  1061. X    char *str;
  1062. X    unsigned char *timing;
  1063. X    int *timing_length;
  1064. X{
  1065. X    long time_long;
  1066. X    int i;
  1067. X    int num_bytes;
  1068. X    int result;
  1069. X    char *chk_ptr;
  1070. X    char **bytes_str;
  1071. X
  1072. X    if ((result = Tcl_SplitList(interp, str, &num_bytes, &bytes_str)) !=
  1073. X        TCL_OK)
  1074. X        return (result);
  1075. X
  1076. X    if (num_bytes == 1) {
  1077. X        time_long = strtol(bytes_str[0], &chk_ptr, 0);
  1078. X        if (bytes_str[0] == chk_ptr) {
  1079. X            Tcl_AppendResult(interp, "Bad timing value ",
  1080. X                bytes_str[0], (char *)NULL);
  1081. X            free((char *)bytes_str);
  1082. X            return (TCL_ERROR);
  1083. X        }
  1084. X        *timing_length = fix2var(time_long, timing);
  1085. X    } else {
  1086. X
  1087. X        for (i = 0; i < num_bytes; i++) {
  1088. X            timing[i] = (unsigned char)strtol(bytes_str[i],
  1089. X                &chk_ptr, 0);
  1090. X            if (chk_ptr == bytes_str[i]) {
  1091. X                Tcl_AppendResult(interp, "Bad timing data ",
  1092. X                    bytes_str[i], (char *)NULL);
  1093. X                free((char *)bytes_str);
  1094. X                return (TCL_ERROR);
  1095. X            }
  1096. X        }
  1097. X        *timing_length = num_bytes;
  1098. X    }
  1099. X    free((char *)bytes_str);
  1100. X    return (TCL_OK);
  1101. X}
  1102. X
  1103. Xstatic int
  1104. XTclm_ConvertBytes(interp, str, bytes, num_bytes)
  1105. X    Tcl_Interp *interp;
  1106. X    char *str;
  1107. X    unsigned char *bytes;
  1108. X    int *num_bytes;
  1109. X{
  1110. X    int i;
  1111. X    int result;
  1112. X    char *chk_ptr;
  1113. X    char **bytes_str;
  1114. X
  1115. X    if ((result = Tcl_SplitList(interp, str, num_bytes, &bytes_str)) !=
  1116. X        TCL_OK)
  1117. X        return (result);
  1118. X
  1119. X    for (i = 0; i < *num_bytes; i++) {
  1120. X        *bytes++ = (unsigned char)strtol(bytes_str[i], &chk_ptr, 0);
  1121. X        if (chk_ptr == bytes_str[i]) {
  1122. X            Tcl_AppendResult(interp, "Bad event data ",
  1123. X                bytes_str[i], (char *)NULL);
  1124. X            free((char *)bytes_str);
  1125. X            return (TCL_ERROR);
  1126. X        }
  1127. X    }
  1128. X    free((char *)bytes_str);
  1129. X    return (TCL_OK);
  1130. X}
  1131. X
  1132. Xint
  1133. XTclm_MidiPut(dummy, interp, argc, argv)
  1134. X    ClientData dummy;
  1135. X    Tcl_Interp *interp;
  1136. X    int argc;
  1137. X    char **argv;
  1138. X{
  1139. X    char *chk_ptr;
  1140. X    char *event_name;
  1141. X    char *event_ptr;
  1142. X    MIDI_FILE *mfile;
  1143. X    int bad_event;
  1144. X    int i;
  1145. X    int length;
  1146. X    int num_bytes;
  1147. X    int result;
  1148. X    int timing_length;
  1149. X    int track_num;
  1150. X    unsigned char timing[4];
  1151. X    unsigned char event[MAX_EVENT_SIZE];
  1152. X
  1153. X    /*
  1154. X     * argv[0] - midiput
  1155. X     * argv[1] - mfileId
  1156. X     * argv[2] - track number
  1157. X     * argv[3] - timing
  1158. X     * argv[4] - event name
  1159. X     * argv[5] - event specific data
  1160. X     * argv[6] - 
  1161. X         * etc.
  1162. X     */
  1163. X
  1164. X    if (argc < 5) {
  1165. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  1166. X            "midiput mfileId track timing eventname ?args ...?\"",
  1167. X            (char *)NULL);
  1168. X        return (TCL_ERROR);
  1169. X    }
  1170. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  1171. X        return (result);
  1172. X
  1173. X    track_num = (int)strtol(argv[2], &chk_ptr, 0);
  1174. X    if (chk_ptr == argv[2] || track_num < 0 ||
  1175. X        track_num > mfile->hchunk.num_trks - 1) {
  1176. X        Tcl_AppendResult(interp, "Bad track number ", argv[2],
  1177. X            (char *)NULL);
  1178. X        return (TCL_ERROR);
  1179. X    }
  1180. X
  1181. X    if ((result = Tclm_ConvertTiming(interp, argv[3], timing,
  1182. X        &timing_length)) != TCL_OK)
  1183. X        return (result);
  1184. X
  1185. X    for (i = 0; i < timing_length; i++)
  1186. X        event[i] = timing[i];
  1187. X    num_bytes = timing_length;
  1188. X    /* do different things depending on the event type */
  1189. X    event_name = argv[4];
  1190. X    length = strlen(event_name);
  1191. X
  1192. X    bad_event = 0;
  1193. X
  1194. X    switch(event_name[0]) {
  1195. X    case 'c':
  1196. X        if (strncmp(event_name, "channelpressure", length) != 0)
  1197. X            bad_event = 1;
  1198. X        else {
  1199. X            /*
  1200. X             * argv[5] - channel
  1201. X             * argv[6] - pressure
  1202. X             */
  1203. X            unsigned char channel;
  1204. X            unsigned char pressure;
  1205. X
  1206. X            if (argc != 7) {
  1207. X                Tcl_AppendResult(interp, "wrong # args: ",
  1208. X                    "should be \"midiput mfileId track ",
  1209. X                    "timing channelpressure channel ",
  1210. X                    "pressure\"", (char *)NULL);
  1211. X                return (TCL_ERROR);
  1212. X            }
  1213. X            channel = (unsigned char)strtol(argv[5], &chk_ptr, 0);
  1214. X            if (chk_ptr == argv[5] || channel & 0x80) {
  1215. X                Tcl_AppendResult(interp, "bad channel ",
  1216. X                    argv[5], (char *)NULL);
  1217. X                return (TCL_ERROR);
  1218. X            }
  1219. X            pressure = (unsigned char)strtol(argv[6], &chk_ptr, 0);
  1220. X            if (chk_ptr == argv[6] || pressure & 0x80) {
  1221. X                Tcl_AppendResult(interp, "bad pressure ",
  1222. X                    argv[6], (char *)NULL);
  1223. X                return (TCL_ERROR);
  1224. X            }
  1225. X
  1226. X            event[num_bytes++] = 0xd0 + channel;
  1227. X            event[num_bytes++] = pressure;
  1228. X        }
  1229. X        break;
  1230. X    case 'k':
  1231. X        if (strncmp(event_name, "keypressure", length) != 0)
  1232. X            bad_event = 1;
  1233. X        else {
  1234. X            /*
  1235. X             * argv[5] - channel
  1236. X             * argv[6] - pitch
  1237. X             * argv[7] - pressure
  1238. X             */
  1239. X            unsigned char channel;
  1240. X            unsigned char pitch;
  1241. X            unsigned char pressure;
  1242. X
  1243. X            if (argc != 8) {
  1244. X                Tcl_AppendResult(interp, "wrong # args: ",
  1245. X                    "should be \"midiput mfileId track ",
  1246. X                    "timing keypressure channel ",
  1247. X                    "pitch pressure\"", (char *)NULL);
  1248. X                return (TCL_ERROR);
  1249. X            }
  1250. X            channel = (unsigned char)strtol(argv[5], &chk_ptr, 0);
  1251. X            if (chk_ptr == argv[5] || channel & 0x80) {
  1252. X                Tcl_AppendResult(interp, "bad channel ",
  1253. X                    argv[5], (char *)NULL);
  1254. X                return (TCL_ERROR);
  1255. X            }
  1256. X            pitch = (unsigned char)strtol(argv[6], &chk_ptr, 0);
  1257. X            if (chk_ptr == argv[6] || pitch & 0x80) {
  1258. X                Tcl_AppendResult(interp, "bad pitch ",
  1259. X                    argv[6], (char *)NULL);
  1260. X                return (TCL_ERROR);
  1261. X            }
  1262. X            pressure = (unsigned char)strtol(argv[7], &chk_ptr, 0);
  1263. X            if (chk_ptr == argv[7] || pressure & 0x80) {
  1264. X                Tcl_AppendResult(interp, "bad pressure ",
  1265. X                    argv[7], (char *)NULL);
  1266. X                return (TCL_ERROR);
  1267. X            }
  1268. X
  1269. X            event[num_bytes++] = 0xa0 + channel;
  1270. X            event[num_bytes++] = pitch;
  1271. X            event[num_bytes++] = pressure;
  1272. X        }
  1273. X        break;
  1274. X    case 'm':
  1275. X        /* META stuff */
  1276. X        if ((result = Tclm_ConvertMeta(interp, argc - 4, argv + 4,
  1277. X            event, &num_bytes)) != TCL_OK)
  1278. X            return (result);
  1279. X        break;
  1280. X    case 'n':
  1281. X        if (strncmp(event_name, "noteoff", length) == 0 ||
  1282. X            strncmp(event_name, "noteon", length) == 0) {
  1283. X            /*
  1284. X             * argv[5] - channel
  1285. X             * argv[6] - pitch
  1286. X             * argv[7] - velocity
  1287. X             */
  1288. X            unsigned char channel;
  1289. X            unsigned char pitch;
  1290. X            unsigned char velocity;
  1291. X
  1292. X            if (event_name[5] == 'n') {
  1293. X                if (argc != 8) {
  1294. X                    Tcl_AppendResult(interp, "wrong #",
  1295. X                        "args: should be \"midiput ",
  1296. X                        "mfileId track timing noteon ",
  1297. X                        "channel pitch velocity\"",
  1298. X                        (char *)NULL);
  1299. X                    return (TCL_ERROR);
  1300. X                }
  1301. X            } else {
  1302. X                if (argc != 7 && argc != 8) {
  1303. X                    Tcl_AppendResult(interp, "wrong #",
  1304. X                        "args: should be \"midiput ",
  1305. X                        "mfileId track timing noteoff ",
  1306. X                        "channel pitch ?velocity?\"",
  1307. X                        (char *)NULL);
  1308. X                    return (TCL_ERROR);
  1309. X                }
  1310. X            }
  1311. X            channel = (unsigned char)strtol(argv[5], &chk_ptr, 0);
  1312. X            if (chk_ptr == argv[5] || channel & 0x80) {
  1313. X                Tcl_AppendResult(interp, "bad channel ",
  1314. X                    argv[5], (char *)NULL);
  1315. X                return (TCL_ERROR);
  1316. X            }
  1317. X            pitch = (unsigned char)strtol(argv[6], &chk_ptr, 0);
  1318. X            if (chk_ptr == argv[6] || pitch & 0x80) {
  1319. X                Tcl_AppendResult(interp, "bad pitch ",
  1320. X                    argv[6], (char *)NULL);
  1321. X                return (TCL_ERROR);
  1322. X            }
  1323. X            if (argc == 8) {
  1324. X                velocity = (unsigned char)strtol(argv[7],
  1325. X                    &chk_ptr, 0);
  1326. X                if (chk_ptr == argv[7] || velocity & 0x80) {
  1327. X                    Tcl_AppendResult(interp, "bad ",
  1328. X                        "velocity ", argv[7],
  1329. X                        (char *)NULL);
  1330. X                    return (TCL_ERROR);
  1331. X                }
  1332. X            } else {
  1333. X                velocity = 0;
  1334. X            }
  1335. X
  1336. X            /*
  1337. X             * if noteoff velocity is zero use noteon
  1338. X             * This will make better use of running state
  1339. X             */
  1340. X            if (event_name[5] == 'f' && velocity != 0)
  1341. X                event[num_bytes++] = 0x80 + channel;
  1342. X            else
  1343. X                event[num_bytes++] = 0x90 + channel;
  1344. X            event[num_bytes++] = pitch;
  1345. X            event[num_bytes++] = velocity;
  1346. X        } else
  1347. X            bad_event = 1;
  1348. X        break;
  1349. X    case 'p':
  1350. X        if (strncmp(event_name, "parameter", length) == 0) {
  1351. X            /*
  1352. X             * argv[5] - channel
  1353. X             * argv[6] - param
  1354. X             * argv[7] - setting
  1355. X             */
  1356. X            unsigned char channel;
  1357. X            unsigned char param;
  1358. X            unsigned char setting;
  1359. X
  1360. X            if (argc != 8) {
  1361. X                Tcl_AppendResult(interp, "wrong # args: ",
  1362. X                    "should be \"midiput mfileId track ",
  1363. X                    "timing parameter channel ",
  1364. X                    "param setting\"", (char *)NULL);
  1365. X                return (TCL_ERROR);
  1366. X            }
  1367. X            channel = (unsigned char)strtol(argv[5], &chk_ptr, 0);
  1368. X            if (chk_ptr == argv[5] || channel & 0x80) {
  1369. X                Tcl_AppendResult(interp, "bad channel ",
  1370. X                    argv[5], (char *)NULL);
  1371. X                return (TCL_ERROR);
  1372. X            }
  1373. X            param = (unsigned char)strtol(argv[6], &chk_ptr, 0);
  1374. X            if (chk_ptr == argv[6] || param & 0x80) {
  1375. X                Tcl_AppendResult(interp, "bad parameter ",
  1376. X                    argv[6], (char *)NULL);
  1377. X                return (TCL_ERROR);
  1378. X            }
  1379. X            setting = (unsigned char)strtol(argv[7], &chk_ptr, 0);
  1380. X            if (chk_ptr == argv[7] || setting & 0x80) {
  1381. X                Tcl_AppendResult(interp, "bad setting ",
  1382. X                    argv[7], (char *)NULL);
  1383. X                return (TCL_ERROR);
  1384. X            }
  1385. X
  1386. X            event[num_bytes++] = 0xb0 + channel;
  1387. X            event[num_bytes++] = param;
  1388. X            event[num_bytes++] = setting;
  1389. X        } else if (strncmp(event_name, "pitchwheel", length) == 0) {
  1390. X            /*
  1391. X             * argv[5] - channel
  1392. X             * argv[6] - value
  1393. X             */
  1394. X            int value;
  1395. X            unsigned char channel;
  1396. X
  1397. X            if (argc != 7) {
  1398. X                Tcl_AppendResult(interp, "wrong # args: ",
  1399. X                    "should be \"midiput mfileId track ",
  1400. X                    "timing pitchwheel channel value\"",
  1401. X                    (char *)NULL);
  1402. X                return (TCL_ERROR);
  1403. X            }
  1404. X            channel = (unsigned char)strtol(argv[5], &chk_ptr, 0);
  1405. X            if (chk_ptr == argv[5] || channel & 0x80) {
  1406. X                Tcl_AppendResult(interp, "bad channel ",
  1407. X                    argv[5], (char *)NULL);
  1408. X                return (TCL_ERROR);
  1409. X            }
  1410. X            value = (int)strtol(argv[6], &chk_ptr, 0);
  1411. X            if (chk_ptr == argv[6]) {
  1412. X                Tcl_AppendResult(interp, "bad wheel value ",
  1413. X                    argv[6], (char *)NULL);
  1414. X                return (TCL_ERROR);
  1415. X            }
  1416. X
  1417. X            event[num_bytes++] = 0xe0 + channel;
  1418. X            event[num_bytes++] = value & 0x7f;
  1419. X            event[num_bytes++] = (value >> 7) & 0x7f;
  1420. X        } else if (strncmp(event_name, "program", length) == 0) {
  1421. X            /*
  1422. X             * argv[5] - channel
  1423. X             * argv[6] - program
  1424. X             */
  1425. X            unsigned char channel;
  1426. X            unsigned char program;
  1427. X
  1428. X            if (argc != 7) {
  1429. X                Tcl_AppendResult(interp, "wrong # args: ",
  1430. X                    "should be \"midiput mfileId track ",
  1431. X                    "timing program channel program\"",
  1432. X                    (char *)NULL);
  1433. X                return (TCL_ERROR);
  1434. X            }
  1435. X            channel = (unsigned char)strtol(argv[5], &chk_ptr, 0);
  1436. X            if (chk_ptr == argv[5] || channel & 0x80) {
  1437. X                Tcl_AppendResult(interp, "bad channel ",
  1438. X                    argv[5], (char *)NULL);
  1439. X                return (TCL_ERROR);
  1440. X            }
  1441. X            program = (unsigned char)strtol(argv[6], &chk_ptr, 0);
  1442. X            if (chk_ptr == argv[6] || program & 0x80) {
  1443. X                Tcl_AppendResult(interp, "bad program ",
  1444. X                    argv[6], (char *)NULL);
  1445. X                return (TCL_ERROR);
  1446. X            }
  1447. X
  1448. X            event[num_bytes++] = 0xc0 + channel;
  1449. X            event[num_bytes++] = program;
  1450. X        } else
  1451. X            bad_event = 1;
  1452. X        break;
  1453. X    case 's':
  1454. X        /* SYSEX */
  1455. X        /*
  1456. X         * argv[5] - ?cont? or sysex bytes
  1457. X         * argv[6] - ?sysex bytes?
  1458. X         */
  1459. X
  1460. X        if (strncmp(event_name, "sysex", length) != 0)
  1461. X            bad_event = 1;
  1462. X        else {
  1463. X            if (argc != 6 && argc != 7) {
  1464. X                Tcl_AppendResult(interp, "wrong # args: ",
  1465. X                    "should be \"midiput mfileId track ",
  1466. X                    "timing sysex ?cont? data\"", (char *)NULL);
  1467. X                return (TCL_ERROR);
  1468. X            }
  1469. X            if (strcmp(argv[5], "cont") == 0) {
  1470. X                event[num_bytes++] = 0xf7;
  1471. X                event_ptr = argv[6];
  1472. X            } else {
  1473. X                event[num_bytes++] = 0xf0;
  1474. X                event_ptr = argv[5];
  1475. X            }
  1476. X            if ((result = Tclm_AddMetaBytes(interp, event,
  1477. X                &num_bytes, event_ptr)) != TCL_OK)
  1478. X                return (result);
  1479. X        }
  1480. X        break;
  1481. X    }
  1482. X
  1483. X    if (bad_event) {
  1484. X        Tcl_AppendResult(interp, "Bad event.  Must be one of (",
  1485. X            event_list, ")", (char *)NULL);
  1486. X        return(TCL_ERROR);
  1487. X    }
  1488. X
  1489. X    if (!put_smf_event(&(mfile->tchunks[track_num]), event, num_bytes)) {
  1490. X        Tcl_AppendResult(interp, "Couldn't put event\n",
  1491. X            MidiError, (char *)NULL);
  1492. X        return (TCL_ERROR);
  1493. X    }
  1494. X
  1495. X    return (TCL_OK);
  1496. X}
  1497. X
  1498. Xstatic int
  1499. XTclm_ConvertMeta(interp, argc, argv, event, num_bytes)
  1500. X    Tcl_Interp *interp;
  1501. X    int argc;
  1502. X    char **argv;
  1503. X    unsigned char *event;
  1504. X    int *num_bytes;
  1505. X{
  1506. X    char *chk_ptr;
  1507. X    char *event_name;
  1508. X    int bad_meta_event;
  1509. X    int i;
  1510. X    int length;
  1511. X    int result;
  1512. X    
  1513. X
  1514. X    /*
  1515. X     * argv[0] - metablah
  1516. X     * argv[1] - args
  1517. X     */
  1518. X    event_name = argv[0];
  1519. X    if (strncmp(event_name, "meta", 4) != 0) {
  1520. X        Tcl_AppendResult(interp, "bad event type ", argv[0],
  1521. X            (char *)NULL);
  1522. X        return (TCL_ERROR);
  1523. X    }
  1524. X    event_name += 4;
  1525. X
  1526. X    /* all meta events start with 0xff */
  1527. X    event[(*num_bytes)++] = 0xff;
  1528. X
  1529. X    length = strlen(event_name);
  1530. X    bad_meta_event = 0;
  1531. X    switch (event_name[0]) {
  1532. X    case 'c':
  1533. X        if (strncmp(event_name, "chanprefix", length) == 0) {
  1534. X            /*
  1535. X             * argv[1] - bytes
  1536. X             */
  1537. X            if (argc != 2) {
  1538. X                Tcl_AppendResult(interp, "wrong # args: ",
  1539. X                    "should be: \"midiput mfileId track ",
  1540. X                    "timing metachanprefix data\"",
  1541. X                    (char *)NULL);
  1542. X                return (TCL_ERROR);
  1543. X            }
  1544. X            event[(*num_bytes)++] = 0x20;
  1545. X            if ((result = Tclm_AddMetaBytes(interp, event,
  1546. X                num_bytes, argv[1])) != TCL_OK)
  1547. X                return (result);
  1548. X        } else if (strncmp(event_name, "cpy", length) == 0) {
  1549. X            /*
  1550. X             * argv[1] - copyright string
  1551. X             */
  1552. X            if (argc != 2) {
  1553. X                Tcl_AppendResult(interp, "wrong # args: ",
  1554. X                    "should be: \"midiput mfileId track ",
  1555. X                    "timing metacpy copyright\"",
  1556. X                    (char *)NULL);
  1557. X                return (TCL_ERROR);
  1558. X            }
  1559. X            event[(*num_bytes)++] = 0x02;
  1560. X            Tclm_AddMetaString(event, num_bytes, argv[1]);
  1561. X        } else if (strncmp(event_name, "cue", length) == 0) {
  1562. X            /*
  1563. X             * argv[1] - cue string
  1564. X             */
  1565. X            if (argc != 2) {
  1566. X                Tcl_AppendResult(interp, "wrong # args: ",
  1567. X                    "should be: \"midiput mfileId track ",
  1568. X                    "timing metacue cue\"",
  1569. X                    (char *)NULL);
  1570. X                return (TCL_ERROR);
  1571. X            }
  1572. X            event[(*num_bytes)++] = 0x07;
  1573. X            Tclm_AddMetaString(event, num_bytes, argv[1]);
  1574. X        } else
  1575. X            bad_meta_event = 1;
  1576. X        break;
  1577. X    case 'e':
  1578. X        if (strncmp(event_name, "eot", length) != 0)
  1579. X            bad_meta_event = 1;
  1580. X        else {
  1581. X            if (argc != 1) {
  1582. X                Tcl_AppendResult(interp, "wrong # args: ",
  1583. X                    "should be: \"midiput mfileId track ",
  1584. X                    "timing metaeot\"",
  1585. X                    (char *)NULL);
  1586. X                return (TCL_ERROR);
  1587. X            }
  1588. X            event[(*num_bytes)++] = 0x2f;
  1589. X            event[(*num_bytes)++] = 0x00;
  1590. X        }
  1591. X        break;
  1592. X    case 'i':
  1593. X        if (strncmp(event_name, "instname", length) != 0)
  1594. X            bad_meta_event = 1;
  1595. X        else {
  1596. X            /*
  1597. X             * argv[1] - instrument string
  1598. X             */
  1599. X            if (argc != 2) {
  1600. X                Tcl_AppendResult(interp, "wrong # args: ",
  1601. X                    "should be: \"midiput mfileId track ",
  1602. X                    "timing metainstname instrument\"",
  1603. X                    (char *)NULL);
  1604. X                return (TCL_ERROR);
  1605. X            }
  1606. X            event[(*num_bytes)++] = 0x04;
  1607. X            Tclm_AddMetaString(event, num_bytes, argv[1]);
  1608. X        }
  1609. X        break;
  1610. X    case 'k':
  1611. X        if (strncmp(event_name, "key", length) != 0)
  1612. X            bad_meta_event = 1;
  1613. X        else {
  1614. X            int bad_key;
  1615. X
  1616. X            /*
  1617. X             * argv[1] - key name
  1618. X             * argv[2] - key class
  1619. X             */
  1620. X            if (argc != 3) {
  1621. X                Tcl_AppendResult(interp, "wrong # args: ",
  1622. X                    "should be: \"midiput mfileId track ",
  1623. X                    "timing metakey key class\"",
  1624. X                    (char *)NULL);
  1625. X                return (TCL_ERROR);
  1626. X            }
  1627. X            event[(*num_bytes)++] = 0x59;
  1628. X            event[(*num_bytes)++] = 2;
  1629. X            bad_key = 0;
  1630. X            switch (argv[1][0]) {
  1631. X            case 'A':
  1632. X                if (strcmp(argv[1], "A") == 0)
  1633. X                    event[(*num_bytes)++] = 3;
  1634. X                else if (strcmp(argv[1], "A flat") == 0)
  1635. X                    event[(*num_bytes)++] =
  1636. X                        (unsigned char)-4;
  1637. X                else
  1638. X                    bad_key = 1;
  1639. X                break;
  1640. X            case 'B':
  1641. X                if (strcmp(argv[1], "B") == 0)
  1642. X                    event[(*num_bytes)++] = 5;
  1643. X                else if (strcmp(argv[1], "B flat") == 0)
  1644. X                    event[(*num_bytes)++] =
  1645. X                        (unsigned char)-2;
  1646. X                else
  1647. X                    bad_key = 1;
  1648. X                break;
  1649. X            case 'C':
  1650. X                if (strcmp(argv[1], "C") == 0)
  1651. X                    event[(*num_bytes)++] = 0;
  1652. X                else if (strcmp(argv[1], "C flat") == 0)
  1653. X                    event[(*num_bytes)++] =
  1654. X                        (unsigned char)-7;
  1655. X                else if (strcmp(argv[1], "C sharp") == 0)
  1656. X                    event[(*num_bytes)++] = 7;
  1657. X                else
  1658. X                    bad_key = 1;
  1659. X                break;
  1660. X            case 'D':
  1661. X                if (strcmp(argv[1], "D") == 0)
  1662. X                    event[(*num_bytes)++] = 2;
  1663. X                else if (strcmp(argv[1], "D flat") == 0)
  1664. X                    event[(*num_bytes)++] =
  1665. X                        (unsigned char)-5;
  1666. X                else
  1667. X                    bad_key = 1;
  1668. X                break;
  1669. X            case 'E':
  1670. X                if (strcmp(argv[1], "E") == 0)
  1671. X                    event[(*num_bytes)++] = 4;
  1672. X                else if (strcmp(argv[1], "E flat") == 0)
  1673. X                    event[(*num_bytes)++] =
  1674. X                        (unsigned char)-3;
  1675. X                else
  1676. X                    bad_key = 1;
  1677. X                break;
  1678. X            case 'F':
  1679. X                if (strcmp(argv[1], "F") == 0)
  1680. X                    event[(*num_bytes)++] =
  1681. X                        (unsigned char)-1;
  1682. X                else if (strcmp(argv[1], "F sharp") == 0)
  1683. X                    event[(*num_bytes)++] = 6;
  1684. X                else
  1685. X                    bad_key = 1;
  1686. X                break;
  1687. X            case 'G':
  1688. X                if (strcmp(argv[1], "G") == 0)
  1689. X                    event[(*num_bytes)++] = 1;
  1690. X                else if (strcmp(argv[1], "G flat") == 0)
  1691. X                    event[(*num_bytes)++] =
  1692. X                        (unsigned char)-6;
  1693. X                else
  1694. X                    bad_key = 1;
  1695. X                break;
  1696. X            default:
  1697. X                bad_key = 1;
  1698. X            }
  1699. X            if (bad_key) {
  1700. X                Tcl_AppendResult(interp, "Bad key.  Must ",
  1701. X                    "be one of: ", (char *)NULL);
  1702. X                for (i = 0; i < sizeof(key_strings) /
  1703. X                    sizeof(key_strings[0]); i++)
  1704. X                    Tcl_AppendResult(interp, "\"",
  1705. X                        key_strings[i], "\" ",
  1706. X                        (char *)NULL);
  1707. X                return (TCL_ERROR);
  1708. X            }
  1709. X            if (strcmp(argv[2], "major") == 0)
  1710. X                event[(*num_bytes)++] = 0;
  1711. X            else if (strcmp(argv[2], "minor") == 0)
  1712. X                event[(*num_bytes)++] = 1;
  1713. X            else {
  1714. X                Tcl_AppendResult(interp, "Bad key class.  ",
  1715. X                    "Must be one of: \"major\" \"minor\"",
  1716. X                    (char *)NULL);
  1717. X                return (TCL_ERROR);
  1718. X            }
  1719. X        }
  1720. X        break;
  1721. X    case 'l':
  1722. X        if (strncmp(event_name, "lyric", length) != 0)
  1723. X            bad_meta_event = 1;
  1724. X        else {
  1725. X            /*
  1726. X             * argv[1] - lyric string
  1727. X             */
  1728. X            if (argc != 2) {
  1729. X                Tcl_AppendResult(interp, "wrong # args: ",
  1730. X                    "should be: \"midiput mfileId track ",
  1731. X                    "timing metalyric lyric\"",
  1732. X                    (char *)NULL);
  1733. X                return (TCL_ERROR);
  1734. X            }
  1735. X            event[(*num_bytes)++] = 0x05;
  1736. X            Tclm_AddMetaString(event, num_bytes, argv[1]);
  1737. X        }
  1738. X        break;
  1739. X    case 'm':
  1740. X        if (strncmp(event_name, "marker", length) != 0)
  1741. X            bad_meta_event = 1;
  1742. X        else {
  1743. X            /*
  1744. X             * argv[1] - marker string
  1745. X             */
  1746. X            if (argc != 2) {
  1747. X                Tcl_AppendResult(interp, "wrong # args: ",
  1748. X                    "should be: \"midiput mfileId track ",
  1749. X                    "timing metachanprefix marker\"",
  1750. X                    (char *)NULL);
  1751. X                return (TCL_ERROR);
  1752. X            }
  1753. X            event[(*num_bytes)++] = 0x06;
  1754. X            Tclm_AddMetaString(event, num_bytes, argv[1]);
  1755. X        }
  1756. X        break;
  1757. X    case 's':
  1758. X        if (strncmp(event_name, "seqname", length) == 0) {
  1759. X            /*
  1760. X             * argv[1] - sequence name string
  1761. X             */
  1762. X            if (argc != 2) {
  1763. X                Tcl_AppendResult(interp, "wrong # args: ",
  1764. X                    "should be: \"midiput mfileId track ",
  1765. X                    "timing metaseqname sequencename\"",
  1766. X                    (char *)NULL);
  1767. X                return (TCL_ERROR);
  1768. X            }
  1769. X            event[(*num_bytes)++] = 0x03;
  1770. X            Tclm_AddMetaString(event, num_bytes, argv[1]);
  1771. X        } else if (strncmp(event_name, "seqnum", length) == 0) {
  1772. X            int number;
  1773. X
  1774. X            /*
  1775. X             * argv[1] - sequence number
  1776. X             */
  1777. X            if (argc != 2) {
  1778. X                Tcl_AppendResult(interp, "wrong # args: ",
  1779. X                    "should be: \"midiput mfileId track ",
  1780. X                    "timing metaseqnum sequencenumber\"",
  1781. X                    (char *)NULL);
  1782. X                return (TCL_ERROR);
  1783. X            }
  1784. X            event[(*num_bytes)++] = 0x00;
  1785. X            event[(*num_bytes)++] = 0x02;
  1786. X            number = (int)strtol(argv[1], &chk_ptr, 0);
  1787. X            if (argv[1] == chk_ptr) {
  1788. X                Tcl_AppendResult(interp, "Bad sequence number ",
  1789. X                    argv[1], (char *)NULL);
  1790. X                return (TCL_ERROR);
  1791. X            }
  1792. X            event[(*num_bytes)++] = (number >> 8) & 0xff;
  1793. X            event[(*num_bytes)++] = number & 0xff;
  1794. X        } else if (strncmp(event_name, "seqspec", length) == 0) {
  1795. X            Tcl_AppendResult(interp, "META event seqspec not ",
  1796. X                "currently implemented (don't know form)",
  1797. X                (char *)NULL);
  1798. X            return (TCL_ERROR);
  1799. X        } else if (strncmp(event_name, "smpte", length) == 0) {
  1800. X            /*
  1801. X             * argv[1] - hour
  1802. X             * argv[2] - minute
  1803. X             * argv[3] - second
  1804. X             * argv[4] - frame
  1805. X             * argv[5] - fractional frame
  1806. X             */
  1807. X            if (argc != 6) {
  1808. X                Tcl_AppendResult(interp, "wrong # args: ",
  1809. X                    "should be: \"midiput mfileId track ",
  1810. X                    "timing metasmpte hour minute second",
  1811. X                    "frame fractionalframe\"",
  1812. X                    (char *)NULL);
  1813. X                return (TCL_ERROR);
  1814. X            }
  1815. X            event[(*num_bytes)++] = 0x54;
  1816. X            event[(*num_bytes)++] = 5;
  1817. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[1],
  1818. X                &chk_ptr, 0);
  1819. X            if (argv[1] == chk_ptr) {
  1820. X                Tcl_AppendResult(interp, "Bad SMPTE hour: ",
  1821. X                    argv[1], (char *)NULL);
  1822. X                return (TCL_ERROR);
  1823. X            }
  1824. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[2],
  1825. X                &chk_ptr, 0);
  1826. X            if (argv[2] == chk_ptr) {
  1827. X                Tcl_AppendResult(interp, "Bad SMPTE minute: ",
  1828. X                    argv[2], (char *)NULL);
  1829. X                return (TCL_ERROR);
  1830. X            }
  1831. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[3],
  1832. X                &chk_ptr, 0);
  1833. X            if (argv[3] == chk_ptr) {
  1834. X                Tcl_AppendResult(interp, "Bad SMPTE second: ",
  1835. X                    argv[3], (char *)NULL);
  1836. X                return (TCL_ERROR);
  1837. X            }
  1838. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[4],
  1839. X                &chk_ptr, 0);
  1840. X            if (argv[4] == chk_ptr) {
  1841. X                Tcl_AppendResult(interp, "Bad SMPTE frame: ",
  1842. X                    argv[4], (char *)NULL);
  1843. X                return (TCL_ERROR);
  1844. X            }
  1845. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[5],
  1846. X                &chk_ptr, 0);
  1847. X            if (argv[5] == chk_ptr) {
  1848. X                Tcl_AppendResult(interp, "Bad SMPTE ",
  1849. X                    "fractional frame: ", argv[5],
  1850. X                    (char *)NULL);
  1851. X                return (TCL_ERROR);
  1852. X            }
  1853. X        } else
  1854. X            bad_meta_event = 1;
  1855. X        break;
  1856. X    case 't':
  1857. X        if (strncmp(event_name, "tempo", length) == 0) {
  1858. X            long tempo;
  1859. X            int is_bpm;
  1860. X            int tempo_length;
  1861. X            char tempo_str[20];
  1862. X
  1863. X            /*
  1864. X             * argv[1] - usec/beat or beat/min
  1865. X             */
  1866. X            if (argc != 2) {
  1867. X                Tcl_AppendResult(interp, "wrong # args: ",
  1868. X                    "should be: \"midiput mfileId track ",
  1869. X                    "timing metachanprefix tempo\"",
  1870. X                    (char *)NULL);
  1871. X                return (TCL_ERROR);
  1872. X            }
  1873. X            event[(*num_bytes)++] = 0x51;
  1874. X            event[(*num_bytes)++] = 3;
  1875. X            strcpy(tempo_str, argv[1]);
  1876. X            tempo_length = strlen(tempo_str);
  1877. X            if (tempo_str[tempo_length - 1] != 'u')
  1878. X                is_bpm = 1;
  1879. X            else {
  1880. X                /* in usec/beat */
  1881. X                tempo_str[tempo_length - 1] = '\0';
  1882. X                is_bpm = 0;
  1883. X            }
  1884. X            tempo = strtol(tempo_str, &chk_ptr, 0);
  1885. X            if (tempo_str == chk_ptr) {
  1886. X                Tcl_AppendResult(interp, "Bad tempo value: ",
  1887. X                    argv[1], (char *)NULL);
  1888. X                return (TCL_ERROR);
  1889. X            }
  1890. X            if (is_bpm)
  1891. X                tempo = 60000000 / tempo;
  1892. X            event[(*num_bytes)++] = tempo / 0x10000;
  1893. X            tempo %= 0x10000;
  1894. X            event[(*num_bytes)++] = tempo / 0x100;
  1895. X            tempo %= 0x100;
  1896. X            event[(*num_bytes)++] = tempo;
  1897. X        } else if (strncmp(event_name, "text", length) == 0) {
  1898. X            /*
  1899. X             * argv[1] - text string
  1900. X             */
  1901. X            if (argc != 2) {
  1902. X                Tcl_AppendResult(interp, "wrong # args: ",
  1903. X                    "should be: \"midiput mfileId track ",
  1904. X                    "timing metatext text\"",
  1905. X                    (char *)NULL);
  1906. X                return (TCL_ERROR);
  1907. X            }
  1908. X            event[(*num_bytes)++] = 0x01;
  1909. X            Tclm_AddMetaString(event, num_bytes, argv[1]);
  1910. X        } else if (strncmp(event_name, "time", length) == 0) {
  1911. X            int denominator;
  1912. X            int pow;
  1913. X
  1914. X            /* 
  1915. X             * argv[1] - numerator
  1916. X             * argv[2] - denominator (in - powers of 2)
  1917. X             * argv[3] - clocks / met. beat
  1918. X             * argv[4] - 32nd notes / quarter notes
  1919. X             */
  1920. X            if (argc != 5) {
  1921. X                Tcl_AppendResult(interp, "wrong # args: ",
  1922. X                    "should be: \"midiput mfileId track ",
  1923. X                    "timing metatime numerator denominator",
  1924. X                    "clockspermet 32ndsperquarter\"",
  1925. X                    (char *)NULL);
  1926. X                return (TCL_ERROR);
  1927. X            }
  1928. X            event[(*num_bytes)++] = 0x58;
  1929. X            event[(*num_bytes)++] = 4;
  1930. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[1],
  1931. X                &chk_ptr, 0);
  1932. X            if (chk_ptr == argv[1]) {
  1933. X                Tcl_AppendResult(interp, "Bad numerator: ",
  1934. X                    argv[1], (char *)NULL);
  1935. X                return (TCL_ERROR);
  1936. X            }
  1937. X            denominator = (unsigned char)strtol(argv[2],
  1938. X                &chk_ptr, 0);
  1939. X            if (chk_ptr == argv[2]) {
  1940. X                Tcl_AppendResult(interp, "Bad denominator: ",
  1941. X                    argv[2], (char *)NULL);
  1942. X                return (TCL_ERROR);
  1943. X            }
  1944. X            for (i = 0, pow = 1; pow <= denominator; pow *= 2, i++);
  1945. X            i--;
  1946. X            event[(*num_bytes)++] = (unsigned char)i;
  1947. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[3],
  1948. X                &chk_ptr, 0);
  1949. X            if (chk_ptr == argv[3]) {
  1950. X                Tcl_AppendResult(interp, "Bad numerator: ",
  1951. X                    argv[3], (char *)NULL);
  1952. X                return (TCL_ERROR);
  1953. X            }
  1954. X            event[(*num_bytes)++] = (unsigned char)strtol(argv[4],
  1955. X                &chk_ptr, 0);
  1956. X            if (chk_ptr == argv[4]) {
  1957. X                Tcl_AppendResult(interp, "Bad numerator: ",
  1958. X                    argv[4], (char *)NULL);
  1959. X                return (TCL_ERROR);
  1960. X            }
  1961. X        } else
  1962. X            bad_meta_event = 1;
  1963. X        break;
  1964. X    }
  1965. X    if (bad_meta_event) {
  1966. X        Tcl_AppendResult(interp, "Bad META event: meta", event_name,
  1967. X            ".  Must be one of (", meta_events, ")", (char *)NULL);
  1968. X        return (TCL_ERROR);
  1969. X    }
  1970. X    return (TCL_OK);
  1971. X}
  1972. X
  1973. Xstatic void
  1974. XTclm_AddMetaString(event, num_bytes, str)
  1975. X    unsigned char *event;
  1976. X    int *num_bytes;
  1977. X    char *str;
  1978. X{
  1979. X    int i;
  1980. X    int str_len;
  1981. X    int var_len;
  1982. X    unsigned char var_bytes[10];
  1983. X
  1984. X    str_len = strlen(str);
  1985. X    var_len = fix2var(str_len, var_bytes);
  1986. X    for (i = 0; i < var_len; i++)
  1987. X        event[(*num_bytes)++] = var_bytes[i];
  1988. X    for (i = 0; i < str_len; i++)
  1989. X        event[(*num_bytes)++] = str[i];
  1990. X}
  1991. X
  1992. Xstatic int
  1993. XTclm_AddMetaBytes(interp, event, num_bytes, data)
  1994. X    Tcl_Interp *interp;
  1995. X    unsigned char *event;
  1996. X    int *num_bytes;
  1997. X    char *data;
  1998. X{
  1999. X    int i;
  2000. X    int result;
  2001. X    int num_data_bytes;
  2002. X    int var_len;
  2003. X    unsigned char data_bytes[MAX_EVENT_SIZE];
  2004. X    unsigned char var_bytes[10];
  2005. X
  2006. X    if ((result = Tclm_ConvertBytes(interp, data, data_bytes,
  2007. X        &num_data_bytes)) != TCL_OK)
  2008. X        return (result);
  2009. X
  2010. X    var_len = fix2var(num_data_bytes, var_bytes);
  2011. X    for (i = 0; i < var_len; i++)
  2012. X        event[(*num_bytes)++] = var_bytes[i];
  2013. X    for (i = 0; i < num_data_bytes; i++)
  2014. X        event[(*num_bytes)++] = data_bytes[i];
  2015. X
  2016. X    return (TCL_OK);
  2017. X}
  2018. X
  2019. Xint
  2020. XTclm_MidiRewind(dummy, interp, argc, argv)
  2021. X    ClientData dummy;
  2022. X    Tcl_Interp *interp;
  2023. X    int argc;
  2024. X    char **argv;
  2025. X{
  2026. X    MIDI_FILE *mfile;
  2027. X    char *chk_ptr;
  2028. X    char **track_list;
  2029. X    int i;
  2030. X    int num_tracks;
  2031. X    int result;
  2032. X    int track;
  2033. X
  2034. X    /*
  2035. X     * argv[0] - midirewind
  2036. X     * argv[1] = mfileId
  2037. X     * argv[2] = optional track list
  2038. X     */
  2039. X    if (argc < 2 || argc > 3) {
  2040. X        Tcl_AppendResult(interp, "bad # args: should be \"",
  2041. X            argv[0], " mfileId ?track list?\"", (char *)NULL);
  2042. X        return (TCL_ERROR);
  2043. X    }
  2044. X
  2045. X    if ((result = Tclm_GetMFile(interp, argv[1], &mfile)) != TCL_OK)
  2046. X        return (result);
  2047. X
  2048. X    if (argc == 2)
  2049. X        for (i = 0; i < mfile->hchunk.num_trks; i++)
  2050. X            rewind_track(&(mfile->tchunks[i]));
  2051. X    else {
  2052. X        if ((result = Tcl_SplitList(interp, argv[2], &num_tracks,
  2053. X            &track_list)) != TCL_OK)
  2054. X            return (result);
  2055. X        for (i = 0; i < num_tracks; i++) {
  2056. X            track = (int)strtol(track_list[i], &chk_ptr, 0);
  2057. X            if (chk_ptr == track_list[i] || track < 0 ||
  2058. X                track >= mfile->hchunk.num_trks) {
  2059. X                Tcl_AppendResult(interp, "Bad track value ",
  2060. X                    track_list[i], (char *)NULL);
  2061. X                free ((char *)track_list);
  2062. X                return (TCL_ERROR);
  2063. X            }
  2064. X            rewind_track(&(mfile->tchunks[track]));
  2065. X        }
  2066. X        free((char *)track_list);
  2067. X    }
  2068. X
  2069. X    return (TCL_OK);
  2070. X}
  2071. X
  2072. Xint
  2073. XTclm_MidiVarToFix(dummy, interp, argc, argv)
  2074. X    ClientData dummy;
  2075. X    Tcl_Interp *interp;
  2076. X    int argc;
  2077. X    char **argv;
  2078. X{
  2079. X    long fix;
  2080. X    int delta;
  2081. X    int num_bytes;
  2082. X    int result;
  2083. X    unsigned char bytes[MAX_EVENT_SIZE];
  2084. X
  2085. X    /*
  2086. X     * argv[0] - midivartofix
  2087. X     * argv[1] - midi event
  2088. X     */
  2089. X    if (argc != 2) {
  2090. X        Tcl_AppendResult(interp, "bad # args: should be\"",
  2091. X            argv[0], " midi_event\"", (char *)NULL);
  2092. X        return (TCL_ERROR);
  2093. X    }
  2094. X    if ((result = Tclm_ConvertBytes(interp, argv[1], bytes, &num_bytes))
  2095. X        != TCL_OK)
  2096. X        return (result);
  2097. X
  2098. X    fix = var2fix(bytes, &delta);
  2099. X    sprintf(interp->result, "%ld", fix);
  2100. X    return (TCL_OK);
  2101. X}
  2102. X
  2103. Xint
  2104. XTclm_MidiFixToVar(dummy, interp, argc, argv)
  2105. X    ClientData dummy;
  2106. X    Tcl_Interp *interp;
  2107. X    int argc;
  2108. X    char **argv;
  2109. X{
  2110. X    long fix;
  2111. X    char *chk_ptr;
  2112. X    int i;
  2113. X    int num_bytes;
  2114. X    unsigned char bytes[4];
  2115. X    char byte_str[10];
  2116. X
  2117. X    /*
  2118. X     * argv[0] - midifixtovar
  2119. X     * argv[1] - fixed length value
  2120. X     */
  2121. X    if (argc != 2) {
  2122. X        Tcl_AppendResult(interp, "bad # args: should be \"",
  2123. X            argv[0], " fixval\"", (char *)NULL);
  2124. X        return (TCL_ERROR);
  2125. X    }
  2126. X
  2127. X    fix = strtol(argv[1], &chk_ptr, 0);
  2128. X    if (chk_ptr == argv[1]) {
  2129. X        Tcl_AppendResult(interp, "Bad fixed length value ", argv[1],
  2130. X            (char *)NULL);
  2131. X        return (TCL_ERROR);
  2132. X    }
  2133. X    num_bytes = fix2var(fix, bytes);
  2134. X    for (i = 0; i < num_bytes; i++) {
  2135. X        sprintf(byte_str, "0x%02x", bytes[i]);
  2136. X        Tcl_AppendElement(interp, byte_str, 0);
  2137. X    }
  2138. X    return (TCL_OK);
  2139. X}
  2140. X
  2141. Xint
  2142. XTclm_MidiTiming(dummy, interp, argc, argv)
  2143. X    ClientData dummy;
  2144. X    Tcl_Interp *interp;
  2145. X    int argc;
  2146. X    char **argv;
  2147. X{
  2148. X    int delta;
  2149. X    int i;
  2150. X    int num_bytes;
  2151. X    int result;
  2152. X    unsigned char bytes[MAX_EVENT_SIZE];
  2153. X    char str[10];
  2154. X
  2155. X    /*
  2156. X     * argv[0] - miditiming
  2157. X     * argv[1] - event
  2158. X     */
  2159. X
  2160. X    if ((result = Tclm_ConvertBytes(interp, argv[1], bytes, &num_bytes))
  2161. X        != TCL_OK)
  2162. X        return (result);
  2163. X
  2164. X    (void)var2fix(bytes, &delta);
  2165. X
  2166. X    for (i = 0; i < delta; i++) {
  2167. X        sprintf(str, "0x%02x", bytes[i]);
  2168. X        Tcl_AppendElement(interp, str, 0);
  2169. X    }
  2170. X    return (TCL_OK);
  2171. X}
  2172. X
  2173. Xint
  2174. XTclm_MidiPlayable(dummy, interp, argc, argv)
  2175. X    ClientData dummy;
  2176. X    Tcl_Interp *interp;
  2177. X    int argc;
  2178. X    char **argv;
  2179. X{
  2180. X
  2181. X    /*
  2182. X     * argv[0] - midiplayable
  2183. X     */
  2184. X    if (argc != 1) {
  2185. X        Tcl_AppendResult(interp, "wrong # args: should be\"",
  2186. X            argv[0], "\"", (char *)NULL);
  2187. X        return (TCL_ERROR);
  2188. X    }
  2189. X
  2190. X#ifdef MIDIPLAY
  2191. X    Tcl_AppendResult(interp, "1", (char *)NULL);
  2192. X#else
  2193. X    Tcl_AppendResult(interp, "0", (char *)NULL);
  2194. X#endif
  2195. X    return (TCL_OK);
  2196. X}
  2197. X
  2198. Xint
  2199. XTclm_TclmVersion(dummy, interp, argc, argv)
  2200. X    ClientData dummy;
  2201. X    Tcl_Interp *interp;
  2202. X    int argc;
  2203. X    char **argv;
  2204. X{
  2205. X
  2206. X    /*
  2207. X     * argv[0] - tclmversion
  2208. X     */
  2209. X    if (argc != 1) {
  2210. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  2211. X            argv[0], "\"", (char *)NULL);
  2212. X        return (TCL_ERROR);
  2213. X    }
  2214. X    Tcl_AppendResult(interp, TCLM_PATCHLEVEL, (char *)NULL);
  2215. X    return (TCL_OK);
  2216. X}
  2217. END_OF_FILE
  2218.   if test 54562 -ne `wc -c <'tclm-1.0/tclmCmd.c'`; then
  2219.     echo shar: \"'tclm-1.0/tclmCmd.c'\" unpacked with wrong size!
  2220.   fi
  2221.   # end of 'tclm-1.0/tclmCmd.c'
  2222. fi
  2223. echo shar: End of archive 1 \(of 5\).
  2224. cp /dev/null ark1isdone
  2225. MISSING=""
  2226. for I in 1 2 3 4 5 ; do
  2227.     if test ! -f ark${I}isdone ; then
  2228.     MISSING="${MISSING} ${I}"
  2229.     fi
  2230. done
  2231. if test "${MISSING}" = "" ; then
  2232.     echo You have unpacked all 5 archives.
  2233.     rm -f ark[1-9]isdone
  2234. else
  2235.     echo You still must unpack the following archives:
  2236.     echo "        " ${MISSING}
  2237. fi
  2238. exit 0
  2239. exit 0 # Just in case...
  2240.