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;