From 44e68e4348f7ca86f4209f3f86ac7b6cb49acd52 Mon Sep 17 00:00:00 2001 From: Miller Puckette Date: Fri, 28 Dec 2007 03:28:31 +0000 Subject: 0.41-10 test 10 - many patches, plus work on callback scheduling svn path=/trunk/; revision=9107 --- pd/portmidi/pm_test/midithread.c | 327 --------------------------------------- 1 file changed, 327 deletions(-) delete mode 100644 pd/portmidi/pm_test/midithread.c (limited to 'pd/portmidi/pm_test/midithread.c') diff --git a/pd/portmidi/pm_test/midithread.c b/pd/portmidi/pm_test/midithread.c deleted file mode 100644 index 861b347c..00000000 --- a/pd/portmidi/pm_test/midithread.c +++ /dev/null @@ -1,327 +0,0 @@ -/* midithread.c -- example program showing how to do midi processing - in a preemptive thread - - Notes: if you handle midi I/O from your main program, there will be - some delay before handling midi messages whenever the program is - doing something like file I/O, graphical interface updates, etc. - - To handle midi with minimal delay, you should do all midi processing - in a separate, high priority thread. A convenient way to get a high - priority thread in windows is to use the timer callback provided by - the PortTime library. That is what we show here. - - If the high priority thread writes to a file, prints to the console, - or does just about anything other than midi processing, this may - create delays, so all this processing should be off-loaded to the - "main" process or thread. Communication between threads can be tricky. - If one thread is writing at the same time the other is reading, very - tricky race conditions can arise, causing programs to behave - incorrectly, but only under certain timing conditions -- a terrible - thing to debug. Advanced programmers know this as a synchronization - problem. See any operating systems textbook for the complete story. - - To avoid synchronization problems, a simple, reliable approach is - to communicate via messages. PortMidi offers a message queue as a - datatype, and operations to insert and remove messages. Use two - queues as follows: midi_to_main transfers messages from the midi - thread to the main thread, and main_to_midi transfers messages from - the main thread to the midi thread. Queues are safe for use between - threads as long as ONE thread writes and ONE thread reads. You must - NEVER allow two threads to write to the same queue. - - This program transposes incoming midi data by an amount controlled - by the main program. To change the transposition, type an integer - followed by return. The main program sends this via a message queue - to the midi thread. To quit, type 'q' followed by return. - - The midi thread can also send a pitch to the main program on request. - Type 'm' followed by return to wait for the next midi message and - print the pitch. - - This program illustrates: - Midi processing in a high-priority thread. - Communication with a main process via message queues. - - */ - -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "assert.h" -#include "portmidi.h" -#include "pmutil.h" -#include "porttime.h" - -/* if INPUT_BUFFER_SIZE is 0, PortMidi uses a default value */ -#define INPUT_BUFFER_SIZE 0 - -#define OUTPUT_BUFFER_SIZE 100 -#define DRIVER_INFO NULL -#define TIME_PROC NULL -#define TIME_INFO NULL -/* use zero latency because we want output to be immediate */ -#define LATENCY 0 - -#define STRING_MAX 80 - -/**********************************/ -/* DATA USED ONLY BY process_midi */ -/* (except during initialization) */ -/**********************************/ - -int active = FALSE; -int monitor = FALSE; -int midi_thru = TRUE; - -long transpose; -PmStream *midi_in; -PmStream *midi_out; - -/****************************/ -/* END OF process_midi DATA */ -/****************************/ - -/* shared queues */ -PmQueue *midi_to_main; -PmQueue *main_to_midi; - -#define QUIT_MSG 1000 -#define MONITOR_MSG 1001 -#define THRU_MSG 1002 - -/* timer interrupt for processing midi data */ -void process_midi(PtTimestamp timestamp, void *userData) -{ - PmError result; - PmEvent buffer; /* just one message at a time */ - long msg; - - /* do nothing until initialization completes */ - if (!active) - return; - - /* check for messages */ - do { - result = Pm_Dequeue(main_to_midi, &msg); - if (result) { - if (msg >= -127 && msg <= 127) - transpose = msg; - else if (msg == QUIT_MSG) { - /* acknowledge receipt of quit message */ - Pm_Enqueue(midi_to_main, &msg); - active = FALSE; - return; - } else if (msg == MONITOR_MSG) { - /* main has requested a pitch. monitor is a flag that - * records the request: - */ - monitor = TRUE; - } else if (msg == THRU_MSG) { - /* toggle Thru on or off */ - midi_thru = !midi_thru; - } - } - } while (result); - - /* see if there is any midi input to process */ - do { - result = Pm_Poll(midi_in); - if (result) { - long status, data1, data2; - if (Pm_Read(midi_in, &buffer, 1) == pmBufferOverflow) - continue; - if (midi_thru) - Pm_Write(midi_out, &buffer, 1); - /* unless there was overflow, we should have a message now */ - status = Pm_MessageStatus(buffer.message); - data1 = Pm_MessageData1(buffer.message); - data2 = Pm_MessageData2(buffer.message); - if ((status & 0xF0) == 0x90 || - (status & 0xF0) == 0x80) { - - /* this is a note-on or note-off, so transpose and send */ - data1 += transpose; - - /* keep within midi pitch range, keep proper pitch class */ - while (data1 > 127) - data1 -= 12; - while (data1 < 0) - data1 += 12; - - /* send the message */ - buffer.message = Pm_Message(status, data1, data2); - Pm_Write(midi_out, &buffer, 1); - - /* if monitor is set, send the pitch to the main thread */ - if (monitor) { - Pm_Enqueue(midi_to_main, &data1); - monitor = FALSE; /* only send one pitch per request */ - } - } - } - } while (result); -} - -void exit_with_message(char *msg) -{ - char line[STRING_MAX]; - printf("%s\n", msg); - fgets(line, STRING_MAX, stdin); - exit(1); -} - -int main() -{ - int id; - long n; - const PmDeviceInfo *info; - char line[STRING_MAX]; - int spin; - int done = FALSE; - - /* determine what type of test to run */ - printf("begin PortMidi multithread test...\n"); - - /* note that it is safe to call PortMidi from the main thread for - initialization and opening devices. You should not make any - calls to PortMidi from this thread once the midi thread begins. - to make PortMidi calls. - */ - - /* make the message queues */ - /* messages can be of any size and any type, but all messages in - * a given queue must have the same size. We'll just use long's - * for our messages in this simple example - */ - midi_to_main = Pm_QueueCreate(32, sizeof(long)); - assert(midi_to_main != NULL); - main_to_midi = Pm_QueueCreate(32, sizeof(long)); - assert(main_to_midi != NULL); - - /* a little test of enqueue and dequeue operations. Ordinarily, - * you would call Pm_Enqueue from one thread and Pm_Dequeue from - * the other. Since the midi thread is not running, this is safe. - */ - n = 1234567890; - Pm_Enqueue(midi_to_main, &n); - n = 987654321; - Pm_Enqueue(midi_to_main, &n); - Pm_Dequeue(midi_to_main, &n); - if (n != 1234567890) { - exit_with_message("Pm_Dequeue produced unexpected result."); - } - Pm_Dequeue(midi_to_main, &n); - if(n != 987654321) { - exit_with_message("Pm_Dequeue produced unexpected result."); - } - - /* always start the timer before you start midi */ - Pt_Start(1, &process_midi, 0); /* start a timer with millisecond accuracy */ - /* the timer will call our function, process_midi() every millisecond */ - - Pm_Initialize(); - - id = Pm_GetDefaultOutputDeviceID(); - info = Pm_GetDeviceInfo(id); - if (info == NULL) { - printf("Could not open default output device (%d).", id); - exit_with_message(""); - } - printf("Opening output device %s %s\n", info->interf, info->name); - - /* use zero latency because we want output to be immediate */ - Pm_OpenOutput(&midi_out, - id, - DRIVER_INFO, - OUTPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO, - LATENCY); - - id = Pm_GetDefaultInputDeviceID(); - info = Pm_GetDeviceInfo(id); - if (info == NULL) { - printf("Could not open default input device (%d).", id); - exit_with_message(""); - } - printf("Opening input device %s %s\n", info->interf, info->name); - Pm_OpenInput(&midi_in, - id, - DRIVER_INFO, - INPUT_BUFFER_SIZE, - TIME_PROC, - TIME_INFO); - - active = TRUE; /* enable processing in the midi thread -- yes, this - is a shared variable without synchronization, but - this simple assignment is safe */ - - printf("Enter midi input; it will be transformed as specified by...\n"); - printf("%s\n%s\n%s\n", - "Type 'q' to quit, 'm' to monitor next pitch, t to toggle thru or", - "type a number to specify transposition.", - "Must terminate with [ENTER]"); - - while (!done) { - long msg; - int len; - fgets(line, STRING_MAX, stdin); - /* remove the newline: */ - len = strlen(line); - if (len > 0) line[len - 1] = 0; /* overwrite the newline char */ - if (strcmp(line, "q") == 0) { - msg = QUIT_MSG; - Pm_Enqueue(main_to_midi, &msg); - /* wait for acknowlegement */ - do { - spin = Pm_Dequeue(midi_to_main, &msg); - } while (spin == 0); /* spin */ ; - done = TRUE; /* leave the command loop and wrap up */ - } else if (strcmp(line, "m") == 0) { - msg = MONITOR_MSG; - Pm_Enqueue(main_to_midi, &msg); - printf("Waiting for note...\n"); - do { - spin = Pm_Dequeue(midi_to_main, &msg); - } while (spin == 0); /* spin */ ; - printf("... pitch is %ld\n", msg); - } else if (strcmp(line, "t") == 0) { - /* reading midi_thru asynchronously could give incorrect results, - e.g. if you type "t" twice before the midi thread responds to - the first one, but we'll do it this way anyway. Perhaps a more - correct way would be to wait for an acknowledgement message - containing the new state. */ - printf("Setting THRU %s\n", (midi_thru ? "off" : "on")); - msg = THRU_MSG; - Pm_Enqueue(main_to_midi, &msg); - } else if (sscanf(line, "%ld", &msg) == 1) { - if (msg >= -127 && msg <= 127) { - /* send transposition value */ - printf("Transposing by %ld\n", msg); - Pm_Enqueue(main_to_midi, &msg); - } else { - printf("Transposition must be within -127...127\n"); - } - } else { - printf("%s\n%s\n%s\n", - "Type 'q' to quit, 'm' to monitor next pitch, or", - "type a number to specify transposition.", - "Must terminate with [ENTER]"); - } - } - - /* at this point, midi thread is inactive and we need to shut down - * the midi input and output - */ - Pt_Stop(); /* stop the timer */ - Pm_QueueDestroy(midi_to_main); - Pm_QueueDestroy(main_to_midi); - - /* Belinda! if close fails here, some memory is deleted, right??? */ - Pm_Close(midi_in); - Pm_Close(midi_out); - - printf("finished portMidi multithread test...enter any character to quit [RETURN]..."); - fgets(line, STRING_MAX, stdin); - return 0; -} -- cgit v1.2.1