00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
#include "fmout.h"
00028
#include <unistd.h>
00029
#include <fcntl.h>
00030
#include <stdio.h>
00031
#include "sndcard.h"
00032
#include <sys/ioctl.h>
00033
#include <errno.h>
00034
#include <string.h>
00035
#include <sys/param.h>
00036
#include <stdlib.h>
00037
#include <limits.h>
00038
#include "midispec.h"
00039
#ifdef HAVE_CONFIG_H
00040
#include <config.h>
00041
#endif
00042
00043 SEQ_USE_EXTBUF();
00044
00045 FMOut::FMOut(
int d,
int total )
00046 {
00047 seqfd = -1;
00048 devicetype = KMID_FM;
00049 device = d;
00050 _ok = 1;
00051
00052
00053 opl = 2;
00054
00055
00056 nvoices = total;
00057 vm =
new VoiceManager (nvoices);
00058 }
00059
00060 FMOut::~FMOut()
00061 {
00062
closeDev();
00063
delete vm;
00064
if (deleteFMPatchesDirectory)
00065 {
00066 free((
char *)FMPatchesDirectory);
00067 deleteFMPatchesDirectory = 0;
00068 FMPatchesDirectory=
"/etc";
00069 }
00070 }
00071
00072 void FMOut::openDev (
int sqfd)
00073 {
00074
#ifdef HAVE_OSS_SUPPORT
00075
_ok=1;
00076 seqfd = sqfd;
00077
00078
if ( seqfd == -1 )
00079 {
00080 printfdebug(
"ERROR: Could not open /dev/sequencer\n");
00081
return;
00082 }
00083
00084 loadFMPatches();
00085
#endif
00086
00087 }
00088
00089 void FMOut::closeDev (
void)
00090 {
00091
if (!
ok())
return;
00092 vm->clearLists();
00093
00094 seqfd = -1;
00095 }
00096
00097 void FMOut::initDev (
void)
00098 {
00099
#ifdef HAVE_OSS_SUPPORT
00100
int chn;
00101
if (!
ok())
return;
00102 uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
00103
sysex(gm_reset,
sizeof(gm_reset));
00104
for (chn=0;chn<16;chn++)
00105 {
00106 chnmute[chn]=0;
00107
chnPatchChange(chn,0);
00108
chnPressure(chn,127);
00109
chnPitchBender(chn, 0x00, 0x40);
00110
chnController(chn, CTL_MAIN_VOLUME,127);
00111
chnController(chn, CTL_EXT_EFF_DEPTH, 0);
00112
chnController(chn, CTL_CHORUS_DEPTH, 0);
00113
chnController(chn, 0x4a, 127);
00114 }
00115
00116
if (opl==3) ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &device);
00117 SEQ_VOLUME_MODE(device,VOL_METHOD_LINEAR);
00118
00119
for (
int i = 0; i < nvoices; i++)
00120 {
00121 SEQ_CONTROL(device, i, SEQ_VOLMODE, VOL_METHOD_LINEAR);
00122 SEQ_STOP_NOTE(device, i, vm->note(i), 64);
00123 }
00124
#endif
00125
}
00126
00127
void FMOut::loadFMPatches(
void)
00128 {
00129
#ifdef HAVE_OSS_SUPPORT
00130
char patchesfile[PATH_MAX];
00131
char drumsfile[PATH_MAX];
00132
int size;
00133
struct sbi_instrument instr;
00134
char tmp[60];
00135
int i,j;
00136
for ( i=0; i<256; i++ )
00137 patchloaded[i] = 0;
00138
int stereoeffect=rand()%3;
00139 FILE *fh;
00140
int datasize;
00141
00142
if (opl==3)
00143 {
00144 snprintf(patchesfile, PATH_MAX,
"%s/std.o3",FMPatchesDirectory);
00145 size=60;
00146 }
00147
else
00148 {
00149 snprintf(patchesfile, PATH_MAX,
"%s/std.sb",FMPatchesDirectory);
00150 size=52;
00151 }
00152 fh=fopen(patchesfile,
"rb");
00153
if (fh==NULL)
return;
00154
00155
for (i=0;i<128;i++)
00156 {
00157 fread(tmp,size,1,fh);
00158 patchloaded[i]=1;
00159 instr.key = ((strncmp(tmp,
"4OP", 3) == 0))? OPL3_PATCH : FM_PATCH;
00160 datasize = (strncmp(tmp,
"4OP", 3) == 0)? 22 : 11;
00161 instr.device=device;
00162 instr.channel = i;
00163
00164 tmp[46] = (tmp[46] & 0xcf) | ((++stereoeffect)<<4);
00165 stereoeffect=stereoeffect%3;
00166
for (j=0; j<22; j++)
00167 instr.operators[j] = tmp[j+36];
00168 SEQ_WRPATCH(&instr,
sizeof(instr));
00169 }
00170 fclose(fh);
00171
00172
if (opl==3)
00173 {
00174 snprintf(drumsfile, PATH_MAX,
"%s/drums.o3",FMPatchesDirectory);
00175 }
00176
else
00177 {
00178 snprintf(drumsfile, PATH_MAX,
"%s/drums.sb",FMPatchesDirectory);
00179 }
00180
00181 fh=fopen(drumsfile,
"rb");
00182
if (fh==NULL)
return;
00183
00184
for (i=128;i<175;i++)
00185 {
00186 fread(tmp,size,1,fh);
00187 patchloaded[i]=1;
00188 instr.key = (strncmp(tmp,
"4OP", 3) == 0)? OPL3_PATCH : FM_PATCH;
00189 datasize = (strncmp(tmp,
"4OP", 3) == 0)? 22 : 11;
00190 instr.device=device;
00191 instr.channel = i;
00192
00193 tmp[46] = (tmp[46] & 0xcf) | ((++stereoeffect)<<4);
00194 stereoeffect=stereoeffect%3;
00195
for (j=0; j<22; j++)
00196 instr.operators[j] = tmp[j+36];
00197 SEQ_WRPATCH(&instr,
sizeof(instr));
00198 }
00199 fclose(fh);
00200
00201
#ifdef FMOUTDEBUG
00202
printfdebug(
"Patches loaded\n");
00203
#endif
00204
#endif
00205
}
00206
00207 int FMOut::patch(
int p)
00208 {
00209
if (patchloaded[p]==1)
return p;
00210
#ifdef FMOUTDEBUG
00211
printfdebug(
"Not loaded %d!\n",p);
00212
#endif
00213
p=0;
00214
while ((p<256)&&(patchloaded[p]==0)) p++;
00215
return p;
00216 }
00217
00218 void FMOut::noteOn (uchar chn, uchar note, uchar vel)
00219 {
00220
if (vel==0)
00221 {
00222
noteOff(chn,note,vel);
00223 }
00224
else
00225 {
00226
if (chn==PERCUSSION_CHANNEL)
00227 {
00228
if (patchloaded[note+128]==0)
return;
00229
else
00230
if (patchloaded[chnpatch[chn]]==0)
return;
00231 }
00232
int v=vm->allocateVoice(chn,note);
00233
int p;
00234
if (chn==PERCUSSION_CHANNEL)
00235 SEQ_SET_PATCH(device,v ,p=
patch(note+128))
00236
else
00237 SEQ_SET_PATCH(device,v ,p=map->
patch(chn,chnpatch[chn]));
00238 SEQ_BENDER(device, v, chnbender[chn]);
00239
00240 SEQ_START_NOTE(device, v, note, vel);
00241
00242
00243 SEQ_CHN_PRESSURE(device, v , chnpressure[chn]);
00244 }
00245
00246
#ifdef FMOUTDEBUG
00247
printfdebug(
"Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
00248
#endif
00249
}
00250
00251 void FMOut::noteOff (uchar chn, uchar note, uchar vel)
00252 {
00253
int i;
00254 vm->initSearch();
00255
while ((i=vm->search(chn,note))!=-1)
00256 {
00257 SEQ_STOP_NOTE(device, i, note, vel);
00258 vm->deallocateVoice(i);
00259 }
00260
00261
#ifdef FMOUTDEBUG
00262
printfdebug(
"Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
00263
#endif
00264
}
00265
00266 void FMOut::keyPressure (uchar chn, uchar note, uchar vel)
00267 {
00268
int i;
00269 vm->initSearch();
00270
while ((i=vm->search(chn,note))!=-1)
00271 SEQ_KEY_PRESSURE(device, i, note,vel);
00272 }
00273
00274 void FMOut::chnPatchChange (uchar chn, uchar patch)
00275 {
00276
if (chn==PERCUSSION_CHANNEL)
return;
00277
int i;
00278 vm->initSearch();
00279
while ((i=vm->search(chn))!=-1)
00280 SEQ_SET_PATCH(device,i,map->
patch(chn,patch));
00281
00282 chnpatch[chn]=patch;
00283 }
00284
00285 void FMOut::chnPressure (uchar chn, uchar vel)
00286 {
00287
int i;
00288 vm->initSearch();
00289
while ((i=vm->search(chn))!=-1)
00290 SEQ_CHN_PRESSURE(device, i , vel);
00291
00292 chnpressure[chn]=vel;
00293 }
00294
00295 void FMOut::chnPitchBender(uchar chn,uchar lsb, uchar msb)
00296 {
00297 chnbender[chn]=((
int)msb<<7) | (lsb & 0x7F);
00298
00299
int i;
00300 vm->initSearch();
00301
while ((i=vm->search(chn))!=-1)
00302 SEQ_BENDER(device, i, chnbender[chn]);
00303
00304 }
00305
00306 void FMOut::chnController (uchar chn, uchar ctl, uchar v)
00307 {
00308
if ((ctl==11)||(ctl==7))
00309 {
00310 v=(v*volumepercentage)/100;
00311
if (v>127) v=127;
00312 }
00313
int i;
00314 vm->initSearch();
00315
while ((i=vm->search(chn))!=-1)
00316 SEQ_CONTROL(device, i, ctl, v);
00317
00318 chncontroller[chn][ctl]=v;
00319 }
00320
00321 void FMOut::sysex(uchar *, ulong )
00322 {
00323
00324 }
00325
00326 void FMOut::setFMPatchesDirectory(
const char *dir)
00327 {
00328
if ((dir==NULL)||(dir[0]==0))
return;
00329
if (deleteFMPatchesDirectory)
00330 free((
char *)FMPatchesDirectory);
00331
00332 FMPatchesDirectory = strdup(dir);
00333
00334 deleteFMPatchesDirectory=1;
00335 }
00336
00337 void FMOut::setVolumePercentage (
int i )
00338 {
00339
#ifdef HAVE_OSS_SUPPORT
00340
int fd=open(
"/dev/mixer0",O_RDWR,0);
00341
if (fd==-1)
return;
00342
int a=i*255/100;
00343
if (a>255) a=255;
00344 a=(a<<8) | a;
00345
if (ioctl(fd,MIXER_WRITE(SOUND_MIXER_SYNTH),&a) == -1)
00346 printfdebug(
"ERROR writing to mixer\n");
00347 close(fd);
00348
#endif
00349
volumepercentage=i;
00350 }
00351
00352
00353
const char *FMOut::FMPatchesDirectory =
"/etc";
00354
int FMOut::deleteFMPatchesDirectory = 0;