1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include <queues/fifo_queue.h>
21
22 #include "../../retroarch.h"
23
24 #include <defines/ps3_defines.h>
25
26 #define AUDIO_BLOCKS 8
27 #define AUDIO_CHANNELS 2
28
29 typedef struct
30 {
31 fifo_buffer_t *buffer;
32 sys_ppu_thread_t thread;
33 sys_lwmutex_t lock;
34 sys_lwmutex_t cond_lock;
35 sys_lwcond_t cond;
36 uint32_t audio_port;
37 bool nonblock;
38 bool started;
39 volatile bool quit_thread;
40 } ps3_audio_t;
41
42
43 #ifdef __PSL1GHT__
event_loop(void * data)44 static void event_loop(void *data)
45 #else
46 static void event_loop(uint64_t data)
47 #endif
48 {
49 float out_tmp[AUDIO_BLOCK_SAMPLES * AUDIO_CHANNELS]
50 __attribute__((aligned(16)));
51 sys_event_queue_t id;
52 sys_ipc_key_t key;
53 sys_event_t event;
54 ps3_audio_t *aud = (ps3_audio_t*)(uintptr_t)data;
55
56 audioCreateNotifyEventQueue(&id, &key);
57 audioSetNotifyEventQueue(key);
58
59 while (!aud->quit_thread)
60 {
61 sysEventQueueReceive(id, &event, PS3_SYS_NO_TIMEOUT);
62
63 sysLwMutexLock(&aud->lock, PS3_SYS_NO_TIMEOUT);
64 if (FIFO_READ_AVAIL(aud->buffer) >= sizeof(out_tmp))
65 fifo_read(aud->buffer, out_tmp, sizeof(out_tmp));
66 else
67 memset(out_tmp, 0, sizeof(out_tmp));
68 sysLwMutexUnlock(&aud->lock);
69 sysLwCondSignal(&aud->cond);
70
71 audioAddData(aud->audio_port, out_tmp,
72 AUDIO_BLOCK_SAMPLES, 1.0);
73 }
74
75 audioRemoveNotifyEventQueue(key);
76 sysThreadExit(0);
77 }
78
ps3_audio_init(const char * device,unsigned rate,unsigned latency,unsigned block_frames,unsigned * new_rate)79 static void *ps3_audio_init(const char *device,
80 unsigned rate, unsigned latency,
81 unsigned block_frames,
82 unsigned *new_rate)
83 {
84 audioPortParam params;
85 ps3_audio_t *data = NULL;
86 #ifdef __PSL1GHT__
87 sys_lwmutex_attr_t lock_attr =
88 {SYS_LWMUTEX_ATTR_PROTOCOL, SYS_LWMUTEX_ATTR_RECURSIVE, "\0"};
89 sys_lwmutex_attr_t cond_lock_attr =
90 {SYS_LWMUTEX_ATTR_PROTOCOL, SYS_LWMUTEX_ATTR_RECURSIVE, "\0"};
91 sys_lwcond_attr_t cond_attr = {"\0"};
92 #else
93 sys_lwmutex_attr_t lock_attr;
94 sys_lwmutex_attr_t cond_lock_attr;
95 sys_lwcond_attr_t cond_attr;
96
97 sys_lwmutex_attribute_initialize(lock_attr);
98 sys_lwmutex_attribute_initialize(cond_lock_attr);
99 sys_lwcond_attribute_initialize(cond_attr);
100 #endif
101
102 data = calloc(1, sizeof(*data));
103 if (!data)
104 return NULL;
105
106 audioInit();
107
108 params.numChannels = AUDIO_CHANNELS;
109 params.numBlocks = AUDIO_BLOCKS;
110 params.param_attrib = 0;
111 #if 0
112 #ifdef HAVE_HEADSET
113 if(global->console.sound.mode == SOUND_MODE_HEADSET)
114 params.param_attrib = CELL_AUDIO_PORTATTR_OUT_SECONDARY;
115 #endif
116 #endif
117
118 if (audioPortOpen(¶ms, &data->audio_port) != CELL_OK)
119 {
120 audioQuit();
121 free(data);
122 return NULL;
123 }
124
125 data->buffer = fifo_new(AUDIO_BLOCK_SAMPLES *
126 AUDIO_CHANNELS * AUDIO_BLOCKS * sizeof(float));
127
128 sysLwMutexCreate(&data->lock, &lock_attr);
129 sysLwMutexCreate(&data->cond_lock, &cond_lock_attr);
130 sysLwCondCreate(&data->cond, &data->cond_lock, &cond_attr);
131
132 audioPortStart(data->audio_port);
133 data->started = true;
134 sysThreadCreate(&data->thread, event_loop,
135 #ifdef __PSL1GHT__
136 data,
137 #else
138 (uint64_t)data,
139 #endif
140 1500, 0x1000, SYS_THREAD_CREATE_JOINABLE, (char*)"sound");
141
142 return data;
143 }
144
ps3_audio_write(void * data,const void * buf,size_t size)145 static ssize_t ps3_audio_write(void *data, const void *buf, size_t size)
146 {
147 ps3_audio_t *aud = data;
148
149 if (aud->nonblock)
150 {
151 if (FIFO_WRITE_AVAIL(aud->buffer) < size)
152 return 0;
153 }
154
155 while (FIFO_WRITE_AVAIL(aud->buffer) < size)
156 sysLwCondWait(&aud->cond, 0);
157
158 sysLwMutexLock(&aud->lock, PS3_SYS_NO_TIMEOUT);
159 fifo_write(aud->buffer, buf, size);
160 sysLwMutexUnlock(&aud->lock);
161
162 return size;
163 }
164
ps3_audio_stop(void * data)165 static bool ps3_audio_stop(void *data)
166 {
167 ps3_audio_t *aud = data;
168 if (aud->started)
169 {
170 audioPortStop(aud->audio_port);
171 aud->started = false;
172 }
173 return true;
174 }
175
ps3_audio_start(void * data,bool is_shutdown)176 static bool ps3_audio_start(void *data, bool is_shutdown)
177 {
178 ps3_audio_t *aud = data;
179 if (!aud->started)
180 {
181 audioPortStart(aud->audio_port);
182 aud->started = true;
183 }
184 return true;
185 }
186
ps3_audio_alive(void * data)187 static bool ps3_audio_alive(void *data)
188 {
189 ps3_audio_t *aud = data;
190 if (!aud)
191 return false;
192 return aud->started;
193 }
194
ps3_audio_set_nonblock_state(void * data,bool toggle)195 static void ps3_audio_set_nonblock_state(void *data, bool toggle)
196 {
197 ps3_audio_t *aud = data;
198 if (aud)
199 aud->nonblock = toggle;
200 }
201
ps3_audio_free(void * data)202 static void ps3_audio_free(void *data)
203 {
204 uint64_t val;
205 ps3_audio_t *aud = data;
206
207 aud->quit_thread = true;
208 ps3_audio_start(aud, false);
209 sysThreadJoin(aud->thread, &val);
210
211 ps3_audio_stop(aud);
212 audioPortClose(aud->audio_port);
213 audioQuit();
214 fifo_free(aud->buffer);
215
216 sysLwMutexDestroy(&aud->lock);
217 sysLwMutexDestroy(&aud->cond_lock);
218 sysLwCondDestroy(&aud->cond);
219
220 free(data);
221 }
222
ps3_audio_use_float(void * data)223 static bool ps3_audio_use_float(void *data)
224 {
225 return true;
226 }
227
ps3_audio_write_avail(void * data)228 static size_t ps3_audio_write_avail(void *data)
229 {
230 /* TODO/FIXME - implement? */
231 return 0;
232 }
233
234 audio_driver_t audio_ps3 = {
235 ps3_audio_init,
236 ps3_audio_write,
237 ps3_audio_stop,
238 ps3_audio_start,
239 ps3_audio_alive,
240 ps3_audio_set_nonblock_state,
241 ps3_audio_free,
242 ps3_audio_use_float,
243 "ps3",
244 NULL,
245 NULL,
246 ps3_audio_write_avail,
247 NULL
248 };
249