Skip to content

Instantly share code, notes, and snippets.

@realmonster
Last active May 22, 2019 20:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realmonster/c11877a71862a1cfe5b396c4396dfb90 to your computer and use it in GitHub Desktop.
Save realmonster/c11877a71862a1cfe5b396c4396dfb90 to your computer and use it in GitHub Desktop.
rrr sound driver test
#include <vector>
#include "ym3438.h"
struct channel
{
Bit16u ptr;
Bit16u seq_ptr;
Bit8u flags; // ??mf ????
Bit8u delay;
Bit8u def_delay;
Bit8u note;
Bit16u patch_ptr;
Bit8u patch;
Bit8u volume;
Bit8u port;
Bit8u opnibble;
Bit8u keyon_channel;
Bit8u enabled; // p??? ???? 1 or (0x80|sfx_id)
Bit8u pitch;
Bit8u _11;
Bit8u _12;
Bit8u bend_after;
Bit8u bend_duration;
Bit8u bend_frac; // fraction part of speed
Bit8u bend_frac_acc; // accumulator of fraction part
Bit8u bend_freq_speed;
Bit16u loop_ptr;
Bit8u loop_cnt;
Bit8u _1B,_1C,_1D,_1E,_1F,_20,_21;
Bit8u algorithm;
Bit8u op1_TL;
Bit8u op2_TL;
Bit8u op3_TL;
Bit8u op4_TL;
Bit16u fm_low_freq_idx;
Bit8u fm_high_freq;
Bit8u fm_target_frac_acc; // fraction accumulator
Bit8u fm_target_frac; // fraction part
Bit8u fm_target_speed;
Bit16u fm_target_low_idx;
Bit8u fm_target_high;
};
Bit8u data[0x4000];
Bit16u FTBL[0x180];
struct SND
{
ym3438_t *ym;
Bit8u byte_8b;
channel chans[7];
std::vector<Bit16s> sound;
int soundz;
Bit16s tmt[24*2];
bool mute;
Bit8u timer;
Bit8u timer_speed;
Bit8u pause_timer;
Bit8u tempo_Y;
Bit8u tempo_X;
Bit8u song_LFO;
Bit8u song_pitch;
Bit8u song_volume;
Bit8u stop_sfx;
Bit8u sfx_to_play;
Bit8u playing_sfx;
Bit8u playing_song;
Bit8u song_to_play;
Bit8u song_to_play_copy;
Bit8u is_play_loop;
Bit8u playing_pcm;
Bit8u is_pcm_playing;
Bit8u sfx_set_freq_id;
Bit16u sfx_set_freq_low;
Bit8u sfx_set_freq_high;
std::vector<Bit8u> pcms;
int pcm_timer;
int pcm_len;
int pcm_offset;
Bit8u *pcm_data;
void Clock()
{
OPN2_Clock(ym, tmt+soundz*2);
soundz += 1;
pcm_timer += int(65536*8000/(24*53267.03869047619));
if (soundz == 24)
{
int i = sound.size();
sound.resize(i+2);
sound[i] = 0;
sound[i+1] = 0;
for (int j=0; j<24*2; ++j)
{
sound[i+(j&1)]+=tmt[j];
tmt[j] = 0;
}
sound[i]*=5;
sound[i+1]*=5;
soundz = 0;
}
}
void ym_write(Bit16u port, Bit8u address, Bit8u data)
{
//printf("%X %X %X\n", port, address, data);
while (OPN2_Read(ym, 0x4000) & 0x80)
Clock();
OPN2_Write(ym, port, address);
Clock();
if ((ym->address & 0xFF) != address)
printf("address error\n");
OPN2_Write(ym, port+1, data);
Clock();
if ((OPN2_Read(ym, 0x4000) & 0x80) == 0)
{
Clock();
if ((OPN2_Read(ym, 0x4000) & 0x80) == 0)
printf("busy error %X\n", ym->busy);
}
/*for (int i=0; i<24; ++i)
{
printf("%2X %2X %2X %2X | %02X %02X %d\n",
ym->ar[i], ym->sr[i], ym->dr[i], ym->rr[i], ym->tl[i], ym->ssg_eg[i], ym->mode_kon[i]);
}
for (int i=0; i<6; ++i)
{
printf("%X %3X %2X %2X | %X %X %X %X %X\n",
ym->connect[i], ym->fnum[i], ym->block[i], ym->kcode[i],
ym->fb[i], ym->pms[i], ym->ams[i], ym->pan_l[i], ym->pan_l[i]);
}
printf("\n");*/
}
void wait_timer_b()
{
while ((OPN2_Read(ym, 0x4000) & 0x2) == 0)
Clock();
byte_8b = (byte_8b & 0x55)|0x2A;
ym_write(0x4000, 0x27, byte_8b);
}
void init()
{
playing_song = 0;
song_to_play = 0;
song_to_play_copy = 0;
playing_sfx = 0;
sfx_to_play = 0;
playing_pcm = 0;
is_pcm_playing = 0;
sfx_set_freq_id = 0;
soundz = 0;
chans[0].port = 0; chans[0].opnibble = 0; chans[0].keyon_channel = 0;
chans[1].port = 0; chans[1].opnibble = 1; chans[1].keyon_channel = 1;
chans[2].port = 0; chans[2].opnibble = 2; chans[2].keyon_channel = 2;
chans[3].port = 2; chans[3].opnibble = 0; chans[3].keyon_channel = 4;
chans[4].port = 2; chans[4].opnibble = 1; chans[4].keyon_channel = 5;
chans[5].port = 2; chans[5].opnibble = 2; chans[5].keyon_channel = 6;
chans[6].port = 2; chans[6].opnibble = 1; chans[6].keyon_channel = 5;
for (int i=0; i<7; ++i)
{
chans[i].enabled = 0;
chans[i].algorithm = 0;
chans[i].fm_low_freq_idx = 0;
}
ym_write(0x4000, 0x22, 0xC); // LFO
for (int port=0x4002; port>=0x4000; port-=2)
for (int reg=0xB4; reg<=0xB6; ++reg)
ym_write(port, reg, 0); // turn off all L/R/AMS/FMS
for (int i=0; i<6; ++i)
ym_write(0x4000, 0x28, i); // turn off key on
for (int port=0x4000; port<=0x4002; port+=2)
{
for (int reg=0x90; reg<0xA0; ++reg)
ym_write(port, reg, 0); // turn off SSG
for (int reg=0x40; reg<0x50; ++reg)
ym_write(port, reg, 0x7F); // turn off TL
}
byte_8b = 4;
ym_write(0x4000, 0x27, byte_8b&0x15);
ym_write(0x4000, 0x26, 200); // timer B
byte_8b = (byte_8b & 0x55)|0x2A;
ym_write(0x4000, 0x27, byte_8b);
mute = false;
}
void update(int len)
{
while(sound.size()<len)
{
if (playing_pcm)
{
while (sound.size()<len)
{
Clock();
if (pcm_timer > 65536)
{
pcm_timer -= 65536;
OPN2_Write(ym, 0x4000, 0x2A);
Clock();
Clock();
Clock();
OPN2_Write(ym, 0x4001, pcm_data[pcm_offset]);
pcm_offset++;
if (pcm_offset == pcm_len)
{
delete [] pcm_data;
pcm_data = 0;
ym_write(0x4000, 0x2B, 0); // DAC disable
if (pcms.size() == 0 && is_pcm_playing != 0)
{
mute = false;
mute_all_fm();
is_pcm_playing = 0;
}
playing_pcm = 0;
sfx_to_play = 0;
break;
}
}
}
}
while (sound.size()<len)
{
wait_timer_b();
main_update_func();
play_pcms();
if (playing_pcm)
{
break;
}
}
}
}
void main_update_func()
{
if (mute)
{
mute_all_fm();
}
else
{
stop_fm_sfx();
music_load();
start_fm_sfx();
sfx_set_freq();
//printf("timer:%X\n", timer);
if (((timer+timer_speed)^timer)&0x80)
--pause_timer;
timer += timer_speed;
for (int cn=0; cn<7; ++cn)
{
channel &ch = chans[cn];
if (!ch.enabled)
continue;
if ((ch.enabled & 0x80) || !pause_timer) // preset
{
--ch.delay;
if (!ch.delay)
{
for (;;)
{
if (!read_buff(cn))
break;
}
continue;
}
else
{
bit_0(cn);
}
}
freq_bend(cn);
bend_to_target(cn);
if ((ch.flags & (1<<5)) == 0)
{
FM_set_freq(cn);
}
}
TL_set(0);
TL_set(1);
TL_set(2);
TL_set(3);
TL_set(5);
if (chans[4].flags & (1<<5))
TL_set(6);
else
TL_set(4);
if (pause_timer)
return;
++pause_timer;
}
}
void music_load()
{
if (pcms.size())
return;
if (is_pcm_playing)
return;
Bit8u song;
if (playing_song)
song = song_to_play_copy;
else
song = song_to_play;
if (!song)
return;
is_play_loop = song & 0x80;
reset_sequences();
FILE *f = fopen("rrr_bank", "rb");
if (!f)
return;
Bit8u qwe[4];
fread(qwe, 1, 4, f);
Bit8u music_id = (song & 0x7F) - 1;
if (music_id >= qwe[2])
return;
playing_song = music_id + 1;
fseek(f, music_id*4+4, SEEK_SET);
fread(qwe, 1, 4, f);
int music_offset = (qwe[0]<<24)|(qwe[1]<<16)|(qwe[2]<<8)|(qwe[3]);
fseek(f, music_offset, SEEK_SET);
fread(qwe, 1, 2, f);
int channels_offset = (qwe[0]|(qwe[1]<<8));
fread(data+0x1400, 1, 0xC00, f);
fclose(f);
//memcpy(data+0x1400, music, 0xC00);
tempo_Y = data[channels_offset];
tempo_X = data[channels_offset+1];
set_tempo();
song_LFO = data[channels_offset+2];
ym_write(0x4000, 0x22, song_LFO);
song_volume = data[channels_offset+3];
for (int cn = 0; cn < 6; ++cn)
{
chans[cn].flags = 0;
chans[cn].delay = 1;
chans[cn].patch = 0;
chans[cn].pitch = 0;
chans[cn].bend_duration = 0;
int tmp = data[channels_offset+4+cn*2+1];
tmp = (tmp<<8)+data[channels_offset+4+cn*2];
printf("offset %X\n", tmp);
if (tmp)
{
int tmp1 = data[tmp+1];
tmp1 = (tmp1<<8)+data[tmp];
chans[cn].ptr = tmp1;
chans[cn].seq_ptr = tmp+2;
++chans[cn].enabled;
}
}
song_pitch = 0;
timer = 0;
pause_timer = 1;
}
void reset_sequences()
{
playing_song = 0;
song_to_play_copy = 0;
song_to_play = 0;
for (int i=0; i<7; ++i)
{
chans[i].enabled = 0;
chans[i].flags = 0;
chans[i].op1_TL = 0x7F;
chans[i].op2_TL = 0x7F;
chans[i].op3_TL = 0x7F;
chans[i].op4_TL = 0x7F;
}
}
void TL_set(int cn)
{
channel &ch = chans[cn];
Bit8u vol = 0;
if (ch.volume)
{
Bit16u v = 0x80;
if (ch.enabled & 0x80)
v = 0x80;
else
v = (song_volume & 0x7F) + 1;
if (v != 1)
{
v = 2*v*(ch.volume+1);
vol = v >> 8;
}
}
//printf("%d %d %d\n", ch.volume, song_volume, vol);
Bit16u port = 0x4000+ch.port;
Bit8u reg = 0x40 + ch.opnibble;
const int carriers[] = {1,1,1,1,3,7,7,15};
int cc = carriers[ch.algorithm];
if (cc & (1<<3))
TL_op_set(vol, 0x7F-ch.op1_TL, port, reg);
else
ym_write(port, reg, ch.op1_TL);
reg += 4;
if (cc & (1<<2))
TL_op_set(vol, 0x7F-ch.op2_TL, port, reg);
else
ym_write(port, reg, ch.op2_TL);
reg += 4;
if (cc & (1<<1))
TL_op_set(vol, 0x7F-ch.op3_TL, port, reg);
else
ym_write(port, reg, ch.op3_TL);
reg += 4;
if (cc & (1<<0))
TL_op_set(vol, 0x7F-ch.op4_TL, port, reg);
else
ym_write(port, reg, ch.op4_TL);
}
void TL_op_set(Bit8u vol, Bit8u op_vol, Bit16u port, Bit8u reg)
{
if (op_vol != 0)
{
Bit16u v = vol;
v *= op_vol;
ym_write(port, reg, 0x7F-((v*2)>>8));
}
else
{
ym_write(port, reg, 0x7F);
}
}
void bit_0(int cn)
{
channel &ch = chans[cn];
if ((ch.flags & 1) == 0
&& data[ch.ptr] != 0x82
&& data[ch.patch_ptr+0x1C] != 0)
{
if (data[ch.patch_ptr+0x1C] == ch.delay)
{
ch.bend_duration = 0;
note_off(cn);
}
}
}
void freq_bend(int cn)
{
channel &ch = chans[cn];
if (ch.bend_after)
{
--ch.bend_after;
return;
}
if (!ch.bend_duration)
return;
--ch.bend_duration;
Bit16u x = ch.bend_frac;
x += ch.bend_frac_acc;
ch.bend_frac_acc = x;
if ((ch.bend_freq_speed & 0x80) == 0)
{
Bit16u y = ch.fm_low_freq_idx;
y = y + ((x>>8)&1) + ch.bend_freq_speed;
if (y >= 0x180)
{
y -= 0x180;
ch.fm_high_freq += 8;
}
ch.fm_low_freq_idx = y;
}
else
{
Bit16u y = ch.fm_low_freq_idx;
y = y - Bit8u(-ch.bend_freq_speed) - ((x>>8)&1);
if (y & 0x8000)
{
y += 0x180;
ch.fm_high_freq -= 8;
}
ch.fm_low_freq_idx = y;
}
}
void bend_to_target(int cn)
{
channel &ch = chans[cn];
if ((ch.flags & (1<<4)) == 0)
return;
Bit16s tmp;
if (ch.fm_high_freq != ch.fm_target_high)
tmp = Bit16s(ch.fm_target_high) - ch.fm_high_freq;
else
tmp = ch.fm_target_low_idx - ch.fm_low_freq_idx;
if (tmp == 0)
return;
else if (tmp > 0)
{
Bit16s tmp1 = ch.fm_target_frac_acc;
tmp1 += ch.fm_target_frac;
ch.fm_target_frac_acc = tmp1;
tmp1 = ch.fm_low_freq_idx + ch.fm_target_speed + (tmp1>>8);
ch.fm_low_freq_idx = tmp1;
if (ch.fm_low_freq_idx >= 0x180)
{
ch.fm_low_freq_idx -= 0x180;
ch.fm_high_freq += 8;
}
if (ch.fm_high_freq < ch.fm_target_high
|| ch.fm_low_freq_idx < ch.fm_target_low_idx)
return;
ch.fm_high_freq = ch.fm_target_high;
ch.fm_low_freq_idx = ch.fm_target_low_idx;
}
else
{
Bit16s tmp1 = ch.fm_target_frac_acc;
tmp1 += ch.fm_target_frac;
ch.fm_target_frac_acc = tmp1;
tmp1 = ch.fm_low_freq_idx - ch.fm_target_speed - (tmp1>>8);
ch.fm_low_freq_idx = tmp1;
if (ch.fm_low_freq_idx & 0x8000)
{
ch.fm_low_freq_idx += 0x180;
ch.fm_high_freq -= 8;
}
if (ch.fm_high_freq > ch.fm_target_high
|| ch.fm_low_freq_idx > ch.fm_target_low_idx)
return;
ch.fm_high_freq = ch.fm_target_high;
ch.fm_low_freq_idx = ch.fm_target_low_idx;
}
}
void FM_set_freq(int cn)
{
channel &ch = chans[cn];
Bit16u v = FTBL[ch.fm_low_freq_idx];
ym_write(0x4000+ch.port, 0xA4+ch.opnibble, (v >> 8) | ch.fm_high_freq);
ym_write(0x4000+ch.port, 0xA0+ch.opnibble, v & 0xFF);
}
void load_patch(int cn, int patch)
{
channel &ch = chans[cn];
ch.patch = patch;
int offs;
if (ch.enabled & 0x80)
offs = 0xE72;
else
offs = 0x1400;
offs += (patch-0xA0)*0x20;
ch.patch_ptr = offs;
ch.algorithm = data[offs+0x18] & 7;
ch.op1_TL = data[offs+4];
ch.op2_TL = data[offs+5];
ch.op3_TL = data[offs+6];
ch.op4_TL = data[offs+7];
//printf("%X %X %X %X\n", ch.op1_TL, ch.op2_TL, ch.op3_TL, ch.op4_TL);
if ((ch.flags & (1<<5)) == 0)
{
Bit8u reg = 0x30+ch.opnibble;
Bit16u port = 0x4000+ch.port;
for (int i=0; i<4; ++i)
{
ym_write(port, reg, data[offs]);
++offs, reg += 4;
}
for (int i=0; i<4; ++i)
{
ym_write(port, reg, 0x7F);
++offs, reg += 4;
}
for (int i=0; i<16; ++i)
{
ym_write(port, reg, data[offs]);
++offs, reg += 4;
}
reg += 0x20;
for (int i=0; i<2; ++i)
{
ym_write(port, reg, data[offs]);
++offs, reg += 4;
}
++offs;
ch.pitch = data[offs];
ch.flags = 0;
ch.volume = 0x7F;
}
else
{
ch.pitch = data[offs+0x1B];
ch.flags = 0x20;
ch.volume = 0x7F;
}
}
void note_set_freq(int cn, int note)
{
channel &ch = chans[cn];
if ((ch.enabled & 0x80) == 0)
note += song_pitch;
note += ch.pitch;
note &= 0xFF;
if (note >= 0x60)
printf("!omg note!\n");
//printf("note %X: %X %X\n", note, (note%12)*32, (note/12)*8);
if (ch.flags & (1<<4))
{
ch.fm_target_low_idx = (note%12)*32;
ch.fm_target_high = (note/12)*8;
}
else
{
ch.fm_low_freq_idx = (note%12)*32;
ch.fm_high_freq = (note/12)*8;
if ((ch.flags & (1<<5)) == 0)
FM_set_freq(cn);
}
}
bool read_buff(int cn)
{
channel &ch = chans[cn];
if (ch.ptr >= 0x2000)
{
printf("!%d %X: %X\n", cn, ch.ptr);
ch.delay = 0xFF;
return false;
}
Bit8u v = data[ch.ptr];
//printf("%d %X: %X\n", cn, ch.ptr, v);
++ch.ptr;
if (v < 0x80)
{
// note
if ((ch.flags & ((1<<5)|1)) == 0)
{
ym_write(0x4000, 0x28, ch.keyon_channel);
}
note_set_freq(cn, v);
if ((ch.flags & (1<<5)) == 0)
{
ym_write(0x4000, 0x28, ch.keyon_channel|0xF0);
}
ch.delay = ch.def_delay;
return false;
}
else if (v >= 0xE0)
{
ch.def_delay = v-0xE0+1;
return true;
}
else if (v >= 0xA0)
{
load_patch(cn, v);
return true;
}
else
{
Bit16u tmp, tmp1;
switch (v)
{
case 0x80: // next
song_to_play_copy = song_to_play;
tmp = (Bit16u(data[ch.seq_ptr+1])<<8)|data[ch.seq_ptr];
if (!tmp)
{
if (is_play_loop)
{
ch.seq_ptr = (Bit16u(data[ch.seq_ptr+3])<<8)|data[ch.seq_ptr+2];
tmp = (Bit16u(data[ch.seq_ptr+1])<<8)|data[ch.seq_ptr];
}
else
{
reset_sequences();
}
}
ch.seq_ptr += 2;
ch.ptr = tmp;
break;
case 0x81: // note off
note_off(cn);
case 0x82: // wait
ch.delay = ch.def_delay;
return false;
case 0x83: // bend
ch.bend_after = data[ch.ptr];
ch.bend_duration = data[ch.ptr+1];
ch.bend_frac = data[ch.ptr+2];
ch.bend_frac_acc = 0;
ch.bend_freq_speed = data[ch.ptr+3];
ch.ptr += 4;
break;
case 0x84: // volume
ch.volume = data[ch.ptr] & 0x7F;
++ch.ptr;
break;
case 0x85:
case 0x89:
case 0x96:
case 0x97:
reset_sequences();
return false;
case 0x86:
ch.pitch = data[ch.ptr];
++ch.ptr;
break;
case 0x87:
song_pitch = data[ch.ptr];
++ch.ptr;
break;
case 0x88: // something with sfx
stop_sfx_ch(cn);
return false;
case 0x8A: // tempo
tempo_Y = data[ch.ptr];
++ch.ptr;
set_tempo();
break;
case 0x8B: // left
set_pan(cn, 0x80);
break;
case 0x8C: // right
set_pan(cn, 0x40);
break;
case 0x8D: // stereo
set_pan(cn, 0xC0);
break;
case 0x8E: // stop
ch.delay = 0xFF;
--ch.ptr;
return false;
case 0x8F:
ch.loop_ptr = ch.ptr+1;
ch.loop_cnt = data[ch.ptr];
++ch.ptr;
break;
case 0x90:
--ch.loop_cnt;
if (ch.loop_cnt)
ch.ptr = ch.loop_ptr;
break;
case 0x91:
ch.volume = (ch.volume+data[ch.ptr])&0x7F;
++ch.ptr;
break;
case 0x92:
ch.volume = (ch.volume-data[ch.ptr])&0x7F;
++ch.ptr;
break;
case 0x93:
ch.flags |= 1;
break;
case 0x94:
ch.flags &= ~1;
break;
case 0x95: // jmp
song_to_play_copy = song_to_play;
ch.ptr = (Bit16u(data[ch.ptr+1])<<8)|data[ch.ptr];
break;
case 0x98:
song_volume = data[ch.ptr];
++ch.ptr;
break;
case 0x99: // start follow
ch.fm_target_frac = data[ch.ptr];
ch.fm_target_speed = data[ch.ptr+1];
ch.ptr += 2;
ch.flags |= (1<<4);
break;
case 0x9A: // stop follow
ch.flags &= ~(1<<4);
break;
case 0x9B:
ch.def_delay = data[ch.ptr];
++ch.ptr;
break;
}
return true;
}
}
void note_off(int cn)
{
if ((chans[cn].flags & (1<<5)) == 0)
{
ym_write(0x4000, 0x28, chans[cn].keyon_channel);
}
}
void mute_all_fm()
{
if (mute)
{
for (int cn=0; cn<7; ++cn)
{
channel &ch = chans[cn];
for (int i=0; i<4; ++i)
{
ym_write(0x4000+ch.port, 0x40+ch.opnibble+i*4, 0x7F);
}
}
}
}
void set_pan(int cn, int pan)
{
channel &ch = chans[cn];
if ((ch.flags & (1<<5)) == 0)
{
ym_write(0x4000|ch.port, 0xB4+ch.opnibble, (data[ch.patch_ptr+0x19]&0x3F)|pan);
}
}
void set_tempo()
{
int a = int(0xE1/double(tempo_X)+0.5);
timer_speed = (tempo_Y*8)/a + 1;
}
void play_pcms()
{
if (chans[5].enabled || chans[6].enabled || !pcms.size())
return;
FILE *f = fopen("rrr_bank","rb");
if (!f)
return;
int id = pcms[0];
pcms.erase(pcms.begin());
playing_pcm = id;
if (playing_song && !is_pcm_playing)
{
is_pcm_playing = 1;
mute = true;
mute_all_fm();
}
Bit8u qwe[12];
fread(qwe, 1, 2, f);
int off = (qwe[0]<<8)|qwe[1];
printf("%X ", off);
int off1 = off+3+12*(id-1);
printf("%X ", off1);
fseek(f, off1, SEEK_SET);
fread(qwe, 1, 12, f);
int offset = (qwe[0]<<16)|(qwe[1]<<8)|qwe[2];
printf("%X ", offset);
pcm_len = (qwe[3]<<8)|qwe[4];
printf("%X\n", pcm_len);
pcm_data = new Bit8u[pcm_len];
fseek(f, offset, SEEK_SET);
fread(pcm_data, 1, pcm_len, f);
fclose(f);
pcm_offset = 0;
pcm_timer = 0;
ym_write(0x4002, 0xB6, 0xC0);
ym_write(0x4000, 0x2B, 0x80);
}
void stop_fm_sfx()
{
if (stop_sfx == 0)
return;
Bit8u sfx = stop_sfx;
stop_sfx = 0;
if (sfx == 0xFF)
{
stop_sfx_ch(5);
stop_sfx_ch(6);
}
else
{
if (chans[5].enabled == (sfx | 0x80))
{
stop_sfx_ch(5);
}
if (chans[6].enabled == (sfx | 0x80))
{
stop_sfx_ch(6);
}
}
}
void stop_sfx_ch(int cn)
{
channel &ch = chans[cn];
playing_sfx = 0;
ch.enabled = 0;
ch.bend_duration = 0;
ch.flags = 0;
// key off
if ((ch.flags & (1<<5)) == 0)
{
ym_write(0x4000, 0x28, ch.keyon_channel);
}
if (cn == 6)
{
Bit8u vol = chans[4].volume;
chans[4].flags &= ~(1<<5);
load_patch(4, chans[4].patch);
chans[4].volume = vol;
FM_set_freq(4);
}
}
void start_fm_sfx()
{
if (sfx_to_play == 0)
return;
playing_sfx = sfx_to_play;
if (sfx_to_play-1 >= 0x1F)
return;
Bit16u off = 0x10F2+(sfx_to_play-1)*2;
Bit16u off1 = data[off]|(data[off+1]<<8);
if (chans[5].enabled)
{
int cn = 5;
if (chans[6].enabled == 0)
{
note_off(4);
chans[4].flags |= (1<<5);
cn = 6;
}
chans[cn].enabled = playing_sfx|0x80;
sfx_to_play = 0;
chans[cn].flags = 0;
chans[cn].bend_duration = 0;
note_off(cn);
chans[cn].delay = 4;
chans[cn].ptr = off1;
}
else
{
chans[5].enabled = playing_sfx|0x80;
sfx_to_play = 0;
chans[5].flags = 0;
chans[5].bend_duration = 0;
chans[5].ptr = off1;
for (;;)
{
if (!read_buff(5))
break;
}
}
}
void sfx_set_freq()
{
Bit8u sfx = sfx_set_freq_id;
if (!sfx)
return;
sfx_set_freq_id = 0;
channel *ch = &chans[5];
if (ch->enabled != (sfx_set_freq_id | 0x80))
{
++ch;
if (ch->enabled != (sfx_set_freq_id | 0x80))
return;
}
ch->fm_target_low_idx = sfx_set_freq_low;
ch->fm_target_high = sfx_set_freq_high*8;
ch->fm_target_frac = 0;
}
};
int main(int argc, char **argv)
{
if (argc != 3)
return 0;
FILE *f = fopen("z80.bin", "rb");
fread(data, 1, sizeof(data), f);
fclose(f);
for (int i=0; i<0x180; ++i)
{
FTBL[i]=(data[0xB72+i*2+1]<<8)|data[0xB72+i*2];
}
f = fopen("out.raw","wb");
if (!f)
return 0;
SND z;
z.ym = new ym3438_t;
OPN2_SetChipType(ym3438_mode_ym2612);
OPN2_Reset(z.ym);
z.init();
z.playing_song = 0;
z.song_to_play = atoi(argv[1]);
z.update(atoi(argv[2]));
fwrite(&z.sound[0],1,z.sound.size()*2,f);
fclose(f);
delete z.ym;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment