34 #include <sys/param.h>
42 #ifdef HAVE_ALSA_ASOUNDLIB_H
43 # include <alsa/asoundlib.h>
44 #elif defined(HAVE_SYS_ASOUNDLIB_H)
45 # include <sys/asoundlib.h>
48 #ifdef HAVE_LIBASOUND2
49 # define HAVE_ALSA_SEQ 1
50 # define snd_seq_flush_output(x) snd_seq_drain_output(x)
51 #elif defined(HAVE_LIBASOUND)
52 # define HAVE_ALSA_SEQ 1
53 # include <linux/asequencer.h>
59 class AlsaOut::AlsaOutPrivate
63 AlsaOutPrivate(
int _client,
int _port,
const char *cname,
const char *pname)
71 tgtname=
new char[strlen(cname)+strlen(pname)+3];
72 strcpy(tgtname, cname);
74 strcat(tgtname, pname);
75 ev=
new snd_seq_event_t;
79 AlsaOutPrivate(
int,
int,
const char *,
const char *)
113 di =
new AlsaOutPrivate( _client, _port, cname, pname);
115 devicetype=KMID_ALSA;
118 volumepercentage=100;
134 #ifndef HAVE_ALSA_SEQ
138 #ifdef HAVE_LIBASOUND2
139 if (snd_seq_open(&di->handle,
"hw", SND_SEQ_OPEN_DUPLEX, 0) < 0)
140 fprintf(stderr,
"Couldn't open sequencer: %s", snd_strerror(errno));
142 if (snd_seq_open(&di->handle, SND_SEQ_OPEN) < 0)
143 fprintf(stderr,
"Couldn't open sequencer: %s", snd_strerror(errno));
146 di->queue = snd_seq_alloc_queue(di->handle);
147 if (di->queue < 0) {fprintf(stderr,
"Couldn't allocate queue");
return; };
148 di->client = snd_seq_client_id(di->handle);
149 if (di->client < 0) {fprintf(stderr,
"Couldn't get client id");
return; };
150 di->tgt =
new snd_seq_addr_t;
151 di->tgt->client=di->tgtclient;
152 di->tgt->port=di->tgtport;
154 di->src =
new snd_seq_addr_t;
155 di->src->client = di->client;
156 int port = snd_seq_create_simple_port(di->handle, NULL,
157 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE
158 | SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
167 snd_seq_free_queue(di->handle, di->queue);
168 snd_seq_close(di->handle);
169 fprintf(stderr,
"Cannot connect to %d:%d\n",di->tgtclient,di->tgtport);
172 di->src->port = port;
175 int r=snd_seq_connect_to(di->handle, di->src->port, di->tgt->client, di->tgt->port);
176 if (r < 0) { _ok=0; fprintf(stderr,
"Cannot connect to %d:%d\n",di->tgtclient,di->tgtport); }
189 snd_seq_delete_simple_port(di->handle,di->src->port);
200 snd_seq_free_queue(di->handle, di->queue);
201 snd_seq_close(di->handle);
214 uchar gm_reset[5]={0x7e, 0x7f, 0x09, 0x01, 0xf7};
215 sysex(gm_reset,
sizeof(gm_reset));
216 for (chn=0;chn<16;chn++)
231 void AlsaOut::eventInit(snd_seq_event_t *ev)
233 snd_seq_ev_clear(ev);
234 snd_seq_real_time_t tmp;
235 tmp.tv_sec=(time)/1000;
236 tmp.tv_nsec=(time%1000)*1000000;
238 if (!di->src) { fprintf(stderr,
"AlsaOut::eventInit : no source\n");
return; }
239 ev->source = *di->src;
240 if (!di->tgt) { fprintf(stderr,
"AlsaOut::eventInit : no target\n");
return; }
243 snd_seq_ev_schedule_real(ev, di->queue, 0, &tmp);
246 void AlsaOut::eventSend(snd_seq_event_t *ev)
248 snd_seq_event_output(di->handle, ev);
271 void AlsaOut::timerEventSend(
int type)
275 ev.queue = di->queue;
276 ev.dest.client = SND_SEQ_CLIENT_SYSTEM;
277 ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
279 ev.data.queue.queue = di->queue;
281 ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_REL;
282 ev.time.time.tv_sec = 0;
283 ev.time.time.tv_nsec = 0;
287 snd_seq_event_output(di->handle, &ev);
288 snd_seq_flush_output(di->handle);
293 #ifndef HAVE_ALSA_SEQ
306 snd_seq_ev_set_noteon(di->ev,map->
channel(chn), map->
key(chn,chnpatch[chn],note), vel);
311 printfdebug(
"Note ON >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
315 #ifndef HAVE_ALSA_SEQ
322 snd_seq_ev_set_noteoff(di->ev,map->
channel(chn), map->
key(chn,chnpatch[chn],note), vel);
326 printfdebug(
"Note OFF >\t chn : %d\tnote : %d\tvel: %d\n",chn,note,vel);
330 #ifndef HAVE_ALSA_SEQ
337 snd_seq_ev_set_keypress(di->ev,map->
channel(chn), map->
key(chn,chnpatch[chn],note), vel);
342 #ifndef HAVE_ALSA_SEQ
349 printfdebug(
"PATCHCHANGE [%d->%d] %d -> %d\n",
353 snd_seq_ev_set_pgmchange(di->ev,map->
channel(chn), map->
patch(chn,patch));
359 #ifndef HAVE_ALSA_SEQ
366 snd_seq_ev_set_chanpress(di->ev,map->
channel(chn), vel);
369 chnpressure[chn]=vel;
373 #ifndef HAVE_ALSA_SEQ
380 chnbender[chn]=((short)msb<<7) | (lsb & 0x7F);
381 chnbender[chn]=chnbender[chn]-0x2000;
384 snd_seq_ev_set_pitchbend(di->ev,map->
channel(chn), chnbender[chn]);
389 #ifndef HAVE_ALSA_SEQ
396 if ((ctl==11)||(ctl==7))
398 v=(v*volumepercentage)/100;
403 snd_seq_ev_set_controller(di->ev,map->
channel(chn), ctl, v);
406 chncontroller[chn][ctl]=v;
410 #ifndef HAVE_ALSA_SEQ
417 snd_seq_ev_set_sysex(di->ev, size, data);
422 printfdebug(
"sysex\n");
426 #ifndef HAVE_ALSA_SEQ
433 for ( i=0; i<127; i++)
440 #ifndef HAVE_ALSA_SEQ
459 void AlsaOut::seqbuf_dump (
void)
461 printf(
"You shouldn't be here.\n");
464 void AlsaOut::seqbuf_clean(
void)
466 printf(
"You shouldn't be here neither.\n");
469 void AlsaOut::wait(
double ticks)
472 time=
static_cast<long int>(ticks);
475 printfdebug(
"Wait >\t ticks: %g\n",ticks);
479 #ifndef HAVE_ALSA_SEQ
480 void AlsaOut::tmrSetTempo(
int )
483 void AlsaOut::tmrSetTempo(
int v)
486 di->ev->type = SND_SEQ_EVENT_TEMPO;
487 snd_seq_ev_set_direct(di->ev);
488 di->ev->data.queue.queue = di->queue;
489 di->ev->data.queue.param.value = v;
490 di->ev->dest.client = SND_SEQ_CLIENT_SYSTEM;
491 di->ev->dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
492 snd_seq_event_output_direct(di->handle, di->ev);
494 printfdebug(
"SETTEMPO >\t tempo: %d\n",v);
499 #ifndef HAVE_ALSA_SEQ
500 void AlsaOut::sync(
int )
503 void AlsaOut::sync(
int i)
507 snd_seq_flush_output(di->handle);
510 if (di->timerStarted && di->src)
513 di->ev->dest = *di->src;
515 snd_seq_flush_output(di->handle);
516 snd_seq_event_input(di->handle,&di->ev);
522 #ifndef HAVE_ALSA_SEQ
523 void AlsaOut::tmrStart(
int )
526 void AlsaOut::tmrStart(
int tpcn)
529 di->timerStarted=
true;
532 #ifdef HAVE_LIBASOUND2
533 snd_seq_queue_tempo_t *queuetempo;
534 snd_seq_queue_tempo_alloca(&queuetempo);
535 snd_seq_queue_tempo_set_ppq(queuetempo, tpcn);
536 snd_seq_queue_tempo_set_tempo(queuetempo, 60*1000000/120);
537 ret = snd_seq_set_queue_tempo(di->handle, di->queue, queuetempo);
539 snd_seq_queue_tempo_t queuetempo;
540 memset(&queuetempo, 0,
sizeof(queuetempo));
541 queuetempo.queue = di->queue;
542 queuetempo.ppq = tpcn;
543 queuetempo.tempo = 60*1000000/120;
544 ret = snd_seq_set_queue_tempo(di->handle, di->queue, &queuetempo);
547 timerEventSend(SND_SEQ_EVENT_START);
548 snd_seq_start_queue(di->handle,di->queue,NULL);
552 void AlsaOut::tmrStop(
void)
555 di->timerStarted=
false;
556 timerEventSend(SND_SEQ_EVENT_STOP);
560 void AlsaOut::tmrContinue(
void)
virtual void channelSilence(uchar chn)
Mutes all notes being played on a given channel.
virtual void chnPatchChange(uchar chn, uchar patch)
See DeviceManager::chnPatchChange()
AlsaOut(int d, int client=64, int port=0, const char *cname="", const char *pname="")
Constructor.
virtual const char * deviceName(void) const
Returns the name and type of this MIDI device.
virtual void channelMute(uchar chn, int b)
Mute or "unmute" a given channel .
virtual ~AlsaOut()
Destructor.
virtual void sysex(uchar *data, ulong size)
See DeviceManager::sysex()
virtual void chnPressure(uchar chn, uchar vel)
See DeviceManager::chnPressure()
virtual void closeDev()
Closes the device.
virtual void openDev(int sqfd)
Opens the device.
virtual void initDev()
Initializes the device sending generic standard midi events and controllers, such as changing the pat...
virtual void keyPressure(uchar chn, uchar note, uchar vel)
See DeviceManager::keyPressure()
virtual void chnController(uchar chn, uchar ctl, uchar v)
See DeviceManager::chnController()
int ok(void)
Returns true if everything's ok and false if there has been any problem.
virtual void noteOff(uchar chn, uchar note, uchar vel)
See DeviceManager::noteOff()
virtual void noteOn(uchar chn, uchar note, uchar vel)
See DeviceManager::noteOn()
virtual void chnPitchBender(uchar chn, uchar lsb, uchar msb)
See DeviceManager::chnPitchBender()
void controller(uchar chn, uchar &ctl, uchar &v)
Returns the value which a given controller and its value should be mapped to when played on channel c...
uchar channel(uchar chn)
Returns the channel which chn should be mapped to.
uchar patch(uchar chn, uchar pgm)
Returns the patch which pgm used on channel chn should be mapped to.
uchar key(uchar chn, uchar pgm, uchar note)
Returns the key that key note playing a pgm patch on channel chn should be mapped to.
void pitchBender(uchar chn, uchar &lsb, uchar &msb)
Returns the value which the pitch bender on channel chn should be mapped to.
External MIDI port output class .