gusout.cc
00001 /************************************************************************** 00002 00003 gusout.cc - class GUSOut which implements support for Gravis 00004 Ultrasound cards through a /dev/sequencer device 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/libtdemid.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., 51 Franklin Street, Fifth Floor, 00022 Boston, MA 02110-1301, USA. 00023 00024 Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org> 00025 00026 ***************************************************************************/ 00027 #include "gusout.h" 00028 #include "sndcard.h" 00029 #include "midispec.h" 00030 #include "gusvoices.h" 00031 #include <sys/stat.h> 00032 #include <unistd.h> 00033 #include <fcntl.h> 00034 #include <stdio.h> 00035 #include <sys/ioctl.h> 00036 #include <errno.h> 00037 #include <string.h> 00038 #include <sys/param.h> 00039 #include <stdlib.h> 00040 #ifdef HAVE_CONFIG_H 00041 #include <config.h> 00042 #endif 00043 00044 SEQ_USE_EXTBUF(); 00045 00046 #ifdef HAVE_OSS_SUPPORT 00047 struct pat_header 00048 { 00049 char magic[12]; 00050 char version[10]; 00051 char description[60]; 00052 unsigned char instruments; 00053 char voices; 00054 char channels; 00055 unsigned short nr_waveforms; 00056 unsigned short master_volume; 00057 unsigned long data_size; 00058 }; 00059 struct sample_header 00060 { 00061 char name[7]; 00062 unsigned char fractions; 00063 long len; 00064 long loop_start; 00065 long loop_end; 00066 unsigned short base_freq; 00067 long low_note; 00068 long high_note; 00069 long base_note; 00070 short detune; 00071 unsigned char panning; 00072 00073 unsigned char envelope_rate[6]; 00074 unsigned char envelope_offset[6]; 00075 00076 unsigned char tremolo_sweep; 00077 unsigned char tremolo_rate; 00078 unsigned char tremolo_depth; 00079 00080 unsigned char vibrato_sweep; 00081 unsigned char vibrato_rate; 00082 unsigned char vibrato_depth; 00083 00084 char modes; 00085 00086 short scale_frequency; 00087 unsigned short scale_factor; 00088 }; 00089 00090 int get_dint(unsigned char *p) 00091 { 00092 unsigned int v=0; 00093 00094 for (int i=0;i<4;i++) 00095 { 00096 v |= (p[i] << (i*8)); 00097 } 00098 return (int)v; 00099 } 00100 00101 unsigned short get_word(unsigned char *p) 00102 { 00103 unsigned short v=0; 00104 00105 for (int i=0;i<2;i++) 00106 v |= (*p++ << (i*8)); 00107 return (short)v; 00108 } 00109 00110 #endif 00111 00112 GUSOut::GUSOut(int d,int total) 00113 { 00114 seqfd = -1; 00115 devicetype=KMID_GUS; 00116 device= d; 00117 _ok=1; 00118 00119 use8bit=0; 00120 nvoices=total; 00121 vm=new VoiceManager(nvoices); 00122 } 00123 00124 GUSOut::~GUSOut() 00125 { 00126 closeDev(); 00127 00128 delete vm; 00129 if (delete_GUS_patches_directory) 00130 { 00131 free((char *)GUS_patches_directory); 00132 delete_GUS_patches_directory = 0; 00133 GUS_patches_directory="/etc"; 00134 } 00135 } 00136 00137 void GUSOut::openDev (int sqfd) 00138 { 00139 _ok=1; 00140 seqfd = sqfd; 00141 //vm->clearLists(); 00142 if (seqfd==-1) 00143 { 00144 printfdebug("ERROR: Could not open /dev/sequencer\n"); 00145 return; 00146 } 00147 00148 #ifdef HAVE_OSS_SUPPORT 00149 00150 //seqbuf_clean(); 00151 //ioctl(seqfd,SNDCTL_SEQ_RESET); 00152 //ioctl(seqfd,SNDCTL_SEQ_PANIC); 00153 00154 if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &device)==-1) 00155 { 00156 printfdebug("Error reseting gus samples. Please report\n"); 00157 }; 00158 use8bit=0; 00159 totalmemory = device; 00160 ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &totalmemory); 00161 freememory = device; 00162 ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &freememory); 00163 00164 #endif 00165 00166 00167 } 00168 00169 void GUSOut::closeDev (void) 00170 { 00171 if (!ok()) return; 00172 vm->clearLists(); 00173 //if (seqfd>=0) 00174 // close(seqfd); 00175 seqfd=-1; 00176 } 00177 00178 void GUSOut::initDev (void) 00179 { 00180 #ifdef HAVE_OSS_SUPPORT 00181 int chn; 00182 if (!ok()) return; 00183 uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7}; 00184 sysex(gm_reset, sizeof(gm_reset)); 00185 for (chn=0;chn<16;chn++) 00186 { 00187 chnmute[chn]=0; 00188 chnPatchChange(chn,0); 00189 // chnPressure(chn,127); 00190 chnPitchBender(chn, 0x00, 0x40); 00191 chnController(chn, CTL_MAIN_VOLUME,127); 00192 chnController(chn, CTL_EXT_EFF_DEPTH, 0); 00193 chnController(chn, CTL_CHORUS_DEPTH, 0); 00194 chnController(chn, 0x4a, 127); 00195 } 00196 00197 00198 for (int i = 0; i < nvoices; i++) 00199 { 00200 SEQ_CONTROL(device, i, SEQ_VOLMODE, VOL_METHOD_LINEAR); 00201 SEQ_STOP_NOTE(device, i, vm->note(i), 64); 00202 } 00203 00204 #endif 00205 } 00206 00207 00208 int GUSOut::patch(int p) 00209 { 00210 if (patchloaded[p]==1) return p; 00211 printfdebug("Not loaded %d!\n",p); 00212 p=0; 00213 while ((p<256)&&(patchloaded[p]==0)) p++; 00214 return p; 00215 } 00216 00217 void GUSOut::noteOn (uchar chn, uchar note, uchar vel) 00218 { 00219 if (vel==0) 00220 { 00221 noteOff(chn,note,vel); 00222 } 00223 else 00224 { 00225 if (chn==PERCUSSION_CHANNEL) 00226 { 00227 if (patchloaded[note+128]==0) return; 00228 else 00229 if (patchloaded[chnpatch[chn]]==0) return; 00230 }; 00231 int v=vm->allocateVoice(chn,note); 00232 int p; 00233 if (chn==PERCUSSION_CHANNEL) 00234 SEQ_SET_PATCH(device,v ,p=patch(note+128)) 00235 else 00236 SEQ_SET_PATCH(device,v ,p=map->patch(chn,chnpatch[chn])); 00237 SEQ_BENDER(device, v, chnbender[chn]); 00238 00239 SEQ_START_NOTE(device, v, note, vel); 00240 // SEQ_CONTROL(device, v, CTL_MAIN_VOLUME, chncontroller[chn][CTL_MAIN_VOLUME]); 00241 SEQ_CHN_PRESSURE(device, v , chnpressure[chn]); 00242 } 00243 00244 printfdebug("Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel); 00245 } 00246 00247 void GUSOut::noteOff (uchar chn, uchar note, uchar vel) 00248 { 00249 int i; 00250 vm->initSearch(); 00251 while ((i=vm->search(chn,note))!=-1) 00252 { 00253 SEQ_STOP_NOTE(device, i, note, vel); 00254 vm->deallocateVoice(i); 00255 } 00256 00257 #ifdef GUSOUTDEBUG 00258 printf("Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel); 00259 #endif 00260 } 00261 00262 void GUSOut::keyPressure (uchar chn, uchar note, uchar vel) 00263 { 00264 int i; 00265 vm->initSearch(); 00266 while ((i=vm->search(chn,note))!=-1) 00267 SEQ_KEY_PRESSURE(device, i, note,vel); 00268 } 00269 00270 void GUSOut::chnPatchChange (uchar chn, uchar patch) 00271 { 00272 if (chn==PERCUSSION_CHANNEL) return; 00273 int i; 00274 vm->initSearch(); 00275 while ((i=vm->search(chn))!=-1) 00276 SEQ_SET_PATCH(device,i,map->patch(chn,patch)); 00277 chnpatch[chn]=patch; 00278 00279 } 00280 00281 void GUSOut::chnPressure (uchar /*chn*/, uchar /*vel*/) 00282 { 00283 /* int i; 00284 vm->initSearch(); 00285 while ((i=vm->search(chn))!=-1) 00286 SEQ_CHN_PRESSURE(device, i , vel); 00287 chnpressure[chn]=vel; 00288 */ 00289 } 00290 00291 void GUSOut::chnPitchBender(uchar chn,uchar lsb, uchar msb) 00292 { 00293 chnbender[chn]=((int)msb<<7) | (lsb & 0x7F); 00294 00295 int i; 00296 vm->initSearch(); 00297 while ((i=vm->search(chn))!=-1) 00298 SEQ_BENDER(device, i, chnbender[chn]); 00299 } 00300 00301 void GUSOut::chnController (uchar chn, uchar ctl, uchar v) 00302 { 00303 if ((ctl==11)||(ctl==7)) 00304 { 00305 v=(v*volumepercentage)/100; 00306 if (v>127) v=127; 00307 }; 00308 00309 int i; 00310 vm->initSearch(); 00311 while ((i=vm->search(chn))!=-1) 00312 SEQ_CONTROL(device, i, ctl, v); 00313 00314 chncontroller[chn][ctl]=v; 00315 } 00316 00317 void GUSOut::sysex(uchar *, ulong ) 00318 { 00319 00320 } 00321 00322 void GUSOut::setGUSPatchesDirectory(const char *dir) 00323 { 00324 if ((dir==NULL)||(dir[0]==0)) return; 00325 if (delete_GUS_patches_directory) 00326 free((char *)GUS_patches_directory); 00327 00328 GUS_patches_directory = strdup(dir); 00329 delete_GUS_patches_directory=1; 00330 } 00331 00332 const char *GUSOut::patchName(int pgm) 00333 { 00334 return GUS_voice_names[pgm]; 00335 } 00336 00337 00338 int GUSOut::loadPatch(int pgm) 00339 { 00340 #ifdef HAVE_OSS_SUPPORT 00341 struct pat_header header; 00342 struct sample_header sample; 00343 if (patchloaded[pgm]==1) 00344 { 00345 #ifdef GUSOUTDEBUG 00346 printf("Trying to reload a patch. This should never happen, please report.\n"); 00347 #endif 00348 return 0; 00349 } 00350 if ((patchName(pgm)==NULL)||((patchName(pgm))[0]==0)) 00351 { 00352 #ifdef GUSOUTDEBUG 00353 printf("Couldn't guess patch name for patch number %d\n",pgm); 00354 #endif 00355 return -1; 00356 } 00357 char *s=new char[strlen(GUS_patches_directory)+strlen(patchName(pgm))+10]; 00358 if (s==NULL) return -1; 00359 sprintf(s,"%s/%s.pat",GUS_patches_directory,patchName(pgm)); 00360 #ifdef GUSOUTDEBUG 00361 printf("Loading patch : %s\n",s); 00362 #endif 00363 struct patch_info *patch=NULL; 00364 struct stat info; 00365 if (stat(s, &info)==-1) 00366 { 00367 #ifdef GUSOUTDEBUG 00368 printf("File %s doesn't exist\n",s); 00369 #endif 00370 return -1; 00371 } 00372 00373 FILE *fh=fopen(s,"rb"); 00374 if (fh==NULL) 00375 { 00376 #ifdef GUSOUTDEBUG 00377 printf("Couldn't open patch %s\n",s); 00378 #endif 00379 return -1; 00380 } 00381 00382 unsigned char tmp[256]; 00383 if (fread(tmp,1,0xef,fh)!=0xef) 00384 { 00385 fclose(fh); 00386 #ifdef GUSOUTDEBUG 00387 printf("Short file ! \n"); 00388 #endif 00389 return -1; 00390 } 00391 memcpy ((char *) &header, tmp, sizeof (header)); 00392 00393 if (strncmp(header.magic,"GF1PATCH110",12)!=0) 00394 { 00395 #ifdef GUSOUTDEBUG 00396 printf("File %s is corrupted or it isn't a patch file\n",s); 00397 #endif 00398 return -1; 00399 } 00400 if (strncmp(header.version,"ID#000002",10)!=0) 00401 { 00402 #ifdef GUSOUTDEBUG 00403 printf("File %s's version is not supported\n",s); 00404 #endif 00405 return -1; 00406 } 00407 unsigned short nWaves= *(unsigned short *)&tmp[85]; 00408 #ifdef GUSOUTDEBUG 00409 unsigned short masterVolume= *(unsigned short *)&tmp[87]; 00410 printf("nWaves: %d\n",nWaves); 00411 printf("masterVolume : %d\n",masterVolume); 00412 #endif 00413 00414 unsigned short i; 00415 int offset=0xef; 00416 for (i=0;i<nWaves;i++) 00417 { 00418 fseek(fh,offset,SEEK_SET); 00419 00420 if (fread(tmp,1,sizeof(sample),fh) != sizeof(sample)) 00421 { 00422 fclose(fh); 00423 #ifdef GUSOUTDEBUG 00424 printf("Short file\n"); 00425 #endif 00426 return -1; 00427 } 00428 memcpy ((char *) &sample, tmp, sizeof (sample)); 00429 sample.fractions = (char)tmp[7]; 00430 sample.len = get_dint(&tmp[8]); 00431 sample.loop_start = get_dint(&tmp[12]); 00432 sample.loop_end = get_dint(&tmp[16]); 00433 sample.base_freq = get_word(&tmp[20]); 00434 sample.low_note = get_dint(&tmp[22]); 00435 sample.high_note = get_dint(&tmp[26]); 00436 sample.base_note = get_dint(&tmp[30]); 00437 sample.detune = (short)get_word(&tmp[34]); 00438 sample.panning = (unsigned char) tmp[36]; 00439 00440 memcpy (sample.envelope_rate, &tmp[37], 6); 00441 memcpy (sample.envelope_offset, &tmp[43], 6); 00442 00443 sample.tremolo_sweep = (unsigned char) tmp[49]; 00444 sample.tremolo_rate = (unsigned char) tmp[50]; 00445 sample.tremolo_depth = (unsigned char) tmp[51]; 00446 00447 sample.vibrato_sweep = (unsigned char) tmp[52]; 00448 sample.vibrato_rate = (unsigned char) tmp[53]; 00449 sample.vibrato_depth = (unsigned char) tmp[54]; 00450 sample.modes = (unsigned char) tmp[55]; 00451 sample.scale_frequency = (short)get_word(&tmp[56]); 00452 sample.scale_factor = get_word(&tmp[58]); 00453 00454 offset = offset + 96; 00455 00456 patch = (struct patch_info *) malloc(sizeof (*patch) + sample.len); 00457 if (patch == NULL) 00458 { 00459 #ifdef GUSOUTDEBUG 00460 printf("Not enough memory\n"); 00461 #endif 00462 return -1; 00463 } 00464 patch->key = GUS_PATCH; 00465 patch->device_no = device; 00466 patch->instr_no = pgm; 00467 patch->mode = sample.modes | WAVE_TREMOLO | WAVE_VIBRATO | WAVE_SCALE; 00468 patch->len = sample.len; 00469 patch->loop_start = sample.loop_start; 00470 patch->loop_end = sample.loop_end; 00471 patch->base_note = sample.base_note; 00472 patch->high_note = sample.high_note; 00473 patch->low_note = sample.low_note; 00474 patch->base_freq = sample.base_freq; 00475 patch->detuning = sample.detune; 00476 patch->panning = (sample.panning - 7) * 16; 00477 00478 memcpy (patch->env_rate, sample.envelope_rate, 6); 00479 memcpy (patch->env_offset, sample.envelope_offset, 6); 00480 00481 patch->tremolo_sweep = sample.tremolo_sweep; 00482 patch->tremolo_rate = sample.tremolo_rate; 00483 patch->tremolo_depth = sample.tremolo_depth; 00484 00485 patch->vibrato_sweep = sample.vibrato_sweep; 00486 patch->vibrato_rate = sample.vibrato_rate; 00487 patch->vibrato_depth = sample.vibrato_depth; 00488 00489 patch->scale_frequency = sample.scale_frequency; 00490 patch->scale_factor = sample.scale_factor; 00491 00492 patch->volume = header.master_volume; 00493 00494 if (fseek (fh, offset, 0) == -1) 00495 { 00496 fclose(fh); 00497 return -1; 00498 } 00499 00500 if ((long)fread (patch->data, 1,sample.len,fh) != sample.len) 00501 { 00502 #ifdef GUSOUTDEBUG 00503 printf ("Short file\n"); 00504 #endif 00505 return -1; 00506 } 00507 00508 SEQ_WRPATCH (patch, sizeof (*patch) + sample.len); 00509 00510 offset = offset + sample.len; 00511 00512 } 00513 patchloaded[pgm]=1; 00514 00515 fclose(fh); 00516 free(patch); // Shouldn't this 'free' be within the 'for' loop ? 00517 delete s; 00518 freememory = device; 00519 ioctl(seqfd, SNDCTL_SYNTH_MEMAVL, &freememory); 00520 #endif 00521 return 0; 00522 } 00523 00524 00525 void GUSOut::setPatchesToUse(int *patchesused) 00526 { 00527 #ifdef HAVE_OSS_SUPPORT 00528 int k; 00529 for (k=0;k<256;k++) patchloaded[k]=0; 00530 00531 int patchesordered[256]; //This holds the pgm used ordered by a method which 00532 // put first the patches more oftenly used, and then the least 00533 // In example, if a song only uses a piano and a splash cymbal, 00534 // This is set to : 0,188,-1,-1,-1,-1 ... 00535 patchesLoadingOrder(patchesused,patchesordered); 00536 00537 // If above line doesn't work, perhaps you could try this ? : 00538 // for (int j=0;j<256;j++) patchesordered[j]=patchesused[j]; 00539 #ifdef GUSOUTDEBUG 00540 printf("Patches used : \n"); 00541 for (k=0;k<256;k++) 00542 { 00543 if (patchesused[k]!=-1) printf("%d,",patchesused[k]); 00544 } 00545 printf("\n Patches used, sorted :\n"); 00546 for (k=0;k<256;k++) 00547 { 00548 if (patchesordered[k]!=-1) printf("%d,",patchesordered[k]); 00549 } 00550 #endif 00551 00552 int i=0; 00553 while (patchesordered[i]!=-1) 00554 { 00555 #ifdef GUSOUTDEBUG 00556 printf("Load Patch : %d\n",patchesordered[i]); 00557 #endif 00558 loadPatch(patchesordered[i]); 00559 i++; 00560 } 00561 #endif 00562 } 00563 00564 int compare_decreasing(const void *a,const void *b) 00565 { 00566 struct instr_gm 00567 { 00568 int used; 00569 int pgm; 00570 }; 00571 instr_gm *ai=(instr_gm *)a; 00572 instr_gm *bi=(instr_gm *)b; 00573 return ai->used<bi->used; 00574 } 00575 00576 00577 void GUSOut::patchesLoadingOrder(int *patchesused,int *patchesordered) 00578 { 00579 struct instr_gm 00580 { 00581 int used; 00582 int pgm; 00583 }; 00584 00585 instr_gm tempmelody[128]; 00586 instr_gm tempdrums[128]; 00587 int i,j; 00588 for (i=0,j=128;i<128;i++,j++) 00589 { 00590 tempmelody[i].used=patchesused[i]; 00591 tempmelody[i].pgm=i; 00592 tempdrums[i].used=patchesused[j]; 00593 tempdrums[i].pgm=j; 00594 } 00595 /* SORT */ // Decreasing order (first most used patch, then less used patch) 00596 qsort(&tempmelody[0],128,sizeof(instr_gm),compare_decreasing); 00597 qsort(&tempdrums[0],128,sizeof(instr_gm),compare_decreasing); 00598 00599 /* Once they are sorted, the result is put on patchesordered in the following 00600 * way : If tempmelody is : M0 M1 M2 M3 ... M127 and tempdrums is : 00601 * D0 D1 D2 D3 ... D127, the result is : 00602 * M0 D0 M1 M2 D1 M3 M4 D2 M5 M6 D3 ... 00603 * P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 P10 ... 00604 */ 00605 00606 #ifdef GUSOUTDEBUG 00607 for (int k=0;k<128;k++) 00608 { 00609 printf("%d - %d\n",tempmelody[k].used,tempmelody[k].pgm); 00610 } 00611 for (int k=0;k<128;k++) 00612 { 00613 printf("%d : %d\n",tempdrums[k].used,tempdrums[k].pgm); 00614 } 00615 #endif 00616 00617 i=0; 00618 int totalmelody=0; 00619 while ((i<128)&&(tempmelody[i].used!=0)) 00620 { 00621 totalmelody++; 00622 i++; 00623 } 00624 i=0; 00625 int totaldrums=0; 00626 while ((i<128)&&(tempdrums[i].used!=0)) 00627 { 00628 totaldrums++; 00629 i++; 00630 } 00631 #ifdef GUSOUTDEBUG 00632 printf("Totalmelody : %d,totaldrums : %d\n",totalmelody,totaldrums); 00633 #endif 00634 int tgt=0; 00635 00636 int tm=totalmelody; 00637 int td=totaldrums; 00638 int cm,cd; 00639 cm=cd=0; 00640 if ((tm!=0)&&(td!=0)) 00641 { 00642 patchesordered[0]=tempmelody[0].pgm; 00643 patchesordered[1]=tempdrums[0].pgm; 00644 tm--;td--; 00645 cm++;cd++; 00646 tgt+=2; 00647 while ((tm>0)&&(td>0)) 00648 { 00649 if (((tgt-1)%3)==0) 00650 { 00651 patchesordered[tgt]=tempdrums[cd].pgm; 00652 cd++; 00653 td--; 00654 } 00655 else 00656 { 00657 patchesordered[tgt]=tempmelody[cm].pgm; 00658 cm++; 00659 tm--; 00660 } 00661 tgt++; 00662 } 00663 } 00664 while (tm>0) 00665 { 00666 patchesordered[tgt]=tempmelody[cm].pgm; 00667 tgt++; 00668 cm++; 00669 tm--; 00670 } 00671 while (td>0) 00672 { 00673 patchesordered[tgt]=tempdrums[cd].pgm; 00674 tgt++; 00675 cd++; 00676 td--; 00677 } 00678 00679 // Now we put as not used (-1) the rest of the array 00680 while (tgt<256) 00681 { 00682 patchesordered[tgt]=-1; 00683 tgt++; 00684 } 00685 } 00686 00687 //char *GUSOut::GUS_patches_directory="/mnt/dosc/gravis/patches"; 00688 const char *GUSOut::GUS_patches_directory="/usr/share/ultrasnd"; 00689 00690 int GUSOut::delete_GUS_patches_directory = 0; 00691 /* No, this doesn't delete any file :-) it's just for internal use */