File: linux_oss.cpp

package info (click to toggle)
amoeba 1.1-19
  • links: PTS
  • area: contrib
  • in suites: lenny
  • size: 816 kB
  • ctags: 966
  • sloc: cpp: 8,286; makefile: 173
file content (120 lines) | stat: -rw-r--r-- 3,105 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
 * A quite standard OSS (Linux) output driver. Uses fragments and byte
 * counters, so your soundcard driver would better support it :-) I've
 * heard not everybody do, but never had a bug report on it yet, at
 * least :-)
 */

#include "audio/linux_oss.h"
#include "exception.h"

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/soundcard.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/time.h>

OSSAudioDriver::OSSAudioDriver(AudioProvider *prv, float jump, MainLoop *lp)
{
	this->prov = prv;

	this->oss_fd = open("/dev/sound/dsp", O_RDWR);
	if (this->oss_fd == -1) {
	        this->oss_fd = open("/dev/dsp", O_RDWR);
	        if (this->oss_fd == -1)
	                throw new FatalException("/dev/dsp", strerror(errno));
	}

	this->set_ioctl(SNDCTL_DSP_RESET, 1);
	this->set_ioctl(SOUND_PCM_WRITE_RATE, 44100);
	this->set_ioctl(SOUND_PCM_WRITE_CHANNELS, 2);
	this->set_ioctl(SNDCTL_DSP_SETFMT, AFMT_S16_LE);
	this->set_ioctl(SNDCTL_DSP_NONBLOCK, 1);
	this->set_ioctl(SNDCTL_DSP_SETFRAGMENT, 0x7fff000c);
	
	this->bytes_played = (int)(jump * 44100.0f) * 4;
	this->eof = false;
	this->in_outbuf = 0;

	{
		struct audio_buf_info abinfo;
		if (ioctl(this->oss_fd, SNDCTL_DSP_GETOSPACE, &abinfo) == -1)
			throw new FatalException("Couldn't call SNDCTL_DSP_GETOSPACE!");
		this->last_fill = abinfo.bytes;
	}
}

OSSAudioDriver::~OSSAudioDriver()
{
}

inline void OSSAudioDriver::set_ioctl(int command, int value)
{
	ioctl(this->oss_fd, command, &value);
}

bool OSSAudioDriver::run()
{
	/* find out how many fragments are free */
	struct audio_buf_info abinfo;
	if (ioctl(this->oss_fd, SNDCTL_DSP_GETOSPACE, &abinfo) == -1)
		throw new FatalException("Couldn't call SNDCTL_DSP_GETOSPACE!");

	/* record how much was actually played */
	this->bytes_played += abinfo.bytes - last_fill;
	last_fill = abinfo.bytes;

	if (this->eof) {
		struct timeval now;
		gettimeofday(&now, NULL);

		float secs =
			(float)(now.tv_sec  - eof_time.tv_sec) +
			(float)(now.tv_usec - eof_time.tv_usec) * 0.000001f;
		this->bytes_played += (int)(secs * 44100.0f * 4.0f);
		memcpy(&this->eof_time, &now, sizeof(struct timeval));
		return true;
	}

	while (abinfo.fragments > 0
	       && abinfo.fragments * abinfo.fragsize >= 8192) {
		/* whopee, we can output :-) */
		int ret = this->prov->fill_buf(this->outbuf + in_outbuf, 65536 - in_outbuf);

		if (ret == 0) {
			/* switch to software timer */
			this->eof = true;
			gettimeofday(&this->eof_time, NULL);
			return true;
		}
		in_outbuf += ret;

		/* write only whole fragments */
		if (in_outbuf >= abinfo.fragsize) {
			int to_write =
			    in_outbuf - (in_outbuf % abinfo.fragsize);

			to_write = write(this->oss_fd, this->outbuf, to_write);
			in_outbuf -= to_write;

			memmove(outbuf, outbuf + to_write, in_outbuf);

			/* avoid another ioctl */
			abinfo.fragments -= to_write / abinfo.fragsize;
			abinfo.bytes -= to_write;

			last_fill = abinfo.bytes;
		}
	}
	return false;
}

float OSSAudioDriver::get_time()
{
        return (float)(this->bytes_played) / 44100.0f / 4.0f;
}