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