PS2SDK
PS2 Homebrew Libraries
adpcm-stream.c
1 /*
2 # _____ ___ ____ ___ ____
3 # ____| | ____| | | |____|
4 # | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5 #-----------------------------------------------------------------------
6 # Copyright 2005, James Lee (jbit<at>jbit<dot>net)
7 # Licenced under Academic Free License version 2.0
8 # Review ps2sdk README & LICENSE files for further details.
9 */
10 
11 #include <types.h>
12 #include <irx.h>
13 #include <sifrpc.h>
14 #include <loadcore.h>
15 #include <stdio.h>
16 #include <libsd.h>
17 #include <thbase.h>
18 #include <intrman.h>
19 #include <sysmem.h>
20 #include <ioman.h>
21 #include <thsemap.h>
22 #include <ps2snd.h>
23 #include "mod.h"
24 
25 #define FLAG_STEREO 0x1
26 #define FLAG_BUFID 0x10000
27 #define STATUS_CLOSED 0
28 #define STATUS_OPEN 1
29 #define STATUS_PLAYING 2
30 #define STATUS_ERROR 3
31 
32 #define stream_endflag (stream_flags&0xF000)
33 
34 /*
35 Flag layout:
36  bit0 = 1=stereo 0=mono
37  bit12-15 = end flag
38 */
39 
40 static u32 stream_flags=0; /* bit0=stereo/mono, bit12->bit15 bit16=current buffer, bit24->bit31=status */
41 static int stream_sema=-1, stream_thid=-1; /* Semaphore ID, Thread ID */
42 static int stream_fd; /* Current FD */
43 static u32 stream_bufid; /* The current buffer the SPU2 is playing */
44 #define stream_bufsafe (1-stream_bufid)
45 static int stream_cur; /* The 16-byte block ID of the begining of bufid */
46 static int stream_status;
47 static u8 *stream_buf; /* buffer */
48 static u32 stream_chans;
49 static u32 stream_buflen; /* length of buffers (clipped to 16bytes) */
50 static u32 stream_bufspu[2]; /* position of buffer in spu ram 0=left, 1=right */
51 static u32 stream_voice[2]; /* voice ID */
52 
53 static inline void setloopflags(int id, u8 *buf, int len)
54 {
55  /* XXX: double check these numbers */
56  /* Set loop flags */
57  for (int i=0;i<len;i+=16)
58  {
59 // if (buf[i+1] == 0)
60 // {
61  if (id == 0 && i==0)
62  buf[i+1] = 6; /* Set 'repeat start' when at the begining of buf0 */
63  else if (id == 1 && ((i+16)>=len))
64  buf[i+1] = 3; /* Set 'repeat from begining' when at the end of buf1 */
65  else
66  buf[i+1] = 2; /* Set 'in repeat section' when elsewhere */
67 // }
68  }
69 }
70 
71 /*
72  fillbuf - Fill a specific buffer
73 
74  This kinda sucks at the moment, should make the IOP blocks smaller than the SPU2 blocks...
75 
76  Load data from file and send it to the SPU2.
77  If we're stereo and near the end of the file, we send half the data to left and half to right
78 
79 */
80 int fillbuf(int id, int chan)
81 {
82  u8 *buf = stream_buf;
83  int size;
84 
85  id &= 1;
86 
87  size = read(stream_fd, buf, stream_buflen);
88  if (size<0) /* Error */
89  {
90  dprintf(OUT_ERROR, "Channel%d: error: %d\n", chan, size);
91  return(-1);
92  }
93  if (size==0)
94  return(0); /* EOF */
95 
96  /* If we're stereo and we've read less than a chunk, we're screwed */
97  if ((stream_chans>1) && ((u32)size<stream_buflen))
98  {
99  dprintf(OUT_ERROR, "Channel%d: failed to read entire chunk (read %d bytes)\n", chan, size);
100  return(-1);
101  }
102 
103  setloopflags(id, buf, size);
104 
105  sceSdVoiceTrans(0, SD_TRANS_WRITE | SD_TRANS_MODE_DMA, buf, (u32*)(stream_bufspu[chan]+(id*stream_buflen)), size);
106  sceSdVoiceTransStatus(0, 1);
107 
108  return(size);
109 }
110 
111 
112 void stream_thread(void *a)
113 {
114  (void)a;
115 
116  while(1)
117  {
118  /* Wait for an interrupt */
119  WaitSema(stream_sema);
120 
121  /* Update stuff */
122  stream_bufid = stream_bufsafe;
123  stream_cur+=(stream_buflen/16);
124 
125  dprintf(OUT_DEBUG, "SPU2 now playing buffer %d (block %d)\n", (int)stream_bufid, stream_cur);
126 
127  /* Fill the buffer the SPU2 isn't playing */
128  for (int i=0;(u32)i<stream_chans;i++)
129  {
130  int r;
131  r = fillbuf(stream_bufsafe, i);
132  if ((u32)r<stream_buflen) /* treat EOF and errors as the same thing atm */
133  {
134  sndStreamClose();
135  return;
136  }
137  }
138 
139  sceSdSetAddr(SD_ADDR_IRQA, stream_bufspu[0]+(stream_bufsafe*stream_buflen));
140  sceSdSetCoreAttr(SD_CORE_IRQ_ENABLE, 1);
141  }
142 
143 }
144 
145 int stream_handler(int core, void *data)
146 {
147  (void)core;
148  (void)data;
149 
150  iSignalSema(stream_sema);
151  return(0); /* What should i return here? */
152 }
153 
154 int sndStreamOpen(char *file, u32 voices, u32 flags, u32 bufaddr, u32 bufsize)
155 {
157 
158  dprintf(OUT_DEBUG, "%s\n", file);
159  if (stream_status)
160  {
161  dprintf(OUT_WARNING, "There's a stream already open\n");
162  return(-1);
163  }
164 
165  stream_voice[0] = voices&0xffff;
166  stream_voice[1] = (voices>>16)&0xffff;
167 
168  dprintf(OUT_INFO, "%s Voices %d:%d and %d:%d\n", file, (int)stream_voice[0]&1, (int)stream_voice[0]>>1, (int)stream_voice[1]&1, (int)stream_voice[1]>>1);
169 
170  if ((stream_voice[0]&1) != (stream_voice[1]&1))
171  {
172  dprintf(OUT_ERROR, "Stream voices arn't on the same core!!!!\n");
173  return(-1);
174  }
175 
176  /* Setup Semaphore */
177  if (stream_sema<0)
178  {
179  iop_sema_t sema;
180 
181  sema.attr = 0;
182  sema.option = 0;
183  sema.initial = 0;
184  sema.max = 1;
185 
186  stream_sema = CreateSema(&sema);
187  if (stream_sema<0)
188  dprintf(OUT_ERROR, "Failed to get a semaphore\n");
189  }
190 
191  stream_buflen = bufsize*16;
192  stream_flags = flags&0xffff; /* only the bottom 16bits are for user use! */
193 
194  stream_buf = AllocSysMemory(ALLOC_FIRST, stream_buflen, NULL);
195  if (stream_buf==NULL)
196  {
197  dprintf(OUT_ERROR, "malloc failed (%u bytes)\n", (unsigned int)stream_buflen);
198  return(-2);
199  }
200 
201  /* Try to open file... */
202  stream_fd = open(file, O_RDONLY);
203  if (stream_fd<0)
204  {
205  dprintf(OUT_ERROR, "open failed (%d)\n", stream_fd);
206  FreeSysMemory(stream_buf);
207  return(-3);
208  }
209 
210  stream_bufspu[0] = bufaddr;
211  stream_bufspu[1] = bufaddr + (stream_buflen*2);
212 
213  u32 buffer[3];
214  int size = read(stream_fd, buffer, sizeof(buffer));
215  if (size<0) /* Error */
216  {
217  dprintf(OUT_ERROR, "read error: %d\n", size);
218  return(-1);
219  }
220 
221  int pitch = buffer[2];
222  stream_chans = (buffer[1] >> 8) & 0xFF;
223 
224  /* Setup SPU2 voice volumes.... */
225  if (stream_chans == 2)
226  {
227  dprintf(OUT_INFO, "stereo...\n");
228  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLL, 0x1fff);
229  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLR, 0);
230  sceSdSetParam(stream_voice[1] | SD_VPARAM_VOLL, 0);
231  sceSdSetParam(stream_voice[1] | SD_VPARAM_VOLR, 0x1fff);
232  }
233  else
234  {
235  dprintf(OUT_INFO, "mono...\n");
236  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLL, 0x1fff);
237  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLR, 0x1fff);
238  }
239 
240  /* Setup other SPU2 voice stuff... */
241  for (int i=0;(u32)i<stream_chans;i++) /* XXX */
242  {
243  sceSdSetParam(stream_voice[i] | SD_VPARAM_PITCH, pitch); /* 0x1000 = normal pitch */
244  sceSdSetParam(stream_voice[i] | SD_VPARAM_ADSR1, SD_SET_ADSR1(SD_ADSR_AR_EXPi, 0, 0xf, 0xf));
245  sceSdSetParam(stream_voice[i] | SD_VPARAM_ADSR2, SD_SET_ADSR2(SD_ADSR_SR_EXPd, 127, SD_ADSR_RR_EXPd, 0));
246  }
247 
248  /* Get a thread */
249  thread.attr = TH_C;
250  thread.thread = stream_thread;
251  thread.priority = 40;
252  thread.stacksize = 0x800;
253  thread.option = 0;
254  stream_thid = CreateThread(&thread);
255  if (stream_thid<0)
256  {
257  dprintf(OUT_ERROR, "Failed to make play thread\n");
258  sndStreamClose();
259  return(-1);
260  }
261 
262  /* Start the thread (it'll wait patiently until it gets an interrupt) */
263  StartThread(stream_thid, NULL);
264 
265  /* Get some interrupts going! */
266  sceSdSetCoreAttr(SD_CORE_IRQ_ENABLE, 0); /* disable pesky interrupts for a minute */
267  sceSdSetSpu2IntrHandler(stream_handler, (void*)1);
268 
269  stream_status = STATUS_OPEN;
270 
271  stream_cur = 0;
272  if (sndStreamSetPosition(0)<0)
273  {
274  sndStreamClose();
275  return(-1);
276  }
277 
278  dprintf(OUT_INFO, "Opened %s\n", file);
279 
280  return(0);
281 }
282 
283 int sndStreamClose(void)
284 {
285  if (stream_status==STATUS_PLAYING)
286  sndStreamPause();
287 
288  if (stream_status==STATUS_CLOSED)
289  return(-1);
290 
291  if (stream_thid>=0)
292  DeleteThread(stream_thid);
293 
294 
295  close(stream_fd);
296 
297  FreeSysMemory(stream_buf);
298  sceSdSetSpu2IntrHandler(NULL, NULL);
299 
300  stream_status = STATUS_CLOSED;
301  dprintf(OUT_INFO, "Closed!\n");
302  return(0);
303 }
304 
305 int sndStreamPlay(void)
306 {
307  if (stream_status==STATUS_CLOSED)
308  return(-1);
309  if (stream_status==STATUS_PLAYING)
310  return(0);
311 
312  /* Press down the keys :) */
313  if (stream_chans>1)
314  sceSdSetSwitch((stream_voice[0]&1) | SD_SWITCH_KEYDOWN, 1<<(stream_voice[0]>>1) | 1<<(stream_voice[1]>>1));
315  else
316  sceSdSetSwitch((stream_voice[0]&1) | SD_SWITCH_KEYDOWN, 1<<(stream_voice[0]>>1));
317 
318  sceSdSetCoreAttr(SD_CORE_IRQ_ENABLE, 1);
319 
320  stream_status=STATUS_PLAYING;
321 
322  dprintf(OUT_INFO, "Playing!\n");
323  return(1);
324 }
325 
326 
327 int sndStreamPause(void)
328 {
329  if (stream_status==STATUS_CLOSED)
330  return(-1);
331  if (stream_status==STATUS_OPEN)
332  return(0);
333 
334  /* Release keys */
335  if (stream_chans>1)
336  sceSdSetSwitch((stream_voice[0]&1) | SD_SWITCH_KEYUP, 1<<(stream_voice[0]>>1) | 1<<(stream_voice[1]>>1));
337  else
338  sceSdSetSwitch((stream_voice[0]&1) | SD_SWITCH_KEYUP, 1<<(stream_voice[0]>>1));
339 
340  sceSdSetCoreAttr(SD_CORE_IRQ_ENABLE, 0); /* disable pesky interrupts for a minute */
341 
342  stream_status=STATUS_OPEN;
343 
344  dprintf(OUT_INFO, "Paused!\n");
345  return(1);
346 }
347 
348 int sndStreamSetPosition(int block)
349 {
350  int chunk;
351  int r=0;
352  if (stream_status==STATUS_CLOSED)
353  return(-1);
354  if (stream_status==STATUS_PLAYING)
355  r = sndStreamPause();
356  if (block<0)
357  return(-2);
358 
359  /* lock block number to a chunk */
360  chunk = (stream_buflen/16)*stream_chans;
361  dprintf(OUT_DEBUG, "chunk = %d\n", chunk);
362  block = (block/chunk)*chunk;
363 
364  stream_cur = block;
365  lseek(stream_fd, block*16*stream_chans, SEEK_SET);
366 
367  stream_bufid = 0;
368 
369  sceSdSetAddr(SD_ADDR_IRQA, stream_bufspu[0]+stream_buflen);
370 
371  for (int i=0;i<2;i++)
372  for (int c=0;(u32)c<stream_chans;c++)
373  if (fillbuf(i, c)<=0)
374  {
375  dprintf(OUT_ERROR, "Hit EOF or error on buffer fill %d\n", i);
376  sndStreamClose();
377  return(-1);
378  }
379 
380  for (int c=0;(u32)c<stream_chans;c++)
381  sceSdSetAddr(stream_voice[c] | SD_VADDR_SSA, stream_bufspu[c]);
382 
383  /* Restart playing if we were playing before */
384  if (r)
385  sndStreamPlay();
386 
387  dprintf(OUT_INFO, "Position %d!\n", stream_cur);
388 
389  return(block);
390 }
391 
392 int sndStreamGetPosition(void)
393 {
394  int i;
395  if (stream_status==STATUS_CLOSED)
396  return(-1);
397 
398  if (stream_status==STATUS_OPEN)
399  return(stream_cur);
400 
401  /* During playback we can get the current position in the buffer from the SPU2! */
402  i = sceSdGetAddr(stream_voice[0] | SD_VADDR_NAX);
403  i -= stream_bufspu[0]+(stream_buflen*stream_bufid);
404  i /= 16;
405 
406  return(stream_cur+i);
407 }
408 
409 int sndStreamSetVolume(int left, int right)
410 {
411  if (stream_status==STATUS_CLOSED)
412  return(-1);
413 
414  if (stream_chans>1)
415  {
416  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLL, left);
417  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLR, 0);
418  sceSdSetParam(stream_voice[1] | SD_VPARAM_VOLL, 0);
419  sceSdSetParam(stream_voice[1] | SD_VPARAM_VOLR, right);
420  }
421  else
422  {
423  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLL, left);
424  sceSdSetParam(stream_voice[0] | SD_VPARAM_VOLR, right);
425  }
426  return(0);
427 }
ps2snd.h
thbase.h
iop_sema_t
Definition: thsemap.h:38
thread
Definition: thcommon.h:143
loadcore.h
irx.h
thsemap.h
stdio.h
ioman.h
_iop_thread
Definition: thbase.h:39
sysmem.h
intrman.h