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

  1. Newsgroups: comp.sources.misc
  2. From: durian@advtech.uswest.com (Mike Durian)
  3. Subject: v37i044:  tclm - TCL extensions for MIDI file manipulation, Part02/05
  4. Message-ID: <1993May10.215118.3747@sparky.imd.sterling.com>
  5. X-Md4-Signature: 79ee0564170eda41d3e3c1a582e47a78
  6. Date: Mon, 10 May 1993 21:51:18 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: durian@advtech.uswest.com (Mike Durian)
  10. Posting-number: Volume 37, Issue 44
  11. Archive-name: tclm/part02
  12. Environment: BSD/386, Esix SV4, SunOS, TCL 6.x
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then feed it
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # Contents:  tclm-1.0/mlib/mfileutil.c tclm-1.0/mlib/mpu_bsd386.c
  19. #   tclm-1.0/mseq tclm-1.0/tclmPlay.c
  20. # Wrapped by kent@sparky on Mon May 10 09:43:32 1993
  21. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  22. echo If this archive is complete, you will see the following message:
  23. echo '          "shar: End of archive 2 (of 5)."'
  24. if test -f 'tclm-1.0/mlib/mfileutil.c' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'tclm-1.0/mlib/mfileutil.c'\"
  26. else
  27.   echo shar: Extracting \"'tclm-1.0/mlib/mfileutil.c'\" \(20842 characters\)
  28.   sed "s/^X//" >'tclm-1.0/mlib/mfileutil.c' <<'END_OF_FILE'
  29. X/*-
  30. X * Copyright (c) 1993 Michael B. Durian.  All rights reserved.
  31. X *
  32. X * Redistribution and use in source and binary forms, with or without
  33. X * modification, are permitted provided that the following conditions
  34. X * are met:
  35. X * 1. Redistributions of source code must retain the above copyright
  36. X *    notice, this list of conditions and the following disclaimer.
  37. X * 2. Redistributions in binary form must reproduce the above copyright
  38. X *    notice, this list of conditions and the following disclaimer in the
  39. X *    documentation and/or other materials provided with the distribution.
  40. X * 3. All advertising materials mentioning features or use of this software
  41. X *    must display the following acknowledgement:
  42. X *    This product includes software developed by Michael B. Durian.
  43. X * 4. The name of the the Author may be used to endorse or promote 
  44. X *    products derived from this software without specific prior written 
  45. X *    permission.
  46. X *
  47. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
  48. X * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  49. X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
  50. X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  51. X * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  52. X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  53. X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  54. X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  55. X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  56. X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  57. X * SUCH DAMAGE.
  58. X */
  59. X/*
  60. X * mfileutil.c,v 1.13 1993/05/07 17:45:18 durian Exp
  61. X */
  62. Xstatic char cvsid[] = "mfileutil.c,v 1.13 1993/05/07 17:45:18 durian Exp";
  63. X
  64. X#include <stdio.h>
  65. X#include <stdlib.h>
  66. X#include <unistd.h>
  67. X#include <string.h>
  68. X#include <sys/fcntl.h>
  69. X#include "mutil.h"
  70. X
  71. X/* hack so we know if a read failed because of an eof instead of bad data */
  72. Xint MidiEof = 0;
  73. Xchar MidiError[256];
  74. X
  75. Xint
  76. Xread_header_chunk(mfile, hchunk)
  77. X    int mfile;
  78. X    HCHUNK *hchunk;
  79. X{
  80. X
  81. X    if (mread(mfile, hchunk->str, sizeof(hchunk->str)) !=
  82. X        sizeof(hchunk->str)) {
  83. X        if (!MidiEof)
  84. X            sprintf(MidiError,
  85. X                "Couldn't read header chunk identifier");
  86. X        return (0);
  87. X    }
  88. X
  89. X    if (mread(mfile, (char *)&hchunk->length, sizeof(hchunk->length)) !=
  90. X        sizeof(hchunk->length)) {
  91. X        sprintf(MidiError, "Couldn't read header chunk length");
  92. X        return (0);
  93. X    }
  94. X
  95. X    if (mread(mfile, (char *)&hchunk->format, sizeof(hchunk->format)) !=
  96. X        sizeof(hchunk->format)) {
  97. X        sprintf(MidiError, "Couldn't read header chunk format");
  98. X        return (0);
  99. X    }
  100. X
  101. X    if (mread(mfile, (char *)&hchunk->num_trks, sizeof(hchunk->num_trks))
  102. X        != sizeof(hchunk->num_trks)) {
  103. X        sprintf(MidiError, "Couldn't read header chunk num_trks");
  104. X        return (0);
  105. X    }
  106. X
  107. X    if (mread(mfile, (char *)&hchunk->division, sizeof(hchunk->division))
  108. X        != sizeof(hchunk->division)) {
  109. X        sprintf(MidiError, "Couldn't read header chunk division");
  110. X        return (0);
  111. X    }
  112. X
  113. X    /* fix byte ordering */
  114. X    hchunk->length = mtohl(hchunk->length);
  115. X    hchunk->format = mtohs(hchunk->format);
  116. X    hchunk->num_trks = mtohs(hchunk->num_trks);
  117. X    hchunk->division = mtohs(hchunk->division);
  118. X
  119. X    if (strncmp(hchunk->str, "MThd", 4) != 0) {
  120. X        sprintf(MidiError, "Not a standard MIDI file");
  121. X        return (0);
  122. X    }
  123. X
  124. X    if (hchunk->length != 6) {
  125. X        sprintf(MidiError, "Bad header chunk size");
  126. X        return (0);
  127. X    }
  128. X
  129. X    if (hchunk->format == 0 && hchunk->num_trks != 1) {
  130. X        sprintf(MidiError,
  131. X            "Midi format 0, but number of tracks (%d) is > 1",
  132. X            hchunk->num_trks);
  133. X        return (0);
  134. X    }
  135. X
  136. X    return (1);
  137. X}
  138. X
  139. Xint
  140. Xread_track_chunk(mfile, tracks)
  141. X    int mfile;
  142. X    TCHUNK *tracks;
  143. X{
  144. X
  145. X    if (mread(mfile, tracks->str, 4) != 4) {
  146. X        sprintf(MidiError, "Couldn't read track chunk identifier");
  147. X        return (0);
  148. X    }
  149. X
  150. X    if (strncmp(tracks->str, "MTrk", 4) != 0) {
  151. X        sprintf(MidiError, "Bad track chunk identifier");
  152. X        return (0);
  153. X    }
  154. X
  155. X    if (mread(mfile, (char *)&tracks->length, sizeof(tracks->length)) !=
  156. X        sizeof(tracks->length)) {
  157. X        sprintf(MidiError, "Couldn't read track length");
  158. X        return (0);
  159. X    }
  160. X
  161. X    tracks->msize = tracks->length = mtohl(tracks->length);
  162. X    tracks->pos = 0;
  163. X
  164. X    /* allocate space for tracks events */
  165. X    if ((tracks->events = (unsigned char *) malloc(tracks->length)) ==
  166. X        NULL) {
  167. X        sprintf(MidiError, "Not enough memory for track data");
  168. X        return (0);
  169. X    }
  170. X    tracks->event_start = tracks->events;
  171. X
  172. X    if (mread(mfile, (char *)tracks->events, tracks->length) !=
  173. X        tracks->length) {
  174. X            sprintf(MidiError, "Couldn't read track data");
  175. X            return (0);
  176. X    }
  177. X    tracks->events = tracks->event_start;
  178. X
  179. X    return (1);
  180. X}
  181. X
  182. Xint
  183. Xskip_track_chunk(mfile)
  184. X    int mfile;
  185. X{
  186. X    long length;
  187. X    char str[4];
  188. X
  189. X    if (mread(mfile, str, sizeof(str)) != sizeof(str)) {
  190. X        sprintf(MidiError, "Couldn't read track chunk identifier");
  191. X        return (0);
  192. X    }
  193. X
  194. X    if (strncmp(str, "MTrk", 4) != 0) {
  195. X        sprintf(MidiError, "Bad track chunk identifier");
  196. X        return (0);
  197. X    }
  198. X
  199. X    if (mread(mfile, (char *)&length, sizeof(length)) != sizeof(length)) {
  200. X        sprintf(MidiError, "Couldn't read track length");
  201. X        return (0);
  202. X    }
  203. X
  204. X    length = mtohl(length);
  205. X
  206. X    if (lseek(mfile, length, SEEK_CUR) == -1) {
  207. X        sprintf(MidiError, "Couldn't seek past track");
  208. X        return (0);
  209. X    }
  210. X
  211. X    return (1);
  212. X}
  213. X
  214. Xint
  215. Xsplit_type_zero(tracks)
  216. X    TCHUNK *tracks;
  217. X{
  218. X    long tempo_delta;
  219. X    long data_delta;
  220. X    long timing;
  221. X    EVENT_TYPE event_type;
  222. X    int data_size;
  223. X    int event_size;
  224. X    int i;
  225. X    int offset;
  226. X    int malloced_data;
  227. X    int malloced_tempo;
  228. X    int malloc_inc;
  229. X    int tempo_size;
  230. X    int time_len;
  231. X    unsigned char event[256];
  232. X    unsigned char *data_events;
  233. X    unsigned char *data_ptr;
  234. X    unsigned char *last_events;
  235. X    unsigned char *tempo_events;
  236. X    unsigned char *tempo_ptr;
  237. X    unsigned char var_time[4];
  238. X
  239. X    data_size = 0;
  240. X    tempo_size = 0;
  241. X    tempo_delta = 0;
  242. X    data_delta = 0;
  243. X    malloc_inc = 512;
  244. X
  245. X    if ((data_events = (unsigned char *) malloc(malloc_inc))
  246. X            == NULL) {
  247. X        sprintf(MidiError, "Not enought memory to split track");
  248. X        return (0);
  249. X    }
  250. X    malloced_data = malloc_inc;
  251. X    if ((tempo_events = (unsigned char *) malloc(malloc_inc))
  252. X            == NULL) {
  253. X        sprintf(MidiError, "Not enought memory to split track");
  254. X        return (0);
  255. X    }
  256. X    malloced_tempo = malloc_inc;
  257. X
  258. X    data_ptr = data_events;
  259. X    tempo_ptr = tempo_events;
  260. X    while (event_size = get_smf_event(&tracks[0], event, &event_type)) {
  261. X        if (event_size == -1) {
  262. X            sprintf(MidiError,
  263. X                "Problem getting event while splitting");
  264. X            return (0);
  265. X        }
  266. X
  267. X        if (event_type ==  NORMAL || event_type == SYSEX) {
  268. X            timing = var2fix(event, &offset);
  269. X            data_delta += timing;
  270. X            time_len = fix2var(data_delta, var_time);
  271. X            data_size += event_size - offset + time_len;
  272. X            if (data_size > malloced_data) {
  273. X                last_events = data_events;
  274. X                if (!more_memory((char **)&data_events,
  275. X                    malloced_data + malloc_inc)) {
  276. X                    sprintf(MidiError,
  277. X                        "Not enough memory");
  278. X                    return (0);
  279. X                }
  280. X                if (data_events != last_events)
  281. X                    data_ptr = data_events + (data_ptr -
  282. X                        last_events);
  283. X            }
  284. X            /* copy in timing */
  285. X            for (i = 0; i < time_len; i++)
  286. X                *data_ptr++ = var_time[i];
  287. X
  288. X            /* copy in data */
  289. X            for (i = offset; i < event_size; i++)
  290. X                *data_ptr++ = event[i];
  291. X
  292. X            /* add timing to tempo delta */
  293. X            tempo_delta += timing;
  294. X            data_delta = 0;
  295. X        } else {
  296. X            timing = var2fix(event, &offset);
  297. X            tempo_delta += timing;
  298. X            time_len = fix2var(tempo_delta, var_time);
  299. X            tempo_size += event_size - offset + time_len;
  300. X            if (tempo_size > malloced_tempo) {
  301. X                last_events = tempo_events;
  302. X                if (!more_memory((char **)&tempo_events,
  303. X                    malloced_tempo + malloc_inc)) {
  304. X                    sprintf(MidiError,
  305. X                        "Not enough memory");
  306. X                    return (0);
  307. X                }
  308. X                if (tempo_events != last_events)
  309. X                    tempo_ptr = data_events + (tempo_ptr -
  310. X                        last_events);
  311. X            }
  312. X            /* copy in timing */
  313. X            for (i = 0; i < time_len; i++)
  314. X                *tempo_ptr++ = var_time[i];
  315. X
  316. X            /* copy in data */
  317. X            for (i = offset; i < event_size; i++)
  318. X                *tempo_ptr++ = event[i];
  319. X
  320. X            /* add timing to tempo delta */
  321. X            data_delta += timing;
  322. X            tempo_delta = 0;
  323. X        }
  324. X    }
  325. X
  326. X    /* add eot to tempo track */
  327. X    event[0] = 0xff;
  328. X    event[1] = METAEOT;
  329. X    event[2] = 0;
  330. X    event_size = 3;
  331. X
  332. X    time_len = fix2var(tempo_delta, var_time);
  333. X    tempo_size += event_size + time_len;
  334. X    if (tempo_size > malloced_tempo) {
  335. X        last_events = tempo_events;
  336. X        if (!more_memory((char **)&tempo_events, malloced_tempo +
  337. X            malloc_inc)) {
  338. X            sprintf(MidiError, "Not enough memory");
  339. X            return (0);
  340. X        }
  341. X        if (tempo_events != last_events)
  342. X            tempo_ptr = data_events + (tempo_ptr - last_events);
  343. X    }
  344. X    /* copy in timing */
  345. X    for (i = 0; i < time_len; i++)
  346. X        *tempo_ptr++ = var_time[i];
  347. X
  348. X    /* copy in data */
  349. X    for (i = 0; i < event_size; i++)
  350. X        *tempo_ptr++ = event[i];
  351. X            
  352. X    free(tracks[0].events);
  353. X    tracks[0].events = tracks[0].event_start = tempo_events;
  354. X    tracks[1].events = tracks[1].event_start = data_events;
  355. X    tracks[0].msize = malloced_tempo;
  356. X    tracks[1].msize = malloced_data;
  357. X    tracks[0].length = tempo_size;
  358. X    tracks[1].length = data_size;
  359. X    tracks[0].pos = 0;
  360. X    tracks[1].pos = 0;
  361. X    (void) strncpy(tracks[0].str, "MTrk", 4);
  362. X    (void) strncpy(tracks[1].str, "MTrk", 4);
  363. X    return (1);
  364. X}
  365. X
  366. Xint
  367. Xmore_memory(ptr, size)
  368. X    char **ptr;
  369. X    int size;
  370. X{
  371. X
  372. X    if ((*ptr = realloc(*ptr, size)) == NULL)
  373. X        return (0);
  374. X    else
  375. X        return (1);
  376. X}
  377. X
  378. Xint
  379. Xget_smf_event(track, event, event_type)
  380. X    TCHUNK *track;
  381. X    unsigned char *event;
  382. X    EVENT_TYPE *event_type;
  383. X{
  384. X    long length = 0;
  385. X    int i;
  386. X    int size;
  387. X    static int extra_bytes[] = {2, 2, 2, 2, 1, 1, 2, 0};
  388. X    unsigned char etype;
  389. X            
  390. X    if (track->pos >= track->length)
  391. X        return (0);
  392. X
  393. X    /*
  394. X     * get timing bytes
  395. X     */
  396. X    if (track->pos++ == track->length)
  397. X        return (-1);
  398. X    *event++ = *track->events++;
  399. X    size = 1;
  400. X    while (*(event - 1) & 0x80) {
  401. X        if (track->pos++ == track->length)
  402. X            return (-1);
  403. X        *event++ = *track->events++;
  404. X        size++;
  405. X    }
  406. X
  407. X    if (track->pos++ == track->length)
  408. X        return (-1);
  409. X    *event = *track->events++;
  410. X
  411. X    /* get event type */
  412. X    switch (*event) {
  413. X    case 0xff:
  414. X        /* meta event */
  415. X        if (track->pos++ == track->length)
  416. X            return (-1);
  417. X        size++;
  418. X        event++;
  419. X        *event_type = *event++ = *track->events++;
  420. X        size++;
  421. X        /* get length as a variable length */
  422. X        do {
  423. X            if (track->pos++ == track->length)
  424. X                return (-1);
  425. X            length = (length << 7) + (*track->events & 0x7f);
  426. X            *event = *track->events++;
  427. X            size++;
  428. X        } while (*event++ & 0x80);
  429. X        for (; length > 0; length--) {
  430. X            /* get event */
  431. X            if (track->pos++ == track->length)
  432. X                return (-1);
  433. X            *event++ = *track->events++;
  434. X            size++;
  435. X        }
  436. X        track->read_rs = 0;
  437. X        break;
  438. X    case 0xf0:
  439. X    case 0xf7:
  440. X        event++;
  441. X        size++;
  442. X        *event_type = SYSEX;
  443. X        /* get length as variable length value */
  444. X        do {
  445. X            if (track->pos++ == track->length)
  446. X                return (-1);
  447. X            length = (length << 7) + (*track->events & 0x7f);
  448. X            *event = *track->events++;
  449. X            size++;
  450. X        } while (*event++ & 0x80);
  451. X        for (; length > 0; length--) {
  452. X            /* get event */
  453. X            if (track->pos++ == track->length)
  454. X                return (-1);
  455. X            *event++ = *track->events++;
  456. X            size++;
  457. X        }
  458. X        track->read_rs = 0;
  459. X        break;
  460. X    default:
  461. X        *event_type = NORMAL;
  462. X        size++;
  463. X        if (*event & 0x80) {
  464. X            etype = *event;
  465. X            track->read_rs = etype;
  466. X            for (i = 0; i < extra_bytes[(etype >> 4) & 0x07];
  467. X                i++) {
  468. X                event++;
  469. X                if (track->pos++ == track->length)
  470. X                    return(-1);
  471. X                *event = *track->events++;
  472. X                size++;
  473. X            }
  474. X        } else {
  475. X            /* it is using running state */
  476. X            /* get other data bytes */
  477. X            switch ((track->read_rs >> 4) & 0x0f) {
  478. X            case 0x08:
  479. X            case 0x09:
  480. X            case 0x0a:
  481. X            case 0x0b:
  482. X            case 0x0e:
  483. X                event++;
  484. X                if (track->pos++ == track->length)
  485. X                    return (-1);
  486. X                *event = *track->events++;
  487. X                size++;
  488. X                break;
  489. X            default:
  490. X                break;
  491. X            }
  492. X        }
  493. X    }
  494. X    return (size);
  495. X}
  496. X
  497. Xint
  498. Xput_smf_event(track, event, event_size)
  499. X    TCHUNK *track;
  500. X    unsigned char *event;
  501. X    int event_size;
  502. X{
  503. X    unsigned char *event_cpy;
  504. X    unsigned char *event_ptr;
  505. X    int i;
  506. X    int pos;
  507. X
  508. X    /* make a copy of the event we can modify */
  509. X    if ((event_cpy = (unsigned char *)malloc(event_size)) == NULL) {
  510. X        sprintf(MidiError, "Out of memory in put_smf_event");
  511. X        return (0);
  512. X    }
  513. X    for (i = 0; i < event_size; i++)
  514. X        event_cpy[i] = event[i];
  515. X
  516. X    /* skip timing part */
  517. X    (void)var2fix(event_cpy, &pos);
  518. X    event_ptr = event_cpy + pos;
  519. X
  520. X    /*
  521. X     * check event type to see if we can dump the running state
  522. X     * If there is no running state, assume whoever sent us
  523. X     * the data got it right.
  524. X     */
  525. X    if (*event_ptr & 0x80) {
  526. X        switch (*event_ptr & 0xf0) {
  527. X        case 0xf0:
  528. X            /* clear running state for these */
  529. X            track->write_rs = 0;
  530. X            break;
  531. X        default:
  532. X            if (*event_ptr != track->write_rs)
  533. X                track->write_rs = *event_ptr;
  534. X            else {
  535. X                /* it's the same as the last, so dump it */
  536. X                event_size--;
  537. X                while (pos++ < event_size) {
  538. X                    *event_ptr = *(event_ptr + 1);
  539. X                    event_ptr++;
  540. X                }
  541. X            }
  542. X        }
  543. X    }
  544. X    if (track->msize == 0) {
  545. X        if ((track->event_start = (unsigned char *)malloc(
  546. X            BUFSIZ)) == NULL) {
  547. X            sprintf(MidiError, "Not enough memory for new event");
  548. X            return (0);
  549. X        }
  550. X        track->msize = BUFSIZ;
  551. X        track->events = track->event_start;
  552. X    }
  553. X    track->length += event_size;
  554. X    if (track->length > track->msize) {
  555. X        track->msize += BUFSIZ;
  556. X        if ((track->event_start =
  557. X            (unsigned char *)realloc(track->event_start, track->msize))
  558. X            == NULL) {
  559. X            perror("");
  560. X            sprintf(MidiError, "Not enough memory for new event");
  561. X            track->length -= event_size;
  562. X            track->msize -= BUFSIZ;
  563. X            return (0);
  564. X        }
  565. X        /* starting position might move */
  566. X        track->events = track->event_start + track->pos;
  567. X    }
  568. X
  569. X    memcpy(track->event_start + track->length - event_size, event_cpy,
  570. X        event_size);
  571. X    free(event_cpy);
  572. X    return (1);
  573. X}
  574. X
  575. Xvoid
  576. Xrewind_track(track)
  577. X    TCHUNK *track;
  578. X{
  579. X    track->pos = 0;
  580. X    track->events = track->event_start;
  581. X    track->read_rs = 0;
  582. X}
  583. X
  584. Xvoid
  585. Xreset_track(track)
  586. X    TCHUNK *track;
  587. X{
  588. X    track->pos = 0;
  589. X    track->length = 0;
  590. X    track->events = track->event_start;
  591. X    track->read_rs = 0;
  592. X    track->write_rs = 0;
  593. X}
  594. X
  595. X
  596. Xint
  597. Xwrite_header_chunk(mfile, hchunk)
  598. X    int mfile;
  599. X    HCHUNK *hchunk;
  600. X{
  601. X
  602. X    (void)strncpy(hchunk->str, "MThd", 4);
  603. X    if (mwrite(mfile, hchunk->str, 4) != 4) {
  604. X        sprintf(MidiError, "Couldn't write header chunk identifier");
  605. X        return (0);
  606. X    }
  607. X
  608. X    hchunk->length = 6;
  609. X    hchunk->length = htoml(hchunk->length);
  610. X    if (mwrite(mfile, (char *)&hchunk->length, sizeof(hchunk->length)) !=
  611. X        sizeof(hchunk->length)) {
  612. X        sprintf(MidiError, "Couldn't write header chunk length");
  613. X        return (0);
  614. X    }
  615. X    hchunk->length = mtohl(hchunk->length);
  616. X
  617. X    hchunk->format = htoms(hchunk->format);
  618. X    if (mwrite(mfile, (char *)&hchunk->format, sizeof(hchunk->format)) !=
  619. X        sizeof(hchunk->format)) {
  620. X        sprintf(MidiError, "Couldn't write header chunk format");
  621. X        return (0);
  622. X    }
  623. X    hchunk->format = mtohs(hchunk->format);
  624. X
  625. X    hchunk->num_trks = htoms(hchunk->num_trks);
  626. X    if (mwrite(mfile, (char *)&hchunk->num_trks, sizeof(hchunk->num_trks))
  627. X        != sizeof(hchunk->num_trks)) {
  628. X        sprintf(MidiError, "Couldn't write number of tracks");
  629. X        return (0);
  630. X    }
  631. X    hchunk->num_trks = mtohs(hchunk->num_trks);
  632. X
  633. X    hchunk->division = htoms(hchunk->division);
  634. X    if (mwrite(mfile, (char *)&hchunk->division, sizeof(hchunk->division))
  635. X        != sizeof(hchunk->division)) {
  636. X        sprintf(MidiError, "Couldn't write header chunk division");
  637. X        return (0);
  638. X    }
  639. X    hchunk->division = mtohs(hchunk->division);
  640. X
  641. X    return (1);
  642. X}
  643. X
  644. Xint
  645. Xwrite_track_chunk(mfile, tchunk)
  646. X    int mfile;
  647. X    TCHUNK *tchunk;
  648. X{
  649. X    long midi_length;
  650. X
  651. X    (void)strncpy(tchunk->str, "MTrk", 4);
  652. X    if (mwrite(mfile, tchunk->str, 4) != 4) {
  653. X        sprintf(MidiError, "Couldn't write track chunk identifier");
  654. X        return (0);
  655. X    }
  656. X
  657. X    midi_length = htoml(tchunk->length);
  658. X    if (mwrite(mfile, (char *)&midi_length, sizeof(midi_length)) !=
  659. X        sizeof(midi_length)) {
  660. X        sprintf(MidiError, "Couldn't write track length");
  661. X        return (0);
  662. X    }
  663. X
  664. X    if (mwrite(mfile, (char *)tchunk->event_start, tchunk->length)
  665. X        != tchunk->length) {
  666. X        sprintf(MidiError, "Couldn't write track events");
  667. X        return (0);
  668. X    }
  669. X
  670. X    return (1);
  671. X}
  672. X
  673. Xint
  674. Xmerge_tracks(out, in, tscalar, num, delta)
  675. X    TCHUNK *out;
  676. X    TCHUNK **in;
  677. X    int *tscalar;
  678. X    int num;
  679. X    int delta;
  680. X{
  681. X    struct evnt {
  682. X        long    timing;
  683. X        EVENT_TYPE    type;
  684. X        int    size;
  685. X        short    remainder;
  686. X        unsigned    char event[256];
  687. X        unsigned    char run_state;
  688. X    } *ev;
  689. X    long timing;
  690. X    int *atracks;
  691. X    int endtime;
  692. X    int i;
  693. X    int j;
  694. X    int foo;
  695. X    int lasttime;
  696. X    int next;
  697. X    int num_active;
  698. X    unsigned char event[256];
  699. X
  700. X    if ((ev = (struct evnt *)malloc(sizeof(struct evnt) * num))
  701. X        == NULL) {
  702. X        sprintf(MidiError, "Not enough memory for event buffers");
  703. X        return (-1);
  704. X    }
  705. X    if ((atracks = (int *)malloc(sizeof(int) * num)) == NULL) {
  706. X        sprintf(MidiError, "Not enought memory for atracks");
  707. X        free((char *)ev);
  708. X        return (-1);
  709. X    }
  710. X
  711. X    endtime = delta;
  712. X    /* get first events from each in track */
  713. X    for (i = 0; i < num; i++) {
  714. X        if ((ev[i].size = get_smf_event(in[i], ev[i].event,
  715. X            &ev[i].type)) == -1) {
  716. X            free((char *)ev);
  717. X            free((char *)atracks);
  718. X            return (-1);
  719. X        }
  720. X        ev[i].run_state = get_running_state(in[i]);
  721. X
  722. X        /* convert time */
  723. X        timing = var2fix(ev[i].event, &foo);
  724. X        ev[i].timing = timing / tscalar[i] + delta;
  725. X        ev[i].remainder = timing % tscalar[i];
  726. X        /* alway force inclusion of running status */
  727. X        ev[i].size -= foo;
  728. X        for (j = 0; j < ev[i].size; j++)
  729. X            ev[i].event[j] = ev[i].event[foo + j];
  730. X        if (ev[i].type == METAEOT) {
  731. X            if (ev[i].timing > endtime)
  732. X                endtime = ev[i].timing;
  733. X        } else
  734. X            atracks[i] = 1;
  735. X    }
  736. X    num_active = num;
  737. X
  738. X    lasttime = 0;
  739. X    while (num_active) {
  740. X        /* find first active track */
  741. X        next = 0;
  742. X        for (i = 0; i < num; i++)
  743. X            if (atracks[i]) {
  744. X                next = i;
  745. X                break;
  746. X            }
  747. X        /* now compare to find active track with smallest timing */
  748. X        for (; i < num; i++)
  749. X            if (atracks[i])
  750. X                if (ev[i].timing < ev[next].timing)
  751. X                    next = i;
  752. X
  753. X        foo = fix2var(ev[next].timing - lasttime, event);
  754. X
  755. X        /* stick in a running state if it is missing */
  756. X        if (!(ev[next].event[0] & 0x80))
  757. X            event[foo++] = ev[next].run_state;
  758. X
  759. X        for (i = 0; i < ev[next].size; i++)
  760. X            event[foo + i] = ev[next].event[i];
  761. X
  762. X/*
  763. X{
  764. Xint x;
  765. Xprintf("wrote: ");
  766. Xfor (x = 0; x < ev[next].size + foo; x++)
  767. Xprintf("0x%02x ", event[x]);
  768. Xprintf("\n");
  769. X}
  770. X*/
  771. X
  772. X        /* change timing value of event */
  773. X        if (!put_smf_event(out, event, ev[next].size + foo)) {
  774. X            free((char *)ev);
  775. X            free((char *)atracks);
  776. X            return (-1);
  777. X        }
  778. X
  779. X        lasttime = ev[next].timing;
  780. X
  781. X        /* get replacement event */
  782. X        if ((ev[next].size = get_smf_event(in[next], ev[next].event,
  783. X            &ev[next].type)) == -1) {
  784. X            free((char *)ev);
  785. X            free((char *)atracks);
  786. X            return (-1);
  787. X        }
  788. X/*
  789. X{
  790. Xint x;
  791. Xprintf("read: ");
  792. Xfor (x = 0; x < ev[next].size; x++)
  793. Xprintf("0x%02x ", ev[next].event[x]);
  794. Xprintf("\n");
  795. X}
  796. X*/
  797. X        ev[next].run_state = get_running_state(in[next]);
  798. X        /* convert time */
  799. X        timing = var2fix(ev[next].event, &foo) + ev[next].remainder;
  800. X        ev[next].timing += timing / tscalar[next];
  801. X        ev[next].remainder = timing % tscalar[next];
  802. X        ev[next].size -= foo;
  803. X        for (j = 0; j < ev[next].size; j++)
  804. X            ev[next].event[j] = ev[next].event[foo + j];
  805. X        if (ev[next].type == METAEOT) {
  806. X            if (ev[next].timing > endtime)
  807. X                endtime = ev[next].timing;
  808. X            atracks[next] = 0;
  809. X            num_active--;
  810. X        }
  811. X    }
  812. X    free((char *)ev);
  813. X    free((char *)atracks);
  814. X    return (endtime - lasttime);
  815. X}
  816. X
  817. Xint
  818. Xfix2var(val, ptr)
  819. X    long val;
  820. X    unsigned char *ptr;
  821. X{
  822. X    int i;
  823. X    unsigned char buf[4];
  824. X    unsigned char *bptr;
  825. X
  826. X    buf[0] = buf[1] = buf[2] = buf[3] = 0;
  827. X    bptr = buf;
  828. X    *bptr++ = val & 0x7f;
  829. X    while ((val >>= 7) > 0) {
  830. X        *bptr |= 0x80;
  831. X        *bptr++ += (val & 0x7f);
  832. X    }
  833. X
  834. X    i = 0;
  835. X    do {
  836. X        *ptr++ = *--bptr;
  837. X        i++;
  838. X    } while (bptr != buf);
  839. X
  840. X    return (i);
  841. X}
  842. X
  843. Xlong
  844. Xvar2fix(var, delta)
  845. X    unsigned char *var;
  846. X    int *delta;
  847. X{
  848. X    long fix;
  849. X
  850. X    fix = 0;
  851. X    *delta = 0;
  852. X    if (*var & 0x80)
  853. X        do {
  854. X            fix = (fix << 7) + (*var & 0x7f);
  855. X            (*delta)++;
  856. X        } while (*var++ & 0x80);
  857. X    else {
  858. X        fix = *var++;
  859. X        (*delta)++;
  860. X    }
  861. X
  862. X    return (fix);
  863. X}
  864. X
  865. Xvoid
  866. Xfree_track(track)
  867. X    TCHUNK *track;
  868. X{
  869. X
  870. X    if (track == NULL)
  871. X        return;
  872. X    if (track->event_start != NULL)
  873. X        free(track->event_start);
  874. X    track->event_start = NULL;
  875. X    track->events = NULL;
  876. X    track->length = 0;
  877. X    track->pos = 0;
  878. X    track->msize = 0;
  879. X    track->read_rs = 0;
  880. X    track->write_rs = 0;
  881. X}
  882. X
  883. Xvoid
  884. Xinit_track(track)
  885. X    TCHUNK *track;
  886. X{
  887. X    track->event_start = NULL;
  888. X    track->events = NULL;
  889. X    track->msize = 0;
  890. X    track->length = 0;
  891. X    track->pos = 0;
  892. X    track->read_rs = 0;
  893. X    track->write_rs = 0;
  894. X    strncpy(track->str, "MTrk", 4);
  895. X}
  896. X
  897. Xunsigned char
  898. Xget_running_state(track)
  899. X    TCHUNK *track;
  900. X{
  901. X
  902. X    return (track->read_rs);
  903. X}
  904. X
  905. Xint
  906. Xmread(dev, buf, size)
  907. X    int dev;
  908. X    char *buf;
  909. X    int size;
  910. X{
  911. X    int num_read;
  912. X    int total_read;
  913. X
  914. X    total_read = 0;
  915. X    do {
  916. X        if ((num_read = read(dev, buf, size - total_read)) == -1) {
  917. X            perror("");
  918. X            return (-1);
  919. X        }
  920. X        if (num_read == 0)
  921. X            break;
  922. X        total_read += num_read;
  923. X        buf += num_read;
  924. X    } while (size > total_read);
  925. X    if (total_read == 0)
  926. X        MidiEof = 1;
  927. X    return (total_read);
  928. X}
  929. X
  930. Xint
  931. Xmwrite(dev, buf, size)
  932. X    int dev;
  933. X    char *buf;
  934. X    int size;
  935. X{
  936. X    int num_written;
  937. X    int total_written;
  938. X
  939. X    total_written = 0;
  940. X    do {
  941. X        if ((num_written = write(dev, buf, size - total_written))
  942. X            == -1) {
  943. X            perror("");
  944. X            return (-1);
  945. X        }
  946. X        if (num_written == 0)
  947. X            break;
  948. X        total_written += num_written;
  949. X        buf += num_written;
  950. X    } while (size > total_written);
  951. X    return (total_written);
  952. X}
  953. END_OF_FILE
  954.   if test 20842 -ne `wc -c <'tclm-1.0/mlib/mfileutil.c'`; then
  955.     echo shar: \"'tclm-1.0/mlib/mfileutil.c'\" unpacked with wrong size!
  956.   fi
  957.   # end of 'tclm-1.0/mlib/mfileutil.c'
  958. fi
  959. if test -f 'tclm-1.0/mlib/mpu_bsd386.c' -a "${1}" != "-c" ; then 
  960.   echo shar: Will not clobber existing file \"'tclm-1.0/mlib/mpu_bsd386.c'\"
  961. else
  962.   echo shar: Extracting \"'tclm-1.0/mlib/mpu_bsd386.c'\" \(10123 characters\)
  963.   sed "s/^X//" >'tclm-1.0/mlib/mpu_bsd386.c' <<'END_OF_FILE'
  964. X/*-
  965. X * Copyright (c) 1993 Michael B. Durian.  All rights reserved.
  966. X *
  967. X * Redistribution and use in source and binary forms, with or without
  968. X * modification, are permitted provided that the following conditions
  969. X * are met:
  970. X * 1. Redistributions of source code must retain the above copyright
  971. X *    notice, this list of conditions and the following disclaimer.
  972. X * 2. Redistributions in binary form must reproduce the above copyright
  973. X *    notice, this list of conditions and the following disclaimer in the
  974. X *    documentation and/or other materials provided with the distribution.
  975. X * 3. All advertising materials mentioning features or use of this software
  976. X *    must display the following acknowledgement:
  977. X *    This product includes software developed by Michael B. Durian.
  978. X * 4. The name of the the Author may be used to endorse or promote 
  979. X *    products derived from this software without specific prior written 
  980. X *    permission.
  981. X *
  982. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
  983. X * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  984. X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
  985. X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  986. X * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  987. X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  988. X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  989. X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  990. X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  991. X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  992. X * SUCH DAMAGE.
  993. X */
  994. X/*
  995. X * mpu_bsd386.c,v 1.9 1993/05/07 17:45:20 durian Exp
  996. X */
  997. X
  998. X#ifdef MIDIPLAY
  999. Xstatic char cvsid[] = "mpu_bsd386.c,v 1.9 1993/05/07 17:45:20 durian Exp";
  1000. X
  1001. X#include <stdio.h>
  1002. X#include <stdlib.h>
  1003. X#include <unistd.h>
  1004. X#include <sys/file.h>
  1005. X#include <string.h>
  1006. X#include <errno.h>
  1007. X#include <sys/ioctl.h>
  1008. X#include <i386/isa/midiioctl.h>
  1009. X#include "mutil.h"
  1010. X#include "mdevice.h"
  1011. X
  1012. X#define MAX_EVENT_SIZE 256
  1013. X/* global */
  1014. Xvolatile int StopProcessing;
  1015. X
  1016. Xstatic int adjust_division _ANSI_ARGS_((int division, long *div_ioctl,
  1017. X    int *tempo_scalar));
  1018. Xstatic unsigned char double2tempo _ANSI_ARGS_((double d));
  1019. X
  1020. Xint
  1021. Xplay_tracks(dev, tracks, indices, num, repeat)
  1022. X    int dev;
  1023. X    TCHUNK *tracks;
  1024. X    int *indices;
  1025. X    int num;
  1026. X    int repeat;
  1027. X{
  1028. X
  1029. X    return (record_tracks(dev, tracks, indices, num, NULL, repeat));
  1030. X}
  1031. X
  1032. Xint
  1033. Xrecord_tracks(dev, p_tracks, indices, p_num, r_track, repeat)
  1034. X    int dev;
  1035. X    TCHUNK *p_tracks;
  1036. X    int *indices;
  1037. X    int p_num;
  1038. X    TCHUNK *r_track;
  1039. X    int repeat;
  1040. X{
  1041. X    TCHUNK tmptrack;
  1042. X    struct timeval mwait;
  1043. X    TCHUNK **track_list;
  1044. X    int *tscalars;
  1045. X    fd_set w_select;
  1046. X    fd_set r_select;
  1047. X    EVENT_TYPE event_type;
  1048. X    int endtime;
  1049. X    int event_len;
  1050. X    int i;
  1051. X    int select_return;
  1052. X    int track_done;
  1053. X    unsigned char event[MAX_EVENT_SIZE];
  1054. X
  1055. X    tscalars = NULL;
  1056. X    track_list = NULL;
  1057. X    if (p_num > 0) {
  1058. X        /* merge play tracks */
  1059. X        if ((tscalars = (int *)malloc(sizeof(int) * p_num)) == NULL) {
  1060. X            sprintf(MidiError, "Out of memory");
  1061. X            return (0);
  1062. X        }
  1063. X        if ((track_list = (TCHUNK **)malloc(sizeof(TCHUNK *) * p_num))
  1064. X            == NULL) {
  1065. X            sprintf(MidiError, "Out of memory");
  1066. X            return (0);
  1067. X        }
  1068. X        for (i = 0; i < p_num; i++) {
  1069. X            tscalars[i] = 1;
  1070. X            track_list[i] = &p_tracks[i];
  1071. X            
  1072. X        }
  1073. X        init_track(&tmptrack);
  1074. X        endtime = merge_tracks(&tmptrack, track_list, tscalars, p_num,
  1075. X            0);
  1076. X        event_len = fix2var(endtime, event);
  1077. X        event[event_len] = METAEOT;
  1078. X        event[event_len + 1] = 0;
  1079. X        if (!put_smf_event(&tmptrack, event, event_len + 2)) {
  1080. X            sprintf(MidiError, "Couldn't add METAEOT event");
  1081. X            free(tscalars);
  1082. X            free(track_list);
  1083. X            free_track(&tmptrack);
  1084. X            return (0);
  1085. X        }
  1086. X    }
  1087. X
  1088. X    StopProcessing = 0;
  1089. X    do {
  1090. X        track_done = 0;
  1091. X        rewind_track(&tmptrack);
  1092. X        while (!StopProcessing && !track_done) {
  1093. X            /* select on play */
  1094. X            FD_ZERO(&w_select);
  1095. X            if (p_num != 0)
  1096. X                FD_SET(dev, &w_select);
  1097. X            /* also on record */
  1098. X            FD_ZERO(&r_select);
  1099. X            if (r_track != NULL)
  1100. X                FD_SET(dev, &r_select);
  1101. X            /*
  1102. X             * we time out every so often to check and see
  1103. X             * if StopProcessing has changed
  1104. X             */
  1105. X            mwait.tv_sec = 0;
  1106. X            mwait.tv_usec = 100000;
  1107. X            if ((select_return = select(getdtablesize(), &r_select,
  1108. X                &w_select, NULL, &mwait)) == -1) {
  1109. X                if (errno == EINTR)
  1110. X                    break;
  1111. X                else {
  1112. X                    sprintf(MidiError,
  1113. X                        "Error in select: %s",
  1114. X                        sys_errlist[errno]);
  1115. X                    if (tscalars != NULL)
  1116. X                        free(tscalars);
  1117. X                    if (track_list != NULL)
  1118. X                        free(track_list);
  1119. X                    free_track(&tmptrack);
  1120. X                    return (0);
  1121. X                }
  1122. X            }
  1123. X            if (StopProcessing)
  1124. X                break;
  1125. X            if (select_return == 0)
  1126. X                continue;
  1127. X    
  1128. X            /* write event if not blocked */
  1129. X            if (p_num != 0 && FD_ISSET(dev, &w_select)) {
  1130. X                event_len = get_smf_event(&tmptrack,
  1131. X                    event, &event_type);
  1132. X                switch (event_len) {
  1133. X                case -1:
  1134. X                    sprintf(MidiError,
  1135. X                        "Couldn't get event from tmptrack");
  1136. X                    if (tscalars != NULL)
  1137. X                        free(tscalars);
  1138. X                    if (track_list != NULL)
  1139. X                        free(track_list);
  1140. X                    free_track(&tmptrack);
  1141. X                    return (0);
  1142. X                case 0:
  1143. X                    track_done = 1;
  1144. X                    break;
  1145. X                default:
  1146. X                    if (write(dev, event, event_len) !=
  1147. X                        event_len) {
  1148. X                        sprintf(MidiError,
  1149. X                            "Error writing event: %s",
  1150. X                            sys_errlist[errno]);
  1151. X                        if (tscalars != NULL)
  1152. X                            free(tscalars);
  1153. X                        if (track_list != NULL)
  1154. X                            free(track_list);
  1155. X                        free_track(&tmptrack);
  1156. X                        return (0);
  1157. X                    }
  1158. X                    break;
  1159. X                }
  1160. X            }
  1161. X
  1162. X
  1163. X            if (r_track != NULL && FD_ISSET(dev, &r_select)) {
  1164. X                if ((event_len = read(dev, event,
  1165. X                    MAX_EVENT_SIZE)) == -1) {
  1166. X                    sprintf(MidiError,
  1167. X                        "Error reading event: %s",
  1168. X                        sys_errlist[errno]);
  1169. X                    if (tscalars != NULL)
  1170. X                        free(tscalars);
  1171. X                    if (track_list != NULL)
  1172. X                        free(track_list);
  1173. X                    free_track(&tmptrack);
  1174. X                    return (0);
  1175. X                }
  1176. X                if (!put_smf_event(r_track, event, event_len)) {
  1177. X                    sprintf(MidiError,
  1178. X                        "Coudln't put smf event");
  1179. X                    if (tscalars != NULL)
  1180. X                        free(tscalars);
  1181. X                    if (track_list != NULL)
  1182. X                        free(track_list);
  1183. X                    free_track(&tmptrack);
  1184. X                    return (0);
  1185. X                }
  1186. X            }
  1187. X        }
  1188. X    } while (repeat && !StopProcessing);
  1189. X
  1190. X    if (!StopProcessing) {
  1191. X        /* this waits until we're done playing */
  1192. X        if (ioctl(dev, MFLUSHQ, NULL) == -1)
  1193. X            sprintf(MidiError, "Error flushing queue: %s",
  1194. X                sys_errlist[errno]);
  1195. X        /* check for any final data to be read */
  1196. X        FD_ZERO(&r_select);
  1197. X        if (r_track != NULL)
  1198. X            FD_SET(dev, &r_select);
  1199. X        mwait.tv_sec = 0;
  1200. X        mwait.tv_usec = 0;
  1201. X        if ((select_return = select(getdtablesize(), NULL, &w_select,
  1202. X            NULL, &mwait)) == -1) {
  1203. X            if (errno != EINTR) {
  1204. X                sprintf(MidiError, "Error in select: %s",
  1205. X                    sys_errlist[errno]);
  1206. X                if (tscalars != NULL)
  1207. X                    free(tscalars);
  1208. X                if (track_list != NULL)
  1209. X                    free(track_list);
  1210. X                free_track(&tmptrack);
  1211. X                return (0);
  1212. X            }
  1213. X        }
  1214. X        if (r_track != NULL && FD_ISSET(dev, &r_select)) {
  1215. X            if ((event_len = read(dev, event, MAX_EVENT_SIZE))
  1216. X                == -1) {
  1217. X                sprintf(MidiError, "Error reading event: %s",
  1218. X                    sys_errlist[errno]);
  1219. X                if (tscalars != NULL)
  1220. X                    free(tscalars);
  1221. X                if (track_list != NULL)
  1222. X                    free(track_list);
  1223. X                free_track(&tmptrack);
  1224. X                return (0);
  1225. X            }
  1226. X            if (!put_smf_event(r_track, event, event_len)) {
  1227. X                sprintf(MidiError, "Coudln't put smf event");
  1228. X                if (tscalars != NULL)
  1229. X                    free(tscalars);
  1230. X                if (track_list != NULL)
  1231. X                    free(track_list);
  1232. X                free_track(&tmptrack);
  1233. X                return (0);
  1234. X            }
  1235. X        }
  1236. X    }
  1237. X    if (tscalars != NULL)
  1238. X        free(tscalars);
  1239. X    if (track_list != NULL)
  1240. X        free(track_list);
  1241. X    free_track(&tmptrack);
  1242. X    return (1);
  1243. X}
  1244. X
  1245. Xint
  1246. Xstop_processing(dev)
  1247. X    int dev;
  1248. X{
  1249. X
  1250. X    StopProcessing = 1;
  1251. X    /* if we leave, leave now - don't hang on close */
  1252. X    if (ioctl(dev, MCLRQ, NULL) == -1) {
  1253. X        sprintf(MidiError, "Error clearing queue: %s",
  1254. X            sys_errlist[errno]);
  1255. X        return (0);
  1256. X    }
  1257. X    return (0);
  1258. X}
  1259. X
  1260. Xint
  1261. Xopen_midi_device(mode)
  1262. X    PlayMode mode;
  1263. X{
  1264. X    mode_t m;
  1265. X    int dev;
  1266. X
  1267. X    switch (mode) {
  1268. X    case PLAY:
  1269. X        m =  O_WRONLY;
  1270. X        break;
  1271. X    case RECORD:
  1272. X        m = O_RDONLY;
  1273. X        break;
  1274. X    case PLAYRECORD:
  1275. X        m = O_RDWR;
  1276. X        break;
  1277. X    }
  1278. X    if ((dev = open("/dev/midi0", m)) == -1) {
  1279. X        sprintf(MidiError, "Couldn't open /dev/midi0: %s",
  1280. X            sys_errlist[errno]);
  1281. X    }
  1282. X    return (dev);
  1283. X}
  1284. X
  1285. Xint
  1286. Xinit_midi_device(dev, hd, reltempo)
  1287. X    int dev;
  1288. X    HCHUNK *hd;
  1289. X    double reltempo;
  1290. X{
  1291. X    long div_ioctl;
  1292. X    int tscalar;
  1293. X    unsigned char fixreltempo;
  1294. X
  1295. X    if (!adjust_division(hd->division, &div_ioctl, &tscalar)) {
  1296. X        sprintf(MidiError, "Bad division value.  Must be on of \
  1297. X48, 72, 96, 120, 144, 168, 192 or an integer multiple thereof");
  1298. X        return (0);
  1299. X    }
  1300. X
  1301. X    if (ioctl(dev, div_ioctl, NULL) == -1) {
  1302. X        sprintf(MidiError, "Couldn't set division: %s",
  1303. X            sys_errlist[errno]);
  1304. X        return (0);
  1305. X    }
  1306. X    if (ioctl(dev, MTSCALAR, &tscalar) == -1) {
  1307. X        sprintf(MidiError, "Couldn't set tscalar: %s",
  1308. X            sys_errlist[errno]);
  1309. X        return (0);
  1310. X    }
  1311. X    fixreltempo = double2tempo(reltempo);
  1312. X    if (ioctl(dev, MSETRELTMP, &fixreltempo) == -1) {
  1313. X        sprintf(MidiError, "Couldn't set relative tempo: %s",
  1314. X            sys_errlist[errno]);
  1315. X        return (0);
  1316. X    }
  1317. X
  1318. X    return (1);
  1319. X}
  1320. X
  1321. Xint
  1322. Xstart_midi_device(dev, mode)
  1323. X    int dev;
  1324. X    PlayMode mode;
  1325. X{
  1326. X
  1327. X    /* automatically started by open */
  1328. X    return (1);
  1329. X}
  1330. X
  1331. Xint
  1332. Xstop_midi_device(dev, mode)
  1333. X    int dev;
  1334. X    PlayMode mode;
  1335. X{
  1336. X    /* automatically stopped by close */
  1337. X    return (1);
  1338. X}
  1339. X
  1340. Xint
  1341. Xclose_midi_device(dev)
  1342. X    int dev;
  1343. X{
  1344. X
  1345. X    close(dev);
  1346. X    return (1);
  1347. X}
  1348. X
  1349. Xint
  1350. Xadjust_division(division, div_ioctl, tempo_scalar)
  1351. X    int division;
  1352. X    long *div_ioctl;
  1353. X    int *tempo_scalar;
  1354. X{
  1355. X    long ioctls[] = {MRES192, MRES168, MRES144, MRES120, MRES96,
  1356. X        MRES72, MRES48};
  1357. X    int divs[] = {192, 168, 144, 120, 96, 72, 48};
  1358. X    int i;
  1359. X    int ret_val;
  1360. X
  1361. X    ret_val = 0;
  1362. X    for (i = 0; i < sizeof(divs) / sizeof(divs[0]); i++) {
  1363. X        if (division % divs[i] == 0) {
  1364. X            *div_ioctl = ioctls[i];
  1365. X            *tempo_scalar = 1000 * division / divs[i];
  1366. X            ret_val = 1;
  1367. X            break;
  1368. X        }
  1369. X    }
  1370. X    return (ret_val);
  1371. X}
  1372. X
  1373. Xunsigned char
  1374. Xdouble2tempo(d)
  1375. X    double d;
  1376. X{
  1377. X    double bit_vals[] = {2.0, 1.0, 0.5, 0.25, 0.125, 0.0625,
  1378. X        0.03125, 0.015625};
  1379. X    int i;
  1380. X    unsigned char tempo_scalar;
  1381. X
  1382. X    for (i = 0; i < 8; i++) {
  1383. X        if (d < bit_vals[i])
  1384. X            tempo_scalar = (tempo_scalar << 1) & ~1;
  1385. X        else {
  1386. X            tempo_scalar = (tempo_scalar << 1) | 1;
  1387. X            d -= bit_vals[i];
  1388. X        }
  1389. X    }
  1390. X    return(tempo_scalar);
  1391. X}
  1392. X#endif
  1393. END_OF_FILE
  1394.   if test 10123 -ne `wc -c <'tclm-1.0/mlib/mpu_bsd386.c'`; then
  1395.     echo shar: \"'tclm-1.0/mlib/mpu_bsd386.c'\" unpacked with wrong size!
  1396.   fi
  1397.   # end of 'tclm-1.0/mlib/mpu_bsd386.c'
  1398. fi
  1399. if test -f 'tclm-1.0/mseq' -a "${1}" != "-c" ; then 
  1400.   echo shar: Will not clobber existing file \"'tclm-1.0/mseq'\"
  1401. else
  1402.   echo shar: Extracting \"'tclm-1.0/mseq'\" \(7875 characters\)
  1403.   sed "s/^X//" >'tclm-1.0/mseq' <<'END_OF_FILE'
  1404. X#!/usr/local/bin/tclm -f
  1405. X#
  1406. X# Copyright (c) 1993 Michael B. Durian.  All rights reserved.
  1407. X#
  1408. X# Redistribution and use in source and binary forms, with or without
  1409. X# modification, are permitted provided that the following conditions
  1410. X# are met:
  1411. X# 1. Redistributions of source code must retain the above copyright
  1412. X#    notice, this list of conditions and the following disclaimer.
  1413. X# 2. Redistributions in binary form must reproduce the above copyright
  1414. X#    notice, this list of conditions and the following disclaimer in the
  1415. X#    documentation and/or other materials provided with the distribution.
  1416. X# 3. All advertising materials mentioning features or use of this software
  1417. X#    must display the following acknowledgement:
  1418. X#    This product includes software developed by Michael B. Durian.
  1419. X# 4. The name of the the Author may be used to endorse or promote 
  1420. X#    products derived from this software without specific prior written 
  1421. X#    permission.
  1422. X#
  1423. X# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
  1424. X# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  1425. X# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
  1426. X# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  1427. X# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  1428. X# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  1429. X# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  1430. X# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  1431. X# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  1432. X# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  1433. X# SUCH DAMAGE.
  1434. X
  1435. X# mseq,v 1.4 1993/04/08 04:16:05 durian Exp
  1436. X
  1437. Xset ScopeList ""
  1438. Xset CurrentScope ""
  1439. Xset ScopeDuration long
  1440. X
  1441. Xset TrackNumber 0
  1442. Xset CurrentTrack ""
  1443. X
  1444. Xset InFileName stdin
  1445. Xset OutFileName stdout
  1446. Xset LineNumber 0
  1447. X
  1448. Xset Division -1
  1449. X
  1450. Xproc ReadLine {file} {
  1451. X    global LineNumber
  1452. X
  1453. X    # we want to skip blank lines
  1454. X    # and escape both curly braces
  1455. X    if {[gets $file line0] == -1} {
  1456. X        return ""
  1457. X    }
  1458. X    incr LineNumber
  1459. X    if {![regsub -all \{ $line0 \\\{ line1]} {
  1460. X        set line1 $line0
  1461. X    }
  1462. X    if {![regsub -all \} $line1 \\\} line2]} {
  1463. X        set line2 $line1
  1464. X    }
  1465. X    while {[llength $line2] == 0} {
  1466. X        if {[gets $file line0] == -1} {
  1467. X            return ""
  1468. X        }
  1469. X        incr LineNumber
  1470. X        if {![regsub -all \{ $line0 \\\{ line1]} {
  1471. X            set line1 $line0
  1472. X        }
  1473. X        if {![regsub -all \{ $line2 \\\{ line1]} {
  1474. X            set line2 $line1
  1475. X        }
  1476. X    }
  1477. X    return $line2
  1478. X}
  1479. X
  1480. X
  1481. Xproc CollapseAndAdd {outfile infilename outtimes} {
  1482. X    global Division
  1483. X
  1484. X    if {[catch {open $infilename "r"} file]} {
  1485. X        puts stderr $file
  1486. X        exit 1
  1487. X    }
  1488. X    set infile [midiread $file]
  1489. X    set outtime0 [lindex $outtimes 0]
  1490. X    set outtime1 [lindex $outtimes 1]
  1491. X
  1492. X    set form [midiconfig $infile format]
  1493. X    if {[midiconfig $infile format] != 1} {
  1494. X        puts stderr "Sorry!  mseq only handles format 1 files currently."
  1495. X        exit 1
  1496. X    }
  1497. X    if {$Division == -1} {
  1498. X        set Division [midiconfig $infile division]
  1499. X        set scalar 1
  1500. X    } else {
  1501. X        set scalar [expr {[midiconfig $infile division] / $Division}]
  1502. X    }
  1503. X
  1504. X    # copy over track 0
  1505. X    set outtime0 [midimerge "$outfile 0" "\"$infile 0 $scalar\"" $outtime0]
  1506. X
  1507. X    # now merge the other tracks to track 1
  1508. X    set num_tracks [midiconfig $infile tracks]
  1509. X    for {set i 1} {$i < $num_tracks} {incr i} {
  1510. X        lappend inputs "$infile $i $scalar"
  1511. X    }
  1512. X    set outtime1 [midimerge "$outfile 1" $inputs $outtime1]
  1513. X
  1514. X    midifree $infile
  1515. X    close $file
  1516. X
  1517. X    return "$outtime0 $outtime1"
  1518. X}
  1519. X
  1520. X# parse command line args
  1521. X# mseq [input.seq [output.mid]]
  1522. Xif {[string compare [lindex $argv 0] -f] == 0} {
  1523. X    set argv [lrange $argv 2 end]
  1524. X    set argc [expr {$argc - 2}]
  1525. X}
  1526. Xif {$argc > 2} {
  1527. X    puts stderr "Usage: mseq [input.seq [output.mid]]"
  1528. X    exit 1
  1529. X}
  1530. X
  1531. Xset InFile stdin
  1532. Xset OutFile stdout
  1533. Xif {$argc > 0} {
  1534. X    set InFileName [lindex $argv 0]
  1535. X    if {[catch {open $InFileName "r"} InFile]} {
  1536. X        puts stderr $InFile
  1537. X        exit 1
  1538. X    }
  1539. X    if {$argc > 1} {
  1540. X        set OutFileName [lindex $argv 1]
  1541. X        set OutFile [open $OutFileName "w"]
  1542. X        if {[catch {open $OutFileName "w"} OutFile]} {
  1543. X            puts stderr $OutFile
  1544. X            exit 1
  1545. X        }
  1546. X    }
  1547. X}
  1548. X
  1549. X
  1550. X# pretty ugly huh?
  1551. X# get a line and stick it into the variable line
  1552. X# also get the length of that same line
  1553. X# and stick that result in the variable line_length
  1554. X# then check to see if that is zero
  1555. Xwhile {[set line_length [llength [set line [ReadLine $InFile]]]] != 0} {
  1556. X    set comment 0
  1557. X    for {set i 0} {$i < $line_length} {incr i} {
  1558. X        set word [lindex $line $i]
  1559. X        case $word in {
  1560. X        "*:" {
  1561. X            # this is a label
  1562. X            set ScopeList [linsert $ScopeList 0 $word]
  1563. X            set CurrentScope $word
  1564. X            set ScopeDuration short
  1565. X        } "\{" {
  1566. X            # this opens a block
  1567. X            set ScopeDuration long
  1568. X        } "\}" {
  1569. X            # this closes a block
  1570. X            set ScopeList [lrange $ScopeList 1 end]
  1571. X            set CurrentScope [lindex $ScopeList 0]
  1572. X        } "repeat" {
  1573. X            if {[llength $ScopeList] == 0} {
  1574. X                puts stderr "No track specified"
  1575. X                puts stderr "Line $LineNumber File: $InFileName"
  1576. X                exit 1
  1577. X            }
  1578. X            # our one and only command
  1579. X            incr i
  1580. X            if {$i == $line_length} {
  1581. X                puts stderr [concat "Must follow \"repeat\" "\
  1582. X                    "with a block name"]
  1583. X                puts stderr "Line $LineNumber File: $InFileName"
  1584. X                exit 1
  1585. X            }
  1586. X            set block [lindex $line $i]
  1587. X            incr i
  1588. X            if {$i < $line_length} {
  1589. X                set num_repeats [lindex $line $i]
  1590. X            } else {
  1591. X                set num_repeats 1
  1592. X            }
  1593. X            for {set j 0} {$j < $num_repeats} {incr j} {
  1594. X                # some major contortions to get
  1595. X                # recursive variable names
  1596. X                set var "\$${CurrentTrack}($block)"
  1597. X
  1598. X                foreach scope $ScopeList {
  1599. X                    eval "append ${CurrentTrack}($scope) \
  1600. X                        { } $var"
  1601. X                }
  1602. X            }
  1603. X        } "track" {
  1604. X            if {[llength $ScopeList] > 1} {
  1605. X                puts stderr "No nesting tracks"
  1606. X                puts stderr "Line $LineNumber File: $InFileName"
  1607. X                exit 1
  1608. X            }
  1609. X            set ScopeList main:
  1610. X            set CurrentScope main:
  1611. X            set CurrentTrack track${TrackNumber}
  1612. X            incr TrackNumber
  1613. X        } "#" {
  1614. X            set comment 1
  1615. X        } default {
  1616. X            # other wise we're a file name
  1617. X            # we must append word to all scopes in ScopeList
  1618. X            if {[llength $ScopeList] == 0} {
  1619. X                puts stderr "No track specified"
  1620. X                puts stderr "Line $LineNumber File: $InFileName"
  1621. X                exit 1
  1622. X            }
  1623. X            foreach scope $ScopeList {
  1624. X                lappend ${CurrentTrack}($scope) $word
  1625. X            }
  1626. X            if {[string compare $ScopeDuration short] == 0} {
  1627. X                set ScopeList [lrange $ScopeList 1 end]
  1628. X                set CurrentScope [lindex $ScopeList 0]
  1629. X                set ScopeDuration long
  1630. X            }
  1631. X        }
  1632. X        }
  1633. X        if {$comment} {
  1634. X            break
  1635. X        }
  1636. X    }
  1637. X}
  1638. X
  1639. Xfor {set i 0} {$i < $TrackNumber} {incr i} {
  1640. X    puts stderr "Track [expr {$i + 1}]:"
  1641. X    set var track${i}(main:)
  1642. X    puts stderr [eval "set $var"]
  1643. X    puts stderr ""
  1644. X
  1645. X    # we want to collapse and concat each track to a mfile
  1646. X    set mfile [midimake]
  1647. X    midiconfig $mfile format 1
  1648. X    midiconfig $mfile tracks 2
  1649. X    lappend MFileList $mfile
  1650. X
  1651. X    # initially we are at the begining of the track
  1652. X    set track_time "0 0"
  1653. X    foreach filename [eval "set $var"] {
  1654. X        set track_time [CollapseAndAdd $mfile $filename $track_time]
  1655. X    }
  1656. X
  1657. X    # set the division to what was determined by CollapseAndAdd
  1658. X    midiconfig $mfile division $Division
  1659. X
  1660. X    # stick eot's on tracks 0 and 1
  1661. X    midiput $mfile 0 [lindex $track_time 0] metaeot
  1662. X    midiput $mfile 1 [lindex $track_time 1] metaeot
  1663. X
  1664. X    # and rewind it for future use
  1665. X    midirewind $mfile
  1666. X
  1667. X}
  1668. X
  1669. X# and then create one final mfile from each individual track mfile
  1670. X# track 0's must merge - other tracks stay separate
  1671. Xset moutfile [midimake]
  1672. Xmidiconfig $moutfile format 1
  1673. Xmidiconfig $moutfile track [expr {$TrackNumber + 1}]
  1674. Xmidiconfig $moutfile division $Division
  1675. X
  1676. X# by now everything is in the same division so we can use tscalars of 1
  1677. X
  1678. Xputs stderr "Final Merge"
  1679. X
  1680. X# make track 0 merge list
  1681. X# and append other tracks
  1682. Xset track 1
  1683. Xforeach mfile $MFileList {
  1684. X    lappend mlist "$mfile 0 1"
  1685. X    set d [midimerge "$moutfile $track" "\"$mfile 1 1\"" 0]
  1686. X    midiput $moutfile $track $d metaeot
  1687. X    incr track
  1688. X}
  1689. X
  1690. Xset delta0 [midimerge "$moutfile 0" $mlist 0]
  1691. Xmidiput $moutfile 0 $delta0 metaeot
  1692. X
  1693. Xforeach mfile $MFileList {
  1694. X    midifree $mfile
  1695. X}
  1696. X
  1697. Xmidiwrite $moutfile $OutFile
  1698. Xmidifree $moutfile
  1699. Xclose $OutFile
  1700. Xexit 0
  1701. END_OF_FILE
  1702.   if test 7875 -ne `wc -c <'tclm-1.0/mseq'`; then
  1703.     echo shar: \"'tclm-1.0/mseq'\" unpacked with wrong size!
  1704.   fi
  1705.   chmod +x 'tclm-1.0/mseq'
  1706.   # end of 'tclm-1.0/mseq'
  1707. fi
  1708. if test -f 'tclm-1.0/tclmPlay.c' -a "${1}" != "-c" ; then 
  1709.   echo shar: Will not clobber existing file \"'tclm-1.0/tclmPlay.c'\"
  1710. else
  1711.   echo shar: Extracting \"'tclm-1.0/tclmPlay.c'\" \(16902 characters\)
  1712.   sed "s/^X//" >'tclm-1.0/tclmPlay.c' <<'END_OF_FILE'
  1713. X/*-
  1714. X * Copyright (c) 1993 Michael B. Durian.  All rights reserved.
  1715. X *
  1716. X * Redistribution and use in source and binary forms, with or without
  1717. X * modification, are permitted provided that the following conditions
  1718. X * are met:
  1719. X * 1. Redistributions of source code must retain the above copyright
  1720. X *    notice, this list of conditions and the following disclaimer.
  1721. X * 2. Redistributions in binary form must reproduce the above copyright
  1722. X *    notice, this list of conditions and the following disclaimer in the
  1723. X *    documentation and/or other materials provided with the distribution.
  1724. X * 3. All advertising materials mentioning features or use of this software
  1725. X *    must display the following acknowledgement:
  1726. X *    This product includes software developed by Michael B. Durian.
  1727. X * 4. The name of the the Author may be used to endorse or promote 
  1728. X *    products derived from this software without specific prior written 
  1729. X *    permission.
  1730. X *
  1731. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 
  1732. X * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  1733. X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
  1734. X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  1735. X * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  1736. X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  1737. X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  1738. X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  1739. X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  1740. X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  1741. X * SUCH DAMAGE.
  1742. X */
  1743. X/*
  1744. X * tclmPlay.c,v 1.9 1993/05/07 17:45:11 durian Exp
  1745. X */
  1746. X#ifdef MIDIPLAY
  1747. X
  1748. Xstatic char cvsid[] = "tclmPlay.c,v 1.9 1993/05/07 17:45:11 durian Exp";
  1749. X
  1750. X#include <signal.h>
  1751. X#include <sys/ioctl.h>
  1752. X#include <i386/isa/midiioctl.h>
  1753. X#include "tclInt.h"
  1754. X#include "tclUnix.h"
  1755. X#include "mutil.h"
  1756. X#include "mdevice.h"
  1757. X#include "tclm.h"
  1758. X#include "tclmPlay.h"
  1759. X
  1760. Xstatic int now_playing = 0;
  1761. Xstatic int Dev;
  1762. Xstatic PlayMode Mode;
  1763. Xstatic int Pipe[2];
  1764. X
  1765. Xstatic char *play_usage = "midiplay [bg | background] [repeat] \
  1766. X[tracks track_list] [reltempo tempo_scalar] mfileId";
  1767. Xstatic char *record_usage = "midirecord [bg | background] [play play_mfileId \
  1768. X[repeat] [tracks track_list] [reltempo tempo_scalar]] record_mfileId";
  1769. X
  1770. Xvoid
  1771. XTclm_InitPlay(interp)
  1772. X    Tcl_Interp *interp;
  1773. X{
  1774. X
  1775. X    Tcl_CreateCommand(interp, "midiplay", Tclm_MidiPlay, NULL, NULL);
  1776. X    Tcl_CreateCommand(interp, "midirecord", Tclm_MidiRecord, NULL, NULL);
  1777. X    Tcl_CreateCommand(interp, "midistop", Tclm_MidiStop, NULL, NULL);
  1778. X    signal(SIGCHLD, watchdog);
  1779. X    signal(SIGHUP, Tclm_CatchStop);
  1780. X}
  1781. X
  1782. X
  1783. Xint
  1784. XTclm_MidiPlay(dummy, interp, argc, argv)
  1785. X    ClientData dummy;
  1786. X    Tcl_Interp *interp;
  1787. X    int argc;
  1788. X    char **argv;
  1789. X{
  1790. X    double reltempo;
  1791. X    MIDI_FILE *mfile;
  1792. X    char *mfile_name;
  1793. X    int *tracks;
  1794. X    int background;
  1795. X    int i;
  1796. X    int num_tracks;
  1797. X    int pid;
  1798. X    int repeat;
  1799. X    int result;
  1800. X    
  1801. X
  1802. X    if (argc < 2) {
  1803. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  1804. X            play_usage, "\"", (char *)NULL);
  1805. X        return (TCL_ERROR);
  1806. X    }
  1807. X
  1808. X    reltempo = 1.0;
  1809. X    repeat = 0;
  1810. X    background = 0;
  1811. X    mfile_name = NULL;
  1812. X    tracks = NULL;
  1813. X    num_tracks = 0;
  1814. X
  1815. X    for (i = 1; i < argc; i++) {
  1816. X        switch(argv[i][0]) {
  1817. X        case 'b':
  1818. X            if (strncmp(argv[i], "bg", sizeof(argv[i])) == 0 ||
  1819. X                strncmp(argv[i], "background", sizeof(argv[i]))
  1820. X                == 0)
  1821. X                background = 1;
  1822. X            else if (mfile_name == NULL)
  1823. X                mfile_name = argv[i];
  1824. X            else {
  1825. X                Tcl_AppendResult(interp, "bad option: ",
  1826. X                    "should be \"", play_usage, "\"",
  1827. X                    (char *)NULL);
  1828. X                if (tracks != NULL)
  1829. X                    free(tracks);
  1830. X                return (TCL_ERROR);
  1831. X            }
  1832. X            break;
  1833. X        case 'r':
  1834. X            if (strncmp(argv[i], "reltempo", strlen(argv[i]))
  1835. X                == 0)
  1836. X                reltempo = atof(argv[++i]);
  1837. X            else if (strncmp(argv[i], "repeat", strlen(argv[i]))
  1838. X                == 0)
  1839. X                repeat = 1;
  1840. X            else if (mfile_name == NULL)
  1841. X                mfile_name = argv[i];
  1842. X            else {
  1843. X                Tcl_AppendResult(interp, "bad option: ",
  1844. X                    "should be \"", play_usage, "\"",
  1845. X                    (char *)NULL);
  1846. X                if (tracks != NULL)
  1847. X                    free(tracks);
  1848. X                return (TCL_ERROR);
  1849. X            }
  1850. X
  1851. X            break;
  1852. X        case 't':
  1853. X            if (strncmp(argv[i], "tracks", strlen(argv[i]))
  1854. X                == 0) {
  1855. X                if ((num_tracks = Tclm_ParseTracks(interp,
  1856. X                    argv[i + 1], &tracks)) == -1)
  1857. X                    return (TCL_ERROR);
  1858. X                i++;
  1859. X            } else if (mfile_name == NULL) {
  1860. X                mfile_name = argv[i];
  1861. X            } else {
  1862. X                Tcl_AppendResult(interp, "bad option: ",
  1863. X                    "should be \"", play_usage, "\"",
  1864. X                    (char *)NULL);
  1865. X                if (tracks != NULL)
  1866. X                    free(tracks);
  1867. X                return (TCL_ERROR);
  1868. X            }
  1869. X            break;
  1870. X        default:
  1871. X            if (mfile_name == NULL)
  1872. X                mfile_name = argv[i];
  1873. X            else {
  1874. X                Tcl_AppendResult(interp, "bad option: ",
  1875. X                    "should be \"", play_usage, "\"",
  1876. X                    (char *)NULL);
  1877. X                if (tracks != NULL)
  1878. X                    free(tracks);
  1879. X                return (TCL_ERROR);
  1880. X            }
  1881. X        }
  1882. X    }
  1883. X
  1884. X    if ((result = Tclm_GetMFile(interp, mfile_name, &mfile)) != TCL_OK) {
  1885. X        if (tracks != NULL)
  1886. X            free(tracks);
  1887. X        return (result);
  1888. X    }
  1889. X
  1890. X    /* If track list isn't set use all tracks */
  1891. X    if (num_tracks == 0) {
  1892. X        num_tracks = mfile->hchunk.num_trks;
  1893. X        if ((tracks = (int *)malloc(sizeof(int) * num_tracks))
  1894. X            == NULL) {
  1895. X            Tcl_AppendResult(interp, "Not enough memory",
  1896. X                (char *)NULL);
  1897. X            return (TCL_ERROR);
  1898. X        }
  1899. X        for (i = 0; i < num_tracks; i++)
  1900. X            tracks[i] = i;
  1901. X    }
  1902. X
  1903. X    Mode = PLAY;
  1904. X    if ((Dev = open_midi_device(Mode)) == -1) {
  1905. X        Tcl_AppendResult(interp, MidiError, (char *)NULL);
  1906. X        free(tracks);
  1907. X        return (TCL_ERROR);
  1908. X    }
  1909. X
  1910. X    if (!init_midi_device(Dev, &mfile->hchunk, reltempo)) {
  1911. X        Tcl_AppendResult(interp, MidiError, (char *)NULL);
  1912. X        free(tracks);
  1913. X        return (TCL_ERROR);
  1914. X    }
  1915. X
  1916. X    if (!start_midi_device(Dev, Mode)) {
  1917. X        Tcl_AppendResult(interp, MidiError, (char *)NULL);
  1918. X        free(tracks);
  1919. X        return (TCL_ERROR);
  1920. X    }
  1921. X
  1922. X    if (!background) {
  1923. X        now_playing = 1;
  1924. X        if (!play_tracks(Dev, mfile->tchunks, tracks, num_tracks,
  1925. X            repeat)) {
  1926. X            Tcl_AppendResult(interp, "Couldn't play tracks\n",
  1927. X                MidiError, (char *)NULL);
  1928. X            free(tracks);
  1929. X            return (TCL_ERROR);
  1930. X        }
  1931. X        now_playing = 0;
  1932. X        if (!stop_midi_device(Dev, Mode)) {
  1933. X            Tcl_AppendResult(interp, MidiError, (char *)NULL);
  1934. X            free(tracks);
  1935. X            return (TCL_ERROR);
  1936. X        }
  1937. X
  1938. X        /*
  1939. X         * give time for the stop to take effect
  1940. X         * since stop might not happen until next clock
  1941. X         */
  1942. X         sleep(1);
  1943. X
  1944. X        if (!close_midi_device(Dev)) {
  1945. X            Tcl_AppendResult(interp, MidiError, (char *)NULL);
  1946. X            free(tracks);
  1947. X            return (TCL_ERROR);
  1948. X        }
  1949. X        Tcl_AppendResult(interp, "0", (char *)NULL);
  1950. X    } else {
  1951. X        switch(pid = fork()) {
  1952. X        case -1:
  1953. X            Tcl_AppendResult(interp, "Couldn't fork",
  1954. X                (char *)NULL);
  1955. X            free(tracks);
  1956. X            return (TCL_ERROR);
  1957. X        case 0:
  1958. X            /* child */
  1959. X            now_playing = 1;
  1960. X            if (!play_tracks(Dev, mfile->tchunks, tracks,
  1961. X                num_tracks, repeat)) {
  1962. X                Tcl_AppendResult(interp,
  1963. X                    "Couldn't play tracks\n", MidiError,
  1964. X                    (char *)NULL);
  1965. X                free(tracks);
  1966. X                return (TCL_ERROR);
  1967. X            }
  1968. X            now_playing = 0;
  1969. X            if (!stop_midi_device(Dev, Mode)) {
  1970. X                Tcl_AppendResult(interp, MidiError,
  1971. X                    (char *)NULL);
  1972. X                free(tracks);
  1973. X                return (TCL_ERROR);
  1974. X            }
  1975. X
  1976. X            /*
  1977. X             * give time for the stop to take effect
  1978. X             * since stop might not happen until next clock
  1979. X             */
  1980. X             sleep(1);
  1981. X
  1982. X            if (!close_midi_device(Dev)) {
  1983. X                Tcl_AppendResult(interp, MidiError,
  1984. X                    (char *)NULL);
  1985. X                free(tracks);
  1986. X                return (TCL_ERROR);
  1987. X            }
  1988. X            exit(0);
  1989. X        default:
  1990. X            if (!close_midi_device(Dev)) {
  1991. X                Tcl_AppendResult(interp, MidiError,
  1992. X                    (char *)NULL);
  1993. X                free(tracks);
  1994. X                return (TCL_ERROR);
  1995. X            }
  1996. X            sprintf(interp->result, "%d", pid);
  1997. X            break;
  1998. X        }
  1999. X    }
  2000. X
  2001. X    free(tracks);
  2002. X    return (TCL_OK);
  2003. X}
  2004. X
  2005. Xint
  2006. XTclm_MidiRecord(dummy, interp, argc, argv)
  2007. X    ClientData dummy;
  2008. X    Tcl_Interp *interp;
  2009. X    int argc;
  2010. X    char **argv;
  2011. X{
  2012. X    double reltempo;
  2013. X    MIDI_FILE *pfile;
  2014. X    MIDI_FILE *rfile;
  2015. X    TCHUNK *play_tracks;
  2016. X    TCHUNK *tmp_track;
  2017. X    char *pfile_name;
  2018. X    char *rfile_name;
  2019. X    int *tracks;
  2020. X    int background;
  2021. X    int i;
  2022. X    int num_tracks;
  2023. X    int pid;
  2024. X    int repeat;
  2025. X    int result;
  2026. X    
  2027. X
  2028. X    if (argc < 2) {
  2029. X        Tcl_AppendResult(interp, "wrong # args: should be \"",
  2030. X            record_usage, "\"", (char *)NULL);
  2031. X        return (TCL_ERROR);
  2032. X    }
  2033. X
  2034. X    reltempo = 1.0;
  2035. X    repeat = 0;
  2036. X    background = 0;
  2037. X    pfile = NULL;
  2038. X    rfile = NULL;
  2039. X    pfile_name = NULL;
  2040. X    rfile_name = NULL;
  2041. X    tracks = NULL;
  2042. X    num_tracks = 0;
  2043. X
  2044. X    for (i = 1; i < argc; i++) {
  2045. X        switch(argv[i][0]) {
  2046. X        case 'b':
  2047. X            if (strncmp(argv[i], "bg", sizeof(argv[i])) == 0 ||
  2048. X                strncmp(argv[i], "background", sizeof(argv[i]))
  2049. X                == 0)
  2050. X                background = 1;
  2051. X            else if (rfile_name == NULL)
  2052. X                rfile_name = argv[i];
  2053. X            else {
  2054. X                Tcl_AppendResult(interp, "bad option: ",
  2055. X                    "should be \"", record_usage, "\"",
  2056. X                    (char *)NULL);
  2057. X                if (tracks != NULL)
  2058. X                    free(tracks);
  2059. X                return (TCL_ERROR);
  2060. X            }
  2061. X            break;
  2062. X        case 'p':
  2063. X            if (strncmp(argv[i], "play", sizeof(argv[i])) == 0)
  2064. X                pfile_name = argv[++i];
  2065. X            else if (rfile_name == NULL)
  2066. X                rfile_name = argv[i];
  2067. X            else {
  2068. X                Tcl_AppendResult(interp, "bad option: ",
  2069. X                    "should be \"", record_usage, "\"",
  2070. X                    (char *)NULL);
  2071. X                if (tracks != NULL)
  2072. X                    free(tracks);
  2073. X                return (TCL_ERROR);
  2074. X            }
  2075. X            break;
  2076. X        case 'r':
  2077. X            if (strncmp(argv[i], "reltempo", strlen(argv[i]))
  2078. X                == 0)
  2079. X                reltempo = atof(argv[++i]);
  2080. X            else if (strncmp(argv[i], "repeat", strlen(argv[i]))
  2081. X                == 0)
  2082. X                repeat = 1;
  2083. X            else if (rfile_name == NULL)
  2084. X                rfile_name = argv[i];
  2085. X            else {
  2086. X                Tcl_AppendResult(interp, "bad option: ",
  2087. X                    "should be \"", record_usage, "\"",
  2088. X                    (char *)NULL);
  2089. X                if (tracks != NULL)
  2090. X                    free(tracks);
  2091. X                return (TCL_ERROR);
  2092. X            }
  2093. X
  2094. X            break;
  2095. X        case 't':
  2096. X            if (strncmp(argv[i], "tracks", strlen(argv[i]))
  2097. X                == 0) {
  2098. X                if ((num_tracks = Tclm_ParseTracks(interp,
  2099. X                    argv[i + 1], &tracks)) == -1)
  2100. X                    return (TCL_ERROR);
  2101. X                i++;
  2102. X            } else if (rfile_name == NULL) {
  2103. X                rfile_name = argv[i];
  2104. X            } else {
  2105. X                Tcl_AppendResult(interp, "bad option: ",
  2106. X                    "should be \"", record_usage, "\"",
  2107. X                    (char *)NULL);
  2108. X                if (tracks != NULL)
  2109. X                    free(tracks);
  2110. X                return (TCL_ERROR);
  2111. X            }
  2112. X            break;
  2113. X        default:
  2114. X            if (rfile_name == NULL)
  2115. X                rfile_name = argv[i];
  2116. X            else {
  2117. X                Tcl_AppendResult(interp, "bad option: ",
  2118. X                    "should be \"", record_usage, "\"",
  2119. X                    (char *)NULL);
  2120. X                if (tracks != NULL)
  2121. X                    free(tracks);
  2122. X                return (TCL_ERROR);
  2123. X            }
  2124. X        }
  2125. X    }
  2126. X
  2127. X    if (rfile_name == NULL) {
  2128. X        Tcl_AppendResult(interp, "Must specify rfile", (char *)NULL);
  2129. X        if (tracks != NULL)
  2130. X            free(tracks);
  2131. X        return (TCL_ERROR);
  2132. X    } else {
  2133. X        if ((result = Tclm_GetMFile(interp, rfile_name, &rfile))
  2134. X            != TCL_OK) {
  2135. X            if (tracks != NULL)
  2136. X                free(tracks);
  2137. X            return (result);
  2138. X        }
  2139. X    }
  2140. X
  2141. X
  2142. X    if (pfile_name == NULL)
  2143. X        play_tracks = NULL;
  2144. X    else {
  2145. X        if ((result = Tclm_GetMFile(interp, pfile_name, &pfile)) !=
  2146. X            TCL_OK) {
  2147. X            if (tracks != NULL)
  2148. X                free(tracks);
  2149. X            return (result);
  2150. X        }
  2151. X        play_tracks = pfile->tchunks;
  2152. X    }
  2153. X
  2154. X    /* If track list isn't set use all tracks */
  2155. X    if (pfile != NULL && num_tracks == 0) {
  2156. X        num_tracks = pfile->hchunk.num_trks;
  2157. X        if ((tracks = (int *)malloc(sizeof(int) * num_tracks))
  2158. X            == NULL) {
  2159. X            Tcl_AppendResult(interp, "Not enough memory",
  2160. X                (char *)NULL);
  2161. X            return (TCL_ERROR);
  2162. X        }
  2163. X        for (i = 0; i < num_tracks; i++)
  2164. X            tracks[i] = i;
  2165. X    }
  2166. X    if (pfile != NULL)
  2167. X        Mode = PLAYRECORD;
  2168. X    else {
  2169. X        num_tracks = 0;
  2170. X        Mode = RECORD;
  2171. X    }
  2172. X
  2173. X    if ((Dev = open_midi_device(Mode)) == -1) {
  2174. X        Tcl_AppendResult(interp, MidiError, (char *)NULL);
  2175. X        if (tracks != NULL)
  2176. X            free(tracks);
  2177. X        return (TCL_ERROR);
  2178. X    }
  2179. X
  2180. X    if (!init_midi_device(Dev, &rfile->hchunk, reltempo)) {
  2181. X        Tcl_AppendResult(interp, MidiError, (char *)NULL);
  2182. X        if (tracks != NULL)
  2183. X            free(tracks);
  2184. X        return (TCL_ERROR);
  2185. X    }
  2186. X
  2187. X    if (!start_midi_device(Dev, Mode)) {
  2188. X        Tcl_AppendResult(interp, MidiError, (char *)NULL);
  2189. X        if (tracks != NULL)
  2190. X            free(tracks);
  2191. X        return (TCL_ERROR);
  2192. X    }
  2193. X
  2194. X    if (!background) {
  2195. X        now_playing = 1;
  2196. X        if (!record_tracks(Dev, play_tracks, tracks, num_tracks,
  2197. X            &rfile->tchunks[0], repeat)) {
  2198. X            Tcl_AppendResult(interp, "Couldn't record track\n",
  2199. X                MidiError, (char *)NULL);
  2200. X            if (tracks != NULL)
  2201. X                free(tracks);
  2202. X            return (TCL_ERROR);
  2203. X        }
  2204. X        now_playing = 0;
  2205. X        if (!stop_midi_device(Dev, Mode)) {
  2206. X            Tcl_AppendResult(interp, MidiError, (char *)NULL);
  2207. X            if (tracks != NULL)
  2208. X                free(tracks);
  2209. X            return (TCL_ERROR);
  2210. X        }
  2211. X
  2212. X        /*
  2213. X         * give time for the stop to take effect
  2214. X         * since stop might not happen until next clock
  2215. X         */
  2216. X         sleep(1);
  2217. X
  2218. X        if (!close_midi_device(Dev)) {
  2219. X            Tcl_AppendResult(interp, MidiError, (char *)NULL);
  2220. X            if (tracks != NULL)
  2221. X                free(tracks);
  2222. X            return (TCL_ERROR);
  2223. X        }
  2224. X        Tcl_AppendResult(interp, "0", (char *)NULL);
  2225. X    } else {
  2226. X        if (pipe(Pipe) == -1) {
  2227. X            Tcl_AppendResult(interp, "Couldn't open pipe: ",
  2228. X                sys_errlist[errno]);
  2229. X            if (tracks != NULL)
  2230. X                free(tracks);
  2231. X            return (TCL_ERROR);
  2232. X        }
  2233. X        switch(pid = fork()) {
  2234. X        case -1:
  2235. X            Tcl_AppendResult(interp, "Couldn't fork",
  2236. X                (char *)NULL);
  2237. X            if (tracks != NULL)
  2238. X                free(tracks);
  2239. X            return (TCL_ERROR);
  2240. X        case 0:
  2241. X            /* child */
  2242. X            close(Pipe[0]);
  2243. X            now_playing = 1;
  2244. X            if (!record_tracks(Dev, play_tracks, tracks,
  2245. X                num_tracks, &rfile->tchunks[0], repeat))
  2246. X                exit(1);
  2247. X            now_playing = 0;
  2248. X            if (!stop_midi_device(Dev, Mode))
  2249. X                exit(1);
  2250. X
  2251. X            /*
  2252. X             * give time for the stop to take effect
  2253. X             * since stop might not happen until next clock
  2254. X             */
  2255. X             sleep(1);
  2256. X
  2257. X            if (!close_midi_device(Dev))
  2258. X                exit(1);
  2259. X            /*
  2260. X             * write new track back to parent
  2261. X             */
  2262. X            if (mwrite(Pipe[1], (char *)&rfile->tchunks[0].length,
  2263. X                sizeof(rfile->tchunks[0].length)) !=
  2264. X                sizeof(rfile->tchunks[0].length))
  2265. X                exit(1);
  2266. X            if (mwrite(Pipe[1],
  2267. X                (char *)rfile->tchunks[0].event_start,
  2268. X                rfile->tchunks[0].length) !=
  2269. X                rfile->tchunks[0].length)
  2270. X                exit(1);
  2271. X            close(Pipe[1]);
  2272. X            free(tmp_track);
  2273. X            exit(0);
  2274. X        default:
  2275. X            close(Pipe[1]);
  2276. X            if (!close_midi_device(Dev)) {
  2277. X                Tcl_AppendResult(interp, MidiError,
  2278. X                    (char *)NULL);
  2279. X                if (tracks != NULL)
  2280. X                    free(tracks);
  2281. X                return (TCL_ERROR);
  2282. X            }
  2283. X            sprintf(interp->result, "%d", pid);
  2284. X            break;
  2285. X        }
  2286. X    }
  2287. X
  2288. X    if (tracks != NULL)
  2289. X        free(tracks);
  2290. X    return (TCL_OK);
  2291. X}
  2292. X
  2293. Xvoid
  2294. Xwatchdog()
  2295. X{
  2296. X#ifdef UNION_WAIT
  2297. X    union wait wstatus;
  2298. X#else
  2299. X    int wstatus;
  2300. X#endif
  2301. X
  2302. X    (void)wait(&wstatus);
  2303. X}
  2304. X
  2305. Xint
  2306. XTclm_ParseTracks(interp, list, tracks)
  2307. X    Tcl_Interp *interp;
  2308. X    char *list;
  2309. X    int **tracks;
  2310. X{
  2311. X    char **track_strs;
  2312. X    char *chk_ptr;
  2313. X    int i;
  2314. X    int num_tracks;
  2315. X
  2316. X    if (Tcl_SplitList(interp, list, &num_tracks, &track_strs) != TCL_OK) {
  2317. X        Tcl_AppendResult(interp, "Bad track list", (char *)NULL);
  2318. X        return (-1);
  2319. X    }
  2320. X    if ((*tracks = (int *)malloc(sizeof(int) * num_tracks)) == NULL) {
  2321. X        Tcl_AppendResult(interp, "No more memory", (char *)NULL);
  2322. X        return (-1);
  2323. X    }
  2324. X    for (i = 0; i < num_tracks; i++) {
  2325. X        (*tracks)[i] = (int)strtol(track_strs[i], &chk_ptr, 0);
  2326. X        if (chk_ptr == track_strs[i]) {
  2327. X            Tcl_AppendResult(interp, "Bad track value ",
  2328. X                track_strs[i], (char *)NULL);
  2329. X            free(*tracks);
  2330. X            return (-1);
  2331. X        }
  2332. X    }
  2333. X    free((char *)track_strs);
  2334. X    return (num_tracks);
  2335. X}
  2336. X
  2337. Xint
  2338. XTclm_MidiStop(dummy, interp, argc, argv)
  2339. X    ClientData dummy;
  2340. X    Tcl_Interp *interp;
  2341. X    int argc;
  2342. X    char **argv;
  2343. X{
  2344. X    MIDI_FILE *rfile;
  2345. X    char *chk_ptr;
  2346. X    unsigned char *events;
  2347. X    long length;
  2348. X    int pid;
  2349. X    int result;
  2350. X
  2351. X    /*
  2352. X     * argv[0] - midistop
  2353. X     * argv[1] - pid
  2354. X     * argv[2] - [rfile]
  2355. X     */
  2356. X    if (argc < 2 || argc > 3) {
  2357. X        Tcl_AppendResult(interp, "wrong # args: should be\"",
  2358. X            argv[0], " pid [rfile]\"", (char *)NULL);
  2359. X        return (TCL_ERROR);
  2360. X    }
  2361. X
  2362. X    pid = (int)strtol(argv[1], &chk_ptr, 0);
  2363. X    if (chk_ptr == argv[1] || pid <= 0) {
  2364. X        Tcl_AppendResult(interp, "bad pid value: ", argv[1],
  2365. X            (char *)NULL);
  2366. X        return (TCL_ERROR);
  2367. X    }
  2368. X    if (kill(pid, SIGHUP) != -1)
  2369. X        Tcl_AppendResult(interp, "1", (char *)NULL);
  2370. X    else {
  2371. X        if (errno == ESRCH)
  2372. X            Tcl_AppendResult(interp, "0", (char *)NULL);
  2373. X        else {
  2374. X            Tcl_AppendResult(interp, "Error killing process: ",
  2375. X                sys_errlist[errno], (char *)NULL);
  2376. X            return (TCL_ERROR);
  2377. X        }
  2378. X    }
  2379. X    /* pick up recorded file if specified */
  2380. X    if (argc == 3) {
  2381. X        if ((result = Tclm_GetMFile(interp, argv[2], &rfile))
  2382. X            != TCL_OK)
  2383. X            return (result);
  2384. X        if (mread(Pipe[0], (char *)&length, sizeof(length)) !=
  2385. X            sizeof(length)) {
  2386. X            Tcl_AppendResult(interp, "Couldn't read rfile: ",
  2387. X                sys_errlist[errno]);
  2388. X            close(Pipe[0]);
  2389. X            return (TCL_ERROR);
  2390. X        }
  2391. X        if ((events = (unsigned char *) malloc(length)) == NULL) {
  2392. X            Tcl_AppendResult(interp, "Not enough memory ",
  2393. X                "for record file", (char *)NULL);
  2394. X            close(Pipe[0]);
  2395. X            return (TCL_ERROR);
  2396. X        }
  2397. X        if (mread(Pipe[0], (char *)events, length) != length) {
  2398. X            Tcl_AppendResult(interp, "Couldn't read record ",
  2399. X                "track: ", sys_errlist[errno], (char *)NULL);
  2400. X            close(Pipe[0]);
  2401. X            free(events);
  2402. X            return (TCL_ERROR);
  2403. X        }
  2404. X        if (!put_smf_event(&rfile->tchunks[0], events, length)) {
  2405. X            Tcl_AppendResult(interp, "Couldn't add to ",
  2406. X                "record track: ", MidiError, (char *)NULL);
  2407. X            close(Pipe[0]);
  2408. X            free(events);
  2409. X            return (TCL_ERROR);
  2410. X        }
  2411. X        free(events);
  2412. X        close(Pipe[0]);
  2413. X    }
  2414. X
  2415. X    return (TCL_OK);
  2416. X}
  2417. X
  2418. Xvoid
  2419. XTclm_CatchStop()
  2420. X{
  2421. X    int ret;
  2422. X
  2423. X    ret = stop_processing(Dev);
  2424. X}
  2425. X#endif
  2426. END_OF_FILE
  2427.   if test 16902 -ne `wc -c <'tclm-1.0/tclmPlay.c'`; then
  2428.     echo shar: \"'tclm-1.0/tclmPlay.c'\" unpacked with wrong size!
  2429.   fi
  2430.   # end of 'tclm-1.0/tclmPlay.c'
  2431. fi
  2432. echo shar: End of archive 2 \(of 5\).
  2433. cp /dev/null ark2isdone
  2434. MISSING=""
  2435. for I in 1 2 3 4 5 ; do
  2436.     if test ! -f ark${I}isdone ; then
  2437.     MISSING="${MISSING} ${I}"
  2438.     fi
  2439. done
  2440. if test "${MISSING}" = "" ; then
  2441.     echo You have unpacked all 5 archives.
  2442.     rm -f ark[1-9]isdone
  2443. else
  2444.     echo You still must unpack the following archives:
  2445.     echo "        " ${MISSING}
  2446. fi
  2447. exit 0
  2448. exit 0 # Just in case...
  2449.