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(&params, &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