[PATCH] alsa-lib: issues in snd_seq_change_bit and some enhancements

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello hackers,

I found two issues in sequencer part of alsa-lib while working with the
python binding:
1) snd_seq_change_bit is not working as expected.
2) There is no sane way to clear the event_filter of
snd_seq_client_info_t structure. Because the structure is opaque, we
cannot memset to zero because the user doesn't know the size of the
bitmap.

The first attached patch is a demo of both issues. For 1) please apply
snd_seq_change_bit.txt patch. For 2) I propose to clear the bitmap if
NULL is passed, see snd_seq_client_info_set_event_filter.txt patch.



Also, I think it makes no sense that the bitmap is returned or expected
to be given in both snd_seq_client_info_{set,get}_event_filter; the
client_info structure is opaque and the alsa-lib user can't/shouldn't
known the bitmap size. The only sane way is to use the middle interface
function snd_seq_set_client_event_filter(), but it only can be used for
an opened sequencer's client_info.

So, the last patch is a proposal for changing this:
- mark as deprecated both snd_seq_client_info_{set,get}_event_filter
- snd_seq_client_info_event_filter_clear() clears the event_filter
- snd_seq_client_info_event_filter_add() add an event to the event
filter
- snd_seq_client_info_event_filter_del() removes an event from the event
filter
- snd_seq_client_info_event_filter_check() test if an event is present
in the filtering

Any comments? Thanks!



-- 
Aldrin Martoq <amartoq@xxxxxxxxxxxxx>
diff --git a/test/Makefile.am b/test/Makefile.am
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,6 +1,6 @@ check_PROGRAMS=control pcm pcm_min laten
 check_PROGRAMS=control pcm pcm_min latency seq \
 	       playmidi1 timer rawmidi midiloop \
-	       oldapi queue_timer namehint
+	       oldapi queue_timer namehint event_filter
 
 control_LDADD=../src/libasound.la
 pcm_LDADD=../src/libasound.la
@@ -15,6 +15,7 @@ queue_timer_LDADD=../src/libasound.la
 queue_timer_LDADD=../src/libasound.la
 namehint_LDADD=../src/libasound.la
 code_CFLAGS=-Wall -pipe -g -O2
+event_filter_LDADD=../src/libasound.la
 
 INCLUDES=-I$(top_srcdir)/include
 AM_CFLAGS=-Wall -pipe -g
diff --git a/test/event_filter.c b/test/event_filter.c
new file mode 100644
--- /dev/null
+++ b/test/event_filter.c
@@ -0,0 +1,49 @@
+#include <alsa/asoundlib.h>
+
+int main(void) {
+	snd_seq_t *seq;
+	snd_seq_client_info_t *client_info;
+	const unsigned char *event_filter;
+	int isnote;
+	int ispgmc;
+	int oldnote;
+
+	snd_seq_client_info_alloca(&client_info);
+	snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
+
+	snd_seq_get_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	printf("Initial event_filter=0x%p\n", event_filter);
+
+	snd_seq_set_client_event_filter(seq, SND_SEQ_EVENT_NOTEON);
+	snd_seq_get_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	isnote = snd_seq_get_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	printf("after snd_seq_set_client_event (EVENT_NOTEON)    : "
+		"event_filter=0x%p isnote=%d\n",
+		event_filter, isnote);
+	oldnote = snd_seq_change_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	isnote = snd_seq_get_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	printf("after snd_seq_change_bit                         : "
+		"event_filter=0x%p isnote=%d oldnote=%d\n ==> %s %s\n",
+		event_filter, isnote, oldnote, "snd_seq_change_bit",
+		(isnote == 0 ? "changed" : "ERROR: NOT CHANGED"));
+
+	snd_seq_get_client_info(seq, client_info);
+	snd_seq_client_info_set_event_filter(client_info, NULL);
+	snd_seq_set_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	printf("after snd_seq_client_info_set_event_filter(NULL) : "
+		"event_filter=0x%p\n",
+		event_filter);
+	
+	snd_seq_set_client_event_filter(seq, SND_SEQ_EVENT_PGMCHANGE);
+	snd_seq_get_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	isnote = snd_seq_get_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	ispgmc = snd_seq_get_bit(SND_SEQ_EVENT_PGMCHANGE, (void *) event_filter);
+	printf("after snd_seq_set_client_event (EVENT_PGMCHANGE) : "
+		"event_filter=0x%p isnote=%d ispgmc=%d\n ==> %s %s\n",
+		event_filter, isnote, ispgmc, "snd_seq_client_info_set_event_filter(NULL)",
+		(isnote == 0 ? "cleared" : "ERROR: NOT CLEARED"));
+}
diff --git a/src/seq/seq.c b/src/seq/seq.c
--- a/src/seq/seq.c
+++ b/src/seq/seq.c
@@ -4670,7 +4670,7 @@ int snd_seq_change_bit(int nr, void *arr
 	int result;
 
 	result = ((((unsigned int *)array)[nr >> 5]) & (1UL << (nr & 31))) ? 1 : 0;
-	((unsigned int *)array)[nr >> 5] |= 1UL << (nr & 31);
+	((unsigned int *)array)[nr >> 5] ^= 1UL << (nr & 31);
 	return result;
 }
 
diff --git a/src/seq/seq.c b/src/seq/seq.c
--- a/src/seq/seq.c
+++ b/src/seq/seq.c
@@ -1633,9 +1633,10 @@ void snd_seq_client_info_set_event_filte
 void snd_seq_client_info_set_event_filter(snd_seq_client_info_t *info, unsigned char *filter)
 {
 	assert(info);
-	if (! filter)
+	if (! filter) {
 		info->filter &= ~SNDRV_SEQ_FILTER_USE_EVENT;
-	else {
+		memset(info->event_filter, 0, sizeof(info->event_filter));
+	} else {
 		info->filter |= SNDRV_SEQ_FILTER_USE_EVENT;
 		memcpy(info->event_filter, filter, sizeof(info->event_filter));
 	}
diff --git a/include/seq.h b/include/seq.h
--- a/include/seq.h
+++ b/include/seq.h
@@ -152,6 +152,11 @@ void snd_seq_client_info_set_broadcast_f
 void snd_seq_client_info_set_broadcast_filter(snd_seq_client_info_t *info, int val);
 void snd_seq_client_info_set_error_bounce(snd_seq_client_info_t *info, int val);
 void snd_seq_client_info_set_event_filter(snd_seq_client_info_t *info, unsigned char *filter);
+
+void snd_seq_client_info_event_filter_clear(snd_seq_client_info_t *info);
+void snd_seq_client_info_event_filter_add(snd_seq_client_info_t *info, int event_type);
+void snd_seq_client_info_event_filter_del(snd_seq_client_info_t *info, int event_type);
+int snd_seq_client_info_event_filter_check(snd_seq_client_info_t *info, int event_type);
 
 int snd_seq_get_client_info(snd_seq_t *handle, snd_seq_client_info_t *info);
 int snd_seq_get_any_client_info(snd_seq_t *handle, int client, snd_seq_client_info_t *info);
@@ -575,6 +580,7 @@ int snd_seq_remove_events(snd_seq_t *han
  */
 
 void snd_seq_set_bit(int nr, void *array);
+void snd_seq_unset_bit(int nr, void *array);
 int snd_seq_change_bit(int nr, void *array);
 int snd_seq_get_bit(int nr, void *array);
 
diff --git a/src/seq/seq.c b/src/seq/seq.c
--- a/src/seq/seq.c
+++ b/src/seq/seq.c
@@ -1522,11 +1522,16 @@ int snd_seq_client_info_get_error_bounce
 }
 
 /**
- * \brief Get the event filter bitmap of a client_info container
+ * \brief (DEPRECATED) Get the event filter bitmap of a client_info container
  * \param info client_info container
  * \return NULL if no event filter, or pointer to event filter bitmap
  *
- * \sa snd_seq_get_client_info(), snd_seq_client_info_set_event_filter()
+ * \sa snd_seq_client_info_event_filter_add(),
+ *     snd_seq_client_info_event_filter_del(),
+ *     snd_seq_client_info_event_filter_check(),
+ *     snd_seq_client_info_event_filter_clear(),
+ *     snd_seq_get_client_info(),
+ *     snd_seq_client_info_set_event_filter()
  */
 const unsigned char *snd_seq_client_info_get_event_filter(const snd_seq_client_info_t *info)
 {
@@ -1623,23 +1628,110 @@ void snd_seq_client_info_set_error_bounc
 }
 
 /**
- * \brief Set the event filter bitmap of a client_info container
+ * \brief (DEPRECATED) Set the event filter bitmap of a client_info container
  * \param info client_info container
- * \param filter event filter bitmap
- *
- * \sa snd_seq_get_client_info(), snd_seq_client_info_get_event_filger(),
+ * \param filter event filter bitmap, pass NULL for clearing the bitmap and disable event filtering
+ *
+ * \sa snd_seq_client_info_event_filter_add(),
+ *     snd_seq_client_info_event_filter_del(),
+ *     snd_seq_client_info_event_filter_check(),
+ *     snd_seq_client_info_event_filter_clear(),
+ *     snd_seq_get_client_info(),
+ *     snd_seq_client_info_get_event_filter(),
  *     snd_seq_set_client_event_filter()
  */
 void snd_seq_client_info_set_event_filter(snd_seq_client_info_t *info, unsigned char *filter)
 {
 	assert(info);
-	if (! filter)
+	if (! filter) {
 		info->filter &= ~SNDRV_SEQ_FILTER_USE_EVENT;
-	else {
+		memset(info->event_filter, 0, sizeof(info->event_filter));
+	} else {
 		info->filter |= SNDRV_SEQ_FILTER_USE_EVENT;
 		memcpy(info->event_filter, filter, sizeof(info->event_filter));
 	}
 }
+
+/**
+ * \brief Disable event filtering of a client_info container
+ * \param info client_info container
+ *
+ * Remove all event types added with #snd_seq_client_info_event_filter_add and clear
+ * the event filtering flag of this client_info container.
+ * 
+ * \sa snd_seq_client_info_event_filter_add(),
+ *     snd_seq_client_info_event_filter_del(),
+ *     snd_seq_client_info_event_filter_check(),
+ *     snd_seq_get_client_info(),
+ *     snd_seq_set_client_info()
+ */
+void snd_seq_client_info_event_filter_clear(snd_seq_client_info_t *info)
+{
+	assert(info);
+	info->filter &= ~SNDRV_SEQ_FILTER_USE_EVENT;
+	memset(info->event_filter, 0, sizeof(info->event_filter));
+}
+
+/**
+ * \brief Add an event type to the event filtering of a client_info container
+ * \param info client_info container
+ * \param event_type event type to be added
+ * 
+ * Set the event filtering flag of this client_info and add the specified event type to the 
+ * filter bitmap of this client_info container.
+ *
+ * \sa snd_seq_get_client_info(),
+ *     snd_seq_set_client_info(),
+ *     snd_seq_client_info_event_filter_del(),
+ *     snd_seq_client_info_event_filter_check(),
+ *     snd_seq_client_info_event_filter_clear()
+ */
+void snd_seq_client_info_event_filter_add(snd_seq_client_info_t *info, int event_type)
+{
+	assert(info);
+	info->filter |= SNDRV_SEQ_FILTER_USE_EVENT;
+	snd_seq_set_bit(event_type, info->event_filter);
+}
+
+/**
+ * \brief Remove an event type from the event filtering of a client_info container
+ * \param info client_info container
+ * \param event_type event type to be removed
+ *
+ * Removes the specified event from the filter bitmap of this client_info container. It will
+ * not clear the event filtering flag, for this see #snd_seq_client_info_event_filter_clear
+ *
+ * \sa snd_seq_get_client_info(),
+ *     snd_seq_set_client_info(),
+ *     snd_seq_client_info_event_filter_add(),
+ *     snd_seq_client_info_event_filter_check(),
+ *     snd_seq_client_info_event_filter_clear()
+ */
+void snd_seq_client_info_event_filter_del(snd_seq_client_info_t *info, int event_type)
+{
+	assert(info);
+	snd_seq_unset_bit(event_type, info->event_filter);
+}
+
+/**
+ * \brief Check if an event type is present in the event filtering of a client_info container
+ * \param info client_info container
+ * \param event_type event_type to be checked
+ * \return 1 if the event type is present, 0 otherwise
+ *
+ * Test if the event type is in the filter bitamp of this client_info container.
+ *
+ * \sa snd_seq_get_client_info(),
+ *     snd_seq_set_client_info(),
+ *     snd_seq_client_info_event_filter_add(),
+ *     snd_seq_client_info_event_filter_del(),
+ *     snd_seq_client_info_event_filter_clear()
+ */
+int snd_seq_client_info_event_filter_check(snd_seq_client_info_t *info, int event_type)
+{
+	assert(info);
+	return snd_seq_get_bit(event_type, info->event_filter);
+} 
 
 
 /**
@@ -4663,6 +4755,14 @@ void snd_seq_set_bit(int nr, void *array
 }
 
 /**
+ * \brief unset a bit flag
+ */
+void snd_seq_unset_bit(int nr, void *array)
+{
+	((unsigned int *)array)[nr >> 5] &= ~(1UL << (nr & 31));
+}
+
+/**
  * \brief change a bit flag
  */
 int snd_seq_change_bit(int nr, void *array)
@@ -4670,7 +4770,7 @@ int snd_seq_change_bit(int nr, void *arr
 	int result;
 
 	result = ((((unsigned int *)array)[nr >> 5]) & (1UL << (nr & 31))) ? 1 : 0;
-	((unsigned int *)array)[nr >> 5] |= 1UL << (nr & 31);
+	((unsigned int *)array)[nr >> 5] ^= 1UL << (nr & 31);
 	return result;
 }
 
diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c
--- a/src/seq/seqmid.c
+++ b/src/seq/seqmid.c
@@ -251,8 +251,7 @@ int snd_seq_set_client_event_filter(snd_
 
 	if ((err = snd_seq_get_client_info(seq, &info)) < 0)
 		return err;
-	info.filter |= SNDRV_SEQ_FILTER_USE_EVENT;
-	snd_seq_set_bit(event_type, info.event_filter);
+	snd_seq_client_info_event_filter_add(&info, event_type);
 	return snd_seq_set_client_info(seq, &info);
 }
 
diff --git a/test/Makefile.am b/test/Makefile.am
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,6 +1,6 @@ check_PROGRAMS=control pcm pcm_min laten
 check_PROGRAMS=control pcm pcm_min latency seq \
 	       playmidi1 timer rawmidi midiloop \
-	       oldapi queue_timer namehint
+	       oldapi queue_timer namehint event_filter
 
 control_LDADD=../src/libasound.la
 pcm_LDADD=../src/libasound.la
@@ -15,6 +15,7 @@ queue_timer_LDADD=../src/libasound.la
 queue_timer_LDADD=../src/libasound.la
 namehint_LDADD=../src/libasound.la
 code_CFLAGS=-Wall -pipe -g -O2
+event_filter_LDADD=../src/libasound.la
 
 INCLUDES=-I$(top_srcdir)/include
 AM_CFLAGS=-Wall -pipe -g
diff --git a/test/event_filter.c b/test/event_filter.c
new file mode 100644
--- /dev/null
+++ b/test/event_filter.c
@@ -0,0 +1,86 @@
+#include <alsa/asoundlib.h>
+
+void dump_event_filter(snd_seq_client_info_t *client_info) {
+	int i, b;
+
+	for (i = 0; i <= 255;) {
+		b = snd_seq_client_info_event_filter_check(client_info, i);
+		i++;
+		printf("%c%s%s", (b ? 'X' : '.'),
+			(i % 8 == 0 ? " " : ""),
+			(i % 32 == 0 ? "\n" : ""));
+	}
+	printf("\n");
+}
+
+int main(void) {
+	snd_seq_t *seq;
+	snd_seq_client_info_t *client_info;
+	const unsigned char *event_filter;
+	int isnote;
+	int ispgmc;
+	int oldnote;
+
+	snd_seq_client_info_alloca(&client_info);
+	snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
+
+	snd_seq_get_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	printf("Initial event_filter=0x%p\n", event_filter);
+
+	snd_seq_set_client_event_filter(seq, SND_SEQ_EVENT_NOTEON);
+	snd_seq_get_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	isnote = snd_seq_get_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	printf("after snd_seq_set_client_event (EVENT_NOTEON)    : "
+		"event_filter=0x%p isnote=%d\n",
+		event_filter, isnote);
+	oldnote = snd_seq_change_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	isnote = snd_seq_get_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	printf("after snd_seq_change_bit                         : "
+		"event_filter=0x%p isnote=%d oldnote=%d\n ==> %s %s\n",
+		event_filter, isnote, oldnote, "snd_seq_change_bit",
+		(isnote == 0 ? "changed" : "ERROR: NOT CHANGED"));
+
+	snd_seq_get_client_info(seq, client_info);
+	snd_seq_client_info_set_event_filter(client_info, NULL);
+	snd_seq_set_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	printf("after snd_seq_client_info_set_event_filter(NULL) : "
+		"event_filter=0x%p\n",
+		event_filter);
+	
+	snd_seq_set_client_event_filter(seq, SND_SEQ_EVENT_PGMCHANGE);
+	snd_seq_get_client_info(seq, client_info);
+	event_filter = snd_seq_client_info_get_event_filter(client_info);
+	isnote = snd_seq_get_bit(SND_SEQ_EVENT_NOTEON, (void *) event_filter);
+	ispgmc = snd_seq_get_bit(SND_SEQ_EVENT_PGMCHANGE, (void *) event_filter);
+	printf("after snd_seq_set_client_event (EVENT_PGMCHANGE) : "
+		"event_filter=0x%p isnote=%d ispgmc=%d\n ==> %s %s\n",
+		event_filter, isnote, ispgmc, "snd_seq_client_info_set_event_filter(NULL)",
+		(isnote == 0 ? "cleared" : "ERROR: NOT CLEARED"));
+
+	snd_seq_client_info_event_filter_clear(client_info);
+	printf("after snd_seq_client_info_event_filter_clear(client_info);\n");
+	dump_event_filter(client_info);
+
+	snd_seq_client_info_event_filter_add(client_info, SND_SEQ_EVENT_NOTEON);
+	printf("after snd_seq_client_info_event_filter_add(client_info, SND_SEQ_EVENT_NOTEON);\n");
+	dump_event_filter(client_info);
+
+	snd_seq_client_info_event_filter_add(client_info, SND_SEQ_EVENT_PGMCHANGE);
+	printf("after snd_seq_client_info_event_filter_add(client_info, SND_SEQ_EVENT_PGMCHANGE);\n");
+	dump_event_filter(client_info);
+
+	snd_seq_client_info_event_filter_del(client_info, SND_SEQ_EVENT_NOTEON);
+	printf("after snd_seq_client_info_event_filter_del(client_info, SND_SEQ_EVENT_NOTEON);\n");
+	dump_event_filter(client_info);
+
+	snd_seq_client_info_event_filter_clear(client_info);
+	printf("after snd_seq_client_info_event_filter_clear(client_info);\n");
+	dump_event_filter(client_info);
+
+	snd_seq_client_info_event_filter_add(client_info, SND_SEQ_EVENT_NOTEON);
+	printf("after snd_seq_client_info_event_filter_add(client_info, SND_SEQ_EVENT_NOTEON);\n");
+	dump_event_filter(client_info);
+}
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux