libkmid Library API Documentation

fmout.cc

00001 /**************************************************************************
00002 
00003     fmout.cc   - class fmOut which handles the /dev/sequencer device
00004             for fm synths
00005     This file is part of LibKMid 0.9.5
00006     Copyright (C) 1998,99,2000  Antonio Larrosa Jimenez
00007     LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
00008 
00009     This library is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU Library General Public
00011     License as published by the Free Software Foundation; either
00012     version 2 of the License, or (at your option) any later version.
00013 
00014     This library is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017     Library General Public License for more details.
00018 
00019     You should have received a copy of the GNU Library General Public License
00020     along with this library; see the file COPYING.LIB.  If not, write to
00021     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00022     Boston, MA 02111-1307, USA.
00023 
00024     Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
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   // Put opl=3 for opl/3 (better quality/ 6 voices)
00052   //  or opl=2 for fm output (less quality/ 18 voices, which is better imho) :
00053   opl = 2;
00054   // But be aware that opl=3 is not intended to be fully supported by now
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   //vm->clearLists();
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   //if (seqfd>=0) close(seqfd);
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     // Let's get some stereo effect ...
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     // Let's get some stereo effect ...
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     //    SEQ_CONTROL(device, v, CTL_MAIN_VOLUME, chncontroller[chn][CTL_MAIN_VOLUME]);
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;
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:21:59 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001