/********************************************************************************************** * * OKIM 6295 ADPCM chip: * * Command bytes are sent: * * 1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample number to trigger * abcd vvvv = second half of command; one of the abcd bits is set to indicate which voice * the v bits seem to be volumed * * 0abc d000 = stop playing; one or more of the abcd bits is set to indicate which voice(s) * * Status is read: * * ???? abcd = one bit per voice, set to 0 if nothing is playing, or 1 if it is active * ***********************************************************************************************/ /********************************************************************************************** okim6295_update -- update the sound chip so that it is in sync with CPU execution ***********************************************************************************************/ //static STREAM_UPDATE( okim6295_update ) private void okim6295_update(byte ChipID, int[][] outputs, int samples) { //System.Console.Write("samples:{0}\n" , samples); //okim6295_state *chip = (okim6295_state *)param; okim6295_state chip = OKIM6295Data[ChipID]; int i; //memset(outputs[0], 0, samples * sizeof(*outputs[0])); for (i = 0; i < samples; i++) { outputs[0][i] = 0; } for (i = 0; i < okim6295_state.OKIM6295_VOICES; i++) // for (i = 0; i < 1; i++) { ADPCMVoice voice = chip.voice[i]; infos[ChipID].chInfo[i].mask = voice.Muted == 0; if (voice.Muted == 0) { int[][] buffer = outputs; int ptrBuffer = 0; short[] sample_data = new short[MAX_SAMPLE_CHUNK]; int remaining = samples; /* loop while we have samples remaining */ while (remaining != 0) { int Samples = (remaining > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : remaining; int samp; generate_adpcm(chip, voice, sample_data, Samples); for (samp = 0; samp < Samples; samp++) { buffer[0][ptrBuffer++] += sample_data[samp]; //if (sample_data[samp] != 0) //{ // System.Console.WriteLine("ch:{0} sampledata[{1}]={2} count:{3} sample:{4}" // , i, samp, sample_data[samp] // , voice.count, voice.sample); //} } remaining -= samples; } } } //memcpy(outputs[1], outputs[0], samples * sizeof(*outputs[0])); for (i = 0; i < samples; i++) { outputs[1][i] = outputs[0][i]; } }
public static int okim6295_status_r() { int i, result; result = 0xf0; Sound.okistream.stream_update(); for (i = 0; i < 4; i++) { ADPCMVoice voice = OKI.voice[i]; if (voice.playing) { result |= 1 << i; } } return(result); }
private void generate_adpcm(okim6295_state chip, ADPCMVoice voice, short[] buffer, int samples) { int ptrBuffer = 0; /* if this voice is active */ if (voice.playing != 0) { //System.Console.Write("base_offset[{0:X}] sample[{1:X}] count[{2:X}]\n", voice.base_offset, voice.sample, voice.count); int iBase = (int)voice.base_offset; int sample = (int)voice.sample; int count = (int)voice.count; /* loop while we still have samples to generate */ while (samples != 0) { /* compute the new amplitude and update the current step */ //int nibble = memory_raw_read_byte(chip->device->space(), base + sample / 2) >> (((sample & 1) << 2) ^ 4); //System.Console.Write("nibblecal1[{0:d}]2[{1:d}]\n", iBase + sample / 2, (((sample & 1) << 2) ^ 4)); byte nibble = (byte)(memory_raw_read_byte(chip, iBase + sample / 2) >> (((sample & 1) << 2) ^ 4)); //System.Console.Write( "nibble[{0:X}]\n", nibble); /* output to the buffer, scaling by the volume */ /* signal in range -2048..2047, volume in range 2..32 => signal * volume / 2 in range -32768..32767 */ buffer[ptrBuffer++] = (short)(clock_adpcm(voice.adpcm, nibble) * voice.volume / 2); //System.Console.Write("*buffer[{0}]\n", buffer[ptrBuffer-1]); samples--; /* next! */ if (++sample >= count) { voice.playing = 0; break; } } /* update the parameters */ voice.sample = (uint)sample; } /* fill the rest with silence */ while (samples-- != 0) { buffer[ptrBuffer++] = 0; } }
/********************************************************************************************** okim6295_status_r -- read the status port of an OKIM6295-compatible chip ***********************************************************************************************/ //READ8_DEVICE_HANDLER( okim6295_r ) private byte okim6295_r(byte ChipID, int offset) { //okim6295_state *info = get_safe_token(device); okim6295_state info = OKIM6295Data[ChipID]; int i, result; result = 0xf0; /* naname expects bits 4-7 to be 1 */ /* set the bit to 1 if something is playing on a given channel */ //stream_update(info->stream); for (i = 0; i < okim6295_state.OKIM6295_VOICES; i++) { ADPCMVoice voice = info.voice[i]; /* set the bit if it's playing */ if (voice.playing != 0) result |= 1 << i; } return (byte)result; }
/********************************************************************************************** okim6295_data_w -- write to the data port of an OKIM6295-compatible chip ***********************************************************************************************/ //WRITE8_DEVICE_HANDLER( okim6295_w ) private void okim6295_write_command(okim6295_state info, byte data,okim6295Info Info) { //okim6295_state *info = get_safe_token(device); /* if a command is pending, process the second half */ if (info.command != -1) { int temp = data >> 4, i, start, stop; int iBase; /* the manual explicitly says that it's not possible to start multiple voices at the same time */ // if (temp != 0 && temp != 1 && temp != 2 && temp != 4 && temp != 8) // System.Console.Write("OKI6295 start %x contact MAMEDEV\n", temp); /* update the stream */ //stream_update(info->stream); /* determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte) */ for (i = 0; i < okim6295_state.OKIM6295_VOICES; i++, temp >>= 1) { if ((temp & 1) != 0) { ADPCMVoice voice = info.voice[i]; /* determine the start/stop positions */ iBase = info.command * 8; //start = memory_raw_read_byte(device->space(), base + 0) << 16; start = memory_raw_read_byte(info, iBase + 0) << 16; start |= memory_raw_read_byte(info, iBase + 1) << 8; start |= memory_raw_read_byte(info, iBase + 2) << 0; start &= 0x3ffff; Info.chInfo[i].stAdr = start; stop = memory_raw_read_byte(info, iBase + 3) << 16; stop |= memory_raw_read_byte(info, iBase + 4) << 8; stop |= memory_raw_read_byte(info, iBase + 5) << 0; stop &= 0x3ffff; Info.chInfo[i].edAdr = stop; /* set up the voice to play this sample */ if (start < stop) { if (voice.playing == 0) /* fixes Got-cha and Steel Force */ { voice.playing = 1; voice.base_offset = (uint)start; voice.sample = 0; voice.count = (uint)(2 * (stop - start + 1)); /* also reset the ADPCM parameters */ reset_adpcm(voice.adpcm); voice.volume = (uint)volume_table[data & 0x0f]; Info.keyon[i] = true; } else { //logerror("OKIM6295:'%s' requested to play sample %02x on non-stopped voice\n",device->tag(),info->command); // just displays warnings when seeking //logerror("OKIM6295: Voice %u requested to play sample %02x on non-stopped voice\n",i,info->command); } } /* invalid samples go here */ else { //logerror("OKIM6295:'%s' requested to play invalid sample %02x\n",device->tag(),info->command); //System.Console.Write("OKIM6295: Voice {0} requested to play invalid sample {1:X2} StartAddr {2:X} StopAdr {3:X} \n", i, info.command, start, stop); voice.playing = 0; } } } /* reset the command */ info.command = -1; } /* if this is the start of a command, remember the sample number for next time */ else if ((data & 0x80) != 0) { info.command = data & 0x7f; } /* otherwise, see if this is a silence command */ else { int temp = data >> 3, i; /* update the stream, then turn it off */ //stream_update(info->stream); /* determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command */ for (i = 0; i < okim6295_state.OKIM6295_VOICES; i++, temp >>= 1) { if ((temp & 1) != 0) { ADPCMVoice voice = info.voice[i]; voice.playing = 0; } } } }
public ADPCM() { this.sound_num = Mame.SOUND_ADPCM; this.name = "ADPCM"; for (int i = 0; i < Mame.MAX_ADPCM; i++) adpcm[i] = new ADPCMVoice(); }
static void generate_adpcm(ADPCMVoice voice, _ShortPtr buffer, int samples) { /* if this voice is active */ if (voice.playing != 0) { _BytePtr _base = voice._base; int sample = (int)voice.sample; int signal = (int)voice.signal; int count = (int)voice.count; int step = (int)voice.step; int val; /* loop while we still have samples to generate */ while (samples != 0) { /* compute the new amplitude and update the current step */ val = _base[sample / 2] >> (((sample & 1) << 2) ^ 4); signal += diff_lookup[step * 16 + (val & 15)]; /* clamp to the maximum */ if (signal > 2047) signal = 2047; else if (signal < -2048) signal = -2048; /* adjust the step size and clamp */ step += index_shift[val & 7]; if (step > 48) step = 48; else if (step < 0) step = 0; /* output to the buffer, scaling by the volume */ buffer.write16(0, (ushort)(signal * voice.volume / 16)); buffer.offset += 2; samples--; /* next! */ if (++sample > count) { voice.playing = 0; break; } } /* update the parameters */ voice.sample = (uint)sample; voice.signal = (uint)signal; voice.step = (uint)step; } /* fill the rest with silence */ while (samples-- != 0) { buffer.write16(0, 0); buffer.offset += 2; } }