📄 abplay.c
字号:
#include <unistd.h>#include <fcntl.h>#include <stropts.h>#include <errno.h>#include <values.h>#include "abplay.h"#include <multimedia/libaudio.h>#include <multimedia/audio_device.h>#include <xview/notify.h>#define AUDIO_DEV "/dev/audio"#define AUDIO_CTLDEV "/dev/audioctl"static int device_fd = 0; /* Audio device fd */static int devctl_fd = 0; /* Audioctl device fd (for status changes) */#define SIG_SIZE 4096 /* Audio output buffer size (>= STEPMAX!) */static char ptbuf[SIG_SIZE]; /* Signal translation buffer */static int event_posted = FALSE; /* Synchronous notifier client called */static struct { /* State of background audio stream */ int state; /* IDLE, DRAIN, or PLAY */ int id; /* ID of currently (or most recently) playing data */ char *segstart; /* Pointer to beginning of segment */ char *segend; /* Pointer to end of segment */ char *posn; /* Current I/O location in signal file */ char *bp; /* Current I/O location in translation buffer */ int nb; /* Unsent bytes left in translation buffer */ int trans; /* Audio file translation code */ int nmax; /* Max input to avoid translation buffer overflow */} audio;static struct { int id; /* File ID of request (-1 if none) */ float start; /* Starting point of segment (seconds) */ float end; /* Ending point of segment */} request;Notify_value sigpoll_handler();abp_open_device(int dtype){ int flags; char *dev = AUDIO_DEV; if (device_fd <= 0) { audio.state = IDLE; if ((device_fd = open(dev, O_WRONLY | O_NDELAY)) < 0) { perror(dev); if ((errno==EINTR) || (errno==EBUSY)) device_fd = -2; } else { flags = fcntl(device_fd, F_GETFL, 0) | O_NONBLOCK; if (fcntl(device_fd, F_SETFL, flags) < 0) perror("audio device F_SETFL fcntl"); /* Set non-blocking */ if (ioctl(device_fd, I_SETSIG, S_OUTPUT | S_MSG) < 0) perror("audio device I_SETSIG"); /*Enable play SIGPOLL*/ if ((devctl_fd = open(AUDIO_CTLDEV, O_RDWR)) < 0) perror(AUDIO_CTLDEV); if (ioctl(devctl_fd, I_SETSIG, S_MSG) < 0) perror("audio_ctl I_SETSIG"); /* Enable status SIGPOLL */ /* Set up to catch SIGPOLL asynchronously to service audio stream */ /* Notifier client id is meaningless, but must be unique, so use the virtual memory address of something random that we own. */ (void) notify_set_signal_func((int)&audio, (Notify_func)sigpoll_handler, SIGPOLL, NOTIFY_ASYNC); } } return device_fd; /* -1 = error, -2 = try again */}abp_close_device(){ if (device_fd > 0) close(device_fd); if (devctl_fd > 0) close(devctl_fd); device_fd = devctl_fd = 0;}abp_start_play(int id, int segnum){ struct segstate sg; if (abp_open_device(0) < 0) return; sg = abp_get_segstate(); request.id = id; request.start = request.end = 0.0; if (segnum > 0 && segnum <= sg.nsegs) { request.start = sg.segs[segnum-1].start; request.end = sg.segs[segnum-1].end; } audio.state = DRAIN; kill(getpid(), SIGPOLL); /* kick SIGPOLL to start play */}abp_stop_play(){ audio.state = DRAIN; audio_play_eof(device_fd); /* EOF will SIGPOLL after buffer drains */}abp_abort_play(){ audio.state = DRAIN; audio_flush_play(device_fd); kill(getpid(), SIGPOLL); /* Quit immediately */}abp_get_audio_status(struct audio_status *status){ status->id = audio.id; status->state = audio.state; event_posted = FALSE;}/************************* End of public interface *********************//*********************** The rest is implementation ********************/play_setup(){ int i; int id, n1, n2; struct abfile *abf; Audio_hdr dev_hdr, f_hdr; char *base; id = request.id; request.id = -1; if ((abf = abp_get_abfile(id)) == (struct abfile *)0) { fprintf(stderr, "play: invalid file (%d)\n", id); return -1; } f_hdr = abf->h; if (request.end <= 0.0) { n1 = 0; n2 = MAXINT; } else { n1 = audio_secs_to_bytes(&f_hdr, request.start + abf->p.delay); n2 = audio_secs_to_bytes(&f_hdr, request.end + abf->p.delay) - 1; } if (n1 >= n2) return -1; audio.trans = audio_set_play_translate(device_fd, &f_hdr, &dev_hdr); if (audio.trans >= 0) { audio.state = PLAY; audio.nb = 0; audio.id = id; base = abf->data + abf->hsize; audio.segstart = base + MAX(0, n1); audio.segend = base + MIN(f_hdr.data_size-1, n2); audio.posn = audio.segstart; audio.nmax = (SIG_SIZE * f_hdr.bytes_per_unit) / dev_hdr.bytes_per_unit; } return audio.trans;}play_service(){ int outcnt, n; outcnt = audio.segend + 1 - audio.posn; while (outcnt > 0 || audio.nb > 0) { if (audio.nb == 0) { if (audio.trans == 0) { audio.bp = audio.posn; audio.nb = n = outcnt; } else { audio.bp = ptbuf; n = MIN(outcnt, audio.nmax); audio.nb = audio_translate(audio.trans, audio.posn, ptbuf, n); } audio.posn += n; outcnt -= n; } n = write(device_fd, audio.bp, audio.nb); if (n < 0 && errno != EWOULDBLOCK) { perror("audio device write err"); audio_flush_play(device_fd); } if (n <= 0) break; audio.nb -= n; audio.bp += n; if (outcnt == 0 && audio.nb == 0) abp_stop_play(); }}/* Asynchronous SIGPOLL handler. Service audio device */Notify_valuesigpoll_handler(Notify_client client, int sig, Notify_signal_mode when){ int n; unsigned active = 0; static char *sttr[] = {"IDLE", "DRAIN", "PLAY"};/* fprintf(stderr, "APOLL in: %d (%s), req id=%d\n", audio.state, sttr[audio.state], request.id);*/ if (audio.state == DRAIN) { audio_get_play_active(device_fd, &active); if (active == 0) audio.state = IDLE; } if (audio.state == IDLE && request.id >= 0) { play_setup(); } if (audio.state == PLAY) play_service();/* Post a synchronous notifier event to client "SIGPOLL", to notify * display code that something may have happened. */ if (!event_posted) { event_posted = TRUE; notify_post_event(SIGPOLL, NULL, NOTIFY_SAFE); }/* fprintf(stderr, "APOLL out: %d (%s), %d %d\n", audio.state, sttr[audio.state], audio.id, active);*/ return NOTIFY_DONE;}audio_set_play_translate(int fd, Audio_hdr *f_hdr, Audio_hdr *d_hdr){/* * Set up audio file translation * fd = audio device file descriptor * f_hdr = file encoding * d_hdr = audio device encoding (returned) * Return value: * < 0: File is not compatible with audio device * = 0: Audio device is configured to match file * > 0: File can be translated to match device */ int i; char str[AUDIO_MAX_ENCODE_INFO];/* Try to set audio device to match file */ if (fd > 0) { *d_hdr = *f_hdr; i = audio_set_play_config(fd, d_hdr); if (i == AUDIO_SUCCESS) return 0; else if (i != AUDIO_ERR_NOEFFECT) { printf("status %d, fd = %d\n", i, fd); perror("audio_set_play_config"); audio_enc_to_str(f_hdr, str); fprintf(stderr,"Can't convert: %s\n", str); audio_enc_to_str(d_hdr, str); fprintf(stderr," to: %s\n", str); return -1; } }/* Can't set device to match file - see if we can convert file to match device */ return audio_setup_translate(*f_hdr, *d_hdr, 1);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -