Move the PCM/audio playback part of SDL into the target tree.
[kugel-rb.git] / firmware / target / sdl / pcm-sdl.c
blob9c2b40cb09c90a2131091b907591445a28672d46
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 extern int sim_volume; /* in firmware/sound.c */
128 void write_to_soundcard(struct pcm_udata *udata)
130 #ifdef DEBUG
131 if (debug_audio && (udata->debug == NULL)) {
132 udata->debug = fopen("audiodebug.raw", "ab");
133 DEBUGF("Audio debug file open\n");
135 #endif
136 if (cvt.needed) {
137 Uint32 rd = udata->num_in;
138 Uint32 wr = (double)rd * cvt.len_ratio;
140 if (wr > udata->num_out) {
141 wr = udata->num_out;
142 rd = (double)wr / cvt.len_ratio;
144 if (rd > udata->num_in)
146 rd = udata->num_in;
147 wr = (double)rd * cvt.len_ratio;
151 if (wr == 0 || rd == 0)
153 udata->num_out = udata->num_in = 0;
154 return;
157 if (cvt_status > 0) {
158 cvt.len = rd * pcm_sample_bytes;
159 cvt.buf = (Uint8 *) malloc(cvt.len * cvt.len_mult);
161 memcpy(cvt.buf, pcm_data, cvt.len);
163 SDL_ConvertAudio(&cvt);
164 SDL_MixAudio(udata->stream, cvt.buf, cvt.len_cvt, sim_volume);
166 udata->num_in = cvt.len / pcm_sample_bytes;
167 udata->num_out = cvt.len_cvt / pcm_sample_bytes;
169 #ifdef DEBUG
170 if (udata->debug != NULL) {
171 fwrite(cvt.buf, sizeof(Uint8), cvt.len_cvt, udata->debug);
173 #endif
174 free(cvt.buf);
176 else {
177 /* Convert is bad, so do silence */
178 Uint32 num = wr*obtained.channels;
179 udata->num_in = rd;
180 udata->num_out = wr;
182 switch (pcm_channel_bytes)
184 case 1:
186 Uint8 *stream = udata->stream;
187 while (num-- > 0)
188 *stream++ = obtained.silence;
189 break;
191 case 2:
193 Uint16 *stream = (Uint16 *)udata->stream;
194 while (num-- > 0)
195 *stream++ = obtained.silence;
196 break;
199 #ifdef DEBUG
200 if (udata->debug != NULL) {
201 fwrite(udata->stream, sizeof(Uint8), wr, udata->debug);
203 #endif
205 } else {
206 udata->num_in = udata->num_out = MIN(udata->num_in, udata->num_out);
207 SDL_MixAudio(udata->stream, pcm_data,
208 udata->num_out * pcm_sample_bytes, sim_volume);
209 #ifdef DEBUG
210 if (udata->debug != NULL) {
211 fwrite(pcm_data, sizeof(Uint8), udata->num_out * pcm_sample_bytes,
212 udata->debug);
214 #endif
218 void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
220 udata->stream = stream;
222 /* Write what we have in the PCM buffer */
223 if (pcm_data_size > 0)
224 goto start;
226 /* Audio card wants more? Get some more then. */
227 while (len > 0) {
228 if ((ssize_t)pcm_data_size <= 0) {
229 pcm_data_size = 0;
231 if (pcm_callback_for_more)
232 pcm_callback_for_more(&pcm_data, &pcm_data_size);
235 if (pcm_data_size > 0) {
236 start:
237 udata->num_in = pcm_data_size / pcm_sample_bytes;
238 udata->num_out = len / pcm_sample_bytes;
240 write_to_soundcard(udata);
242 udata->num_in *= pcm_sample_bytes;
243 udata->num_out *= pcm_sample_bytes;
245 pcm_data += udata->num_in;
246 pcm_data_size -= udata->num_in;
247 udata->stream += udata->num_out;
248 len -= udata->num_out;
249 } else {
250 DEBUGF("sdl_audio_callback: No Data.\n");
251 pcm_play_dma_stop();
252 pcm_play_dma_stopped_callback();
253 break;
258 const void * pcm_play_dma_get_peak_buffer(int *count)
260 uintptr_t addr = (uintptr_t)pcm_data;
261 *count = pcm_data_size / 4;
262 return (void *)((addr + 2) & ~3);
265 #ifdef HAVE_RECORDING
266 void pcm_rec_lock(void)
270 void pcm_rec_unlock(void)
274 void pcm_rec_dma_init(void)
278 void pcm_rec_dma_close(void)
282 void pcm_rec_dma_start(void *start, size_t size)
284 (void)start;
285 (void)size;
288 void pcm_rec_dma_stop(void)
292 void pcm_record_more(void *start, size_t size)
294 (void)start;
295 (void)size;
298 unsigned long pcm_rec_status(void)
300 return 0;
303 const void * pcm_rec_dma_get_peak_buffer(int *count)
305 *count = 0;
306 return NULL;
309 #endif /* HAVE_RECORDING */
311 void pcm_play_dma_init(void)
313 if (SDL_InitSubSystem(SDL_INIT_AUDIO))
315 DEBUGF("Could not initialize SDL audio subsystem!\n");
316 return;
319 SDL_AudioSpec wanted_spec;
320 #ifdef DEBUG
321 udata.debug = NULL;
322 if (debug_audio) {
323 udata.debug = fopen("audiodebug.raw", "wb");
324 DEBUGF("Audio debug file open\n");
326 #endif
327 /* Set 16-bit stereo audio at 44Khz */
328 wanted_spec.freq = 44100;
329 wanted_spec.format = AUDIO_S16SYS;
330 wanted_spec.channels = 2;
331 wanted_spec.samples = 2048;
332 wanted_spec.callback =
333 (void (SDLCALL *)(void *userdata,
334 Uint8 *stream, int len))sdl_audio_callback;
335 wanted_spec.userdata = &udata;
337 /* Open the audio device and start playing sound! */
338 if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) {
339 DEBUGF("Unable to open audio: %s\n", SDL_GetError());
340 return;
343 switch (obtained.format)
345 case AUDIO_U8:
346 case AUDIO_S8:
347 pcm_channel_bytes = 1;
348 break;
349 case AUDIO_U16LSB:
350 case AUDIO_S16LSB:
351 case AUDIO_U16MSB:
352 case AUDIO_S16MSB:
353 pcm_channel_bytes = 2;
354 break;
355 default:
356 DEBUGF("Unknown sample format obtained: %u\n",
357 (unsigned)obtained.format);
358 return;
361 pcm_sample_bytes = obtained.channels * pcm_channel_bytes;
363 pcm_dma_apply_settings_nolock();
366 void pcm_postinit(void)
370 void pcm_set_mixer_volume(int volume)
372 sim_volume = volume;
375 #endif /* CONFIG_CODEC == SWCODEC */