1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file pcm/pcm_simple.c
3d5ac70f0Sopenharmony_ci * \ingroup PCM_Simple
4d5ac70f0Sopenharmony_ci * \brief PCM Simple Interface
5d5ac70f0Sopenharmony_ci * \author Jaroslav Kysela <perex@perex.cz>
6d5ac70f0Sopenharmony_ci * \date 2004
7d5ac70f0Sopenharmony_ci */
8d5ac70f0Sopenharmony_ci/*
9d5ac70f0Sopenharmony_ci *
10d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
11d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
12d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
13d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
14d5ac70f0Sopenharmony_ci *
15d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
16d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
19d5ac70f0Sopenharmony_ci *
20d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
21d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
22d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23d5ac70f0Sopenharmony_ci *
24d5ac70f0Sopenharmony_ci */
25d5ac70f0Sopenharmony_ci
26d5ac70f0Sopenharmony_ci#include "pcm_local.h"
27d5ac70f0Sopenharmony_ci
28d5ac70f0Sopenharmony_cistatic int set_buffer_time(snd_spcm_latency_t latency,
29d5ac70f0Sopenharmony_ci			   unsigned int *buffer_time)
30d5ac70f0Sopenharmony_ci{
31d5ac70f0Sopenharmony_ci	switch (latency) {
32d5ac70f0Sopenharmony_ci	case SND_SPCM_LATENCY_STANDARD:
33d5ac70f0Sopenharmony_ci		*buffer_time = 350000;
34d5ac70f0Sopenharmony_ci		break;
35d5ac70f0Sopenharmony_ci	case SND_SPCM_LATENCY_MEDIUM:
36d5ac70f0Sopenharmony_ci		*buffer_time = 25000;
37d5ac70f0Sopenharmony_ci		break;
38d5ac70f0Sopenharmony_ci	case SND_SPCM_LATENCY_REALTIME:
39d5ac70f0Sopenharmony_ci		*buffer_time = 2500;
40d5ac70f0Sopenharmony_ci		break;
41d5ac70f0Sopenharmony_ci	default:
42d5ac70f0Sopenharmony_ci		return -EINVAL;
43d5ac70f0Sopenharmony_ci	}
44d5ac70f0Sopenharmony_ci	return 0;
45d5ac70f0Sopenharmony_ci}
46d5ac70f0Sopenharmony_ci
47d5ac70f0Sopenharmony_cistatic int set_hw_params(snd_pcm_t *pcm,
48d5ac70f0Sopenharmony_ci			 snd_pcm_hw_params_t *hw_params,
49d5ac70f0Sopenharmony_ci			 unsigned int *rate,
50d5ac70f0Sopenharmony_ci			 unsigned int channels,
51d5ac70f0Sopenharmony_ci			 snd_pcm_format_t format,
52d5ac70f0Sopenharmony_ci			 snd_pcm_subformat_t subformat,
53d5ac70f0Sopenharmony_ci			 unsigned int *buffer_time,
54d5ac70f0Sopenharmony_ci			 unsigned int *period_time,
55d5ac70f0Sopenharmony_ci			 snd_pcm_access_t access)
56d5ac70f0Sopenharmony_ci{
57d5ac70f0Sopenharmony_ci	int err;
58d5ac70f0Sopenharmony_ci
59d5ac70f0Sopenharmony_ci	/*
60d5ac70f0Sopenharmony_ci	 * hardware parameters
61d5ac70f0Sopenharmony_ci	 */
62d5ac70f0Sopenharmony_ci	err = snd_pcm_hw_params_any(pcm, hw_params);
63d5ac70f0Sopenharmony_ci	if (err < 0)
64d5ac70f0Sopenharmony_ci		return err;
65d5ac70f0Sopenharmony_ci	err = snd_pcm_hw_params_set_access(pcm, hw_params, access);
66d5ac70f0Sopenharmony_ci	if (err < 0)
67d5ac70f0Sopenharmony_ci		return err;
68d5ac70f0Sopenharmony_ci	err = snd_pcm_hw_params_set_format(pcm, hw_params, format);
69d5ac70f0Sopenharmony_ci	if (err < 0)
70d5ac70f0Sopenharmony_ci		return err;
71d5ac70f0Sopenharmony_ci	if (subformat != SND_PCM_SUBFORMAT_STD) {
72d5ac70f0Sopenharmony_ci		err = snd_pcm_hw_params_set_subformat(pcm, hw_params, subformat);
73d5ac70f0Sopenharmony_ci		if (err < 0)
74d5ac70f0Sopenharmony_ci			return err;
75d5ac70f0Sopenharmony_ci	}
76d5ac70f0Sopenharmony_ci	err = snd_pcm_hw_params_set_channels(pcm, hw_params, channels);
77d5ac70f0Sopenharmony_ci	if (err < 0)
78d5ac70f0Sopenharmony_ci		return err;
79d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, 0);
80d5ac70f0Sopenharmony_ci	if (err < 0)
81d5ac70f0Sopenharmony_ci		return err;
82d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(pcm, hw_params, buffer_time, NULL);
83d5ac70f0Sopenharmony_ci	if (err < 0)
84d5ac70f0Sopenharmony_ci		return err;
85d5ac70f0Sopenharmony_ci	if (period_time == NULL || *period_time == 0) {
86d5ac70f0Sopenharmony_ci		unsigned int periods = 3;
87d5ac70f0Sopenharmony_ci		err = INTERNAL(snd_pcm_hw_params_set_periods_near)(pcm, hw_params, &periods, NULL);
88d5ac70f0Sopenharmony_ci		if (err < 0)
89d5ac70f0Sopenharmony_ci			return err;
90d5ac70f0Sopenharmony_ci		if (periods == 1)
91d5ac70f0Sopenharmony_ci			return -EINVAL;
92d5ac70f0Sopenharmony_ci		if (period_time) {
93d5ac70f0Sopenharmony_ci			err = INTERNAL(snd_pcm_hw_params_get_period_time)(hw_params, period_time, NULL);
94d5ac70f0Sopenharmony_ci			if (err < 0)
95d5ac70f0Sopenharmony_ci				return err;
96d5ac70f0Sopenharmony_ci		}
97d5ac70f0Sopenharmony_ci	} else {
98d5ac70f0Sopenharmony_ci		err = snd_pcm_hw_params_set_period_time(pcm, hw_params, *period_time, 0);
99d5ac70f0Sopenharmony_ci		if (err < 0)
100d5ac70f0Sopenharmony_ci			return err;
101d5ac70f0Sopenharmony_ci		if (*buffer_time == *period_time)
102d5ac70f0Sopenharmony_ci			return -EINVAL;
103d5ac70f0Sopenharmony_ci	}
104d5ac70f0Sopenharmony_ci	err = snd_pcm_hw_params(pcm, hw_params);
105d5ac70f0Sopenharmony_ci	if (err < 0)
106d5ac70f0Sopenharmony_ci		return err;
107d5ac70f0Sopenharmony_ci	return 0;
108d5ac70f0Sopenharmony_ci}
109d5ac70f0Sopenharmony_ci
110d5ac70f0Sopenharmony_cistatic int set_sw_params(snd_pcm_t *pcm,
111d5ac70f0Sopenharmony_ci			 snd_pcm_sw_params_t *sw_params,
112d5ac70f0Sopenharmony_ci		         snd_spcm_xrun_type_t xrun_type)
113d5ac70f0Sopenharmony_ci{
114d5ac70f0Sopenharmony_ci	int err;
115d5ac70f0Sopenharmony_ci
116d5ac70f0Sopenharmony_ci	err = snd_pcm_sw_params_current(pcm, sw_params);
117d5ac70f0Sopenharmony_ci	if (err < 0)
118d5ac70f0Sopenharmony_ci		return err;
119d5ac70f0Sopenharmony_ci	err = snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (pcm->buffer_size / pcm->period_size) * pcm->period_size);
120d5ac70f0Sopenharmony_ci	if (err < 0)
121d5ac70f0Sopenharmony_ci		return err;
122d5ac70f0Sopenharmony_ci	err = snd_pcm_sw_params_set_avail_min(pcm, sw_params, pcm->period_size);
123d5ac70f0Sopenharmony_ci	if (err < 0)
124d5ac70f0Sopenharmony_ci		return err;
125d5ac70f0Sopenharmony_ci	switch (xrun_type) {
126d5ac70f0Sopenharmony_ci	case SND_SPCM_XRUN_STOP:
127d5ac70f0Sopenharmony_ci		err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->buffer_size);
128d5ac70f0Sopenharmony_ci		break;
129d5ac70f0Sopenharmony_ci	case SND_SPCM_XRUN_IGNORE:
130d5ac70f0Sopenharmony_ci		err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->boundary);
131d5ac70f0Sopenharmony_ci		break;
132d5ac70f0Sopenharmony_ci	default:
133d5ac70f0Sopenharmony_ci		return -EINVAL;
134d5ac70f0Sopenharmony_ci	}
135d5ac70f0Sopenharmony_ci	if (err < 0)
136d5ac70f0Sopenharmony_ci		return err;
137d5ac70f0Sopenharmony_ci	err = snd_pcm_sw_params(pcm, sw_params);
138d5ac70f0Sopenharmony_ci	if (err < 0)
139d5ac70f0Sopenharmony_ci		return err;
140d5ac70f0Sopenharmony_ci	return 0;
141d5ac70f0Sopenharmony_ci}
142d5ac70f0Sopenharmony_ci
143d5ac70f0Sopenharmony_ci/**
144d5ac70f0Sopenharmony_ci * \brief Set up a simple PCM
145d5ac70f0Sopenharmony_ci * \param pcm PCM handle
146d5ac70f0Sopenharmony_ci * \param rate Sample rate
147d5ac70f0Sopenharmony_ci * \param channels Number of channels
148d5ac70f0Sopenharmony_ci * \param format PCM format
149d5ac70f0Sopenharmony_ci * \param subformat PCM subformat
150d5ac70f0Sopenharmony_ci * \param latency Latency type
151d5ac70f0Sopenharmony_ci * \param access PCM acceess type
152d5ac70f0Sopenharmony_ci * \param xrun_type XRUN type
153d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
154d5ac70f0Sopenharmony_ci *
155d5ac70f0Sopenharmony_ci * \warning The simple PCM API may be broken in the current release.
156d5ac70f0Sopenharmony_ci */
157d5ac70f0Sopenharmony_ciint snd_spcm_init(snd_pcm_t *pcm,
158d5ac70f0Sopenharmony_ci		  unsigned int rate,
159d5ac70f0Sopenharmony_ci		  unsigned int channels,
160d5ac70f0Sopenharmony_ci		  snd_pcm_format_t format,
161d5ac70f0Sopenharmony_ci		  snd_pcm_subformat_t subformat,
162d5ac70f0Sopenharmony_ci		  snd_spcm_latency_t latency,
163d5ac70f0Sopenharmony_ci		  snd_pcm_access_t access,
164d5ac70f0Sopenharmony_ci		  snd_spcm_xrun_type_t xrun_type)
165d5ac70f0Sopenharmony_ci{
166d5ac70f0Sopenharmony_ci	int err;
167d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_t hw_params = {0};
168d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_t sw_params = {0};
169d5ac70f0Sopenharmony_ci	unsigned int rrate;
170d5ac70f0Sopenharmony_ci	unsigned int buffer_time;
171d5ac70f0Sopenharmony_ci
172d5ac70f0Sopenharmony_ci	assert(pcm);
173d5ac70f0Sopenharmony_ci	assert(rate >= 5000 && rate <= 786000);
174d5ac70f0Sopenharmony_ci	assert(channels >= 1 && channels <= 512);
175d5ac70f0Sopenharmony_ci
176d5ac70f0Sopenharmony_ci	rrate = rate;
177d5ac70f0Sopenharmony_ci	err = set_buffer_time(latency, &buffer_time);
178d5ac70f0Sopenharmony_ci	if (err < 0)
179d5ac70f0Sopenharmony_ci		return err;
180d5ac70f0Sopenharmony_ci	err = set_hw_params(pcm, &hw_params,
181d5ac70f0Sopenharmony_ci			    &rrate, channels, format, subformat,
182d5ac70f0Sopenharmony_ci			    &buffer_time, NULL, access);
183d5ac70f0Sopenharmony_ci	if (err < 0)
184d5ac70f0Sopenharmony_ci		return err;
185d5ac70f0Sopenharmony_ci
186d5ac70f0Sopenharmony_ci	err = set_sw_params(pcm, &sw_params, xrun_type);
187d5ac70f0Sopenharmony_ci	if (err < 0)
188d5ac70f0Sopenharmony_ci		return err;
189d5ac70f0Sopenharmony_ci
190d5ac70f0Sopenharmony_ci	return 0;
191d5ac70f0Sopenharmony_ci}
192d5ac70f0Sopenharmony_ci
193d5ac70f0Sopenharmony_ci/**
194d5ac70f0Sopenharmony_ci * \brief Initialize simple PCMs in the duplex mode
195d5ac70f0Sopenharmony_ci * \param playback_pcm PCM handle for playback
196d5ac70f0Sopenharmony_ci * \param capture_pcm PCM handle for capture
197d5ac70f0Sopenharmony_ci * \param rate Sample rate
198d5ac70f0Sopenharmony_ci * \param channels Number of channels
199d5ac70f0Sopenharmony_ci * \param format PCM format
200d5ac70f0Sopenharmony_ci * \param subformat PCM subformat
201d5ac70f0Sopenharmony_ci * \param latency Latency type
202d5ac70f0Sopenharmony_ci * \param access PCM acceess type
203d5ac70f0Sopenharmony_ci * \param xrun_type XRUN type
204d5ac70f0Sopenharmony_ci * \param duplex_type Duplex mode
205d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
206d5ac70f0Sopenharmony_ci *
207d5ac70f0Sopenharmony_ci * \warning The simple PCM API may be broken in the current release.
208d5ac70f0Sopenharmony_ci */
209d5ac70f0Sopenharmony_ciint snd_spcm_init_duplex(snd_pcm_t *playback_pcm,
210d5ac70f0Sopenharmony_ci			 snd_pcm_t *capture_pcm,
211d5ac70f0Sopenharmony_ci			 unsigned int rate,
212d5ac70f0Sopenharmony_ci			 unsigned int channels,
213d5ac70f0Sopenharmony_ci			 snd_pcm_format_t format,
214d5ac70f0Sopenharmony_ci			 snd_pcm_subformat_t subformat,
215d5ac70f0Sopenharmony_ci			 snd_spcm_latency_t latency,
216d5ac70f0Sopenharmony_ci			 snd_pcm_access_t access,
217d5ac70f0Sopenharmony_ci			 snd_spcm_xrun_type_t xrun_type,
218d5ac70f0Sopenharmony_ci			 snd_spcm_duplex_type_t duplex_type)
219d5ac70f0Sopenharmony_ci{
220d5ac70f0Sopenharmony_ci	int err, i;
221d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_t hw_params = {0};
222d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_t sw_params = {0};
223d5ac70f0Sopenharmony_ci	unsigned int rrate;
224d5ac70f0Sopenharmony_ci	unsigned int xbuffer_time, buffer_time[2];
225d5ac70f0Sopenharmony_ci	unsigned int period_time[2];
226d5ac70f0Sopenharmony_ci	snd_pcm_t *pcms[2];
227d5ac70f0Sopenharmony_ci
228d5ac70f0Sopenharmony_ci	assert(playback_pcm);
229d5ac70f0Sopenharmony_ci	assert(capture_pcm);
230d5ac70f0Sopenharmony_ci	assert(rate >= 5000 && rate <= 768000);
231d5ac70f0Sopenharmony_ci	assert(channels >= 1 && channels <= 512);
232d5ac70f0Sopenharmony_ci
233d5ac70f0Sopenharmony_ci	pcms[0] = playback_pcm;
234d5ac70f0Sopenharmony_ci	pcms[1] = capture_pcm;
235d5ac70f0Sopenharmony_ci
236d5ac70f0Sopenharmony_ci	/*
237d5ac70f0Sopenharmony_ci	 * hardware parameters
238d5ac70f0Sopenharmony_ci	 */
239d5ac70f0Sopenharmony_ci	err = set_buffer_time(latency, &xbuffer_time);
240d5ac70f0Sopenharmony_ci	if (err < 0)
241d5ac70f0Sopenharmony_ci		return err;
242d5ac70f0Sopenharmony_ci
243d5ac70f0Sopenharmony_ci	for (i = 0; i < 2; i++) {
244d5ac70f0Sopenharmony_ci		buffer_time[i] = xbuffer_time;
245d5ac70f0Sopenharmony_ci		period_time[i] = i > 0 ? period_time[0] : 0;
246d5ac70f0Sopenharmony_ci		rrate = rate;
247d5ac70f0Sopenharmony_ci		err = set_hw_params(pcms[i], &hw_params,
248d5ac70f0Sopenharmony_ci				    &rrate, channels, format, subformat,
249d5ac70f0Sopenharmony_ci				    &buffer_time[i], &period_time[i], access);
250d5ac70f0Sopenharmony_ci		if (err < 0)
251d5ac70f0Sopenharmony_ci			return err;
252d5ac70f0Sopenharmony_ci	}
253d5ac70f0Sopenharmony_ci	if (buffer_time[0] == buffer_time[1] &&
254d5ac70f0Sopenharmony_ci	    period_time[0] == period_time[1])
255d5ac70f0Sopenharmony_ci		goto __sw_params;
256d5ac70f0Sopenharmony_ci	if (duplex_type == SND_SPCM_DUPLEX_LIBERAL)
257d5ac70f0Sopenharmony_ci		goto __sw_params;
258d5ac70f0Sopenharmony_ci	/* FIXME: */
259d5ac70f0Sopenharmony_ci	return -EINVAL;
260d5ac70f0Sopenharmony_ci
261d5ac70f0Sopenharmony_ci	/*
262d5ac70f0Sopenharmony_ci	 * software parameters
263d5ac70f0Sopenharmony_ci	 */
264d5ac70f0Sopenharmony_ci      __sw_params:
265d5ac70f0Sopenharmony_ci	for (i = 0; i < 2; i++) {
266d5ac70f0Sopenharmony_ci		err = set_sw_params(pcms[i], &sw_params, xrun_type);
267d5ac70f0Sopenharmony_ci		if (err < 0)
268d5ac70f0Sopenharmony_ci			return err;
269d5ac70f0Sopenharmony_ci	}
270d5ac70f0Sopenharmony_ci
271d5ac70f0Sopenharmony_ci	return 0;
272d5ac70f0Sopenharmony_ci}
273d5ac70f0Sopenharmony_ci
274d5ac70f0Sopenharmony_ci/**
275d5ac70f0Sopenharmony_ci * \brief Get the set up of simple PCM
276d5ac70f0Sopenharmony_ci * \param pcm PCM handle
277d5ac70f0Sopenharmony_ci * \param rate Pointer to store the current sample rate
278d5ac70f0Sopenharmony_ci * \param buffer_size Pointer to store the current buffer size
279d5ac70f0Sopenharmony_ci * \param period_size Pointer to store the current period size
280d5ac70f0Sopenharmony_ci * \return 0 if successful, or a negative error code
281d5ac70f0Sopenharmony_ci *
282d5ac70f0Sopenharmony_ci * \warning The simple PCM API may be broken in the current release.
283d5ac70f0Sopenharmony_ci */
284d5ac70f0Sopenharmony_ciint snd_spcm_init_get_params(snd_pcm_t *pcm,
285d5ac70f0Sopenharmony_ci			     unsigned int *rate,
286d5ac70f0Sopenharmony_ci			     snd_pcm_uframes_t *buffer_size,
287d5ac70f0Sopenharmony_ci			     snd_pcm_uframes_t *period_size)
288d5ac70f0Sopenharmony_ci{
289d5ac70f0Sopenharmony_ci	assert(pcm);
290d5ac70f0Sopenharmony_ci	if (!pcm->setup)
291d5ac70f0Sopenharmony_ci		return -EBADFD;
292d5ac70f0Sopenharmony_ci	if (rate)
293d5ac70f0Sopenharmony_ci		*rate = pcm->rate;
294d5ac70f0Sopenharmony_ci	if (buffer_size)
295d5ac70f0Sopenharmony_ci		*buffer_size = pcm->buffer_size;
296d5ac70f0Sopenharmony_ci	if (period_size)
297d5ac70f0Sopenharmony_ci		*period_size = pcm->period_size;
298d5ac70f0Sopenharmony_ci	return 0;
299d5ac70f0Sopenharmony_ci}
300