libkmid Library API Documentation

alsaout.cc

00001 /**************************************************************************
00002 
00003     alsaout.cc   - class AlsaOut which represents an alsa client/port pair
00004     This file is part of LibKMid 0.9.5
00005     Copyright (C) 2000  Antonio Larrosa Jimenez
00006     LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021     Boston, MA 02111-1307, USA.
00022 
00023     Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
00024 
00025 ***************************************************************************/
00026 #include "alsaout.h"
00027 #include <unistd.h>
00028 #include <fcntl.h>
00029 #include <stdio.h>
00030 #include "sndcard.h"
00031 #include <errno.h>
00032 #include <string.h>
00033 #include <stdlib.h>
00034 #include <sys/param.h>
00035 #include "midispec.h"
00036 
00037 
00038 #ifdef HAVE_CONFIG_H
00039 #include <config.h>
00040 #endif
00041 
00042 #ifdef HAVE_ALSA_ASOUNDLIB_H
00043 #   include <alsa/asoundlib.h>
00044 #elif defined(HAVE_SYS_ASOUNDLIB_H)
00045 #   include <sys/asoundlib.h>
00046 #endif
00047 
00048 #ifdef HAVE_LIBASOUND2
00049 #   define HAVE_ALSA_SEQ 1
00050 #   define snd_seq_flush_output(x)  snd_seq_drain_output(x)
00051 #elif defined(HAVE_LIBASOUND)
00052 #   define HAVE_ALSA_SEQ 1
00053 #   include <linux/asequencer.h>
00054 #endif
00055 
00056 
00057 SEQ_USE_EXTBUF();
00058 
00059 class AlsaOut::AlsaOutPrivate
00060 {
00061 public:
00062 #ifdef HAVE_ALSA_SEQ
00063   AlsaOutPrivate(int _client, int _port, const char *cname,const char *pname)
00064     {
00065       handle=0L;
00066       src=tgt=0L;
00067       queue=0;
00068       tPCN=1;
00069       tgtclient=_client;
00070       tgtport=_port;
00071       tgtname=new char[strlen(cname)+strlen(pname)+3];
00072       strcpy(tgtname, cname);
00073       strcat(tgtname, "  ");
00074       strcat(tgtname, pname);
00075       ev=new snd_seq_event_t;
00076       timerStarted=false;
00077     }
00078 #else
00079   AlsaOutPrivate(int, int, const char *,const char *)
00080     {
00081     }
00082 #endif
00083 
00084   ~AlsaOutPrivate()
00085     {
00086 #ifdef HAVE_ALSA_SEQ
00087       delete ev;
00088       delete tgtname;
00089 #endif
00090     }
00091 
00092 #ifdef HAVE_ALSA_SEQ
00093   snd_seq_t *handle;
00094   int  client;
00095   int  queue;
00096   snd_seq_addr_t *src;
00097   snd_seq_addr_t *tgt;
00098 
00099   snd_seq_event_t *ev;
00100   int tPCN;
00101 
00102   int tgtclient;
00103   int tgtport;
00104   char *tgtname;
00105 
00106   bool timerStarted;
00107 
00108 #endif
00109 };
00110 
00111 AlsaOut::AlsaOut(int d,int _client, int _port, const char *cname,const char *pname) : MidiOut (d)
00112 {
00113   di = new AlsaOutPrivate( _client, _port, cname, pname);
00114   seqfd = 0;
00115   devicetype=KMID_ALSA;
00116   device= d;
00117 
00118   volumepercentage=100;
00119 #ifdef HAVE_ALSA_SEQ
00120 //  printf("%d %d %d (%s)\n",device, di->tgtclient, di->tgtport, di->tgtname);
00121 #endif
00122 
00123   _ok=1;
00124 }
00125 
00126 AlsaOut::~AlsaOut()
00127 {
00128   closeDev();
00129   delete di;
00130 }
00131 
00132 void AlsaOut::openDev (int)
00133 {
00134 #ifndef HAVE_ALSA_SEQ
00135   return;
00136 #else
00137   _ok=1;
00138 #ifdef HAVE_LIBASOUND2
00139   if (snd_seq_open(&di->handle, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0)
00140                 fprintf(stderr, "Couldn't open sequencer: %s", snd_strerror(errno));
00141 #else
00142   if (snd_seq_open(&di->handle, SND_SEQ_OPEN) < 0)
00143                 fprintf(stderr, "Couldn't open sequencer: %s", snd_strerror(errno));
00144 #endif
00145 
00146   di->queue = snd_seq_alloc_queue(di->handle);
00147   if (di->queue < 0) {fprintf(stderr, "Couldn't allocate queue"); return; };
00148   di->client = snd_seq_client_id(di->handle);
00149   if (di->client < 0) {fprintf(stderr, "Couldn't get client id"); return; };
00150   di->tgt = new snd_seq_addr_t;
00151   di->tgt->client=di->tgtclient;
00152   di->tgt->port=di->tgtport;
00153 
00154   di->src = new snd_seq_addr_t;
00155   di->src->client = di->client;
00156   int port = snd_seq_create_simple_port(di->handle, NULL,
00157     SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE
00158     | SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
00159   if ( port < 0 )
00160   {
00161     delete di->src;
00162     delete di->tgt;
00163     di->src=0;
00164     di->tgt=0;
00165     _ok=0;
00166     time=0;
00167     snd_seq_free_queue(di->handle, di->queue);
00168     snd_seq_close(di->handle);
00169     fprintf(stderr, "Cannot connect to %d:%d\n",di->tgtclient,di->tgtport);
00170     return;
00171   }
00172   di->src->port = port;
00173   
00174 
00175   int r=snd_seq_connect_to(di->handle, di->src->port, di->tgt->client, di->tgt->port);
00176   if (r < 0) { _ok=0; fprintf(stderr, "Cannot connect to %d:%d\n",di->tgtclient,di->tgtport); }
00177   time=0;
00178 #endif
00179 }
00180 
00181 void AlsaOut::closeDev (void)
00182 {
00183   if (!ok()) return;
00184 #ifdef HAVE_ALSA_SEQ
00185   if (di->handle)
00186   {
00187     if (di->src) 
00188     {
00189        snd_seq_delete_simple_port(di->handle,di->src->port);
00190        delete di->src;
00191     }
00192     if (di->tgt) delete di->tgt;
00193     if (di->queue) 
00194     {
00195       snd_seq_free_queue(di->handle, di->queue);
00196       snd_seq_close(di->handle);
00197     }
00198     di->handle=0;
00199   }
00200 
00201 #endif
00202 }
00203 
00204 void AlsaOut::initDev (void)
00205 {
00206 #ifdef HAVE_ALSA_SEQ
00207   int chn;
00208   if (!ok()) return;
00209   uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
00210   sysex(gm_reset, sizeof(gm_reset));
00211   for (chn=0;chn<16;chn++)
00212   {
00213     chnmute[chn]=0;
00214     if (chn!=9) chnPatchChange(chn,0);
00215     chnPressure(chn,64);
00216     chnPitchBender(chn, 0x00, 0x40);
00217     chnController(chn, CTL_MAIN_VOLUME,110*volumepercentage);
00218     chnController(chn, CTL_EXT_EFF_DEPTH, 0);
00219     chnController(chn, CTL_CHORUS_DEPTH, 0);
00220     chnController(chn, 0x4a, 127);
00221   }
00222 #endif
00223 }
00224 
00225 #ifdef HAVE_ALSA_SEQ
00226 void AlsaOut::eventInit(snd_seq_event_t *ev)
00227 {
00228   snd_seq_ev_clear(ev);
00229   snd_seq_real_time_t tmp;
00230   tmp.tv_sec=(time)/1000;
00231   tmp.tv_nsec=(time%1000)*1000000;
00232 //  printf("time : %d %d %d\n",(int)time,(int)tmp.tv_sec, (int)tmp.tv_nsec);
00233   if (!di->src) fprintf(stderr,"AlsaOut::eventInit : no source\n");
00234   ev->source = *di->src;
00235   if (!di->tgt) fprintf(stderr,"AlsaOut::eventInit : no target\n");
00236   ev->dest = *di->tgt;
00237 
00238   snd_seq_ev_schedule_real(ev, di->queue, 0, &tmp);
00239 
00240 }
00241 
00242 void AlsaOut::eventSend(snd_seq_event_t *ev)
00243 {
00244     /*int err = */ snd_seq_event_output(di->handle, ev);
00245 /*        if (err < 0)
00246                 return;
00247 */
00248 //#ifndef SND_SEQ_IOCTL_GET_CLIENT_POOL
00249         /*
00250          * If this is not defined then block mode writes will not be
00251          * working correctly.  Therefore loop until all events are flushed
00252          * out.
00253          */
00254 /*        err = 0;
00255         do {
00256                 err = snd_seq_flush_output(di->handle);
00257                 if (err > 0)
00258                         usleep(2000);
00259         } while (err > 0);
00260 
00261 #endif
00262 
00263         return ;
00264 */
00265 }
00266 
00267 void AlsaOut::timerEventSend(int type)
00268 {
00269   snd_seq_event_t ev;
00270 
00271   ev.queue = di->queue;
00272   ev.dest.client = SND_SEQ_CLIENT_SYSTEM;
00273   ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
00274 
00275   ev.data.queue.queue = di->queue;
00276 
00277   ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_REL;
00278   ev.time.time.tv_sec = 0;
00279   ev.time.time.tv_nsec = 0;
00280 
00281   ev.type = type;
00282 
00283   snd_seq_event_output(di->handle, &ev);
00284   snd_seq_flush_output(di->handle);
00285 
00286 }
00287 
00288 #endif // HAVE_ALSA_SEQ
00289 
00290 #ifndef HAVE_ALSA_SEQ
00291 void AlsaOut::noteOn  (uchar , uchar , uchar )
00292 {
00293 #else
00294 void AlsaOut::noteOn  (uchar chn, uchar note, uchar vel)
00295 {
00296   if (vel==0)
00297   {
00298     noteOff(chn,note,vel);
00299   }
00300   else
00301   {
00302     eventInit(di->ev);
00303     snd_seq_ev_set_noteon(di->ev,map->channel(chn), map->key(chn,chnpatch[chn],note), vel);
00304     eventSend(di->ev);
00305   }
00306 #endif
00307 #ifdef MIDIOUTDEBUG
00308   printfdebug("Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
00309 #endif
00310 }
00311 
00312 #ifndef HAVE_ALSA_SEQ
00313 void AlsaOut::noteOff (uchar , uchar , uchar )
00314 {
00315 #else
00316 void AlsaOut::noteOff (uchar chn, uchar note, uchar vel)
00317 {
00318   eventInit(di->ev);
00319   snd_seq_ev_set_noteoff(di->ev,map->channel(chn), map->key(chn,chnpatch[chn],note), vel);
00320   eventSend(di->ev);
00321 #endif
00322 #ifdef MIDIOUTDEBUG
00323   printfdebug("Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
00324 #endif
00325 }
00326 
00327 #ifndef HAVE_ALSA_SEQ
00328 void AlsaOut::keyPressure (uchar , uchar , uchar )
00329 {
00330 #else
00331 void AlsaOut::keyPressure (uchar chn, uchar note, uchar vel)
00332 {
00333   eventInit(di->ev);
00334   snd_seq_ev_set_keypress(di->ev,map->channel(chn), map->key(chn,chnpatch[chn],note), vel);
00335   eventSend(di->ev);
00336 #endif
00337 }
00338 
00339 #ifndef HAVE_ALSA_SEQ
00340 void AlsaOut::chnPatchChange (uchar , uchar )
00341 {
00342 #else
00343 void AlsaOut::chnPatchChange (uchar chn, uchar patch)
00344 {
00345 #ifdef MIDIOUTDEBUG
00346   printfdebug("PATCHCHANGE [%d->%d] %d -> %d\n",
00347       chn,map->channel(chn),patch,map->patch(chn,patch));
00348 #endif
00349   eventInit(di->ev);
00350   snd_seq_ev_set_pgmchange(di->ev,map->channel(chn), map->patch(chn,patch));
00351   eventSend(di->ev);
00352   chnpatch[chn]=patch;
00353 #endif
00354 }
00355 
00356 #ifndef HAVE_ALSA_SEQ
00357 void AlsaOut::chnPressure (uchar , uchar )
00358 {
00359 #else
00360 void AlsaOut::chnPressure (uchar chn, uchar vel)
00361 {
00362   eventInit(di->ev);
00363   snd_seq_ev_set_chanpress(di->ev,map->channel(chn), vel);
00364   eventSend(di->ev);
00365 
00366   chnpressure[chn]=vel;
00367 #endif
00368 }
00369 
00370 #ifndef HAVE_ALSA_SEQ
00371 void AlsaOut::chnPitchBender(uchar ,uchar , uchar )
00372 {
00373 #else
00374 void AlsaOut::chnPitchBender(uchar chn,uchar lsb, uchar msb)
00375 {
00376   map->pitchBender(chn,lsb,msb);
00377   chnbender[chn]=((short)msb<<7) | (lsb & 0x7F);
00378   chnbender[chn]=chnbender[chn]-0x2000;
00379 
00380   eventInit(di->ev);
00381   snd_seq_ev_set_pitchbend(di->ev,map->channel(chn), chnbender[chn]);
00382   eventSend(di->ev);
00383 #endif
00384 }
00385 
00386 #ifndef HAVE_ALSA_SEQ
00387 void AlsaOut::chnController (uchar , uchar , uchar )
00388 {
00389 #else
00390 void AlsaOut::chnController (uchar chn, uchar ctl, uchar v)
00391 {
00392   map->controller(chn,ctl,v);
00393   if ((ctl==11)||(ctl==7))
00394   {
00395     v=(v*volumepercentage)/100;
00396     if (v>127) v=127;
00397   }
00398 
00399   eventInit(di->ev);
00400   snd_seq_ev_set_controller(di->ev,map->channel(chn), ctl, v);
00401   eventSend(di->ev);
00402 
00403   chncontroller[chn][ctl]=v;
00404 #endif
00405 }
00406 
00407 #ifndef HAVE_ALSA_SEQ
00408 void AlsaOut::sysex(uchar *, ulong )
00409 {
00410 #else
00411 void AlsaOut::sysex(uchar *data, ulong size)
00412 {
00413   eventInit(di->ev);
00414   snd_seq_ev_set_sysex(di->ev, size, data);
00415   eventSend(di->ev);
00416 #endif
00417 
00418 #ifdef MIDIOUTDEBUG
00419   printfdebug("sysex\n");
00420 #endif
00421 }
00422 
00423 #ifndef HAVE_ALSA_SEQ
00424 void AlsaOut::channelSilence (uchar )
00425 {
00426 #else
00427 void AlsaOut::channelSilence (uchar chn)
00428 {
00429   uchar i;
00430   for ( i=0; i<127; i++)
00431   {
00432     noteOff(chn,i,0);
00433   }
00434 #endif
00435 }
00436 
00437 #ifndef HAVE_ALSA_SEQ
00438 void AlsaOut::channelMute(uchar , int )
00439 {
00440 #else
00441 void AlsaOut::channelMute(uchar chn, int a)
00442 {
00443   if (a==1)
00444   {
00445     chnmute[chn]=a;
00446     channelSilence(chn);
00447   }
00448   else if (a==0)
00449   {
00450     chnmute[chn]=a;
00451   }
00452   /*  else ignore the call to this procedure */
00453 #endif
00454 }
00455 
00456 void AlsaOut::seqbuf_dump (void)
00457 {
00458   printf("You shouldn't be here.\n");
00459 }
00460 
00461 void AlsaOut::seqbuf_clean(void)
00462 {
00463   printf("You shouldn't be here neither.\n");
00464 }
00465 
00466 void AlsaOut::wait(double ticks)
00467 {
00468 //  SEQ_WAIT_TIME(((int)(ticks/convertrate)));
00469   time=(long int)ticks;
00470 
00471 #ifdef MIDIOUTDEBUG
00472   printfdebug("Wait  >\t ticks: %g\n",ticks);
00473 #endif
00474 }
00475 
00476 #ifndef HAVE_ALSA_SEQ
00477 void AlsaOut::tmrSetTempo(int )
00478 {
00479 #else
00480 void AlsaOut::tmrSetTempo(int v)
00481 {
00482   eventInit(di->ev);
00483   di->ev->type = SND_SEQ_EVENT_TEMPO;
00484   snd_seq_ev_set_direct(di->ev);
00485   di->ev->data.queue.queue = di->queue;
00486   di->ev->data.queue.param.value = v;
00487   di->ev->dest.client = SND_SEQ_CLIENT_SYSTEM;
00488   di->ev->dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
00489   snd_seq_event_output_direct(di->handle, di->ev);
00490 #ifdef MIDIOUTDEBUG
00491   printfdebug("SETTEMPO  >\t tempo: %d\n",v);
00492 #endif
00493 #endif
00494 }
00495 
00496 #ifndef HAVE_ALSA_SEQ
00497 void AlsaOut::sync(int )
00498 {
00499 #else
00500 void AlsaOut::sync(int i)
00501 {
00502   if (i==1)
00503   {
00504     snd_seq_flush_output(di->handle);
00505   }
00506 
00507   if (di->timerStarted) 
00508   {
00509     eventInit(di->ev);
00510     di->ev->dest = *di->src;
00511     eventSend(di->ev);
00512     snd_seq_flush_output(di->handle);
00513     snd_seq_event_input(di->handle,&di->ev);
00514   }
00515 
00516 #endif
00517 }
00518 
00519 #ifndef HAVE_ALSA_SEQ
00520 void AlsaOut::tmrStart(int )
00521 {
00522 #else
00523 void AlsaOut::tmrStart(int tpcn)
00524 {
00525   int  ret;
00526   di->timerStarted=true;
00527   di->tPCN=tpcn;
00528 
00529 #ifdef HAVE_LIBASOUND2
00530   snd_seq_queue_tempo_t *queuetempo;
00531   snd_seq_queue_tempo_alloca(&queuetempo);
00532   snd_seq_queue_tempo_set_ppq(queuetempo, tpcn);
00533   snd_seq_queue_tempo_set_tempo(queuetempo, 60*1000000/120);
00534   ret = snd_seq_set_queue_tempo(di->handle, di->queue, queuetempo);
00535 #else
00536   snd_seq_queue_tempo_t queuetempo;
00537   memset(&queuetempo, 0, sizeof(queuetempo));
00538   queuetempo.queue = di->queue;
00539   queuetempo.ppq = tpcn;
00540   queuetempo.tempo = 60*1000000/120;
00541   ret = snd_seq_set_queue_tempo(di->handle, di->queue, &queuetempo);
00542 #endif
00543 
00544   timerEventSend(SND_SEQ_EVENT_START);
00545   snd_seq_start_queue(di->handle,di->queue,NULL);
00546 #endif
00547 }
00548 
00549 void AlsaOut::tmrStop(void)
00550 {
00551 #ifdef HAVE_LIBASOUND
00552   di->timerStarted=false;
00553   timerEventSend(SND_SEQ_EVENT_STOP);
00554 #endif
00555 }
00556 
00557 void AlsaOut::tmrContinue(void)
00558 {
00559 }
00560 
00561 const char * AlsaOut::deviceName(void) const
00562 {
00563 #ifdef HAVE_LIBASOUND
00564   return di->tgtname;
00565 #else
00566   return 0L;
00567 #endif
00568 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:15:59 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001