2 terminatorX - realtime audio scratching software
3 Copyright (C) 1999-2003 Alexander König
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 File: tX_aduiodevice.cc
21 Description: Implements Audiodevice handling...
24 #define ALSA_PCM_NEW_HW_PARAMS_API
26 #include "tX_audiodevice.h"
29 #include <sys/types.h>
31 #include <sys/ioctl.h>
33 #include <sys/soundcard.h>
36 #include "tX_endian.h"
39 # define __USE_XOPEN // we need this for swab()
49 #include "tX_engine.h"
51 tX_audiodevice :: tX_audiodevice() : samples_per_buffer(0),
52 current_buffer(0), buffer_pos(0)
54 sample_buffer[0]=NULL;
55 sample_buffer[1]=NULL;
56 engine=tX_engine::get_instance();
59 void tX_audiodevice :: start() {
60 sample_buffer[0]=new int16_t[samples_per_buffer*2];
61 sample_buffer[1]=new int16_t[samples_per_buffer*2];
63 vtt_buffer_size=vtt_class::get_mix_buffer_size()<<1;
67 while (!engine->is_stopped()) {
68 current=current ? 0 : 1;
70 int16_t *current_buffer=sample_buffer[current];
71 int16_t *next_buffer=sample_buffer[current ? 0 : 1];
73 fill_buffer(current_buffer, next_buffer);
77 delete [] sample_buffer[0];
78 delete [] sample_buffer[1];
81 void tX_audiodevice :: fill_buffer(int16_t *target_buffer, int16_t *next_target_buffer) {
84 while (buffer_pos <= samples_per_buffer) {
85 int16_t *data=engine->render_cycle();
87 int rest=(buffer_pos+vtt_buffer_size)-samples_per_buffer;
90 memcpy(&target_buffer[buffer_pos], data, vtt_buffer_size << 1);
92 memcpy(&target_buffer[buffer_pos], data, (vtt_buffer_size-rest) << 1);
93 memcpy(next_target_buffer, &data[vtt_buffer_size-rest], rest << 1);
97 buffer_pos+=vtt_buffer_size;
103 /* Driver Specific Code follows.. */
107 int tX_audiodevice_oss :: open()
114 fd=::open(globals.oss_device, O_WRONLY, 0);
116 /* setting buffer size */
117 buff_cfg=(globals.oss_buff_no<<16) | globals.oss_buff_size;
120 tX_debug("tX_adudiodevice_oss::open() - buff_no: %i, buff_size: %i, buff_cfg: %08x", globals.oss_buff_no, globals.oss_buff_size, buff_cfg);
122 i = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &p);
123 ioctl(fd, SNDCTL_DSP_RESET, 0);
128 i += ioctl(fd, SOUND_PCM_WRITE_BITS, &p);
133 i += ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &p);
137 p = globals.oss_samplerate;
138 i += ioctl(fd, SOUND_PCM_WRITE_RATE, &p);
140 sample_rate=globals.oss_samplerate;
142 /* Figure actual blocksize.. */
144 i += ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &blocksize);
146 samples_per_buffer=blocksize/sizeof(int16_t);
148 tX_debug("tX_adudiodevice_oss::open() - blocksize: %i, samples_per_buffer: %i", blocksize, samples_per_buffer);
150 ioctl(fd, SNDCTL_DSP_SYNC, 0);
155 int tX_audiodevice_oss :: close()
168 tX_audiodevice_oss :: tX_audiodevice_oss() : tX_audiodevice(),
169 fd(0), blocksize(0) {}
171 double tX_audiodevice_oss :: get_latency()
176 void tX_audiodevice_oss :: play(int16_t *buffer)
178 #ifdef BIG_ENDIAN_MACHINE
179 swapbuffer (buffer, samples_per_buffer);
181 int res=write(fd, buffer, blocksize);
183 tX_error("failed to write to audiodevice: %s", strerror(errno));
192 int tX_audiodevice_alsa :: open()
194 snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
195 snd_pcm_hw_params_t *hw_params;
203 sscanf(globals.alsa_device, "%i-%i: %s", &card, &device, foo);
204 sprintf(pcm_name, "hw:%i,%i", card, device);
206 if (snd_pcm_open(&pcm_handle, pcm_name, stream, SND_PCM_NONBLOCK) < 0) {
207 tX_error("ALSA: Failed to access PCM device \"%s\"", pcm_name);
211 snd_pcm_hw_params_alloca(&hw_params);
213 if (snd_pcm_hw_params_any(pcm_handle, hw_params) < 0) {
214 tX_error("ALSA: Failed to configure PCM device \"%s\"", pcm_name);
215 snd_pcm_hw_params_free (hw_params);
219 /* Setting INTERLEAVED stereo... */
220 #ifdef USE_ALSA_MEMCPY
221 if (snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
223 if (snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
225 tX_error("ALSA: Failed to set interleaved access for PCM device \"%s\"", pcm_name);
226 snd_pcm_hw_params_free (hw_params);
230 /* Make it 16 Bit LE - we handle converting from BE anyway... */
231 if (snd_pcm_hw_params_set_format(pcm_handle, hw_params, SND_PCM_FORMAT_S16_LE) < 0) {
232 tX_error("ALSA: Error setting 16 Bit sample width for PCM device \"%s\"", pcm_name);
233 snd_pcm_hw_params_free (hw_params);
237 /* Setting sampling rate */
238 unsigned int hw_rate=(unsigned int)globals.alsa_samplerate;
241 int res = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &hw_rate, &dir);
244 tX_warning("ALSA: The PCM device \"%s\" doesnt support 44100 Hz playback - using %i instead", pcm_name, hw_rate);
249 /* Using stereo output */
250 if (snd_pcm_hw_params_set_channels(pcm_handle, hw_params, 2) < 0) {
251 tX_error("ALSA: PCM device \"%s\" does not support stereo operation", pcm_name);
252 snd_pcm_hw_params_free (hw_params);
256 unsigned int buffer_time=globals.alsa_buffer_time;
257 unsigned int period_time=globals.alsa_period_time;
259 if (snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hw_params, &buffer_time, &dir) < 0) {
260 tX_error("ALSA: failed to set the buffer time opf %lu usecs", globals.alsa_buffer_time);
264 long unsigned int buffer_size;
266 if (snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size) < 0) {
267 tX_error("ALSA: failed to retreive buffer size");
271 tX_debug("ALSA: buffer size is %i", buffer_size);
273 if (snd_pcm_hw_params_set_period_time_near(pcm_handle, hw_params, &period_time, &dir) < 0) {
274 tX_error("ALSA: failed to set period time %i", globals.alsa_period_time);
278 if (snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir)<0) {
279 tX_error("ALSA: failed to retreive period size");
283 samples_per_buffer=period_size;
285 /* Apply all that setup work.. */
286 if (snd_pcm_hw_params(pcm_handle, hw_params) < 0) {
287 tX_error("ALSA: Failed to apply settings to PCM device \"%s\"", pcm_name);
288 snd_pcm_hw_params_free (hw_params);
292 snd_pcm_hw_params_free (hw_params);
293 return snd_pcm_prepare(pcm_handle);
296 int tX_audiodevice_alsa :: close()
298 snd_pcm_close(pcm_handle);
303 double tX_audiodevice_alsa :: get_latency()
309 tX_audiodevice_alsa :: tX_audiodevice_alsa() : tX_audiodevice(),
312 void tX_audiodevice_alsa :: play(int16_t *buffer)
314 snd_pcm_sframes_t pcmreturn;
315 #ifdef BIG_ENDIAN_MACHINE
316 swapbuffer (buffer, samples_per_buffer);
319 #ifdef USE_ALSA_MEMCPY
320 pcmreturn = snd_pcm_writei(pcm_handle, buffer, samples_per_buffer >> 1);
322 pcmreturn = snd_pcm_mmap_writei(pcm_handle, buffer, samples_per_buffer >> 1);
325 while (pcmreturn==-EPIPE) {
326 snd_pcm_prepare(pcm_handle);
328 #ifdef USE_ALSA_MEMCPY
329 pcmreturn = snd_pcm_writei(pcm_handle, buffer, samples_per_buffer >> 1);
331 pcmreturn = snd_pcm_mmap_writei(pcm_handle, buffer, samples_per_buffer >> 1);
333 //tX_warning("ALSA: ** buffer underrun **");
337 printf("snd_pcm_writei says: %s.\n", strerror(-1*pcmreturn));