src/JackHandler.h
branchv_0
changeset 11 1c0b9981eb6d
parent 10 6aa881c10efd
child 12 f56620b95180
equal deleted inserted replaced
10:6aa881c10efd 11:1c0b9981eb6d
    50 class JackHandler : public relpipe::reader::handlers::RelationalReaderStringHandler {
    50 class JackHandler : public relpipe::reader::handlers::RelationalReaderStringHandler {
    51 private:
    51 private:
    52 	Configuration& configuration;
    52 	Configuration& configuration;
    53 	std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding
    53 	std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding
    54 
    54 
    55 	jack_client_t* jackClient = nullptr;
       
    56 	jack_port_t* jackPort = nullptr;
       
    57 	jack_ringbuffer_t* ringBuffer = nullptr;
       
    58 
       
    59 	std::atomic<bool> continueProcessing{true};
    55 	std::atomic<bool> continueProcessing{true};
    60 
       
    61 	const int RING_BUFFER_SIZE = 100;
       
    62 
    56 
    63 	/**
    57 	/**
    64 	 * Is passed through the ring buffer
    58 	 * Is passed through the ring buffer
    65 	 * from the relpipe-reading thread to the jack-writing thread (callback).
    59 	 * from the relpipe-reading thread to the jack-writing thread (callback).
    66 	 */
    60 	 */
    67 	struct MidiMessage {
    61 	struct MidiMessage {
    68 		uint8_t buffer[4096];
    62 		uint8_t buffer[4096];
    69 		uint32_t size;
    63 		uint32_t size;
    70 		uint32_t time;
    64 		uint32_t time;
    71 	};
    65 	};
       
    66 
       
    67 	/**
       
    68 	 * JACK callbacks (called from the real-time thread)
       
    69 	 */
       
    70 	class RealTimeContext {
       
    71 	public:
       
    72 		jack_client_t* jackClient = nullptr;
       
    73 		jack_port_t* jackPort = nullptr;
       
    74 		jack_ringbuffer_t* ringBuffer = nullptr;
       
    75 
       
    76 		const int RING_BUFFER_SIZE = 100;
       
    77 
       
    78 		int processCallback(jack_nframes_t frames) {
       
    79 			const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage);
       
    80 			void* jackPortBuffer = jack_port_get_buffer(jackPort, frames); // jack_port_get_buffer() must be called outside the loop, otherwise it will multiply the MIDI events
       
    81 			jack_midi_clear_buffer(jackPortBuffer); // TODO: clean buffer?
       
    82 			for (size_t i = 0; i < queuedMessages && i < frames; i++) {
       
    83 				// TODO: correct timing?
       
    84 				MidiMessage m;
       
    85 				jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage));
       
    86 				jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size);
       
    87 				memcpy(midiData, m.buffer, m.size);
       
    88 			}
       
    89 			return 0;
       
    90 		}
       
    91 
       
    92 		static int processCallback(jack_nframes_t frames, void* arg) {
       
    93 			return static_cast<RealTimeContext*> (arg)->processCallback(frames);
       
    94 		}
       
    95 
       
    96 	} realTimeContext;
    72 
    97 
    73 	/**
    98 	/**
    74 	 * Temporary storage of values read from relational input.
    99 	 * Temporary storage of values read from relational input.
    75 	 */
   100 	 */
    76 	class RelationContext {
   101 	class RelationContext {
   121 public:
   146 public:
   122 
   147 
   123 	JackHandler(Configuration& configuration) : configuration(configuration) {
   148 	JackHandler(Configuration& configuration) : configuration(configuration) {
   124 		// Initialize JACK connection:
   149 		// Initialize JACK connection:
   125 		std::string clientName = convertor.to_bytes(configuration.jackClientName);
   150 		std::string clientName = convertor.to_bytes(configuration.jackClientName);
   126 		jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr);
   151 		realTimeContext.jackClient = jack_client_open(clientName.c_str(), JackNullOption, nullptr);
   127 		if (jackClient == nullptr) throw JackException(L"Could not create JACK client.");
   152 		if (realTimeContext.jackClient == nullptr) throw JackException(L"Could not create JACK client.");
   128 
   153 
   129 		ringBuffer = jack_ringbuffer_create(RING_BUFFER_SIZE * sizeof (MidiMessage));
   154 		realTimeContext.ringBuffer = jack_ringbuffer_create(realTimeContext.RING_BUFFER_SIZE * sizeof (MidiMessage));
   130 
   155 
   131 		jack_set_process_callback(jackClient, relpipe::out::jack::dequeueMessages, this);
   156 		jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext);
   132 		// TODO: report also other events (connections etc.)
   157 		// TODO: report also other events (connections etc.)
   133 
   158 
   134 		jackPort = jack_port_register(jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
   159 		realTimeContext.jackPort = jack_port_register(realTimeContext.jackClient, "output", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
   135 		if (jackPort == nullptr) throw JackException(L"Could not register port.");
   160 		if (realTimeContext.jackPort == nullptr) throw JackException(L"Could not register port.");
   136 
   161 
   137 		if (mlockall(MCL_CURRENT | MCL_FUTURE)) fwprintf(stderr, L"Warning: Can not lock memory.\n");
   162 		if (mlockall(MCL_CURRENT | MCL_FUTURE)) fwprintf(stderr, L"Warning: Can not lock memory.\n");
   138 
   163 
   139 		int jackError = jack_activate(jackClient);
   164 		int jackError = jack_activate(realTimeContext.jackClient);
   140 		if (jackError) throw JackException(L"Could not activate client.");
   165 		if (jackError) throw JackException(L"Could not activate client.");
   141 
   166 
   142 		// Wait for a port connection, because it does not make much sense to send MIDI events nowhere:
   167 		// Wait for a port connection, because it does not make much sense to send MIDI events nowhere:
   143 		while (jack_port_connected(jackPort) < configuration.requiredJackConnections) usleep(10000);
   168 		while (jack_port_connected(realTimeContext.jackPort) < configuration.requiredJackConnections) usleep(10000);
   144 
   169 
   145 		// TODO: configurable auto-connection to another client/port
   170 		// TODO: configurable auto-connection to another client/port
   146 	}
   171 	}
   147 
   172 
   148 	void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override {
   173 	void startRelation(const relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override {
   160 			else if (attributeName == L"note_on") relationContext.indexOfNoteOn = i;
   185 			else if (attributeName == L"note_on") relationContext.indexOfNoteOn = i;
   161 			else if (attributeName == L"note_pitch") relationContext.indexOfNotePitch = i;
   186 			else if (attributeName == L"note_pitch") relationContext.indexOfNotePitch = i;
   162 			else if (attributeName == L"note_velocity") relationContext.indexOfNoteVelocity = i;
   187 			else if (attributeName == L"note_velocity") relationContext.indexOfNoteVelocity = i;
   163 			else if (attributeName == L"raw") relationContext.indexOfRaw = i;
   188 			else if (attributeName == L"raw") relationContext.indexOfRaw = i;
   164 		}
   189 		}
   165 		
   190 
   166 		// TODO: check also data types and skipt relation if important attributes are missing
   191 		// TODO: check also data types and skipt relation if important attributes are missing
   167 		// TODO: configurable relation name
   192 		// TODO: configurable relation name
   168 	}
   193 	}
   169 
   194 
   170 	void attribute(const relpipe::common::type::StringX& value) override {
   195 	void attribute(const relpipe::common::type::StringX& value) override {
   194 		}
   219 		}
   195 
   220 
   196 		relationContext.recordContext.attributeIndex++;
   221 		relationContext.recordContext.attributeIndex++;
   197 
   222 
   198 		if (relationContext.recordContext.attributeIndex == relationContext.attributeCount) {
   223 		if (relationContext.recordContext.attributeIndex == relationContext.attributeCount) {
   199 			if (jack_ringbuffer_write_space(ringBuffer) >= sizeof (MidiMessage)) {
   224 			if (jack_ringbuffer_write_space(realTimeContext.ringBuffer) >= sizeof (MidiMessage)) {
   200 				MidiMessage m;
   225 				MidiMessage m;
   201 
   226 
   202 				// TODO: convert relationContext.recordContext to m
   227 				// TODO: convert relationContext.recordContext to m
   203 				m.time = 0;
   228 				m.time = 0;
   204 				m.size = 3;
   229 				m.size = 3;
   205 				m.buffer[0] = 0x90;
   230 				m.buffer[0] = 0x90;
   206 				m.buffer[1] = 0x24;
   231 				m.buffer[1] = 0x24;
   207 				m.buffer[2] = 0x40;
   232 				m.buffer[2] = 0x40;
   208 
   233 
   209 				jack_ringbuffer_write(ringBuffer, (const char *) &m, sizeof (m));
   234 				jack_ringbuffer_write(realTimeContext.ringBuffer, (const char *) &m, sizeof (m));
   210 			} else {
   235 			} else {
   211 				fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n");
   236 				fwprintf(stderr, L"Error: ring buffer is full → skipping event.\n");
   212 			}
   237 			}
   213 
   238 
   214 			relationContext.recordContext = RelationContext::RecordContext();
   239 			relationContext.recordContext = RelationContext::RecordContext();
   218 
   243 
   219 	void endOfPipe() {
   244 	void endOfPipe() {
   220 		// TODO: send optional (configurable) MIDI events
   245 		// TODO: send optional (configurable) MIDI events
   221 
   246 
   222 		// Wait until the ring buffer is empty
   247 		// Wait until the ring buffer is empty
   223 		while (continueProcessing && jack_ringbuffer_read_space(ringBuffer)) usleep(1000);
   248 		while (continueProcessing && jack_ringbuffer_read_space(realTimeContext.ringBuffer)) usleep(1000);
   224 		usleep(1000000);
   249 		usleep(1000000);
   225 		// TODO: better waiting (ringBuffer might be empty, but events have not been sent to JACK yet)
   250 		// TODO: better waiting (ringBuffer might be empty, but events have not been sent to JACK yet)
   226 		// TODO: optionally mute all; probably enabled by default
   251 		// TODO: optionally mute all; probably enabled by default
   227 	}
   252 	}
   228 
   253 
   229 	/**
       
   230 	 * TODO: use separate class/instance for JACK callbacks to minimize scope and prevent mistakes.
       
   231 	 */
       
   232 	int dequeueMessages(jack_nframes_t frames) {
       
   233 		const size_t queuedMessages = jack_ringbuffer_read_space(ringBuffer) / sizeof (MidiMessage);
       
   234 		void* jackPortBuffer = jack_port_get_buffer(jackPort, frames); // jack_port_get_buffer() must be called outside the loop, otherwise it will multiply the MIDI events
       
   235 		jack_midi_clear_buffer(jackPortBuffer); // TODO: clean buffer?
       
   236 		for (size_t i = 0; i < queuedMessages && i < frames; i++) {
       
   237 			// TODO: correct timing?
       
   238 			MidiMessage m;
       
   239 			jack_ringbuffer_read(ringBuffer, (char*) &m, sizeof (MidiMessage));
       
   240 			jack_midi_data_t* midiData = jack_midi_event_reserve(jackPortBuffer, m.time, m.size);
       
   241 			memcpy(midiData, m.buffer, m.size);
       
   242 		}
       
   243 		return 0;
       
   244 	}
       
   245 
       
   246 	void finish(int sig) {
   254 	void finish(int sig) {
   247 		continueProcessing = false;
   255 		continueProcessing = false;
   248 	}
   256 	}
   249 
   257 
   250 	virtual ~JackHandler() {
   258 	virtual ~JackHandler() {
   251 		// Close JACK connection:
   259 		// Close JACK connection:
   252 		jack_deactivate(jackClient);
   260 		jack_deactivate(realTimeContext.jackClient);
   253 		jack_client_close(jackClient);
   261 		jack_client_close(realTimeContext.jackClient);
   254 		jack_ringbuffer_free(ringBuffer);
   262 		jack_ringbuffer_free(realTimeContext.ringBuffer);
   255 	}
   263 	}
   256 
   264 
   257 };
   265 };
   258 
       
   259 int dequeueMessages(jack_nframes_t frames, void* arg) {
       
   260 	JackHandler* instance = (JackHandler*) arg;
       
   261 	return instance->dequeueMessages(frames);
       
   262 }
       
   263 
   266 
   264 }
   267 }
   265 }
   268 }
   266 }
   269 }