PS2SDK
PS2 Homebrew Libraries
Loading...
Searching...
No Matches
ahx_irx.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright 2001-2004, ps2dev - http://www.ps2dev.org
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11#include "irx_imports.h"
12#include "libsd.h"
13#include "ahx.h"
14
15// if this is defined the irx will be compiled without PRINTF output
16#define COMPACT_CODE
17
18#define MODNAME "AHXplayer" // module name
19#define MODVERSION "1.0b" // module version
20#define MODAUTHOR "Raizor" // module author
21#define M_PRINTF(format, args...) printf(MODNAME ": " format, ##args) // module printf
22
23IRX_ID(MODNAME, 1, 1);
24
25// LIBSD defines
26#define SD_CORE_1 1
27#define SD_INIT_COLD 0
28
29u8 *spubuf = NULL; // buffer for storing data, SPU2 grabs it from here
30char *pcmbuf = NULL; // ahx data is mixed to PCM straight into this buffer
31int transfer_sema = 0; // semaphore to indicate when a transfer has completed
32int play_tid = 0; // play_thread ID
33int intr_state; // interrupt state
34
35// IRX Stuff for Export and ID
36#define AHX_IRX 0xC001D0E // unique ID of our IRX
37#define AHX_INIT 0x01
38#define AHX_PLAY 0x02
39#define AHX_PAUSE 0x03
40#define AHX_QUIT 0x04
41#define AHX_LOADSONG 0x05
42#define AHX_SETVOLUME 0x06
43#define AHX_SETBOOST 0x07
44#define AHX_OVERSAMPLE 0x08
45#define AHX_SUBSONG 0x09
46#define TH_C 0x02000000 // I have no idea what this is o_O
47
48int readpos = 0; // offset that spu is reading audio data from
49int seg_done[8]; // we have 8 segments each containing a full AHX->PCM buffer
50int playing = 0; // are we playing a tune at the moment?
51int songloaded = 0; // is a song loaded and ready?
52int boost_val = 0; // sound output multiply value
53int oversample_enabled = 0; // are we oversampling? (nicer sound)
54u16 SPU2_Volume = 0x3fff; // current output volume of SPU2
55char mod_buffer[50 * 1024]; // 50kb buffer for loading songs
56
59
60extern void wmemcpy(void *dest, void *src, int numwords);
61static unsigned int buffer[0x80] __attribute__((__aligned__(4)));
62
63// function prototypes
64void AHX_Thread(void *param);
65void AHX_PlayThread(void *param);
66static int AHX_TransCallback(void *param);
67void AHX_SetVol(u16 vol);
68void AHX_ResetPlayThread();
69void AHX_ClearSoundBuffers();
70void *AHX_rpc_server(unsigned int funcno, void *data, int size);
71void *AHX_Init(unsigned int *sbuff);
72void *AHX_LoadSong(unsigned int *sbuff);
73void *AHX_SubSong(unsigned int *sbuff);
74void *AHX_Play(unsigned int *sbuff);
75void *AHX_Pause(unsigned int *sbuff);
76void *AHX_SetVolume(unsigned int *sbuff);
77void *AHX_SetBoost(unsigned int *sbuff);
78void *AHX_ToggleOversampling(unsigned int *sbuff);
79void *AHX_Quit(unsigned int *sbuff);
80
87int _start(int argc, char *argv[])
88{
89 iop_thread_t param;
90 int th;
91
92 (void)argc;
93 (void)argv;
94
95 FlushDcache();
97 EnableIntr(40); // Enables SPU DMA (channel 1) interrupt.
98 param.attr = TH_C;
99 param.thread = AHX_Thread;
100 param.priority = 40;
101 param.stacksize = 0x800;
102 param.option = 0;
103 th = CreateThread(&param);
104 if (th > 0) {
105 StartThread(th, 0);
106 return MODULE_RESIDENT_END;
107 } else {
108 return MODULE_NO_RESIDENT_END;
109 }
110}
111
120void AHX_Thread(void *param)
121{
122 // int i;
123 iop_sema_t sema; // semaphore
124
125 (void)param;
126
127 // set sema properties and create...
128 sema.attr = 0; // SA_THFIFO;
129 sema.initial = 0;
130 sema.max = 1;
131 transfer_sema = CreateSema(&sema);
132
133 // check sema created successfully, or exit
134 if (transfer_sema <= 0) {
135#ifndef COMPACT_CODE
136 M_PRINTF("FATAL - Failed to create semaphore!\n");
137#endif
138 ExitDeleteThread();
139 }
140
141 // allocate memory for SPU2 xfer buffer
142 spubuf = AllocSysMemory(0, 0x800, NULL); // 2048 bytes (enough for 2 SPU cycles at 512 bytes per channel)
143 pcmbuf = AllocSysMemory(0, 0xF00 * 8, NULL); // enough to hold 8 full AHX-PCM decoded chunks (0xF00 per chunk)
144 if (spubuf == NULL) {
145#ifndef COMPACT_CODE
146 M_PRINTF("FATAL - Failed to allocate memory for sound buffer!\n");
147#endif
148 ExitDeleteThread();
149 }
150
151// print version #
152#ifndef COMPACT_CODE
153 M_PRINTF("AHXplayer %s Started\n", MODVERSION);
154#endif
155
156 AHX_ClearSoundBuffers();
157
158#ifndef COMPACT_CODE
159 M_PRINTF("Memory Allocated. %d bytes left.\n", QueryTotalFreeMemSize());
160#endif
161
162 // Init the RPC server
163 SifInitRpc(0);
164 SifSetRpcQueue(&qd, GetThreadId());
165 SifRegisterRpc(&Sd0, AHX_IRX, (void *)AHX_rpc_server, (void *)&buffer[0], 0, 0, &qd);
166 SifRpcLoop(&qd);
167}
168
176void AHX_PlayThread(void *param)
177{
178 int i;
179 int chunk; // we transfer to 2 blocks of SPU mem, are we working chunk 1 or 2?
180
181 (void)param;
182
183 // reset position and flags etc
184 AHX_ResetPlayThread();
185
186 // main thread loop
187 while (1) {
188 // backfill chunks that have already been passed to SPU. We do this before the WaitSema
189 // because we could be stuck there waiting for a while. If any spare time exists, we use
190 // it to mix the buffers...
191 if (playing) {
192 for (i = 0; i < 7; i++) {
193 // check if we've just passed a section of data and refill it
194 if (readpos >= 0xF00 * (i + 1) && readpos <= 0xF00 * (i + 2) && !seg_done[i]) {
195 if (playing) {
196 AHXOutput_MixBuffer((short *)(pcmbuf + (0xF00 * i))); // remix
197 }
198 seg_done[i] = 1; // mark segment as mixed and ready
199 if (i > 0)
200 seg_done[i - 1] = 0;
201 else
202 seg_done[7] = 0; // catch end segment
203 }
204 }
205 }
206
207 // wait for SPU2 to set sema to indicate it's ready for more data
208 WaitSema(transfer_sema);
209
210 // suspend interrupts so our sema trigger doesn't get fired more than once
211 // while we work in here...
212 CpuSuspendIntr(&intr_state);
213
214 if (playing) {
215 // get SPU transfer status to determine which area of SPU buffer to write PCM data to
216 chunk = 1 - (sceSdBlockTransStatus(1, 0) >> 24);
217
218 wmemcpy(spubuf + (1024 * chunk), pcmbuf + readpos, 512); // left channel
219 wmemcpy(spubuf + (1024 * chunk) + 512, pcmbuf + readpos, 512); // right channel (same cos we're mono)
220
221 // SPU2 reads 512 bytes for each channel per frame, we're using the same PCM buffer for each
222 // channel (left/right), so update read position by 512 bytes. SPU reads data like so:
223 // 512 bytes for left channel | 512 bytes for right channel | 512 for left... etc etc
224 readpos += 512;
225
226 // check to see whether we've reached the end of our PCM buffer, if so, reset read position
227 // remix end chunk of AHX data in PCM buffer and mark segment as complete
228 if (readpos >= 0xF00 * 8) {
229 readpos = 0;
230 AHXOutput_MixBuffer((short *)(pcmbuf + (0xF00 * 7)));
231 seg_done[7] = 1;
232 seg_done[6] = 0;
233 }
234 }
235
236 // re-enable interrupts
237 CpuResumeIntr(intr_state);
238 }
239}
240
245static int AHX_TransCallback(void *param)
246{
247 (void)param;
248
249 // signal our semaphore to indicate that SPU2 has finished
250 // processing current sound data and that it's time to
251 // xfer some more data across...
252 iSignalSema(transfer_sema);
253 return 1;
254}
255
261void AHX_SetVol(u16 vol)
262{
263 sceSdSetParam(SD_CORE_1 | SD_PARAM_BVOLL, vol);
264 sceSdSetParam(SD_CORE_1 | SD_PARAM_BVOLR, vol);
265}
266
271void AHX_ResetPlayThread()
272{
273 int i;
274
275 // loop through and mark all segments as not done
276 for (i = 0; i < 8; i++) {
277 seg_done[i] = 0;
278 }
279
280 // set play marker back to beginning
281 readpos = 0;
282}
283
288void AHX_ClearSoundBuffers()
289{
290 // clear sound buffer
291 memset(spubuf, 0, 0x800);
292 memset(pcmbuf, 0, 0xF00 * 8);
293}
294
301void *AHX_rpc_server(unsigned int funcno, void *data, int size)
302{
303 (void)size;
304
305 switch (funcno) {
306 case AHX_INIT:
307 return AHX_Init((unsigned *)data);
308 case AHX_PLAY:
309 return AHX_Play((unsigned *)data);
310 case AHX_PAUSE:
311 return AHX_Pause((unsigned *)data);
312 case AHX_QUIT:
313 return AHX_Quit((unsigned *)data);
314 case AHX_LOADSONG:
315 return AHX_LoadSong((unsigned *)data);
316 case AHX_SETVOLUME:
317 return AHX_SetVolume((unsigned *)data);
318 case AHX_SETBOOST:
319 return AHX_SetBoost((unsigned *)data);
320 case AHX_OVERSAMPLE:
321 return AHX_ToggleOversampling((unsigned *)data);
322 case AHX_SUBSONG:
323 return AHX_SubSong((unsigned *)data);
324 }
325 return NULL;
326}
327
333void *AHX_Init(unsigned int *sbuff)
334{
335 iop_thread_t play_thread; // play thread
336
337 // init AXH Player
338 AHXPlayer_Init();
339
340#ifndef COMPACT_CODE
341 printf("AHX INIT DONE!\n");
342#endif
343
344 // Initialise SPU
345 sceSdInit(SD_INIT_COLD);
346
347 // set left and right channel volumes
348 sceSdSetParam(SD_CORE_1 | SD_PARAM_MVOLL, 0x3fff);
349 sceSdSetParam(SD_CORE_1 | SD_PARAM_MVOLR, 0x3fff);
350
351 // Start audio streaming
352 sceSdBlockTrans(1, SD_TRANS_LOOP, spubuf, 0x800, 0);
353
354 // set SPU2 callback function pointer
355 sceSdSetTransCallback(1, (void *)AHX_TransCallback);
356
357 // Start playing thread
358 play_thread.attr = TH_C;
359 play_thread.thread = AHX_PlayThread;
360 play_thread.priority = 39;
361 play_thread.stacksize = 0x800;
362 play_thread.option = 0;
363 play_tid = CreateThread(&play_thread);
364 if (play_tid > 0)
365 StartThread(play_tid, 0);
366 else {
367#ifndef COMPACT_CODE
368 M_PRINTF("FATAL - Failed to start playing thread!\n");
369#endif
370 ExitDeleteThread();
371 }
372
373 // volume up
374 AHX_SetVol(0x3fff);
375
376 // send out addresses for EE RPC to write data to
377 sbuff[1] = (unsigned)mod_buffer;
378
379 return sbuff;
380}
381
386void *AHX_LoadSong(unsigned int *sbuff)
387{
388 // if we're playing, stop
389 if (playing) {
390 AHX_Pause(sbuff);
391 }
392
393 // reset playing params
394 AHX_ResetPlayThread();
395
396#ifndef COMPACT_CODE
397 printf("Loading song - oversampling = %d, boost = %d\n", oversample_enabled, boost_val);
398#endif
399
400 // load song
401 sbuff[1] = (unsigned)AHXPlayer_LoadSongBuffer(mod_buffer, (int)sbuff[0]);
402
403 // wait for SPU
404 WaitSema(transfer_sema);
405
406 AHX_ClearSoundBuffers();
407
408 // init first sub-song
409 AHXPlayer_InitSubsong(0);
410
411 AHX_Play(sbuff);
412
413#ifndef COMPACT_CODE
414 M_PRINTF("Memory Allocated. %d bytes left.\n", QueryTotalFreeMemSize());
415#endif
416
417 return sbuff; // return number of subsongs
418}
419
424void *AHX_Play(unsigned int *sbuff)
425{
426 AHX_SetVol(SPU2_Volume);
427 playing = 1;
428 return sbuff;
429}
430
435void *AHX_Pause(unsigned int *sbuff)
436{
437 AHX_SetVol(0);
438 playing = 0;
439 return sbuff;
440}
441
446void *AHX_SetVolume(unsigned int *sbuff)
447{
448 SPU2_Volume = (u16)(0x3fff * (u16)sbuff[1]) / 100;
449 AHX_SetVol(SPU2_Volume);
450 return sbuff;
451}
452
459void *AHX_SetBoost(unsigned int *sbuff)
460{
461 boost_val = (int)sbuff[0];
462 AHXPlayer_SetBoost(boost_val);
463 return sbuff;
464}
465
472void *AHX_ToggleOversampling(unsigned int *sbuff)
473{
474 oversample_enabled = oversample_enabled ? 0 : 1;
475 AHXPlayer_SetOversampling(oversample_enabled);
476 return sbuff;
477}
478
485void *AHX_SubSong(unsigned int *sbuff)
486{
487 AHXPlayer_InitSubsong((int)sbuff[0]);
488 return sbuff;
489}
490
495void *AHX_Quit(unsigned int *sbuff)
496{
497 sceSdSetTransCallback(1, NULL);
498 sceSdBlockTrans(1, SD_TRANS_STOP, 0, 0, 0);
499
500 TerminateThread(play_tid);
501 DeleteThread(play_tid);
502
503 DeleteSema(transfer_sema);
504 return sbuff;
505}
int AHX_SetBoost(int boostValue)
Definition ahx_rpc.c:132
int AHX_Init()
Definition ahx_rpc.c:48
int AHX_Play()
Definition ahx_rpc.c:74
int AHX_SetVolume(int volumePercentage)
Definition ahx_rpc.c:117
int AHX_SubSong(int songNo)
Definition ahx_rpc.c:102
int AHX_ToggleOversampling()
Definition ahx_rpc.c:147
int AHX_Quit()
Definition ahx_rpc.c:161
int AHX_Pause()
Definition ahx_rpc.c:88
int AHX_LoadSong(char *filename)
Definition ahx_rpc.c:199
static void play_thread(void *arg)
Definition audsrv.c:429
int CpuEnableIntr()
Definition intrman.c:250
int CpuResumeIntr(int state)
Definition intrman.c:227
int CpuSuspendIntr(int *state)
Definition intrman.c:205
int EnableIntr(int irq)
Definition intrman.c:346