diff src/dssound7.c @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/dssound7.c	Fri Mar 13 00:39:12 2009 -0700
     1.3 @@ -0,0 +1,429 @@
     1.4 +/*---------------------------------------------------------------------------------
     1.5 +
     1.6 +	LOCKJAW Tetromino Game: ARM7 sound code
     1.7 +
     1.8 +	based on
     1.9 +	default ARM7 core
    1.10 +
    1.11 +	Copyright (C) 2007
    1.12 +		Damian Yerrick (tepples)
    1.13 +
    1.14 +	This software is provided 'as-is', without any express or implied
    1.15 +	warranty.  In no event will the authors be held liable for any
    1.16 +	damages arising from the use of this software.
    1.17 +
    1.18 +	Permission is granted to anyone to use this software for any
    1.19 +	purpose, including commercial applications, and to alter it and
    1.20 +	redistribute it freely, subject to the following restrictions:
    1.21 +
    1.22 +	1.	The origin of this software must not be misrepresented; you
    1.23 +		must not claim that you wrote the original software. If you use
    1.24 +		this software in a product, an acknowledgment in the product
    1.25 +		documentation would be appreciated but is not required.
    1.26 +	2.	Altered source versions must be plainly marked as such, and
    1.27 +		must not be misrepresented as being the original software.
    1.28 +	3.	This notice may not be removed or altered from any source
    1.29 +		distribution.
    1.30 +
    1.31 +---------------------------------------------------------------------------------*/
    1.32 +#include <nds.h>
    1.33 +#include <sys/types.h>
    1.34 +
    1.35 +extern const unsigned short midi2psgFreq[120];
    1.36 +
    1.37 +#define C(octave)  (octave * 12 +  0)
    1.38 +#define CS(octave) (octave * 12 +  1)
    1.39 +#define D(octave)  (octave * 12 +  2)
    1.40 +#define DS(octave) (octave * 12 +  3)
    1.41 +#define E(octave)  (octave * 12 +  4)
    1.42 +#define F(octave)  (octave * 12 +  5)
    1.43 +#define FS(octave) (octave * 12 +  6)
    1.44 +#define G(octave)  (octave * 12 +  7)
    1.45 +#define GS(octave) (octave * 12 +  8)
    1.46 +#define A(octave)  (octave * 12 +  9)
    1.47 +#define AS(octave) (octave * 12 + 10)
    1.48 +#define B(octave)  (octave * 12 + 11)
    1.49 +
    1.50 +
    1.51 +#define N_MUSIC_CHANNELS 2
    1.52 +
    1.53 +static unsigned short chNote[N_MUSIC_CHANNELS];
    1.54 +static unsigned char chVol[N_MUSIC_CHANNELS];
    1.55 +
    1.56 +
    1.57 +static const unsigned char startVol[N_MUSIC_CHANNELS] = {32, 30};
    1.58 +static const unsigned char fade[N_MUSIC_CHANNELS] = {0, 1};
    1.59 +static const unsigned char chDuty[N_MUSIC_CHANNELS] = {0, 3};
    1.60 +
    1.61 +static const signed char bassLine[64] = {
    1.62 +  A(2), 0, A(3), 0, -1, 0, A(2), 0,
    1.63 +  A(3), 0, -1, 0, 0, 0, 0, 0,
    1.64 +  0, 0, 0, 0, 0, 0, 0, 0,
    1.65 +  C(3), 0, C(4), 0, G(2), 0, G(3), 0,
    1.66 +  A(2), 0, A(3), 0, -1, 0, A(2), 0,
    1.67 +  A(3), 0, -1, 0, 0, 0, 0, 0,
    1.68 +  0, 0, 0, 0, 0, 0, 0, 0,
    1.69 +  C(3), 0, C(4), 0, G(2), 0, G(3), 0,
    1.70 +};
    1.71 +
    1.72 +static const signed char mel1[64] = {
    1.73 +  E(6), 0, 0, 0, B(5), 0, C(6), 0,
    1.74 +  D(6), 0, 0, 0, C(6), 0, B(5), 0,
    1.75 +  A(5), 0, 0, 0, A(5), 0, C(6), 0,
    1.76 +  E(6), 0, 0, 0, D(6), 0, C(6), 0,
    1.77 +  B(5), 0, 0, 0, 0, 0, C(6), 0,
    1.78 +  D(6), 0, 0, 0, E(6), 0, 0, 0,
    1.79 +  C(6), 0, 0, 0, A(5), 0, 0, 0,
    1.80 +  A(5)
    1.81 +};
    1.82 +
    1.83 +static const signed char mel2[64] = {
    1.84 +  0, 0, D(6), 0, 0, 0, F(6), 0,
    1.85 +  A(6), 0, 0, 0, G(6), 0, F(6), 0,
    1.86 +  E(6), 0, 0, 0, 0, 0, C(6), 0,
    1.87 +  E(6), 0, 0, 0, D(6), 0, C(6), 0,
    1.88 +  B(5), 0, 0, 0, B(5), 0, C(6), 0,
    1.89 +  D(6), 0, 0, 0, E(6), 0, 0, 0,
    1.90 +  C(6), 0, 0, 0, A(5), 0, 0, 0,
    1.91 +  A(5)
    1.92 +};
    1.93 +
    1.94 +static const signed char *const scripts[4][N_MUSIC_CHANNELS] = {
    1.95 +  { bassLine, mel1 },
    1.96 +  { bassLine, mel1 },
    1.97 +  { bassLine, mel2 },
    1.98 +  { bassLine, mel2 }
    1.99 +};
   1.100 +
   1.101 +int order = 0;
   1.102 +char ticksLeft = 1;
   1.103 +char row = 0;
   1.104 +char musicPaused = 1;
   1.105 +
   1.106 +/*
   1.107 + * Plays a repeating drum track and bass track.
   1.108 + */
   1.109 +void ljMusic(void) {
   1.110 +  if (musicPaused) {
   1.111 +    for (int ch = 0; ch < N_MUSIC_CHANNELS; ++ch) {
   1.112 +      chVol[ch] = 0;
   1.113 +    }
   1.114 +  } else if (--ticksLeft <= 0) {
   1.115 +    ticksLeft += 6;
   1.116 +    for (int ch = 0; ch < N_MUSIC_CHANNELS; ++ch) {
   1.117 +      const signed char *script = scripts[order][ch];
   1.118 +      int note = script ? script[(int)row] : 0;
   1.119 +      if (note < 0) {
   1.120 +        chVol[ch] = 0;
   1.121 +      } else if (note > 0) {
   1.122 +        chVol[ch] = startVol[ch];
   1.123 +        chNote[ch] = midi2psgFreq[note];
   1.124 +      }
   1.125 +    }
   1.126 +    ++row;
   1.127 +    if (row >= 64) {
   1.128 +      row = 0;
   1.129 +      ++order;
   1.130 +      if (order == 4) {
   1.131 +        order = 0;
   1.132 +      }
   1.133 +    }
   1.134 +  }
   1.135 +  
   1.136 +  
   1.137 +  // Play notes
   1.138 +  for (int ch = 0; ch < N_MUSIC_CHANNELS; ++ch) {
   1.139 +    SCHANNEL_CR(8 + ch) = SCHANNEL_ENABLE | SOUND_FORMAT_PSG | SOUND_PAN(64)
   1.140 +                   | (chDuty[ch] << 24) | chVol[ch];
   1.141 +    SCHANNEL_TIMER(8 + ch) = chNote[ch];
   1.142 +    
   1.143 +    // update volumes
   1.144 +    if (chVol[ch] > fade[ch]) {
   1.145 +      chVol[ch] -= fade[ch];
   1.146 +    } else {
   1.147 +      chVol[ch] = 0;
   1.148 +    }
   1.149 +  }
   1.150 +}
   1.151 +
   1.152 +#define FX(note, volume, duty) ((note) | ((volume) << 7) | ((duty) << 14))
   1.153 +
   1.154 +static const u16 rotateEffect[] = {
   1.155 +  0, 6,
   1.156 +  FX(A(5), 32, 1), FX(A(5), 8, 1),
   1.157 +  FX(D(6), 32, 1), FX(D(6), 8, 1),
   1.158 +  FX(G(6), 32, 1), FX(G(6), 8, 1)
   1.159 +};
   1.160 +
   1.161 +static const u16 moveEffect[] = {
   1.162 +  0, 2,
   1.163 +  FX(A(6), 32, 1), FX(A(6), 8, 1)
   1.164 +};
   1.165 +
   1.166 +static const u16 landEffect[] = {
   1.167 +  0, 8,
   1.168 +  FX(C(4), 64, 3), FX(GS(3), 56, 3),
   1.169 +  FX(F(3), 48, 3), FX(D(3), 40, 3),
   1.170 +  FX(C(3), 32, 3), FX(B(2), 24, 3),
   1.171 +  FX(C(3), 16, 3), FX(B(2), 8, 3),
   1.172 +};
   1.173 +
   1.174 +static const u16 lockEffect[] = {
   1.175 +  1, 2,
   1.176 +  FX(C(8), 16, 1), FX(C(8), 4, 1)
   1.177 +};
   1.178 +
   1.179 +static const u16 lineEffect[] = {
   1.180 +  0, 15,
   1.181 +  FX(C(6), 64, 2), FX(DS(6), 59, 2), FX(F(6), 54, 2),
   1.182 +  FX(C(6), 49, 2), FX(D(6), 44, 2), FX(F(6), 39, 2),
   1.183 +  FX(C(6), 34, 3), FX(DS(6), 29, 3), FX(F(6), 24, 3),
   1.184 +  FX(C(6), 20, 3), FX(D(6), 16, 3), FX(F(6), 12, 3),
   1.185 +  FX(C(6), 9, 3), FX(DS(6), 6, 3), FX(F(6), 3, 3)
   1.186 +};
   1.187 +
   1.188 +static const u16 homerEffect[] = {
   1.189 +  0, 25,
   1.190 +  FX(C(6), 64, 2), FX(DS(6), 59, 2), FX(F(6), 54, 2),
   1.191 +  FX(C(6), 49, 2), FX(D(6), 44, 2), FX(F(6), 39, 2),
   1.192 +  FX(C(6), 34, 3), FX(DS(6), 29, 3), FX(F(6), 24, 3),
   1.193 +  FX(C(6), 20, 3),
   1.194 +  FX(DS(6), 64, 2), FX(FS(6), 59, 2), FX(GS(6), 54, 2),
   1.195 +  FX(DS(6), 49, 2), FX(F(6), 44, 2), FX(GS(6), 39, 2),
   1.196 +  FX(DS(6), 34, 3), FX(FS(6), 29, 3), FX(GS(6), 24, 3),
   1.197 +  FX(DS(6), 20, 3), FX(F(6), 16, 3), FX(GS(6), 12, 3),
   1.198 +  FX(DS(6), 9, 3), FX(F(6), 6, 3), FX(GS(6), 3, 3)
   1.199 +};
   1.200 +
   1.201 +static const u16 streakEffect[] = {
   1.202 +  0, 35,
   1.203 +  FX(C(6), 64, 2), FX(DS(6), 59, 2), FX(F(6), 54, 2),
   1.204 +  FX(C(6), 49, 2), FX(D(6), 44, 2), FX(F(6), 39, 2),
   1.205 +  FX(C(6), 34, 3), FX(DS(6), 29, 3), FX(F(6), 24, 3),
   1.206 +  FX(C(6), 20, 3),
   1.207 +  FX(DS(6), 64, 2), FX(FS(6), 59, 2), FX(GS(6), 54, 2),
   1.208 +  FX(DS(6), 49, 2), FX(F(6), 44, 2), FX(GS(6), 39, 2),
   1.209 +  FX(DS(6), 34, 3), FX(FS(6), 29, 3), FX(GS(6), 24, 3),
   1.210 +  FX(DS(6), 20, 3),
   1.211 +  FX(FS(6), 64, 2), FX(A(6), 59, 2), FX(B(6), 54, 2),
   1.212 +  FX(FS(6), 49, 2), FX(GS(6), 44, 2), FX(B(6), 39, 2),
   1.213 +  FX(FS(6), 34, 3), FX(A(6), 29, 3), FX(B(6), 24, 3),
   1.214 +  FX(FS(6), 20, 3), FX(GS(6), 16, 3), FX(B(6), 12, 3),
   1.215 +  FX(FS(6), 9, 3), FX(GS(6), 6, 3), FX(B(6), 3, 3)
   1.216 +};
   1.217 +
   1.218 +static const u16 holdEffect[] = {
   1.219 +  1, 10,
   1.220 +  FX(E(5), 20, 0), FX(G(5), 25, 0),
   1.221 +  FX(B(5), 30, 0), FX(B(5), 30, 0),
   1.222 +  FX(G(5), 28, 0), FX(E(5), 24, 0),
   1.223 +  FX(D(5), 20, 0), FX(C(5), 15, 0),
   1.224 +  FX(C(5), 10, 0), FX(C(5), 5, 0)
   1.225 +};
   1.226 +
   1.227 +
   1.228 +static const u16 irsEffect[] = {
   1.229 +  0, 12,
   1.230 +  FX(CS(7), 40, 3), FX(CS(7), 40, 3),
   1.231 +  FX(FS(7), 40, 3), FX(FS(7), 40, 3),
   1.232 +  FX(B(7), 40, 3), FX(B(7), 35, 3),
   1.233 +  FX(B(7), 30, 3), FX(B(7), 25, 3),
   1.234 +  FX(B(7), 20, 3), FX(B(7), 15, 3),
   1.235 +  FX(B(7), 10, 3), FX(B(7), 5, 3),
   1.236 +};
   1.237 +
   1.238 +static const u16 countEffect[] = {
   1.239 +  0, 14,
   1.240 +  FX(D(5), 64, 0), FX(D(5), 64, 0),
   1.241 +  FX(CS(5), 64, 0), FX(CS(5), 64, 0),
   1.242 +  FX(C(5), 64, 1), FX(C(5), 64, 1),
   1.243 +  FX(B(4), 64, 1), FX(AS(4), 64, 1),
   1.244 +  FX(A(4), 64, 2), FX(GS(4), 64, 2),
   1.245 +  FX(GS(4), 64, 3), FX(G(4), 48, 3),
   1.246 +  FX(G(4), 32, 3), FX(G(4), 16, 3),
   1.247 +};
   1.248 +
   1.249 +static const u16 winEffect1[] = {
   1.250 +  0, 54,
   1.251 +  FX(C(6), 32, 2), FX(C(6), 48, 1), FX(C(6), 48, 1), FX(C(6), 48, 1),
   1.252 +  FX(C(6), 32, 2), 0, 0, 0, 0,
   1.253 +  FX(AS(5), 32, 2), FX(AS(5), 48, 1), FX(AS(5), 48, 1), FX(AS(5), 48, 1),
   1.254 +  FX(AS(5), 32, 2), 0, 0, 0, 0,
   1.255 +  FX(C(6), 32, 2), FX(C(6), 48, 1), FX(C(6), 48, 1), FX(C(6), 48, 1),
   1.256 +  FX(D(6), 32, 2), 0, 0, 0, 0,
   1.257 +
   1.258 +  FX(D(6), 32, 2), FX(D(6), 48, 1), FX(D(6), 48, 1), FX(D(6), 48, 1),
   1.259 +  FX(D(6), 47, 1), FX(D(6), 46, 1), FX(D(6), 45, 1), FX(D(6), 44, 1),
   1.260 +  FX(D(6), 43, 1), FX(D(6), 42, 1), FX(D(6), 41, 1), FX(D(6), 40, 1),
   1.261 +  FX(D(6), 39, 1), FX(D(6), 38, 1), FX(D(6), 37, 1), FX(D(6), 36, 1),
   1.262 +  FX(D(6), 35, 1), FX(D(6), 34, 1), FX(D(6), 33, 1), FX(D(6), 32, 1),
   1.263 +  FX(D(6), 31, 1), FX(D(6), 30, 1), FX(D(6), 29, 1), FX(D(6), 28, 1),
   1.264 +  FX(D(6), 27, 1), FX(D(6), 26, 1), FX(D(6), 25, 1), FX(D(6), 24, 1),
   1.265 +  FX(D(6), 18, 2), FX(D(6), 12, 2), FX(D(6), 6, 2)
   1.266 +};
   1.267 +
   1.268 +static const u16 winEffect2[] = {
   1.269 +  0, 54,
   1.270 +  FX(E(6), 32, 2), FX(E(6), 48, 1), FX(E(6), 48, 1), FX(E(6), 48, 1),
   1.271 +  FX(E(6), 32, 2), 0, 0, 0, 0,
   1.272 +  FX(D(6), 32, 2), FX(D(6), 48, 1), FX(D(6), 48, 1), FX(D(6), 48, 1),
   1.273 +  FX(D(6), 32, 2), 0, 0, 0, 0,
   1.274 +  FX(E(6), 32, 2), FX(E(6), 48, 1), FX(E(6), 48, 1), FX(E(6), 48, 1),
   1.275 +  FX(E(6), 32, 2), 0, 0, 0, 0,
   1.276 +
   1.277 +  FX(FS(6), 32, 2), FX(FS(6), 48, 1), FX(FS(6), 48, 1), FX(FS(6), 48, 1),
   1.278 +  FX(FS(6), 47, 1), FX(FS(6), 46, 1), FX(FS(6), 45, 1), FX(FS(6), 44, 1),
   1.279 +  FX(FS(6), 43, 1), FX(FS(6), 42, 1), FX(FS(6), 41, 1), FX(FS(6), 40, 1),
   1.280 +  FX(FS(6), 39, 1), FX(FS(6), 38, 1), FX(FS(6), 37, 1), FX(FS(6), 36, 1),
   1.281 +  FX(FS(6), 35, 1), FX(FS(6), 34, 1), FX(FS(6), 33, 1), FX(FS(6), 32, 1),
   1.282 +  FX(FS(6), 31, 1), FX(FS(6), 30, 1), FX(FS(6), 29, 1), FX(FS(6), 28, 1),
   1.283 +  FX(FS(6), 27, 1), FX(FS(6), 26, 1), FX(FS(6), 25, 1), FX(FS(6), 24, 1),
   1.284 +  FX(FS(6), 18, 2), FX(FS(6), 12, 2), FX(FS(6), 6, 2)
   1.285 +};
   1.286 +
   1.287 +static const u16 dieEffect1[] = {
   1.288 +  0, 60,
   1.289 +  FX(E(3), 60, 2), FX(E(3), 59, 2), FX(E(3), 58, 2), FX(E(3), 57, 2),
   1.290 +  FX(E(3), 56, 2), FX(E(3), 55, 2), FX(E(3), 54, 2), FX(E(3), 53, 2),
   1.291 +  FX(E(3), 52, 2), FX(E(3), 51, 2), FX(E(3), 50, 2), FX(E(3), 49, 2),
   1.292 +  FX(E(3), 48, 2), FX(E(3), 47, 2), FX(E(3), 46, 2), FX(E(3), 45, 2),
   1.293 +  FX(E(3), 44, 2), FX(E(3), 43, 2), FX(E(3), 42, 2), FX(E(3), 41, 2),
   1.294 +  FX(E(3), 40, 2), FX(E(3), 39, 2), FX(E(3), 38, 2), FX(E(3), 37, 2),
   1.295 +  FX(E(3), 36, 2), FX(E(3), 35, 2), FX(E(3), 34, 2), FX(E(3), 33, 2),
   1.296 +  FX(E(3), 32, 2), FX(E(3), 31, 2), FX(E(3), 30, 2), FX(E(3), 29, 2),
   1.297 +  FX(E(3), 28, 2), FX(E(3), 27, 2), FX(E(3), 26, 2), FX(E(3), 25, 2),
   1.298 +  FX(E(3), 24, 2), FX(E(3), 23, 2), FX(E(3), 22, 2), FX(E(3), 21, 2),
   1.299 +  FX(E(3), 20, 2), FX(E(3), 19, 2), FX(E(3), 18, 2), FX(E(3), 17, 2),
   1.300 +  FX(E(3), 16, 2), FX(E(3), 15, 2), FX(E(3), 14, 2), FX(E(3), 13, 2),
   1.301 +  FX(E(3), 12, 2), FX(E(3), 11, 2), FX(E(3), 10, 2), FX(E(3),  9, 2),
   1.302 +  FX(E(3),  8, 2), FX(E(3),  7, 2), FX(E(3),  6, 2), FX(E(3),  5, 2),
   1.303 +  FX(E(3),  4, 2), FX(E(3),  3, 2), FX(E(3),  2, 2), FX(E(3),  1, 2)
   1.304 +};
   1.305 +
   1.306 +static const u16 dieEffect2[] = {
   1.307 +  1, 60,
   1.308 +  FX(E(4), 60, 2), FX(B(3), 59, 2), FX(E(3), 58, 2), FX(E(3), 57, 2),
   1.309 +  FX(E(3), 56, 2), FX(E(3), 55, 2), FX(E(3), 54, 2), FX(E(3), 53, 2),
   1.310 +  FX(E(3), 52, 2), FX(E(3), 51, 2), FX(E(3), 50, 2), FX(E(3), 49, 2),
   1.311 +  FX(E(3), 48, 2), FX(E(3), 47, 2), FX(E(3), 46, 2), FX(E(3), 45, 2),
   1.312 +  FX(E(3), 44, 2), FX(E(3), 43, 2), FX(E(3), 42, 2), FX(E(3), 41, 2),
   1.313 +  FX(E(3), 40, 2), FX(E(3), 39, 2), FX(E(3), 38, 2), FX(E(3), 37, 2),
   1.314 +  FX(E(3), 36, 2), FX(E(3), 35, 2), FX(E(3), 34, 2), FX(E(3), 33, 2),
   1.315 +  FX(E(3), 32, 2), FX(E(3), 31, 2), FX(E(3), 30, 2), FX(E(3), 29, 2),
   1.316 +  FX(E(3), 28, 2), FX(E(3), 27, 2), FX(E(3), 26, 2), FX(E(3), 25, 2),
   1.317 +  FX(E(3), 24, 2), FX(E(3), 23, 2), FX(E(3), 22, 2), FX(E(3), 21, 2),
   1.318 +  FX(E(3), 20, 2), FX(E(3), 19, 2), FX(E(3), 18, 2), FX(E(3), 17, 2),
   1.319 +  FX(E(3), 16, 2), FX(E(3), 15, 2), FX(E(3), 14, 2), FX(E(3), 13, 2),
   1.320 +  FX(E(3), 12, 2), FX(E(3), 11, 2), FX(E(3), 10, 2), FX(E(3),  9, 2),
   1.321 +  FX(E(3),  8, 2), FX(E(3),  7, 2), FX(E(3),  6, 2), FX(E(3),  5, 2),
   1.322 +  FX(E(3),  4, 2), FX(E(3),  3, 2), FX(E(3),  2, 2), FX(E(3),  1, 2)
   1.323 +};
   1.324 +
   1.325 +static const u16 *effects[32] = {
   1.326 +  rotateEffect,
   1.327 +  moveEffect,
   1.328 +  NULL,  // drop: not used
   1.329 +  landEffect,
   1.330 +  lockEffect,
   1.331 +  lineEffect,
   1.332 +  homerEffect,
   1.333 +  streakEffect,
   1.334 +  NULL,  // spawn
   1.335 +  holdEffect,  // hold
   1.336 +  irsEffect,  // irs
   1.337 +  NULL,  // 4x4 square
   1.338 +  NULL, NULL, NULL, NULL,  // unused
   1.339 +  dieEffect1, dieEffect2
   1.340 +};
   1.341 +
   1.342 +
   1.343 +
   1.344 +static const u16 *fxPtr[5];
   1.345 +static u8 fxLen[5];
   1.346 +static s8 lastCD;
   1.347 +
   1.348 +
   1.349 +
   1.350 +static void startEffect(const u16 *s) {
   1.351 +
   1.352 +  // skip effects that lack an actual effect
   1.353 +  if (!s) {
   1.354 +    return;
   1.355 +  }
   1.356 +  
   1.357 +  unsigned int type = s[0];
   1.358 +  unsigned int len = s[1];
   1.359 +  unsigned int min = 0;
   1.360 +  unsigned int max = 2;
   1.361 +  
   1.362 +  if (type > 0) {
   1.363 +    min = 3;
   1.364 +    max = 4;
   1.365 +  }
   1.366 +
   1.367 +  // prepare to kill the channel with the shortest time remaining
   1.368 +  unsigned int shortestLen = fxLen[min];
   1.369 +  unsigned int shortestCh = min;
   1.370 +  for (int ch = min + 1; ch <= max; ++ch) {
   1.371 +    if (fxLen[ch] < shortestLen) {
   1.372 +      shortestLen = fxLen[ch];
   1.373 +      shortestCh = ch;
   1.374 +    }
   1.375 +  }
   1.376 +  
   1.377 +  // if the channel is suitable, kill it
   1.378 +  if (shortestLen < len) {
   1.379 +    fxPtr[shortestCh] = s + 2;
   1.380 +    fxLen[shortestCh] = len;
   1.381 +  }
   1.382 +}
   1.383 +
   1.384 +static void pollEffects(void) {
   1.385 +  for (int ch = 0; ch < 5; ++ch) {
   1.386 +    unsigned int note = 36;
   1.387 +    unsigned int vol = 0;
   1.388 +    unsigned int duty = 0;
   1.389 +    if (fxLen[ch] > 0) {
   1.390 +      unsigned int data = *fxPtr[ch]++;
   1.391 +      duty = (data >> 14) & 0x03;
   1.392 +      vol = (data >> 7) & 0x7F;
   1.393 +      note = data & 0x7F;
   1.394 +      --fxLen[ch];
   1.395 +    }
   1.396 +    SCHANNEL_CR(11 + ch) = SCHANNEL_ENABLE | SOUND_FORMAT_PSG | SOUND_PAN(64)
   1.397 +                   | (duty << 24) | vol;
   1.398 +    SCHANNEL_TIMER(11 + ch) = midi2psgFreq[note];
   1.399 +  }
   1.400 +}
   1.401 +
   1.402 +void ljSoundEffects(unsigned long int sounds,
   1.403 +                           int countdown) {
   1.404 +
   1.405 +  // cancel out overlapping line clear sounds
   1.406 +  if (sounds & 0x0080) {
   1.407 +    sounds &= ~0x0060;
   1.408 +  } else if (sounds & 0x0040) {
   1.409 +    sounds &= ~0x0020;
   1.410 +  }
   1.411 +  
   1.412 +  for (int y = 0; sounds; ++y, sounds >>= 1) {
   1.413 +    if (sounds & 1) {
   1.414 +      startEffect(effects[y]);
   1.415 +    }
   1.416 +  }
   1.417 +  if (countdown < 0) {
   1.418 +    countdown = 0;
   1.419 +  } else if (countdown > 6) {
   1.420 +    countdown = 6;
   1.421 +  }
   1.422 +  if (countdown < lastCD) {
   1.423 +    if (countdown > 0) {
   1.424 +      startEffect(countEffect);
   1.425 +    } else {
   1.426 +      startEffect(winEffect1);
   1.427 +      startEffect(winEffect2);
   1.428 +    }
   1.429 +  }
   1.430 +  lastCD = countdown;
   1.431 +  pollEffects();
   1.432 +}