PS2SDK
PS2 Homebrew Libraries
Loading...
Searching...
No Matches
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/*
35Flag layout:
36 bit0 = 1=stereo 0=mono
37 bit12-15 = end flag
38*/
39
40static u32 stream_flags=0; /* bit0=stereo/mono, bit12->bit15 bit16=current buffer, bit24->bit31=status */
41static int stream_sema=-1, stream_thid=-1; /* Semaphore ID, Thread ID */
42static int stream_fd; /* Current FD */
43static u32 stream_bufid; /* The current buffer the SPU2 is playing */
44#define stream_bufsafe (1-stream_bufid)
45static int stream_cur; /* The 16-byte block ID of the begining of bufid */
46static int stream_status;
47static u8 *stream_buf; /* buffer */
48static u32 stream_chans;
49static u32 stream_buflen; /* length of buffers (clipped to 16bytes) */
50static u32 stream_bufspu[2]; /* position of buffer in spu ram 0=left, 1=right */
51static u32 stream_voice[2]; /* voice ID */
52
53static 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*/
80int 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
112void 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
145int 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
154int 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
283int 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
305int 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
327int 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
348int 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
392int 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
409int 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}