PS2SDK
PS2 Homebrew Libraries
audsrv.c
Go to the documentation of this file.
1 /*
2 # _____ ___ ____ ___ ____
3 # ____| | ____| | | |____|
4 # | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5 #-----------------------------------------------------------------------
6 # Copyright 2005, ps2dev - http://www.ps2dev.org
7 # Licenced under GNU Library General Public License version 2
8 */
9 
15 #include <stdio.h>
16 #include <thbase.h>
17 #include <thsemap.h>
18 #include <loadcore.h>
19 #include <sysmem.h>
20 #include <intrman.h>
21 #include <sifcmd.h>
22 #include <libsd.h>
23 #include <sysclib.h>
24 
25 #include <audsrv.h>
26 #include "audsrv_internal.h"
27 #include "common.h"
28 #include "rpc_server.h"
29 #include "rpc_client.h"
30 #include "upsamplers.h"
31 #include "hw.h"
32 #include "spu.h"
33 #include "debug_printf.h"
34 
35 #define MODNAME "audsrv"
36 #define VERSION "0.93"
37 IRX_ID(MODNAME, 1, 4);
38 
39 /* globals */
41 static int core1_volume = MAX_VOLUME;
43 static int core1_freq = 0;
45 static int core1_bits = 0;
47 static int core1_channels = 0;
49 static int core1_sample_shift = 0;
50 
51 /* status */
53 static int initialized = 0;
55 static int playing = 0;
56 
57 
58 /* ring buffer properties */
60 static char ringbuf[20480];
62 static int ringbuf_size = sizeof(ringbuf);
64 static int readpos;
66 static int writepos;
67 
69 static int play_tid = 0;
71 static int queue_sema = 0;
73 static int transfer_sema = 0;
75 static int fillbuf_threshold = 0;
76 
78 static int format_changed = 0;
79 
81 static u8 core1_buf[0x1000] __attribute__((aligned (16)));
82 
83 static short rendered_left [ 512 ];
84 static short rendered_right[ 512 ];
85 
87 extern struct irx_export_table _exp_audsrv;
88 
89 /* forward declarations */
90 static void play_thread(void *arg);
91 
99 static int transfer_complete(void *arg)
100 {
101  (void)arg;
102 
103  iSignalSema(transfer_sema);
104  return 1;
105 }
106 
108 static void update_volume()
109 {
110  int vol;
111 
112  /* external input */
113  sceSdSetParam(SD_CORE_1 | SD_PARAM_AVOLL, 0x7fff);
114  sceSdSetParam(SD_CORE_1 | SD_PARAM_AVOLR, 0x7fff);
115 
116  /* core0 input */
117  sceSdSetParam(SD_CORE_0 | SD_PARAM_BVOLL, 0);
118  sceSdSetParam(SD_CORE_0 | SD_PARAM_BVOLR, 0);
119 
120  /* core1 input */
121  vol = playing ? core1_volume : 0;
122  sceSdSetParam(SD_CORE_1 | SD_PARAM_BVOLL, vol);
123  sceSdSetParam(SD_CORE_1 | SD_PARAM_BVOLR, vol);
124 
125  /* set master volume for core 0 */
126  sceSdSetParam(SD_CORE_0 | SD_PARAM_MVOLL, 0);
127  sceSdSetParam(SD_CORE_0 | SD_PARAM_MVOLR, 0);
128 
129  /* set master volume for core 1 */
130  sceSdSetParam(SD_CORE_1 | SD_PARAM_MVOLL, MAX_VOLUME);
131  sceSdSetParam(SD_CORE_1 | SD_PARAM_MVOLR, MAX_VOLUME);
132 }
133 
140 {
141  /* audio is still playing, just mute */
142  playing = 0;
143  update_volume();
144  fillbuf_threshold = 0;
145 
146  return AUDSRV_ERR_NOERROR;
147 }
148 
156 int audsrv_format_ok(int freq, int bits, int channels)
157 {
158  if (find_upsampler(freq, bits, channels) != NULL)
159  {
160  return 1;
161  }
162 
163  /* unsupported format */
164  return 0;
165 }
166 
178 int audsrv_set_format(int freq, int bits, int channels)
179 {
180  int feed_size;
181 
182  if (audsrv_format_ok(freq, bits, channels) == 0)
183  {
184  return -AUDSRV_ERR_FORMAT_NOT_SUPPORTED;
185  }
186 
187  /* update shift-right count */
188  core1_sample_shift = 0;
189  if (bits == 16)
190  {
192  }
193 
194  if (channels == 2)
195  {
197  }
198 
199  core1_freq = freq;
200  core1_bits = bits;
201  core1_channels = channels;
202 
203  /* set ring buffer size to 10 iterations worth of data (~50 ms) */
204  feed_size = ((512 * core1_freq) / 48000) << core1_sample_shift;
205  ringbuf_size = feed_size * 10;
206 
207  writepos = 0;
208  readpos = (feed_size * 5) & ~3;
209 
210  DPRINTF("freq %d bits %d channels %d ringbuf_sz %d feed_size %d shift %d\n", freq, bits, channels, ringbuf_size, feed_size, core1_sample_shift);
211 
212  format_changed = 1;
213  return AUDSRV_ERR_NOERROR;
214 }
215 
220 {
221  if (initialized)
222  {
223  return 0;
224  }
225 
226  /* initialize libsd */
227  if (sceSdInit(SD_INIT_COLD) < 0)
228  {
229  DPRINTF("failed to initialize libsd\n");
230  return -1;
231  }
232 
233  readpos = 0;
234  writepos = 0;
235 
236  /* initialize transfer_complete's semaphore */
237  transfer_sema = CreateMutex(0);
238  if (transfer_sema < 0)
239  {
240  return AUDSRV_ERR_OUT_OF_MEMORY;
241  }
242 
243  queue_sema = CreateMutex(0);
244  if (queue_sema < 0)
245  {
246  DeleteSema(transfer_sema);
247  return AUDSRV_ERR_OUT_OF_MEMORY;
248  }
249 
250  /* audio is always playing in the background. trick is to
251  * set the data input volume to zero
252  */
254 
255  /* initialize transfer-complete callback */
256  sceSdSetTransCallback(AUDSRV_BLOCK_DMA_CH, (void *)transfer_complete);
257  sceSdBlockTrans(AUDSRV_BLOCK_DMA_CH, SD_TRANS_LOOP, core1_buf, sizeof(core1_buf), 0);
258 
259  /* default to SPU's native */
260  audsrv_set_format(48000, 16, 2);
261 
263  DPRINTF("playing thread 0x%x started\n", play_tid);
264 
265  DPRINTF("kickstarted\n");
266 
267  initialized = 1;
268  return AUDSRV_ERR_NOERROR;
269 }
270 
279 {
280  if (writepos <= readpos)
281  {
282  return readpos - writepos;
283  }
284  else
285  {
286  return (ringbuf_size - (writepos - readpos));
287  }
288 }
289 
296 {
297  if (writepos < readpos)
298  {
299  return (ringbuf_size - (readpos - writepos));
300  }
301  else
302  {
303  return writepos - readpos;
304  }
305 }
306 
314 int audsrv_wait_audio(int buflen)
315 {
316  if (ringbuf_size < buflen)
317  {
318  /* this will never happen */
319  return AUDSRV_ERR_ARGS;
320  }
321 
322  while (1)
323  {
324  if (audsrv_available() >= buflen)
325  {
326  /* enough space! */
327  return AUDSRV_ERR_NOERROR;
328  }
329 
330  WaitSema(queue_sema);
331  }
332 }
333 
344 int audsrv_play_audio(const char *buf, int buflen)
345 {
346  int sent = 0;
347 
348  if (initialized == 0)
349  {
350  return -AUDSRV_ERR_NOT_INITIALIZED;
351  }
352 
353  if (playing == 0)
354  {
355  /* audio is always playing, just change the volume */
356  playing = 1;
357  update_volume();
358  }
359 
360  //DPRINTF("play audio %d bytes, readpos %d, writepos %d avail %d\n", buflen, readpos, writepos, audsrv_available());
361 
362  /* limit to what's available, no crossing possible */
363  buflen = MIN(buflen, audsrv_available());
364 
365  while (buflen > 0)
366  {
367  int copy = buflen;
368  if (writepos >= readpos)
369  {
370  copy = MIN(ringbuf_size - writepos, buflen);
371  }
372 
373  memcpy(ringbuf + writepos, buf, copy);
374  buf = buf + copy;
375  buflen = buflen - copy;
376  sent = sent + copy;
377 
378  writepos = writepos + copy;
379  if (writepos >= ringbuf_size)
380  {
381  /* rewind */
382  writepos = 0;
383  }
384  }
385 
386  return sent;
387 }
388 
393 int audsrv_set_volume(int vol)
394 {
395  if (vol < 0 || vol > MAX_VOLUME)
396  {
397  /* bad joke */
398  return AUDSRV_ERR_ARGS;
399  }
400 
401  core1_volume = vol;
402  update_volume();
403  return AUDSRV_ERR_NOERROR;
404 }
405 
406 int audsrv_set_threshold(int amount)
407 {
408  if (amount > (ringbuf_size / 2))
409  {
410  /* amount is greater than what we'd recommend */
411  return AUDSRV_ERR_ARGS;
412  }
413 
414  DPRINTF("callback threshold: %d\n", amount);
415  fillbuf_threshold = amount;
416  return 0;
417 }
418 
429 static void play_thread(void *arg)
430 {
431  int intr_state;
432  int step;
433  struct upsample_t up;
434  upsampler_t upsampler = NULL;
435 
436  (void)arg;
437 
438  DPRINTF("starting play thread\n");
439  while (1)
440  {
441  int block;
442  u8 *bufptr;
443  int available;
444 
445  if (format_changed)
446  {
448  format_changed = 0;
449  }
450 
451  if (playing && upsampler != NULL)
452  {
453  up.src = (const unsigned char *)ringbuf + readpos;
454  up.left = rendered_left;
455  up.right = rendered_right;
456  step = upsampler(&up);
457 
458  readpos = readpos + step;
459  if (readpos >= ringbuf_size)
460  {
461  /* wrap around */
462  readpos = 0;
463  }
464  }
465  else
466  {
467  /* not playing */
468  memset(rendered_left, '\0', sizeof(rendered_left));
469  memset(rendered_right, '\0', sizeof(rendered_right));
470  }
471 
472  /* wait until it's safe to transmit another block */
473  WaitSema(transfer_sema);
474 
475  /* suspend all interrupts */
476  CpuSuspendIntr(&intr_state);
477 
478  /* one block is playing currently, other is idle */
479  block = 1 - (sceSdBlockTransStatus(AUDSRV_BLOCK_DMA_CH, 0) >> 24);
480 
481  /* copy 1024 bytes from left and right buffers, into core1_buf */
482  bufptr = core1_buf + (block << 11);
483  wmemcpy(bufptr + 0, rendered_left + 0, 512);
484  wmemcpy(bufptr + 512, rendered_right + 0, 512);
485  wmemcpy(bufptr + 1024, rendered_left + 256, 512);
486  wmemcpy(bufptr + 1536, rendered_right + 256, 512);
487 
488  CpuResumeIntr(intr_state);
489 
490  available = audsrv_available();
491  if (available >= (ringbuf_size / 10))
492  {
493  /* arbitrarily selected ringbuf_size / 10, to reduce
494  * number of semaphores signalled.
495  */
496  SignalSema(queue_sema);
497  }
498 
499  if (fillbuf_threshold > 0 && available >= fillbuf_threshold)
500  {
501  /* EE client requested a callback */
502  call_client_callback(AUDSRV_FILLBUF_CALLBACK);
503  }
504 
505  //DPRINTF("avaiable: %d, queued: %d\n", available, ringbuf_size - available);
506  }
507 }
508 
513 {
514  /* silence! */
516  audsrv_stop_cd();
518 
519  /* stop transmission */
520  sceSdSetTransCallback(AUDSRV_BLOCK_DMA_CH, NULL);
521  sceSdBlockTrans(AUDSRV_BLOCK_DMA_CH, SD_TRANS_STOP, 0, 0, 0);
522 
523  /* stop playing thread */
524  if (play_tid > 0)
525  {
526  TerminateThread(play_tid);
527  DeleteThread(play_tid);
528  play_tid = 0;
529  }
530 
531 #ifndef NO_RPC_THREAD
532  /* Deinitialize RPC client, only once the playback thread is stopped. */
533  deinitialize_rpc_client();
534 #endif
535 
536  if (transfer_sema > 0)
537  {
538  DeleteSema(transfer_sema);
539  transfer_sema = 0;
540  }
541 
542  if (queue_sema > 0)
543  {
544  DeleteSema(queue_sema);
545  queue_sema = 0;
546  }
547 
548  return 0;
549 }
550 
556 int _start(int argc, char *argv[])
557 {
558  int err;
559 
560  (void)argc;
561  (void)argv;
562 
563  FlushDcache();
564  CpuEnableIntr();
565 
566  printf("greetings from version " VERSION " !\n");
567 
568  err = RegisterLibraryEntries(&_exp_audsrv);
569  if (err != 0)
570  {
571  DPRINTF("couldn't register library entries. Error %d\n", err);
572  return MODULE_NO_RESIDENT_END;
573  }
574 
576 
577  /* create RPC listener thread */
578  initialize_rpc_thread();
579 
580  return MODULE_RESIDENT_END;
581 }
rpc_server.h
core1_freq
static int core1_freq
Definition: audsrv.c:43
__attribute__
static u8 core1_buf[0x1000] __attribute__((aligned(16)))
Definition: netcnfif.c:19
audsrv_wait_audio
int audsrv_wait_audio(int buflen)
Definition: audsrv.c:314
CpuEnableIntr
int CpuEnableIntr()
Definition: intrman.c:240
ringbuf
static char ringbuf[20480]
Definition: audsrv.c:60
thbase.h
audsrv_init
int audsrv_init()
Definition: audsrv.c:219
sysclib.h
audsrv_format_ok
int audsrv_format_ok(int freq, int bits, int channels)
Definition: audsrv.c:156
format_changed
static int format_changed
Definition: audsrv.c:78
play_tid
static int play_tid
Definition: audsrv.c:69
queue_sema
static int queue_sema
Definition: audsrv.c:71
ringbuf_size
static int ringbuf_size
Definition: audsrv.c:62
audsrv_adpcm_init
int audsrv_adpcm_init()
Definition: audsrv_rpc.c:394
core1_bits
static int core1_bits
Definition: audsrv.c:45
MIN
#define MIN(a, b)
Definition: audsrv_internal.h:19
initialized
static int initialized
Definition: audsrv.c:53
CpuSuspendIntr
int CpuSuspendIntr(int *state)
Definition: intrman.c:195
loadcore.h
common.h
core1_channels
static int core1_channels
Definition: audsrv.c:47
hw.h
transfer_sema
static int transfer_sema
Definition: audsrv.c:73
_exp_audsrv
struct irx_export_table _exp_audsrv
thsemap.h
audsrv_set_volume
int audsrv_set_volume(int vol)
Definition: audsrv.c:393
irx_export_table
Definition: irx.h:90
playing
static int playing
Definition: audsrv.c:55
_start
int _start(int argc, char *argv[])
Definition: audsrv.c:556
stdio.h
MAX_VOLUME
#define MAX_VOLUME
Definition: audsrv.h:25
CpuResumeIntr
int CpuResumeIntr(int state)
Definition: intrman.c:217
spu.h
writepos
static int writepos
Definition: audsrv.c:66
audsrv_play_audio
int audsrv_play_audio(const char *buf, int buflen)
Definition: audsrv.c:344
audsrv_set_format
int audsrv_set_format(int freq, int bits, int channels)
Definition: audsrv.c:178
sysmem.h
__attribute__
Definition: gif_registers.h:38
play_thread
static void play_thread(void *arg)
Definition: audsrv.c:429
AUDSRV_ERR_NOERROR
#define AUDSRV_ERR_NOERROR
Definition: audsrv.h:28
rpc_client.h
upsamplers.h
upsample_t
Definition: upsamplers.h:18
audsrv_stop_audio
int audsrv_stop_audio()
Definition: audsrv.c:139
find_upsampler
upsampler_t find_upsampler(int freq, int bits, int channels)
Definition: upsamplers.c:636
intrman.h
audsrv_quit
int audsrv_quit()
Definition: audsrv.c:512
transfer_complete
static int transfer_complete(void *arg)
Definition: audsrv.c:99
core1_volume
static int core1_volume
Definition: audsrv.c:41
fillbuf_threshold
static int fillbuf_threshold
Definition: audsrv.c:75
readpos
static int readpos
Definition: audsrv.c:64
core1_sample_shift
static int core1_sample_shift
Definition: audsrv.c:49
audsrv_queued
int audsrv_queued()
Definition: audsrv.c:295
audsrv_stop_cd
int audsrv_stop_cd()
Definition: audsrv_rpc.c:207
update_volume
static void update_volume()
Definition: audsrv.c:108
audsrv_internal.h
create_thread
int create_thread(void(*func)(void *param), int priority, void *param)
Definition: common.c:34
audsrv_available
int audsrv_available()
Definition: audsrv.c:278