// Data transmission methods. bool qmidinetJackMidiDevice::sendData ( unsigned char *data, unsigned short len, int port ) const { if (port < 0 || port >= m_nports) return false; if (m_pJackBufferOut == NULL) return false; const unsigned int nlimit = jack_ringbuffer_write_space(m_pJackBufferOut); if (sizeof(qmidinetJackMidiEvent) + len < nlimit) { unsigned char achBuffer[nlimit]; unsigned char *pchBuffer = &achBuffer[0]; qmidinetJackMidiEvent *pJackEventOut = (struct qmidinetJackMidiEvent *) pchBuffer; pchBuffer += sizeof(qmidinetJackMidiEvent); memcpy(pchBuffer, data, len); pJackEventOut->event.time = jack_frame_time(m_pJackClient); pJackEventOut->event.buffer = (jack_midi_data_t *) pchBuffer; pJackEventOut->event.size = len; pJackEventOut->port = port; #ifdef CONFIG_DEBUG // - show (output) event for debug purposes... fprintf(stderr, "JACK MIDI Out Port %d:", port); for (unsigned int i = 0; i < len; ++i) fprintf(stderr, " 0x%02x", (unsigned char) pchBuffer[i]); fprintf(stderr, "\n"); #endif jack_ringbuffer_write(m_pJackBufferOut, (const char *) achBuffer, sizeof(qmidinetJackMidiEvent) + len); } return true; }
static PaTime GetStreamTime( PaStream *s ) { PaJackStream *stream = (PaJackStream*)s; /* TODO: what if we're recording-only? */ return jack_frame_time( stream->jack_client ) - stream->t0; }
/* * =================== Input/output port handling ========================= */ static void set_process_info(struct process_info *info, alsa_seqmidi_t *self, int dir, jack_nframes_t nframes) { const snd_seq_real_time_t* alsa_time; snd_seq_queue_status_t *status; snd_seq_queue_status_alloca(&status); info->dir = dir; info->period_start = jack_last_frame_time(self->jack); info->nframes = nframes; info->sample_rate = jack_get_sample_rate(self->jack); info->cur_frames = jack_frame_time(self->jack); // immediately get alsa'a real time (uhh, why everybody has their own 'real' time) snd_seq_get_queue_status(self->seq, self->queue, status); alsa_time = snd_seq_queue_status_get_real_time(status); info->alsa_time = alsa_time->tv_sec * NSEC_PER_SEC + alsa_time->tv_nsec; if (info->period_start + info->nframes < info->cur_frames) { int periods_lost = (info->cur_frames - info->period_start) / info->nframes; info->period_start += periods_lost * info->nframes; debug_log("xrun detected: %d periods lost\n", periods_lost); } }
static PaTime GetStreamTime( PaStream *s ) { PaJackStream *stream = (PaJackStream*)s; /* TODO: what if we're recording-only? */ int delta_t = jack_frame_time( stream->jack_client ) - stream->t0; return delta_t / (PaTime)jack_get_sample_rate( stream->jack_client ); }
static PyObject* get_frame_time(PyObject* self, PyObject* args) { pyjack_client_t * client = self_or_global_client(self); if(client->pjc == NULL) { PyErr_SetString(JackNotConnectedError, "Jack connection has not yet been established."); return NULL; } int frt = jack_frame_time(client->pjc); return Py_BuildValue("i", frt); }
static int _get(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj* const *objv) { if (argc != 2) return fw_error_obj(interp, Tcl_ObjPrintf("usage: %s get", Tcl_GetString(objv[0]))); _t *data = (_t *)clientData; Tcl_Obj *result[] = { Tcl_NewIntObj(jack_frame_time(data->fw.client)), Tcl_NewDoubleObj(data->sam.pll.freq.f), NULL }; Tcl_SetObjResult(interp, Tcl_NewListObj(2, result)); return TCL_OK; }
static void jack_process(midi_stream_t *str, jack_nframes_t nframes) { int r, w; process_jack_t proc; jack_nframes_t cur_frames; if (!str->owner->keep_walking) return; proc.midi = str->owner; proc.nframes = nframes; proc.frame_time = jack_last_frame_time(proc.midi->client); cur_frames = jack_frame_time(proc.midi->client); int periods_diff = cur_frames - proc.frame_time; if (periods_diff < proc.nframes) { int periods_lost = periods_diff / proc.nframes; proc.frame_time += periods_lost * proc.nframes; debug_log("xrun detected: %d periods lost", periods_lost); } // process existing ports for (r=0, w=0; r<str->jack.nports; ++r) { midi_port_t *port = str->jack.ports[r]; proc.port = port; assert (port->state > PORT_ADDED_TO_JACK && port->state < PORT_REMOVED_FROM_JACK); proc.buffer = jack_port_get_buffer(port->jack, nframes); if (str->mode == POLLIN) jack_midi_clear_buffer(proc.buffer); if (port->state == PORT_REMOVED_FROM_MIDI) { port->state = PORT_REMOVED_FROM_JACK; // this signals to scan thread continue; // this effectively removes port from the midi->in.jack.ports[] } (str->process_jack)(&proc); if (r != w) str->jack.ports[w] = port; ++w; } if (str->jack.nports != w) debug_log("jack_%s: nports %d -> %d", str->name, str->jack.nports, w); str->jack.nports = w; jack_add_ports(str); // it makes no sense to add them earlier since they have no data yet // wake midi thread write(str->wake_pipe[1], &r, 1); }
// MIDI events capture method. void qmidinetJackMidiDevice::capture (void) { if (m_pJackBufferIn == NULL) return; char *pchBuffer; qmidinetJackMidiEvent ev; while (jack_ringbuffer_peek(m_pJackBufferIn, (char *) &ev, sizeof(ev)) == sizeof(ev)) { jack_ringbuffer_read_advance(m_pJackBufferIn, sizeof(ev)); pchBuffer = m_pQueueIn->push(ev.port, ev.event.time, ev.event.size); if (pchBuffer) jack_ringbuffer_read(m_pJackBufferIn, pchBuffer, ev.event.size); else jack_ringbuffer_read_advance(m_pJackBufferIn, ev.event.size); } float sample_rate = jack_get_sample_rate(m_pJackClient); jack_nframes_t frame_time = jack_frame_time(m_pJackClient); while ((pchBuffer = m_pQueueIn->pop( &ev.port, &ev.event.time, &ev.event.size)) != NULL) { ev.event.time += m_last_frame_time; if (ev.event.time > frame_time) { unsigned long sleep_time = ev.event.time - frame_time; float secs = float(sleep_time) / sample_rate; if (secs > 0.0001f) { #if 0 // defined(__GNUC__) && defined(Q_OS_LINUX) struct timespec ts; ts.tv_sec = time_t(secs); ts.tv_nsec = long(1E+9f * (secs - ts.tv_sec)); ::nanosleep(&ts, NULL); #else m_pRecvThread->usleep(long(1E+6f * secs)); #endif } frame_time = ev.event.time; } #ifdef CONFIG_DEBUG // - show (input) event for debug purposes... fprintf(stderr, "JACK MIDI In Port %d: (%d)", ev.port, int(ev.event.size)); for (unsigned int i = 0; i < ev.event.size; ++i) fprintf(stderr, " 0x%02x", (unsigned char) pchBuffer[i]); fprintf(stderr, "\n"); #endif recvData((unsigned char *) pchBuffer, ev.event.size, ev.port); } }
static int jack_process(jack_nframes_t nframes, void *arg) { struct userdata *u = arg; unsigned c; jack_nframes_t frame_time; pa_assert(u); /* We just forward the request to our other RT thread */ for (c = 0; c < u->channels; c++) pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes)); frame_time = jack_frame_time(u->client); pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0); return 0; }
static void showtime() { jack_position_t current; jack_transport_state_t transport_state; jack_nframes_t frame_time; transport_state = jack_transport_query(client, ¤t); frame_time = jack_frame_time(client); float time = (float) current.frame / (float) current.frame_rate; char current_time[256]; snprintf(current_time, sizeof(current_time), "%f", time); send_udp(udp_ip, udp_port, current_time); if (lastframe != current.frame) { printf( "\x1b[Aframe= %u frame_time= %u usecs= %lld fr_in:%i fr_out:%i time: %f\t ", current.frame, frame_time, current.usecs, current.frame_rate, framerate_out , time); lastframe = current.frame; switch (transport_state) { case JackTransportStopped: printf("state: Stopped"); break; case JackTransportRolling: printf("state: Rolling"); break; case JackTransportStarting: printf("state: Starting"); break; default: printf("state: [unknown]"); break; } if (current.valid & JackPositionBBT) printf("\tBBT: %3" PRIi32 "|%" PRIi32 "|%04" PRIi32, current.bar, current.beat, current.tick); if (current.valid & JackPositionTimecode) printf("\tTC: (%.6f, %.6f)", current.frame_time, current.next_time); printf(" \n"); } }
void SC_JackDriver::Run() { jack_client_t* client = mClient; World* world = mWorld; #ifdef SC_JACK_USE_DLL mDLL.Update(secondsSinceEpoch(getTime())); #if SC_JACK_DEBUG_DLL static int tick = 0; if (++tick >= 10) { tick = 0; scprintf("DLL: t %.6f p %.9f sr %.6f e %.9f avg(e) %.9f inc %.9f\n", mDLL.PeriodTime(), mDLL.Period(), mDLL.SampleRate(), mDLL.Error(), mDLL.AvgError(), mOSCincrement * kOSCtoSecs); } #endif #else HostTime hostTime = getTime(); double hostSecs = secondsSinceEpoch(hostTime); double sampleTime = (double)(jack_frame_time(client) + jack_frames_since_cycle_start(client)); if (mStartHostSecs == 0) { mStartHostSecs = hostSecs; mStartSampleTime = sampleTime; } else { double instSampleRate = (sampleTime - mPrevSampleTime) / (hostSecs - mPrevHostSecs); double smoothSampleRate = mSmoothSampleRate; smoothSampleRate = smoothSampleRate + 0.002 * (instSampleRate - smoothSampleRate); if (fabs(smoothSampleRate - mSampleRate) > 10.) { smoothSampleRate = mSampleRate; } mOSCincrement = (int64)(mOSCincrementNumerator / smoothSampleRate); mSmoothSampleRate = smoothSampleRate; #if 0 double avgSampleRate = (sampleTime - mStartSampleTime)/(hostSecs - mStartHostSecs); double jitter = (smoothSampleRate * (hostSecs - mPrevHostSecs)) - (sampleTime - mPrevSampleTime); double drift = (smoothSampleRate - mSampleRate) * (hostSecs - mStartHostSecs); #endif } mPrevHostSecs = hostSecs; mPrevSampleTime = sampleTime; #endif try { mFromEngine.Free(); mToEngine.Perform(); mOscPacketsToEngine.Perform(); int numInputs = mInputList->mSize; int numOutputs = mOutputList->mSize; jack_port_t **inPorts = mInputList->mPorts; jack_port_t **outPorts = mOutputList->mPorts; sc_jack_sample_t **inBuffers = mInputList->mBuffers; sc_jack_sample_t **outBuffers = mOutputList->mBuffers; int numSamples = NumSamplesPerCallback(); int bufFrames = mWorld->mBufLength; int numBufs = numSamples / bufFrames; float *inBuses = mWorld->mAudioBus + mWorld->mNumOutputs * bufFrames; float *outBuses = mWorld->mAudioBus; int32 *inTouched = mWorld->mAudioBusTouched + mWorld->mNumOutputs; int32 *outTouched = mWorld->mAudioBusTouched; int minInputs = sc_min(numInputs, (int)mWorld->mNumInputs); int minOutputs = sc_min(numOutputs, (int)mWorld->mNumOutputs); int bufFramePos = 0; // cache I/O buffers for (int i = 0; i < minInputs; ++i) { inBuffers[i] = (sc_jack_sample_t*)jack_port_get_buffer(inPorts[i], numSamples); } for (int i = 0; i < minOutputs; ++i) { outBuffers[i] = (sc_jack_sample_t*)jack_port_get_buffer(outPorts[i], numSamples); } // main loop #ifdef SC_JACK_USE_DLL int64 oscTime = mOSCbuftime = (int64)((mDLL.PeriodTime() - mMaxOutputLatency) * kSecondsToOSCunits + .5); // int64 oscInc = mOSCincrement = (int64)(mOSCincrementNumerator / mDLL.SampleRate()); int64 oscInc = mOSCincrement = (int64)((mDLL.Period() / numBufs) * kSecondsToOSCunits + .5); mSmoothSampleRate = mDLL.SampleRate(); double oscToSamples = mOSCtoSamples = mSmoothSampleRate * kOSCtoSecs /* 1/pow(2,32) */; #else int64 oscTime = mOSCbuftime = OSCTime(hostTime) - (int64)(mMaxOutputLatency * kSecondsToOSCunits + .5); int64 oscInc = mOSCincrement; double oscToSamples = mOSCtoSamples; #endif for (int i = 0; i < numBufs; ++i, mWorld->mBufCounter++, bufFramePos += bufFrames) { int32 bufCounter = mWorld->mBufCounter; int32 *tch; // copy+touch inputs tch = inTouched; for (int k = 0; k < minInputs; ++k) { sc_jack_sample_t *src = inBuffers[k] + bufFramePos; float *dst = inBuses + k * bufFrames; for (int n = 0; n < bufFrames; ++n) { *dst++ = *src++; } *tch++ = bufCounter; } // run engine int64 schedTime; int64 nextTime = oscTime + oscInc; while ((schedTime = mScheduler.NextTime()) <= nextTime) { float diffTime = (float)(schedTime - oscTime) * oscToSamples + 0.5; float diffTimeFloor = floor(diffTime); world->mSampleOffset = (int)diffTimeFloor; world->mSubsampleOffset = diffTime - diffTimeFloor; if (world->mSampleOffset < 0) world->mSampleOffset = 0; else if (world->mSampleOffset >= world->mBufLength) world->mSampleOffset = world->mBufLength-1; SC_ScheduledEvent event = mScheduler.Remove(); event.Perform(); } world->mSampleOffset = 0; world->mSubsampleOffset = 0.f; World_Run(world); // copy touched outputs tch = outTouched; for (int k = 0; k < minOutputs; ++k) { sc_jack_sample_t *dst = outBuffers[k] + bufFramePos; if (*tch++ == bufCounter) { float *src = outBuses + k * bufFrames; for (int n = 0; n < bufFrames; ++n) { *dst++ = *src++; } } else { for (int n = 0; n < bufFrames; ++n) { *dst++ = 0.0f; } } } // advance OSC time mOSCbuftime = oscTime = nextTime; } } catch (std::exception& exc) { scprintf("%s: exception in real time: %s\n", kJackDriverIdent, exc.what()); } catch (...) { scprintf("%s: unknown exception in real time\n", kJackDriverIdent); } double cpuUsage = (double)jack_cpu_load(mClient); mAvgCPU = mAvgCPU + 0.1 * (cpuUsage - mAvgCPU); if (cpuUsage > mPeakCPU || --mPeakCounter <= 0) { mPeakCPU = cpuUsage; mPeakCounter = mMaxPeakCounter; } mAudioSync.Signal(); }
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *memchunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { case SINK_MESSAGE_RENDER: /* Handle the request from the JACK thread */ if (u->sink->thread_info.state == PA_SINK_RUNNING) { pa_memchunk chunk; size_t nbytes; void *p; pa_assert(offset > 0); nbytes = (size_t) offset * pa_frame_size(&u->sink->sample_spec); pa_sink_render_full(u->sink, nbytes, &chunk); p = (uint8_t*) pa_memblock_acquire(chunk.memblock) + chunk.index; pa_deinterleave(p, u->buffer, u->channels, sizeof(float), (unsigned) offset); pa_memblock_release(chunk.memblock); pa_memblock_unref(chunk.memblock); } else { unsigned c; pa_sample_spec ss; /* Humm, we're not RUNNING, hence let's write some silence */ ss = u->sink->sample_spec; ss.channels = 1; for (c = 0; c < u->channels; c++) pa_silence_memory(u->buffer[c], (size_t) offset * pa_sample_size(&ss), &ss); } u->frames_in_buffer = (jack_nframes_t) offset; u->saved_frame_time = * (jack_nframes_t*) data; u->saved_frame_time_valid = TRUE; return 0; case SINK_MESSAGE_BUFFER_SIZE: pa_sink_set_max_request_within_thread(u->sink, (size_t) offset * pa_frame_size(&u->sink->sample_spec)); return 0; case SINK_MESSAGE_ON_SHUTDOWN: pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); return 0; case PA_SINK_MESSAGE_GET_LATENCY: { jack_nframes_t l, ft, d; size_t n; /* This is the "worst-case" latency */ l = jack_port_get_total_latency(u->client, u->port[0]) + u->frames_in_buffer; if (u->saved_frame_time_valid) { /* Adjust the worst case latency by the time that * passed since we last handed data to JACK */ ft = jack_frame_time(u->client); d = ft > u->saved_frame_time ? ft - u->saved_frame_time : 0; l = l > d ? l - d : 0; } /* Convert it to usec */ n = l * pa_frame_size(&u->sink->sample_spec); *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); return 0; } } return pa_sink_process_msg(o, code, data, offset, memchunk); }
int main(int argc, char *argv[]) { int ch; char *file_name, *autoconnect_port_name = NULL; #ifdef WITH_LASH lash_args_t *lash_args; #endif g_thread_init(NULL); #ifdef WITH_LASH lash_args = lash_extract_args(&argc, &argv); #endif g_log_set_default_handler(log_handler, NULL); while ((ch = getopt(argc, argv, "a:dnqr:stVx")) != -1) { switch (ch) { case 'a': autoconnect_port_name = strdup(optarg); break; case 'd': debug = 1; break; case 'n': start_stopped = 1; break; case 'q': be_quiet = 1; break; case 'r': rate_limit = strtod(optarg, NULL); if (rate_limit <= 0.0) { g_critical("Invalid rate limit specified.\n"); exit(EX_USAGE); } break; case 's': just_one_output = 1; break; case 't': use_transport = 0; break; case 'x': remote_control = 1; break; case 'V': show_version(); break; case '?': default: usage(); break; } } argc -= optind; argv += optind; if (argv[0] == NULL) { g_critical("No file name given."); usage(); } file_name = argv[0]; if (!remote_control) { smf_vol = smf = smf_load(file_name); if (smf == NULL) { g_critical("Loading SMF file failed."); exit(-1); } if (!be_quiet) g_message("%s.", smf_decode(smf)); if (smf->number_of_tracks > MAX_NUMBER_OF_TRACKS) { g_warning("Number of tracks (%d) exceeds maximum for per-track output; implying '-s' option.", smf->number_of_tracks); just_one_output = 1; } } #ifdef WITH_LASH init_lash(lash_args); #endif g_timeout_add(1000, emergency_exit_timeout, (gpointer)0); signal(SIGINT, ctrl_c_handler); init_jack(); if (autoconnect_port_name) { if (connect_to_input_port(autoconnect_port_name)) { g_critical("Couldn't connect to '%s', exiting.", autoconnect_port_name); exit(EX_UNAVAILABLE); } } if (use_transport && !start_stopped) { jack_transport_locate(jack_client, 0); jack_transport_start(jack_client); } if (!use_transport) playback_started = jack_frame_time(jack_client); if (remote_control) remote_control_start(argv[0]); g_main_loop_run(g_main_loop_new(NULL, TRUE)); /* Not reached. */ return 0; }
static int JackCallback( jack_nframes_t frames, void *userData ) { PaJackStream *stream = (PaJackStream*)userData; PaStreamCallbackTimeInfo timeInfo; int callbackResult; int chn; int framesProcessed; /* TODO: make this a lot more accurate */ PaTime now = GetStreamTime(stream); timeInfo.currentTime = now; timeInfo.outputBufferDacTime = now; timeInfo.inputBufferAdcTime = now; if( stream->t0 == -1 ) { if( stream->num_outgoing_connections == 0 ) { /* TODO: how to handle stream time for capture-only operation? */ } else { /* the beginning time needs to be initialized */ stream->t0 = jack_frame_time( stream->jack_client ) - jack_frames_since_cycle_start( stream->jack_client) + jack_port_get_total_latency( stream->jack_client, stream->local_output_ports[0] ); } } PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* @todo pass underflow/overflow flags when necessary */ ); for( chn = 0; chn < stream->num_incoming_connections; chn++ ) { jack_default_audio_sample_t *channel_buf; channel_buf = (jack_default_audio_sample_t*) jack_port_get_buffer( stream->local_input_ports[chn], frames ); PaUtil_SetNonInterleavedInputChannel( &stream->bufferProcessor, chn, channel_buf ); } for( chn = 0; chn < stream->num_outgoing_connections; chn++ ) { jack_default_audio_sample_t *channel_buf; channel_buf = (jack_default_audio_sample_t*) jack_port_get_buffer( stream->local_output_ports[chn], frames ); PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor, chn, channel_buf ); } if( stream->num_incoming_connections > 0 ) PaUtil_SetInputFrameCount( &stream->bufferProcessor, frames ); if( stream->num_outgoing_connections > 0 ) PaUtil_SetOutputFrameCount( &stream->bufferProcessor, frames ); callbackResult = paContinue; framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); stream->total_frames_sent += frames; if( callbackResult == paContinue ) { /* nothing special */ } else if( callbackResult == paAbort ) { /* finish playback immediately */ /* TODO: memset 0 the outgoing samples to "cancel" them */ stream->is_active = FALSE; /* return nonzero so we get deactivated (and the callback won't * get called again) */ return 1; } else { /* User callback has asked us to stop with paComplete or other non-zero value. */ stream->is_active = FALSE; /* return nonzero so we get deactivated (and the callback won't * get called again) */ return 1; } return 0; }
static void *midi_thread(void *arg) { midi_stream_t *str = arg; alsa_rawmidi_t *midi = str->owner; struct pollfd pfds[MAX_PFDS]; int npfds; jack_time_t wait_nsec = 1000*1000*1000; // 1 sec process_midi_t proc; proc.midi = midi; proc.mode = str->mode; pfds[0].fd = str->wake_pipe[0]; pfds[0].events = POLLIN|POLLERR|POLLNVAL; npfds = 1; if (jack_is_realtime(midi->client)) set_threaded_log_function(); //debug_log("midi_thread(%s): enter", str->name); while (midi->keep_walking) { int poll_timeout; int wait_nanosleep; int r=1, w=1; // read,write pos in pfds int rp=0, wp=0; // read, write pos in ports // sleep //if (wait_nsec != 1000*1000*1000) { // debug_log("midi_thread(%s): ", str->name); // assert (wait_nsec == 1000*1000*1000); //} poll_timeout = wait_nsec / (1000*1000); wait_nanosleep = wait_nsec % (1000*1000); if (wait_nanosleep > NANOSLEEP_RESOLUTION) { struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = wait_nanosleep; clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); } int res = poll((struct pollfd*)&pfds, npfds, poll_timeout); //debug_log("midi_thread(%s): poll exit: %d", str->name, res); if (!midi->keep_walking) break; if (res < 0) { if (errno == EINTR) continue; error_log("midi_thread(%s) poll failed: %s", str->name, strerror(errno)); break; } // check wakeup pipe if (pfds[0].revents & ~POLLIN) break; if (pfds[0].revents & POLLIN) { char c; read(pfds[0].fd, &c, 1); } // add new ports while (jack_ringbuffer_read_space(str->midi.new_ports) >= sizeof(midi_port_t*) && str->midi.nports < MAX_PORTS) { midi_port_t *port; jack_ringbuffer_read(str->midi.new_ports, (char*)&port, sizeof(port)); str->midi.ports[str->midi.nports++] = port; debug_log("midi_thread(%s): added port %s", str->name, port->name); } // if (res == 0) // continue; // process ports proc.cur_time = 0; //jack_frame_time(midi->client); proc.next_time = NFRAMES_INF; for (rp = 0; rp < str->midi.nports; ++rp) { midi_port_t *port = str->midi.ports[rp]; proc.cur_time = jack_frame_time(midi->client); proc.port = port; proc.rpfds = &pfds[r]; proc.wpfds = &pfds[w]; proc.max_pfds = MAX_PFDS - w; r += port->npfds; if (!(str->process_midi)(&proc)) { port->state = PORT_REMOVED_FROM_MIDI; // this signals to jack thread continue; // this effectively removes port from array } w += port->npfds; if (rp != wp) str->midi.ports[wp] = port; ++wp; } if (str->midi.nports != wp) debug_log("midi_%s: nports %d -> %d", str->name, str->midi.nports, wp); str->midi.nports = wp; if (npfds != w) debug_log("midi_%s: npfds %d -> %d", str->name, npfds, w); npfds = w; /* * Input : ports do not set proc.next_time. * Output: port sets proc.next_time ONLY if it does not have queued data. * So, zero timeout will not cause busy-looping. */ if (proc.next_time < proc.cur_time) { debug_log("%s: late: next_time = %d, cur_time = %d", str->name, (int)proc.next_time, (int)proc.cur_time); wait_nsec = 0; // we are late } else if (proc.next_time != NFRAMES_INF) { jack_time_t wait_frames = proc.next_time - proc.cur_time; jack_nframes_t rate = jack_get_sample_rate(midi->client); wait_nsec = (wait_frames * (1000*1000*1000)) / rate; debug_log("midi_%s: timeout = %d", str->name, (int)wait_frames); } else wait_nsec = 1000*1000*1000; //debug_log("midi_thread(%s): wait_nsec = %lld", str->name, wait_nsec); } return NULL; }
void * midi_thread_function(void *arg) { int rc; struct sched_param rtparam; snd_seq_event_t *ev = 0; /* try to get low-priority real-time scheduling */ memset (&rtparam, 0, sizeof (rtparam)); rtparam.sched_priority = 1; /* just above SCHED_OTHER */ if ((rc = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) { if (rc == EPERM) { ghss_debug_rt(GDB_MIDI, " midi thread: no permission for SCHED_FIFO, continuing..."); } else { ghss_debug_rt(GDB_MIDI, " midi thread: error getting SCHED_FIFO, continuing..."); } } midi_thread_running = 1; do { /* while(!host_exiting) */ rc = poll(alsaClient_pfd, alsaClient_npfd, 500); if (rc <= 0) { if (rc < 0 && rc != EINTR) { ghss_debug_rt(GDB_MIDI, " midi thread: poll error: %s", strerror(errno)); usleep(500); } continue; } pthread_mutex_lock(&midiEventBufferMutex); do { if (snd_seq_event_input(alsaClient, &ev) > 0) { if (midiEventReadIndex == midiEventWriteIndex + 1) { ghss_debug_rt(GDB_MIDI, " midi thread: MIDI event buffer overflow!"); continue; } midiEventBuffer[midiEventWriteIndex] = *ev; ev = &midiEventBuffer[midiEventWriteIndex]; /* We don't need to handle EVENT_NOTE here, because ALSA won't ever deliver them on the sequencer queue -- it unbundles them into NOTE_ON and NOTE_OFF when they're dispatched. We would only need worry about them when retrieving MIDI events from some other source. */ if (ev->type == SND_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0) { ev->type = SND_SEQ_EVENT_NOTEOFF; } /* fprintf(stderr, "midi: flags %02x, tick %u, sec %u nsec %u\n", * ev->flags, ev->time.tick, ev->time.time.tv_sec, ev->time.time.tv_nsec); * fflush(stderr); */ /* -FIX- Ideally, we would use ev->time to figure out how long ago * this event was generated, and adjust accordingly. Instead, we * take the easy-and-fast route of just restamping the event with * the JACK rolling frame time at its arrival, which seems to work * pretty well.... */ /* -FIX- Rosegarden has example of setting up input queue, see * /t/src/example/rosegarden-CVS-20050109/sound/AlsaDriver.cpp */ /* -FIX- snd_seq_ioctl_get_queue_status, aka snd_seq_MUMBLE_get_queue_status() * should return current queue time, subtract event time from that to get offset * into past that event arrived? */ ev->time.tick = jack_frame_time(jackClient); /* fprintf(stderr, "midi: %u\n", ev->time.tick); fflush(stderr); */ ev->dest.client = 0; /* flag as from MIDI thread */ midiEventWriteIndex = (midiEventWriteIndex + 1) % EVENT_BUFFER_SIZE; } } while (snd_seq_event_input_pending(alsaClient, 0) > 0); pthread_mutex_unlock(&midiEventBufferMutex); } while(!host_exiting); midi_thread_running = 0; return NULL; }
uint32_t JackAudioSystem::time_stamp() { if( !_client ) return 0; return jack_frame_time(_client); }
static void a2j_input_event (struct a2j * self, snd_seq_event_t * alsa_event) { jack_midi_data_t data[MAX_EVENT_SIZE]; struct a2j_stream *str = &self->stream; long size; struct a2j_port *port; jack_nframes_t now; now = jack_frame_time (self->jack_client); if ((port = a2j_port_get(str->port_hash, alsa_event->source)) == NULL) { return; } /* * RPNs, NRPNs, Bank Change, etc. need special handling * but seems, ALSA does it for us already. */ snd_midi_event_reset_decode(str->codec); if ((size = snd_midi_event_decode(str->codec, data, sizeof(data), alsa_event))<0) { return; } // fixup NoteOn with vel 0 if ((data[0] & 0xF0) == 0x90 && data[2] == 0x00) { data[0] = 0x80 + (data[0] & 0x0F); data[2] = 0x40; } a2j_debug("input: %d bytes at event_frame=%u", (int)size, now); if (jack_ringbuffer_write_space(port->inbound_events) >= (sizeof(struct a2j_alsa_midi_event) + size)) { struct a2j_alsa_midi_event ev; char *ev_charp = (char*) &ev; size_t limit; size_t to_write = sizeof(ev); jack_ringbuffer_data_t vec[2]; jack_ringbuffer_get_write_vector( port->inbound_events, vec ); ev.time = now; ev.size = size; limit = (to_write > vec[0].len ? vec[0].len : to_write); if( limit ) { memcpy( vec[0].buf, ev_charp, limit ); to_write -= limit; ev_charp += limit; vec[0].buf += limit; vec[0].len -= limit; } if( to_write ) { memcpy( vec[1].buf, ev_charp, to_write ); vec[1].buf += to_write; vec[1].len -= to_write; } to_write = size; ev_charp = (char *)data; limit = (to_write > vec[0].len ? vec[0].len : to_write); if( limit ) memcpy( vec[0].buf, ev_charp, limit ); to_write -= limit; ev_charp += limit; if( to_write ) memcpy( vec[1].buf, ev_charp, to_write ); jack_ringbuffer_write_advance( port->inbound_events, sizeof(ev) + size ); } else { a2j_error ("MIDI data lost (incoming event buffer full): %ld bytes lost", size); } }
static int a2j_process (jack_nframes_t nframes, void * arg) { struct a2j* self = (struct a2j *) arg; struct a2j_stream * stream_ptr; int i; struct a2j_port ** port_ptr; struct a2j_port * port; if (g_freewheeling) { return 0; } self->cycle_start = jack_last_frame_time (self->jack_client); stream_ptr = &self->stream; a2j_add_ports (stream_ptr); // process ports for (i = 0 ; i < PORT_HASH_SIZE ; i++) { port_ptr = &stream_ptr->port_hash[i]; while (*port_ptr != NULL) { struct a2j_alsa_midi_event ev; jack_nframes_t now; jack_nframes_t one_period; char *ev_buf; port = *port_ptr; if (port->is_dead) { if (jack_ringbuffer_write_space (self->port_del) >= sizeof(port_ptr)) { a2j_debug("jack: removed port %s", port->name); *port_ptr = port->next; jack_ringbuffer_write (self->port_del, (char*)&port, sizeof(port)); } else { a2j_error ("port deletion lost - no space in event buffer!"); } port_ptr = &port->next; continue; } port->jack_buf = jack_port_get_buffer(port->jack_port, nframes); /* grab data queued by the ALSA input thread and write it into the JACK port buffer. it will delivered during the JACK period that this function is called from. */ /* first clear the JACK port buffer in preparation for new data */ // a2j_debug ("PORT: %s process input", jack_port_name (port->jack_port)); jack_midi_clear_buffer (port->jack_buf); now = jack_frame_time (self->jack_client); one_period = jack_get_buffer_size (self->jack_client); while (jack_ringbuffer_peek (port->inbound_events, (char*)&ev, sizeof(ev) ) == sizeof(ev) ) { jack_midi_data_t* buf; jack_nframes_t offset; if (ev.time >= self->cycle_start) { break; } //jack_ringbuffer_read_advance (port->inbound_events, sizeof (ev)); ev_buf = (char *) alloca( sizeof(ev) + ev.size ); if (jack_ringbuffer_peek (port->inbound_events, ev_buf, sizeof(ev) + ev.size ) != sizeof(ev) + ev.size) break; offset = self->cycle_start - ev.time; if (offset > one_period) { /* from a previous cycle, somehow. cram it in at the front */ offset = 0; } else { /* offset from start of the current cycle */ offset = one_period - offset; } a2j_debug ("event at %d offset %d", ev.time, offset); /* make sure there is space for it */ buf = jack_midi_event_reserve (port->jack_buf, offset, ev.size); if (buf) { /* grab the event */ memcpy( buf, ev_buf + sizeof(ev), ev.size ); } else { /* throw it away (no space) */ a2j_error ("threw away MIDI event - not reserved at time %d", ev.time); } jack_ringbuffer_read_advance (port->inbound_events, sizeof(ev) + ev.size); a2j_debug("input on %s: sucked %d bytes from inbound at %d", jack_port_name (port->jack_port), ev.size, ev.time); } port_ptr = &port->next; } } return 0; }
static int _frame_time(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj* const *objv) { _t *dp = (_t *)clientData; if (argc != 2) return fw_error_str(interp, "jack-client frame-time"); Tcl_SetObjResult(interp, Tcl_NewIntObj(jack_frame_time(dp->fw.client))); return TCL_OK; }
int process(jack_nframes_t nframes, void *arg) { struct guitarseq *guitarseq = arg; if(!guitarseq) { fprintf(stderr, "No guitarseq instance!\n"); return 1; } void* port_buf; jack_nframes_t now = jack_frame_time (guitarseq->jack_client); //Output port_buf = jack_port_get_buffer(guitarseq->out_port, nframes); jack_midi_clear_buffer(port_buf); while (1) { jack_nframes_t time; //TODO: Do a safer read, in case only part of the message is here if (!jack_ringbuffer_read (guitarseq->out_buffer, (char *)&time, sizeof(time))) { break; } // from the future? if (time >= now) { break; } // time it right jack_nframes_t offset = time - now + nframes - 1; // get the size of the event size_t size; jack_ringbuffer_read(guitarseq->out_buffer, (char *)&size, sizeof(size)); INFO("out event at %u%+d size %zu\n", now, offset, size); if (offset > nframes) // from the past, somehow. cram it in at the front offset = 0; // proceed to giving it to jack jack_midi_data_t *buffer = jack_midi_event_reserve (port_buf, offset, size); if(buffer) { jack_ringbuffer_read(guitarseq->out_buffer, (char *)buffer, size); } else { // throw it away :( TODO: find more jack_ringbuffer_read_advance (guitarseq->out_buffer, size); ERROR("threw away MIDI event - no space reserved at time %u offset %d\n",time,offset); } } // Input port_buf = jack_port_get_buffer(guitarseq->in_port, nframes); jack_nframes_t event_count = jack_midi_get_event_count(port_buf); for(jack_nframes_t i=0; i<event_count; i++) { jack_midi_event_t in_event; jack_midi_event_get(&in_event, port_buf, i); //adds a note to the ringbuffer if (jack_ringbuffer_write_space(guitarseq->in_buffer) >= sizeof(in_event.time)+sizeof(in_event.size)+in_event.size) { jack_ringbuffer_write(guitarseq->in_buffer, (char *)&in_event.time, sizeof(in_event.time)); jack_ringbuffer_write(guitarseq->in_buffer, (char *)&in_event.size, sizeof(in_event.size)); jack_ringbuffer_write(guitarseq->in_buffer, (char *)in_event.buffer, in_event.size); } else { ERROR("Couldn't write to ringbuffer at %u, %zu midi data bytes lost\n", in_event.time, in_event.size); } } return 0; }
jack_nframes_t luajack_frame_time(luajack_t *client) { cud_t *cud = get_cud(client); if(!cud) return 0; return jack_frame_time(cud->client); }