⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 abplay.c

📁 lpc 2400 bps语音编解码程序
💻 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_value
sigpoll_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 + -