static void do_jack_output(alsa_seqmidi_t *self, port_t *port, struct process_info* info) { stream_t *str = &self->stream[info->dir]; int nevents = jack_midi_get_event_count(port->jack_buf); int i; for (i=0; i<nevents; ++i) { jack_midi_event_t jack_event; snd_seq_event_t alsa_event; int64_t frame_offset; int64_t out_time; snd_seq_real_time_t out_rt; int err; jack_midi_event_get(&jack_event, port->jack_buf, i); snd_seq_ev_clear(&alsa_event); snd_midi_event_reset_encode(str->codec); if (!snd_midi_event_encode(str->codec, jack_event.buffer, jack_event.size, &alsa_event)) continue; // invalid event snd_seq_ev_set_source(&alsa_event, self->port_id); snd_seq_ev_set_dest(&alsa_event, port->remote.client, port->remote.port); /* NOTE: in case of xrun it could become negative, so it is essential to use signed type! */ frame_offset = (int64_t)jack_event.time + info->period_start + info->nframes - info->cur_frames; if (frame_offset < 0) { frame_offset = info->nframes + jack_event.time; error_log("internal xrun detected: frame_offset = %"PRId64"\n", frame_offset); } /* Ken Ellinwood reported problems with this assert. * Seems, magic 2 should be replaced with nperiods. */ //FIXME: assert (frame_offset < info->nframes*2); //if (frame_offset < info->nframes * info->nperiods) // debug_log("alsa_out: BLAH-BLAH-BLAH"); out_time = info->alsa_time + (frame_offset * NSEC_PER_SEC) / info->sample_rate; debug_log("alsa_out: frame_offset = %lld, info->alsa_time = %lld, out_time = %lld, port->last_out_time = %lld", frame_offset, info->alsa_time, out_time, port->last_out_time); // we should use absolute time to prevent reordering caused by rounding errors if (out_time < port->last_out_time) { debug_log("alsa_out: limiting out_time %lld at %lld", out_time, port->last_out_time); out_time = port->last_out_time; } else port->last_out_time = out_time; out_rt.tv_nsec = out_time % NSEC_PER_SEC; out_rt.tv_sec = out_time / NSEC_PER_SEC; snd_seq_ev_schedule_real(&alsa_event, self->queue, 0, &out_rt); err = snd_seq_event_output(self->seq, &alsa_event); debug_log("alsa_out: written %d bytes to %s at %+d (%lld): %d", (int)jack_event.size, port->name, (int)frame_offset, out_time, err); } }
void MIDIDevice::feedBack(t_input_channel channel, t_input_value value) { /* MIDI devices can have only 128 notes or controllers */ if (channel < 128) { snd_seq_event_t ev; MIDIInput* plugin; plugin = static_cast<MIDIInput*> (parent()); Q_ASSERT(plugin != NULL); Q_ASSERT(plugin->alsa() != NULL); Q_ASSERT(m_address != NULL); /* Setup an event structure */ snd_seq_ev_clear(&ev); snd_seq_ev_set_dest(&ev, m_address->client, m_address->port); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); char scaled = static_cast <char> (SCALE(double(value), double(0), double(KInputValueMax), double(0), double(127))); if (m_mode == ControlChange) { /* Send control change */ snd_seq_ev_set_controller(&ev, midiChannel(), channel, scaled); snd_seq_event_output(plugin->alsa(), &ev); snd_seq_drain_output(plugin->alsa()); } else { /* Send note on/off */ if (value == 0) { snd_seq_ev_set_noteoff(&ev, midiChannel(), channel, scaled); } else { snd_seq_ev_set_noteon(&ev, midiChannel(), channel, scaled); } snd_seq_event_output(plugin->alsa(), &ev); snd_seq_drain_output(plugin->alsa()); } } }
void MIDIDevice::feedBack(quint32 channel, uchar value) { MIDIInput* plugin = static_cast<MIDIInput*> (parent()); Q_ASSERT(plugin != NULL); Q_ASSERT(plugin->alsa() != NULL); Q_ASSERT(m_address != NULL); uchar cmd = 0; uchar data1 = 0; uchar data2 = 0; bool d2v = false; if (QLCMIDIProtocol::feedbackToMidi(channel, value, midiChannel(), &cmd, &data1, &data2, &d2v) == true) { /* Setup an event structure */ snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_set_dest(&ev, m_address->client, m_address->port); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); if (MIDI_CMD(cmd) == MIDI_NOTE_OFF) { /* Send data as note off command */ snd_seq_ev_set_noteoff(&ev, midiChannel(), data1, data2); snd_seq_event_output(plugin->alsa(), &ev); snd_seq_drain_output(plugin->alsa()); } else if (MIDI_CMD(cmd) == MIDI_NOTE_ON) { /* Send data as note on command */ snd_seq_ev_set_noteon(&ev, midiChannel(), data1, data2); snd_seq_event_output(plugin->alsa(), &ev); snd_seq_drain_output(plugin->alsa()); } else if (MIDI_CMD(cmd) == MIDI_CONTROL_CHANGE) { /* Send data as control change command */ snd_seq_ev_set_controller(&ev, midiChannel(), data1, data2); snd_seq_event_output(plugin->alsa(), &ev); snd_seq_drain_output(plugin->alsa()); } } }
void AlsaMidiOutputDevice::writeSysEx(QByteArray message) { if(message.isEmpty()) return; if (isOpen() == false) return; snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_set_dest(&ev, m_receiver_address->client, m_receiver_address->port); //snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); snd_seq_ev_set_sysex (&ev, message.count(), message.data()); if (snd_seq_event_output(m_alsa, &ev) < 0) qDebug() << "snd_seq_event_output ERROR"; // Make sure that all values go to the MIDI endpoint snd_seq_drain_output(m_alsa); }
void AlsaMidiOutputDevice::writeFeedback(uchar cmd, uchar data1, uchar data2) { if (isOpen() == false) return; // Setup a common event structure for all values snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_set_dest(&ev, m_receiver_address->client, m_receiver_address->port); //snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); uchar midiCmd = MIDI_CMD(cmd); uchar midiCh = MIDI_CH(cmd); bool invalidCmd = false; switch(midiCmd) { case MIDI_NOTE_OFF: snd_seq_ev_set_noteoff(&ev, midiCh, data1, data2); break; case MIDI_NOTE_ON: snd_seq_ev_set_noteon(&ev, midiCh, data1, data2); break; case MIDI_CONTROL_CHANGE: snd_seq_ev_set_controller(&ev, midiCh, data1, data2); break; case MIDI_PROGRAM_CHANGE: snd_seq_ev_set_pgmchange(&ev, midiCh, data1); break; case MIDI_NOTE_AFTERTOUCH: snd_seq_ev_set_keypress(&ev, midiCh, data1, data2); break; case MIDI_CHANNEL_AFTERTOUCH: snd_seq_ev_set_chanpress(&ev, midiCh, data1); break; case MIDI_PITCH_WHEEL: snd_seq_ev_set_pitchbend(&ev, midiCh, ((data1 & 0x7f) | ((data2 & 0x7f) << 7)) - 8192); break; default: // What to do here ?? invalidCmd = true; break; } if (!invalidCmd) { if (snd_seq_event_output(m_alsa, &ev) < 0) qDebug() << "snd_seq_event_output ERROR"; } // Make sure that all values go to the MIDI endpoint snd_seq_drain_output(m_alsa); }
void AlsaMidiOutputDevice::writeUniverse(const QByteArray& universe) { if (isOpen() == false) return; // Setup a common event structure for all values snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_set_dest(&ev, m_receiver_address->client, m_receiver_address->port); //snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); // Since MIDI devices can have only 128 real channels, we don't // attempt to write more than that. for (uchar channel = 0; channel < MAX_MIDI_DMX_CHANNELS && channel < universe.size(); channel++) { // Scale 0-255 to 0-127 char scaled = DMX2MIDI(universe[channel]); bool invalidData = false; // Since MIDI is so slow, we only send values that are actually changed if (m_universe[channel] == scaled) continue; // Store the changed MIDI value m_universe[channel] = scaled; if (mode() == Note) { qDebug() << "Send out NOTE"; // 0 is sent as a note off // 1-127 is sent as note on if (scaled == 0) snd_seq_ev_set_noteoff(&ev, midiChannel(), channel, scaled); else snd_seq_ev_set_noteon(&ev, midiChannel(), channel, scaled); snd_seq_event_output(m_alsa, &ev); } else if (mode() == ProgramChange) { qDebug() << "Send out Program Change"; snd_seq_ev_set_pgmchange(&ev, midiChannel(), channel); } else if (mode() == ControlChange) { qDebug() << "Send out CC. Channel: " << midiChannel() << ", CC: " << channel << ", val: " << scaled; // Control change snd_seq_ev_set_controller(&ev, midiChannel(), channel, scaled); } else invalidData = true; if (!invalidData) if (snd_seq_event_output(m_alsa, &ev) < 0) qDebug() << "snd_seq_event_output ERROR"; } // Make sure that all values go to the MIDI endpoint snd_seq_drain_output(m_alsa); }
void MIDIDevice::writeRange(t_value* values, t_channel num) { Q_UNUSED(num); MIDIOut* plugin = static_cast<MIDIOut*> (parent()); Q_ASSERT(plugin != NULL); Q_ASSERT(plugin->alsa() != NULL); Q_ASSERT(m_address != NULL); /* Setup a common event structure for all values */ snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_set_dest(&ev, m_address->client, m_address->port); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); /* Since MIDI devices can have only 128 real channels, we don't attempt to write more than that */ for (unsigned char channel = 0; channel < MAX_MIDI_DMX_CHANNELS; channel++) { /* Scale 0-255 to 0-127 */ char scaled = static_cast <char> (SCALE(double(values[channel]), double(0), double(KInputValueMax), double(0), double(127))); /* Since MIDI is so slow, we only send values that are actually changed. */ if (m_values[channel] == scaled) continue; /* Store the changed MIDI value */ m_values[channel] = scaled; if (mode() == Note) { if (scaled == 0) { /* 0 is sent as a note off command */ snd_seq_ev_set_noteoff(&ev, midiChannel(), channel, scaled); } else { /* 1-127 is sent as note on command */ snd_seq_ev_set_noteon(&ev, midiChannel(), channel, scaled); } snd_seq_event_output(plugin->alsa(), &ev); } else { /* Control change */ snd_seq_ev_set_controller(&ev, midiChannel(), channel, scaled); snd_seq_event_output_buffer(plugin->alsa(), &ev); } } /* Make sure that all values go to the MIDI endpoint */ snd_seq_drain_output(plugin->alsa()); }
/************************************************************************** * modLongData [internal] */ static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize) { int len_add = 0; LPBYTE lpData, lpNewData = NULL; snd_seq_event_t event; TRACE("(%04X, %p, %08lX);\n", wDevID, lpMidiHdr, dwSize); /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure * but it seems to be used only for midi input. * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption. */ if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID; if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE; if (midiSeq == NULL) { WARN("can't play !\n"); return MIDIERR_NODEVICE; } lpData = (LPBYTE) lpMidiHdr->lpData; if (lpData == NULL) return MIDIERR_UNPREPARED; if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED; if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING; lpMidiHdr->dwFlags &= ~MHDR_DONE; lpMidiHdr->dwFlags |= MHDR_INQUEUE; /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive * data, or can it also contain raw MIDI data, to be split up and sent to * modShortData() ? * If the latest is true, then the following WARNing will fire up */ if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) { WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n"); lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2); } TRACE("dwBufferLength=%lu !\n", lpMidiHdr->dwBufferLength); TRACE(" %02X %02X %02X ... %02X %02X %02X\n", lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3], lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]); switch (MidiOutDev[wDevID].caps.wTechnology) { case MOD_FMSYNTH: /* FIXME: I don't think there is much to do here */ break; case MOD_MIDIPORT: if (lpData[0] != 0xF0) { /* Send start of System Exclusive */ len_add = 1; lpData[0] = 0xF0; memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength); WARN("Adding missing 0xF0 marker at the beginning of " "system exclusive byte stream\n"); } if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) { /* Send end of System Exclusive */ memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength); lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0; len_add++; WARN("Adding missing 0xF7 marker at the end of " "system exclusive byte stream\n"); } snd_seq_ev_clear(&event); snd_seq_ev_set_direct(&event); snd_seq_ev_set_source(&event, port_out); snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port); TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port); snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData); snd_seq_event_output_direct(midiSeq, &event); if (lpNewData) HeapFree(GetProcessHeap(), 0, lpData); break; default: WARN("Technology not supported (yet) %d !\n", MidiOutDev[wDevID].caps.wTechnology); return MMSYSERR_NOTENABLED; } lpMidiHdr->dwFlags &= ~MHDR_INQUEUE; lpMidiHdr->dwFlags |= MHDR_DONE; if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) { WARN("can't notify client !\n"); return MMSYSERR_INVALPARAM; } return MMSYSERR_NOERROR; }
/************************************************************************** * modData [internal] */ static DWORD modData(WORD wDevID, DWORD dwParam) { BYTE evt = LOBYTE(LOWORD(dwParam)); BYTE d1 = HIBYTE(LOWORD(dwParam)); BYTE d2 = LOBYTE(HIWORD(dwParam)); TRACE("(%04X, %08lX);\n", wDevID, dwParam); if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID; if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE; if (midiSeq == NULL) { WARN("can't play !\n"); return MIDIERR_NODEVICE; } switch (MidiOutDev[wDevID].caps.wTechnology) { case MOD_SYNTH: case MOD_MIDIPORT: { int handled = 1; /* Assume event is handled */ snd_seq_event_t event; snd_seq_ev_clear(&event); snd_seq_ev_set_direct(&event); snd_seq_ev_set_source(&event, port_out); snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port); switch (evt & 0xF0) { case MIDI_CMD_NOTE_OFF: snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2); break; case MIDI_CMD_NOTE_ON: snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2); break; case MIDI_CMD_NOTE_PRESSURE: snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2); break; case MIDI_CMD_CONTROL: snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2); break; case MIDI_CMD_BENDER: snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000); break; case MIDI_CMD_PGM_CHANGE: snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1); break; case MIDI_CMD_CHANNEL_PRESSURE: snd_seq_ev_set_chanpress(&event, evt&0x0F, d1); break; case MIDI_CMD_COMMON_SYSEX: switch (evt & 0x0F) { case 0x00: /* System Exclusive, don't do it on modData, * should require modLongData*/ case 0x01: /* Undefined */ case 0x04: /* Undefined. */ case 0x05: /* Undefined. */ case 0x07: /* End of Exclusive. */ case 0x09: /* Undefined. */ case 0x0D: /* Undefined. */ handled = 0; break; case 0x06: /* Tune Request */ case 0x08: /* Timing Clock. */ case 0x0A: /* Start. */ case 0x0B: /* Continue */ case 0x0C: /* Stop */ case 0x0E: /* Active Sensing. */ /* FIXME: Is this function suitable for these purposes (and also Song Select and Song Position Pointer) */ snd_seq_ev_set_sysex(&event, 1, &evt); break; case 0x0F: /* Reset */ /* snd_seq_ev_set_sysex(&event, 1, &evt); this other way may be better */ { BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7}; snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq); } break; case 0x03: /* Song Select. */ { BYTE buf[2]; buf[0] = evt; buf[1] = d1; snd_seq_ev_set_sysex(&event, sizeof(buf), buf); } break; case 0x02: /* Song Position Pointer. */ { BYTE buf[3]; buf[0] = evt; buf[1] = d1; buf[2] = d2; snd_seq_ev_set_sysex(&event, sizeof(buf), buf); } break; } break; } if (handled) snd_seq_event_output_direct(midiSeq, &event); } break; default: WARN("Technology not supported (yet) %d !\n", MidiOutDev[wDevID].caps.wTechnology); return MMSYSERR_NOTENABLED; } return MMSYSERR_NOERROR; }