Merge pull request #154 from softrabbit/stable-0.4
OpulenZ improvements: pitch bend, velocity, aftertouch
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(OPL2 opl2instrument.cpp opl2instrument.h opl.h kemuopl.h adlibemu.c adlibemu.h fmopl.c fmopl.h temuopl.cpp temuopl.h MOCFILES opl2instrument.h EMBEDDED_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.png)
|
||||
BUILD_PLUGIN(OPL2 opl2instrument.cpp opl2instrument.h opl.h fmopl.c fmopl.h temuopl.cpp temuopl.h MOCFILES opl2instrument.h EMBEDDED_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.png)
|
||||
@@ -1,608 +0,0 @@
|
||||
/*
|
||||
* ADLIBEMU.C
|
||||
* Copyright (C) 1998-2001 Ken Silverman
|
||||
* Ken Silverman's official web site: "http://www.advsys.net/ken"
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
This file is a digital Adlib emulator for OPL2 and possibly OPL3
|
||||
|
||||
Features that could be added in a future version:
|
||||
- Amplitude and Frequency Vibrato Bits (not hard, but a big speed hit)
|
||||
- Global Keyboard Split Number Bit (need to research this one some more)
|
||||
- 2nd Adlib chip for OPL3 (simply need to make my cell array bigger)
|
||||
- Advanced connection modes of OPL3 (Just need to add more "docell" cases)
|
||||
- L/R Stereo bits of OPL3 (Need adlibgetsample to return stereo)
|
||||
|
||||
Features that aren't worth supporting:
|
||||
- Anything related to adlib timers&interrupts (Sorry - I always used IRQ0)
|
||||
- Composite sine wave mode (CSM) (Supported only on ancient cards)
|
||||
|
||||
I'm not sure about a few things in my code:
|
||||
- Attack curve. What function is this anyway? I chose to use an order-3
|
||||
polynomial to approximate but this doesn't seem right.
|
||||
- Attack/Decay/Release constants - my constants may not be exact
|
||||
- What should ADJUSTSPEED be?
|
||||
- Haven't verified that Global Keyboard Split Number Bit works yet
|
||||
- Some of the drums don't always sound right. It's pretty hard to guess
|
||||
the exact waveform of drums when you look at random data which is
|
||||
slightly randomized due to digital ADC recording.
|
||||
- Adlib seems to have a lot more treble than my emulator does. I'm not
|
||||
sure if this is simply unfixable due to the sound blaster's different
|
||||
filtering on FM and digital playback or if it's a serious bug in my
|
||||
code.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(max) && !defined(__cplusplus)
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
#if !defined(min) && !defined(__cplusplus)
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define PI 3.141592653589793
|
||||
#define MAXCELLS 18
|
||||
#define WAVPREC 2048
|
||||
|
||||
static float AMPSCALE=(8192.0);
|
||||
#define FRQSCALE (49716/512.0)
|
||||
|
||||
//Constants for Ken's Awe32, on a PII-266 (Ken says: Use these for KSM's!)
|
||||
#define MODFACTOR 4.0 //How much of modulator cell goes into carrier
|
||||
#define MFBFACTOR 1.0 //How much feedback goes back into modulator
|
||||
#define ADJUSTSPEED 0.75 //0<=x<=1 Simulate finite rate of change of state
|
||||
|
||||
//Constants for Ken's Awe64G, on a P-133
|
||||
//#define MODFACTOR 4.25 //How much of modulator cell goes into carrier
|
||||
//#define MFBFACTOR 0.5 //How much feedback goes back into modulator
|
||||
//#define ADJUSTSPEED 0.85 //0<=x<=1 Simulate finite rate of change of state
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float val, t, tinc, vol, sustain, amp, mfb;
|
||||
float a0, a1, a2, a3, decaymul, releasemul;
|
||||
short *waveform;
|
||||
long wavemask;
|
||||
void (*cellfunc)(void *, float);
|
||||
unsigned char flags, dum0, dum1, dum2;
|
||||
} celltype;
|
||||
|
||||
static long numspeakers, bytespersample;
|
||||
static float recipsamp;
|
||||
static celltype cell[MAXCELLS];
|
||||
static signed short wavtable[WAVPREC*3];
|
||||
static float kslmul[4] = {0.0,0.5,0.25,1.0};
|
||||
static float frqmul[16] = {.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}, nfrqmul[16];
|
||||
static unsigned char adlibreg[256], ksl[8][16];
|
||||
static unsigned char modulatorbase[9] = {0,1,2,8,9,10,16,17,18};
|
||||
static unsigned char odrumstat = 0;
|
||||
static unsigned char base2cell[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8};
|
||||
|
||||
float lvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on left speaker
|
||||
float rvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on right speaker
|
||||
long lplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on left speaker
|
||||
long rplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on right speaker
|
||||
|
||||
long nlvol[9], nrvol[9];
|
||||
long nlplc[9], nrplc[9];
|
||||
long rend = 0;
|
||||
#define FIFOSIZ 256
|
||||
static float *rptr[9], *nrptr[9];
|
||||
static float rbuf[9][FIFOSIZ*2];
|
||||
static float snd[FIFOSIZ*2];
|
||||
|
||||
#ifndef USING_ASM
|
||||
#define _inline
|
||||
#endif
|
||||
|
||||
#ifdef USING_ASM
|
||||
static _inline void ftol (float f, long *a)
|
||||
{
|
||||
_asm
|
||||
{
|
||||
mov eax, a
|
||||
fld f
|
||||
fistp dword ptr [eax]
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void ftol(float f, long *a) {
|
||||
*a=f;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ctc ((celltype *)c) //A rare attempt to make code easier to read!
|
||||
void docell4 (void *c, float modulator) { }
|
||||
void docell3 (void *c, float modulator)
|
||||
{
|
||||
long i;
|
||||
|
||||
ftol(ctc->t+modulator,&i);
|
||||
ctc->t += ctc->tinc;
|
||||
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
|
||||
}
|
||||
void docell2 (void *c, float modulator)
|
||||
{
|
||||
long i;
|
||||
|
||||
ftol(ctc->t+modulator,&i);
|
||||
|
||||
void *amp_void = &ctc->amp;
|
||||
long *amp_long = (long *)amp_void;
|
||||
if (*amp_long <= 0x37800000)
|
||||
{
|
||||
ctc->amp = 0;
|
||||
ctc->cellfunc = docell4;
|
||||
}
|
||||
ctc->amp *= ctc->releasemul;
|
||||
|
||||
ctc->t += ctc->tinc;
|
||||
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
|
||||
}
|
||||
void docell1 (void *c, float modulator)
|
||||
{
|
||||
long i;
|
||||
|
||||
ftol(ctc->t+modulator,&i);
|
||||
|
||||
void *amp_void = &ctc->amp;
|
||||
long *amp_long = (long *)amp_void;
|
||||
void *sustain_void = &ctc->sustain;
|
||||
long *sustain_long = (long *)sustain_void;
|
||||
if (*amp_long <= *sustain_long)
|
||||
{
|
||||
if (ctc->flags&32)
|
||||
{
|
||||
ctc->amp = ctc->sustain;
|
||||
ctc->cellfunc = docell3;
|
||||
}
|
||||
else
|
||||
ctc->cellfunc = docell2;
|
||||
}
|
||||
else
|
||||
ctc->amp *= ctc->decaymul;
|
||||
|
||||
ctc->t += ctc->tinc;
|
||||
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
|
||||
}
|
||||
void docell0 (void *c, float modulator)
|
||||
{
|
||||
long i;
|
||||
|
||||
ftol(ctc->t+modulator,&i);
|
||||
|
||||
ctc->amp = ((ctc->a3*ctc->amp + ctc->a2)*ctc->amp + ctc->a1)*ctc->amp + ctc->a0;
|
||||
void *amp_void = &ctc->amp;
|
||||
long *amp_long = (long *)amp_void;
|
||||
if (*amp_long > 0x3f800000)
|
||||
{
|
||||
ctc->amp = 1;
|
||||
ctc->cellfunc = docell1;
|
||||
}
|
||||
|
||||
ctc->t += ctc->tinc;
|
||||
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
|
||||
}
|
||||
|
||||
|
||||
static long waveform[8] = {WAVPREC,WAVPREC>>1,WAVPREC,(WAVPREC*3)>>2,0,0,(WAVPREC*5)>>2,WAVPREC<<1};
|
||||
static long wavemask[8] = {WAVPREC-1,WAVPREC-1,(WAVPREC>>1)-1,(WAVPREC>>1)-1,WAVPREC-1,((WAVPREC*3)>>2)-1,WAVPREC>>1,WAVPREC-1};
|
||||
static long wavestart[8] = {0,WAVPREC>>1,0,WAVPREC>>2,0,0,0,WAVPREC>>3};
|
||||
static float attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744};
|
||||
static float decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608};
|
||||
void cellon (long i, long j, celltype *c, unsigned char iscarrier)
|
||||
{
|
||||
long frn, oct, toff;
|
||||
float f;
|
||||
|
||||
frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
|
||||
oct = ((((long)adlibreg[i+0xb0])>>2)&7);
|
||||
toff = (oct<<1) + ((frn>>9)&((frn>>8)|(((adlibreg[8]>>6)&1)^1)));
|
||||
if (!(adlibreg[j+0x20]&16)) toff >>= 2;
|
||||
|
||||
f = pow(2.0,(adlibreg[j+0x60]>>4)+(toff>>2)-1)*attackconst[toff&3]*recipsamp;
|
||||
c->a0 = .0377*f; c->a1 = 10.73*f+1; c->a2 = -17.57*f; c->a3 = 7.42*f;
|
||||
f = -7.4493*decrelconst[toff&3]*recipsamp;
|
||||
c->decaymul = pow(2.0,f*pow(2.0,(adlibreg[j+0x60]&15)+(toff>>2)));
|
||||
c->releasemul = pow(2.0,f*pow(2.0,(adlibreg[j+0x80]&15)+(toff>>2)));
|
||||
c->wavemask = wavemask[adlibreg[j+0xe0]&7];
|
||||
c->waveform = &wavtable[waveform[adlibreg[j+0xe0]&7]];
|
||||
if (!(adlibreg[1]&0x20)) c->waveform = &wavtable[WAVPREC];
|
||||
c->t = wavestart[adlibreg[j+0xe0]&7];
|
||||
c->flags = adlibreg[j+0x20];
|
||||
c->cellfunc = docell0;
|
||||
c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
|
||||
c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
|
||||
(float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
|
||||
c->sustain = pow(2.0,(float)(adlibreg[j+0x80]>>4) * -.5);
|
||||
if (!iscarrier) c->amp = 0;
|
||||
c->mfb = pow(2.0,((adlibreg[i+0xc0]>>1)&7)+5)*(WAVPREC/2048.0)*MFBFACTOR;
|
||||
if (!(adlibreg[i+0xc0]&14)) c->mfb = 0;
|
||||
c->val = 0;
|
||||
}
|
||||
|
||||
//This function (and bug fix) written by Chris Moeller
|
||||
void cellfreq (signed long i, signed long j, celltype *c)
|
||||
{
|
||||
long frn, oct;
|
||||
|
||||
frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
|
||||
oct = ((((long)adlibreg[i+0xb0])>>2)&7);
|
||||
|
||||
c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
|
||||
c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
|
||||
(float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
|
||||
}
|
||||
|
||||
static long initfirstime = 0;
|
||||
void adlibinit (long dasamplerate, long danumspeakers, long dabytespersample)
|
||||
{
|
||||
long i, j, frn, oct;
|
||||
|
||||
memset((void *)adlibreg,0,sizeof(adlibreg));
|
||||
memset((void *)cell,0,sizeof(celltype)*MAXCELLS);
|
||||
memset((void *)rbuf,0,sizeof(rbuf));
|
||||
rend = 0; odrumstat = 0;
|
||||
|
||||
for(i=0;i<MAXCELLS;i++)
|
||||
{
|
||||
cell[i].cellfunc = docell4;
|
||||
cell[i].amp = 0;
|
||||
cell[i].vol = 0;
|
||||
cell[i].t = 0;
|
||||
cell[i].tinc = 0;
|
||||
cell[i].wavemask = 0;
|
||||
cell[i].waveform = &wavtable[WAVPREC];
|
||||
}
|
||||
|
||||
numspeakers = danumspeakers;
|
||||
bytespersample = dabytespersample;
|
||||
|
||||
recipsamp = 1.0 / (float)dasamplerate;
|
||||
for(i=15;i>=0;i--) nfrqmul[i] = frqmul[i]*recipsamp*FRQSCALE*(WAVPREC/2048.0);
|
||||
|
||||
if (!initfirstime)
|
||||
{
|
||||
initfirstime = 1;
|
||||
|
||||
for(i=0;i<(WAVPREC>>1);i++)
|
||||
{
|
||||
wavtable[i] =
|
||||
wavtable[(i<<1) +WAVPREC] = (signed short)(16384*sin((float)((i<<1) )*PI*2/WAVPREC));
|
||||
wavtable[(i<<1)+1+WAVPREC] = (signed short)(16384*sin((float)((i<<1)+1)*PI*2/WAVPREC));
|
||||
}
|
||||
for(i=0;i<(WAVPREC>>3);i++)
|
||||
{
|
||||
wavtable[i+(WAVPREC<<1)] = wavtable[i+(WAVPREC>>3)]-16384;
|
||||
wavtable[i+((WAVPREC*17)>>3)] = wavtable[i+(WAVPREC>>2)]+16384;
|
||||
}
|
||||
|
||||
//[table in book]*8/3
|
||||
ksl[7][0] = 0; ksl[7][1] = 24; ksl[7][2] = 32; ksl[7][3] = 37;
|
||||
ksl[7][4] = 40; ksl[7][5] = 43; ksl[7][6] = 45; ksl[7][7] = 47;
|
||||
ksl[7][8] = 48; for(i=9;i<16;i++) ksl[7][i] = i+41;
|
||||
for(j=6;j>=0;j--)
|
||||
for(i=0;i<16;i++)
|
||||
{
|
||||
oct = (long)ksl[j+1][i]-8; if (oct < 0) oct = 0;
|
||||
ksl[j][i] = (unsigned char)oct;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i=0;i<9;i++)
|
||||
{
|
||||
frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
|
||||
oct = ((((long)adlibreg[i+0xb0])>>2)&7);
|
||||
cell[i].tinc = (float)(frn<<oct)*nfrqmul[adlibreg[modulatorbase[i]+0x20]&15];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adlib0 (long i, long v)
|
||||
{
|
||||
unsigned char tmp = adlibreg[i];
|
||||
adlibreg[i] = v;
|
||||
|
||||
if (i == 0xbd)
|
||||
{
|
||||
if ((v&16) > (odrumstat&16)) //BassDrum
|
||||
{
|
||||
cellon(6,16,&cell[6],0);
|
||||
cellon(6,19,&cell[15],1);
|
||||
cell[15].vol *= 2;
|
||||
}
|
||||
if ((v&8) > (odrumstat&8)) //Snare
|
||||
{
|
||||
cellon(16,20,&cell[16],0);
|
||||
cell[16].tinc *= 2*(nfrqmul[adlibreg[17+0x20]&15] / nfrqmul[adlibreg[20+0x20]&15]);
|
||||
if (((adlibreg[20+0xe0]&7) >= 3) && ((adlibreg[20+0xe0]&7) <= 5)) cell[16].vol = 0;
|
||||
cell[16].vol *= 2;
|
||||
}
|
||||
if ((v&4) > (odrumstat&4)) //TomTom
|
||||
{
|
||||
cellon(8,18,&cell[8],0);
|
||||
cell[8].vol *= 2;
|
||||
}
|
||||
if ((v&2) > (odrumstat&2)) //Cymbal
|
||||
{
|
||||
cellon(17,21,&cell[17],0);
|
||||
|
||||
cell[17].wavemask = wavemask[5];
|
||||
cell[17].waveform = &wavtable[waveform[5]];
|
||||
cell[17].tinc *= 16; cell[17].vol *= 2;
|
||||
|
||||
//cell[17].waveform = &wavtable[WAVPREC]; cell[17].wavemask = 0;
|
||||
//if (((adlibreg[21+0xe0]&7) == 0) || ((adlibreg[21+0xe0]&7) == 6))
|
||||
// cell[17].waveform = &wavtable[(WAVPREC*7)>>2];
|
||||
//if (((adlibreg[21+0xe0]&7) == 2) || ((adlibreg[21+0xe0]&7) == 3))
|
||||
// cell[17].waveform = &wavtable[(WAVPREC*5)>>2];
|
||||
}
|
||||
if ((v&1) > (odrumstat&1)) //Hihat
|
||||
{
|
||||
cellon(7,17,&cell[7],0);
|
||||
if (((adlibreg[17+0xe0]&7) == 1) || ((adlibreg[17+0xe0]&7) == 4) ||
|
||||
((adlibreg[17+0xe0]&7) == 5) || ((adlibreg[17+0xe0]&7) == 7)) cell[7].vol = 0;
|
||||
if ((adlibreg[17+0xe0]&7) == 6) { cell[7].wavemask = 0; cell[7].waveform = &wavtable[(WAVPREC*7)>>2]; }
|
||||
}
|
||||
|
||||
odrumstat = v;
|
||||
}
|
||||
else if (((unsigned)(i-0x40) < (unsigned)22) && ((i&7) < 6))
|
||||
{
|
||||
if ((i&7) < 3) // Modulator
|
||||
cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]]);
|
||||
else // Carrier
|
||||
cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]+9]);
|
||||
}
|
||||
else if ((unsigned)(i-0xa0) < (unsigned)9)
|
||||
{
|
||||
cellfreq(i-0xa0,modulatorbase[i-0xa0],&cell[i-0xa0]);
|
||||
cellfreq(i-0xa0,modulatorbase[i-0xa0]+3,&cell[i-0xa0+9]);
|
||||
}
|
||||
else if ((unsigned)(i-0xb0) < (unsigned)9)
|
||||
{
|
||||
if ((v&32) > (tmp&32))
|
||||
{
|
||||
cellon(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0],0);
|
||||
cellon(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9],1);
|
||||
}
|
||||
else if ((v&32) < (tmp&32))
|
||||
cell[i-0xb0].cellfunc = cell[i-0xb0+9].cellfunc = docell2;
|
||||
cellfreq(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0]);
|
||||
cellfreq(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9]);
|
||||
}
|
||||
|
||||
//outdata(i,v);
|
||||
}
|
||||
|
||||
#ifdef USING_ASM
|
||||
static long fpuasm;
|
||||
static float fakeadd = 8388608.0+128.0;
|
||||
static _inline void clipit8 (float f, long a)
|
||||
{
|
||||
_asm
|
||||
{
|
||||
mov edi, a
|
||||
fld dword ptr f
|
||||
fadd dword ptr fakeadd
|
||||
fstp dword ptr fpuasm
|
||||
mov eax, fpuasm
|
||||
test eax, 0x007fff00
|
||||
jz short skipit
|
||||
shr eax, 16
|
||||
xor eax, -1
|
||||
skipit: mov byte ptr [edi], al
|
||||
}
|
||||
}
|
||||
|
||||
static _inline void clipit16 (float f, long a)
|
||||
{
|
||||
_asm
|
||||
{
|
||||
mov eax, a
|
||||
fld dword ptr f
|
||||
fist word ptr [eax]
|
||||
cmp word ptr [eax], 0x8000
|
||||
jne short skipit2
|
||||
fst dword ptr [fpuasm]
|
||||
cmp fpuasm, 0x80000000
|
||||
sbb word ptr [eax], 0
|
||||
skipit2: fstp st
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void clipit8(float f,unsigned char *a) {
|
||||
f/=256.0;
|
||||
f+=128.0;
|
||||
if (f>254.5) *a=255;
|
||||
else if (f<0.5) *a=0;
|
||||
else *a=f;
|
||||
}
|
||||
|
||||
static void clipit16(float f,short *a) {
|
||||
if (f>32766.5) *a=32767;
|
||||
else if (f<-32767.5) *a=-32768;
|
||||
else *a=f;
|
||||
}
|
||||
#endif
|
||||
|
||||
void adlibsetvolume(int i) {
|
||||
AMPSCALE=i;
|
||||
}
|
||||
|
||||
void adlibgetsample (unsigned char *sndptr, long numbytes)
|
||||
{
|
||||
long i, j, k=0, ns, endsamples, rptrs, numsamples;
|
||||
celltype *cptr;
|
||||
float f;
|
||||
short *sndptr2=(short *)sndptr;
|
||||
|
||||
numsamples = (numbytes>>(numspeakers+bytespersample-2));
|
||||
|
||||
if (bytespersample == 1) f = AMPSCALE/256.0; else f = AMPSCALE;
|
||||
if (numspeakers == 1)
|
||||
{
|
||||
nlvol[0] = lvol[0]*f;
|
||||
for(i=0;i<9;i++) rptr[i] = &rbuf[0][0];
|
||||
rptrs = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rptrs = 0;
|
||||
for(i=0;i<9;i++)
|
||||
{
|
||||
if ((!i) || (lvol[i] != lvol[i-1]) || (rvol[i] != rvol[i-1]) ||
|
||||
(lplc[i] != lplc[i-1]) || (rplc[i] != rplc[i-1]))
|
||||
{
|
||||
nlvol[rptrs] = lvol[i]*f;
|
||||
nrvol[rptrs] = rvol[i]*f;
|
||||
nlplc[rptrs] = rend-min(max(lplc[i],0),FIFOSIZ);
|
||||
nrplc[rptrs] = rend-min(max(rplc[i],0),FIFOSIZ);
|
||||
rptrs++;
|
||||
}
|
||||
rptr[i] = &rbuf[rptrs-1][0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//CPU time used to be somewhat less when emulator was only mono!
|
||||
// Because of no delay fifos!
|
||||
|
||||
for(ns=0;ns<numsamples;ns+=endsamples)
|
||||
{
|
||||
endsamples = min(FIFOSIZ*2-rend,FIFOSIZ);
|
||||
endsamples = min(endsamples,numsamples-ns);
|
||||
|
||||
for(i=0;i<9;i++)
|
||||
nrptr[i] = &rptr[i][rend];
|
||||
for(i=0;i<rptrs;i++)
|
||||
memset((void *)&rbuf[i][rend],0,endsamples*sizeof(float));
|
||||
|
||||
if (adlibreg[0xbd]&0x20)
|
||||
{
|
||||
//BassDrum (j=6)
|
||||
if (cell[15].cellfunc != docell4)
|
||||
{
|
||||
if (adlibreg[0xc6]&1)
|
||||
{
|
||||
for(i=0;i<endsamples;i++)
|
||||
{
|
||||
(cell[15].cellfunc)((void *)&cell[15],0.0);
|
||||
nrptr[6][i] += cell[15].val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i=0;i<endsamples;i++)
|
||||
{
|
||||
(cell[6].cellfunc)((void *)&cell[6],cell[6].val*cell[6].mfb);
|
||||
(cell[15].cellfunc)((void *)&cell[15],cell[6].val*WAVPREC*MODFACTOR);
|
||||
nrptr[6][i] += cell[15].val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Snare/Hihat (j=7), Cymbal/TomTom (j=8)
|
||||
if ((cell[7].cellfunc != docell4) || (cell[8].cellfunc != docell4) || (cell[16].cellfunc != docell4) || (cell[17].cellfunc != docell4))
|
||||
{
|
||||
for(i=0;i<endsamples;i++)
|
||||
{
|
||||
k = k*1664525+1013904223;
|
||||
(cell[16].cellfunc)((void *)&cell[16],k&((WAVPREC>>1)-1)); //Snare
|
||||
(cell[7].cellfunc)((void *)&cell[7],k&(WAVPREC-1)); //Hihat
|
||||
(cell[17].cellfunc)((void *)&cell[17],k&((WAVPREC>>3)-1)); //Cymbal
|
||||
(cell[8].cellfunc)((void *)&cell[8],0.0); //TomTom
|
||||
nrptr[7][i] += cell[7].val + cell[16].val;
|
||||
nrptr[8][i] += cell[8].val + cell[17].val;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(j=9-1;j>=0;j--)
|
||||
{
|
||||
if ((adlibreg[0xbd]&0x20) && (j >= 6) && (j < 9)) continue;
|
||||
|
||||
cptr = &cell[j]; k = j;
|
||||
if (adlibreg[0xc0+k]&1)
|
||||
{
|
||||
if ((cptr[9].cellfunc == docell4) && (cptr->cellfunc == docell4)) continue;
|
||||
for(i=0;i<endsamples;i++)
|
||||
{
|
||||
(cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
|
||||
(cptr->cellfunc)((void *)&cptr[9],0);
|
||||
nrptr[j][i] += cptr[9].val + cptr->val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cptr[9].cellfunc == docell4) continue;
|
||||
for(i=0;i<endsamples;i++)
|
||||
{
|
||||
(cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
|
||||
(cptr[9].cellfunc)((void *)&cptr[9],cptr->val*WAVPREC*MODFACTOR);
|
||||
nrptr[j][i] += cptr[9].val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numspeakers == 1)
|
||||
{
|
||||
if (bytespersample == 1)
|
||||
{
|
||||
for(i=endsamples-1;i>=0;i--)
|
||||
clipit8(nrptr[0][i]*nlvol[0],sndptr+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i=endsamples-1;i>=0;i--)
|
||||
clipit16(nrptr[0][i]*nlvol[0],sndptr2+i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memset((void *)snd,0,endsamples*sizeof(float)*2);
|
||||
for(j=0;j<rptrs;j++)
|
||||
{
|
||||
for(i=0;i<endsamples;i++)
|
||||
{
|
||||
snd[(i<<1) ] += rbuf[j][(nlplc[j]+i)&(FIFOSIZ*2-1)]*nlvol[j];
|
||||
snd[(i<<1)+1] += rbuf[j][(nrplc[j]+i)&(FIFOSIZ*2-1)]*nrvol[j];
|
||||
}
|
||||
nlplc[j] += endsamples;
|
||||
nrplc[j] += endsamples;
|
||||
}
|
||||
|
||||
if (bytespersample == 1)
|
||||
{
|
||||
for(i=(endsamples<<1)-1;i>=0;i--)
|
||||
clipit8(snd[i],sndptr+i);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i=(endsamples<<1)-1;i>=0;i--)
|
||||
clipit16(snd[i],sndptr2+i);
|
||||
}
|
||||
}
|
||||
|
||||
sndptr = sndptr+(numspeakers*endsamples);
|
||||
sndptr2 = sndptr2+(numspeakers*endsamples);
|
||||
rend = ((rend+endsamples)&(FIFOSIZ*2-1));
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* ADLIBEMU.H
|
||||
* Copyright (C) 1998-2001 Ken Silverman
|
||||
* Ken Silverman's official web site: "http://www.advsys.net/ken"
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
void adlibinit(long dasamplerate,long danumspeakers,long dabytespersample);
|
||||
void adlib0(long i,long v);
|
||||
void adlibgetsample(void *sndptr,long numbytes);
|
||||
void adlibsetvolume(int i);
|
||||
void randoinsts();
|
||||
extern float lvol[9],rvol[9],lplc[9],rplc[9];
|
||||
@@ -596,7 +596,7 @@ static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
|
||||
OPL->AR_TABLE[i] = rate / ARRATE;
|
||||
OPL->DR_TABLE[i] = rate / DRRATE;
|
||||
}
|
||||
for (i = 60;i < 76;i++)
|
||||
for (i = 60;i < 75;i++)
|
||||
{
|
||||
OPL->AR_TABLE[i] = EG_AED-1;
|
||||
OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
|
||||
* Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* kemuopl.h - Emulated OPL using Ken Silverman's emulator, by Simon Peter
|
||||
* <dn.tlp@gmx.net>
|
||||
*/
|
||||
|
||||
#ifndef H_ADPLUG_KEMUOPL
|
||||
#define H_ADPLUG_KEMUOPL
|
||||
|
||||
#include "opl.h"
|
||||
extern "C" {
|
||||
#include "adlibemu.h"
|
||||
}
|
||||
|
||||
class CKemuopl: public Copl
|
||||
{
|
||||
public:
|
||||
CKemuopl(int rate, bool bit16, bool usestereo)
|
||||
: use16bit(bit16), stereo(usestereo)
|
||||
{
|
||||
adlibinit(rate, usestereo ? 2 : 1, bit16 ? 2 : 1);
|
||||
currType = TYPE_OPL2;
|
||||
};
|
||||
|
||||
void update(short *buf, int samples)
|
||||
{
|
||||
if(use16bit) samples *= 2;
|
||||
if(stereo) samples *= 2;
|
||||
adlibgetsample(buf, samples);
|
||||
}
|
||||
|
||||
// template methods
|
||||
void write(int reg, int val)
|
||||
{
|
||||
if(currChip == 0)
|
||||
adlib0(reg, val);
|
||||
};
|
||||
|
||||
void init() {};
|
||||
|
||||
private:
|
||||
bool use16bit,stereo;
|
||||
};
|
||||
|
||||
#endif
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.1 KiB |
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* OPL2 FM synth
|
||||
*
|
||||
* Copyright (c) 2013 Raine M. Ekman <raine/at/iki/fi>
|
||||
* Copyright (c) 2014 Raine M. Ekman <raine/at/iki/fi>
|
||||
*
|
||||
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
||||
*
|
||||
@@ -23,10 +23,10 @@
|
||||
*/
|
||||
|
||||
// TODO:
|
||||
// - Pitch bend
|
||||
// - Velocity (and aftertouch) sensitivity
|
||||
// - in FM mode: OP2 level, add mode: OP1 and OP2 levels
|
||||
// - Better voice allocation: long releases get cut short :(
|
||||
// - .sbi (or similar) file loading into models
|
||||
// - RT safety = get rid of mutex = make emulator code thread-safe
|
||||
|
||||
// - Extras:
|
||||
// - double release: first release is in effect until noteoff (heard if percussive sound),
|
||||
// second is switched in just before key bit cleared (is this useful???)
|
||||
@@ -53,7 +53,6 @@
|
||||
|
||||
#include "opl.h"
|
||||
#include "temuopl.h"
|
||||
#include "kemuopl.h"
|
||||
|
||||
#include "embed.cpp"
|
||||
#include "math.h"
|
||||
@@ -92,6 +91,9 @@ Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
|
||||
// the emulator code isn't really ready for threads
|
||||
QMutex opl2instrument::emulatorMutex;
|
||||
|
||||
// Weird ordering of voice parameters
|
||||
const unsigned int adlib_opadd[9] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};
|
||||
|
||||
opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) :
|
||||
Instrument( _instrument_track, &OPL2_plugin_descriptor ),
|
||||
m_patchModel( 0, 0, 127, this, tr( "Patch" ) ),
|
||||
@@ -139,10 +141,11 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) :
|
||||
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
|
||||
// Voices are laid out in a funny way...
|
||||
// adlib_opadd = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};
|
||||
|
||||
// Create an emulator - samplerate, 16 bit, mono
|
||||
// CTemuopl is the better one, CKemuopl kinda sucks (some sounds silent, pitch goes flat after a while)
|
||||
emulatorMutex.lock();
|
||||
// theEmulator = new CKemuopl(engine::mixer()->processingSampleRate(), true, false);
|
||||
theEmulator = new CTemuopl(engine::mixer()->processingSampleRate(), true, false);
|
||||
theEmulator->init();
|
||||
// Enable waveform selection
|
||||
@@ -155,12 +158,15 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) :
|
||||
frameCount = engine::mixer()->framesPerPeriod();
|
||||
renderbuffer = new short[frameCount];
|
||||
|
||||
// Some kind of sane default
|
||||
// Some kind of sane defaults
|
||||
pitchbend = 0;
|
||||
tuneEqual(69, 440);
|
||||
|
||||
for(int i=1; i<9; ++i) {
|
||||
voiceNote[i] = OPL2_VOICE_FREE;
|
||||
voiceLRU[i] = i;
|
||||
}
|
||||
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ),
|
||||
this, SLOT( reloadEmulator() ) );
|
||||
// Connect knobs
|
||||
@@ -207,8 +213,15 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) :
|
||||
MOD_CON( trem_depth_mdl );
|
||||
}
|
||||
|
||||
opl2instrument::~opl2instrument() {
|
||||
delete theEmulator;
|
||||
engine::mixer()->removePlayHandles( instrumentTrack() );
|
||||
delete [] renderbuffer;
|
||||
}
|
||||
|
||||
// Samplerate changes when choosing oversampling, so this is more or less mandatory
|
||||
void opl2instrument::reloadEmulator() {
|
||||
delete theEmulator;
|
||||
emulatorMutex.lock();
|
||||
theEmulator = new CTemuopl(engine::mixer()->processingSampleRate(), true, false);
|
||||
theEmulator->init();
|
||||
@@ -216,50 +229,135 @@ void opl2instrument::reloadEmulator() {
|
||||
emulatorMutex.unlock();
|
||||
for(int i=1; i<9; ++i) {
|
||||
voiceNote[i] = OPL2_VOICE_FREE;
|
||||
voiceLRU[i] = i;
|
||||
}
|
||||
updatePatch();
|
||||
}
|
||||
|
||||
// This shall only be called from code protected by the holy Mutex!
|
||||
void opl2instrument::setVoiceVelocity(int voice, int vel) {
|
||||
int vel_adjusted;
|
||||
// Velocity calculation, some kind of approximation
|
||||
// Only calculate for operator 1 if in adding mode, don't want to change timbre
|
||||
if( fm_mdl.value() == false ) {
|
||||
vel_adjusted = 63 - ( op1_lvl_mdl.value() * vel/127.0) ;
|
||||
} else {
|
||||
vel_adjusted = 63 - op1_lvl_mdl.value();
|
||||
}
|
||||
theEmulator->write(0x40+adlib_opadd[voice],
|
||||
( (int)op1_scale_mdl.value() & 0x03 << 6) +
|
||||
( vel_adjusted & 0x3f ) );
|
||||
|
||||
|
||||
vel_adjusted = 63 - ( op2_lvl_mdl.value() * vel/127.0 );
|
||||
// vel_adjusted = 63 - op2_lvl_mdl.value();
|
||||
theEmulator->write(0x43+adlib_opadd[voice],
|
||||
( (int)op2_scale_mdl.value() & 0x03 << 6) +
|
||||
( vel_adjusted & 0x3f ) );
|
||||
// printf("vel %d for voice %d (%f)\n",vel_adjusted,voice,op2_lvl_mdl.value() );
|
||||
}
|
||||
|
||||
// Pop least recently used voice - why does it sometimes lose a voice (mostly 0)?
|
||||
int opl2instrument::popVoice() {
|
||||
int tmp = voiceLRU[0];
|
||||
for( int i=0; i<8; ++i) {
|
||||
voiceLRU[i] = voiceLRU[i+1];
|
||||
}
|
||||
voiceLRU[8] = OPL2_NO_VOICE;
|
||||
/* printf("pop: %d %d %d %d %d %d %d %d %d \n",
|
||||
voiceLRU[0],voiceLRU[1],voiceLRU[2],
|
||||
voiceLRU[3],voiceLRU[4],voiceLRU[5],
|
||||
voiceLRU[6],voiceLRU[7],voiceLRU[8]); */
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int opl2instrument::pushVoice(int v) {
|
||||
int i;
|
||||
for(i=8; i>0; --i) {
|
||||
if( voiceLRU[i-1] != OPL2_NO_VOICE ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
voiceLRU[i] = v;
|
||||
/*printf("%d %d %d %d %d %d %d %d %d \n",
|
||||
voiceLRU[0],voiceLRU[1],voiceLRU[2],
|
||||
voiceLRU[3],voiceLRU[4],voiceLRU[5],
|
||||
voiceLRU[6],voiceLRU[7],voiceLRU[8]); */
|
||||
return i;
|
||||
}
|
||||
|
||||
bool opl2instrument::handleMidiEvent( const midiEvent & _me,
|
||||
const midiTime & _time )
|
||||
{
|
||||
emulatorMutex.lock();
|
||||
// Real dummy version... Should at least add:
|
||||
// - smarter voice allocation:
|
||||
// - reuse same note, now we have round robin-ish
|
||||
// - what to do when voices run out and so on...
|
||||
// - mono mode
|
||||
//
|
||||
int key;
|
||||
static int lastvoice=0;
|
||||
if( _me.m_type == MidiNoteOn ) {
|
||||
// to get us in line with MIDI
|
||||
int key, vel, voice, tmp_pb;
|
||||
|
||||
switch(_me.m_type) {
|
||||
case MidiNoteOn:
|
||||
// to get us in line with MIDI(?)
|
||||
key = _me.key() +12;
|
||||
for(int i=lastvoice+1; i!=lastvoice; ++i,i%=9) {
|
||||
if( voiceNote[i] == OPL2_VOICE_FREE ) {
|
||||
theEmulator->write(0xA0+i, fnums[key] & 0xff);
|
||||
theEmulator->write(0xB0+i, 32 + ((fnums[key] & 0x1f00) >> 8) );
|
||||
// printf("%d: %d %d\n", key, (fnums[key] & 0x1c00) >> 10, fnums[key] & 0x3ff);
|
||||
voiceNote[i] = key;
|
||||
// printf("Voice %d on\n",i);
|
||||
lastvoice=i;
|
||||
break;
|
||||
vel = _me.velocity();
|
||||
|
||||
voice = popVoice();
|
||||
if( voice != OPL2_NO_VOICE ) {
|
||||
// Turn voice on, NB! the frequencies are straight by voice number,
|
||||
// not by the adlib_opadd table!
|
||||
theEmulator->write(0xA0+voice, fnums[key] & 0xff);
|
||||
theEmulator->write(0xB0+voice, 32 + ((fnums[key] & 0x1f00) >> 8) );
|
||||
setVoiceVelocity(voice, vel);
|
||||
voiceNote[voice] = key;
|
||||
velocities[key] = vel;
|
||||
// printf("%d %d\n",voice,vel);
|
||||
}
|
||||
break;
|
||||
case MidiNoteOff:
|
||||
key = _me.key() +12;
|
||||
for(voice=0; voice<9; ++voice) {
|
||||
if( voiceNote[voice] == key ) {
|
||||
theEmulator->write(0xA0+voice, fnums[key] & 0xff);
|
||||
theEmulator->write(0xB0+voice, (fnums[key] & 0x1f00) >> 8 );
|
||||
voiceNote[voice] = OPL2_VOICE_FREE;
|
||||
pushVoice(voice);
|
||||
}
|
||||
}
|
||||
velocities[key] = 0;
|
||||
break;
|
||||
case MidiKeyPressure:
|
||||
key = _me.key() +12;
|
||||
vel = _me.velocity();
|
||||
if( velocities[key] != 0) {
|
||||
velocities[key] = vel;
|
||||
}
|
||||
for(voice=0; voice<9; ++voice) {
|
||||
if(voiceNote[voice] == key) {
|
||||
setVoiceVelocity(voice, vel);
|
||||
}
|
||||
}
|
||||
} else if( _me.m_type == MidiNoteOff ) {
|
||||
key = _me.key() +12;
|
||||
for(int i=0; i<9; ++i) {
|
||||
if( voiceNote[i] == key ) {
|
||||
theEmulator->write(0xA0+i, fnums[key] & 0xff);
|
||||
theEmulator->write(0xB0+i, (fnums[key] & 0x1f00) >> 8 );
|
||||
voiceNote[i] = OPL2_VOICE_FREE;
|
||||
}
|
||||
break;
|
||||
case MidiPitchBend:
|
||||
// Update fnumber table
|
||||
// Pitchbend should be in the range 0...16383 but the new range knob gets it wrong.
|
||||
// tmp_pb = (2*BEND_CENTS)*((float)_me.m_data.m_param[0]/16383)-BEND_CENTS;
|
||||
|
||||
// Something like 100 cents = 8192, but offset by 8192 so the +/-100 cents range goes from 0...16383?
|
||||
tmp_pb = ( _me.m_data.m_param[0]-8192 ) * BEND_CENTS / 8192;
|
||||
|
||||
printf("Pitch bend: %d -> %d cents\n",_me.m_data.m_param[0],tmp_pb);
|
||||
if( tmp_pb != pitchbend ) {
|
||||
pitchbend = tmp_pb;
|
||||
tuneEqual(69, 440.0);
|
||||
}
|
||||
} else {
|
||||
printf("Midi event type %d\n",_me.m_type);
|
||||
// 224 - pitch wheel
|
||||
// 160 - aftertouch?
|
||||
}
|
||||
// Update pitch of sounding notes
|
||||
for( int v=0; v<9; ++v ) {
|
||||
if( voiceNote[v] != OPL2_VOICE_FREE ) {
|
||||
theEmulator->write(0xA0+v, fnums[voiceNote[v] ] & 0xff);
|
||||
theEmulator->write(0xB0+v, 32 + ((fnums[voiceNote[v]] & 0x1f00) >> 8) );
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Midi event type %d\n",_me.m_type);
|
||||
}
|
||||
emulatorMutex.unlock();
|
||||
return true;
|
||||
}
|
||||
@@ -332,7 +430,6 @@ void opl2instrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
|
||||
void opl2instrument::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
printf("loadSettings!\n");
|
||||
op1_a_mdl.loadSettings( _this, "op1_a" );
|
||||
op1_d_mdl.loadSettings( _this, "op1_d" );
|
||||
op1_s_mdl.loadSettings( _this, "op1_s" );
|
||||
@@ -366,19 +463,14 @@ void opl2instrument::loadSettings( const QDomElement & _this )
|
||||
|
||||
}
|
||||
|
||||
// Load a preset in binary form
|
||||
// Load a patch into the emulator
|
||||
void opl2instrument::loadPatch(unsigned char inst[14]) {
|
||||
const unsigned int adlib_opadd[] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};
|
||||
// Set all voices
|
||||
printf("%02x %02x %02x %02x %02x ",inst[0],inst[1],inst[2],inst[3],inst[4]);
|
||||
printf("%02x %02x %02x %02x %02x %02x\n",inst[5],inst[6],inst[7],inst[8],inst[9],inst[10]);
|
||||
|
||||
emulatorMutex.lock();
|
||||
for(int v=0; v<9; ++v) {
|
||||
theEmulator->write(0x20+adlib_opadd[v],inst[0]); // op1 AM/VIB/EG/KSR/Multiplier
|
||||
theEmulator->write(0x23+adlib_opadd[v],inst[1]); // op2
|
||||
theEmulator->write(0x40+adlib_opadd[v],inst[2]); // op1 KSL/Output Level
|
||||
theEmulator->write(0x43+adlib_opadd[v],inst[3]); // op2
|
||||
// theEmulator->write(0x40+adlib_opadd[v],inst[2]); // op1 KSL/Output Level - these are handled by noteon/aftertouch code
|
||||
// theEmulator->write(0x43+adlib_opadd[v],inst[3]); // op2
|
||||
theEmulator->write(0x60+adlib_opadd[v],inst[4]); // op1 A/D
|
||||
theEmulator->write(0x63+adlib_opadd[v],inst[5]); // op2
|
||||
theEmulator->write(0x80+adlib_opadd[v],inst[6]); // op1 S/R
|
||||
@@ -391,10 +483,10 @@ void opl2instrument::loadPatch(unsigned char inst[14]) {
|
||||
}
|
||||
|
||||
void opl2instrument::tuneEqual(int center, float Hz) {
|
||||
float tmp;
|
||||
for(int n=0; n<128; ++n) {
|
||||
float tmp = Hz*pow(2, (n-center)/12.0);
|
||||
tmp = Hz*pow( 2, ( n - center ) / 12.0 + pitchbend / 1200.0 );
|
||||
fnums[n] = Hz2fnum( tmp );
|
||||
//printf("%d: %d %d %f\n", n, (fnums[n] & 0x1c00) >> 10, fnums[n] & 0x3ff,tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +504,6 @@ int opl2instrument::Hz2fnum(float Hz) {
|
||||
// Load one of the default patches
|
||||
void opl2instrument::loadGMPatch() {
|
||||
unsigned char *inst = midi_fm_instruments[m_patchModel.value()];
|
||||
// printf("loadGMPatch: %d ", m_patchModel.value());
|
||||
loadPatch(inst);
|
||||
}
|
||||
|
||||
@@ -423,7 +514,6 @@ void opl2instrument::loadGMPatch() {
|
||||
|
||||
// Update patch from the models to the chip emulation
|
||||
void opl2instrument::updatePatch() {
|
||||
printf("updatePatch()\n");
|
||||
unsigned char *inst = midi_fm_instruments[0];
|
||||
inst[0] = ( op1_trem_mdl.value() ? 128 : 0 ) +
|
||||
( op1_vib_mdl.value() ? 64 : 0 ) +
|
||||
@@ -456,10 +546,16 @@ void opl2instrument::updatePatch() {
|
||||
inst[12] = 0;
|
||||
inst[13] = 0;
|
||||
|
||||
// Not part of the patch per se
|
||||
// Not part of the per-voice patch info
|
||||
theEmulator->write(0xBD, (trem_depth_mdl.value() ? 128 : 0 ) +
|
||||
(vib_depth_mdl.value() ? 64 : 0 ));
|
||||
|
||||
// have to do this, as the level knobs might've changed
|
||||
for( int voice = 0; voice < 9 ; ++voice) {
|
||||
if(voiceNote[voice]!=OPL2_VOICE_FREE) {
|
||||
setVoiceVelocity(voice, velocities[voiceNote[voice]] );
|
||||
}
|
||||
}
|
||||
loadPatch(inst);
|
||||
}
|
||||
|
||||
@@ -552,9 +648,9 @@ opl2instrumentView::opl2instrumentView( Instrument * _instrument,
|
||||
pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap(
|
||||
"artwork" ) );
|
||||
setPalette( pal );
|
||||
|
||||
|
||||
|
||||
}
|
||||
opl2instrumentView::~opl2instrumentView() {
|
||||
// Nobody else seems to delete their knobs and buttons?
|
||||
}
|
||||
|
||||
void opl2instrumentView::modelChanged()
|
||||
@@ -604,4 +700,5 @@ void opl2instrumentView::modelChanged()
|
||||
|
||||
}
|
||||
|
||||
|
||||
#include "moc_opl2instrument.cxx"
|
||||
|
||||
@@ -34,12 +34,17 @@
|
||||
#include "pixmap_button.h"
|
||||
|
||||
#define OPL2_VOICE_FREE 255
|
||||
#define OPL2_NO_VOICE 255
|
||||
// The "normal" range for LMMS pitchbends
|
||||
#define BEND_CENTS 100
|
||||
|
||||
class opl2instrument : public Instrument
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
opl2instrument( InstrumentTrack * _instrument_track );
|
||||
virtual ~opl2instrument();
|
||||
|
||||
virtual QString nodeName() const;
|
||||
virtual PluginView * instantiateView( QWidget * _parent );
|
||||
|
||||
@@ -107,20 +112,33 @@ private:
|
||||
fpp_t frameCount;
|
||||
short *renderbuffer;
|
||||
int voiceNote[9];
|
||||
int heldNotes[128];
|
||||
// Least recently used voices
|
||||
int voiceLRU[9];
|
||||
// 0 - no note, >0 - note on velocity
|
||||
int velocities[128];
|
||||
// These include both octave and Fnumber
|
||||
int fnums[128];
|
||||
// in cents, range defaults to +/-100 cents (should this be changeable?)
|
||||
int pitchbend;
|
||||
|
||||
|
||||
|
||||
int popVoice();
|
||||
int pushVoice(int v);
|
||||
|
||||
int Hz2fnum(float Hz);
|
||||
static QMutex emulatorMutex;
|
||||
void setVoiceVelocity(int voice, int vel);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class opl2instrumentView : public InstrumentView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
opl2instrumentView( Instrument * _instrument, QWidget * _parent );
|
||||
virtual ~opl2instrumentView();
|
||||
lcdSpinBox *m_patch;
|
||||
void modelChanged();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user