public override int start(Mame.MachineSound msound) { int i, j, sweep, charge, countdown, generator, bit1, bit2; int[] lfovol = { LFO_VOLUME, LFO_VOLUME, LFO_VOLUME }; #if SAMPLES Mame.Machine.samples = Mame.readsamples(galaxian_sample_names, Mame.Machine.gamedrv.name); #endif channelnoise = Mame.mixer_allocate_channel(NOISE_VOLUME); Mame.mixer_set_name(channelnoise, "Noise"); channelshoot = Mame.mixer_allocate_channel(SHOOT_VOLUME); Mame.mixer_set_name(channelshoot, "Shoot"); channellfo = Mame.mixer_allocate_channels(3, lfovol); Mame.mixer_set_name(channellfo + 0, "Background #0"); Mame.mixer_set_name(channellfo + 1, "Background #1"); Mame.mixer_set_name(channellfo + 2, "Background #2"); #if SAMPLES if (Mame.Machine.samples != null && Mame.Machine.samples.sample[0] != null) /* We should check also that Samplename[0] = 0 */ shootsampleloaded = 1; else shootsampleloaded = 0; if (Mame.Machine.samples != null && Mame.Machine.samples.sample[1] != null) /* We should check also that Samplename[0] = 0 */ deathsampleloaded = 1; else deathsampleloaded = 0; #endif if ((noisewave = new _ShortPtr((int)(NOISE_LENGTH * sizeof(short)))) == null) { return 1; } #if NEW_SHOOT byte SHOOT_SEC = 2; shoot_rate = Mame.Machine.sample_rate; shoot_length = SHOOT_SEC * shoot_rate; if ((shootwave = new _ShortPtr(shoot_length * sizeof(short))) == null) #else if( (shootwave = new _ShortPtr(SHOOT_LENGTH * sizeof(short))) == null ) #endif { noisewave = null; return 1; } /* * The RNG shifter is clocked with RNG_RATE, bit 17 is * latched every 2V cycles (every 2nd scanline). * This signal is used as a noise source. */ generator = 0; countdown = (int)(NOISE_RATE / 2); for (i = 0; i < NOISE_LENGTH; i++) { countdown -= (int)RNG_RATE; while (countdown < 0) { generator <<= 1; bit1 = (~generator >> 17) & 1; bit2 = (generator >> 5) & 1; if ((bit1 ^ bit2) != 0) generator |= 1; countdown += (int)NOISE_RATE; } noisewave.write16(i, (ushort)(((generator >> 17) & 1) != 0 ? (ushort)NOISE_AMPLITUDE : -(ushort)NOISE_AMPLITUDE)); } #if NEW_SHOOT /* dummy */ sweep = 100; charge = +2; j = 0; { int R41__ = 100000; int R44__ = 10000; int R45__ = 22000; int R46__ = 10000; int R47__ = 2200; int R48__ = 2200; double C25__ = 0.000001; double C27__ = 0.00000001; double C28__ = 0.000047; double C29__ = 0.00000001; double IC8L3_L = 0.2; /* 7400 L level */ double IC8L3_H = 4.5; /* 7400 H level */ double NOISE_L = 0.2; /* 7474 L level */ double NOISE_H = 4.5; /* 7474 H level */ /* key on/off time is programmable Therefore, it is necessary to make separate sample with key on/off. And, calculate the playback point according to the voltage of c28. */ double SHOOT_KEYON_TIME = 0.1; /* second */ /* NE555-FM input calculation is wrong. The frequency is not proportional to the voltage of FM input. And, duty will be changed,too. */ double NE555_FM_ADJUST_RATE = 0.80; /* discharge : 100K * 1uF */ double v = 5.0; double vK = (shoot_rate) != 0 ? Math.Exp(-1 / (R41__ * C25__) / shoot_rate) : 0; /* -- SHOOT KEY port -- */ double IC8L3 = IC8L3_L; /* key on */ int IC8Lcnt = (int)(SHOOT_KEYON_TIME * shoot_rate); /* count for key off */ /* C28 : KEY port capacity */ /* connection : 8L-3 - R47(2.2K) - C28(47u) - R48(2.2K) - C29 */ double c28v = IC8L3_H - (IC8L3_H - (NOISE_H + NOISE_L) / 2) / (R46__ + R47__ + R48__) * R47__; double c28K = (shoot_rate) != 0 ? Math.Exp(-1 / (22000 * 0.000047) / shoot_rate) : 0; /* C29 : NOISE capacity */ /* connection : NOISE - R46(10K) - C29(0.1u) - R48(2.2K) - C28 */ double c29v = IC8L3_H - (IC8L3_H - (NOISE_H + NOISE_L) / 2) / (R46__ + R47__ + R48__) * (R47__ + R48__); double c29K1 = (shoot_rate) != 0 ? Math.Exp(-1 / (22000 * 0.00000001) / shoot_rate) : 0; /* form C28 */ double c29K2 = (shoot_rate) != 0 ? Math.Exp(-1 / (100000 * 0.00000001) / shoot_rate) : 0; /* from noise */ /* NE555 timer */ /* RA = 10K , RB = 22K , C=.01u ,FM = C29 */ double ne555cnt = 0; double ne555step = (shoot_rate) != 0 ? ((1.44 / ((R44__ + R45__ * 2) * C27__)) / shoot_rate) : 0; double ne555duty = (double)(R44__ + R45__) / (R44__ + R45__ * 2); /* t1 duty */ double ne555sr; /* threshold (FM) rate */ /* NOISE source */ double ncnt = 0.0; double nstep = (shoot_rate) != 0 ? ((double)NOISE_RATE / shoot_rate) : 0; double noise_sh2; /* voltage level */ for (i = 0; i < shoot_length; i++) { /* noise port */ noise_sh2 = noisewave.read16((int)(ncnt % NOISE_LENGTH)) == NOISE_AMPLITUDE ? NOISE_H : NOISE_L; ncnt += nstep; /* calculate NE555 threshold level by FM input */ ne555sr = c29v * NE555_FM_ADJUST_RATE / (5.0 * 2 / 3); /* calc output */ ne555cnt += ne555step; if (ne555cnt >= ne555sr) ne555cnt -= ne555sr; if (ne555cnt < ne555sr * ne555duty) { /* t1 time */ shootwave.write16(i, (ushort)(v / 5 * 0x7fff)); /* discharge output level */ if (IC8L3 == IC8L3_H) v *= vK; } else shootwave.write16(i, 0); /* C28 charge/discharge */ c28v += (IC8L3 - c28v) - (IC8L3 - c28v) * c28K; /* from R47 */ c28v += (c29v - c28v) - (c29v - c28v) * c28K; /* from R48 */ /* C29 charge/discharge */ c29v += (c28v - c29v) - (c28v - c29v) * c29K1; /* from R48 */ c29v += (noise_sh2 - c29v) - (noise_sh2 - c29v) * c29K2; /* from R46 */ /* key off */ if (IC8L3 == IC8L3_L && --IC8Lcnt == 0) IC8L3 = IC8L3_H; } } #else /* * Ra is 10k, Rb is 22k, C is 0.01uF * charge time t1 = 0.693 * (Ra + Rb) * C . 221.76us * discharge time t2 = 0.693 * (Rb) * C . 152.46us * average period 374.22us . 2672Hz * I use an array of 10 values to define some points * of the charge/discharge curve. The wave is modulated * using the charge/discharge timing of C28, a 47uF capacitor, * over a 2k2 resistor. This will change the frequency from * approx. Favg-Favg/3 up to Favg+Favg/3 down to Favg-Favg/3 again. */ sweep = 100; charge = +2; countdown = sweep / 2; for( i = 0, j = 0; i < SHOOT_LENGTH; i++ ) { shootwave[i] = charge_discharge[j]; LOG((errorlog, "shoot[%5d] $%04x (sweep: %3d, j:%d)\n", i, shootwave[i] & 0xffff, sweep, j)); /* * The current sweep and a 2200/10000 fraction (R45 and R48) * of the noise are frequency modulating the NE555 chip. */ countdown -= sweep + noisewave[i % NOISE_LENGTH] / (2200*NOISE_AMPLITUDE/10000); while( countdown < 0 ) { countdown += 100; j = ++j % 10; } /* sweep from 100 to 133 and down to 66 over the time of SHOOT_LENGTH */ if( i % (SHOOT_LENGTH / 33 / 3 ) == 0 ) { sweep += charge; if( sweep >= 133 ) charge = -1; } } #endif //memset(tonewave, 0, sizeof(tonewave)); for (i = 0; i < TOOTHSAW_LENGTH; i++) { //#define V(r0,r1) 2*TOOTHSAW_AMPLITUDE*(r0)/(r0+r1)-TOOTHSAW_AMPLITUDE double r0a = 1.0 / 1e12, r1a = 1.0 / 1e12; double r0b = 1.0 / 1e12, r1b = 1.0 / 1e12; /* #0: VOL1=0 and VOL2=0 * only the 33k and the 22k resistors R51 and R50 */ if ((i & 1) != 0) { r1a += 1.0 / 33000; r1b += 1.0 / 33000; } else { r0a += 1.0 / 33000; r0b += 1.0 / 33000; } if ((i & 4) != 0) { r1a += 1.0 / 22000; r1b += 1.0 / 22000; } else { r0a += 1.0 / 22000; r0b += 1.0 / 22000; } for (int k = 0; k < 4; k++) tonewave[k] = new _BytePtr(TOOTHSAW_LENGTH); tonewave[0][i] = (byte)V(1.0 / r0a, 1.0 / r1a); //#define V(r0,r1) 2*TOOTHSAW_AMPLITUDE*(r0)/(r0+r1)-TOOTHSAW_AMPLITUDE /* #1: VOL1=1 and VOL2=0 * add the 10k resistor R49 for bit QC */ if ((i & 4) != 0) r1a += 1.0 / 10000; else r0a += 1.0 / 10000; tonewave[1][i] = (byte)V(1.0 / r0a, 1.0 / r1a); /* #2: VOL1=0 and VOL2=1 * add the 15k resistor R52 for bit QD */ if ((i & 8) != 0) r1b += 1.0 / 15000; else r0b += 1.0 / 15000; tonewave[2][i] = (byte)V(1.0 / r0b, 1.0 / r1b); /* #3: VOL1=1 and VOL2=1 * add the 10k resistor R49 for QC */ if ((i & 4) != 0) r0b += 1.0 / 10000; else r1b += 1.0 / 10000; tonewave[3][i] = (byte)V(1.0 / r0b, 1.0 / r1b); //LOG((errorlog, "tone[%2d]: $%02x $%02x $%02x $%02x\n", i, tonewave[0][i], tonewave[1][i], tonewave[2][i], tonewave[3][i])); } pitch = 0; vol = 0; tone_stream = Mame.stream_init("Tone", TOOTHSAW_VOLUME, (int)(SOUND_CLOCK / STEPS), 0, tone_update); #if SAMPLES if (deathsampleloaded == 0) #endif { Mame.mixer_set_volume(channelnoise, 0); Mame.mixer_play_sample_16(channelnoise, noisewave, (int)NOISE_LENGTH, (int)NOISE_RATE, true); } #if SAMPLES if (shootsampleloaded == 0) #endif { Mame.mixer_set_volume(channelshoot, 0); Mame.mixer_play_sample_16(channelshoot, shootwave, SHOOT_LENGTH, SHOOT_RATE, false); } Mame.mixer_set_volume(channellfo + 0, 0); Mame.mixer_play_sample_16(channellfo + 0, backgroundwave, backgroundwave.buffer.Length / 2, 1000, true); Mame.mixer_set_volume(channellfo + 1, 0); Mame.mixer_play_sample_16(channellfo + 1, backgroundwave, backgroundwave.buffer.Length / 2, 1000, true); Mame.mixer_set_volume(channellfo + 2, 0); Mame.mixer_play_sample_16(channellfo + 2, backgroundwave, backgroundwave.buffer.Length / 2, 1000, true); return 0; }
static void shuffle(_ShortPtr buf, int len) { if (len == 2) return; if ((len % 4) != 0) throw new Exception(); /* must not happen */ len /= 2; for (int i = 0; i < len / 2; i++) { ushort t = buf.read16(len / 2 + i); buf.write16(len / 2 + i, buf.read16(len + i)); buf.write16(len + i, t); } shuffle(buf, len); shuffle(new _ShortPtr(buf, len*2 ), len); }