145 lines4.3 KBC
1/*
2audio_ps3.c: the UI's sound effects, through cellAudio.
3
4Three short sounds, made here (no files): a soft tick for moving, a rising
5two-note blip for Cross, a falling one for Circle. A thread keeps the audio
6port's ring of blocks filled, mixing whatever's playing; with nothing playing
7it fills silence. Quiet, so the XMB's own music isn't drowned.
8*/
9#include <stdlib.h>
10#include <unistd.h>
11#include <string.h>
12#include <math.h>
13#include <ppu-types.h>
14#include <audio/audio.h>
15#include <sys/event_queue.h>
16#include <sysmodule/sysmodule.h>
17#include "../plat.h"
18
19#define RATE 48000
20#define VOICES 4
21
22static float *pcm[SND_COUNT]; // mono, RATE a second
23static int pcm_len[SND_COUNT];
24static volatile int asked[SND_COUNT]; // counted up by plat_sound; the thread plays one per step
25static volatile int on = 1, running;
26static u32 port;
27static sys_event_queue_t queue;
28static sys_ipc_key_t key;
29
30// a short tone gliding from f0 to f1 Hz, with a soft attack and an exponential fade
31static void Make(int id, float ms, float f0, float f1, float level)
32{
33 int n = (int)(RATE * ms / 1000);
34 float *p = malloc(n * sizeof(float)), ph = 0;
35 if (!p) return;
36 for (int i = 0; i < n; i++) {
37 float t = (float)i / n, f = f0 + (f1 - f0) * t;
38 float env = (i < RATE / 1000 ? i / (RATE / 1000.0f) : 1.0f) * expf(-5.0f * t);
39 ph += 2 * (float)M_PI * f / RATE;
40 p[i] = sinf(ph) * env * level;
41 }
42 pcm[id] = p;
43 pcm_len[id] = n;
44}
45
46typedef struct { int id, pos; } voice_t;
47
48static void Thread(void *arg)
49{
50 (void)arg;
51 audioPortConfig cfg;
52 audioGetPortConfig(port, &cfg);
53 volatile u64 *read_index = (volatile u64 *)(u64)cfg.readIndex;
54 float *ring = (float *)(u64)cfg.audioDataStart;
55 int blocks = (int)cfg.numBlocks, next = 1, seen[SND_COUNT] = {0};
56 voice_t v[VOICES];
57 memset(v, 0, sizeof(v));
58 for (int i = 0; i < VOICES; i++) v[i].id = -1;
59 while (running) {
60 sys_event_t ev;
61 sysEventQueueReceive(queue, &ev, 20 * 1000);
62 // new sounds asked for
63 for (int s = 0; s < SND_COUNT; s++)
64 while (seen[s] != asked[s]) {
65 seen[s]++;
66 if (!on || !pcm[s]) continue;
67 int k = 0;
68 for (int i = 1; i < VOICES; i++) // a free voice, or the one furthest along
69 if (v[i].id < 0 || (v[k].id >= 0 && v[i].pos > v[k].pos)) k = i;
70 v[k].id = s;
71 v[k].pos = 0;
72 }
73 // every block the port isn't playing yet, up to the one it is
74 while (next != (int)(*read_index % blocks)) {
75 float *out = ring + (size_t)next * AUDIO_BLOCK_SAMPLES * 2;
76 for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
77 float m = 0;
78 for (int k = 0; k < VOICES; k++) {
79 if (v[k].id < 0) continue;
80 m += pcm[v[k].id][v[k].pos++];
81 if (v[k].pos >= pcm_len[v[k].id]) v[k].id = -1;
82 }
83 out[i * 2] = out[i * 2 + 1] = m;
84 }
85 next = (next + 1) % blocks;
86 }
87 }
88}
89
90int plat_sound_init(void)
91{
92 Make(SND_MOVE, 28, 1500, 1350, 0.05f);
93 Make(SND_OK, 70, 660, 990, 0.08f);
94 Make(SND_BACK, 70, 880, 560, 0.07f);
95 audioPortParam param;
96 memset(&param, 0, sizeof(param));
97 param.numChannels = AUDIO_PORT_2CH;
98 param.numBlocks = AUDIO_BLOCK_8;
99 param.level = 1.0f;
100 s32 r;
101 if ((r = sysModuleLoad(SYSMODULE_AUDIO)) != 0 && r != 0x80012003) // (already loaded is fine)
102 plat_log("sound: sysModuleLoad = 0x%08x", (unsigned)r);
103 if ((r = audioInit()) != 0 && r != (s32)0x80310701) { // (already initialised)
104 plat_log("sound: audioInit = 0x%08x: no sound effects", (unsigned)r);
105 return 0;
106 }
107 if ((r = audioPortOpen(&param, &port)) != 0) {
108 plat_log("sound: audioPortOpen = 0x%08x: no sound effects", (unsigned)r);
109 return 0;
110 }
111 if ((r = audioCreateNotifyEventQueue(&queue, &key)) != 0 || (r = audioSetNotifyEventQueue(key)) != 0) {
112 plat_log("sound: event queue = 0x%08x: no sound effects", (unsigned)r);
113 audioPortClose(port);
114 return 0;
115 }
116 audioPortStart(port);
117 running = 1;
118 if (!plat_thread_start(Thread, NULL, 900, 64, "sound")) { // (above the UI: a late block is a click)
119 running = 0;
120 audioPortStop(port);
121 audioPortClose(port);
122 return 0;
123 }
124 plat_log("sound: on");
125 return 1;
126}
127
128void plat_sound(int id)
129{
130 if (id >= 0 && id < SND_COUNT && running && on) asked[id]++;
131}
132
133void plat_sound_enable(int enable) { on = enable; }
134
135void plat_sound_quit(void)
136{
137 if (!running) return;
138 running = 0;
139 usleep(60 * 1000); // (the thread's last wait)
140 audioPortStop(port);
141 audioRemoveNotifyEventQueue(key);
142 sysEventQueueDestroy(queue, 0);
143 audioPortClose(port);
144 audioQuit();
145}