rev |
line source |
paulo@0
|
1 #include <string.h>
|
paulo@0
|
2 #include "ljgba.h"
|
paulo@0
|
3 #include "pin8gba_sound.h"
|
paulo@0
|
4 extern const unsigned short note_freqs[];
|
paulo@0
|
5
|
paulo@0
|
6 static const u16 moveEffect[] = {
|
paulo@0
|
7 0, 3,
|
paulo@0
|
8 0x516D, 0x0000, 0x0100
|
paulo@0
|
9 };
|
paulo@0
|
10
|
paulo@0
|
11 static const u16 rotateEffect[] = {
|
paulo@0
|
12 1, 7,
|
paulo@0
|
13 0x5161, 0x0000, 0x5166, 0x0000, 0x516b, 0x0000, 0x0100
|
paulo@0
|
14 };
|
paulo@0
|
15
|
paulo@0
|
16 static const u16 landEffect[] = {
|
paulo@0
|
17 1, 6,
|
paulo@0
|
18 0x8188, 0x7185, 0x6182, 0x5181, 0x3180, 0x0000
|
paulo@0
|
19 };
|
paulo@0
|
20
|
paulo@0
|
21 static const u16 lockEffect[] = {
|
paulo@0
|
22 3, 1,
|
paulo@0
|
23 0x2138
|
paulo@0
|
24 };
|
paulo@0
|
25
|
paulo@0
|
26 static const u16 lineEffect[] = {
|
paulo@0
|
27 0, 12,
|
paulo@0
|
28 0xB1A4, 0xA1A6, 0x91A9, 0x81A4, 0x71A7, 0x61A9,
|
paulo@0
|
29 0x61A4, 0x51A6, 0x41A9, 0x31A4, 0x21A7, 0x11A9
|
paulo@0
|
30 };
|
paulo@0
|
31
|
paulo@0
|
32 static const u16 holdEffect[] = {
|
paulo@0
|
33 3, 8,
|
paulo@0
|
34 0x3030, 0x0000, 0x4032, 0x0000, 0x3130, 0x0000, 0x202c, 0x2128
|
paulo@0
|
35 };
|
paulo@0
|
36
|
paulo@0
|
37 static const u16 irsRotateEffect[] = {
|
paulo@0
|
38 1, 12,
|
paulo@0
|
39 0x5161, 0x51B1, 0x5166, 0x51B6, 0x516B, 0x51BB, 0x0000, 0x0000,
|
paulo@0
|
40 0x0000, 0x0000, 0x0000, 0x0100
|
paulo@0
|
41 };
|
paulo@0
|
42
|
paulo@0
|
43 static const u16 homerEffect[] = {
|
paulo@0
|
44 0, 22,
|
paulo@0
|
45 0xB1A4, 0xA1A6, 0x91A9, 0x81A4, 0x71A7, 0x61A9,
|
paulo@0
|
46 0x61A4, 0x51A6, 0x41A9, 0x31A4,
|
paulo@0
|
47 0xB1A7, 0xA1A9, 0x91AC, 0x81A7, 0x71AA, 0x61AC,
|
paulo@0
|
48 0x51A7, 0x41A9, 0x31AC, 0x21A7, 0x11AA, 0x01AC
|
paulo@0
|
49 };
|
paulo@0
|
50
|
paulo@0
|
51 static const u16 streakEffect[] = {
|
paulo@0
|
52 0, 30,
|
paulo@0
|
53 0xB1A4, 0xA1A6, 0x91A9, 0x81A4, 0x71A7, 0x61A9,
|
paulo@0
|
54 0x61A4, 0x51A6, 0x41A9, 0x31A4,
|
paulo@0
|
55 0xB1A7, 0xA1A9, 0x91AC, 0x81A7, 0x71AA, 0x61AC,
|
paulo@0
|
56 0x51A7, 0x41A9, 0x31AC, 0x21A7,
|
paulo@0
|
57 0xB1AA, 0xA1AC, 0x91AF, 0x71AA, 0x61AD, 0x51AF,
|
paulo@0
|
58 0x31AA, 0x21AC, 0x11AF, 0x01AA
|
paulo@0
|
59 };
|
paulo@0
|
60
|
paulo@0
|
61 static const u16 sectionEffect[] = {
|
paulo@0
|
62 1, 30,
|
paulo@0
|
63 0xB1B0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
64 0xB1B4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
65 0xB1B7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
66 0xB1BC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
67 0xB1B0, 0x0000
|
paulo@0
|
68 };
|
paulo@0
|
69
|
paulo@0
|
70 static const u16 gameOverEffect1[] = {
|
paulo@0
|
71 1, 40,
|
paulo@0
|
72 0xF504, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
73 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
74 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
75 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
76 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
paulo@0
|
77 };
|
paulo@0
|
78
|
paulo@0
|
79 static const u16 gameOverEffect2[] = {
|
paulo@0
|
80 3, 40,
|
paulo@0
|
81 0xC12C, 0xA725, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
82 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
83 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
84 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
85 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
paulo@0
|
86 };
|
paulo@0
|
87
|
paulo@0
|
88 static const u16 winEffect1[] = {
|
paulo@0
|
89 1, 30,
|
paulo@0
|
90 0xA564, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
91 0xA562, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
92 0xA564, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
93 0xA566, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
94 0x0000, 0x0000
|
paulo@0
|
95 };
|
paulo@0
|
96
|
paulo@0
|
97 static const u16 winEffect2[] = {
|
paulo@0
|
98 0, 30,
|
paulo@0
|
99 0xA568, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
100 0xA566, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
101 0xA568, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
102 0xA56A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
paulo@0
|
103 0x0000, 0x0000
|
paulo@0
|
104 };
|
paulo@0
|
105
|
paulo@0
|
106
|
paulo@0
|
107 static const u16 *const effects[] = {
|
paulo@0
|
108 moveEffect, rotateEffect, landEffect, lockEffect, lineEffect,
|
paulo@0
|
109 holdEffect, irsRotateEffect, homerEffect, streakEffect, sectionEffect,
|
paulo@0
|
110 gameOverEffect1, gameOverEffect2, winEffect1, winEffect2
|
paulo@0
|
111 };
|
paulo@0
|
112
|
paulo@0
|
113 const unsigned short tri_vol_table[5] = {
|
paulo@0
|
114 0,
|
paulo@0
|
115 TRILENVOL_25,
|
paulo@0
|
116 TRILENVOL_50,
|
paulo@0
|
117 TRILENVOL_75,
|
paulo@0
|
118 TRILENVOL_100
|
paulo@0
|
119 };
|
paulo@0
|
120
|
paulo@0
|
121 void play_note(unsigned int ch, unsigned int note, unsigned int instrument) {
|
paulo@0
|
122 switch (ch) {
|
paulo@0
|
123 case 0:
|
paulo@0
|
124 SQR1CTRL = instrument;
|
paulo@0
|
125 SQR1FREQ = note_freqs[note] | FREQ_RESET;
|
paulo@0
|
126 break;
|
paulo@0
|
127 case 1:
|
paulo@0
|
128 SQR2CTRL = instrument;
|
paulo@0
|
129 SQR2FREQ = note_freqs[note] | FREQ_RESET;
|
paulo@0
|
130 break;
|
paulo@0
|
131 case 2:
|
paulo@0
|
132 {
|
paulo@0
|
133 int vol = instrument >> 13;
|
paulo@0
|
134 TRILENVOL = tri_vol_table[(vol + 1) / 2];
|
paulo@0
|
135 TRIFREQ = note_freqs[note] | FREQ_RESET;
|
paulo@0
|
136 }
|
paulo@0
|
137 break;
|
paulo@0
|
138 case 3:
|
paulo@0
|
139 NOISECTRL = instrument;
|
paulo@0
|
140 {
|
paulo@0
|
141 int octave = (note & 0x3E) << 2;
|
paulo@0
|
142 int pitch = note & 0x03;
|
paulo@0
|
143 NOISEFREQ = (octave | pitch) ^ (0xF7 | FREQ_RESET);
|
paulo@0
|
144 }
|
paulo@0
|
145 break;
|
paulo@0
|
146 }
|
paulo@0
|
147 }
|
paulo@0
|
148
|
paulo@0
|
149 void gba_poll_sound(struct LJPCView *v) {
|
paulo@0
|
150 for (int i = 0; i < 4; ++i) {
|
paulo@0
|
151 if (v->sndLeft[i]) {
|
paulo@0
|
152 unsigned int data = *v->sndData[i];
|
paulo@0
|
153 if (data) {
|
paulo@0
|
154 play_note(i, data & 0x003F, data & 0xFFC0);
|
paulo@0
|
155 }
|
paulo@0
|
156 ++v->sndData[i];
|
paulo@0
|
157 --v->sndLeft[i];
|
paulo@0
|
158 }
|
paulo@0
|
159 }
|
paulo@0
|
160 }
|
paulo@0
|
161
|
paulo@0
|
162 void gba_play_sound(struct LJPCView *v, int effect) {
|
paulo@0
|
163 const u16 *data = effects[effect];
|
paulo@0
|
164 int ch = data[0];
|
paulo@0
|
165 int len = data[1];
|
paulo@0
|
166
|
paulo@0
|
167 // square waves can be played on either channel 0 or 1;
|
paulo@0
|
168 // switch if this channel is occupied and the other is free
|
paulo@0
|
169 if (ch < 2
|
paulo@0
|
170 && v->sndLeft[ch] > v->sndLeft[1 - ch]) {
|
paulo@0
|
171 ch = 1 - ch;
|
paulo@0
|
172 }
|
paulo@0
|
173 v->sndData[ch] = data + 2;
|
paulo@0
|
174 v->sndLeft[ch] = len;
|
paulo@0
|
175 }
|
paulo@0
|
176
|
paulo@0
|
177 /**
|
paulo@0
|
178 * Sets the sound bias to mid and resolution to 8-bit.
|
paulo@0
|
179 * Setting bias is needed because some launchers (such as pogoshell)
|
paulo@0
|
180 * set it to a state that mutes the tone generators.
|
paulo@0
|
181 */
|
paulo@0
|
182 static void set_bias(void)
|
paulo@0
|
183 {
|
paulo@0
|
184 asm volatile("mov r2, #2; lsl r2, #8; swi 0x19" ::: "r0", "r1", "r2", "r3");
|
paulo@0
|
185 SETSNDRES(1);
|
paulo@0
|
186 }
|
paulo@0
|
187
|
paulo@0
|
188 __attribute__((aligned(4)))
|
paulo@0
|
189 static const unsigned char triangleWave[16] = {
|
paulo@0
|
190 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
|
paulo@0
|
191 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10
|
paulo@0
|
192 };
|
paulo@0
|
193 unsigned char lastWaveBank = 0;
|
paulo@0
|
194
|
paulo@0
|
195 void tri_set_waveform(const void *waveform) {
|
paulo@0
|
196 memcpy((void *)TRIWAVERAM, waveform, 16);
|
paulo@0
|
197 lastWaveBank = !lastWaveBank;
|
paulo@0
|
198 TRICTRL = TRICTRL_2X32 | TRICTRL_BANK(lastWaveBank) | TRICTRL_ENABLE;
|
paulo@0
|
199 }
|
paulo@0
|
200
|
paulo@0
|
201 void install_sound(struct LJPCView *v) {
|
paulo@0
|
202 SNDSTAT = SNDSTAT_ENABLE;
|
paulo@0
|
203 DMGSNDCTRL = DMGSNDCTRL_LVOL(7) | DMGSNDCTRL_RVOL(7)
|
paulo@0
|
204 | DMGSNDCTRL_LSQR1 | DMGSNDCTRL_RSQR1
|
paulo@0
|
205 | DMGSNDCTRL_LSQR2 | DMGSNDCTRL_RSQR2
|
paulo@0
|
206 | DMGSNDCTRL_LTRI | DMGSNDCTRL_RTRI
|
paulo@0
|
207 | DMGSNDCTRL_LNOISE | DMGSNDCTRL_RNOISE;
|
paulo@0
|
208 DSOUNDCTRL = DSOUNDCTRL_DMG100;
|
paulo@0
|
209 set_bias();
|
paulo@0
|
210 SQR1SWEEP = SQR1SWEEP_OFF;
|
paulo@0
|
211
|
paulo@0
|
212 #if 0
|
paulo@0
|
213 TRICTRL = TRICTRL_2X32 | TRICTRL_BANK(0) | TRICTRL_ENABLE;
|
paulo@0
|
214 tri_set_waveform(triangleWave);
|
paulo@0
|
215 TRILENVOL = 0;
|
paulo@0
|
216 TRIFREQ = 1024 | FREQ_RESET;
|
paulo@0
|
217 #endif
|
paulo@0
|
218
|
paulo@0
|
219 v->sndLeft[0] = 0;
|
paulo@0
|
220 v->sndLeft[1] = 0;
|
paulo@0
|
221 v->sndLeft[2] = 0;
|
paulo@0
|
222 v->sndLeft[3] = 0;
|
paulo@0
|
223 }
|
paulo@0
|
224
|