/* * rplayd.c * * Copyright (c) 1992 by Mark Boyns * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. This software is provided "as is" without express or * implied warranty. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libst.h" #include "rplay.h" /* * The defines below can be used to customize rplayd. */ #define MAX_SOUNDS 1024 /* maximum sounds that rplayd can handle */ /* this must be more than the sounds in rplay.conf */ #define SPOOL_SIZE 8 /* number of sounds that can be played at once */ #define SOUND_LIST_SIZE 32 /* maximum size of a sound list */ #define AUDIO_BUFSIZE 500 /* rplayd audio buffer size */ #define TIME_SLICE 50000 /* micro seconds */ #define RPLAYD_TIMEOUT 6000 /* 50000usec = 1/20sec, 20*5*60 = 5 mins */ #define AUDIO_CLOSE_TIMEOUT 100 /* close audio device when not in use */ /* 50000usec = 1/20sec, 20*5 = 5 secs */ /* * These should not be changed */ #define MINPRIO 1 #define MAXPRIO 10 #define SUN_AUDIO_DEVICE "/dev/audio" #define SUN_MAGIC 0x2e736e64 #define SUN_HDRSIZE 24 typedef struct sound { char *name; /* name of the sound file */ char path[MAXPATHLEN]; /* pathname of the sound file */ char *buf; /* the sound buffer */ char *start; /* where to start */ char *stop; /* where to stop */ int size; /* sizeof the sound buffer */ } Sound; typedef struct spool { int nsounds; /* nsounds in the sound list */ int curr; /* current playing sound in the list */ int state; /* state of the current sound */ Sound *slist[SOUND_LIST_SIZE];/* the sound list */ int vlist[SOUND_LIST_SIZE]; /* the volume list */ char *ptr; /* sound buffer pointer */ char *end; /* the end of the sound buffer */ } Spool; Spool spool[SPOOL_SIZE]; /* the sound spool */ Sound *list[SOUND_LIST_SIZE]; /* temporary list used by the server */ char recvbuf[MAX_PACKET]; /* buffer for incoming packets */ ENTRY *hsearch(); Sound *lookup(); int scheduler(), player(), server(); int in = 0, out = 0; int audio_fd = -1; main() { config(); spool_init(); pod_setmaxpri(MAXPRIO); lwp_setstkcache(4096, 4); lwp_create((thread_t *)0, scheduler, MAXPRIO, 0, lwp_newstk(), 0); lwp_create((thread_t *)0, player, MINPRIO, 0, lwp_newstk(), 0); lwp_create((thread_t *)0, server, MINPRIO, 0, lwp_newstk(), 0); exit(0); } config() { Sound *s; ENTRY e; FILE *fp; char buf[MAXPATHLEN], *p; fp = fopen(RPLAY_CONF, "r"); if (fp == NULL) { fprintf(stderr, "rplayd: cannot open %s\n", RPLAY_CONF); exit(1); } if (hcreate(MAX_SOUNDS) == 0) { fprintf(stderr, "rplayd: cannot create hash table\n"); exit(1); } while(fgets(buf, sizeof(buf), fp) != NULL) { if (buf[0] == '#') { continue; } s = (Sound *)malloc(sizeof(Sound)); sscanf(buf, "%s", s->path); p = rindex(s->path, '/'); s->name = p == NULL ? s->path : p+1; s->buf = NULL; s->size = 0; e.key = s->name; e.data = (char *)s; if (hsearch(e, ENTER) == NULL) { fprintf(stderr, "rplayd: the hash table is full, too many sounds\n"); exit(1); } } fclose(fp); } spool_init() { int i; for(i = 0; i < SPOOL_SIZE; i++) { spool_clear(i); } } Sound *lookup(name) char *name; { ENTRY e, *found; Sound *s; e.key = name; found = hsearch(e, FIND); if (found == NULL) { return NULL; } s = (Sound *)found->data; if (s->buf == NULL) { struct stat st; long magic, hdr_size; int fd; fd = open(s->path, O_RDONLY, 0); if (fd < 0) { return NULL; } if (fstat(fd, &st) < 0) { return NULL; } s->size = st.st_size; /* * use mmap to load the sound */ s->buf = mmap(0, s->size, PROT_READ, MAP_SHARED, fd, 0); if (s->buf == (caddr_t)-1) { return NULL; } /* * make sure it is a Sun audio file */ magic = *((long *)s->buf); if (magic != SUN_MAGIC) { return NULL; } /* * ignore the header */ hdr_size = *((long *)s->buf + 1); if (hdr_size < SUN_HDRSIZE) { return NULL; } s->start = s->buf + hdr_size; s->stop = s->buf + s->size - 1; close(fd); } return s; } server() { int sock_fd, x, restarted, len; struct sockaddr_in s, f; int flen = sizeof(f); char *p; int attr, i, n, j; int found; Sound *sound; #ifdef INETD sock_fd = 0; #else bzero(&s, sizeof(s)); s.sin_family = AF_INET; s.sin_port = htons(RPLAY_PORT); s.sin_addr.s_addr = INADDR_ANY; sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) { perror("socket"); pod_exit(1); } if (bind(sock_fd, &s, sizeof(s)) < 0) { perror("bind"); pod_exit(1); } #endif INETD for(;;) { len = recvfrom(sock_fd, recvbuf, sizeof(recvbuf), 0, &f, &flen); p = recvbuf; attr = *p++; switch(attr) { case RPLAY_PLAY: for(i = 0; i < SPOOL_SIZE; i++) { if (spool[i].state == RPLAY_NULL) { break; } } if (i == SPOOL_SIZE) { break; } do { sound = lookup(p); if (sound == NULL) { spool_clear(i); break; } p += strlen(p) + 1; spool[i].slist[spool[i].nsounds] = sound; spool[i].vlist[spool[i].nsounds] = (unsigned char)*p++; spool[i].nsounds++; } while(*p != NULL); if (sound != NULL) { spool[i].ptr = spool[i].slist[0]->start; spool[i].end = spool[i].slist[0]->stop; spool[i].curr = 0; spool[i].state = attr; in++; } break; case RPLAY_STOP: case RPLAY_PAUSE: case RPLAY_CONTINUE: n = 0; do { list[n] = lookup(p); if (list[n] == NULL) { break; } n++; p += strlen(p) + 1; } while(*p != NULL); if (list[n-1] == NULL) { break; } for(i = 0; i < SPOOL_SIZE; i++) { if (spool[i].nsounds == n) { found = 1; for(j = 0; j < n; j++) { if (list[j] != spool[i].slist[j]) { found = 0; break; } } if (found) { spool[i].state = attr; break; } } } break; default: fprintf(stderr, "rplayd: unknown rplay attribute (0x%0x)\n", attr); break; } } } scheduler() { struct timeval t; int cnt; cnt = 0; t.tv_sec = 0; t.tv_usec = TIME_SLICE; for(;;) { if (in == out) { cnt++; switch(cnt) { case RPLAYD_TIMEOUT: pod_exit(0); case AUDIO_CLOSE_TIMEOUT: if (audio_fd != -1) { close(audio_fd); audio_fd = -1; } break; } } else { cnt = 0; } lwp_sleep(&t); lwp_resched(MINPRIO); } } player() { int i, index, empty; long total; long val; char buf[AUDIO_BUFSIZE]; index = 0; for(;;) { total = 0; empty = 1; for(i = 0; i < SPOOL_SIZE; i++) { switch(spool[i].state) { case RPLAY_CONTINUE: spool[i].state = RPLAY_PLAY; case RPLAY_PLAY: empty = 0; val = st_ulaw_to_linear((unsigned char)*spool[i].ptr) * spool[i].vlist[spool[i].curr]; total += val >> 7; if (spool[i].ptr == spool[i].end) { spool[i].curr++; if (spool[i].curr == spool[i].nsounds) { spool_clear(i); out++; } else { spool[i].ptr = spool[i].slist[spool[i].curr]->start; spool[i].end = spool[i].slist[spool[i].curr]->stop; } } else { spool[i].ptr++; } break; case RPLAY_STOP: spool_clear(i); out++; break; default: break; } } if (empty) { if (index > 0) { if (audio_fd == -1) { audio_fd = open(SUN_AUDIO_DEVICE, O_WRONLY | O_NDELAY, 0); } if (audio_fd != -1) { write(audio_fd, buf, index); } index = 0; } lwp_yield(SELF); } else { buf[index++] = st_linear_to_ulaw(total); if (index == AUDIO_BUFSIZE) { if (audio_fd == -1) { audio_fd = open(SUN_AUDIO_DEVICE, O_WRONLY | O_NDELAY, 0); } if (audio_fd != -1) { write(audio_fd, buf, index); } index = 0; } } } } spool_clear(i) int i; { spool[i].curr = 0; spool[i].nsounds = 0; spool[i].ptr = NULL; spool[i].end = NULL; spool[i].state = RPLAY_NULL; }