Mercurial > pmdwin
diff pmdwin.cpp @ 0:c55ea9478c80
Hello Gensokyo!
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Tue, 21 May 2013 10:29:21 +0200 |
parents | |
children |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/pmdwin.cpp @@ -0,0 +1,5616 @@ +//============================================================================= +// Professional Music Driver [P.M.D.] version 4.8 +// Programmed By M.Kajihara +// Windows Converted by C60 +//============================================================================= + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <limits.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> +#if defined(WIN32) || defined(WIN64) +#include <stdarg.h> +#include <windef.h> +#include <winbase.h> +#include <winnt.h> +#else +#include <sys/mman.h> +#endif +#include "jisx0208.h" +#include "pmdwin.h" + +/****************************************************************************** +; Part Table +******************************************************************************/ +static const uint8_t part_table[][3] = { // for PMDB2 + // Part<94>ヤ<8d><86>,Partb,<89>ケ<8c>ケ<94>ヤ<8d><86> + { 0, 1, 0 }, //A + { 1, 2, 0 }, //B + { 2, 3, 0 }, //C + { 3, 1, 1 }, //D + { 4, 2, 1 }, //E + { 5, 3, 1 }, //F + { 6, 1, 2 }, //G + { 7, 2, 2 }, //H + { 8, 3, 2 }, //I + { 9, 1, 3 }, //J + { 10, 3, 4 }, //K + { 11, 3, 0 }, //c2 + { 12, 3, 0 }, //c3 + { 13, 3, 0 }, //c4 + { 255, 0, 255 }, //Rhythm + { 22, 3, 1 }, //Effect +}; + +static char *notes[12] = { + "C ", "Db", "D ", "Eb", "E ", "F ", "F#", "G ", "G#", "A ", "Bb", "B " +}; + +static char *drums[6] = { + "Bass ", "Snare ", "RideCym", "Hi-Hat ", "Tom-Tom", "Rim " +}; + +static inline unsigned int av_log2(unsigned int x) { + unsigned int ret; + __asm__ volatile("or $1, %1 \n\t" + "bsr %1, %0 \n\t" + :"=a"(ret) : "D"(x)); + return ret; +} + +/****************************************************************************** +; <89>ケ<8a>KDATA(FM) +******************************************************************************/ +static const uint16_t fnum_data[] = { + 0x026a, // C + 0x028f, // D- + 0x02b6, // D + 0x02df, // E- + 0x030b, // E + 0x0339, // F + 0x036a, // G- + 0x039e, // G + 0x03d5, // A- + 0x0410, // A + 0x044e, // B- + 0x048f // B +}; + +/****************************************************************************** +; <89>ケ<8a>KDATA(PSG) +******************************************************************************/ +static const uint16_t psg_tune_data[] = { + 0x0ee8, // C + 0x0e12, // D- + 0x0d48, // D + 0x0c89, // E- + 0x0bd5, // E + 0x0b2b, // F + 0x0a8a, // G- + 0x09f3, // G + 0x0964, // A- + 0x08dd, // A + 0x085e, // B- + 0x07e6, // B +}; + + +/****************************************************************************** +; <82>e<82>l<89>ケ<90>F<82>フ<83>L<83><83><83><8a><83>A<82>フ<83>e<81>[<83>u<83><8b> +******************************************************************************/ +static const uint8_t carrier_table[] = { + 0x80, 0x80, 0x80, 0x80, 0xa0, 0xe0, 0xe0, 0xf0, + 0xee, 0xee, 0xee, 0xee, 0xcc, 0x88, 0x88, 0x00 +}; + +/****************************************************************************** +; <83>h<83><89><83><80><83>X<97>p<83><8a><83>Y<83><80><83>f<81>[<83>^ +******************************************************************************/ +static const uint8_t rhydat[][3] = { + // PT,PAN/VOLUME,KEYON + { 0x18, 0xdf, 0x01 }, // <83>o<83>X + { 0x19, 0xdf, 0x02 }, // <83>X<83>l<83>A + { 0x1c, 0x5f, 0x10 }, // <83>^<83><80>[LOW] + { 0x1c, 0xdf, 0x10 }, // <83>^<83><80>[MID] + { 0x1c, 0x9f, 0x10 }, // <83>^<83><80>[HIGH] + { 0x1d, 0xd3, 0x20 }, // <83><8a><83><80> + { 0x19, 0xdf, 0x02 }, // <83>N<83><89><83>b<83>v + { 0x1b, 0x9c, 0x88 }, // C<83>n<83>C<83>n<83>b<83>g + { 0x1a, 0x9d, 0x04 }, // O<83>n<83>C<83>n<83>b<83>g + { 0x1a, 0xdf, 0x04 }, // <83>V<83><93><83>o<83><8b> + { 0x1a, 0x5e, 0x04 } // RIDE<83>V<83><93><83>o<83><8b> +}; + + +//============================================================================= +// TimerAの処理[メイン] +//============================================================================= +void PMDWIN::TimerA_main(void) +{ + open_work.TimerAflag = 1; + open_work.TimerAtime++; + + if(effwork.effon) { + effplay(); // SSG効果音処理 + } + + open_work.TimerAflag = 0; +} + + +//============================================================================= +// TimerBの処理[メイン] +//============================================================================= +void PMDWIN::TimerB_main(void) +{ + open_work.TimerBflag = 1; + if(pmdwork.music_flag) { + if(pmdwork.music_flag & 1) { + mstart(); + } + + if(pmdwork.music_flag & 2) { + mstop(); + } + } + + if(open_work.play_flag) { + mmain(); + settempo_b(); + syousetu_count(); + pmdwork.lastTimerAtime = open_work.TimerAtime; + } + open_work.TimerBflag = 0; +} + + +//============================================================================= +// MUSIC PLAYER MAIN [FROM TIMER-B] +//============================================================================= +void PMDWIN::mmain(void) +{ + int i; + + pmdwork.loop_work = 3; + for(i = 0; i < 3; i++) { + pmdwork.partb = i + 1; + psgmain(&SSGPart[i]); + } + + pmdwork.fmsel = 0x100; + for(i = 0; i < 3; i++) { + pmdwork.partb = i + 1; + fmmain(&FMPart[i+3]); + } + + pmdwork.fmsel = 0; + for(i = 0; i < 3; i++) { + pmdwork.partb = i + 1; + fmmain(&FMPart[i]); + } + rhythmmain(&RhythmPart); + + for(i = 0; i < 8; i++) { + pmdwork.partb = i; + } + if(pmdwork.loop_work == 0) return; + + for(i = 0; i < 6; i++) { + if(FMPart[i].loopcheck != 3) FMPart[i].loopcheck = 0; + } + + for(i = 0; i < 3; i++) { + if(SSGPart[i].loopcheck != 3) SSGPart[i].loopcheck = 0; + } + if(RhythmPart.loopcheck != 3) RhythmPart.loopcheck = 0; + + if(pmdwork.loop_work != 3) { + open_work.status2++; + if(open_work.status2 == 255) open_work.status2 = 1; + } else { + open_work.status2 = -1; + } +} + +//============================================================================= +// FM音源演奏メイン +//============================================================================= +void PMDWIN::fmmain(QQ *qq) +{ + uchar *si; + + if(qq->address == NULL) return; + + si = qq->address; + + qq->leng--; + + if(qq->partmask) { + qq->keyoff_flag = -1; + } else { + // KEYOFF CHECK & Keyoff + if((qq->keyoff_flag & 3) == 0) { // 既にkeyoffしたか? + if(qq->leng <= qq->qdat) { + keyoff(qq); + qq->keyoff_flag = -1; + } + } + } + + // LENGTH CHECK + if(qq->leng == 0) { + if(qq->partmask == 0) qq->lfoswi &= 0xf7; + + while(1) { + if(*si > 0x80 && *si != 0xda) { + si = commands(qq, si); + } else if(*si == 0x80) { + qq->address = si; + qq->loopcheck = 3; + qq->onkai = 255; + if(qq->partloop == NULL) { + if(qq->partmask) { + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + pmdwork.loop_work &= qq->loopcheck; + return; + } else { + break; + } + } + // "L"があった時 + si = qq->partloop; + qq->loopcheck = 1; + } else { + if(*si == 0xda) { // ポルタメント + si = porta(qq, ++si); + pmdwork.loop_work &= qq->loopcheck; + return; + } else if(qq->partmask == 0) { + // TONE SET + lfoinit(qq, *si); + fnumset(qq, oshift(qq, *si++)); + + qq->leng = *si++; + si = calc_q(qq, si); + + if(qq->volpush && qq->onkai != 255) { + if(--pmdwork.volpush_flag) { + pmdwork.volpush_flag = 0; + qq->volpush = 0; + } + } + + volset(qq); + otodasi(qq); + keyon(qq); + + qq->keyon_flag++; + qq->address = si; + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + + if(*si == 0xfb) { // '&'が直後にあったらkeyoffしない + qq->keyoff_flag = 2; + } else { + qq->keyoff_flag = 0; + } + pmdwork.loop_work &= qq->loopcheck; + return; + } else { + si++; + qq->fnum = 0; //休符に設定 + qq->onkai = 255; + qq->onkai_def = 255; + qq->leng = *si++; + qq->keyon_flag++; + qq->address = si; + + if(--pmdwork.volpush_flag) { + qq->volpush = 0; + } + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + break; + } + } + } + } + + if(qq->partmask == 0) { + + // LFO & Portament & Fadeout 処理 をして終了 + if(qq->hldelay_c) { + if(--qq->hldelay_c == 0) { + OPNASetReg(&opna, pmdwork.fmsel + + (pmdwork.partb - 1 + 0xb4), qq->fmpan); + } + } + + if(qq->sdelay_c) { + if(--qq->sdelay_c == 0) { + if((qq->keyoff_flag & 1) == 0) { // 既にkeyoffしたか? + keyon(qq); + } + } + } + + if(qq->lfoswi) { + pmdwork.lfo_switch = qq->lfoswi & 8; + if(qq->lfoswi & 3) { + if(lfo(qq)) { + pmdwork.lfo_switch |= (qq->lfoswi & 3); + } + } + + if(qq->lfoswi & 0x30) { + lfo_change(qq); + if(lfo(qq)) { + lfo_change(qq); + pmdwork.lfo_switch |= (qq->lfoswi & 0x30); + } else { + lfo_change(qq); + } + } + + if(pmdwork.lfo_switch & 0x19) { + if(pmdwork.lfo_switch & 8) { + porta_calc(qq); + + } + otodasi(qq); + } + + if(pmdwork.lfo_switch & 0x22) { + volset(qq); + pmdwork.loop_work &= qq->loopcheck; + return; + } + } + } + + pmdwork.loop_work &= qq->loopcheck; +} + + +//============================================================================= +// KEY OFF +//============================================================================= +void PMDWIN::keyoff(QQ *qq) +{ + if(qq->onkai == 255) return; + kof1(qq); +} + + +void PMDWIN::kof1(QQ *qq) +{ + if(pmdwork.fmsel == 0) { + pmdwork.omote_key[pmdwork.partb-1] + = (~qq->slotmask) & (pmdwork.omote_key[pmdwork.partb-1]); + OPNASetReg(&opna, 0x28, (pmdwork.partb-1) | pmdwork.omote_key[pmdwork.partb-1]); + } else { + pmdwork.ura_key[pmdwork.partb-1] + = (~qq->slotmask) & (pmdwork.ura_key[pmdwork.partb-1]); + OPNASetReg(&opna, 0x28, ((pmdwork.partb-1) | pmdwork.ura_key[pmdwork.partb-1]) | 4); + } +} + + +//============================================================================= +// FM KEYON +//============================================================================= +void PMDWIN::keyon(QQ *qq) +{ + int al; + + if(qq->onkai == 255) return; // キュウフ ノ トキ + uint32_t op = ((pmdwork.partb-1)&7)|(!!pmdwork.fmsel << 2); + switch(op) { + case 0: + pmdstatus[0] = (qq->onkai & 0x0f); + break; + case 1: + pmdstatus[1] = (qq->onkai & 0x0f); + break; + case 2: + pmdstatus[2] = (qq->onkai & 0x0f); + break; + case 4: + pmdstatus[3] = (qq->onkai & 0x0f); + break; + case 5: + pmdstatus[4] = (qq->onkai & 0x0f); + break; + case 6: + pmdstatus[5] = (qq->onkai & 0x0f); + break; + default: + break; + } + + if(pmdwork.fmsel == 0) { + al = pmdwork.omote_key[pmdwork.partb-1] | qq->slotmask; + if(qq->sdelay_c) { + al &= qq->sdelay_m; + } + + pmdwork.omote_key[pmdwork.partb-1] = al; + OPNASetReg(&opna, 0x28, (pmdwork.partb-1) | al); + } else { + al = pmdwork.ura_key[pmdwork.partb-1] | qq->slotmask; + if(qq->sdelay_c) { + al &= qq->sdelay_m; + } + + pmdwork.ura_key[pmdwork.partb-1] = al; + OPNASetReg(&opna, 0x28, ((pmdwork.partb-1) | al) | 4); + } +} + + +//============================================================================= +// Set [ FNUM/BLOCK + DETUNE + LFO ] +//============================================================================= +void PMDWIN::otodasi(QQ *qq) +{ + int ax, cx; + + if(qq->fnum == 0) return; + if(qq->slotmask == 0) return; + + cx = (qq->fnum & 0x3800); // cx=BLOCK + ax = (qq->fnum) & 0x7ff; // ax=FNUM + + // Portament/LFO/Detune SET + ax += qq->porta_num + qq->detune; + + if(pmdwork.partb == 3 && pmdwork.fmsel == 0 && open_work.ch3mode != 0x3f) { + ch3_special(qq, ax, cx); + } else { + if(qq->lfoswi & 1) { + ax += qq->lfodat; + } + + if(qq->lfoswi & 0x10) { + ax += qq->_lfodat; + } + + fm_block_calc(&cx, &ax); + + // SET BLOCK/FNUM TO OPN + + ax |= cx; + + OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xa4-1, ax >> 8); + OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xa4-5, ax & 0xff); + } +} + + +//============================================================================= +// FM音源のdetuneでオクターブが変わる時の修正 +// input CX:block / AX:fnum+detune +// output CX:block / AX:fnum +//============================================================================= +void PMDWIN::fm_block_calc(int *cx, int *ax) +{ + while(*ax >= 0x26a) { + if(*ax < (0x26a*2)) return; + + *cx += 0x800; // oct.up + if(*cx != 0x4000) { + *ax -= 0x26a; // 4d2h-26ah + } else { // モウ コレイジョウ アガンナイヨン + *cx = 0x3800; + if(*ax >= 0x800) + *ax = 0x7ff; // 4d2h + return; + } + } + + while(*ax < 0x26a) { + *cx -= 0x800; // oct.down + if(*cx >= 0) { + *ax += 0x26a; // 4d2h-26ah + } else { // モウ コレイジョウ サガンナイヨン + *cx = 0; + if(*ax < 8) { + *ax = 8; + } + return; + } + } +} + + +//============================================================================= +// パートを判別してch3ならmode設定 +//============================================================================= +int PMDWIN::ch3_setting(QQ *qq) +{ + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + ch3mode_set(qq); + return 1; + } else { + return 0; + } +} + + +void PMDWIN::cm_clear(int *ah, int *al) +{ + *al ^= 0xff; + if((pmdwork.slot3_flag &= *al) == 0) { + if(pmdwork.slotdetune_flag != 1) { + *ah = 0x3f; + } else { + *ah = 0x7f; + } + } else { + *ah = 0x7f; + } +} + + +//============================================================================= +// FM3のmodeを設定する +//============================================================================= +void PMDWIN::ch3mode_set(QQ *qq) +{ + int ah, al; + + if(qq == &FMPart[3-1]) { + al = 1; + } else { + al = 8; + } + + if((qq->slotmask & 0xf0) == 0) { // s0 + cm_clear(&ah, &al); + } else if(qq->slotmask != 0xf0) { + pmdwork.slot3_flag |= al; + ah = 0x7f; + } else if((qq->volmask & 0x0f) == 0) { + cm_clear(&ah, &al); + } else if((qq->lfoswi & 1) != 0) { + pmdwork.slot3_flag |= al; + ah = 0x7f; + } else if((qq->_volmask & 0x0f) == 0) { + cm_clear(&ah, &al); + } else if(qq->lfoswi & 0x10) { + pmdwork.slot3_flag |= al; + ah = 0x7f; + } else { + cm_clear(&ah, &al); + } + + if(ah == open_work.ch3mode) return; // 以前と変更無しなら何もしない + open_work.ch3mode = ah; + OPNASetReg(&opna, 0x27, ah & 0xcf); // Resetはしない + + // 効果音モードに移った場合はそれ以前のFM3パートで音程書き換え + if(ah == 0x3f || qq == &FMPart[2]) return; + if(FMPart[2].partmask == 0) otodasi(&FMPart[2]); +} + + +//============================================================================= +// ch3=効果音モード を使用する場合の音程設定 +// input CX:block AX:fnum +//============================================================================= +void PMDWIN::ch3_special(QQ *qq, int ax, int cx) +{ + int ax_, bh, ch, si; + int shiftmask = 0x80; + + si = cx; + + if((qq->volmask & 0x0f) == 0) { + bh = 0xf0; // all + } else { + bh = qq->volmask; // bh=lfo1 mask 4321xxxx + } + + if((qq->_volmask & 0x0f) == 0) { + ch = 0xf0; // all + } else { + ch = qq->_volmask; // ch=lfo2 mask 4321xxxx + } + + // slot 4 + if(qq->slotmask & 0x80) { + ax_ = ax; + ax += open_work.slot_detune4; + if((bh & shiftmask) && (qq->lfoswi & 0x01)) ax += qq->lfodat; + if((ch & shiftmask) && (qq->lfoswi & 0x10)) ax += qq->_lfodat; + shiftmask >>= 1; + + cx = si; + fm_block_calc(&cx, &ax); + ax |= cx; + + OPNASetReg(&opna, 0xa6, ax >> 8); + OPNASetReg(&opna, 0xa2, ax & 0xff); + + ax = ax_; + } + + // slot 3 + if(qq->slotmask & 0x40) { + ax_ = ax; + ax += open_work.slot_detune3; + if((bh & shiftmask) && (qq->lfoswi & 0x01)) ax += qq->lfodat; + if((ch & shiftmask) && (qq->lfoswi & 0x10)) ax += qq->_lfodat; + shiftmask >>= 1; + + cx = si; + fm_block_calc(&cx, &ax); + ax |= cx; + + OPNASetReg(&opna, 0xac, ax >> 8); + OPNASetReg(&opna, 0xa8, ax & 0xff); + + ax = ax_; + } + + // slot 2 + if(qq->slotmask & 0x20) { + ax_ = ax; + ax += open_work.slot_detune2; + if((bh & shiftmask) && (qq->lfoswi & 0x01)) ax += qq->lfodat; + if((ch & shiftmask) && (qq->lfoswi & 0x10)) ax += qq->_lfodat; + shiftmask >>= 1; + + cx = si; + fm_block_calc(&cx, &ax); + ax |= cx; + + OPNASetReg(&opna, 0xae, ax >> 8); + OPNASetReg(&opna, 0xaa, ax & 0xff); + + ax = ax_; + } + + // slot 1 + if(qq->slotmask & 0x10) { + ax_ = ax; + ax += open_work.slot_detune1; + if((bh & shiftmask) && (qq->lfoswi & 0x01)) ax += qq->lfodat; + if((ch & shiftmask) && (qq->lfoswi & 0x10)) ax += qq->_lfodat; + cx = si; + fm_block_calc(&cx, &ax); + ax |= cx; + + OPNASetReg(&opna, 0xad, ax >> 8); + OPNASetReg(&opna, 0xa9, ax & 0xff); + + ax = ax_; + } +} + + +//============================================================================= +// 'p' COMMAND [FM PANNING SET] +//============================================================================= +uchar * PMDWIN::panset(QQ *qq, uchar *si) +{ + panset_main(qq, *si++); + return si; +} + + +void PMDWIN::panset_main(QQ *qq, int al) +{ + qq->fmpan = ((qq->fmpan & 0x3f) | ((al << 6) & 0xc0)); + + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + // FM3の場合は 4つのパート総て設定 + FMPart[2].fmpan = qq->fmpan; + } + + if(qq->partmask == 0) { // パートマスクされているか? + // dl = al; + OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xb4-1, + calc_panout(qq)); + } +} + + +//============================================================================= +// 0b4h〜に設定するデータを取得 out.dl +//============================================================================= +uchar PMDWIN::calc_panout(QQ *qq) +{ + int dl; + + dl = qq->fmpan; + if(qq->hldelay_c) dl &= 0xc0; // HLFO Delayが残ってる場合はパンのみ設定 + return dl; +} + + +//============================================================================= +// Pan setting Extend +//============================================================================= +uchar * PMDWIN::panset_ex(QQ *qq, uchar *si) +{ + int al; + + al = *(char *)si++; + si++; // 逆走flagは読み飛ばす + + if(al > 0) { + al = 2; + panset_main(qq, al); + } else if(al == 0) { + al = 3; + panset_main(qq, al); + } else { + al = 1; + panset_main(qq, al); + } + return si; +} + + +//============================================================================= +// FM音源用 Entry +//============================================================================= +void PMDWIN::lfoinit(QQ *qq, int al) +{ + int ah; + + ah = al & 0x0f; + if(ah == 0x0c) { + al = qq->onkai_def; + ah = al & 0x0f; + } + + qq->onkai_def = al; + + if(ah == 0x0f) { // キューフ ノ トキ ハ INIT シナイヨ + lfo_exit(qq); + return; + } + + qq->porta_num = 0; // ポルタメントは初期化 + + if((pmdwork.tieflag & 1) == 0) { + lfin1(qq); + } else { + lfo_exit(qq); + } +} + + +//============================================================================= +// FM BLOCK,F-NUMBER SET +// INPUTS -- AL [KEY#,0-7F] +//============================================================================= +void PMDWIN::fnumset(QQ *qq, int al) +{ + int ax,bx; + + if((al & 0x0f) != 0x0f) { // 音符の場合 + qq->onkai = al; + + // BLOCK/FNUM CALICULATE + bx = al & 0x0f; // bx=onkai + ax = fnum_data[bx]; + + // BLOCK SET + ax |= (((al >> 1) & 0x38) << 8); + qq->fnum = ax; + } else { // 休符の場合 + qq->onkai = 255; + if((qq->lfoswi & 0x11) == 0) { + qq->fnum = 0; // 音程LFO未使用 + } + } +} + + +//============================================================================= +// FM音量設定メイン +//============================================================================= +void PMDWIN::volset(QQ *qq) +{ + int bh, bl, cl, dh; + uchar vol_tbl[4] = {0, 0, 0, 0}; + + if(qq->slotmask == 0) return; + if(qq->volpush) { + cl = qq->volpush - 1; + } else { + cl = qq->volume; + } + + //-------------------------------------------------------------------- + // 音量down計算 + //-------------------------------------------------------------------- + if(open_work.fm_voldown) { + cl = ((256-open_work.fm_voldown) * cl) >> 8; + } + + //------------------------------------------------------------------------ + // 音量をcarrierに設定 & 音量LFO処理 + // input cl to Volume[0-127] + // bl to SlotMask + //------------------------------------------------------------------------ + + bh=0; // Vol Slot Mask + bl=qq->slotmask; // ch=SlotMask Push + + vol_tbl[0] = 0x80; + vol_tbl[1] = 0x80; + vol_tbl[2] = 0x80; + vol_tbl[3] = 0x80; + + cl = 255-cl; // cl=carrierに設定する音量+80H(add) + bl &= qq->carrier; // bl=音量を設定するSLOT xxxx0000b + bh |= bl; + + if(bl & 0x80) vol_tbl[0] = cl; + if(bl & 0x40) vol_tbl[1] = cl; + if(bl & 0x20) vol_tbl[2] = cl; + if(bl & 0x10) vol_tbl[3] = cl; + + if(cl != 255) { + if(qq->lfoswi & 2) { + bl = qq->volmask; + bl &= qq->slotmask; // bl=音量LFOを設定するSLOT xxxx0000b + bh |= bl; + fmlfo_sub(qq, qq->lfodat, bl, vol_tbl); + } + + if(qq->lfoswi & 0x20) { + bl = qq->_volmask; + bl &= qq->slotmask; + bh |= bl; + fmlfo_sub(qq, qq->_lfodat, bl, vol_tbl); + } + } + + dh = 0x4c - 1 + pmdwork.partb; // dh=FM Port Address + + if(bh & 0x80) volset_slot(dh, qq->slot4, vol_tbl[0]); + if(bh & 0x40) volset_slot(dh-8, qq->slot3, vol_tbl[1]); + if(bh & 0x20) volset_slot(dh-4, qq->slot2, vol_tbl[2]); + if(bh & 0x10) volset_slot(dh-12, qq->slot1, vol_tbl[3]); +} + + +//----------------------------------------------------------------------------- +// スロット毎の計算 & 出力 マクロ +// in. dl 元のTL値 +// dh Outするレジスタ +// al 音量変動値 中心=80h +//----------------------------------------------------------------------------- +void PMDWIN::volset_slot(int dh, int dl, int al) +{ + if((al += dl) > 255) al = 255; + if((al -= 0x80) < 0) al = 0; + OPNASetReg(&opna, pmdwork.fmsel + dh, al); +} + + +//----------------------------------------------------------------------------- +// 音量LFO用サブ +//----------------------------------------------------------------------------- +void PMDWIN::fmlfo_sub(QQ *qq, int al, int bl, uchar *vol_tbl) +{ + if(bl & 0x80) vol_tbl[0] = Limit(vol_tbl[0] - al, 255, 0); + if(bl & 0x40) vol_tbl[1] = Limit(vol_tbl[1] - al, 255, 0); + if(bl & 0x20) vol_tbl[2] = Limit(vol_tbl[2] - al, 255, 0); + if(bl & 0x10) vol_tbl[3] = Limit(vol_tbl[3] - al, 255, 0); +} + + +//============================================================================= +// SSG音源 演奏 メイン +//============================================================================= +void PMDWIN::psgmain(QQ *qq) +{ + uchar *si; + int temp; + + if(qq->address == NULL) return; + si = qq->address; + + qq->leng--; + + // KEYOFF CHECK & Keyoff + if(qq->partmask) { + qq->keyoff_flag = -1; + } else { + if((qq->keyoff_flag & 3) == 0) { // 既にkeyoffしたか? + if(qq->leng <= qq->qdat) { + keyoffp(qq); + qq->keyoff_flag = -1; + } + } + } + + // LENGTH CHECK + if(qq->leng == 0) { + qq->lfoswi &= 0xf7; + + // DATA READ + while(1) { + if(*si == 0xda && ssgdrum_check(qq, *si) < 0) { + si++; + } else if(*si > 0x80 && *si != 0xda) { + si = commandsp(qq, si); + } else if(*si == 0x80) { + qq->address = si; + qq->loopcheck = 3; + qq->onkai = 255; + if(qq->partloop == NULL) { + if(qq->partmask) { + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + pmdwork.loop_work &= qq->loopcheck; + return; + } else { + break; + } + } + // "L"があった時 + si = qq->partloop; + qq->loopcheck = 1; + } else { + if(*si == 0xda) { // ポルタメント + si = portap(qq, ++si); + pmdwork.loop_work &= qq->loopcheck; + return; + } else if(qq->partmask) { + if(ssgdrum_check(qq, *si) == 0) { + si++; + qq->fnum = 0; //休符に設定 + qq->onkai = 255; + qq->leng = *si++; + qq->keyon_flag++; + qq->address = si; + + if(--pmdwork.volpush_flag) { + qq->volpush = 0; + } + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + break; + } + } + + // TONE SET + lfoinitp(qq, *si); + fnumsetp(qq, oshiftp(qq, *si++)); + + qq->leng = *si++; + si = calc_q(qq, si); + + if(qq->volpush && qq->onkai != 255) { + if(--pmdwork.volpush_flag) { + pmdwork.volpush_flag = 0; + qq->volpush = 0; + } + } + + volsetp(qq); + otodasip(qq); + keyonp(qq); + + qq->keyon_flag++; + qq->address = si; + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + qq->keyoff_flag = 0; + + if(*si == 0xfb) { // '&'が直後にあったらkeyoffしない + qq->keyoff_flag = 2; + } + pmdwork.loop_work &= qq->loopcheck; + return; + } + } + } + + pmdwork.lfo_switch = (qq->lfoswi & 8); + + if(qq->lfoswi) { + if(qq->lfoswi & 3) { + if(lfo(qq)) { + pmdwork.lfo_switch |= (qq->lfoswi & 3); + } + } + + if(qq->lfoswi & 0x30) { + lfo_change(qq); + if(lfo(qq)) { + lfo_change(qq); + pmdwork.lfo_switch |= (qq->lfoswi & 0x30); + } else { + lfo_change(qq); + } + } + + if(pmdwork.lfo_switch & 0x19) { + if(pmdwork.lfo_switch & 0x08) { + porta_calc(qq); + } + + // SSG 3ch で休符かつ SSG Drum 発音中は操作しない + if(!(qq == &SSGPart[2] && qq->onkai == 255 && open_work.kshot_dat)) { + otodasip(qq); + } + } + } + + temp = soft_env(qq); + if(temp || pmdwork.lfo_switch & 0x22) { + // SSG 3ch で休符かつ SSG Drum 発音中は volume set しない + if(!(qq == &SSGPart[2] && qq->onkai == 255 && open_work.kshot_dat)) { + volsetp(qq); + } + } + + pmdwork.loop_work &= qq->loopcheck; +} + + +void PMDWIN::keyoffp(QQ *qq) +{ + if(qq->onkai == 255) return; // キュウフ ノ トキ + if(qq->envf != -1) { + qq->envf = 2; + } else { + qq->eenv_count = 4; + } +} + + +//============================================================================= +// リズムパート 演奏 メイン +//============================================================================= +void PMDWIN::rhythmmain(QQ *qq) +{ + uchar *si, *bx; + int al, result; + + if(qq->address == NULL) return; + + si = qq->address; + + if(--qq->leng == 0) { + bx = open_work.rhyadr; + + rhyms00: + do { + result = 1; + al = *bx++; + if(al != 0xff) { + if(al & 0x80) { + bx = rhythmon(qq, bx, al, &result); + if(result == 0) continue; + } else { + open_work.kshot_dat = 0; //rest + } + + al = *bx++; + open_work.rhyadr = bx; + qq->leng = al; + qq->keyon_flag++; + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + pmdwork.loop_work &= qq->loopcheck; + return; + } + }while(result == 0); + + while(1) { + while((al = *si++) != 0x80) { + if(al < 0x80) { + qq->address = si; + +// bx = (ushort *)&open_work.radtbl[al]; +// bx = open_work.rhyadr = &open_work.mmlbuf[*bx]; + bx = open_work.rhyadr = &open_work.mmlbuf[open_work.radtbl[al]]; + goto rhyms00; + } + + // al > 0x80 + si = commandsr(qq, si-1); + } + + qq->address = --si; + qq->loopcheck = 3; + bx = qq->partloop; + if(bx == NULL) { + open_work.rhyadr = (uchar *)&pmdwork.rhydmy; + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + pmdwork.loop_work &= qq->loopcheck; + return; + } else { // "L"があった時 + si = bx; + qq->loopcheck = 1; + } + } + } + + pmdwork.loop_work &= qq->loopcheck; +} + + +//============================================================================= +// PSGリズム ON +//============================================================================= +uchar * PMDWIN::rhythmon(QQ *qq, uchar *bx, int al, int *result) +{ + int cl, dl, bx_; + + if(al & 0x40) { + bx = commandsr(qq, bx-1); + *result = 0; + return bx; + } + + *result = 1; + + if(qq->partmask) { // maskされている場合 + open_work.kshot_dat = 0; + return ++bx; + } + + al = ((al << 8) + *bx++) & 0x3fff; + open_work.kshot_dat = al; + if(al == 0) return bx; + open_work.rhyadr = bx; + + for(cl = 0; cl < 11; cl++) { + if(al & (1 << cl)) { + OPNASetReg(&opna, rhydat[cl][0], rhydat[cl][1]); + if((dl = (rhydat[cl][2] & open_work.rhythmmask))) { + if(dl < 0x80) { + OPNASetReg(&opna, 0x10, dl); + } else { + OPNASetReg(&opna, 0x10, 0x84); + dl = open_work.rhythmmask & 8; + if(dl) { + OPNASetReg(&opna, 0x10, dl); + } + } + } + } + } + + bx_ = al; + al = 0; + do { + while((bx_ & 1) == 0) { + bx_ >>= 1; + al++; + } + eff_main(al); + bx_ >>= 1; + }while(pmdwork.ppsdrv_flag && bx_); // PPSDRVなら2音目以上も鳴らしてみる + + return open_work.rhyadr; +} + + +//============================================================================= +// PSG ドラムス&効果音 ルーチン +// From WT298 +// +// AL に 効果音No.を入れて CALLする +// ppsdrvがあるならそっちを鳴らす +//============================================================================= +void PMDWIN::eff_main(int al) +{ + int priority; + pmdstatus[8] = al; + effwork.psgefcnum = al; + if (al == 7 || al == 8) return; // PSG hihat sounds godawful, don't play it at all + priority = ((al > 10) ? 2 : 1); + if(effwork.effon <= priority) { + SSGPart[2].partmask |= 2; // Part Mask + efffor(efftbl[al]); // 1発目を発音 + effwork.effon = priority; + // 優先順位を設定(発音開始) + } +} + + +//============================================================================= +// こーかおん えんそう めいん +// From VRTC +//============================================================================= +void PMDWIN::effplay(void) +{ + if(--effwork.effcnt) { + effsweep(); // 新しくセットされない + } else { + efffor(effwork.effadr); + } +} + + +void PMDWIN::efffor(const int *si) +{ + int al, ch, cl; + + al = *si++; + if(al == -1) { + effend(); + } else { + effwork.effcnt = al; // カウント数 + cl = *si; + PSGSetReg(&(opna.psg), 4, *si++); // 周波数セット + ch = *si; + PSGSetReg(&(opna.psg), 5, *si++); // 周波数セット + effwork.eswthz = (ch << 8) + cl; + + open_work.psnoi_last = effwork.eswnhz = *si; + PSGSetReg(&(opna.psg), 6, *si++); // ノイズ + + PSGSetReg(&(opna.psg), 7, ((*si++ << 2) & 0x24) | (PSGGetReg(&(opna.psg), 0x07) & 0xdb)); + + PSGSetReg(&(opna.psg), 10, *si++); // ボリューム + PSGSetReg(&(opna.psg), 11, *si++); // エンベロープ周波数 + PSGSetReg(&(opna.psg), 12, *si++); // + PSGSetReg(&(opna.psg), 13, *si++); // エンベロープPATTERN + + effwork.eswtst = *si++; // スイープ増分 (TONE) + effwork.eswnst = *si++; // スイープ増分 (NOISE) + + effwork.eswnct = effwork.eswnst & 15; // スイープカウント (NOISE) + + effwork.effadr = (int *)si; + } +} + + +void PMDWIN::effend(void) +{ + PSGSetReg(&(opna.psg), 0x0a, 0x00); + PSGSetReg(&(opna.psg), 0x07, ((PSGGetReg(&(opna.psg), 0x07)) & 0xdb) | 0x24); + effwork.effon = 0; + effwork.psgefcnum = -1; +} + + +// 普段の処理 +void PMDWIN::effsweep(void) +{ + int dl; + + effwork.eswthz += effwork.eswtst; + PSGSetReg(&(opna.psg), 4, effwork.eswthz & 0xff); + PSGSetReg(&(opna.psg), 5, effwork.eswthz >> 8); + + if(effwork.eswnst == 0) return; // ノイズスイープ無し + if(--effwork.eswnct) return; + + dl = effwork.eswnst; + effwork.eswnct = dl & 15; + + effwork.eswnhz += dl / 16; + + PSGSetReg(&(opna.psg), 6, effwork.eswnhz); + open_work.psnoi_last = effwork.eswnhz; +} + + +//============================================================================= +// SSGドラムを消してSSGを復活させるかどうかcheck +// input AL <- Command +// output cy=1 : 復活させる +//============================================================================= +int PMDWIN::ssgdrum_check(QQ *qq, int al) +{ + // SSGマスク中はドラムを止めない + // SSGドラムは鳴ってない + if((qq->partmask & 1) || ((qq->partmask & 2) == 0)) return 0; + + // 普通の効果音は消さない + if(effwork.effon >= 2) return 0; + + al = (al & 0x0f); + + // 休符の時はドラムは止めない + if(al == 0x0f) return 0; + + // SSGドラムはまだ再生中か? + if(effwork.effon == 1) { + effend(); // SSGドラムを消す + } + + if((qq->partmask &= 0xfd) == 0) return -1; + return 0; +} + + +//============================================================================= +// 各種特殊コマンド処理 +//============================================================================= +uchar * PMDWIN::commands(QQ *qq, uchar *si) +{ + int al; + + al = *si++; + switch(al) { + case 0xff : si = comat(qq, si); break; + case 0xfe : qq->qdata = *si++; qq->qdat3 = 0; break; + case 0xfd : qq->volume = *si++; break; + case 0xfc : si = comt(si); break; + + case 0xfb : pmdwork.tieflag |= 1; break; + case 0xfa : qq->detune = *(short *)si; si+=2; break; + case 0xf9 : si = comstloop(qq, si); break; + case 0xf8 : si = comedloop(qq, si); break; + case 0xf7 : si = comexloop(qq, si); break; + case 0xf6 : qq->partloop = si; break; + case 0xf5 : qq->shift = *(char *)si++; break; + case 0xf4 : if((qq->volume+=4) > 127) qq->volume = 127; break; + case 0xf3 : if(qq->volume < 4) qq->volume=0; else qq->volume-=4; break; + case 0xf2 : si = lfoset(qq, si); break; + case 0xf1 : si = lfoswitch(qq, si); ch3_setting(qq); break; + case 0xf0 : si+=4; break; + + case 0xef : OPNASetReg(&opna, pmdwork.fmsel + *si, *(si+1)); si+=2; break; + case 0xee : si++; break; + case 0xed : si++; break; + case 0xec : si = panset(qq, si); break; // FOR SB2 + case 0xeb : si = rhykey(si); break; + case 0xea : si = rhyvs(si); break; + case 0xe9 : si = rpnset(si); break; + case 0xe8 : si = rmsvs(si); break; + // + case 0xe7 : qq->shift += *(char *)si++; break; + case 0xe6 : si = rmsvs_sft(si); break; + case 0xe5 : si = rhyvs_sft(si); break; + // + case 0xe4 : qq->hldelay = *si++; break; + //追加 for V2.3 + case 0xe3 : if((qq->volume += *si++) > 127) qq->volume = 127; break; + case 0xe2 : + if(qq->volume < *si) qq->volume = 0; else qq->volume -= *si; + si++; + break; + // + case 0xe1 : si = hlfo_set(qq, si); break; + case 0xe0 : open_work.port22h = *si; OPNASetReg(&opna, 0x22, *si++); break; + // + case 0xdf : open_work.syousetu_lng = *si++; break; + // + case 0xde : si = vol_one_up_fm(qq, si); break; + case 0xdd : si = vol_one_down(qq, si); break; + // + case 0xdc : open_work.status = *si++; break; + case 0xdb : open_work.status += *si++; break; + // + case 0xda : si = porta(qq, si); break; + // + case 0xd9 : si++; break; + case 0xd8 : si++; break; + case 0xd7 : si++; break; + // + case 0xd6 : qq->mdspd = qq->mdspd2 = *si++; qq->mdepth = *(char *)si++; break; + case 0xd5 : qq->detune += *(short *)si; si+=2; break; + // + case 0xd4 : si++; break; + case 0xd3 : si++; break; + case 0xd2 : + si++; + break; + // + case 0xd1 : si++; break; + case 0xd0 : si++; break; + // + case 0xcf : si = slotmask_set(qq, si); break; + case 0xce : si+=6; break; + case 0xcd : si+=5; break; + case 0xcc : si++; break; + case 0xcb : qq->lfo_wave = *si++; break; + case 0xca : + qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1); + break; + + case 0xc9 : si++; break; + case 0xc8 : si = slotdetune_set(qq, si); break; + case 0xc7 : si = slotdetune_set2(qq, si); break; + case 0xc6 : break; + case 0xc5 : si = volmask_set(qq, si); break; + case 0xc4 : qq->qdatb = *si++; break; + case 0xc3 : si = panset_ex(qq, si); break; + case 0xc2 : qq->delay = qq->delay2 = *si++; lfoinit_main(qq); break; + case 0xc1 : break; + case 0xc0 : si = fm_mml_part_mask(qq, si); break; + case 0xbf : lfo_change(qq); si = lfoset(qq, si); lfo_change(qq);break; + case 0xbe : si = _lfoswitch(qq, si); ch3_setting(qq); break; + case 0xbd : + lfo_change(qq); + qq->mdspd = qq->mdspd2 = *si++; + qq->mdepth = *(char *)si++; + lfo_change(qq); + break; + + case 0xbc : lfo_change(qq); qq->lfo_wave=*si++; lfo_change(qq); break; + case 0xbb : + lfo_change(qq); + qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1); + lfo_change(qq); + break; + + case 0xba : si = _volmask_set(qq, si); break; + case 0xb9 : + lfo_change(qq); + qq->delay = qq->delay2 = *si++; lfoinit_main(qq); + lfo_change(qq); + break; + + case 0xb8 : si = tl_set(qq, si); break; + case 0xb7 : si = mdepth_count(qq, si); break; + case 0xb6 : si = fb_set(qq, si); break; + case 0xb5 : + qq->sdelay_m = (~(*si++) << 4) & 0xf0; + qq->sdelay_c = qq->sdelay = *si++; + break; + + case 0xb4 : si+=16; break; + case 0xb3 : qq->qdat2 = *si++; break; + case 0xb2 : qq->shift_def = *(char *)si++; break; + case 0xb1 : qq->qdat3 = *si++; break; + + default : + si--; + *si = 0x80; + } + + return si; +} + + +uchar * PMDWIN::commandsp(QQ *qq, uchar *si) +{ + int al; + + al = *si++; + switch(al) { + case 0xff : si++; break; + case 0xfe : qq->qdata = *si++; qq->qdat3 = 0; break; + case 0xfd : qq->volume = *si++; break; + case 0xfc : si = comt(si); break; + + case 0xfb : pmdwork.tieflag |= 1; break; + case 0xfa : qq->detune = *(short *)si; si+=2; break; + case 0xf9 : si = comstloop(qq, si); break; + case 0xf8 : si = comedloop(qq, si); break; + case 0xf7 : si = comexloop(qq, si); break; + case 0xf6 : qq->partloop = si; break; + case 0xf5 : qq->shift = *(char *)si++; break; + case 0xf4 : if(qq->volume < 15) qq->volume++; break; + case 0xf3 : if(qq->volume > 0) qq->volume--; break; + case 0xf2 : si = lfoset(qq, si); break; + case 0xf1 : si = lfoswitch(qq, si); break; + case 0xf0 : si = psgenvset(qq, si); break; + + case 0xef : OPNASetReg(&opna, *si, *(si+1)); si+=2; break; + case 0xee : open_work.psnoi = *si++; break; + case 0xed : qq->psgpat = *si++; break; + // + case 0xec : si++; break; + case 0xeb : si = rhykey(si); break; + case 0xea : si = rhyvs(si); break; + case 0xe9 : si = rpnset(si); break; + case 0xe8 : si = rmsvs(si); break; + // + case 0xe7 : qq->shift += *(char *)si++; break; + case 0xe6 : si = rmsvs_sft(si); break; + case 0xe5 : si = rhyvs_sft(si); break; + // + case 0xe4 : si++; break; + //追加 for V2.3 + case 0xe3 : if((qq->volume + *si) < 16) qq->volume += *si; si++;break; + case 0xe2 : if((qq->volume - *si) >= 0) qq->volume -= *si; si++;break; + // + case 0xe1 : si++; break; + case 0xe0 : si++; break; + // + case 0xdf : open_work.syousetu_lng = *si++; break; + // + case 0xde : si = vol_one_up_psg(qq, si); break; + case 0xdd : si = vol_one_down(qq, si); break; + // + case 0xdc : open_work.status = *si++; break; + case 0xdb : open_work.status += *si++; break; + // + case 0xda : si = portap(qq, si); break; + // + case 0xd9 : si++; break; + case 0xd8 : si++; break; + case 0xd7 : si++; break; + // + case 0xd6 : qq->mdspd = qq->mdspd2 = *si++; qq->mdepth = *(char *)si++; break; + case 0xd5 : qq->detune += *(short *)si; si+=2; break; + // + case 0xd4 : si++; break; + case 0xd3 : si++; break; + case 0xd2 : + si++; + break; + // + case 0xd1 : si++; break; + case 0xd0 : si = psgnoise_move(si); break; + // + case 0xcf : si++; break; + case 0xce : si+=6; break; + case 0xcd : si = extend_psgenvset(qq, si); break; + case 0xcc : + qq->extendmode = (qq->extendmode & 0xfe) | (*si++ & 1); + break; + + case 0xcb : qq->lfo_wave = *si++; break; + case 0xca : + qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1); + break; + + case 0xc9 : + qq->extendmode = (qq->extendmode & 0xfb) | ((*si++ & 1) << 2); + break; + + case 0xc8 : si+=3; break; + case 0xc7 : si+=3; break; + case 0xc6 : si+=6; break; + case 0xc5 : si++; break; + case 0xc4 : qq->qdatb = *si++; break; + case 0xc3 : si+=2; break; + case 0xc2 : qq->delay = qq->delay2 = *si++; lfoinit_main(qq); break; + case 0xc1 : break; + case 0xc0 : si = ssg_mml_part_mask(qq, si); break; + case 0xbf : lfo_change(qq); si = lfoset(qq, si); lfo_change(qq);break; + case 0xbe : + qq->lfoswi = (qq->lfoswi & 0x8f) | ((*si++ & 7) << 4); + lfo_change(qq); lfoinit_main(qq); lfo_change(qq); + break; + + case 0xbd : + lfo_change(qq); + qq->mdspd = qq->mdspd2 = *si++; + qq->mdepth = *(char *)si++; + lfo_change(qq); + break; + + case 0xbc : lfo_change(qq); qq->lfo_wave=*si++; lfo_change(qq); break; + case 0xbb : + lfo_change(qq); + qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1); + lfo_change(qq); + break; + + case 0xba : si++; break; + case 0xb9 : + lfo_change(qq); + qq->delay = qq->delay2 = *si++; lfoinit_main(qq); break; + lfo_change(qq); + break; + + case 0xb8 : si+=2; break; + case 0xb7 : si = mdepth_count(qq, si); break; + case 0xb6 : si++; break; + case 0xb5 : si+= 2; break; + case 0xb4 : si+=16; break; + case 0xb3 : qq->qdat2 = *si++; break; + case 0xb2 : qq->shift_def = *(char *)si++; break; + case 0xb1 : qq->qdat3 = *si++; break; + + default : + si--; + *si = 0x80; + } + + return si; +} + + +uchar * PMDWIN::commandsr(QQ *qq, uchar *si) +{ + int al; + + al = *si++; + switch(al) { + case 0xff : si++; break; + case 0xfe : si++; break; + case 0xfd : qq->volume = *si++; break; + case 0xfc : si = comt(si); break; + + case 0xfb : pmdwork.tieflag |= 1; break; + case 0xfa : qq->detune = *(short *)si; si+=2; break; + case 0xf9 : si = comstloop(qq, si); break; + case 0xf8 : si = comedloop(qq, si); break; + case 0xf7 : si = comexloop(qq, si); break; + case 0xf6 : qq->partloop = si; break; + case 0xf5 : si++; break; + case 0xf4 : if(qq->volume < 15) qq->volume++; break; + case 0xf3 : if(qq->volume > 0) qq->volume--; break; + case 0xf2 : si+=4; break; + case 0xf1 : si++; break; + case 0xf0 : si+=4; break; + + case 0xef : OPNASetReg(&opna, *si, *(si+1)); si+=2; break; + case 0xee : si++; break; + case 0xed : si++; break; + // + case 0xec : si++; break; + case 0xeb : si = rhykey(si); break; + case 0xea : si = rhyvs(si); break; + case 0xe9 : si = rpnset(si); break; + case 0xe8 : si = rmsvs(si); break; + // + case 0xe7 : si++; break; + case 0xe6 : si = rmsvs_sft(si); break; + case 0xe5 : si = rhyvs_sft(si); break; + // + case 0xe4 : si++; break; + //追加 for V2.3 + case 0xe3 : if((qq->volume + *si) < 16) qq->volume += *si; si++;break; + case 0xe2 : if((qq->volume - *si) >= 0) qq->volume -= *si; si++;break; + // + case 0xe1 : si++; break; + case 0xe0 : si++; break; + // + case 0xdf : open_work.syousetu_lng = *si++; break; + // + case 0xde : si = vol_one_up_psg(qq, si); break; + case 0xdd : si = vol_one_down(qq, si); break; + // + case 0xdc : open_work.status = *si++; break; + case 0xdb : open_work.status += *si++; break; + // + case 0xda : si++; break; + // + case 0xd9 : si++; break; + case 0xd8 : si++; break; + case 0xd7 : si++; break; + // + case 0xd6 : si+=2; break; + case 0xd5 : qq->detune += *(short *)si; si+=2; break; + // + case 0xd4 : si++; break; + case 0xd3 : si++; break; + case 0xd2 : + si++; + break; + // + case 0xd1 : si++; break; + case 0xd0 : si++; break; + // + case 0xcf : si++; break; + case 0xce : si+=6; break; + case 0xcd : si+=5; break; + case 0xcc : si++; break; + case 0xcb : si++; break; + case 0xca : si++; break; + case 0xc9 : si++; break; + case 0xc8 : si+=3; break; + case 0xc7 : si+=3; break; + case 0xc6 : si+=6; break; + case 0xc5 : si++; break; + case 0xc4 : si++; break; + case 0xc3 : si+=2; break; + case 0xc2 : si++; break; + case 0xc1 : break; + case 0xc0 : si = rhythm_mml_part_mask(qq, si); break; + case 0xbf : si+=4; break; + case 0xbe : si++; break; + case 0xbd : si+=2; break; + case 0xbc : si++; break; + case 0xbb : si++; break; + case 0xba : si++; break; + case 0xb9 : si++; break; + case 0xb8 : si+=2; break; + case 0xb7 : si++; break; + case 0xb6 : si++; break; + case 0xb5 : si+= 2; break; + case 0xb4 : si+=16; break; + case 0xb3 : si++; break; + case 0xb2 : si++; break; + case 0xb1 : si++; break; + + default : + si--; + *si = 0x80; + } + + return si; +} + +//============================================================================= +// COMMAND '@' [PROGRAM CHANGE] +//============================================================================= +uchar * PMDWIN::comat(QQ *qq, uchar *si) +{ + uchar *bx; + int al, dl; + + qq->voicenum = al = *si++; + dl = qq->voicenum; + + if(qq->partmask == 0) { // パートマスクされているか? + neiroset(qq, dl); + return si; + } else { + bx = toneadr_calc(qq, dl); + qq->alg_fb = dl = bx[24]; + bx += 4; + + // tl設定 + qq->slot1 = bx[0]; + qq->slot3 = bx[1]; + qq->slot2 = bx[2]; + qq->slot4 = bx[3]; + + // FM3chで、マスクされていた場合、fm3_alg_fbを設定 + if(pmdwork.partb == 3 && qq->neiromask) { + if(pmdwork.fmsel == 0) { + // in. dl = alg/fb + if((qq->slotmask & 0x10) == 0) { + al = pmdwork.fm3_alg_fb & 0x38; // fbは前の値を使用 + dl = (dl & 7) | al; + } + + pmdwork.fm3_alg_fb = dl; + qq->alg_fb = al; + } + } + + } + return si; +} + +//============================================================================= +// 音色の設定 +// INPUTS -- [PARTB] +// -- dl [TONE_NUMBER] +// -- di [PART_DATA_ADDRESS] +//============================================================================= +void PMDWIN::neiroset(QQ *qq, int dl) +{ + uchar *bx; + int ah, al, dh; + + bx = toneadr_calc(qq, dl); + if(silence_fmpart(qq)) { + // neiromask=0の時 (TLのworkのみ設定) + bx += 4; + + // tl設定 + qq->slot1 = bx[0]; + qq->slot3 = bx[1]; + qq->slot2 = bx[2]; + qq->slot4 = bx[3]; + return; + } + + //========================================================================= + // 音色設定メイン + //========================================================================= + //------------------------------------------------------------------------- + // AL/FBを設定 + //------------------------------------------------------------------------- + + dh = 0xb0 - 1 + pmdwork.partb; + + if(pmdwork.af_check) { // ALG/FBは設定しないmodeか? + dl = qq->alg_fb; + } else { + dl = bx[24]; + } + + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + if(pmdwork.af_check != 0) { // ALG/FBは設定しないmodeか? + dl = pmdwork.fm3_alg_fb; + } else { + if((qq->slotmask & 0x10) == 0) { // slot1を使用しているか? + dl = (pmdwork.fm3_alg_fb & 0x38) | (dl & 7); + } + pmdwork.fm3_alg_fb = dl; + } + } + + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + qq->alg_fb = dl; + dl &= 7; // dl = algo + + //------------------------------------------------------------------------- + // Carrierの位置を調べる (VolMaskにも設定) + //------------------------------------------------------------------------- + + if((qq->volmask & 0x0f) == 0) { + qq->volmask = carrier_table[dl]; + } + + if((qq->_volmask & 0x0f) == 0) { + qq->_volmask = carrier_table[dl]; + } + + qq->carrier = carrier_table[dl]; + ah = carrier_table[dl+8]; // slot2/3の逆転データ(not済み) + al = qq->neiromask; + ah &= al; // AH=TL用のmask / AL=その他用のmask + + //------------------------------------------------------------------------- + // 各音色パラメータを設定 (TLはモジュレータのみ) + //------------------------------------------------------------------------- + + dh = 0x30 - 1 + pmdwork.partb; + dl = *bx++; // DT/ML + if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; // TL + if(ah & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(ah & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(ah & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(ah & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; // KS/AR + if(al & 0x08) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x04) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x02) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x01) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; // AM/DR + if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; // SR + if(al & 0x08) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x04) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x02) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x01) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; // SL/RR + if(al & 0x80) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + dh+=4; + + dl = *bx++; + if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + +/* + dl = *bx++; // SL/RR + if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; + + dl = *bx++; + if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + dh+=4; +*/ + + //------------------------------------------------------------------------- + // SLOT毎のTLをワークに保存 + //------------------------------------------------------------------------- + bx -= 20; + qq->slot1 = bx[0]; + qq->slot3 = bx[1]; + qq->slot2 = bx[2]; + qq->slot4 = bx[3]; +} + + +//============================================================================= +// [PartB]のパートの音を完璧に消す (TL=127 and RR=15 and KEY-OFF) +// cy=1 ・・・ 全スロットneiromaskされている +//============================================================================= +int PMDWIN::silence_fmpart(QQ *qq) +{ + int dh; + + if(qq->neiromask == 0) { + return 1; + } + + dh = pmdwork.partb + 0x40 - 1; + + if(qq->neiromask & 0x80) { + OPNASetReg(&opna, pmdwork.fmsel + dh, 127); + OPNASetReg(&opna, (pmdwork.fmsel + 0x40) + dh, 127); + } + dh += 4; + + if(qq->neiromask & 0x40) { + OPNASetReg(&opna, pmdwork.fmsel + dh, 127); + OPNASetReg(&opna, pmdwork.fmsel + 0x40 + dh, 127); + } + dh += 4; + + if(qq->neiromask & 0x20) { + OPNASetReg(&opna, pmdwork.fmsel + dh, 127); + OPNASetReg(&opna, pmdwork.fmsel + 0x40 + dh, 127); + } + dh += 4; + + if(qq->neiromask & 0x10) { + OPNASetReg(&opna, pmdwork.fmsel + dh, 127); + OPNASetReg(&opna, pmdwork.fmsel + 0x40 + dh, 127); + } + + kof1(qq); + return 0; +} + + +//============================================================================= +// TONE DATA START ADDRESS を計算 +// input dl tone_number +// output bx address +//============================================================================= +uchar * PMDWIN::toneadr_calc(QQ *qq, int dl) +{ + uchar *bx; + + if(open_work.prg_flg == 0) { + return NULL; + //return open_work.tondat + (dl << 5); + } else { + bx = open_work.prgdat_adr; + + while(*bx != dl) { + bx += 26; + } + + return bx+1; + } +} + + +//============================================================================= +// FM音源ハードLFOの設定(V2.4拡張分) +//============================================================================= +uchar * PMDWIN::hlfo_set(QQ *qq, uchar *si) +{ + qq->fmpan = (qq->fmpan & 0xc0) | *si++; + + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + // 2608の時のみなので part_eはありえない + // FM3の場合は 4つのパート総て設定 + FMPart[2].fmpan = qq->fmpan; + } + + if(qq->partmask == 0) { // パートマスクされているか? + OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb + 0xb4 - 1, + calc_panout(qq)); + } + return si; +} + + +//============================================================================= +// ボリュームを次の一個だけ変更(V2.7拡張分) +//============================================================================= +uchar * PMDWIN::vol_one_up_fm(QQ *qq, uchar *si) +{ + int al; + + al = (int)qq->volume + 1 + *si++; + if(al > 128) al = 128; + + qq->volpush = al; + pmdwork.volpush_flag = 1; + return si; +} + + +//============================================================================= +// ポルタメント(FM) +//============================================================================= +uchar * PMDWIN::porta(QQ *qq, uchar *si) +{ + int ax, cx, cl, bx, bh; + + if(qq->partmask) { + qq->fnum = 0; //休符に設定 + qq->onkai = 255; + qq->leng = *(si+2); + qq->keyon_flag++; + qq->address = si+3; + + if(--pmdwork.volpush_flag) { + qq->volpush = 0; + } + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + pmdwork.loop_work &= qq->loopcheck; + + return si+3; // 読み飛ばす (Mask時) + } + + lfoinit(qq, *si); + fnumset(qq, oshift(qq, *si++)); + + cx = qq->fnum; + cl = qq->onkai; + fnumset(qq, oshift(qq, *si++)); + bx = qq->fnum; // bx=ポルタメント先のfnum値 + qq->onkai = cl; + qq->fnum = cx; // cx=ポルタメント元のfnum値 + + bh = (int)((bx / 256) & 0x38) - ((cx / 256) & 0x38); // 先のoctarb - 元のoctarb + if(bh) { + bh /= 8; + ax = bh * 0x26a; // ax = 26ah * octarb差 + } else { + ax = 0; + } + + bx = (bx & 0x7ff) - (cx & 0x7ff); + ax += bx; // ax=26ah*octarb差 + 音程差 + + qq->leng = *si++; + si = calc_q(qq, si); + + qq->porta_num2 = ax / qq->leng; // 商 + qq->porta_num3 = ax % qq->leng; // 余り + qq->lfoswi |= 8; // Porta ON + + if(qq->volpush && qq->onkai != 255) { + if(--pmdwork.volpush_flag) { + pmdwork.volpush_flag = 0; + qq->volpush = 0; + } + } + + volset(qq); + otodasi(qq); + keyon(qq); + + qq->keyon_flag++; + qq->address = si; + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + + if(*si == 0xfb) { // '&'が直後にあったらkeyoffしない + qq->keyoff_flag = 2; + } else { + qq->keyoff_flag = 0; + } + pmdwork.loop_work &= qq->loopcheck; + return si; +} + + +//============================================================================= +// FM slotmask set +//============================================================================= +uchar * PMDWIN::slotmask_set(QQ *qq, uchar *si) +{ + uchar *bx; + int ah, al, bl; + + ah = al = *si++; + + if(al &= 0x0f) { + qq->carrier = al << 4; + } else { + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + bl = pmdwork.fm3_alg_fb; + } else { + bx = toneadr_calc(qq, qq->voicenum); + bl = bx[24]; + } + qq->carrier = carrier_table[bl & 7]; + } + + ah &= 0xf0; + if(qq->slotmask != ah) { + qq->slotmask = ah; + if((ah & 0xf0) == 0) { + qq->partmask |= 0x20; // s0の時パートマスク + } else { + qq->partmask &= 0xdf; // s0以外の時パートマスク解除 + } + + if(ch3_setting(qq)) { // FM3chの場合のみ ch3modeの変更処理 + // ch3なら、それ以前のFM3パートでkeyon処理 + if(qq != &FMPart[2]) { + if(FMPart[2].partmask == 0 && + (FMPart[2].keyoff_flag & 1) == 0) { + keyon(&FMPart[2]); + } + } + } + + ah = 0; + if(qq->slotmask & 0x80) ah += 0x11; // slot4 + if(qq->slotmask & 0x40) ah += 0x44; // slot3 + if(qq->slotmask & 0x20) ah += 0x22; // slot2 + if(qq->slotmask & 0x10) ah += 0x88; // slot1 + qq->neiromask = ah; + } + return si; +} + + +//============================================================================= +// Slot Detune Set +//============================================================================= +uchar * PMDWIN::slotdetune_set(QQ *qq, uchar *si) +{ + int ax, bl; + + if(pmdwork.partb != 3 || pmdwork.fmsel) { + return si+3; + } + + bl = *si++; + ax = *(short *)si; + si+=2; + + if(bl & 1) { + open_work.slot_detune1 = ax; + } + + if(bl & 2) { + open_work.slot_detune2 = ax; + } + + if(bl & 4) { + open_work.slot_detune3 = ax; + } + + if(bl & 8) { + open_work.slot_detune4 = ax; + } + + if(open_work.slot_detune1 || open_work.slot_detune2 || + open_work.slot_detune3 || open_work.slot_detune4) { + pmdwork.slotdetune_flag = 1; + } else { + pmdwork.slotdetune_flag = 0; + open_work.slot_detune1 = 0; + } + ch3mode_set(qq); + return si; +} + + +//============================================================================= +// Slot Detune Set(相対) +//============================================================================= +uchar * PMDWIN::slotdetune_set2(QQ *qq, uchar *si) +{ + int ax, bl; + + if(pmdwork.partb != 3 || pmdwork.fmsel) { + return si+3; + } + + bl = *si++; + ax = *(short *)si; + si+=2; + + if(bl & 1) { + open_work.slot_detune1 += ax; + } + + if(bl & 2) { + open_work.slot_detune2 += ax; + } + + if(bl & 4) { + open_work.slot_detune3 += ax; + } + + if(bl & 8) { + open_work.slot_detune4 += ax; + } + + if(open_work.slot_detune1 || open_work.slot_detune2 || + open_work.slot_detune3 || open_work.slot_detune4) { + pmdwork.slotdetune_flag = 1; + } else { + pmdwork.slotdetune_flag = 0; + open_work.slot_detune1 = 0; + } + ch3mode_set(qq); + return si; +} + + +//============================================================================= +// 音量マスクslotの設定 +//============================================================================= +uchar * PMDWIN::volmask_set(QQ *qq, uchar *si) +{ + int al; + + al = *si++ & 0x0f; + if(al) { + al = (al << 4) | 0x0f; + qq->volmask = al; + } else { + qq->volmask = qq->carrier; + } + ch3_setting(qq); + return si; +} + + +//============================================================================= +// 0c0hの追加special命令 +//============================================================================= +uchar * PMDWIN::special_0c0h(QQ *qq, uchar *si, uchar al) +{ + switch(al) { + case 0xff : open_work.fm_voldown = *si++; break; + case 0xfe : si = _vd_fm(qq, si); break; + case 0xfd : open_work.ssg_voldown = *si++; break; + case 0xfc : si = _vd_ssg(qq, si); break; + case 0xfb : si++; break; + case 0xfa : si = NULL; break; + case 0xf9 : open_work.rhythm_voldown = *si++; break; + case 0xf8 : si = _vd_rhythm(qq, si); break; + case 0xf7 : si++; break; + case 0xf6 : si++; break; + case 0xf5 : break; + default : + si--; + *si = 0x80; + } + return si; +} + + +uchar * PMDWIN::_vd_fm(QQ *qq, uchar *si) +{ + int al; + + al = *(char *)si++; + if(al) { + open_work.fm_voldown = Limit(al + open_work.fm_voldown, 255, 0); + } else { + open_work.fm_voldown = open_work._fm_voldown; + } + return si; +} + + +uchar * PMDWIN::_vd_ssg(QQ *qq, uchar *si) +{ + int al; + + al = *(char *)si++; + if(al) { + open_work.ssg_voldown = Limit(al + open_work.ssg_voldown, 255, 0); + } else { + open_work.ssg_voldown = open_work._ssg_voldown; + } + return si; +} + + +uchar * PMDWIN::_vd_rhythm(QQ *qq, uchar *si) +{ + int al; + + al = *(char *)si++; + if(al) { + open_work.rhythm_voldown = Limit(al + open_work.rhythm_voldown, 255, 0); + } else { + open_work.rhythm_voldown = open_work._rhythm_voldown; + } + return si; +} + + +//============================================================================= +// 演奏中パートのマスクon/off +//============================================================================= +uchar * PMDWIN::fm_mml_part_mask(QQ *qq, uchar *si) +{ + int al; + + al = *si++; + if(al >= 2) { + si = special_0c0h(qq, si, al); + } else if(al) { + qq->partmask |= 0x40; + if(qq->partmask == 0x40) { + silence_fmpart(qq); // 音消去 + } + } else { + if((qq->partmask &= 0xbf) == 0) { + neiro_reset(qq); // 音色再設定 + } + } + return si; +} + + +uchar * PMDWIN::ssg_mml_part_mask(QQ *qq, uchar *si) +{ + int ah, al; + + al = *si++; + if(al >= 2) { + si = special_0c0h(qq, si, al); + } else if(al) { + qq->partmask |= 0x40; + if(qq->partmask == 0x40) { + ah = ((1 << (pmdwork.partb-1)) | (4 << pmdwork.partb)); + al = PSGGetReg(&(opna.psg), 0x07); + PSGSetReg(&(opna.psg), 0x07, ah | al); // PSG keyoff + } + } else { + qq->partmask &= 0xbf; + } + return si; +} + + +uchar * PMDWIN::rhythm_mml_part_mask(QQ *qq, uchar *si) +{ + int al; + + al = *si++; + if(al >= 2) { + si = special_0c0h(qq, si, al); + } else if(al) { + qq->partmask |= 0x40; + } else { + qq->partmask &= 0xbf; + } + return si; +} + + +//============================================================================= +// FM音源の音色を再設定 +//============================================================================= +void PMDWIN::neiro_reset(QQ *qq) +{ + int dh, al, s1, s2, s3, s4; + + if(qq->neiromask == 0) return; + + s1 = qq->slot1; + s2 = qq->slot2; + s3 = qq->slot3; + s4 = qq->slot4; + pmdwork.af_check = 1; + neiroset(qq, qq->voicenum); // 音色復帰 + pmdwork.af_check = 0; + qq->slot1 = s1; + qq->slot2 = s2; + qq->slot3 = s3; + qq->slot4 = s4; + + al = ((~qq->carrier) & qq->slotmask) & 0xf0; + // al<- TLを再設定していいslot 4321xxxx + if(al) { + dh = 0x4c - 1 + pmdwork.partb; // dh=TL FM Port Address + if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, s4); + + dh -= 8; + if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, s3); + + dh += 4; + if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, s2); + + dh -= 8; + if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, s1); + } + + dh = pmdwork.partb + 0xb4 - 1; + OPNASetReg(&opna, pmdwork.fmsel + dh, calc_panout(qq)); +} + + +uchar * PMDWIN::_lfoswitch(QQ *qq, uchar *si) +{ + qq->lfoswi = (qq->lfoswi & 0x8f) | ((*si++ & 7) << 4); + lfo_change(qq); + lfoinit_main(qq); + lfo_change(qq); + return si; +} + + +uchar * PMDWIN::_volmask_set(QQ *qq, uchar *si) +{ + int al; + + al = *si++ & 0x0f; + if(al) { + al = (al << 4) | 0x0f; + qq->_volmask = al; + } else { + qq->_volmask = qq->carrier; + } + ch3_setting(qq); + return si; +} + + +//============================================================================= +// TL変化 +//============================================================================= +uchar * PMDWIN::tl_set(QQ *qq, uchar *si) +{ + int ah, al, ch, dh, dl; + + dh = 0x40 - 1 + pmdwork.partb; // dh=TL FM Port Address + al = *(char *)si++; + ah = al & 0x0f; + ch = (qq->slotmask >> 4) | ((qq->slotmask << 4) & 0xf0); + ah &= ch; // ah=変化させるslot 00004321 + dl = *(char *)si++; + + if(al >= 0) { + dl &= 127; + if(ah & 1) { + qq->slot1 = dl; + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + } + + dh += 8; + if(ah & 2) { + qq->slot2 = dl; + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + } + + dh -= 4; + if(ah & 4) { + qq->slot3 = dl; + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + } + + dh += 8; + if(ah & 8) { + qq->slot4 = dl; + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + } + } else { + // 相対変化 + al = dl; + if(ah & 1) { + if((dl = (int)qq->slot1 + al) < 0) { + dl = 0; + if(al >= 0) dl = 127; + } + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + qq->slot1 = dl; + } + + dh += 8; + if(ah & 2) { + if((dl = (int)qq->slot2 + al) < 0) { + dl = 0; + if(al >= 0) dl = 127; + } + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + qq->slot2 = dl; + } + + dh -= 4; + if(ah & 4) { + if((dl = (int)qq->slot3 + al) < 0) { + dl = 0; + if(al >= 0) dl = 127; + } + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + qq->slot3 = dl; + } + + dh += 8; + if(ah & 8) { + if((dl = (int)qq->slot4 + al) < 0) { + dl = 0; + if(al >= 0) dl = 127; + } + if(qq->partmask == 0) { + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + } + qq->slot4 = dl; + } + } + return si; +} + + +//============================================================================= +// FB変化 +//============================================================================= +uchar * PMDWIN::fb_set(QQ *qq, uchar *si) +{ + int al, dh, dl; + + + dh = pmdwork.partb + 0xb0 - 1; // dh=ALG/FB port address + al = *(char *)si++; + if(al >= 0) { + // in al 00000xxx 設定するFB + al = ((al << 3) & 0xff) | (al >> 5); + + // in al 00xxx000 設定するFB + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + if((qq->slotmask & 0x10) == 0) return si; + dl = (pmdwork.fm3_alg_fb & 7) | al; + pmdwork.fm3_alg_fb = dl; + } else { + dl = (qq->alg_fb & 7) | al; + } + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + qq->alg_fb = dl; + return si; + } else { + if((al & 0x40) == 0) al &= 7; + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + dl = pmdwork.fm3_alg_fb; + } else { + dl = qq->alg_fb; + + } + + dl = (dl >> 3) & 7; + if((al += dl) >= 0) { + if(al >= 8) { + al = 0x38; + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + if((qq->slotmask & 0x10) == 0) return si; + + dl = (pmdwork.fm3_alg_fb & 7) | al; + pmdwork.fm3_alg_fb = dl; + } else { + dl = (qq->alg_fb & 7) | al; + } + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + qq->alg_fb = dl; + return si; + } else { + // in al 00000xxx 設定するFB + al = ((al << 3) & 0xff) | (al >> 5); + + // in al 00xxx000 設定するFB + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + if((qq->slotmask & 0x10) == 0) return si; + dl = (pmdwork.fm3_alg_fb & 7) | al; + pmdwork.fm3_alg_fb = dl; + } else { + dl = (qq->alg_fb & 7) | al; + } + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + qq->alg_fb = dl; + return si; + } + } else { + al = 0; + if(pmdwork.partb == 3 && pmdwork.fmsel == 0) { + if((qq->slotmask & 0x10) == 0) return si; + + dl = (pmdwork.fm3_alg_fb & 7) | al; + pmdwork.fm3_alg_fb = dl; + } else { + dl = (qq->alg_fb & 7) | al; + } + OPNASetReg(&opna, pmdwork.fmsel + dh, dl); + qq->alg_fb = dl; + return si; + } + } +} + + +//============================================================================= +// COMMAND 't' [TEMPO CHANGE1] +// COMMAND 'T' [TEMPO CHANGE2] +// COMMAND 't±' [TEMPO CHANGE 相対1] +// COMMAND 'T±' [TEMPO CHANGE 相対2] +//============================================================================= +uchar * PMDWIN::comt(uchar *si) +{ + int al; + + al = *si++; + if(al < 251) { + open_work.tempo_d = al; // T (FC) + open_work.tempo_d_push = al; + calc_tb_tempo(); + + } else if(al == 0xff) { + al = *si++; // t (FC FF) + if(al < 18) al = 18; + open_work.tempo_48 = al; + open_work.tempo_48_push = al; + calc_tempo_tb(); + + } else if(al == 0xfe) { + al = char(*si++); // T± (FC FE) + if(al >= 0) { + al += open_work.tempo_d_push; + } else { + al += open_work.tempo_d_push; + if(al < 0) { + al = 0; + } + } + + if(al > 250) al = 250; + + open_work.tempo_d = al; + open_work.tempo_d_push = al; + calc_tb_tempo(); + + } else { + al = char(*si++); // t± (FC FD) + if(al >= 0) { + al += open_work.tempo_48_push; + if(al > 255) { + al = 255; + } + } else { + al += open_work.tempo_48_push; + if(al < 0) al = 18; + } + open_work.tempo_48 = al; + open_work.tempo_48_push = al; + calc_tempo_tb(); + } + return si; +} + + +//============================================================================= +// COMMAND '[' [ループ スタート] +//============================================================================= +uchar * PMDWIN::comstloop(QQ *qq, uchar *si) +{ + uchar *ax = open_work.mmlbuf; + ax[*(ushort *)si+1] = 0; + si+=2; + return si; +} + + +//============================================================================= +// COMMAND ']' [ループ エンド] +//============================================================================= +uchar * PMDWIN::comedloop(QQ *qq, uchar *si) +{ + int ah, al, ax; + ah = *si++; + + if(ah) { + (*si)++; + al = *si++; + if(ah == al) { + si+=2; + return si; + } + } else { // 0 ナラ ムジョウケン ループ + si++; + qq->loopcheck = 1; + } + + ax = *(ushort *)si + 2; + si = open_work.mmlbuf + ax; + return si; +} + + +//============================================================================= +// COMMAND ':' [ループ ダッシュツ] +//============================================================================= +uchar * PMDWIN::comexloop(QQ *qq, uchar *si) +{ + uchar *bx; + int dl; + bx = open_work.mmlbuf; + bx += *(ushort *)si; + si+=2; + + dl = *bx++ - 1; + if(dl != *bx) return si; + si = bx + 3; + return si; +} + + +//============================================================================= +// LFO パラメータ セット +//============================================================================= +uchar * PMDWIN::lfoset(QQ *qq, uchar *si) +{ + qq->delay = *si; + qq->delay2 = *si++; + qq->speed = *si; + qq->speed2 = *si++; + qq->step = *(char *)si; + qq->step2 = *(char *)si++; + qq->time = *si; + qq->time2 = *si++; + lfoinit_main(qq); + return si; +} + + +//============================================================================= +// LFO SWITCH +//============================================================================= +uchar * PMDWIN::lfoswitch(QQ *qq, uchar *si) +{ + int al; + + al = *si++; + + if(al & 0xf8) { + al = 1; + } + al &= 7; + qq->lfoswi = (qq->lfoswi & 0xf8) | al; + lfoinit_main(qq); + return si; +} + + +//============================================================================= +// PSG ENVELOPE SET +//============================================================================= +uchar * PMDWIN::psgenvset(QQ *qq, uchar *si) +{ + qq->eenv_ar = *si; qq->eenv_arc = *si++; + qq->eenv_dr = *(char *)si++; + qq->eenv_sr = *si; qq->eenv_src = *si++; + qq->eenv_rr = *si; qq->eenv_rrc = *si++; + if(qq->envf == -1) { + qq->envf = 2; // RR + qq->eenv_volume = -15; // volume + } + return si; +} + + +//============================================================================= +// "¥?" COMMAND [ OPNA Rhythm Keyon/Dump ] +//============================================================================= +uchar * PMDWIN::rhykey(uchar *si) +{ + int dl; + + if((dl = (*si++ & open_work.rhythmmask)) == 0) { + return si; + } + + if(dl < 0x80) { + if(dl & 0x01) OPNASetReg(&opna, 0x18, open_work.rdat[0]); + if(dl & 0x02) OPNASetReg(&opna, 0x19, open_work.rdat[1]); + if(dl & 0x04) OPNASetReg(&opna, 0x1a, open_work.rdat[2]); + if(dl & 0x08) OPNASetReg(&opna, 0x1b, open_work.rdat[3]); + if(dl & 0x10) OPNASetReg(&opna, 0x1c, open_work.rdat[4]); + if(dl & 0x20) OPNASetReg(&opna, 0x1d, open_work.rdat[5]); + } + + OPNASetReg(&opna, 0x10, dl); + if(dl >= 0x80) { + open_work.rshot_dat &= (~dl); + } else { + open_work.rshot_dat |= dl; + } + + return si; +} + + +//============================================================================= +// "¥v?n" COMMAND +//============================================================================= +uchar * PMDWIN::rhyvs(uchar *si) +{ + int *bx; + int dh, dl; + + dl = *si & 0x1f; + dh = *si++ >> 5; + bx = &open_work.rdat[dh-1]; + dh = 0x18 - 1 + dh; + dl |= (*bx & 0xc0); + *bx = dl; + + OPNASetReg(&opna, dh, dl); + + return si; +} + + +uchar * PMDWIN::rhyvs_sft(uchar *si) +{ + int *bx; + int al, dh, dl; + + bx = &open_work.rdat[*si-1]; + dh = *si++ + 0x18 - 1; + dl = *bx & 0x1f; + + al = (*(char *)si++ + dl); + if(al >= 32) { + al = 31; + } else if(al < 0) { + al = 0; + } + + dl = (al &= 0x1f); + dl = *bx = ((*bx & 0xe0) | dl); + OPNASetReg(&opna, dh, dl); + + return si; +} + + +//============================================================================= +// "¥p?" COMMAND +//============================================================================= +uchar * PMDWIN::rpnset(uchar *si) +{ + int *bx; + int dh, dl; + + dl = (*si & 3) << 6; + + dh = (*si++ >> 5) & 0x07; + bx = &open_work.rdat[dh-1]; + + dh += 0x18 - 1; + dl |= (*bx & 0x1f); + *bx = dl; + OPNASetReg(&opna, dh, dl); + + return si; +} + + +//============================================================================= +// "¥Vn" COMMAND +//============================================================================= +uchar * PMDWIN::rmsvs(uchar *si) +{ + int dl; + dl = *si++; + if(open_work.rhythm_voldown) { + dl = ((256 - open_work.rhythm_voldown) * dl) >> 8; + } + open_work.rhyvol = dl; + OPNASetReg(&opna, 0x11, dl); + return si; +} + + +uchar *PMDWIN::rmsvs_sft(uchar *si) +{ + int dl; + dl = open_work.rhyvol + *(char *)si++; + if(dl >= 64) { + if(dl & 0x80) { + dl = 0; + } else { + dl = 63; + } + } + open_work.rhyvol = dl; + OPNASetReg(&opna, 0x11, dl); + return si; +} + + +//============================================================================= +// ボリュームを次の一個だけ変更(V2.7拡張分) +//============================================================================= +uchar * PMDWIN::vol_one_up_psg(QQ *qq, uchar *si) +{ + int al; + + al = qq->volume + *si++; + if(al > 15) al = 15; + qq->volpush = ++al; + pmdwork.volpush_flag = 1; + return si; +} + + +uchar * PMDWIN::vol_one_down(QQ *qq, uchar *si) +{ + int al; + + al = qq->volume - *si++; + if(al < 0) { + al = 0; + } else { + if(al >= 255) al = 254; + } + + qq->volpush = ++al; + pmdwork.volpush_flag = 1; + return si; +} + + +//============================================================================= +// ポルタメント(PSG) +//============================================================================= +uchar * PMDWIN::portap(QQ *qq, uchar *si) +{ + int ax, al_, bx_; + + if(qq->partmask) { + qq->fnum = 0; //休符に設定 + qq->onkai = 255; + qq->leng = *(si+2); + qq->keyon_flag++; + qq->address = si+3; + + if(--pmdwork.volpush_flag) { + qq->volpush = 0; + } + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + pmdwork.loop_work &= qq->loopcheck; + return si+3; // 読み飛ばす (Mask時) + } + + lfoinitp(qq, *si); + fnumsetp(qq, oshiftp(qq, *si++)); + + bx_ = qq->fnum; + al_ = qq->onkai; + fnumsetp(qq, oshiftp(qq, *si++)); + ax = qq->fnum; // ax = ポルタメント先のpsg_tune値 + + qq->onkai = al_; + qq->fnum = bx_; // bx = ポルタメント元のpsg_tune値 + ax -= bx_; + + qq->leng = *si++; + si = calc_q(qq, si); + + qq->porta_num2 = ax / qq->leng; // 商 + qq->porta_num3 = ax % qq->leng; // 余り + qq->lfoswi |= 8; // Porta ON + + if(qq->volpush && qq->onkai != 255) { + if(--pmdwork.volpush_flag) { + pmdwork.volpush_flag = 0; + qq->volpush = 0; + } + } + + volsetp(qq); + otodasip(qq); + keyonp(qq); + + qq->keyon_flag++; + qq->address = si; + + pmdwork.tieflag = 0; + pmdwork.volpush_flag = 0; + qq->keyoff_flag = 0; + + if(*si == 0xfb) { // '&'が直後にあったらkeyoffしない + qq->keyoff_flag = 2; + } + + pmdwork.loop_work &= qq->loopcheck; + return si; +} + + +//============================================================================= +// 'w' COMMAND [PSG NOISE ヘイキン シュウハスウ] +//============================================================================= +uchar * PMDWIN::psgnoise_move(uchar *si) +{ + open_work.psnoi += *(char *)si++; + if(open_work.psnoi < 0) open_work.psnoi = 0; + if(open_work.psnoi > 31) open_work.psnoi = 31; + return si; +} + + +//============================================================================= +// PSG Envelope set (Extend) +//============================================================================= +uchar * PMDWIN::extend_psgenvset(QQ *qq, uchar *si) +{ + qq->eenv_ar = *si++ & 0x1f; + qq->eenv_dr = *si++ & 0x1f; + qq->eenv_sr = *si++ & 0x1f; + qq->eenv_rr = *si & 0x0f; + qq->eenv_sl = ((*si++ >> 4) & 0x0f) ^ 0x0f; + qq->eenv_al = *si++ & 0x0f; + + if(qq->envf != -1) { // ノーマル>拡張に移行したか? + qq->envf = -1; + qq->eenv_count = 4; // RR + qq->eenv_volume = 0; // Volume + } + return si; +} + + +uchar * PMDWIN::mdepth_count(QQ *qq, uchar *si) +{ + int al; + + al = *si++; + + if(al >= 0x80) { + if((al &= 0x7f) == 0) al = 255; + qq->_mdc = al; + qq->_mdc2 = al; + return si; + } + + if(al == 0) al = 255; + qq->mdc = al; + qq->mdc2 = al; + return si; +} + + +//============================================================================= +// LFOとPSG/PCMのソフトウエアエンベロープの初期化 +//============================================================================= +//============================================================================= +// PSG/PCM音源用 Entry +//============================================================================= +void PMDWIN::lfoinitp(QQ *qq, int al) +{ + int ah; + + ah = al & 0x0f; + + if(ah == 0x0c) { + al = qq->onkai_def; + ah = al & 0x0f; + } + + qq->onkai_def = al; + + if(ah == 0x0f) { // キューフ ノ トキ ハ INIT シナイヨ + lfo_exit(qq); + return; + } + + qq->porta_num = 0; // ポルタメントは初期化 + + if(pmdwork.tieflag & 1) { + lfo_exit(qq); + return; + } + + //------------------------------------------------------------------------ + // ソフトウエアエンベロープ初期化 + //------------------------------------------------------------------------ + if(qq->envf != -1) { + qq->envf = 0; + qq->eenv_volume = 0; + qq->eenv_ar = qq->eenv_arc; + + if(qq->eenv_ar == 0) { + qq->envf = 1; // ATTACK=0 ... スグ Decay ニ + qq->eenv_volume = qq->eenv_dr; + } + + qq->eenv_sr = qq->eenv_src; + qq->eenv_rr = qq->eenv_rrc; + lfin1(qq); + + } else { + // 拡張ssg_envelope用 + + qq->eenv_arc = qq->eenv_ar - 16; + if(qq->eenv_dr < 16) { + qq->eenv_drc = (qq->eenv_dr-16)*2; + } else { + qq->eenv_drc = qq->eenv_dr-16; + } + + if(qq->eenv_sr < 16) { + qq->eenv_src = (qq->eenv_sr-16)*2; + } else { + qq->eenv_src = qq->eenv_sr-16; + } + + qq->eenv_rrc = (qq->eenv_rr) * 2 - 16; + qq->eenv_volume = qq->eenv_al; + qq->eenv_count = 1; + if(qq->eenv_arc > 0) { + qq->eenv_volume += qq->eenv_arc; + if(qq->eenv_volume < 15) { + qq->eenv_arc = qq->eenv_ar-16; + } else { + qq->eenv_volume = 15; + qq->eenv_count++; + if(qq->eenv_sl == 15) qq->eenv_count++; // SL=0の場合はすぐSRに + } + } else { + if(qq->eenv_ar != 0) qq->eenv_arc++; + } + lfin1(qq); + } +} + + +void PMDWIN::lfo_exit(QQ *qq) +{ + if((qq->lfoswi & 3) != 0) { // 前が & の場合 -> 1回 LFO処理 + lfo(qq); + } + + if((qq->lfoswi & 0x30) != 0) { // 前が & の場合 -> 1回 LFO処理 + lfo_change(qq); + lfo(qq); + lfo_change(qq); + } +} + + +//============================================================================= +// LFO初期化 +//============================================================================= +void PMDWIN::lfin1(QQ *qq) +{ + qq->hldelay_c = qq->hldelay; + + if(qq->hldelay) { + OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xb4-1, (qq->fmpan) & 0xc0); + } + + qq->sdelay_c = qq->sdelay; + + if(qq->lfoswi & 3) { // LFOは未使用 + if((qq->lfoswi & 4) == 0) { //keyon非同期か? + lfoinit_main(qq); + } + lfo(qq); + } + + if(qq->lfoswi & 0x30) { // LFOは未使用 + if((qq->lfoswi & 0x40) == 0) { //keyon非同期か? + lfo_change(qq); + lfoinit_main(qq); + lfo_change(qq); + } + + lfo_change(qq); + lfo(qq); + lfo_change(qq); + } +} + + +void PMDWIN::lfoinit_main(QQ *qq) +{ + qq->lfodat = 0; + qq->delay = qq->delay2; + qq->speed = qq->speed2; + qq->step = qq->step2; + qq->time = qq->time2; + qq->mdc = qq->mdc2; + + if(qq->lfo_wave == 2 || qq->lfo_wave == 3) { // 矩形波 or ランダム波? + qq->speed = 1; // delay直後にLFOが掛かるようにする + } else { + qq->speed++; // それ以外の場合はdelay直後のspeed値を +1 + } +} + + +//============================================================================= +// SHIFT[di] 分移調する +//============================================================================= +int PMDWIN::oshiftp(QQ *qq, int al) +{ + return oshift(qq, al); +} + + +int PMDWIN::oshift(QQ *qq, int al) +{ + int bl, bh, dl; + + if(al == 0x0f) return al; + + if(al == 0x0c) { + if((al = qq->onkai) >= 0x80) { + return 0x0f; + } else { + return al; //@暫定対応 + } + } + + dl = qq->shift + qq->shift_def; + if(dl == 0) return al; + + bl = (al & 0x0f); // bl = ONKAI + bh = (al & 0xf0) >> 4; // bh = OCT + + if(dl < 0) { + // - ホウコウ シフト + if((bl += dl) < 0) { + do { + bh--; + } while((bl+=12) < 0); + } + if(bh < 0) bh = 0; + return (bh << 4) | bl; + + } else { + // + ホウコウ シフト + bl += dl; + while(bl >= 0x0c) { + bh++; + bl -= 12; + } + + if(bh > 7) bh = 7; + return (bh << 4) | bl; + } +} + + +//============================================================================= +// PSG TUNE SET +//============================================================================= +void PMDWIN::fnumsetp(QQ *qq, int al) +{ + int ax, bx, cl; + + if((al & 0x0f) == 0x0f) { // キュウフ ナラ FNUM ニ 0 ヲ セット + qq->onkai = 255; + if(qq->lfoswi & 0x11) return; + qq->fnum = 0; // 音程LFO未使用 + return; + } + + qq->onkai = al; + + cl = (al >> 4) & 0x0f; // cl=oct + bx = al & 0x0f; // bx=onkai + + ax = psg_tune_data[bx]; + if(cl > 0) { + ax = (ax + 1) >> cl; + } + + qq->fnum = ax; +} + + +//============================================================================= +// Q値の計算 +// break dx +//============================================================================= +uchar * PMDWIN::calc_q(QQ *qq, uchar *si) +{ + int dh, dl; + + if(*si == 0xc1) { // && + si++; + qq->qdat = 0; + return si; + } + + dl = qq->qdata; + if(qq->qdatb) { + dl += (qq->leng * qq->qdatb) >> 8; + } + +#if 0 + if(qq->qdat3) { // Random-Q + ax = rand() % ((qq->qdat3 & 0x7f) + 1); + if((qq->qdat3 & 0x80) == 0) { + dl += ax; + } else { + dl -= ax; + if(dl < 0) dl = 0; + } + } +#endif + + if(qq->qdat2) { + if((dh = qq->leng - qq->qdat2) < 0) { + qq->qdat = 0; + return si; + } + + if(dl < dh) { + qq->qdat = dl; + } else { + qq->qdat = dh; + } + } else { + qq->qdat = dl; + } + + return si; +} + + +//============================================================================= +// PSG VOLUME SET +//============================================================================= +void PMDWIN::volsetp(QQ *qq) +{ + int ax, dl; + + if(qq->envf == 3 || (qq->envf == -1 && qq->eenv_count == 0)) return; + + if(qq->volpush) { + dl = qq->volpush-1; + } else { + dl = qq->volume; + } + + //------------------------------------------------------------------------ + // 音量down計算 + //------------------------------------------------------------------------ + dl = ((256-open_work.ssg_voldown) * dl) >> 8; + + //------------------------------------------------------------------------ + // ENVELOPE 計算 + //------------------------------------------------------------------------ + if(dl <= 0) { + OPNASetReg(&opna, pmdwork.partb+8-1, 0); + return; + } + + if(qq->envf == -1) { + if(qq->eenv_volume == 0) { + OPNASetReg(&opna, pmdwork.partb+8-1, 0); + return; + } + dl = ((((dl * (qq->eenv_volume + 1))) >> 3) + 1) >> 1; + } else { + dl += qq->eenv_volume; + if(dl <= 0) { + OPNASetReg(&opna, pmdwork.partb+8-1, 0); + return; + } + if(dl > 15) dl = 15; + } + + //-------------------------------------------------------------------- + // 音量LFO計算 + //-------------------------------------------------------------------- + if((qq->lfoswi & 0x22) == 0) { + OPNASetReg(&opna, pmdwork.partb+8-1, dl); + return; + } + + if(qq->lfoswi & 2) { + ax = qq->lfodat; + } else { + ax = 0; + } + + if(qq->lfoswi & 0x20) { + ax += qq->_lfodat; + } + + dl = dl + ax; + if(dl < 0) { + OPNASetReg(&opna, pmdwork.partb+8-1, 0); + return; + } + if(dl > 15) dl = 15; + + //------------------------------------------------------------------------ + // 出力 + //------------------------------------------------------------------------ + OPNASetReg(&opna, pmdwork.partb+8-1, dl); +} + + +//============================================================================= +// PSG 音程設定 +//============================================================================= +void PMDWIN::otodasip(QQ *qq) +{ + int ax, dx; + + if(qq->fnum == 0) return; + + // PSG Portament set + + ax = qq->fnum + qq->porta_num; + dx = 0; + + // PSG Detune/LFO set + if((qq->extendmode & 1) == 0) { + ax -= qq->detune; + if(qq->lfoswi & 1) { + ax -= qq->lfodat; + } + + if(qq->lfoswi & 0x10) { + ax -= qq->_lfodat; + } + } else { + // 拡張DETUNE(DETUNE)の計算 + if(qq->detune) { + dx = (ax * qq->detune) >> 12; // dx:ax=ax * qq->detune + if(dx >= 0) { + dx++; + } else { + dx--; + } + ax -= dx; + } + // 拡張DETUNE(LFO)の計算 + if(qq->lfoswi & 0x11) { + if(qq->lfoswi & 1) { + dx = qq->lfodat; + } else { + dx = 0; + } + + if(qq->lfoswi & 0x10) { + dx += qq->_lfodat; + } + + if(dx) { + dx = (ax * dx) >> 12; + if(dx >= 0) { + dx++; + } else { + dx--; + } + } + ax -= dx; + } + } + + // TONE SET + + if(ax >= 0x1000) { + if(ax >= 0) { + ax = 0xfff; + } else { + ax = 0; + } + } + + OPNASetReg(&opna, (pmdwork.partb-1) * 2, ax & 0xff); + OPNASetReg(&opna, (pmdwork.partb-1) * 2 + 1, ax >> 8); +} + + +//============================================================================= +// PSG KEYON +//============================================================================= +void PMDWIN::keyonp(QQ *qq) +{ + int ah, al; + + if(qq->onkai == 255) return; // キュウフ ノ トキ + int op = ((pmdwork.partb-1)&3); + switch(op) { + case 0: + pmdstatus[6] = (qq->onkai & 0x0f); + break; + case 1: + pmdstatus[7] = (qq->onkai & 0x0f); + break; + case 2: + pmdstatus[8] = (qq->onkai & 0x0f); + break; + default: + break; + } + + ah=(1 << (pmdwork.partb -1)) | (1 << (pmdwork.partb +2)); + al = PSGGetReg(&(opna.psg), 0x07) | ah; + ah = ~(ah & qq->psgpat); + al &= ah; + PSGSetReg(&(opna.psg), 7, al); + + // PSG ノイズ シュウハスウ ノ セット + if(open_work.psnoi != open_work.psnoi_last && effwork.effon == 0) { + PSGSetReg(&(opna.psg), 6, open_work.psnoi); + open_work.psnoi_last = open_work.psnoi; + } +} + + +//============================================================================= +// LFO処理 +// Don't Break cl +// output cy=1 変化があった +//============================================================================= +int PMDWIN::lfo(QQ *qq) +{ + int ax, ch; + + if(qq->delay) { + qq->delay--; + return 0; + } + + if(qq->extendmode & 2) { // TimerAと合わせるか? + // そうじゃないなら無条件にlfo処理 + ch = open_work.TimerAtime - pmdwork.lastTimerAtime; + if(ch == 0) return 0; + ax = qq->lfodat; + + for(; ch > 0; ch--) { + lfo_main(qq); + } + } else { + + ax = qq->lfodat; + lfo_main(qq); + } + + if(ax == qq->lfodat) { + return 0; + } + return 1; +} + + +void PMDWIN::lfo_main(QQ *qq) +{ + int al, ax; + + if(qq->speed != 1) { + if(qq->speed != 255) qq->speed--; + return; + } + + qq->speed = qq->speed2; + + if(qq->lfo_wave == 0 || qq->lfo_wave == 4 || qq->lfo_wave == 5) { + // 三角波 lfowave = 0,4,5 + if(qq->lfo_wave == 5) { + ax = abs(qq->step) * qq->step; + } else { + ax = qq->step; + } + + if((qq->lfodat += ax) == 0) { + md_inc(qq); + } + + al = qq->time; + if(al != 255) { + if(--al == 0) { + al = qq->time2; + if(qq->lfo_wave != 4) { + al += al; // lfowave=0,5の場合 timeを反転時2倍にする + } + qq->time = al; + qq->step = -qq->step; + return; + } + } + qq->time = al; + + } else if(qq->lfo_wave == 2) { + // 矩形波 lfowave = 2 + qq->lfodat = (qq->step * qq->time); + md_inc(qq); + qq->step = -qq->step; + + } else if(qq->lfo_wave == 6) { + // ワンショット lfowave = 6 + if(qq->time) { + if(qq->time != 255) { + qq->time--; + } + qq->lfodat += qq->step; + } + } else if(qq->lfo_wave == 1) { + //ノコギリ波 lfowave = 1 + qq->lfodat += qq->step; + al = qq->time; + if(al != -1) { + al--; + if(al == 0) { + qq->lfodat = -qq->lfodat; + md_inc(qq); + al = (qq->time2) * 2; + } + } + qq->time = al; + + } else { + // ランダム波 lfowave = 3 + ax = abs(qq->step) * qq->time; + qq->lfodat = ax - (lfg_rand() % (ax * 2)); + md_inc(qq); + } +} + + +//============================================================================= +// MDコマンドの値によってSTEP値を変更 +//============================================================================= +void PMDWIN::md_inc(QQ *qq) +{ + int al; + + if(--qq->mdspd) return; + + qq->mdspd = qq->mdspd2; + + if(qq->mdc == 0) return; // count = 0 + if(qq->mdc <= 127) { + qq->mdc--; + } + + if(qq->step < 0) { + al = qq->mdepth - qq->step; + if(al >= 0) { + qq->step = -al; + } else { + if(qq->mdepth < 0) { + qq->step = 0; + } else { + qq->step = -127; + } + } + } else { + al = qq->step + qq->mdepth; + if(al >= 0) { + qq->step = al; + } else { + if(qq->mdepth < 0) { + qq->step = 0; + } else { + qq->step = 127; + } + } + } +} + + +void PMDWIN::swap(int *a, int *b) +{ + int temp; + + temp = *a; + *a = *b; + *b = temp; +} + + +//============================================================================= +// LFO1<->LFO2 change +//============================================================================= +void PMDWIN::lfo_change(QQ *qq) +{ + swap(&qq->lfodat, &qq->_lfodat); + qq->lfoswi = ((qq->lfoswi & 0x0f) << 4) + (qq->lfoswi >> 4); + qq->extendmode = ((qq->extendmode & 0x0f) << 4) + (qq->extendmode >> 4); + + swap(&qq->delay, &qq->_delay); + swap(&qq->speed, &qq->_speed); + swap(&qq->step, &qq->_step); + swap(&qq->time, &qq->_time); + swap(&qq->delay2, &qq->_delay2); + swap(&qq->speed2, &qq->_speed2); + swap(&qq->step2, &qq->_step2); + swap(&qq->time2, &qq->_time2); + swap(&qq->mdepth, &qq->_mdepth); + swap(&qq->mdspd, &qq->_mdspd); + swap(&qq->mdspd2, &qq->_mdspd2); + swap(&qq->lfo_wave, &qq->_lfo_wave); + swap(&qq->mdc, &qq->_mdc); + swap(&qq->mdc2, &qq->_mdc2); +} + + +//============================================================================= +// ポルタメント計算なのね +//============================================================================= +void PMDWIN::porta_calc(QQ *qq) +{ + qq->porta_num += qq->porta_num2; + if(qq->porta_num3 == 0) return; + if(qq->porta_num3 > 0) { + qq->porta_num3--; + qq->porta_num++; + } else { + qq->porta_num3++; + qq->porta_num--; + } +} + + +//============================================================================= +// PSG/PCMのソフトウエアエンベロープ +//============================================================================= +int PMDWIN::soft_env(QQ *qq) +{ + int i, cl; + + if(qq->extendmode & 4) { + if(open_work.TimerAtime == pmdwork.lastTimerAtime) return 0; + + cl = 0; + for(i = 0; i < open_work.TimerAtime - pmdwork.lastTimerAtime; i++) { + if(soft_env_main(qq)) { + cl = 1; + } + } + return cl; + } else { + return soft_env_main(qq); + } +} + + +int PMDWIN::soft_env_main(QQ *qq) +{ + int dl; + + if(qq->envf == -1) { + return ext_ssgenv_main(qq); + } + + dl = qq->eenv_volume; + soft_env_sub(qq); + if(dl == qq->eenv_volume) { + return 0; + } else { + return -1; + } +} + + +int PMDWIN::soft_env_sub(QQ *qq) +{ + if(qq->envf == 0) { + // Attack + if(--qq->eenv_ar != 0) { + return 0; + } + + qq->envf = 1; + qq->eenv_volume = qq->eenv_dr; + return 1; + } + + if(qq->envf != 2) { + // Decay + if(qq->eenv_sr == 0) return 0; // DR=0の時は減衰しない + if(--qq->eenv_sr != 0) return 0; + qq->eenv_sr = qq->eenv_src; + qq->eenv_volume--; + + if(qq->eenv_volume >= -15 || qq->eenv_volume < 15) return 0; + qq->eenv_volume = -15; + return 0; + } + + + // Release + if(qq->eenv_rr == 0) { // RR=0の時はすぐに音消し + qq->eenv_volume = -15; + return 0; + } + + if(--qq->eenv_rr != 0) return 0; + qq->eenv_rr = qq->eenv_rrc; + qq->eenv_volume--; + + if(qq->eenv_volume >= -15 && qq->eenv_volume < 15) return 0; + qq->eenv_volume = -15; + return 0; +} + + +// 拡張版 +int PMDWIN::ext_ssgenv_main(QQ *qq) +{ + int dl; + + if(qq->eenv_count == 0) return 0; + + dl = qq->eenv_volume; + esm_sub(qq, qq->eenv_count); + + if(dl == qq->eenv_volume) return 0; + return -1; +} + + +void PMDWIN::esm_sub(QQ *qq, int ah) +{ + if(--ah == 0) { + // [[[ Attack Rate ]]] + if(qq->eenv_arc > 0) { + qq->eenv_volume += qq->eenv_arc; + if(qq->eenv_volume < 15) { + qq->eenv_arc = qq->eenv_ar-16; + return; + } + + qq->eenv_volume = 15; + qq->eenv_count++; + if(qq->eenv_sl != 15) return; // SL=0の場合はすぐSRに + qq->eenv_count++; + return; + } else { + if(qq->eenv_ar == 0) return; + qq->eenv_arc++; + return; + } + } + + if(--ah == 0) { + // [[[ Decay Rate ]]] + if(qq->eenv_drc > 0) { // 0以下の場合はカウントCHECK + qq->eenv_volume -= qq->eenv_drc; + if(qq->eenv_volume < 0 || qq->eenv_volume < qq->eenv_sl) { + qq->eenv_volume = qq->eenv_sl; + qq->eenv_count++; + return; + } + + if(qq->eenv_dr < 16) { + qq->eenv_drc = (qq->eenv_dr - 16) * 2; + } else { + qq->eenv_drc = qq->eenv_dr - 16; + } + return; + } + + if(qq->eenv_dr == 0) return; + qq->eenv_drc++; + return; + } + + if(--ah == 0) { + // [[[ Sustain Rate ]]] + if(qq->eenv_src > 0) { // 0以下の場合はカウントCHECK + if((qq->eenv_volume -= qq->eenv_src) < 0) { + qq->eenv_volume = 0; + } + + if(qq->eenv_sr < 16) { + qq->eenv_src = (qq->eenv_sr - 16) * 2; + } else { + qq->eenv_src = qq->eenv_sr - 16; + } + return; + } + + if(qq->eenv_sr == 0) return; // SR=0? + qq->eenv_src++; + return; + } + + // [[[ Release Rate ]]] + if(qq->eenv_rrc > 0) { // 0以下の場合はカウントCHECK + if((qq->eenv_volume -= qq->eenv_rrc) < 0) { + qq->eenv_volume = 0; + } + + qq->eenv_rrc = (qq->eenv_rr) * 2 - 16; + return; + } + + if(qq->eenv_rr == 0) return; + qq->eenv_rrc++; +} + + +//============================================================================= +// テンポ設定 +//============================================================================= +void PMDWIN::settempo_b(void) +{ + if(open_work.tempo_d != open_work.TimerB_speed) { + open_work.TimerB_speed = open_work.tempo_d; + OPNASetReg(&opna, 0x26, open_work.TimerB_speed); + } +} + + +//============================================================================= +// 小節のカウント +//============================================================================= +void PMDWIN::syousetu_count(void) +{ + if(open_work.opncount + 1 == open_work.syousetu_lng) { + open_work.syousetu++; + open_work.opncount = 0; + } else { + open_work.opncount++; + } +} + + +//============================================================================= +// OPN割り込み許可処理 +//============================================================================= +void PMDWIN::opnint_start(void) +{ + memset(FMPart, 0, sizeof(FMPart)); + memset(SSGPart, 0, sizeof(SSGPart)); + memset(&RhythmPart, 0, sizeof(RhythmPart)); + + open_work.rhythmmask = 255; + pmdwork.rhydmy = 255; + data_init(); + opn_init(); + + PSGSetReg(&(opna.psg), 0x07, 0xbf); + mstop(); + setint(); + OPNASetReg(&opna, 0x29, 0x83); +} + + +//============================================================================= +// DATA AREA の イニシャライズ +//============================================================================= +void PMDWIN::data_init(void) +{ + int i; + int partmask, keyon_flag; + + for(i = 0; i < 6; i++) { + partmask = FMPart[i].partmask; + keyon_flag = FMPart[i].keyon_flag; + memset(&FMPart[i], 0, sizeof(QQ)); + FMPart[i].partmask = partmask & 0x0f; + FMPart[i].keyon_flag = keyon_flag; + FMPart[i].onkai = 255; + FMPart[i].onkai_def = 255; + } + + for(i = 0; i < 3; i++) { + partmask = SSGPart[i].partmask; + keyon_flag = SSGPart[i].keyon_flag; + memset(&SSGPart[i], 0, sizeof(QQ)); + SSGPart[i].partmask = partmask & 0x0f; + SSGPart[i].keyon_flag = keyon_flag; + SSGPart[i].onkai = 255; + SSGPart[i].onkai_def = 255; + } + + partmask = RhythmPart.partmask; + keyon_flag = RhythmPart.keyon_flag; + memset(&RhythmPart, 0, sizeof(QQ)); + RhythmPart.partmask = partmask & 0x0f; + RhythmPart.keyon_flag = keyon_flag; + RhythmPart.onkai = 255; + RhythmPart.onkai_def = 255; + + pmdwork.tieflag = 0; + open_work.status = 0; + open_work.status2 = 0; + open_work.syousetu = 0; + open_work.opncount = 0; + open_work.TimerAtime = 0; + pmdwork.lastTimerAtime = 0; + + pmdwork.omote_key[0] = 0; + pmdwork.omote_key[1] = 0; + pmdwork.omote_key[2] = 0; + pmdwork.ura_key[0] = 0; + pmdwork.ura_key[1] = 0; + pmdwork.ura_key[2] = 0; + + pmdwork.fm3_alg_fb = 0; + pmdwork.af_check = 0; + + open_work.kshot_dat = 0; + open_work.rshot_dat = 0; + + pmdwork.slotdetune_flag = 0; + open_work.slot_detune1 = 0; + open_work.slot_detune2 = 0; + open_work.slot_detune3 = 0; + open_work.slot_detune4 = 0; + + pmdwork.slot3_flag = 0; + open_work.ch3mode = 0x3f; + + pmdwork.fmsel = 0; + + open_work.syousetu_lng = 96; + + open_work.fm_voldown = open_work._fm_voldown; + open_work.ssg_voldown = open_work._ssg_voldown; + open_work.rhythm_voldown = open_work._rhythm_voldown; +} + + +//============================================================================= +// OPN INIT +//============================================================================= +void PMDWIN::opn_init(void) +{ + int i; + OPNASetReg(&opna, 0x29, 0x83); + open_work.psnoi = 0; +//@ if(effwork.effon == 0) { + PSGSetReg(&(opna.psg), 0x06, 0x00); + open_work.psnoi_last = 0; +//@ } + + //------------------------------------------------------------------------ + // PAN/HARDLFO DEFAULT + //------------------------------------------------------------------------ + OPNASetReg(&opna, 0xb4, 0xc0); + OPNASetReg(&opna, 0xb5, 0xc0); + OPNASetReg(&opna, 0xb6, 0xc0); + OPNASetReg(&opna, 0x1b4, 0xc0); + OPNASetReg(&opna, 0x1b5, 0xc0); + OPNASetReg(&opna, 0x1b6, 0xc0); + + open_work.port22h = 0x00; + OPNASetReg(&opna, 0x22, 0x00); + + //------------------------------------------------------------------------ + // Rhythm Default = Pan : Mid , Vol : 15 + //------------------------------------------------------------------------ + for(i = 0; i < 6; i++) { + open_work.rdat[i] = 0xcf; + } + OPNASetReg(&opna, 0x10, 0xff); + + //------------------------------------------------------------------------ + // リズムトータルレベル セット + //------------------------------------------------------------------------ + open_work.rhyvol = 48*4*(256-open_work.rhythm_voldown)/1024; + OPNASetReg(&opna, 0x11, open_work.rhyvol); +} + + +//============================================================================= +// MUSIC STOP +//============================================================================= +void PMDWIN::mstop_f(void) +{ + int i; + if(open_work.TimerAflag || open_work.TimerBflag) { + pmdwork.music_flag |= 2; + } else { + mstop(); + } + + //memset(wavbuf2, 0, sizeof(wavbuf2)); + for(i=0; i<nbufsample; i++) wavbuf2[i].left = 0; + upos = 0; +} + + +void PMDWIN::mstop(void) +{ + pmdwork.music_flag &= 0xfd; + open_work.play_flag = 0; + open_work.status2 = -1; + silence(); +} + + +//============================================================================= +// ALL SILENCE +//============================================================================= +void PMDWIN::silence(void) +{ + OPNASetReg(&opna, 0x80, 0xff); // FM Release = 15 + OPNASetReg(&opna, 0x81, 0xff); + OPNASetReg(&opna, 0x82, 0xff); + OPNASetReg(&opna, 0x84, 0xff); + OPNASetReg(&opna, 0x85, 0xff); + OPNASetReg(&opna, 0x86, 0xff); + OPNASetReg(&opna, 0x88, 0xff); + OPNASetReg(&opna, 0x89, 0xff); + OPNASetReg(&opna, 0x8a, 0xff); + OPNASetReg(&opna, 0x8c, 0xff); + OPNASetReg(&opna, 0x8d, 0xff); + OPNASetReg(&opna, 0x8e, 0xff); + + OPNASetReg(&opna, 0x180, 0xff); + OPNASetReg(&opna, 0x181, 0xff); + OPNASetReg(&opna, 0x184, 0xff); + OPNASetReg(&opna, 0x185, 0xff); + OPNASetReg(&opna, 0x188, 0xff); + OPNASetReg(&opna, 0x189, 0xff); + OPNASetReg(&opna, 0x18c, 0xff); + OPNASetReg(&opna, 0x18d, 0xff); + + OPNASetReg(&opna, 0x182, 0xff); + OPNASetReg(&opna, 0x186, 0xff); + OPNASetReg(&opna, 0x18a, 0xff); + OPNASetReg(&opna, 0x18e, 0xff); + + OPNASetReg(&opna, 0x28, 0x00); // FM KEYOFF + OPNASetReg(&opna, 0x28, 0x01); + OPNASetReg(&opna, 0x28, 0x02); + OPNASetReg(&opna, 0x28, 0x04); // FM KEYOFF [URA] + OPNASetReg(&opna, 0x28, 0x05); + OPNASetReg(&opna, 0x28, 0x06); + +// 2003.11.30 プチノイズ対策のため +//@ if(effwork.effon == 0) { + PSGSetReg(&(opna.psg), 0x07, 0xbf); + PSGSetReg(&(opna.psg), 0x08, 0); // 2003.11.30 プチノイズ対策のため + PSGSetReg(&(opna.psg), 0x09, 0); // 2003.11.30 プチノイズ対策のため + PSGSetReg(&(opna.psg), 0x0a, 0); // 2003.11.30 プチノイズ対策のため +//@ } else { +//@ PSGSetReg(&(opna.psg), 0x07, (PSGGetReg(&(opna.psg), 0x07) & 0x3f) | 0x9b); +//@ } + + OPNASetReg(&opna, 0x10, 0xff); // Rhythm dump + OPNASetReg(&opna, 0x110, 0x80); // TA/TB/EOS を RESET + OPNASetReg(&opna, 0x110, 0x18); // TIMERB/A/EOSのみbit変化あり +} + + +//============================================================================= +// 演奏開始(function) +//============================================================================= +void PMDWIN::mstart_f(void) +{ + if(open_work.TimerAflag || open_work.TimerBflag) { + pmdwork.music_flag |= 1; // TA/TB処理中は 実行しない + return; + } + + mstart(); +} + + +//============================================================================= +// 演奏開始 +//============================================================================= +void PMDWIN::mstart(void) +{ + // TimerB = 0 に設定し、Timer Reset(曲の長さを毎回そろえるため) + open_work.tempo_d = 0; + settempo_b(); + OPNASetReg(&opna, 0x27, 0); // TIMER RESET(timerA,Bとも) + + //------------------------------------------------------------------------ + // 演奏停止 + //------------------------------------------------------------------------ + pmdwork.music_flag &= 0xfe; + mstop(); + + //------------------------------------------------------------------------ + // バッファ初期化 + //------------------------------------------------------------------------ + pos2 = (char *)wavbuf2; // buf に余っているサンプルの先頭位置 + us2 = 0; // buf に余っているサンプル数 + upos = 0; // 演奏開始からの時間(μsec) + + //------------------------------------------------------------------------ + // 演奏準備 + //------------------------------------------------------------------------ + data_init(); + play_init(); + + //------------------------------------------------------------------------ + // OPN初期化 + //------------------------------------------------------------------------ + opn_init(); + + //------------------------------------------------------------------------ + // 音楽の演奏を開始 + //------------------------------------------------------------------------ + setint(); + open_work.play_flag = 1; +} + + +//============================================================================= +// 各パートのスタートアドレス及び初期値をセット +//============================================================================= +void PMDWIN::play_init(void) +{ + int i; + ushort *p; + + //2.6追加分 + if(*open_work.mmlbuf != 2*(max_part2+1)) { + open_work.prgdat_adr = open_work.mmlbuf + *(ushort *)(&open_work.mmlbuf[2*(max_part2+1)]); + open_work.prg_flg = 1; + } else { + open_work.prg_flg = 0; + } + + p = (ushort *)open_work.mmlbuf; + + // Part 0,1,2,3,4,5(FM1〜6)の時 + for(i = 0; i < 6; i++) { + if(open_work.mmlbuf[*p] == 0x80) { //先頭が80hなら演奏しない + FMPart[i].address = NULL; + } else { + FMPart[i].address = &open_work.mmlbuf[*p]; + } + + FMPart[i].leng = 1; + FMPart[i].keyoff_flag = -1; // 現在keyoff中 + FMPart[i].mdc = -1; // MDepth Counter (無限) + FMPart[i].mdc2 = -1; // 同上 + FMPart[i]._mdc = -1; // 同上 + FMPart[i]._mdc2 = -1; // 同上 + FMPart[i].onkai = 255; // rest + FMPart[i].onkai_def = 255; // rest + FMPart[i].volume = 108; // FM VOLUME DEFAULT= 108 + FMPart[i].fmpan = 0xc0; // FM PAN = Middle + FMPart[i].slotmask = 0xf0; // FM SLOT MASK + FMPart[i].neiromask = 0xff; // FM Neiro MASK + p++; + } + + // Part 6,7,8(PSG1〜3)の時 + for(i = 0; i < 3; i++) { + if(open_work.mmlbuf[*p] == 0x80) { //先頭が80hなら演奏しない + SSGPart[i].address = NULL; + } else { + SSGPart[i].address = &open_work.mmlbuf[*p]; + } + + SSGPart[i].leng = 1; + SSGPart[i].keyoff_flag = -1; // 現在keyoff中 + SSGPart[i].mdc = -1; // MDepth Counter (無限) + SSGPart[i].mdc2 = -1; // 同上 + SSGPart[i]._mdc = -1; // 同上 + SSGPart[i]._mdc2 = -1; // 同上 + SSGPart[i].onkai = 255; // rest + SSGPart[i].onkai_def = 255; // rest + SSGPart[i].volume = 8; // PSG VOLUME DEFAULT= 8 + SSGPart[i].psgpat = 7; // PSG = TONE + SSGPart[i].envf = 3; // PSG ENV = NONE/normal + p++; + } + p++; + + // Part 10(Rhythm)の時 + if(open_work.mmlbuf[*p] == 0x80) { //先頭が80hなら演奏しない + RhythmPart.address = NULL; + } else { + RhythmPart.address = &open_work.mmlbuf[*p]; + } + + RhythmPart.leng = 1; + RhythmPart.keyoff_flag = -1; // 現在keyoff中 + RhythmPart.mdc = -1; // MDepth Counter (無限) + RhythmPart.mdc2 = -1; // 同上 + RhythmPart._mdc = -1; // 同上 + RhythmPart._mdc2 = -1; // 同上 + RhythmPart.onkai = 255; // rest + RhythmPart.onkai_def = 255; // rest + RhythmPart.volume = 15; // PPSDRV volume + p++; + + //------------------------------------------------------------------------ + // Rhythm のアドレステーブルをセット + //------------------------------------------------------------------------ + + open_work.radtbl = (ushort *)&open_work.mmlbuf[*p]; + open_work.rhyadr = (uchar *)&pmdwork.rhydmy; +} + + +//============================================================================= +// インタラプト 設定 +// FM音源専用 +//============================================================================= +void PMDWIN::setint(void) +{ + // + // OPN割り込み初期設定 + // + + open_work.tempo_d = 200; + open_work.tempo_d_push = 200; + + calc_tb_tempo(); + settempo_b(); + + OPNASetReg(&opna, 0x25, 0x00); // TIMER A SET (9216μs固定) + OPNASetReg(&opna, 0x24, 0x00); // 一番遅くて丁度いい + OPNASetReg(&opna, 0x27, 0x3f); // TIMER ENABLE + + // + // 小節カウンタリセット + // + + open_work.opncount = 0; + open_work.syousetu = 0; + open_work.syousetu_lng = 96; +} + + +//============================================================================= +// T->t 変換 +// input [tempo_d] +// output [tempo_48] +//============================================================================= +void PMDWIN::calc_tb_tempo(void) +{ +// TEMPO = 0x112C / [ 256 - TB ] timerB -> tempo + int temp; + + if(256 - open_work.tempo_d == 0) { + temp = 255; + } else { + temp = (0x112c * 2 / (256 - open_work.tempo_d) + 1) / 2; + if(temp > 255) temp = 255; + } + + open_work.tempo_48 = temp; + open_work.tempo_48_push = temp; +} + + +//============================================================================= +// t->T 変換 +// input [tempo_48] +// output [tempo_d] +//============================================================================= +void PMDWIN::calc_tempo_tb(void) +{ + int al; + + // TB = 256 - [ 112CH / TEMPO ] tempo -> timerB + + if(open_work.tempo_48 >= 18) { + al = 256 - 0x112c / open_work.tempo_48; + if(0x112c % open_work.tempo_48 >= 128) { + al--; + } + //al = 256 - (0x112c * 2 / open_work.tempo_48 + 1) / 2; + } else { + al = 0; + } + open_work.tempo_d = al; + open_work.tempo_d_push = al; +} + + +//============================================================================= +// 曲の読み込みその3(メモリから、カレントディレクトリあり) +//============================================================================= +int PMDWIN::music_load3(uchar *musdata, unsigned int size) +{ + if(size > sizeof(mdataarea)) { + return ERR_WRONG_MUSIC_FILE; + } + + // 020120 ヘッダ解析のみ Towns に対応 + if((musdata[0] > 0x0f && musdata[0] != 0xff) || (musdata[1] != 0x18 && musdata[1] != 0x1a) || musdata[2]) { + return ERR_WRONG_MUSIC_FILE; // not PMD data + } + + // メモリを 0x00 で初期化する + //memset(mdataarea, 0x00, sizeof(mdataarea)); + memcpy(mdataarea, musdata, size); + return PMDWIN_OK; +} + + +//============================================================================= +// 初期化 +//============================================================================= +bool PMDWIN::init(void) +{ + int i; + struct timeval tv; + open_work.rate = SOUND_44K; + + // OPEN_WORK メンバの初期化 + open_work.rhyvol = 0x3c; + open_work.TimerBflag = 0; + open_work.TimerAflag = 0; + open_work.TimerB_speed = 0x100; + open_work.port22h = 0; + + if(OPNAInit(&opna, OPNAClock, SOUND_44K, true) == false) return false; + SetVolumeFM(&opna, 0); + SetVolumePSG(&(opna.psg), 0); + + // Init RNG state + gettimeofday(&tv, NULL); + lfg_srand(tv.tv_sec); + + for(i=0; i<nbufsample; i++) { + wavbuf2[i].left = 0; + } + pos2 = (char *)wavbuf2; + us2 = 0; + upos = 0; + + //---------------------------------------------------------------- + // 変数の初期化 + //---------------------------------------------------------------- + + open_work.MusPart[ 0] = &FMPart[0]; + open_work.MusPart[ 1] = &FMPart[1]; + open_work.MusPart[ 2] = &FMPart[2]; + open_work.MusPart[ 3] = &FMPart[3]; + open_work.MusPart[ 4] = &FMPart[4]; + open_work.MusPart[ 5] = &FMPart[5]; + open_work.MusPart[ 6] = &SSGPart[0]; + open_work.MusPart[ 7] = &SSGPart[1]; + open_work.MusPart[ 8] = &SSGPart[2]; + open_work.MusPart[ 9] = &RhythmPart; + + mdataarea[0] = 0; + for(i = 0; i < 12; i++) { + mdataarea[i*2+1] = 0x18; + mdataarea[i*2+2] = 0; + } + mdataarea[25] = 0x80; + + open_work.fm_voldown = fmvd_init; // FM_VOLDOWN + open_work._fm_voldown = fmvd_init; // FM_VOLDOWN + open_work.ssg_voldown = 0; // SSG_VOLDOWN + open_work._ssg_voldown = 0; // SSG_VOLDOWN + open_work.rhythm_voldown = 0; // RHYTHM_VOLDOWN + open_work._rhythm_voldown = 0; // RHYTHM_VOLDOWN + + memset(FMPart, 0, sizeof(FMPart)); + memset(SSGPart, 0, sizeof(SSGPart)); + memset(&RhythmPart, 0, sizeof(RhythmPart)); + + pmdwork.music_flag = 0; + pmdwork.ppsdrv_flag = false; // PPSDRV FLAG + + //---------------------------------------------------------------- + // 曲データ,音色データ格納番地を設定 + //---------------------------------------------------------------- + open_work.mmlbuf = &mdataarea[1]; + + //---------------------------------------------------------------- + // 効果音/FMINT/EFCINTを初期化 + //---------------------------------------------------------------- + effwork.effon = 0; + effwork.psgefcnum = 0xff; + + //---------------------------------------------------------------- + // 088/188/288/388 (同INT番号のみ) を初期設定 + //---------------------------------------------------------------- + OPNASetReg(&opna, 0x29, 0x00); + OPNASetReg(&opna, 0x24, 0x00); + OPNASetReg(&opna, 0x25, 0x00); + OPNASetReg(&opna, 0x26, 0x00); + OPNASetReg(&opna, 0x27, 0x3f); + + //---------------------------------------------------------------- + // OPN割り込み開始 + //---------------------------------------------------------------- + opnint_start(); + + return true; +} + + +//============================================================================= +// 合成周波数の設定 +//============================================================================= +void PMDWIN::setpcmrate(unsigned int rate) +{ + if(rate == SOUND_55K || rate == SOUND_55K_2) { + open_work.rate = SOUND_44K; + open_work.fmcalc55k = true; + } else { + open_work.rate = rate; + open_work.fmcalc55k = false; + } + OPNASetRate(&opna, open_work.rate, open_work.fmcalc55k); +} + + +//============================================================================= +// Mask to enable/disable usage of FM (bit 0), SSG (bit 1) and Rhythm (bit 2) +// on the OPNA. Mostly for testing purposes. +//============================================================================= +void PMDWIN::setdevmask(uint8_t mask) +{ + opna.devmask = mask; +} + + +void PMDWIN::setchanmask(uint32_t mask) +{ + OPNASetChannelMask(&opna, mask); +} + + +//============================================================================= +// 演奏開始 +//============================================================================= +void PMDWIN::music_start(void) +{ + mstart_f(); +} + + +//============================================================================= +// 演奏停止 +//============================================================================= +void PMDWIN::music_stop(void) +{ + mstop_f(); +} + + +//============================================================================= +// PCM データ(wave データ)の取得 +//============================================================================= +void PMDWIN::getpcmdata(short *buf, int nsamples) +{ + int copysamples = 0; // コピー済みのサンプル数 + int i, us; + + do { + if(nsamples - copysamples <= us2) { + memcpy(buf, pos2, (nsamples - copysamples)*2); + us2 -= (nsamples - copysamples); + pos2 += (nsamples - copysamples)*2; + copysamples = nsamples; + } else { + memcpy(buf, pos2, us2 * 2); + buf += us2; + copysamples += us2; + pos2 = (char *)wavbuf2; + + if(ReadStatus(&opna) & 0x01) { + TimerA_main(); + } + + if(ReadStatus(&opna) & 0x02) { + TimerB_main(); + } + + OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30); // TIMER RESET(timerA,Bとも) + + + us = OPNAGetNextEvent(&opna); + us2 = (int)((float)us * open_work.rate / 1000000.0f); + OPNATimerCount(&opna, us); + + int32_t tmpbuf[8192]; + for(i=0; i<us2; i++) tmpbuf[i] = 0; + OPNAMix(&opna, (int32_t*)tmpbuf, us2); + upos += us; + + for(i = 0; i < us2; i++) { + wavbuf2[i].left = Limit(tmpbuf[i], 32767, -32768); + } + } + } while(copysamples < nsamples); +} + + +//============================================================================= +// FM で 55kHz合成、一次補完するかどうかの設定 +//============================================================================= +void PMDWIN::setfmcalc55k(bool flag) +{ + open_work.fmcalc55k = flag; + OPNASetRate(&opna, open_work.rate, open_work.fmcalc55k); +} + + +unsigned int PMDWIN::getstatus(char *buf, size_t bufsize) +{ + char *p = buf; + unsigned int i = 0, len; + while(i < 6) { + *p++ = 'F'; + *p++ = 'M'; + *p++ = (i + 0x30); + *p++ = ':'; + *p++ = ' '; + *p++ = notes[pmdstatus[i]][0]; + *p++ = notes[pmdstatus[i]][1]; + *p++ = ' '; + i++; + } + while(i < 9) { + *p++ = 'P'; + *p++ = 'S'; + *p++ = 'G'; + *p++ = (i + 0x30 - 6); + *p++ = ':'; + *p++ = ' '; + *p++ = notes[pmdstatus[i]][0]; + *p++ = notes[pmdstatus[i]][1]; + *p++ = ' '; + i++; + } + p -= 3; + *p++ = (pmdstatus[8] + 0x30); + *p++ = '\0'; + len = (p - buf); + return len; +} + + +//============================================================================= +// メモの取得(内部動作) +//============================================================================= +char* PMDWIN::_getmemo(char *dest, uchar *musdata, int size, int al) +{ + uchar *si, *mmlbuf; + int i, dx; + int maxsize; + + if(musdata == NULL || size == 0) { + mmlbuf = open_work.mmlbuf; + maxsize = sizeof(mdataarea) - 1; + } else { + mmlbuf = &musdata[1]; + maxsize = size - 1; + } + if(maxsize < 2) { + *dest = '\0'; // 曲データが不正 + return NULL; + } + + if(mmlbuf[0] != 0x1a || mmlbuf[1] != 0x00) { + *dest = '\0'; // 音色がないfile=メモのアドレス取得不能 + return dest; + } + + if(maxsize < 0x18+1) { + *dest = '\0'; // 曲データが不正 + return NULL; + } + + if(maxsize < *(ushort *)&mmlbuf[0x18] - 4 + 3) { + *dest = '\0'; // 曲データが不正 + return NULL; + } + + si = &mmlbuf[*(ushort *)&mmlbuf[0x18] - 4]; + if(*(si + 2) != 0x40) { + if(*(si + 3) != 0xfe || *(si + 2) < 0x41) { + *dest = '\0'; // 音色がないfile=メモのアドレス取得不能 + return dest; + } + } + + if(*(si + 2) >= 0x42) { + al++; + } + + if(*(si + 2) >= 0x48) { + al++; + } + + if(al < 0) { + *dest = '\0'; // 登録なし + return dest; + } + + si = &mmlbuf[*(ushort *)si]; + + for(i = 0; i <= al; i++) { + if(maxsize < si - mmlbuf + 1) { + *dest = '\0'; // 曲データが不正 + return NULL; + } + + dx = *(ushort *)si; + if(dx == 0) { + *dest = '\0'; + return dest; + } + + if(maxsize < dx) { + *dest = '\0'; // 曲データが不正 + return NULL; + } + + if(mmlbuf[dx] == '/') { + *dest = '\0'; + return dest; + } + + si += 2; + } + + for(i = dx; i < maxsize; i++) { + if(mmlbuf[i] == '\0') break; + } + + // 終端の \0 がない場合 + if(i >= maxsize) { + memcpy(dest, &mmlbuf[dx], maxsize - dx); + dest[maxsize - dx - 1] = '\0'; + } else { + memcpy(dest, &mmlbuf[dx], i); + dest[i] = '\0'; + } + return dest; +} + +/* + Conversion between SJIS codes (s1,s2) and JISX0208 codes (c1,c2): + Example. (s1,s2) = 0x8140, (c1,c2) = 0x2121. + 0x81 <= s1 <= 0x9F || 0xE0 <= s1 <= 0xEA, + 0x40 <= s2 <= 0x7E || 0x80 <= s2 <= 0xFC, + 0x21 <= c1 <= 0x74, 0x21 <= c2 <= 0x7E. + Invariant: + 94*2*(s1 < 0xE0 ? s1-0x81 : s1-0xC1) + (s2 < 0x80 ? s2-0x40 : s2-0x41) + = 94*(c1-0x21)+(c2-0x21) + Conversion (s1,s2) -> (c1,c2): + t1 := (s1 < 0xE0 ? s1-0x81 : s1-0xC1) + t2 := (s2 < 0x80 ? s2-0x40 : s2-0x41) + c1 := 2*t1 + (t2 < 0x5E ? 0 : 1) + 0x21 + c2 := (t2 < 0x5E ? t2 : t2-0x5E) + 0x21 + Conversion (c1,c2) -> (s1,s2): + t1 := (c1 - 0x21) >> 1 + t2 := ((c1 - 0x21) & 1) * 0x5E + (c2 - 0x21) + s1 := (t1 < 0x1F ? t1+0x81 : t1+0xC1) + s2 := (t2 < 0x3F ? t2+0x40 : t2+0x41) + */ + +static int sjis_to_ucs4(uint32_t *pwc, const uint8_t *s, int n) +{ + uint8_t c = *s; + if (c <= 0x80) { + if (c == 0x5c) + *pwc = (uint32_t) 0x00a5; + else if (c == 0x7e) + *pwc = (uint32_t) 0x203e; + else + *pwc = (uint32_t) c; + return 1; + } else if (c >= 0xa1 && c <= 0xdf) { + *pwc = (uint32_t) c + 0xfec0; + return 1; + } else { + uint8_t s1, s2; + s1 = c; + if ((s1 >= 0x81 && s1 <= 0x9f) || (s1 >= 0xe0 && s1 <= 0xea)) { + if (n < 2) + return -2; + s2 = s[1]; + if ((s2 >= 0x40 && s2 <= 0x7e) || (s2 >= 0x80 && s2 <= 0xfc)) { + uint8_t t1 = (s1 < 0xe0 ? s1-0x81 : s1-0xc1); + uint8_t t2 = (s2 < 0x80 ? s2-0x40 : s2-0x41); + uint8_t buf[2]; + buf[0] = 2*t1 + (t2 < 0x5e ? 0 : 1) + 0x21; + buf[1] = (t2 < 0x5e ? t2 : t2-0x5e) + 0x21; + return jisx0208_mbtowc(pwc,buf,2); + } + } else if (s1 >= 0xf0 && s1 <= 0xf9) { + /* User-defined range. See + * Ken Lunde's "CJKV Information Processing", table 4-66, p. 206. */ + if (n < 2) + return -2; + s2 = s[1]; + if ((s2 >= 0x40 && s2 <= 0x7e) || (s2 >= 0x80 && s2 <= 0xfc)) { + *pwc = 0xe000 + 188*(s1 - 0xf0) + (s2 < 0x80 ? s2-0x40 : s2-0x41); + return 2; + } + } + return -1; + } +} + +static int ucs4_to_utf8(uint8_t *utf8, uint32_t ucs4) +{ + if (ucs4 <= 0x0000007fL) { + utf8[0] = ucs4; + return 1; + } else if (ucs4 <= 0x000007ffL) { + utf8[0] = 0xc0 | ((ucs4 >> 6) & 0x1f); + utf8[1] = 0x80 | ((ucs4 >> 0) & 0x3f); + return 2; + } else if (ucs4 <= 0x0000ffffL) { + utf8[0] = 0xe0 | ((ucs4 >> 12) & 0x0f); + utf8[1] = 0x80 | ((ucs4 >> 6) & 0x3f); + utf8[2] = 0x80 | ((ucs4 >> 0) & 0x3f); + return 3; + } + + /* default */ + utf8[0] = '?'; + return 1; +} + +static size_t sjis_to_utf8(uint8_t *outbuf, uint8_t *inbuf, size_t insize) { + uint32_t wc = 0; + uint8_t *utf8str = outbuf; + uint8_t *utf8str_start = utf8str; + int ret; + while(insize > 0) { + ret = sjis_to_ucs4(&wc, inbuf, insize); + if(ret < 0) { + inbuf++; insize--; + continue; + } + utf8str += ucs4_to_utf8(utf8str, wc); + inbuf += ret; + insize -= ret; + } + return (utf8str - utf8str_start); +} + +char* PMDWIN::_getmemo3(char *dest, uchar *musdata, int size, int al) +{ + char buf[mdata_def*1024]; + _getmemo((char *)buf, musdata, size, al); + sjis_to_utf8((uint8_t*)dest, (uint8_t*)buf, strlen(buf)+1); + return dest; +} + + +//============================================================================= +// パートのマスク +//============================================================================= +int PMDWIN::maskon(unsigned int ch) +{ + int ah, fmseltmp; + + + if(ch >= sizeof(open_work.MusPart) / sizeof(QQ *)) { + return ERR_WRONG_PARTNO; // part number error + } + + if(part_table[ch][0] < 0) { + open_work.rhythmmask = 0; // Rhythm音源をMask + OPNASetReg(&opna, 0x10, 0xff); // Rhythm音源を全部Dump + } else { + fmseltmp = pmdwork.fmsel; + if(open_work.MusPart[ch]->partmask == 0 && open_work.play_flag) { + if(part_table[ch][2] == 0) { + pmdwork.partb = part_table[ch][1]; + pmdwork.fmsel = 0; + silence_fmpart(open_work.MusPart[ch]); // 音を完璧に消す + } else if(part_table[ch][2] == 1) { + pmdwork.partb = part_table[ch][1]; + pmdwork.fmsel = 0x100; + silence_fmpart(open_work.MusPart[ch]); // 音を完璧に消す + } else if(part_table[ch][2] == 2) { + pmdwork.partb = part_table[ch][1]; + ah = 1 << (pmdwork.partb - 1); + ah |= (ah << 3); + // PSG keyoff + PSGSetReg(&(opna.psg), 0x07, ah | PSGGetReg(&(opna.psg), 0x07)); + } else if(part_table[ch][2] == 4) { + if(effwork.psgefcnum < 11) { + effend(); + } + } + } + open_work.MusPart[ch]->partmask |= 1; + pmdwork.fmsel = fmseltmp; + } + return PMDWIN_OK; +} + + +//============================================================================= +// パートのマスク解除 +//============================================================================= +int PMDWIN::maskoff(unsigned int ch) +{ + int fmseltmp; + + if(ch >= sizeof(open_work.MusPart) / sizeof(QQ *)) { + return ERR_WRONG_PARTNO; // part number error + } + + if(part_table[ch][0] < 0) { + open_work.rhythmmask = 0xff; + } else { + if(open_work.MusPart[ch]->partmask == 0) return ERR_NOT_MASKED; // マスクされていない + // 効果音でまだマスクされている + if((open_work.MusPart[ch]->partmask &= 0xfe) != 0) return ERR_EFFECT_USED; + // 曲が止まっている + if(open_work.play_flag == 0) return ERR_MUSIC_STOPPED; + + fmseltmp = pmdwork.fmsel; + if(open_work.MusPart[ch]->address != NULL) { + if(part_table[ch][2] == 0) { // FM音源(表) + pmdwork.fmsel = 0; + pmdwork.partb = part_table[ch][1]; + neiro_reset(open_work.MusPart[ch]); + } else if(part_table[ch][2] == 1) { // FM音源(裏) + pmdwork.fmsel = 0x100; + pmdwork.partb = part_table[ch][1]; + neiro_reset(open_work.MusPart[ch]); + } + } + pmdwork.fmsel = fmseltmp; + + } + return PMDWIN_OK; +} + + +//============================================================================= +// FM Volume Down の設定 +//============================================================================= +void PMDWIN::setfmvoldown(int voldown) +{ + open_work.fm_voldown = open_work._fm_voldown = voldown; +} + + +//============================================================================= +// SSG Volume Down の設定 +//============================================================================= +void PMDWIN::setssgvoldown(int voldown) +{ + open_work.ssg_voldown = open_work._ssg_voldown = voldown; +} + + +//============================================================================= +// Rhythm Volume Down の設定 +//============================================================================= +void PMDWIN::setrhythmvoldown(int voldown) +{ + open_work.rhythm_voldown = open_work._rhythm_voldown = voldown; + open_work.rhyvol = 48*4*(256-open_work.rhythm_voldown)/1024; + OPNASetReg(&opna, 0x11, open_work.rhyvol); + +} + + +//============================================================================= +// FM Volume Down の取得 +//============================================================================= +int PMDWIN::getfmvoldown(void) +{ + return open_work.fm_voldown; +} + + +//============================================================================= +// FM Volume Down の取得(その2) +//============================================================================= +int PMDWIN::getfmvoldown2(void) +{ + return open_work._fm_voldown; +} + + +//============================================================================= +// SSG Volume Down の取得 +//============================================================================= +int PMDWIN::getssgvoldown(void) +{ + return open_work.ssg_voldown; +} + + +//============================================================================= +// SSG Volume Down の取得(その2) +//============================================================================= +int PMDWIN::getssgvoldown2(void) +{ + return open_work._ssg_voldown; +} + + +//============================================================================= +// Rhythm Volume Down の取得 +//============================================================================= +int PMDWIN::getrhythmvoldown(void) +{ + return open_work.rhythm_voldown; +} + + +//============================================================================= +// Rhythm Volume Down の取得(その2) +//============================================================================= +int PMDWIN::getrhythmvoldown2(void) +{ + return open_work._rhythm_voldown; +} + + +//============================================================================= +// 再生位置の移動(pos : ms) +//============================================================================= +void PMDWIN::setpos(int pos) +{ + uint64_t _pos; + int us; + + _pos = (uint64_t)pos * 1000; // (ms -> usec への変換) + + if(upos > _pos) { + mstart(); + pos2 = (char *)wavbuf2; // buf に余っているサンプルの先頭位置 + us2 = 0; // buf に余っているサンプル数 + upos = 0; // 演奏開始からの時間(μsec) + } + + while(upos < _pos) { + if(ReadStatus(&opna) & 0x01) { + TimerA_main(); + } + + if(ReadStatus(&opna) & 0x02) { + TimerB_main(); + } + + OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30); // TIMER RESET(timerA,Bとも) + + us = OPNAGetNextEvent(&opna); + OPNATimerCount(&opna, us); + upos += us; + } + + if(open_work.status2 == -1) { + silence(); + } +} + + +//============================================================================= +// 再生位置の移動(pos : count 単位) +//============================================================================= +void PMDWIN::setpos2(int pos) +{ + int us; + + if(open_work.syousetu_lng * open_work.syousetu + open_work.opncount > pos) { + mstart(); + pos2 = (char *)wavbuf2; // buf に余っているサンプルの先頭位置 + us2 = 0; // buf に余っているサンプル数 + } + + while(open_work.syousetu_lng * open_work.syousetu + open_work.opncount < pos) { + if(ReadStatus(&opna) & 0x01) { + TimerA_main(); + } + + if(ReadStatus(&opna) & 0x02) { + TimerB_main(); + } + + OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30); // TIMER RESET(timerA,Bとも) + + us = OPNAGetNextEvent(&opna); + OPNATimerCount(&opna, us); + } + + if(open_work.status2 == -1) { + silence(); + } +} + + +//============================================================================= +// 再生位置の取得(pos : ms) +//============================================================================= +int PMDWIN::getpos(void) +{ + return (int)(upos / 1000); +} + + +//============================================================================= +// 再生位置の取得(pos : count 単位) +//============================================================================= +int PMDWIN::getpos2(void) +{ + return open_work.syousetu_lng * open_work.syousetu + open_work.opncount; +} + + +//============================================================================= +// 曲の長さの取得(pos : ms) +//============================================================================= +bool PMDWIN::getlength(uint *length, uint *loop) +{ + int us; + mstart(); + upos = 0; // 演奏開始からの時間(μsec) + *length = 0; + + do { + if(ReadStatus(&opna) & 0x01) { + TimerA_main(); + } + + if(ReadStatus(&opna) & 0x02) { + TimerB_main(); + } + + OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30); // TIMER RESET(timerA,Bとも) + + us = OPNAGetNextEvent(&opna); + OPNATimerCount(&opna, us); + upos += us; + if(open_work.status2 == 1 && *length == 0) { // ループ時 + *length = (int)(upos / 1000); + } else if(open_work.status2 == -1) { // ループなし終了時 + *length = (int)(upos / 1000); + *loop = 0; + mstop(); + return true; + } else if(getpos2() >= 65536) { // 65536クロック以上なら強制終了 + *length = (int)(upos / 1000); + *loop = *length; + return true; + } + } while(open_work.status2 < 2); + + *loop = (int)(upos / 1000) - *length; + mstop(); + return true; +} + + +//============================================================================= +// ループ回数の取得 +//============================================================================= +int PMDWIN::getloopcount(void) +{ + return open_work.status2; +} + + +//============================================================================= +// OPEN_WORKのポインタの取得 +//============================================================================= +OPEN_WORK* PMDWIN::getopenwork(void) +{ + return &open_work; +} + + +//============================================================================= +// パートワークのポインタの取得 +//============================================================================= +QQ* PMDWIN::getpartwork(unsigned int ch) +{ + if(ch >= sizeof(open_work.MusPart) / sizeof(QQ *)) { + return NULL; + } + return open_work.MusPart[ch]; +} + +PMDWIN* pmdwin; +#ifdef WIN32 +//============================================================================= +// DLL Export Functions +//============================================================================= +BOOL DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + //DisableThreadLibraryCalls(hModule); + pmdwin = NULL; + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +//============================================================================= +// バーション取得 +//============================================================================= +DLLEXPORT int getversion(void) +{ + return DLLVersion; +} + + +//============================================================================= +// 初期化 +//============================================================================= +DLLEXPORT bool pmdwininit(void) +{ + bool result; + + if(pmdwin == NULL) { + //pmdwin = new PMDWIN; +#if defined(WIN32) || defined(WIN64) + pmdwin = (PMDWIN*)VirtualAlloc(NULL, sizeof(PMDWIN), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); +#else + pmdwin = (PMDWIN*)mmap(NULL, sizeof(PMDWIN), PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); +#endif + } + result = pmdwin->init(); + return result; +} + + +DLLEXPORT void pmdwinfree(void) +{ +#if defined(WIN32) || defined(WIN64) + VirtualFree(pmdwin, 0, MEM_RELEASE); +#else + munmap(pmdwin, sizeof(PMDWIN)); +#endif +} + + +//============================================================================= +// 合成周波数の設定 +//============================================================================= +DLLEXPORT void setpcmrate(unsigned int rate) +{ + pmdwin->setpcmrate(rate); +} + + +DLLEXPORT void setdevmask(uint8_t mask) +{ + pmdwin->setdevmask(mask); +} + +DLLEXPORT void setchanmask(uint32_t mask) +{ + pmdwin->setchanmask(mask); +} + + +//============================================================================= +// 曲の読み込みその2(メモリから) +//============================================================================= +DLLEXPORT int music_load3(uchar *musdata, int size) +{ + return pmdwin->music_load3(musdata, size); +} + + +//============================================================================= +// 演奏開始 +//============================================================================= +DLLEXPORT void music_start(void) +{ + pmdwin->music_start(); +} + + +//============================================================================= +// 演奏停止 +//============================================================================= +DLLEXPORT void music_stop(void) +{ + pmdwin->music_stop(); +} + + +//============================================================================= +// PCM データ(wave データ)の取得 +//============================================================================= +DLLEXPORT void getpcmdata(short *buf, int nsamples) +{ + pmdwin->getpcmdata(buf, nsamples); +} + + +//============================================================================= +// FM で 55kHz合成、一次補完するかどうかの設定 +//============================================================================= +DLLEXPORT void setfmcalc55k(bool flag) +{ + pmdwin->setfmcalc55k(flag); +} + + +DLLEXPORT unsigned int getstatus(char *buf, size_t bufsize) +{ + return pmdwin->getstatus(buf, bufsize); +} + + +//============================================================================= +// メモの取得 +//============================================================================= +DLLEXPORT char * getmemo(char *dest, uchar *musdata, int size, int al) +{ + return pmdwin->_getmemo(dest, musdata, size, al); +} + + +//============================================================================= +// メモの取得(2バイト半角→半角文字に変換+ESCシーケンスの除去) +//============================================================================= +DLLEXPORT char * getmemo3(char *dest, uchar *musdata, int size, int al) +{ + return pmdwin->_getmemo3(dest, musdata, size, al); +} + + +//============================================================================= +// パートのマスク +//============================================================================= +DLLEXPORT int maskon(unsigned int ch) +{ + return pmdwin->maskon(ch); +} + + +//============================================================================= +// パートのマスク解除 +//============================================================================= +DLLEXPORT int maskoff(unsigned int ch) +{ + return pmdwin->maskoff(ch); +} + + +//============================================================================= +// FM Volume Down の設定 +//============================================================================= +DLLEXPORT void setfmvoldown(int voldown) +{ + pmdwin->setfmvoldown(voldown); +} + + +//============================================================================= +// SSG Volume Down の設定 +//============================================================================= +DLLEXPORT void setssgvoldown(int voldown) +{ + pmdwin->setssgvoldown(voldown); +} + + +//============================================================================= +// Rhythm Volume Down の設定 +//============================================================================= +DLLEXPORT void setrhythmvoldown(int voldown) +{ + pmdwin->setrhythmvoldown(voldown); +} + + +//============================================================================= +// FM Volume Down の取得 +//============================================================================= +DLLEXPORT int getfmvoldown(void) +{ + return pmdwin->getfmvoldown(); +} + + +//============================================================================= +// FM Volume Down の取得(その2) +//============================================================================= +DLLEXPORT int getfmvoldown2(void) +{ + return pmdwin->getfmvoldown2(); +} + + +//============================================================================= +// SSG Volume Down の取得 +//============================================================================= +DLLEXPORT int getssgvoldown(void) +{ + return pmdwin->getssgvoldown(); +} + + +//============================================================================= +// SSG Volume Down の取得(その2) +//============================================================================= +DLLEXPORT int getssgvoldown2(void) +{ + return pmdwin->getssgvoldown2(); +} + + +//============================================================================= +// Rhythm Volume Down の取得 +//============================================================================= +DLLEXPORT int getrhythmvoldown(void) +{ + return pmdwin->getrhythmvoldown(); +} + + +//============================================================================= +// Rhythm Volume Down の取得(その2) +//============================================================================= +DLLEXPORT int getrhythmvoldown2(void) +{ + return pmdwin->getrhythmvoldown2(); +} + + +//============================================================================= +// 再生位置の移動(pos : ms) +//============================================================================= +DLLEXPORT void setpos(int pos) +{ + pmdwin->setpos(pos); +} + + +//============================================================================= +// 再生位置の移動(pos : count 単位) +//============================================================================= +DLLEXPORT void setpos2(int pos) +{ + pmdwin->setpos2(pos); +} + + +//============================================================================= +// 再生位置の取得(pos : ms) +//============================================================================= +DLLEXPORT int getpos(void) +{ + return pmdwin->getpos(); +} + + +//============================================================================= +// 再生位置の取得(pos : count 単位) +//============================================================================= +DLLEXPORT int getpos2(void) +{ + return pmdwin->getpos2(); +} + + +//============================================================================= +// 曲の長さの取得(pos : ms) +//============================================================================= +DLLEXPORT bool getlength(uint *length, uint *loop) +{ + return pmdwin->getlength(length, loop); +} + + +//============================================================================= +// ループ回数の取得 +//============================================================================= +DLLEXPORT int getloopcount(void) +{ + return pmdwin->getloopcount(); +} + + +//============================================================================= +// OPEN_WORKのポインタの取得 +//============================================================================= +DLLEXPORT OPEN_WORK * getopenwork(void) +{ + return pmdwin->getopenwork(); +} + + +//============================================================================= +// パートワークのポインタの取得 +//============================================================================= +DLLEXPORT QQ * getpartwork(unsigned int ch) +{ + return pmdwin->getpartwork(ch); +} + + +#ifdef __cplusplus +} +#endif