Added OpulenZ, an FM synth for LMMS

Signed-off-by: Tobias Doerffel <tobias.doerffel@gmail.com>
This commit is contained in:
Raine M. Ekman
2014-01-06 21:26:26 +01:00
committed by Tobias Doerffel
parent 90024c3699
commit 8fa4866ff8
27 changed files with 3475 additions and 0 deletions

View File

@@ -224,6 +224,13 @@ sf2InstrumentView knob {
qproperty-lineWidth: 2;
}
opl2instrumentView knob {
color: rgb(128,128,128);
qproperty-outerColor: rgb(255,255,255);
qproperty-innerRadius: 2;
qproperty-outerRadius: 9;
qproperty-lineWidth: 2;
}
/* Notes:
lcd-spinbox colors: (12, 250, 150), (37, 57, 42)

View File

@@ -10,6 +10,7 @@ ADD_SUBDIRECTORY(lb302)
#ADD_SUBDIRECTORY(lb303)
ADD_SUBDIRECTORY(midi_import)
ADD_SUBDIRECTORY(organic)
ADD_SUBDIRECTORY(opl2)
ADD_SUBDIRECTORY(papu)
ADD_SUBDIRECTORY(patman)
ADD_SUBDIRECTORY(peak_controller_effect)

View File

@@ -0,0 +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)

62
plugins/opl2/README Normal file
View File

@@ -0,0 +1,62 @@
Snatched from AdPlug 2.2.1:
adlibemu.c
adlibemu.h
fmopl.c
fmopl.h
kemuopl.h
opl.h
temuopl.cpp
temuopl.h
mididata.h
1.2.2 Sound generation section
------------------------------
The Sound generation section is responsible for generating the OPL's
sound, according to the input of the Playback section. This sound is
either routed back to the application for it to do something with it or
routed directly to a specific audio hardware.
The following headers provide the interface to the sound generation
section: `emuopl.h', `temuopl.h', `kemuopl.h', `realopl.h',
`silentopl.h', `analopl.h' and `diskopl.h'. All classes inside these
headers are derived from the abstract base class `Copl', declared
inside the file `opl.h', which provides the common interface for the
Backend layer of the Playback section. This interface is not meant for
the Frontend layer (i.e. your application). Your application, however,
has to call special methods of these classes in order to route the data
back, if there is any.
`emuopl.h' provides the class `CEmuopl', which implements a virtual
OPL emulator, which automatically selects the best available OPL chip
emulation and type for each replayer.
`temuopl.h' provides the class `CTEmuopl', which is a wrapper class
around Tatsuyuki Satoh's fmopl OPL2 emulator, which generates wave
audio data to be routed back to the application.
`kemuopl.h' provides the class `CKemuopl', which is a wrapper class
around Ken Silverman's adlibemu OPL2 emulator, which generates wave
audio data to be routed back to the application.
`realopl.h' provides the class `CRealopl', which outputs to a real
hardware OPL2 or OPL3 chip. No data is routed back to the application.
This class is currently only working on x86 hardware.
`silentopl.h' provides the class `CSilentopl', which is a dummy
OPL2/3, which generates nothing. All data sent to it is forgotten
immediately. No data is routed back to the application.
`analopl.h' provides the class `CAnalopl', which is the same as `CRealopl',
but also provides a 9-channel loudness pseudo-analyzer interface for
the application. The loudness data is the only data routed back to the
application.
`diskopl.h' provides the class `CDiskopl', which is an OPL3 emulator
that does not output any sound to the soundcard, but instead writes all
received OPL commands to a file in the RdosPlay RAW format.
Patches can be found at:
http://cd.textfiles.com/soundsensations/SYNTH/
(list at http://cd.textfiles.com/soundsensations/NEWMENU/SYNTH.BBS )

600
plugins/opl2/adlibemu.c Normal file
View File

@@ -0,0 +1,600 @@
/*
* 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);
if (*(long *)&ctc->amp <= 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);
if ((*(long *)&ctc->amp) <= (*(long *)&ctc->sustain))
{
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;
if ((*(long *)&ctc->amp) > 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));
}
}

26
plugins/opl2/adlibemu.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* 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];

BIN
plugins/opl2/artwork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

1390
plugins/opl2/fmopl.c Normal file

File diff suppressed because it is too large Load Diff

174
plugins/opl2/fmopl.h Normal file
View File

@@ -0,0 +1,174 @@
#ifndef __FMOPL_H_
#define __FMOPL_H_
/* --- select emulation chips --- */
#define BUILD_YM3812 (HAS_YM3812)
//#define BUILD_YM3526 (HAS_YM3526)
//#define BUILD_Y8950 (HAS_Y8950)
/* --- system optimize --- */
/* select bit size of output : 8 or 16 */
#define OPL_OUTPUT_BIT 16
/* compiler dependence */
#ifndef OSD_CPU_H
#define OSD_CPU_H
typedef unsigned char UINT8; /* unsigned 8bit */
typedef unsigned short UINT16; /* unsigned 16bit */
typedef unsigned int UINT32; /* unsigned 32bit */
typedef signed char INT8; /* signed 8bit */
typedef signed short INT16; /* signed 16bit */
typedef signed int INT32; /* signed 32bit */
#endif
#if (OPL_OUTPUT_BIT==16)
typedef INT16 OPLSAMPLE;
#endif
#if (OPL_OUTPUT_BIT==8)
typedef unsigned char OPLSAMPLE;
#endif
#if BUILD_Y8950
#include "ymdeltat.h"
#endif
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
typedef void (*OPL_IRQHANDLER)(int param,int irq);
typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
/* !!!!! here is private section , do not access there member direct !!!!! */
#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
#define OPL_TYPE_IO 0x08 /* I/O port */
/* Saving is necessary for member of the 'R' mark for suspend/resume */
/* ---------- OPL one of slot ---------- */
typedef struct fm_opl_slot {
INT32 TL; /* total level :TL << 8 */
INT32 TLL; /* adjusted now TL */
UINT8 KSR; /* key scale rate :(shift down bit) */
INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
INT32 SL; /* sustin level :SL_TALBE[SL] */
INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
UINT8 ksl; /* keyscale level :(shift down bits) */
UINT8 ksr; /* key scale rate :kcode>>KSR */
UINT32 mul; /* multiple :ML_TABLE[ML] */
UINT32 Cnt; /* frequency count : */
UINT32 Incr; /* frequency step : */
/* envelope generator state */
UINT8 eg_typ; /* envelope type flag */
UINT8 evm; /* envelope phase */
INT32 evc; /* envelope counter */
INT32 eve; /* envelope counter end point */
INT32 evs; /* envelope counter step */
INT32 evsa; /* envelope step for AR :AR[ksr] */
INT32 evsd; /* envelope step for DR :DR[ksr] */
INT32 evsr; /* envelope step for RR :RR[ksr] */
/* LFO */
UINT8 ams; /* ams flag */
UINT8 vib; /* vibrate flag */
/* wave selector */
INT32 **wavetable;
}OPL_SLOT;
/* ---------- OPL one of channel ---------- */
typedef struct fm_opl_channel {
OPL_SLOT SLOT[2];
UINT8 CON; /* connection type */
UINT8 FB; /* feed back :(shift down bit) */
INT32 *connect1; /* slot1 output pointer */
INT32 *connect2; /* slot2 output pointer */
INT32 op1_out[2]; /* slot1 output for selfeedback */
/* phase generator state */
UINT32 block_fnum; /* block+fnum : */
UINT8 kcode; /* key code : KeyScaleCode */
UINT32 fc; /* Freq. Increment base */
UINT32 ksl_base; /* KeyScaleLevel Base step */
UINT8 keyon; /* key on/off flag */
} OPL_CH;
/* OPL state */
typedef struct fm_opl_f {
UINT8 type; /* chip type */
int clock; /* master clock (Hz) */
int rate; /* sampling rate (Hz) */
double freqbase; /* frequency base */
double TimerBase; /* Timer base time (==sampling time) */
UINT8 address; /* address register */
UINT8 status; /* status flag */
UINT8 statusmask; /* status mask */
UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
/* Timer */
int T[2]; /* timer counter */
UINT8 st[2]; /* timer enable */
/* FM channel slots */
OPL_CH *P_CH; /* pointer of CH */
int max_ch; /* maximum channel */
/* Rythm sention */
UINT8 rythm; /* Rythm mode , key flag */
#if BUILD_Y8950
/* Delta-T ADPCM unit (Y8950) */
YM_DELTAT *deltat; /* DELTA-T ADPCM */
#endif
/* Keyboard / I/O interface unit (Y8950) */
UINT8 portDirection;
UINT8 portLatch;
OPL_PORTHANDLER_R porthandler_r;
OPL_PORTHANDLER_W porthandler_w;
int port_param;
OPL_PORTHANDLER_R keyboardhandler_r;
OPL_PORTHANDLER_W keyboardhandler_w;
int keyboard_param;
/* time tables */
INT32 AR_TABLE[75]; /* atttack rate tables */
INT32 DR_TABLE[75]; /* decay rate tables */
UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
/* LFO */
INT32 *ams_table;
INT32 *vib_table;
INT32 amsCnt;
INT32 amsIncr;
INT32 vibCnt;
INT32 vibIncr;
/* wave selector enable flag */
UINT8 wavesel;
/* external event callback handler */
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
int TimerParam; /* TIMER parameter */
OPL_IRQHANDLER IRQHandler; /* IRQ handler */
int IRQParam; /* IRQ parameter */
OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
int UpdateParam; /* stream update parameter */
} FM_OPL;
/* ---------- Generic interface section ---------- */
#define OPL_TYPE_YM3526 (0)
#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
FM_OPL *OPLCreate(int type, int clock, int rate);
void OPLDestroy(FM_OPL *OPL);
void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
/* Y8950 port handlers */
void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
void OPLResetChip(FM_OPL *OPL);
int OPLWrite(FM_OPL *OPL,int a,int v);
unsigned char OPLRead(FM_OPL *OPL,int a);
int OPLTimerOver(FM_OPL *OPL,int c);
/* YM3626/YM3812 local section */
void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
#endif

61
plugins/opl2/kemuopl.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* 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

BIN
plugins/opl2/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

174
plugins/opl2/mididata.h Normal file
View File

@@ -0,0 +1,174 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
* Copyright (C) 1999, 2000, 2001 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
*
*
* FM instrument definitions below borrowed from the Allegro library by
* Phil Hassey, <philhassey@hotmail.com> - see "adplug/players/mid.cpp"
* for further acknowledgements.
*/
unsigned char midi_fm_instruments[128][14] =
{
/* This set of GM instrument patches was provided by Jorrit Rouwe...
*/
{ 0x21, 0x21, 0x8f, 0x0c, 0xf2, 0xf2, 0x45, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Acoustic Grand */
{ 0x31, 0x21, 0x4b, 0x09, 0xf2, 0xf2, 0x54, 0x56, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Bright Acoustic */
{ 0x31, 0x21, 0x49, 0x09, 0xf2, 0xf2, 0x55, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Electric Grand */
{ 0xb1, 0x61, 0x0e, 0x09, 0xf2, 0xf3, 0x3b, 0x0b, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Honky-Tonk */
{ 0x01, 0x21, 0x57, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 1 */
{ 0x01, 0x21, 0x93, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 2 */
{ 0x21, 0x36, 0x80, 0x17, 0xa2, 0xf1, 0x01, 0xd5, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Harpsichord */
{ 0x01, 0x01, 0x92, 0x09, 0xc2, 0xc2, 0xa8, 0x58, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Clav */
{ 0x0c, 0x81, 0x5c, 0x09, 0xf6, 0xf3, 0x54, 0xb5, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Celesta */
{ 0x07, 0x11, 0x97, 0x89, 0xf6, 0xf5, 0x32, 0x11, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Glockenspiel */
{ 0x17, 0x01, 0x21, 0x09, 0x56, 0xf6, 0x04, 0x04, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Music Box */
{ 0x18, 0x81, 0x62, 0x09, 0xf3, 0xf2, 0xe6, 0xf6, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Vibraphone */
{ 0x18, 0x21, 0x23, 0x09, 0xf7, 0xe5, 0x55, 0xd8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Marimba */
{ 0x15, 0x01, 0x91, 0x09, 0xf6, 0xf6, 0xa6, 0xe6, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Xylophone */
{ 0x45, 0x81, 0x59, 0x89, 0xd3, 0xa3, 0x82, 0xe3, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tubular Bells */
{ 0x03, 0x81, 0x49, 0x89, 0x74, 0xb3, 0x55, 0x05, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Dulcimer */
{ 0x71, 0x31, 0x92, 0x09, 0xf6, 0xf1, 0x14, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Drawbar Organ */
{ 0x72, 0x30, 0x14, 0x09, 0xc7, 0xc7, 0x58, 0x08, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Percussive Organ */
{ 0x70, 0xb1, 0x44, 0x09, 0xaa, 0x8a, 0x18, 0x08, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Rock Organ */
{ 0x23, 0xb1, 0x93, 0x09, 0x97, 0x55, 0x23, 0x14, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Church Organ */
{ 0x61, 0xb1, 0x13, 0x89, 0x97, 0x55, 0x04, 0x04, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Reed Organ */
{ 0x24, 0xb1, 0x48, 0x09, 0x98, 0x46, 0x2a, 0x1a, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Accoridan */
{ 0x61, 0x21, 0x13, 0x09, 0x91, 0x61, 0x06, 0x07, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Harmonica */
{ 0x21, 0xa1, 0x13, 0x92, 0x71, 0x61, 0x06, 0x07, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tango Accordian */
{ 0x02, 0x41, 0x9c, 0x89, 0xf3, 0xf3, 0x94, 0xc8, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(nylon) */
{ 0x03, 0x11, 0x54, 0x09, 0xf3, 0xf1, 0x9a, 0xe7, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(steel) */
{ 0x23, 0x21, 0x5f, 0x09, 0xf1, 0xf2, 0x3a, 0xf8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(jazz) */
{ 0x03, 0x21, 0x87, 0x89, 0xf6, 0xf3, 0x22, 0xf8, 0x01, 0x00, 0x06, 0, 0, 0 }, /* Electric Guitar(clean) */
{ 0x03, 0x21, 0x47, 0x09, 0xf9, 0xf6, 0x54, 0x3a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(muted) */
{ 0x23, 0x21, 0x4a, 0x0e, 0x91, 0x84, 0x41, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Overdriven Guitar */
{ 0x23, 0x21, 0x4a, 0x09, 0x95, 0x94, 0x19, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Distortion Guitar */
{ 0x09, 0x84, 0xa1, 0x89, 0x20, 0xd1, 0x4f, 0xf8, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Guitar Harmonics */
{ 0x21, 0xa2, 0x1e, 0x09, 0x94, 0xc3, 0x06, 0xa6, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Acoustic Bass */
{ 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(finger) */
{ 0x31, 0x31, 0x8d, 0x09, 0xf1, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(pick) */
{ 0x31, 0x32, 0x5b, 0x09, 0x51, 0x71, 0x28, 0x48, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Fretless Bass */
{ 0x01, 0x21, 0x8b, 0x49, 0xa1, 0xf2, 0x9a, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 1 */
{ 0x21, 0x21, 0x8b, 0x11, 0xa2, 0xa1, 0x16, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 2 */
{ 0x31, 0x31, 0x8b, 0x09, 0xf4, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 1 */
{ 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 2 */
{ 0x31, 0x21, 0x15, 0x09, 0xdd, 0x56, 0x13, 0x26, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Violin */
{ 0x31, 0x21, 0x16, 0x09, 0xdd, 0x66, 0x13, 0x06, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Viola */
{ 0x71, 0x31, 0x49, 0x09, 0xd1, 0x61, 0x1c, 0x0c, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Cello */
{ 0x21, 0x23, 0x4d, 0x89, 0x71, 0x72, 0x12, 0x06, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Contrabass */
{ 0xf1, 0xe1, 0x40, 0x09, 0xf1, 0x6f, 0x21, 0x16, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Tremolo Strings */
{ 0x02, 0x01, 0x1a, 0x89, 0xf5, 0x85, 0x75, 0x35, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pizzicato Strings */
{ 0x02, 0x01, 0x1d, 0x89, 0xf5, 0xf3, 0x75, 0xf4, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Orchestral Strings */
{ 0x10, 0x11, 0x41, 0x09, 0xf5, 0xf2, 0x05, 0xc3, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Timpani */
{ 0x21, 0xa2, 0x9b, 0x0a, 0xb1, 0x72, 0x25, 0x08, 0x01, 0x00, 0x0e, 0, 0, 0 }, /* String Ensemble 1 */
{ 0xa1, 0x21, 0x98, 0x09, 0x7f, 0x3f, 0x03, 0x07, 0x01, 0x01, 0x00, 0, 0, 0 }, /* String Ensemble 2 */
{ 0xa1, 0x61, 0x93, 0x09, 0xc1, 0x4f, 0x12, 0x05, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* SynthStrings 1 */
{ 0x21, 0x61, 0x18, 0x09, 0xc1, 0x4f, 0x22, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* SynthStrings 2 */
{ 0x31, 0x72, 0x5b, 0x8c, 0xf4, 0x8a, 0x15, 0x05, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Choir Aahs */
{ 0xa1, 0x61, 0x90, 0x09, 0x74, 0x71, 0x39, 0x67, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Voice Oohs */
{ 0x71, 0x72, 0x57, 0x09, 0x54, 0x7a, 0x05, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Synth Voice */
{ 0x90, 0x41, 0x00, 0x09, 0x54, 0xa5, 0x63, 0x45, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Orchestra Hit */
{ 0x21, 0x21, 0x92, 0x0a, 0x85, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trumpet */
{ 0x21, 0x21, 0x94, 0x0e, 0x75, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trombone */
{ 0x21, 0x61, 0x94, 0x09, 0x76, 0x82, 0x15, 0x37, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tuba */
{ 0x31, 0x21, 0x43, 0x09, 0x9e, 0x62, 0x17, 0x2c, 0x01, 0x01, 0x02, 0, 0, 0 }, /* Muted Trumpet */
{ 0x21, 0x21, 0x9b, 0x09, 0x61, 0x7f, 0x6a, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* French Horn */
{ 0x61, 0x22, 0x8a, 0x0f, 0x75, 0x74, 0x1f, 0x0f, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Brass Section */
{ 0xa1, 0x21, 0x86, 0x8c, 0x72, 0x71, 0x55, 0x18, 0x01, 0x00, 0x00, 0, 0, 0 }, /* SynthBrass 1 */
{ 0x21, 0x21, 0x4d, 0x09, 0x54, 0xa6, 0x3c, 0x1c, 0x00, 0x00, 0x08, 0, 0, 0 }, /* SynthBrass 2 */
{ 0x31, 0x61, 0x8f, 0x09, 0x93, 0x72, 0x02, 0x0b, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Soprano Sax */
{ 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x03, 0x09, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Alto Sax */
{ 0x31, 0x61, 0x91, 0x09, 0x93, 0x82, 0x03, 0x09, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Tenor Sax */
{ 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x0f, 0x0f, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Baritone Sax */
{ 0x21, 0x21, 0x4b, 0x09, 0xaa, 0x8f, 0x16, 0x0a, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Oboe */
{ 0x31, 0x21, 0x90, 0x09, 0x7e, 0x8b, 0x17, 0x0c, 0x01, 0x01, 0x06, 0, 0, 0 }, /* English Horn */
{ 0x31, 0x32, 0x81, 0x09, 0x75, 0x61, 0x19, 0x19, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Bassoon */
{ 0x32, 0x21, 0x90, 0x09, 0x9b, 0x72, 0x21, 0x17, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Clarinet */
{ 0xe1, 0xe1, 0x1f, 0x09, 0x85, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Piccolo */
{ 0xe1, 0xe1, 0x46, 0x09, 0x88, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Flute */
{ 0xa1, 0x21, 0x9c, 0x09, 0x75, 0x75, 0x1f, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Recorder */
{ 0x31, 0x21, 0x8b, 0x09, 0x84, 0x65, 0x58, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pan Flute */
{ 0xe1, 0xa1, 0x4c, 0x09, 0x66, 0x65, 0x56, 0x26, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Blown Bottle */
{ 0x62, 0xa1, 0xcb, 0x09, 0x76, 0x55, 0x46, 0x36, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Skakuhachi */
{ 0x62, 0xa1, 0xa2, 0x09, 0x57, 0x56, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Whistle */
{ 0x62, 0xa1, 0x9c, 0x09, 0x77, 0x76, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Ocarina */
{ 0x22, 0x21, 0x59, 0x09, 0xff, 0xff, 0x03, 0x0f, 0x02, 0x00, 0x00, 0, 0, 0 }, /* Lead 1 (square) */
{ 0x21, 0x21, 0x0e, 0x09, 0xff, 0xff, 0x0f, 0x0f, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Lead 2 (sawtooth) */
{ 0x22, 0x21, 0x46, 0x89, 0x86, 0x64, 0x55, 0x18, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 3 (calliope) */
{ 0x21, 0xa1, 0x45, 0x09, 0x66, 0x96, 0x12, 0x0a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 4 (chiff) */
{ 0x21, 0x22, 0x8b, 0x09, 0x92, 0x91, 0x2a, 0x2a, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Lead 5 (charang) */
{ 0xa2, 0x61, 0x9e, 0x49, 0xdf, 0x6f, 0x05, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Lead 6 (voice) */
{ 0x20, 0x60, 0x1a, 0x09, 0xef, 0x8f, 0x01, 0x06, 0x00, 0x02, 0x00, 0, 0, 0 }, /* Lead 7 (fifths) */
{ 0x21, 0x21, 0x8f, 0x86, 0xf1, 0xf4, 0x29, 0x09, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Lead 8 (bass+lead) */
{ 0x77, 0xa1, 0xa5, 0x09, 0x53, 0xa0, 0x94, 0x05, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Pad 1 (new age) */
{ 0x61, 0xb1, 0x1f, 0x89, 0xa8, 0x25, 0x11, 0x03, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 2 (warm) */
{ 0x61, 0x61, 0x17, 0x09, 0x91, 0x55, 0x34, 0x16, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Pad 3 (polysynth) */
{ 0x71, 0x72, 0x5d, 0x09, 0x54, 0x6a, 0x01, 0x03, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pad 4 (choir) */
{ 0x21, 0xa2, 0x97, 0x09, 0x21, 0x42, 0x43, 0x35, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Pad 5 (bowed) */
{ 0xa1, 0x21, 0x1c, 0x09, 0xa1, 0x31, 0x77, 0x47, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Pad 6 (metallic) */
{ 0x21, 0x61, 0x89, 0x0c, 0x11, 0x42, 0x33, 0x25, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 7 (halo) */
{ 0xa1, 0x21, 0x15, 0x09, 0x11, 0xcf, 0x47, 0x07, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pad 8 (sweep) */
{ 0x3a, 0x51, 0xce, 0x09, 0xf8, 0x86, 0xf6, 0x02, 0x00, 0x00, 0x02, 0, 0, 0 }, /* FX 1 (rain) */
{ 0x21, 0x21, 0x15, 0x09, 0x21, 0x41, 0x23, 0x13, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 2 (soundtrack) */
{ 0x06, 0x01, 0x5b, 0x09, 0x74, 0xa5, 0x95, 0x72, 0x00, 0x00, 0x00, 0, 0, 0 }, /* FX 3 (crystal) */
{ 0x22, 0x61, 0x92, 0x8c, 0xb1, 0xf2, 0x81, 0x26, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* FX 4 (atmosphere) */
{ 0x41, 0x42, 0x4d, 0x09, 0xf1, 0xf2, 0x51, 0xf5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 5 (brightness) */
{ 0x61, 0xa3, 0x94, 0x89, 0x11, 0x11, 0x51, 0x13, 0x01, 0x00, 0x06, 0, 0, 0 }, /* FX 6 (goblins) */
{ 0x61, 0xa1, 0x8c, 0x89, 0x11, 0x1d, 0x31, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* FX 7 (echoes) */
{ 0xa4, 0x61, 0x4c, 0x09, 0xf3, 0x81, 0x73, 0x23, 0x01, 0x00, 0x04, 0, 0, 0 }, /* FX 8 (sci-fi) */
{ 0x02, 0x07, 0x85, 0x0c, 0xd2, 0xf2, 0x53, 0xf6, 0x00, 0x01, 0x00, 0, 0, 0 }, /* Sitar */
{ 0x11, 0x13, 0x0c, 0x89, 0xa3, 0xa2, 0x11, 0xe5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Banjo */
{ 0x11, 0x11, 0x06, 0x09, 0xf6, 0xf2, 0x41, 0xe6, 0x01, 0x02, 0x04, 0, 0, 0 }, /* Shamisen */
{ 0x93, 0x91, 0x91, 0x09, 0xd4, 0xeb, 0x32, 0x11, 0x00, 0x01, 0x08, 0, 0, 0 }, /* Koto */
{ 0x04, 0x01, 0x4f, 0x09, 0xfa, 0xc2, 0x56, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Kalimba */
{ 0x21, 0x22, 0x49, 0x09, 0x7c, 0x6f, 0x20, 0x0c, 0x00, 0x01, 0x06, 0, 0, 0 }, /* Bagpipe */
{ 0x31, 0x21, 0x85, 0x09, 0xdd, 0x56, 0x33, 0x16, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Fiddle */
{ 0x20, 0x21, 0x04, 0x8a, 0xda, 0x8f, 0x05, 0x0b, 0x02, 0x00, 0x06, 0, 0, 0 }, /* Shanai */
{ 0x05, 0x03, 0x6a, 0x89, 0xf1, 0xc3, 0xe5, 0xe5, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tinkle Bell */
{ 0x07, 0x02, 0x15, 0x09, 0xec, 0xf8, 0x26, 0x16, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Agogo */
{ 0x05, 0x01, 0x9d, 0x09, 0x67, 0xdf, 0x35, 0x05, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Steel Drums */
{ 0x18, 0x12, 0x96, 0x09, 0xfa, 0xf8, 0x28, 0xe5, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Woodblock */
{ 0x10, 0x00, 0x86, 0x0c, 0xa8, 0xfa, 0x07, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Taiko Drum */
{ 0x11, 0x10, 0x41, 0x0c, 0xf8, 0xf3, 0x47, 0x03, 0x02, 0x00, 0x04, 0, 0, 0 }, /* Melodic Tom */
{ 0x01, 0x10, 0x8e, 0x09, 0xf1, 0xf3, 0x06, 0x02, 0x02, 0x00, 0x0e, 0, 0, 0 }, /* Synth Drum */
{ 0x0e, 0xc0, 0x00, 0x09, 0x1f, 0x1f, 0x00, 0xff, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Reverse Cymbal */
{ 0x06, 0x03, 0x80, 0x91, 0xf8, 0x56, 0x24, 0x84, 0x00, 0x02, 0x0e, 0, 0, 0 }, /* Guitar Fret Noise */
{ 0x0e, 0xd0, 0x00, 0x0e, 0xf8, 0x34, 0x00, 0x04, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Breath Noise */
{ 0x0e, 0xc0, 0x00, 0x09, 0xf6, 0x1f, 0x00, 0x02, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Seashore */
{ 0xd5, 0xda, 0x95, 0x49, 0x37, 0x56, 0xa3, 0x37, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Bird Tweet */
{ 0x35, 0x14, 0x5c, 0x11, 0xb2, 0xf4, 0x61, 0x15, 0x02, 0x00, 0x0a, 0, 0, 0 }, /* Telephone ring */
{ 0x0e, 0xd0, 0x00, 0x09, 0xf6, 0x4f, 0x00, 0xf5, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Helicopter */
{ 0x26, 0xe4, 0x00, 0x09, 0xff, 0x12, 0x01, 0x16, 0x00, 0x01, 0x0e, 0, 0, 0 }, /* Applause */
{ 0x00, 0x00, 0x00, 0x09, 0xf3, 0xf6, 0xf0, 0xc9, 0x00, 0x02, 0x0e, 0, 0, 0 } /* Gunshot */
};
/* logarithmic relationship between midi and FM volumes */
static int my_midi_fm_vol_table[128] = {
0, 11, 16, 19, 22, 25, 27, 29, 32, 33, 35, 37, 39, 40, 42, 43,
45, 46, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 75, 76, 77,
78, 79, 80, 80, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 89, 89,
90, 91, 91, 92, 93, 93, 94, 95, 96, 96, 97, 97, 98, 99, 99, 100,
101, 101, 102, 103, 103, 104, 104, 105, 106, 106, 107, 107, 108,
109, 109, 110, 110, 111, 112, 112, 113, 113, 114, 114, 115, 115,
116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122,
123, 123, 124, 124, 125, 125, 126, 126, 127
};

69
plugins/opl2/opl.h Normal file
View File

@@ -0,0 +1,69 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
* Copyright (C) 1999 - 2007 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
*
* opl.h - OPL base class, by Simon Peter <dn.tlp@gmx.net>
*/
#ifndef H_ADPLUG_OPL
#define H_ADPLUG_OPL
class Copl
{
public:
typedef enum {
TYPE_OPL2, TYPE_OPL3, TYPE_DUAL_OPL2
} ChipType;
Copl()
: currChip(0), currType(TYPE_OPL2)
{
}
virtual ~Copl()
{
}
virtual void write(int reg, int val) = 0; // combined register select + data write
virtual void setchip(int n) // select OPL chip
{
if(n < 2)
currChip = n;
}
virtual int getchip() // returns current OPL chip
{
return currChip;
}
virtual void init(void) = 0; // reinitialize OPL chip(s)
// return this OPL chip's type
ChipType gettype()
{
return currType;
}
// Emulation only: fill buffer
virtual void update(short *buf, int samples) {}
protected:
int currChip; // currently selected OPL chip number
ChipType currType; // this OPL chip's type
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

View File

@@ -0,0 +1,613 @@
/*
* OPL2 FM synth
*
* Copyright (c) 2013 Raine M. Ekman <raine/at/iki/fi>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
// TODO:
// - Pitch bend
// - Velocity (and aftertouch) sensitivity
// - in FM mode: OP2 level, add mode: OP1 and OP2 levels
// - .sbi (or similar) file loading into models
// - 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???)
// - Unison: 2,3,4, or 9 voices with configurable spread?
// - Portamento (needs mono mode?)
// - Pre-bend/post-bend in poly mode could use portamento speed?
// - SBI file import?
// - Envelope times in ms for UI: t[0] = 0, t[n] = ( 1<<n ) * X, X = 0.11597 for A, 0.6311 for D/R
// - attack 0.0, 0.23194, 0.46388, 0.92776, 1.85552, 3.71104, 7.42208, 14.84416,
// 29.68832, 59.37664, 118.75328, 237.50656, 475.01312, 950.02624, 1900.05248, 3800.10496
// -decay/release 0.0, 1.2622, 2.5244, 5.0488, 10.0976, 20.1952, 40.3904, 80.7808, 161.5616,
// 323.1232, 646.2464, 1292.4928, 2584.9856, 5169.9712, 10339.9424, 20679.8848
#include "opl2instrument.h"
#include "mididata.h"
#include "debug.h"
#include "Instrument.h"
#include "engine.h"
#include "InstrumentPlayHandle.h"
#include "InstrumentTrack.h"
#include <QtXml/QDomDocument>
#include "opl.h"
#include "temuopl.h"
#include "kemuopl.h"
#include "embed.cpp"
#include "math.h"
#include "knob.h"
#include "lcd_spinbox.h"
#include "pixmap_button.h"
#include "tooltip.h"
extern "C"
{
Plugin::Descriptor PLUGIN_EXPORT OPL2_plugin_descriptor =
{
STRINGIFY( PLUGIN_NAME ),
"OpulenZ",
QT_TRANSLATE_NOOP( "pluginBrowser",
"2-operator FM Synth" ),
"Raine M. Ekman <raine/at/iki/fi>",
0x0100,
Plugin::Instrument,
new PluginPixmapLoader( "logo" ),
NULL,
NULL
};
// necessary for getting instance out of shared lib
Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
{
return( new opl2instrument( static_cast<InstrumentTrack *>( _data ) ) );
}
}
// I'd much rather do without a mutex, but it looks like
// the emulator code isn't really ready for threads
QMutex opl2instrument::emulatorMutex;
opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) :
Instrument( _instrument_track, &OPL2_plugin_descriptor ),
m_patchModel( 0, 0, 127, this, tr( "Patch" ) ),
op1_a_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Attack" ) ),
op1_d_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Decay" ) ),
op1_s_mdl(3.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Sustain" ) ),
op1_r_mdl(10.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Release" ) ),
op1_lvl_mdl(62.0, 0.0, 63.0, 1.0, this, tr( "Op 1 Level" ) ),
op1_scale_mdl(0.0, 0.0, 3.0, 1.0, this, tr( "Op 1 Level Scaling" ) ),
op1_mul_mdl(0.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Frequency Multiple" ) ),
feedback_mdl(0.0, 0.0, 7.0, 1.0, this, tr( "Op 1 Feedback" ) ),
op1_ksr_mdl(false, this, tr( "Op 1 Key Scaling Rate" ) ),
op1_perc_mdl(false, this, tr( "Op 1 Percussive Envelope" ) ),
op1_trem_mdl(true, this, tr( "Op 1 Tremolo" ) ),
op1_vib_mdl(false, this, tr( "Op 1 Vibrato" ) ),
op1_w0_mdl( ),
op1_w1_mdl( ),
op1_w2_mdl( ),
op1_w3_mdl( ),
op1_waveform_mdl(0,0,3,this, tr( "Op 1 Waveform" ) ),
op2_a_mdl(1.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Attack" ) ),
op2_d_mdl(3.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Decay" ) ),
op2_s_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Sustain" ) ),
op2_r_mdl(12.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Release" ) ),
op2_lvl_mdl(63.0, 0.0, 63.0, 1.0, this, tr( "Op 2 Level" ) ),
op2_scale_mdl(0.0, 0.0, 3.0, 1.0, this, tr( "Op 2 Level Scaling" ) ),
op2_mul_mdl(1.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Frequency Multiple" ) ),
op2_ksr_mdl(false, this, tr( "Op 2 Key Scaling Rate" ) ),
op2_perc_mdl(false, this, tr( "Op 2 Percussive Envelope" ) ),
op2_trem_mdl(false, this, tr( "Op 2 Tremolo" ) ),
op2_vib_mdl(true, this, tr( "Op 2 Vibrato" ) ),
op2_w0_mdl( ),
op2_w1_mdl( ),
op2_w2_mdl( ),
op2_w3_mdl( ),
op2_waveform_mdl(0,0,3,this, tr( "Op 2 Waveform" ) ),
fm_mdl(true, this, tr( "FM" ) ),
vib_depth_mdl(false, this, tr( "Vibrato Depth" ) ),
trem_depth_mdl(false, this, tr( "Tremolo Depth" ) )
{
unsigned char defaultPreset[] =
{0xa0, 0x61, 0x01, 0x00, 0x11, 0xec, 0xc5,
0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
// Connect the plugin to the mixer...
InstrumentPlayHandle * iph = new InstrumentPlayHandle( this );
engine::getMixer()->addPlayHandle( iph );
// 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::getMixer()->processingSampleRate(), true, false);
theEmulator = new CTemuopl(engine::getMixer()->processingSampleRate(), true, false);
theEmulator->init();
// Enable waveform selection
theEmulator->write(0x01,0x20);
emulatorMutex.unlock();
//loadPatch(midi_fm_instruments[0]);
// loadPatch(defaultPreset);
updatePatch();
// Can the buffer size change suddenly? I bet that would break lots of stuff
frameCount = engine::getMixer()->framesPerPeriod();
renderbuffer = new short[frameCount];
// Some kind of sane default
tuneEqual(69, 440);
for(int i=1; i<9; ++i) {
voiceNote[i] = OPL2_VOICE_FREE;
}
connect( engine::getMixer(), SIGNAL( sampleRateChanged() ),
this, SLOT( reloadEmulator() ) );
// Connect knobs
// This one's for testing...
connect( &m_patchModel, SIGNAL( dataChanged() ), this, SLOT( loadGMPatch() ) );
#define MOD_CON( model ) connect( &model, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) );
MOD_CON( op1_a_mdl );
MOD_CON( op1_d_mdl );
MOD_CON( op1_s_mdl );
MOD_CON( op1_r_mdl );
MOD_CON( op1_lvl_mdl );
MOD_CON( op1_scale_mdl );
MOD_CON( op1_mul_mdl );
MOD_CON( feedback_mdl );
MOD_CON( op1_ksr_mdl );
MOD_CON( op1_perc_mdl );
MOD_CON( op1_trem_mdl );
MOD_CON( op1_vib_mdl );
MOD_CON( op1_w0_mdl );
MOD_CON( op1_w1_mdl );
MOD_CON( op1_w2_mdl );
MOD_CON( op1_w3_mdl );
MOD_CON( op1_waveform_mdl );
MOD_CON( op2_a_mdl );
MOD_CON( op2_d_mdl );
MOD_CON( op2_s_mdl );
MOD_CON( op2_r_mdl );
MOD_CON( op2_lvl_mdl );
MOD_CON( op2_scale_mdl );
MOD_CON( op2_mul_mdl );
MOD_CON( op2_ksr_mdl );
MOD_CON( op2_perc_mdl );
MOD_CON( op2_trem_mdl );
MOD_CON( op2_vib_mdl );
MOD_CON( op2_w0_mdl );
MOD_CON( op2_w1_mdl );
MOD_CON( op2_w2_mdl );
MOD_CON( op2_w3_mdl );
MOD_CON( op2_waveform_mdl );
MOD_CON( fm_mdl );
MOD_CON( vib_depth_mdl );
MOD_CON( trem_depth_mdl );
}
// Samplerate changes when choosing oversampling, so this is more or less mandatory
void opl2instrument::reloadEmulator() {
emulatorMutex.lock();
theEmulator = new CTemuopl(engine::getMixer()->processingSampleRate(), true, false);
theEmulator->init();
theEmulator->write(0x01,0x20);
emulatorMutex.unlock();
for(int i=1; i<9; ++i) {
voiceNote[i] = OPL2_VOICE_FREE;
}
// updatePatch();
}
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 && !isMuted() ) {
// 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;
}
}
} 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;
}
}
} else {
printf("Midi event type %d\n",_me.m_type);
// 224 - pitch wheel
// 160 - aftertouch?
}
emulatorMutex.unlock();
return true;
}
QString opl2instrument::nodeName() const
{
return( OPL2_plugin_descriptor.name );
}
PluginView * opl2instrument::instantiateView( QWidget * _parent )
{
return( new opl2instrumentView( this, _parent ) );
}
void opl2instrument::play( sampleFrame * _working_buffer )
{
emulatorMutex.lock();
theEmulator->update(renderbuffer, frameCount);
for( fpp_t frame = 0; frame < frameCount; ++frame )
{
sample_t s = float(renderbuffer[frame])/32768.0;
for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
{
_working_buffer[frame][ch] = s;
}
}
emulatorMutex.unlock();
// Throw the data to the track...
instrumentTrack()->processAudioBuffer( _working_buffer, frameCount, NULL );
}
void opl2instrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
op1_a_mdl.saveSettings( _doc, _this, "op1_a" );
op1_d_mdl.saveSettings( _doc, _this, "op1_d" );
op1_s_mdl.saveSettings( _doc, _this, "op1_s" );
op1_r_mdl.saveSettings( _doc, _this, "op1_r" );
op1_lvl_mdl.saveSettings( _doc, _this, "op1_lvl" );
op1_scale_mdl.saveSettings( _doc, _this, "op1_scale" );
op1_mul_mdl.saveSettings( _doc, _this, "op1_mul" );
feedback_mdl.saveSettings( _doc, _this, "feedback" );
op1_ksr_mdl.saveSettings( _doc, _this, "op1_ksr" );
op1_perc_mdl.saveSettings( _doc, _this, "op1_perc" );
op1_trem_mdl.saveSettings( _doc, _this, "op1_trem" );
op1_vib_mdl.saveSettings( _doc, _this, "op1_vib" );
op1_waveform_mdl.saveSettings( _doc, _this, "op1_waveform" );
op2_a_mdl.saveSettings( _doc, _this, "op2_a" );
op2_d_mdl.saveSettings( _doc, _this, "op2_d" );
op2_s_mdl.saveSettings( _doc, _this, "op2_s" );
op2_r_mdl.saveSettings( _doc, _this, "op2_r" );
op2_lvl_mdl.saveSettings( _doc, _this, "op2_lvl" );
op2_scale_mdl.saveSettings( _doc, _this, "op2_scale" );
op2_mul_mdl.saveSettings( _doc, _this, "op2_mul" );
op2_ksr_mdl.saveSettings( _doc, _this, "op2_ksr" );
op2_perc_mdl.saveSettings( _doc, _this, "op2_perc" );
op2_trem_mdl.saveSettings( _doc, _this, "op2_trem" );
op2_vib_mdl.saveSettings( _doc, _this, "op2_vib" );
op2_waveform_mdl.saveSettings( _doc, _this, "op2_waveform" );
fm_mdl.saveSettings( _doc, _this, "fm" );
vib_depth_mdl.saveSettings( _doc, _this, "vib_depth" );
trem_depth_mdl.saveSettings( _doc, _this, "trem_depth" );
}
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" );
op1_r_mdl.loadSettings( _this, "op1_r" );
op1_lvl_mdl.loadSettings( _this, "op1_lvl" );
op1_scale_mdl.loadSettings( _this, "op1_scale" );
op1_mul_mdl.loadSettings( _this, "op1_mul" );
feedback_mdl.loadSettings( _this, "feedback" );
op1_ksr_mdl.loadSettings( _this, "op1_ksr" );
op1_perc_mdl.loadSettings( _this, "op1_perc" );
op1_trem_mdl.loadSettings( _this, "op1_trem" );
op1_vib_mdl.loadSettings( _this, "op1_vib" );
op1_waveform_mdl.loadSettings( _this, "op1_waveform" );
op2_a_mdl.loadSettings( _this, "op2_a" );
op2_d_mdl.loadSettings( _this, "op2_d" );
op2_s_mdl.loadSettings( _this, "op2_s" );
op2_r_mdl.loadSettings( _this, "op2_r" );
op2_lvl_mdl.loadSettings( _this, "op2_lvl" );
op2_scale_mdl.loadSettings( _this, "op2_scale" );
op2_mul_mdl.loadSettings( _this, "op2_mul" );
op2_ksr_mdl.loadSettings( _this, "op2_ksr" );
op2_perc_mdl.loadSettings( _this, "op2_perc" );
op2_trem_mdl.loadSettings( _this, "op2_trem" );
op2_vib_mdl.loadSettings( _this, "op2_vib" );
op2_waveform_mdl.loadSettings( _this, "op2_waveform" );
fm_mdl.loadSettings( _this, "fm" );
vib_depth_mdl.loadSettings( _this, "vib_depth" );
trem_depth_mdl.loadSettings( _this, "trem_depth" );
}
// Load a preset in binary form
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(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
theEmulator->write(0x83+adlib_opadd[v],inst[7]); // op2
theEmulator->write(0xe0+adlib_opadd[v],inst[8]); // op1 waveform
theEmulator->write(0xe3+adlib_opadd[v],inst[9]); // op2
theEmulator->write(0xc0+v,inst[10]); // feedback/algorithm
}
emulatorMutex.unlock();
}
void opl2instrument::tuneEqual(int center, float Hz) {
for(int n=0; n<128; ++n) {
float tmp = Hz*pow(2, (n-center)/12.0);
fnums[n] = Hz2fnum( tmp );
//printf("%d: %d %d %f\n", n, (fnums[n] & 0x1c00) >> 10, fnums[n] & 0x3ff,tmp);
}
}
// Find suitable F number in lowest possible block
int opl2instrument::Hz2fnum(float Hz) {
for(int block=0; block<8; ++block) {
unsigned int fnum = Hz * pow(2, 20-block) / 49716;
if(fnum<1023) {
return fnum + (block << 10);
}
}
return 0;
}
// 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);
}
//
/* void opl2instrument::loadSBIFile() {
} */
// 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 ) +
( op1_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus"
( op1_ksr_mdl.value() ? 16 : 0 ) +
((int)op1_mul_mdl.value() & 0x0f);
inst[1] = ( op2_trem_mdl.value() ? 128 : 0 ) +
( op2_vib_mdl.value() ? 64 : 0 ) +
( op2_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus"
( op2_ksr_mdl.value() ? 16 : 0 ) +
((int)op2_mul_mdl.value() & 0x0f);
inst[2] = ( (int)op1_scale_mdl.value() & 0x03 << 6 ) +
(63 - ( (int)op1_lvl_mdl.value() & 0x3f ) );
inst[3] = ( (int)op2_scale_mdl.value() & 0x03 << 6 ) +
(63 - ( (int)op2_lvl_mdl.value() & 0x3f ) );
inst[4] = ((15 - ((int)op1_a_mdl.value() & 0x0f ) ) << 4 )+
(15 - ( (int)op1_d_mdl.value() & 0x0f ) );
inst[5] = ((15 - ( (int)op2_a_mdl.value() & 0x0f ) ) << 4 )+
(15 - ( (int)op2_d_mdl.value() & 0x0f ) );
inst[6] = ((15 - ( (int)op1_s_mdl.value() & 0x0f ) ) << 4 ) +
(15 - ( (int)op1_r_mdl.value() & 0x0f ) );
inst[7] = ((15 - ( (int)op2_s_mdl.value() & 0x0f ) ) << 4 ) +
(15 - ( (int)op2_r_mdl.value() & 0x0f ) );
inst[8] = (int)op1_waveform_mdl.value() & 0x03;
inst[9] = (int)op2_waveform_mdl.value() & 0x03;
inst[10] = (fm_mdl.value() ? 0 : 1 ) +
(((int)feedback_mdl.value() & 0x07 )<< 1);
// These are always 0 in the list I had?
inst[11] = 0;
inst[12] = 0;
inst[13] = 0;
// Not part of the patch per se
theEmulator->write(0xBD, (trem_depth_mdl.value() ? 128 : 0 ) +
(vib_depth_mdl.value() ? 64 : 0 ));
loadPatch(inst);
}
opl2instrumentView::opl2instrumentView( Instrument * _instrument,
QWidget * _parent ) :
InstrumentView( _instrument, _parent )
{
/* Unnecessary?
m_patch = new lcdSpinBox( 3, this , "PRESET");
m_patch->setLabel( "PRESET" );
m_patch->move( 100, 1 );
m_patch->setEnabled( true );
*/
#define KNOB_GEN(knobname, hinttext, hintunit,xpos,ypos) \
knobname = new knob( knobStyled, this );\
knobname->setHintText( tr(hinttext) + "", hintunit );\
knobname->setFixedSize(22,22);\
knobname->setCenterPointX(11.0);\
knobname->setCenterPointY(11.0);\
knobname->setTotalAngle(270.0);\
knobname->move(xpos,ypos);
#define BUTTON_GEN(buttname, tooltip, xpos, ypos) \
buttname = new pixmapButton( this, NULL );\
buttname->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "opl2_led_on" ) );\
buttname->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "opl2_led_off" ) );\
buttname->setCheckable( true );\
toolTip::add( buttname, tr( tooltip ) );\
buttname->move( xpos, ypos );
#define WAVEBUTTON_GEN(buttname, tooltip, xpos, ypos, icon_on, icon_off, buttgroup) \
buttname = new pixmapButton( this, NULL );\
buttname->setActiveGraphic( PLUGIN_NAME::getIconPixmap( icon_on ) ); \
buttname->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( icon_off ) ); \
toolTip::add( buttname, tr( tooltip ) );\
buttname->move( xpos, ypos );\
buttgroup->addButton(buttname);
// OP1 knobs & buttons...
KNOB_GEN(op1_a_kn, "Attack", "", 6, 48);
KNOB_GEN(op1_d_kn, "Decay", "", 34, 48);
KNOB_GEN(op1_s_kn, "Sustain", "", 62, 48);
KNOB_GEN(op1_r_kn, "Release", "", 90, 48);
KNOB_GEN(op1_lvl_kn, "Level", "", 166, 48);
KNOB_GEN(op1_scale_kn, "Scale", "", 194, 48);
KNOB_GEN(op1_mul_kn, "Frequency multiplier", "", 222, 48);
BUTTON_GEN(op1_ksr_btn, "Keyboard scaling rate", 9, 87);
BUTTON_GEN(op1_perc_btn, "Percussive envelope", 36, 87);
BUTTON_GEN(op1_trem_btn, "Tremolo", 65, 87);
BUTTON_GEN(op1_vib_btn, "Vibrato", 93, 87);
KNOB_GEN(feedback_kn, "Feedback", "", 128, 48);
op1_waveform = new automatableButtonGroup( this );
WAVEBUTTON_GEN(op1_w0_btn,"Sine", 154, 86, "wave1_on", "wave1_off", op1_waveform);
WAVEBUTTON_GEN(op1_w1_btn,"Half sine", 178, 86, "wave2_on", "wave2_off", op1_waveform);
WAVEBUTTON_GEN(op1_w2_btn,"Absolute sine", 199, 86, "wave3_on", "wave3_off", op1_waveform);
WAVEBUTTON_GEN(op1_w3_btn,"Quarter sine", 220, 86, "wave4_on", "wave4_off", op1_waveform);
// And the same for OP2
KNOB_GEN(op2_a_kn, "Attack", "", 6, 138);
KNOB_GEN(op2_d_kn, "Decay", "", 34, 138);
KNOB_GEN(op2_s_kn, "Sustain", "", 62, 138);
KNOB_GEN(op2_r_kn, "Release", "", 90, 138);
KNOB_GEN(op2_lvl_kn, "Level", "", 166, 138);
KNOB_GEN(op2_scale_kn, "Scale", "", 194, 138);
KNOB_GEN(op2_mul_kn, "Frequency multiplier", "", 222, 138);
BUTTON_GEN(op2_ksr_btn, "Keyboard scaling rate", 9, 177);
BUTTON_GEN(op2_perc_btn, "Percussive envelope", 36, 177);
BUTTON_GEN(op2_trem_btn, "Tremolo", 65, 177);
BUTTON_GEN(op2_vib_btn, "Vibrato", 93, 177);
op2_waveform = new automatableButtonGroup( this );
WAVEBUTTON_GEN(op2_w0_btn,"Sine", 154, 176, "wave1_on", "wave1_off", op2_waveform);
WAVEBUTTON_GEN(op2_w1_btn,"Half sine", 178, 176, "wave2_on", "wave2_off", op2_waveform);
WAVEBUTTON_GEN(op2_w2_btn,"Absolute sine", 199, 176, "wave3_on", "wave3_off", op2_waveform);
WAVEBUTTON_GEN(op2_w3_btn,"Quarter Sine", 220, 176, "wave4_on", "wave4_off", op2_waveform);
BUTTON_GEN(fm_btn, "FM", 9, 220);
BUTTON_GEN(vib_depth_btn, "Vibrato depth", 65, 220);
BUTTON_GEN(trem_depth_btn, "Tremolo depth", 93, 220);
setAutoFillBackground( true );
QPalette pal;
pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap(
"artwork" ) );
setPalette( pal );
}
void opl2instrumentView::modelChanged()
{
opl2instrument * m = castModel<opl2instrument>();
// m_patch->setModel( &m->m_patchModel );
op1_a_kn->setModel( &m->op1_a_mdl );
op1_d_kn->setModel( &m->op1_d_mdl );
op1_s_kn->setModel( &m->op1_s_mdl );
op1_r_kn->setModel( &m->op1_r_mdl );
op1_lvl_kn->setModel( &m->op1_lvl_mdl );
op1_scale_kn->setModel( &m->op1_scale_mdl );
op1_mul_kn->setModel( &m->op1_mul_mdl );
feedback_kn->setModel( &m->feedback_mdl );
op1_ksr_btn->setModel( &m->op1_ksr_mdl );
op1_perc_btn->setModel( &m->op1_perc_mdl );
op1_trem_btn->setModel( &m->op1_trem_mdl );
op1_vib_btn->setModel( &m->op1_vib_mdl );
/* op1_w0_btn->setModel( &m->op1_w0_mdl );
op1_w1_btn->setModel( &m->op1_w1_mdl );
op1_w2_btn->setModel( &m->op1_w2_mdl );
op1_w3_btn->setModel( &m->op1_w3_mdl ); */
op1_waveform->setModel( &m->op1_waveform_mdl );
op2_a_kn->setModel( &m->op2_a_mdl );
op2_d_kn->setModel( &m->op2_d_mdl );
op2_s_kn->setModel( &m->op2_s_mdl );
op2_r_kn->setModel( &m->op2_r_mdl );
op2_lvl_kn->setModel( &m->op2_lvl_mdl );
op2_scale_kn->setModel( &m->op2_scale_mdl );
op2_mul_kn->setModel( &m->op2_mul_mdl );
op2_ksr_btn->setModel( &m->op2_ksr_mdl );
op2_perc_btn->setModel( &m->op2_perc_mdl );
op2_trem_btn->setModel( &m->op2_trem_mdl );
op2_vib_btn->setModel( &m->op2_vib_mdl );
/* op2_w0_btn->setModel( &m->op2_w0_mdl );
op2_w1_btn->setModel( &m->op2_w1_mdl );
op2_w2_btn->setModel( &m->op2_w2_mdl );
op2_w3_btn->setModel( &m->op2_w3_mdl ); */
op2_waveform->setModel( &m->op2_waveform_mdl );
fm_btn->setModel( &m->fm_mdl );
vib_depth_btn->setModel( &m->vib_depth_mdl );
trem_depth_btn->setModel( &m->trem_depth_mdl );
}
#include "moc_opl2instrument.cxx"

View File

@@ -0,0 +1,173 @@
/*
* OPL2 FM synth
*
* Copyright (c) 2013 Raine M. Ekman <raine/at/iki/fi>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef _OPL2_H
#define _OPL2_H
#include "Instrument.h"
#include "InstrumentView.h"
#include "opl.h"
#include "lcd_spinbox.h"
#include "knob.h"
#include "pixmap_button.h"
#define OPL2_VOICE_FREE 255
class opl2instrument : public Instrument
{
Q_OBJECT
public:
opl2instrument( InstrumentTrack * _instrument_track );
virtual QString nodeName() const;
virtual PluginView * instantiateView( QWidget * _parent );
inline virtual bool isMidiBased() const { return true; }
virtual bool handleMidiEvent( const midiEvent & _me,
const midiTime & _time );
virtual void play( sampleFrame * _working_buffer );
void saveSettings( QDomDocument & _doc, QDomElement & _this );
void loadSettings( const QDomElement & _this );
void loadPatch(unsigned char inst[14]);
void tuneEqual(int center, float Hz);
IntModel m_patchModel;
FloatModel op1_a_mdl;
FloatModel op1_d_mdl;
FloatModel op1_s_mdl;
FloatModel op1_r_mdl;
FloatModel op1_lvl_mdl;
FloatModel op1_scale_mdl;
FloatModel op1_mul_mdl;
FloatModel feedback_mdl;
BoolModel op1_ksr_mdl;
BoolModel op1_perc_mdl;
BoolModel op1_trem_mdl;
BoolModel op1_vib_mdl;
BoolModel op1_w0_mdl;
BoolModel op1_w1_mdl;
BoolModel op1_w2_mdl;
BoolModel op1_w3_mdl;
IntModel op1_waveform_mdl;
FloatModel op2_a_mdl;
FloatModel op2_d_mdl;
FloatModel op2_s_mdl;
FloatModel op2_r_mdl;
FloatModel op2_lvl_mdl;
FloatModel op2_scale_mdl;
FloatModel op2_mul_mdl;
BoolModel op2_ksr_mdl;
BoolModel op2_perc_mdl;
BoolModel op2_trem_mdl;
BoolModel op2_vib_mdl;
BoolModel op2_w0_mdl;
BoolModel op2_w1_mdl;
BoolModel op2_w2_mdl;
BoolModel op2_w3_mdl;
IntModel op2_waveform_mdl;
BoolModel fm_mdl;
BoolModel vib_depth_mdl;
BoolModel trem_depth_mdl;
private slots:
void updatePatch();
void reloadEmulator();
void loadGMPatch();
private:
Copl *theEmulator;
fpp_t frameCount;
short *renderbuffer;
int voiceNote[9];
int heldNotes[128];
// These include both octave and Fnumber
int fnums[128];
int Hz2fnum(float Hz);
static QMutex emulatorMutex;
};
class opl2instrumentView : public InstrumentView
{
Q_OBJECT
public:
opl2instrumentView( Instrument * _instrument, QWidget * _parent );
lcdSpinBox *m_patch;
void modelChanged();
knob *op1_a_kn;
knob *op1_d_kn;
knob *op1_s_kn;
knob *op1_r_kn;
knob *op1_lvl_kn;
knob *op1_scale_kn;
knob *op1_mul_kn;
knob *feedback_kn;
pixmapButton *op1_ksr_btn;
pixmapButton *op1_perc_btn;
pixmapButton *op1_trem_btn;
pixmapButton *op1_vib_btn;
pixmapButton *op1_w0_btn;
pixmapButton *op1_w1_btn;
pixmapButton *op1_w2_btn;
pixmapButton *op1_w3_btn;
automatableButtonGroup *op1_waveform;
knob *op2_a_kn;
knob *op2_d_kn;
knob *op2_s_kn;
knob *op2_r_kn;
knob *op2_lvl_kn;
knob *op2_scale_kn;
knob *op2_mul_kn;
pixmapButton *op2_ksr_btn;
pixmapButton *op2_perc_btn;
pixmapButton *op2_trem_btn;
pixmapButton *op2_vib_btn;
pixmapButton *op2_w0_btn;
pixmapButton *op2_w1_btn;
pixmapButton *op2_w2_btn;
pixmapButton *op2_w3_btn;
automatableButtonGroup *op2_waveform;
pixmapButton *fm_btn;
pixmapButton *vib_depth_btn;
pixmapButton *trem_depth_btn;
};
#endif

75
plugins/opl2/temuopl.cpp Normal file
View File

@@ -0,0 +1,75 @@
/*
* AdPlug - Replayer for many OPL2/OPL3 audio file formats.
* Copyright (C) 1999 - 2004 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
*
* temuopl.cpp - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter <dn.tlp@gmx.net>
*/
#include "temuopl.h"
CTemuopl::CTemuopl(int rate, bool bit16, bool usestereo)
: use16bit(bit16), stereo(usestereo)
{
opl = OPLCreate(OPL_TYPE_YM3812, 3579545, rate);
}
CTemuopl::~CTemuopl()
{
OPLDestroy(opl);
}
void CTemuopl::update(short *buf, int samples)
{
int i;
if(use16bit) {
YM3812UpdateOne(opl,buf,samples);
if(stereo)
for(i=samples-1;i>=0;i--) {
buf[i*2] = buf[i];
buf[i*2+1] = buf[i];
}
} else {
short *tempbuf = new short[stereo ? samples*2 : samples];
int i;
YM3812UpdateOne(opl,tempbuf,samples);
if(stereo)
for(i=samples-1;i>=0;i--) {
tempbuf[i*2] = tempbuf[i];
tempbuf[i*2+1] = tempbuf[i];
}
for(i=0;i<(stereo ? samples*2 : samples);i++)
((char *)buf)[i] = (tempbuf[i] >> 8) ^ 0x80;
delete [] tempbuf;
}
}
void CTemuopl::write(int reg, int val)
{
OPLWrite(opl,0,reg);
OPLWrite(opl,1,val);
}
void CTemuopl::init()
{
OPLResetChip(opl);
}

47
plugins/opl2/temuopl.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
* Copyright (C) 1999 - 2004 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
*
* temuopl.h - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter <dn.tlp@gmx.net>
*/
#ifndef H_ADPLUG_TEMUOPL
#define H_ADPLUG_TEMUOPL
#include "opl.h"
extern "C" {
#include "fmopl.h"
}
class CTemuopl: public Copl
{
public:
CTemuopl(int rate, bool bit16, bool usestereo); // rate = sample rate
virtual ~CTemuopl();
void update(short *buf, int samples); // fill buffer
// template methods
void write(int reg, int val);
void init();
private:
bool use16bit,stereo;
FM_OPL *opl; // holds emulator data
};
#endif

BIN
plugins/opl2/wave1_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

BIN
plugins/opl2/wave1_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

BIN
plugins/opl2/wave2_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

BIN
plugins/opl2/wave2_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

BIN
plugins/opl2/wave3_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

BIN
plugins/opl2/wave3_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

BIN
plugins/opl2/wave4_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

BIN
plugins/opl2/wave4_on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B