1 
2 
3 /*
4  *  Author: Arvin Schnell
5  */
6 
7 
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <iostream>
14 
15 using std::cerr;
16 
17 #include "pcm-alsa.h"
18 
19 
PCMALSA(const char * name,pcm_type_t type,pcm_mode_t mode,pcm_format_t format,int channels,int rate,int buffer_size)20 PCMALSA::PCMALSA (const char* name, pcm_type_t type, pcm_mode_t mode,
21 		  pcm_format_t format, int channels, int rate, int buffer_size)
22     : PCM (name, type, mode, format, channels, rate, buffer_size),
23       handle (0)
24 {
25     if (channels != 1 && channels != 2) {
26 	cerr << "error: Illegal number of channels\n";
27 	return;
28     }
29 
30     int ret;
31 
32     snd_pcm_stream_t stream = mode == WRITE ? SND_PCM_STREAM_PLAYBACK :
33 	SND_PCM_STREAM_CAPTURE;
34     ret = snd_pcm_open (&handle, name, stream, SND_PCM_NONBLOCK);
35     if (ret < 0) {
36 	cerr << "error: Can't open PCM device " << name << ": "
37 	     << snd_strerror (ret) << '\n';
38 	handle = 0;
39 	return;
40     }
41 
42     ret = snd_pcm_nonblock (handle, 0);
43     if (ret < 0) {
44 	cerr << "error: Can't set blocking mode: " << snd_strerror (ret) << '\n';
45 	return;
46     }
47 
48     snd_pcm_hw_params_t* hwparams;
49     snd_pcm_hw_params_alloca (&hwparams);
50 
51     ret = snd_pcm_hw_params_any (handle, hwparams);
52     if (ret < 0) {
53 	cerr << "error: Can't configure the PCM device: "
54 	     << snd_strerror (ret) << '\n';
55 	return;
56     }
57 
58     ret = snd_pcm_hw_params_set_access (handle, hwparams,
59 					SND_PCM_ACCESS_RW_INTERLEAVED);
60     if (ret < 0) {
61 	cerr << "error: Can't set access: " << snd_strerror (ret) << '\n';
62 	return;
63     }
64 
65     snd_pcm_format_t format_array[3] = { SND_PCM_FORMAT_S8, SND_PCM_FORMAT_S16,
66 					 SND_PCM_FORMAT_S32 };
67     ret = snd_pcm_hw_params_set_format (handle, hwparams, format_array[format]);
68     if (ret < 0) {
69 	cerr << "error: Can't set format (resolution): "
70 	     << snd_strerror (ret) << '\n';
71 	return;
72     }
73 
74     ret = snd_pcm_hw_params_set_rate_near (handle, hwparams, rate, 0);
75     if (ret < 0) {
76 	cerr << "error: Can't set rate: " << snd_strerror (ret) << '\n';
77 	return;
78     }
79 
80     if (abs (rate - ret) > 1) {  // we can tolerate 1 Hz difference
81 	cerr << "error: Can't set requested rate: " << snd_strerror (ret) << '\n'
82 	     << "requested rate: " << rate << " Hz, actual rate: "
83 	     << ret << " Hz\n";
84 	return;
85     }
86 
87     ret = snd_pcm_hw_params_set_channels (handle, hwparams, channels);
88     if (ret < 0) {
89 	cerr << "error: Can't set number of channels: " << snd_strerror (ret)
90 	     << '\n';
91 	return;
92     }
93 
94     ret = snd_pcm_hw_params_set_periods (handle, hwparams, 2, 0);
95     if (ret < 0) {
96 	cerr << "error: Can't set number of periods: " << snd_strerror (ret)
97 	     << '\n';
98 	return;
99     }
100 
101     ret = snd_pcm_hw_params_set_buffer_size (handle, hwparams, buffer_size);
102     if (ret < 0) {
103 	cerr << "error: Can't set buffer size: " << snd_strerror (ret) << '\n';
104 	return;
105     }
106 
107     ret = snd_pcm_hw_params (handle, hwparams);
108     if (ret < 0) {
109 	cerr << "error: Can't set HW params: " << snd_strerror (ret) << '\n';
110 	return;
111     }
112 
113     ok = true;
114 }
115 
116 
~PCMALSA()117 PCMALSA::~PCMALSA ()
118 {
119     if (handle)
120 	snd_pcm_close (handle);
121 }
122 
123 
124 void
info() const125 PCMALSA::info () const
126 {
127     cerr << "driver: alsa 0.9\n";
128     PCM::info ();
129 }
130 
131 
132 size_t
size()133 PCMALSA::size ()
134 {
135     return (size_t)(-1);
136 }
137 
138 
139 off_t
seek(off_t,int)140 PCMALSA::seek (off_t, int)
141 {
142     return (off_t)(-1);
143 }
144 
145 
146 size_t
read(void * buffer,size_t frames)147 PCMALSA::read (void* buffer, size_t frames)
148 {
149     int ret = snd_pcm_readi (handle, buffer, frames);
150 
151     if (ret < 0) {
152 	if (ret == -EPIPE) {
153 	    cerr << "warning: Buffer overrun: "
154 		 << snd_strerror (ret) << '\n';;
155 	    snd_pcm_prepare (handle);
156 	} else {
157 	    cerr << "error: " << snd_strerror (ret) << '\n';
158 	    return (size_t)(-1);
159 	}
160     }
161 
162     return frames;
163 }
164 
165 
166 size_t
write(void * buffer,size_t frames)167 PCMALSA::write (void* buffer, size_t frames)
168 {
169     int ret = snd_pcm_writei (handle, buffer, frames);
170 
171     if (ret < 0) {
172 	if (ret == -EPIPE) {
173 	    cerr << "warning: Buffer underrun: "
174 		 << snd_strerror (ret) << '\n';
175 	    snd_pcm_prepare (handle);
176 	} else {
177 	    cerr << "error: " << snd_strerror (ret) << '\n';
178 	    return (size_t)(-1);
179 	}
180     }
181 
182     return frames;
183 }
184