dfsound: add PulseAudio workaround
[pcsx_rearmed.git] / plugins / dfsound / sdl.c
1 /* SDL Driver for P.E.Op.S Sound Plugin
2  * Copyright (c) 2010, Wei Mingzhi <whistler_wmz@users.sf.net>.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA
17  */
18
19 #include <stdlib.h>
20 #include <SDL.h>
21 #include "out.h"
22
23 #define BUFFER_SIZE             22050
24
25 short                   *pSndBuffer = NULL;
26 int                             iBufSize = 0;
27 volatile int    iReadPos = 0, iWritePos = 0;
28
29 static void SOUND_FillAudio(void *unused, Uint8 *stream, int len) {
30         short *p = (short *)stream;
31
32         len /= sizeof(short);
33
34         while (iReadPos != iWritePos && len > 0) {
35                 *p++ = pSndBuffer[iReadPos++];
36                 if (iReadPos >= iBufSize) iReadPos = 0;
37                 --len;
38         }
39
40         // Fill remaining space with zero
41         while (len > 0) {
42                 *p++ = 0;
43                 --len;
44         }
45 }
46
47 static void InitSDL() {
48         if (SDL_WasInit(SDL_INIT_EVERYTHING)) {
49                 SDL_InitSubSystem(SDL_INIT_AUDIO);
50         } else {
51                 SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
52         }
53 }
54
55 static void DestroySDL() {
56         if (SDL_WasInit(SDL_INIT_EVERYTHING & ~SDL_INIT_AUDIO)) {
57                 SDL_QuitSubSystem(SDL_INIT_AUDIO);
58         } else {
59                 SDL_Quit();
60         }
61 }
62
63 static int sdl_init(void) {
64         SDL_AudioSpec                           spec;
65
66         if (pSndBuffer != NULL) return -1;
67
68         InitSDL();
69
70         spec.freq = 44100;
71         spec.format = AUDIO_S16SYS;
72         spec.channels = 2;
73         spec.samples = 512;
74         spec.callback = SOUND_FillAudio;
75
76         if (SDL_OpenAudio(&spec, NULL) < 0) {
77                 DestroySDL();
78                 return -1;
79         }
80
81         iBufSize = BUFFER_SIZE;
82
83         pSndBuffer = (short *)malloc(iBufSize * sizeof(short));
84         if (pSndBuffer == NULL) {
85                 SDL_CloseAudio();
86                 return -1;
87         }
88
89         iReadPos = 0;
90         iWritePos = 0;
91
92         SDL_PauseAudio(0);
93         return 0;
94 }
95
96 static void sdl_finish(void) {
97         if (pSndBuffer == NULL) return;
98
99         SDL_CloseAudio();
100         DestroySDL();
101
102         free(pSndBuffer);
103         pSndBuffer = NULL;
104 }
105
106 static int sdl_busy(void) {
107         int size;
108
109         if (pSndBuffer == NULL) return 1;
110
111         size = iReadPos - iWritePos;
112         if (size <= 0) size += iBufSize;
113
114         if (size < iBufSize / 2) return 1;
115
116         return 0;
117 }
118
119 static void sdl_feed(void *pSound, int lBytes) {
120         short *p = (short *)pSound;
121
122         if (pSndBuffer == NULL) return;
123
124         while (lBytes > 0) {
125                 if (((iWritePos + 1) % iBufSize) == iReadPos) break;
126
127                 pSndBuffer[iWritePos] = *p++;
128
129                 ++iWritePos;
130                 if (iWritePos >= iBufSize) iWritePos = 0;
131
132                 lBytes -= sizeof(short);
133         }
134 }
135
136 void out_register_sdl(struct out_driver *drv)
137 {
138         drv->name = "sdl";
139         drv->init = sdl_init;
140         drv->finish = sdl_finish;
141         drv->busy = sdl_busy;
142         drv->feed = sdl_feed;
143 }