1 /*
2 * OpenBOR - http://www.chronocrash.com
3 * -----------------------------------------------------------------------
4 * All rights reserved, see LICENSE in OpenBOR root for details.
5 *
6 * Copyright (c) 2004 - 2015 OpenBOR Team
7 */
8
9 /* This code was derived from code carrying the following copyright notice:
10
11 Copyright (c) 2008 Francisco Muñoz 'Hermes' <www.elotrolado.net>
12 All rights reserved.
13
14 Proper (standard) vorbis usage by Tantric, 2009
15 Threading modifications/corrections by Tantric, 2009
16
17 Redistribution and use in source and binary forms, with or without
18 modification, are permitted provided that the following conditions are met:
19
20 - Redistributions of source code must retain the above copyright notice, this
21 list of conditions and the following disclaimer.
22 - Redistributions in binary form must reproduce the above copyright notice,
23 this list of conditions and the following disclaimer in the documentation
24 and/or other materials provided with the distribution.
25 - The names of the contributors may not be used to endorse or promote products
26 derived from this software without specific prior written permission.
27
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
29 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
32 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
34 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
35 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
36 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #include <asndlib.h>
41 #include <gccore.h>
42 #include <unistd.h>
43 #include <string.h>
44
45 #include "globals.h"
46 #include "sblaster.h"
47 #include "soundmix.h"
48
49 #define READ_SAMPLES 4*1024//4096 // samples that must be read before sending to hardware
50 #define MAX_PCMOUT 4*1024//4096 // minimum samples to mix
51 #define STACKSIZE 8*1024//8192
52
53 typedef struct
54 {
55 int flag;
56 int volume;
57 int samplerate;
58 short pcmout[2][READ_SAMPLES + MAX_PCMOUT * 2]; /* take 4k out of the data segment, not the stack */
59 int pcmout_pos;
60 int pcm_indx;
61 } private_data;
62
63 static private_data sb_private;
64
65 static u8 sb_stack[STACKSIZE];
66 static lwpq_t sb_queue = LWP_TQUEUE_NULL;
67 static lwp_t sb_thread = LWP_THREAD_NULL;
68 static int sb_thread_running = 0;
69
70 static int sb_inited = 0;
71
SB_init()72 static void SB_init()
73 {
74 if (sb_inited) return;
75 AUDIO_Init(NULL);
76 ASND_Init();
77 ASND_Pause(0);
78 sb_inited = 1;
79 }
80
SB_exit()81 void SB_exit()
82 {
83 ASND_Pause(1);
84 ASND_End();
85 sb_inited = 0;
86 }
87
SB_Callback(int voice)88 static void SB_Callback(int voice)
89 {
90 if (!sb_thread_running)
91 {
92 ASND_StopVoice(0);
93 }
94 else
95 {
96 if (sb_private.pcm_indx >= READ_SAMPLES)
97 {
98 if (ASND_AddVoice(0,
99 (void *) sb_private.pcmout[sb_private.pcmout_pos],
100 sb_private.pcm_indx << 1) == 0)
101 {
102 sb_private.pcmout_pos ^= 1;
103 sb_private.pcm_indx = 0;
104 sb_private.flag = 0;
105 LWP_ThreadSignal(sb_queue);
106 }
107 }
108 else
109 {
110 if (sb_private.flag & 64)
111 {
112 sb_private.flag &= ~64;
113 LWP_ThreadSignal(sb_queue);
114 }
115 }
116 }
117
118 return;
119 }
120
SB_Thread(private_data * priv)121 static void *SB_Thread(private_data * priv)
122 {
123 int first_time = 1;
124
125 LWP_InitQueue(&sb_queue);
126 ASND_Pause(0);
127
128 priv[0].pcm_indx = 0;
129 priv[0].pcmout_pos = 0;
130 priv[0].flag = 0;
131
132 sb_thread_running = 1;
133
134 while (sb_thread_running)
135 {
136 if (priv[0].flag)
137 LWP_ThreadSleep(sb_queue);
138
139 if (priv[0].flag == 0) // wait until all samples are sent
140 {
141 if (ASND_TestPointer(0, priv[0].pcmout[priv[0].pcmout_pos])
142 && ASND_StatusVoice(0) != SND_UNUSED)
143 {
144 priv[0].flag |= 64;
145 continue;
146 }
147 if (priv[0].pcm_indx < READ_SAMPLES)
148 {
149 priv[0].flag = 3;
150
151 update_sample((u8 *) &priv[0].pcmout[priv[0].pcmout_pos][priv[0].pcm_indx], MAX_PCMOUT);
152 priv[0].flag &= 192;
153 priv[0].pcm_indx += MAX_PCMOUT >> 1; //16 bit samples
154 }
155 else
156 priv[0].flag = 1;
157 }
158
159 if (priv[0].flag == 1)
160 {
161 if (ASND_StatusVoice(0) == SND_UNUSED || first_time)
162 {
163 first_time = 0;
164 ASND_SetVoice(0, VOICE_STEREO_16BIT, priv[0].samplerate, 0,
165 (void *) priv[0].pcmout[priv[0].pcmout_pos],
166 priv[0].pcm_indx << 1, priv[0].volume,
167 priv[0].volume, SB_Callback);
168 priv[0].pcmout_pos ^= 1;
169 priv[0].pcm_indx = 0;
170 priv[0].flag = 0;
171 }
172 }
173 usleep(100);
174 }
175 priv[0].pcm_indx = 0;
176
177 return 0;
178 }
179
SB_playstop()180 void SB_playstop()
181 {
182 ASND_StopVoice(0);
183 sb_thread_running = 0;
184
185 if(sb_thread != LWP_THREAD_NULL)
186 {
187 if(sb_queue != LWP_TQUEUE_NULL)
188 LWP_ThreadSignal(sb_queue);
189 LWP_JoinThread(sb_thread, NULL);
190 sb_thread = LWP_THREAD_NULL;
191 }
192 if(sb_queue != LWP_TQUEUE_NULL)
193 {
194 LWP_CloseQueue(sb_queue);
195 sb_queue = LWP_TQUEUE_NULL;
196 }
197 }
198
SB_playstart(int bits,int samplerate)199 int SB_playstart(int bits, int samplerate)
200 {
201 SB_init();
202 SB_playstop();
203
204 sb_private.volume = 127;
205 sb_private.flag = 0;
206 sb_private.samplerate = samplerate;
207
208 if (LWP_CreateThread(&sb_thread, (void *) SB_Thread,
209 &sb_private, sb_stack, STACKSIZE, 80) == -1) // 80 prio
210 {
211 sb_thread_running = 0;
212 return 0;
213 }
214 return 1;
215 }
216
SB_setvolume(char dev,char volume)217 void SB_setvolume(char dev, char volume)
218 {
219 }
220
SB_updatevolume(int volume)221 void SB_updatevolume(int volume)
222 {
223 }
224