1 
2 
3 /*
4  *  Author: Arvin Schnell
5  */
6 
7 
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <fcntl.h>
12 #include <sys/ioctl.h>
13 #ifdef __NetBSD__
14 #  include <soundcard.h>
15 #else
16 #  include <sys/soundcard.h>
17 #endif
18 #include <math.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <iostream>
22 
23 using std::cerr;
24 
25 #include "pcm-oss.h"
26 
27 
PCMOSS(const char * name,pcm_type_t type,pcm_mode_t mode,pcm_format_t format,int channels,int rate,int buffer_size)28 PCMOSS::PCMOSS (const char* name, pcm_type_t type, pcm_mode_t mode,
29 		pcm_format_t format, int channels, int rate, int buffer_size)
30     : PCM (name, type, mode, format, channels, rate, buffer_size),
31       fd (-1)
32 {
33     if (channels != 1 && channels != 2) {
34 	cerr << "error: Illegal number of channels\n";
35 	return;
36     }
37 
38     if (format != S8 && format != S16) {
39 	cerr << "error: Illegal format (resolution)\n";
40 	return;
41     }
42 
43     int openmode = mode == WRITE ? O_WRONLY : O_RDONLY;
44     fd = open (name, openmode, 0);
45     if (fd == -1) {
46 	cerr << "error: Can't open " << name << ": "
47 	     << strerror (errno) << '\n';
48 	return;
49     }
50 
51     const int want_buffer_size = get_bytes_per_frame () * buffer_size;
52     int s = (int) (floor (log (want_buffer_size) / log (2.0) + 0.5));
53     if ((1L << s) != want_buffer_size) {
54 	cerr << "error: Invalid requested buffer size\n";
55 	return;
56     }
57 
58     int arg = 0x00020000 + s;
59     if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &arg)) {
60 	cerr << "error: Can't set fragment size: "
61 	     << strerror (errno) << '\n';
62 	return;
63     }
64 
65     const int want_format = format == S8 ? AFMT_S8 : AFMT_S16_NE;
66     int tmp_format = want_format;
67     if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp_format) == -1) {
68 	cerr << "error: " << strerror (errno) << '\n';
69 	return;
70     }
71 
72     if (want_format != tmp_format) {
73 	cerr << "error: Can't set requested format (resolution)\n";
74 	return;
75     }
76 
77     const int want_stereo = channels == 1 ? 0 : 1;
78     int tmp_stereo = want_stereo;
79     if (ioctl (fd, SNDCTL_DSP_STEREO, &tmp_stereo) == -1) {
80 	cerr << "error: " << strerror (errno) << '\n';
81 	return;
82     }
83 
84     if (want_stereo != tmp_stereo) {
85 	cerr << "error: Can't set mono/stereo\n";
86 	return;
87     }
88 
89     int tmp_rate = rate;
90     if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp_rate) == -1) {
91 	cerr << "error: " << strerror (errno) << '\n';
92 	return;
93     }
94 
95     if (abs (rate - tmp_rate) > 1) {	// we can tolerate 1 Hz difference
96 	cerr << "error: Can't set requested rate\n"
97 	     << "requested rate: " << rate << " Hz, actual rate: "
98 	     << tmp_rate << " Hz\n";
99 	return;
100     }
101 
102     int tmp_buffer_size = want_buffer_size;
103     if (ioctl (fd, SNDCTL_DSP_SETBLKSIZE, &tmp_buffer_size) == -1 ||
104 	ioctl (fd, SNDCTL_DSP_GETBLKSIZE, &tmp_buffer_size) == -1) {
105 	cerr << "error " << strerror (errno) << '\n';
106 	return;
107     }
108 
109     if (want_buffer_size != tmp_buffer_size) {
110 	cerr << "error: Can't set requested buffer size\n"
111 	     << "requested size: " << want_buffer_size << " bytes, actual size: "
112 	     << tmp_buffer_size << " bytes\n";
113 	return;
114     }
115 
116     ok = true;
117 }
118 
119 
~PCMOSS()120 PCMOSS::~PCMOSS ()
121 {
122     if (fd > 0)
123 	close (fd);
124 }
125 
126 
127 void
info() const128 PCMOSS::info () const
129 {
130     cerr << "driver: oss\n";
131     PCM::info ();
132 }
133 
134 
135 size_t
size()136 PCMOSS::size ()
137 {
138     return (size_t)(-1);
139 }
140 
141 
142 off_t
seek(off_t,int)143 PCMOSS::seek (off_t, int)
144 {
145     return (off_t)(-1);
146 }
147 
148 
149 size_t
read(void * buffer,size_t frames)150 PCMOSS::read (void* buffer, size_t frames)
151 {
152     int i = get_bytes_per_frame () * frames;
153 
154     if (::read (fd, buffer, i) != i) {
155 	cerr << "error: " << strerror (errno) << '\n';
156 	return (size_t)(-1);
157     }
158 
159     return frames;
160 }
161 
162 
163 size_t
write(void * buffer,size_t frames)164 PCMOSS::write (void* buffer, size_t frames)
165 {
166     int i = get_bytes_per_frame () * frames;
167 
168     if (::write (fd, buffer, i) != i) {
169 	cerr << "error: " << strerror (errno) << '\n';
170 	return (size_t)(-1);
171     }
172 
173     return frames;
174 }
175