Mercurial > pmdwin
view pmdwin.cpp @ 8:a7e3a45d3002
Remove the embedded memcpy implementation.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Mon, 08 Sep 2014 17:24:29 +0200 |
parents | c55ea9478c80 |
children |
line wrap: on
line source
//============================================================================= // 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