Fix last quirks
[kugel-rb.git] / firmware / target / hosted / sdl / pcm-sdl.c
blob1772db94f427e97ad2d304924614d6a9fad53776
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Nick Lanham
11 * Copyright (C) 2010 by Thomas Martitz
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include "autoconf.h"
25 #include <stdlib.h>
26 #include <stdbool.h>
27 #include <SDL.h>
28 #include "config.h"
29 #include "debug.h"
30 #include "sound.h"
31 #include "audiohw.h"
32 #include "system.h"
34 #include "pcm.h"
35 #include "pcm_sampr.h"
37 #ifdef DEBUG
38 #include <stdio.h>
39 extern bool debug_audio;
40 #endif
42 static int sim_volume = 0;
44 #if CONFIG_CODEC == SWCODEC
45 static int cvt_status = -1;
47 static Uint8* pcm_data;
48 static size_t pcm_data_size;
49 static size_t pcm_sample_bytes;
50 static size_t pcm_channel_bytes;
52 struct pcm_udata
54 Uint8 *stream;
55 Uint32 num_in;
56 Uint32 num_out;
57 #ifdef DEBUG
58 FILE *debug;
59 #endif
60 } udata;
62 static SDL_AudioSpec obtained;
63 static SDL_AudioCVT cvt;
65 void pcm_play_lock(void)
67 SDL_LockAudio();
70 void pcm_play_unlock(void)
72 SDL_UnlockAudio();
75 static void pcm_dma_apply_settings_nolock(void)
77 cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_sampr,
78 obtained.format, obtained.channels, obtained.freq);
80 if (cvt_status < 0) {
81 cvt.len_ratio = (double)obtained.freq / (double)pcm_sampr;
85 void pcm_dma_apply_settings(void)
87 pcm_play_lock();
88 pcm_dma_apply_settings_nolock();
89 pcm_play_unlock();
92 void pcm_play_dma_start(const void *addr, size_t size)
94 pcm_dma_apply_settings_nolock();
96 pcm_data = (Uint8 *) addr;
97 pcm_data_size = size;
99 SDL_PauseAudio(0);
102 void pcm_play_dma_stop(void)
104 SDL_PauseAudio(1);
105 #ifdef DEBUG
106 if (udata.debug != NULL) {
107 fclose(udata.debug);
108 udata.debug = NULL;
109 DEBUGF("Audio debug file closed\n");
111 #endif
114 void pcm_play_dma_pause(bool pause)
116 if (pause)
117 SDL_PauseAudio(1);
118 else
119 SDL_PauseAudio(0);
122 size_t pcm_get_bytes_waiting(void)
124 return pcm_data_size;
127 void write_to_soundcard(struct pcm_udata *udata)
129 #ifdef DEBUG
130 if (debug_audio && (udata->debug == NULL)) {
131 udata->debug = fopen("audiodebug.raw", "ab");
132 DEBUGF("Audio debug file open\n");
134 #endif
135 if (cvt.needed) {
136 Uint32 rd = udata->num_in;
137 Uint32 wr = (double)rd * cvt.len_ratio;
139 if (wr > udata->num_out) {
140 wr = udata->num_out;
141 rd = (double)wr / cvt.len_ratio;
143 if (rd > udata->num_in)
145 rd = udata->num_in;
146 wr = (double)rd * cvt.len_ratio;
150 if (wr == 0 || rd == 0)
152 udata->num_out = udata->num_in = 0;
153 return;
156 if (cvt_status > 0) {
157 cvt.len = rd * pcm_sample_bytes;
158 cvt.buf = (Uint8 *) malloc(cvt.len * cvt.len_mult);
160 memcpy(cvt.buf, pcm_data, cvt.len);
162 SDL_ConvertAudio(&cvt);
163 SDL_MixAudio(udata->stream, cvt.buf, cvt.len_cvt, sim_volume);
165 udata->num_in = cvt.len / pcm_sample_bytes;
166 udata->num_out = cvt.len_cvt / pcm_sample_bytes;
168 #ifdef DEBUG
169 if (udata->debug != NULL) {
170 fwrite(cvt.buf, sizeof(Uint8), cvt.len_cvt, udata->debug);
172 #endif
173 free(cvt.buf);
175 else {
176 /* Convert is bad, so do silence */
177 Uint32 num = wr*obtained.channels;
178 udata->num_in = rd;
179 udata->num_out = wr;
181 switch (pcm_channel_bytes)
183 case 1:
185 Uint8 *stream = udata->stream;
186 while (num-- > 0)
187 *stream++ = obtained.silence;
188 break;
190 case 2:
192 Uint16 *stream = (Uint16 *)udata->stream;
193 while (num-- > 0)
194 *stream++ = obtained.silence;
195 break;
198 #ifdef DEBUG
199 if (udata->debug != NULL) {
200 fwrite(udata->stream, sizeof(Uint8), wr, udata->debug);
202 #endif
204 } else {
205 udata->num_in = udata->num_out = MIN(udata->num_in, udata->num_out);
206 SDL_MixAudio(udata->stream, pcm_data,
207 udata->num_out * pcm_sample_bytes, sim_volume);
208 #ifdef DEBUG
209 if (udata->debug != NULL) {
210 fwrite(pcm_data, sizeof(Uint8), udata->num_out * pcm_sample_bytes,
211 udata->debug);
213 #endif
217 void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
219 udata->stream = stream;
221 /* Write what we have in the PCM buffer */
222 if (pcm_data_size > 0)
223 goto start;
225 /* Audio card wants more? Get some more then. */
226 while (len > 0) {
227 if ((ssize_t)pcm_data_size <= 0) {
228 pcm_data_size = 0;
230 if (pcm_callback_for_more)
231 pcm_callback_for_more(&pcm_data, &pcm_data_size);
234 if (pcm_data_size > 0) {
235 start:
236 udata->num_in = pcm_data_size / pcm_sample_bytes;
237 udata->num_out = len / pcm_sample_bytes;
239 write_to_soundcard(udata);
241 udata->num_in *= pcm_sample_bytes;
242 udata->num_out *= pcm_sample_bytes;
244 pcm_data += udata->num_in;
245 pcm_data_size -= udata->num_in;
246 udata->stream += udata->num_out;
247 len -= udata->num_out;
248 } else {
249 DEBUGF("sdl_audio_callback: No Data.\n");
250 pcm_play_dma_stop();
251 pcm_play_dma_stopped_callback();
252 break;
257 const void * pcm_play_dma_get_peak_buffer(int *count)
259 uintptr_t addr = (uintptr_t)pcm_data;
260 *count = pcm_data_size / 4;
261 return (void *)((addr + 2) & ~3);
264 #ifdef HAVE_RECORDING
265 void pcm_rec_lock(void)
269 void pcm_rec_unlock(void)
273 void pcm_rec_dma_init(void)
277 void pcm_rec_dma_close(void)
281 void pcm_rec_dma_start(void *start, size_t size)
283 (void)start;
284 (void)size;
287 void pcm_rec_dma_stop(void)
291 void pcm_rec_dma_record_more(void *start, size_t size)
293 (void)start;
294 (void)size;
297 unsigned long pcm_rec_status(void)
299 return 0;
302 const void * pcm_rec_dma_get_peak_buffer(void)
304 return NULL;
307 #endif /* HAVE_RECORDING */
309 void pcm_play_dma_init(void)
311 if (SDL_InitSubSystem(SDL_INIT_AUDIO))
313 DEBUGF("Could not initialize SDL audio subsystem!\n");
314 return;
317 SDL_AudioSpec wanted_spec;
318 #ifdef DEBUG
319 udata.debug = NULL;
320 if (debug_audio) {
321 udata.debug = fopen("audiodebug.raw", "wb");
322 DEBUGF("Audio debug file open\n");
324 #endif
325 /* Set 16-bit stereo audio at 44Khz */
326 wanted_spec.freq = 44100;
327 wanted_spec.format = AUDIO_S16SYS;
328 wanted_spec.channels = 2;
329 wanted_spec.samples = 2048;
330 wanted_spec.callback =
331 (void (SDLCALL *)(void *userdata,
332 Uint8 *stream, int len))sdl_audio_callback;
333 wanted_spec.userdata = &udata;
335 /* Open the audio device and start playing sound! */
336 if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) {
337 DEBUGF("Unable to open audio: %s\n", SDL_GetError());
338 return;
341 switch (obtained.format)
343 case AUDIO_U8:
344 case AUDIO_S8:
345 pcm_channel_bytes = 1;
346 break;
347 case AUDIO_U16LSB:
348 case AUDIO_S16LSB:
349 case AUDIO_U16MSB:
350 case AUDIO_S16MSB:
351 pcm_channel_bytes = 2;
352 break;
353 default:
354 DEBUGF("Unknown sample format obtained: %u\n",
355 (unsigned)obtained.format);
356 return;
359 pcm_sample_bytes = obtained.channels * pcm_channel_bytes;
361 pcm_dma_apply_settings_nolock();
364 void pcm_postinit(void)
368 void pcm_set_mixer_volume(int volume)
370 sim_volume = volume;
373 #endif /* CONFIG_CODEC == SWCODEC */