| |
| #ifdef USE_PCM // XXX not yet |
| /* |
| * PCM timer layer |
| */ |
| |
| int pcard = 0; |
| int pdevice = 0; |
| int period_size = 1024; |
| |
| void set_hwparams(snd_pcm_t *phandle) |
| { |
| int err; |
| snd_pcm_hw_params_t *params; |
| |
| err = snd_output_stdio_attach(&log, stderr, 0); |
| if (err < 0) { |
| fprintf(stderr, "cannot attach output stdio\n"); |
| exit(0); |
| } |
| |
| snd_pcm_hw_params_alloca(¶ms); |
| err = snd_pcm_hw_params_any(phandle, params); |
| if (err < 0) { |
| fprintf(stderr, "Broken configuration for this PCM: no configurations available\n"); |
| exit(0); |
| } |
| |
| err = snd_pcm_hw_params_set_access(phandle, params, |
| SND_PCM_ACCESS_RW_INTERLEAVED); |
| if (err < 0) { |
| fprintf(stderr, "Access type not available\n"); |
| exit(0); |
| } |
| err = snd_pcm_hw_params_set_format(phandle, params, SND_PCM_FORMAT_S16_LE); |
| if (err < 0) { |
| fprintf(stderr, "cannot set format\n"); |
| exit(0); |
| } |
| err = snd_pcm_hw_params_set_channels(phandle, params, 2); |
| if (err < 0) { |
| fprintf(stderr, "cannot set channels 2\n"); |
| exit(0); |
| } |
| err = snd_pcm_hw_params_set_rate_near(phandle, params, 44100, 0); |
| if (err < 0) { |
| fprintf(stderr, "cannot set rate\n"); |
| exit(0); |
| } |
| err = snd_pcm_hw_params_set_period_size_near(phandle, params, period_size); |
| if (err < 0) { |
| fprintf(stderr, "cannot set period size\n"); |
| exit(0); |
| } |
| err = snd_pcm_hw_params(phandle, params); |
| if (err < 0) { |
| fprintf(stderr, "Unable to install hw params:\n"); |
| exit(0); |
| } |
| snd_pcm_hw_params_dump(params, log); |
| } |
| |
| #endif |
| /* |
| * Simple event sender |
| */ |
| |
| void event_sender_start_timer(snd_seq_t *handle, |
| int client ATTRIBUTE_UNUSED, |
| int queue, |
| snd_pcm_t *phandle ATTRIBUTE_UNUSED) |
| { |
| int err; |
| |
| #ifdef USE_PCM |
| if (phandle) { |
| snd_pcm_playback_info_t pinfo; |
| snd_seq_queue_timer_t qtimer; |
| |
| if ((err = snd_pcm_playback_info(phandle, &pinfo)) < 0) { |
| fprintf(stderr, "Playback info error: %s\n", snd_strerror(err)); |
| exit(0); |
| } |
| bzero(&qtimer, sizeof(qtimer)); |
| qtimer.type = SND_SEQ_TIMER_MASTER; |
| /* note: last bit from the subdevices specifies playback */ |
| /* or capture direction for the timer specification */ |
| qtimer.number = SND_TIMER_PCM(pcard, pdevice, pinfo.subdevice << 1); |
| if ((err = snd_seq_set_queue_timer(handle, queue, &qtimer)) < 0) { |
| fprintf(stderr, "Sequencer PCM timer setup failed: %s\n", snd_strerror(err)); |
| exit(0); |
| } |
| } |
| #endif |
| if ((err = snd_seq_start_queue(handle, queue, NULL))<0) |
| fprintf(stderr, "Timer event output error: %s\n", snd_strerror(err)); |
| snd_seq_drain_output(handle); |
| } |
| |
| void event_sender_filter(snd_seq_t *handle) |
| { |
| int err; |
| |
| if ((err = snd_seq_set_client_event_filter(handle, SND_SEQ_EVENT_ECHO)) < 0) { |
| fprintf(stderr, "Unable to set client info: %s\n", snd_strerror(err)); |
| return; |
| } |
| } |
| |
| void send_event(snd_seq_t *handle, int queue, int client, int port, |
| snd_seq_addr_t *dest, int *time) |
| { |
| int err; |
| snd_seq_event_t ev; |
| |
| bzero(&ev, sizeof(ev)); |
| ev.queue = queue; |
| ev.source.client = ev.dest.client = client; |
| ev.source.port = ev.dest.port = port; |
| ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS; |
| ev.time.time.tv_sec = *time; (*time)++; |
| ev.type = SND_SEQ_EVENT_ECHO; |
| if ((err = snd_seq_event_output(handle, &ev))<0) |
| fprintf(stderr, "Event output error: %s\n", snd_strerror(err)); |
| ev.dest = *dest; |
| ev.type = SND_SEQ_EVENT_PGMCHANGE; |
| ev.data.control.channel = 0; |
| ev.data.control.value = 16; |
| if ((err = snd_seq_event_output(handle, &ev))<0) |
| fprintf(stderr, "Event output error: %s\n", snd_strerror(err)); |
| ev.type = SND_SEQ_EVENT_NOTE; |
| ev.data.note.channel = 0; |
| ev.data.note.note = 64 + (queue*2); |
| ev.data.note.velocity = 127; |
| ev.data.note.off_velocity = 127; |
| ev.data.note.duration = 500; /* 0.5sec */ |
| if ((err = snd_seq_event_output(handle, &ev))<0) |
| fprintf(stderr, "Event output error: %s\n", snd_strerror(err)); |
| if ((err = snd_seq_drain_output(handle))<0) |
| fprintf(stderr, "Event drain error: %s\n", snd_strerror(err)); |
| } |
| |
| void event_sender(snd_seq_t *handle, int argc, char *argv[]) |
| { |
| snd_seq_event_t *ev; |
| snd_seq_port_info_t *pinfo; |
| snd_seq_port_subscribe_t *sub; |
| snd_seq_addr_t addr; |
| struct pollfd *pfds; |
| int client, port, queue, max, err, v1, v2, time = 0, pcm_flag = 0; |
| char *ptr; |
| snd_pcm_t *phandle = NULL; |
| |
| if (argc < 1) { |
| fprintf(stderr, "Invalid destination...\n"); |
| return; |
| } |
| |
| if ((client = snd_seq_client_id(handle))<0) { |
| fprintf(stderr, "Cannot determine client number: %s\n", snd_strerror(client)); |
| return; |
| } |
| printf("Client ID = %i\n", client); |
| if ((queue = snd_seq_alloc_queue(handle))<0) { |
| fprintf(stderr, "Cannot allocate queue: %s\n", snd_strerror(queue)); |
| return; |
| } |
| printf("Queue ID = %i\n", queue); |
| event_sender_filter(handle); |
| if ((err = snd_seq_nonblock(handle, 1))<0) |
| fprintf(stderr, "Cannot set nonblock mode: %s\n", snd_strerror(err)); |
| |
| snd_seq_port_info_alloca(&pinfo); |
| snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_READ); |
| snd_seq_port_info_set_name(pinfo, "Output"); |
| if ((err = snd_seq_create_port(handle, pinfo)) < 0) { |
| fprintf(stderr, "Cannot create output port: %s\n", snd_strerror(err)); |
| return; |
| } |
| port = snd_seq_port_info_get_port(pinfo); |
| |
| snd_seq_port_subscribe_alloca(&sub); |
| addr.client = client; |
| addr.port = port; |
| snd_seq_port_subscribe_set_sender(sub, &addr); |
| |
| for (max = 0; max < argc; max++) { |
| ptr = argv[max]; |
| if (!ptr) |
| continue; |
| if (!strcmp(ptr, "pcm")) { |
| pcm_flag = 1; |
| continue; |
| } |
| if (sscanf(ptr, "%i.%i", &v1, &v2) != 2) { |
| fprintf(stderr, "Wrong argument '%s'...\n", argv[max]); |
| return; |
| } |
| addr.client = v1; |
| addr.port = v2; |
| snd_seq_port_subscribe_set_dest(sub, &addr); |
| if ((err = snd_seq_subscribe_port(handle, sub))<0) { |
| fprintf(stderr, "Cannot subscribe port %i from client %i: %s\n", v2, v1, snd_strerror(err)); |
| return; |
| } |
| } |
| |
| printf("Destination client = %i, port = %i\n", addr.client, addr.port); |
| |
| #ifdef USE_PCM |
| if (pcm_flag) { |
| if ((err = snd_pcm_open(&phandle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) { |
| fprintf(stderr, "Playback open error: %s\n", snd_strerror(err)); |
| exit(0); |
| } |
| set_hwparams(phandle); |
| pbuf = calloc(1, period_size * 4); |
| if (pbuf == NULL) { |
| fprintf(stderr, "No enough memory...\n"); |
| exit(0); |
| } |
| } |
| #endif |
| event_sender_start_timer(handle, client, queue, phandle); |
| |
| /* send the first event */ |
| send_event(handle, queue, client, port, &addr, &time); |
| #ifdef USE_PCM |
| if (phandle) |
| max += snd_pcm_poll_descriptors_count(phandle); |
| #endif |
| pfds = alloca(sizeof(*pfds) * max); |
| while (1) { |
| int nseqs = snd_seq_poll_descriptors_count(handle, POLLOUT|POLLIN); |
| if (snd_seq_event_output_pending(handle)) |
| snd_seq_poll_descriptors(handle, pfds, nseqs, POLLOUT|POLLIN); |
| else |
| snd_seq_poll_descriptors(handle, pfds, nseqs, POLLIN); |
| max = nseqs; |
| #ifdef USE_PCM |
| if (phandle) { |
| int pmax = snd_pcm_poll_descriptors_count(phandle); |
| snd_seq_poll_descriptors(phandle, pfds + max, pmax); |
| max += pmax; |
| } |
| #endif |
| if (poll(pfds, max, -1) < 0) |
| break; |
| #ifdef USE_PCM |
| if (phandle && (pfds[nseqs].revents & POLLOUT)) { |
| if (snd_pcm_writei(phandle, pbuf, period_size) != period_size) { |
| fprintf(stderr, "Playback write error!!\n"); |
| exit(0); |
| } |
| } |
| #endif |
| if (pfds[0].revents & POLLOUT) |
| snd_seq_drain_output(handle); |
| if (pfds[0].revents & POLLIN) { |
| do { |
| if ((err = snd_seq_event_input(handle, &ev))<0) |
| break; |
| if (!ev) |
| continue; |
| if (ev->type == SND_SEQ_EVENT_ECHO) |
| send_event(handle, queue, client, port, &addr, &time); |
| decode_event(ev); |
| snd_seq_free_event(ev); |
| } while (err > 0); |
| } |
| } |
| } |