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