mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	module-ffado: implement MIDI send and receive
This commit is contained in:
		
							parent
							
								
									f3d9b823bd
								
							
						
					
					
						commit
						60d68e0d66
					
				
					 1 changed files with 141 additions and 11 deletions
				
			
		| 
						 | 
					@ -143,6 +143,7 @@ struct port {
 | 
				
			||||||
	struct spa_latency_info latency[2];
 | 
						struct spa_latency_info latency[2];
 | 
				
			||||||
	bool latency_changed[2];
 | 
						bool latency_changed[2];
 | 
				
			||||||
	unsigned int is_midi:1;
 | 
						unsigned int is_midi:1;
 | 
				
			||||||
 | 
						unsigned int cleared:1;
 | 
				
			||||||
	void *buffer;
 | 
						void *buffer;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,6 +217,13 @@ struct impl {
 | 
				
			||||||
	uint32_t ffado_xrun;
 | 
						uint32_t ffado_xrun;
 | 
				
			||||||
	uint32_t frame_time;
 | 
						uint32_t frame_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8_t event_byte;
 | 
				
			||||||
 | 
						uint8_t event_type;
 | 
				
			||||||
 | 
						uint32_t buffer_time;
 | 
				
			||||||
 | 
						uint8_t buffer[512];
 | 
				
			||||||
 | 
						uint32_t buffer_pos;
 | 
				
			||||||
 | 
						int buffer_pending;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned int do_disconnect:1;
 | 
						unsigned int do_disconnect:1;
 | 
				
			||||||
	unsigned int fix_midi:1;
 | 
						unsigned int fix_midi:1;
 | 
				
			||||||
	unsigned int started:1;
 | 
						unsigned int started:1;
 | 
				
			||||||
| 
						 | 
					@ -251,6 +259,14 @@ static inline void do_volume(float *dst, const float *src, struct volume *vol, u
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void clear_port_buffer(struct port *p, uint32_t n_samples)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!p->cleared) {
 | 
				
			||||||
 | 
							memset(p->buffer, 0, n_samples * sizeof(uint32_t));
 | 
				
			||||||
 | 
							p->cleared = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void fix_midi_event(uint8_t *data, size_t size)
 | 
					static inline void fix_midi_event(uint8_t *data, size_t size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* fixup NoteOn with vel 0 */
 | 
						/* fixup NoteOn with vel 0 */
 | 
				
			||||||
| 
						 | 
					@ -260,11 +276,13 @@ static inline void fix_midi_event(uint8_t *data, size_t size)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void midi_to_ffado(struct impl *impl, float *dst, float *src, uint32_t n_samples)
 | 
					static void midi_to_ffado(struct impl *impl, struct port *p, float *src, uint32_t n_samples)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct spa_pod *pod;
 | 
						struct spa_pod *pod;
 | 
				
			||||||
	struct spa_pod_sequence *seq;
 | 
						struct spa_pod_sequence *seq;
 | 
				
			||||||
	struct spa_pod_control *c;
 | 
						struct spa_pod_control *c;
 | 
				
			||||||
 | 
						uint32_t index = 0;
 | 
				
			||||||
 | 
						uint32_t *dst = p->buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (src == NULL)
 | 
						if (src == NULL)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -276,6 +294,8 @@ static void midi_to_ffado(struct impl *impl, float *dst, float *src, uint32_t n_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seq = (struct spa_pod_sequence*)pod;
 | 
						seq = (struct spa_pod_sequence*)pod;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clear_port_buffer(p, n_samples);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SPA_POD_SEQUENCE_FOREACH(seq, c) {
 | 
						SPA_POD_SEQUENCE_FOREACH(seq, c) {
 | 
				
			||||||
		switch(c->type) {
 | 
							switch(c->type) {
 | 
				
			||||||
		case SPA_CONTROL_Midi:
 | 
							case SPA_CONTROL_Midi:
 | 
				
			||||||
| 
						 | 
					@ -283,9 +303,12 @@ static void midi_to_ffado(struct impl *impl, float *dst, float *src, uint32_t n_
 | 
				
			||||||
			uint8_t *data = SPA_POD_BODY(&c->value);
 | 
								uint8_t *data = SPA_POD_BODY(&c->value);
 | 
				
			||||||
			size_t size = SPA_POD_BODY_SIZE(&c->value);
 | 
								size_t size = SPA_POD_BODY_SIZE(&c->value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (impl->fix_midi)
 | 
								if (index < c->offset)
 | 
				
			||||||
				fix_midi_event(data, size);
 | 
									index = SPA_ROUND_UP_N(c->offset, 8);
 | 
				
			||||||
 | 
								for (uint32_t i = 0; i < size; i++) {
 | 
				
			||||||
 | 
									dst[index] = 0x01000000 | (uint32_t) data[i];
 | 
				
			||||||
 | 
									index += 8;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
| 
						 | 
					@ -294,17 +317,122 @@ static void midi_to_ffado(struct impl *impl, float *dst, float *src, uint32_t n_
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ffado_to_midi(float *dst, float *src, uint32_t size)
 | 
					static int take_bytes(struct impl *impl, uint32_t *frame, uint8_t **bytes, size_t *size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (impl->buffer_pos == 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						*frame = impl->buffer_time;
 | 
				
			||||||
 | 
						*bytes = impl->buffer;
 | 
				
			||||||
 | 
						*size = impl->buffer_pos;
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const int status_len[] = {
 | 
				
			||||||
 | 
						2,		/* noteoff */
 | 
				
			||||||
 | 
						2,		/* noteon */
 | 
				
			||||||
 | 
						2,		/* keypress */
 | 
				
			||||||
 | 
						2,		/* controller */
 | 
				
			||||||
 | 
						1,		/* pgmchange */
 | 
				
			||||||
 | 
						1,		/* chanpress */
 | 
				
			||||||
 | 
						2,		/* pitchbend */
 | 
				
			||||||
 | 
						-1,		/* invalid */
 | 
				
			||||||
 | 
						1,		/* sysex 0xf0 */
 | 
				
			||||||
 | 
						1,		/* qframe 0xf1 */
 | 
				
			||||||
 | 
						2,		/* songpos 0xf2 */
 | 
				
			||||||
 | 
						1,		/* songsel 0xf3 */
 | 
				
			||||||
 | 
						-1,		/* none 0xf4 */
 | 
				
			||||||
 | 
						-1,		/* none 0xf5 */
 | 
				
			||||||
 | 
						0,		/* tune request 0xf6 */
 | 
				
			||||||
 | 
						-1,		/* none 0xf7 */
 | 
				
			||||||
 | 
						0,		/* clock 0xf8 */
 | 
				
			||||||
 | 
						-1,		/* none 0xf9 */
 | 
				
			||||||
 | 
						0,		/* start 0xfa */
 | 
				
			||||||
 | 
						0,		/* continue 0xfb */
 | 
				
			||||||
 | 
						0,		/* stop 0xfc */
 | 
				
			||||||
 | 
						-1,		/* none 0xfd */
 | 
				
			||||||
 | 
						0,		/* sensing 0xfe */
 | 
				
			||||||
 | 
						0,		/* reset 0xff */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int process_byte(struct impl *impl, uint32_t time, uint8_t byte,
 | 
				
			||||||
 | 
							uint32_t *frame, uint8_t **bytes, size_t *size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int res = 0;
 | 
				
			||||||
 | 
						if (byte >= 0xf8) {
 | 
				
			||||||
 | 
							if (byte == 0xfd) {
 | 
				
			||||||
 | 
								pw_log_warn("droping invalid MIDI status bytes %08x", byte);
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							impl->event_byte = byte;
 | 
				
			||||||
 | 
							*frame = time;
 | 
				
			||||||
 | 
							*bytes = &impl->event_byte;
 | 
				
			||||||
 | 
							*size = 1;
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if ((byte & 0x80) && (byte != 0xf7 || impl->event_type != 8)) {
 | 
				
			||||||
 | 
							/* new command */
 | 
				
			||||||
 | 
							impl->buffer[0] = byte;
 | 
				
			||||||
 | 
							impl->buffer_time = time;
 | 
				
			||||||
 | 
							if ((byte & 0xf0) == 0xf0) /* system message */
 | 
				
			||||||
 | 
								impl->event_type = (byte & 0x0f) + 8;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								impl->event_type = (byte >> 4) & 0x07;
 | 
				
			||||||
 | 
							impl->buffer_pos = 1;
 | 
				
			||||||
 | 
							impl->buffer_pending = status_len[impl->event_type];
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							 if (impl->buffer_pending > 0) {
 | 
				
			||||||
 | 
								/* rest of command */
 | 
				
			||||||
 | 
								if (impl->buffer_pos < sizeof(impl->buffer))
 | 
				
			||||||
 | 
									impl->buffer[impl->buffer_pos++] = byte;
 | 
				
			||||||
 | 
								if (impl->event_type != 8)
 | 
				
			||||||
 | 
									impl->buffer_pending--;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								/* running status */
 | 
				
			||||||
 | 
								impl->buffer[1] = byte;
 | 
				
			||||||
 | 
								impl->buffer_time = time;
 | 
				
			||||||
 | 
								impl->buffer_pending = status_len[impl->event_type] - 1;
 | 
				
			||||||
 | 
								impl->buffer_pos = 2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (impl->buffer_pending == 0) {
 | 
				
			||||||
 | 
							res = take_bytes(impl, frame, bytes, size);
 | 
				
			||||||
 | 
							if (impl->event_type >= 8)
 | 
				
			||||||
 | 
								impl->event_type = 7;
 | 
				
			||||||
 | 
						} else if (impl->event_type == 8) {
 | 
				
			||||||
 | 
							if (byte == 0xf7 || impl->buffer_pos >= sizeof(impl->buffer)) {
 | 
				
			||||||
 | 
								res = take_bytes(impl, frame, bytes, size);
 | 
				
			||||||
 | 
								impl->buffer_pos = 0;
 | 
				
			||||||
 | 
								if (byte == 0xf7) {
 | 
				
			||||||
 | 
									impl->buffer_pending = 0;
 | 
				
			||||||
 | 
									impl->event_type = 7;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ffado_to_midi(struct impl *impl, float *dst, uint32_t *src, uint32_t size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct spa_pod_builder b = { 0, };
 | 
						struct spa_pod_builder b = { 0, };
 | 
				
			||||||
	uint32_t i, count;
 | 
						uint32_t i, count;
 | 
				
			||||||
	struct spa_pod_frame f;
 | 
						struct spa_pod_frame f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	count = src ? 0 : 0;
 | 
						count = src ? size : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_pod_builder_init(&b, dst, size);
 | 
						spa_pod_builder_init(&b, dst, size);
 | 
				
			||||||
	spa_pod_builder_push_sequence(&b, &f, 0);
 | 
						spa_pod_builder_push_sequence(&b, &f, 0);
 | 
				
			||||||
	for (i = 0; i < count; i++) {
 | 
						for (i = 0; i < count; i++) {
 | 
				
			||||||
 | 
							uint32_t data = src[i], frame;
 | 
				
			||||||
 | 
							uint8_t *bytes;
 | 
				
			||||||
 | 
							size_t size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((data & 0xff000000) == 0)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (process_byte(impl, i, data & 0xff, &frame, &bytes, &size)) {
 | 
				
			||||||
 | 
								spa_pod_builder_control(&b, frame, SPA_CONTROL_Midi);
 | 
				
			||||||
 | 
						                spa_pod_builder_bytes(&b, bytes, size);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
	spa_pod_builder_pop(&b, &f);
 | 
						spa_pod_builder_pop(&b, &f);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -415,14 +543,16 @@ static void sink_process(void *d, struct spa_io_position *position)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		src = pw_filter_get_dsp_buffer(p, n_samples);
 | 
							src = pw_filter_get_dsp_buffer(p, n_samples);
 | 
				
			||||||
		if (src == NULL) {
 | 
							if (src == NULL) {
 | 
				
			||||||
			memset(p->buffer, 0, n_samples * sizeof(float));
 | 
								clear_port_buffer(p, n_samples);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (SPA_UNLIKELY(p->is_midi))
 | 
							if (SPA_UNLIKELY(p->is_midi))
 | 
				
			||||||
			midi_to_ffado(impl, p->buffer, src, n_samples);
 | 
								midi_to_ffado(impl, p, src, n_samples);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			do_volume(p->buffer, src, &s->volume, i, n_samples);
 | 
								do_volume(p->buffer, src, &s->volume, i, n_samples);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							p->cleared = false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ffado_streaming_transfer_playback_buffers(impl->dev);
 | 
						ffado_streaming_transfer_playback_buffers(impl->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -441,7 +571,7 @@ static void silence_playback(struct impl *impl)
 | 
				
			||||||
	for (i = 0; i < s->n_ports; i++) {
 | 
						for (i = 0; i < s->n_ports; i++) {
 | 
				
			||||||
		struct port *p = s->ports[i];
 | 
							struct port *p = s->ports[i];
 | 
				
			||||||
		if (p != NULL)
 | 
							if (p != NULL)
 | 
				
			||||||
			memset(p->buffer, 0, impl->period_size * sizeof(float));
 | 
								clear_port_buffer(p, impl->period_size);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ffado_streaming_transfer_playback_buffers(impl->dev);
 | 
						ffado_streaming_transfer_playback_buffers(impl->dev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -477,7 +607,7 @@ static void source_process(void *d, struct spa_io_position *position)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (SPA_UNLIKELY(p->is_midi))
 | 
							if (SPA_UNLIKELY(p->is_midi))
 | 
				
			||||||
			ffado_to_midi(dst, p->buffer, n_samples);
 | 
								ffado_to_midi(impl, dst, p->buffer, n_samples);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			do_volume(dst, p->buffer, &s->volume, i, n_samples);
 | 
								do_volume(dst, p->buffer, &s->volume, i, n_samples);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue