/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Audio input test program for the Allegro library. * * By Shawn Hargreaves. * * See readme.txt for copyright information. */ #include #include #include #include "allegro.h" /* the current sound */ SAMPLE *the_sample = NULL; /* table of voice numbers for each MIDI key */ int voice[128]; /* lookup table for the MIDI pitch -> sample frequency conversion */ float pitch[128]; /* ring buffer for storing MIDI input */ #define MIDI_BUFFER_SIZE 4096 volatile int midi_buffer_head = 0; volatile int midi_buffer_tail = 0; volatile unsigned char midi_buffer[MIDI_BUFFER_SIZE]; /* the current MIDI command and data bytes */ int midi_cmd = -1; int midi_data1 = -1; int midi_data2 = -1; /* interrupt hook for dealing with MIDI input */ void midi_input_callback(unsigned char data) { midi_buffer[midi_buffer_tail] = data; midi_buffer_tail++; if (midi_buffer_tail >= MIDI_BUFFER_SIZE) midi_buffer_tail = 0; } END_OF_FUNCTION(midi_input_callback); /* checks if there is any data in the MIDI input buffer */ int midi_waiting(void) { if (midi_buffer_head == midi_buffer_tail) return 0; else return 1; } /* reads a byte from the MIDI input buffer */ int get_midi(void) { int ret; if (midi_buffer_head == midi_buffer_tail) return -1; ret = midi_buffer[midi_buffer_head]; midi_buffer_head++; if (midi_buffer_head >= MIDI_BUFFER_SIZE) midi_buffer_head = 0; return ret; } /* trigger a sample */ void start_note(int note, int vol) { if (voice[note] >= 0) { voice_stop(voice[note]); deallocate_voice(voice[note]); } voice[note] = allocate_voice(the_sample); if (voice[note] >= 0) { voice_set_frequency(voice[note], MIN(pitch[note]*the_sample->freq, 0x7FFFF)); voice_set_volume(voice[note], vol); voice_start(voice[note]); } } /* kill off a playing sample */ void stop_note(int note) { if (voice[note] >= 0) { voice_stop(voice[note]); deallocate_voice(voice[note]); voice[note] = -1; } } /* load samples in from disk */ int load_proc(int msg, DIALOG *d, int c) { int ret = d_button_proc(msg, d, c); if (ret & D_CLOSE) { static char name[80*6] = EMPTY_STRING; /* 80 chars * max UTF8 char width */ if (file_select_ex("Load Sample", name, "wav;voc", sizeof(name), 0, 0)) { if (the_sample) destroy_sample(the_sample); the_sample = load_sample(name); if (!the_sample) alert("Error reading sample", NULL, NULL, "What a pity", NULL, 13, 0); } return D_REDRAW; } return ret; } char record_length[16] = "5.0"; char record_freq[16] = ""; int waveform_proc(int msg, DIALOG *d, int c); DIALOG record_dialog[] = { /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ { d_shadow_box_proc, 160, 120, 321, 265, 255, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { d_ctext_proc, 320, 136, 0, 0, 255, 0, 0, 0, 0, 0, "Record Sample", NULL, NULL }, { d_button_proc, 208, 336, 97, 25, 255, 0, 13, D_EXIT, 0, 0, "OK", NULL, NULL }, { d_button_proc, 338, 336, 97, 25, 255, 0, 27, D_EXIT, 0, 0, "Cancel", NULL, NULL }, { d_text_proc, 208, 176, 0, 0, 255, 0, 0, 0, 0, 0, "Length:", NULL, NULL }, { d_edit_proc, 277, 176, 64, 8, 255, 0, 0, 0, 15, 0, record_length, NULL, NULL }, { d_text_proc, 208, 200, 0, 0, 255, 0, 0, 0, 0, 0, "Freq:", NULL, NULL }, { d_edit_proc, 277, 200, 64, 8, 255, 0, 0, 0, 15, 0, record_freq, NULL, NULL }, { d_text_proc, 208, 224, 0, 0, 255, 0, 0, 0, 0, 0, "16 bit:", NULL, NULL }, { d_check_proc, 272, 221, 17, 13, 255, 0, 0, 0, 0, 0, "", NULL, NULL }, { d_text_proc, 208, 248, 0, 0, 255, 0, 0, 0, 0, 0, "Stereo:", NULL, NULL }, { d_check_proc, 272, 245, 17, 13, 255, 0, 0, 0, 0, 0, "", NULL, NULL }, { d_radio_proc, 368, 176, 49, 13, 255, 0, 0, D_SELECTED, 1, 0, "Mic", NULL, NULL }, { d_radio_proc, 368, 200, 49, 13, 255, 0, 0, 0, 1, 0, "Line", NULL, NULL }, { d_radio_proc, 368, 224, 49, 13, 255, 0, 0, 0, 1, 0, "CD", NULL, NULL }, { waveform_proc, 208, 280, 226, 32, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } }; #define RECORD_OK 2 #define RECORD_CANCEL 3 #define RECORD_LENGTH 5 #define RECORD_FREQ 7 #define RECORD_16BIT 9 #define RECORD_STEREO 11 #define RECORD_MIC 12 #define RECORD_LINE 13 #define RECORD_CD 14 int waveform_size = 0; unsigned char *waveform_buffer = NULL; /* display the input waveform for the sampling dialog */ int waveform_proc(int msg, DIALOG *d, int c) { int rate, source, i; static int last_source = -1; switch (msg) { case MSG_START: rate = get_sound_input_cap_parm(8000, 8, FALSE); if (rate > 0) rate = 8000; else if (rate < 0) rate = -rate; else return D_O_K; waveform_size = start_sound_input(rate, 8, FALSE); if (waveform_size > 0) { waveform_buffer = malloc(waveform_size); memset(waveform_buffer, 0x80, waveform_size); } break; case MSG_END: if (waveform_buffer) { stop_sound_input(); free(waveform_buffer); waveform_buffer = NULL; waveform_size = 0; } break; case MSG_DRAW: if (waveform_buffer) { for (i=0; iw, waveform_size); i++) { if (waveform_buffer[i] < 0x80) { vline(screen, d->x+i, d->y, d->y+waveform_buffer[i]*d->h/255-1, d->bg); vline(screen, d->x+i, d->y+waveform_buffer[i]*d->h/255, d->y+d->h/2, d->fg); vline(screen, d->x+i, d->y+d->h/2+1, d->y+d->h, d->bg); } else { vline(screen, d->x+i, d->y, d->y+d->h/2-1, d->bg); vline(screen, d->x+i, d->y+d->h/2, d->y+waveform_buffer[i]*d->h/255, d->fg); vline(screen, d->x+i, d->y+waveform_buffer[i]*d->h/255+1, d->y+d->h, d->bg); } } } break; case MSG_IDLE: if (waveform_buffer) { if (read_sound_input(waveform_buffer)) { freeze_mouse_flag = TRUE; poll_mouse(); object_message(d, MSG_DRAW, 0); freeze_mouse_flag = FALSE; } } if (record_dialog[RECORD_MIC].flags & D_SELECTED) source = SOUND_INPUT_MIC; else if (record_dialog[RECORD_LINE].flags & D_SELECTED) source = SOUND_INPUT_LINE; else source = SOUND_INPUT_CD; if (source != last_source) { set_sound_input_source(source); last_source = source; } break; } return D_O_K; } /* main sample recording function */ int record_proc(int msg, DIALOG *d, int c) { int ret = d_button_proc(msg, d, c); int sel = -1; int bits; int stereo; int freq, freq2; int buffer_size; int bytes_per_sample; int sample_len; int sample_todo; int sample_todo_orig; char *sample_data; float length; if (ret & D_CLOSE) { bits = get_sound_input_cap_bits(); stereo = get_sound_input_cap_stereo(); if (!bits) { alert("The current audio input driver does", "not support sample recording", NULL, "I'll change it then", NULL, 13, 0); return D_REDRAW; } if (bits&16) record_dialog[RECORD_16BIT].flags |= D_SELECTED; else record_dialog[RECORD_16BIT].flags &= ~D_SELECTED; if (stereo) record_dialog[RECORD_STEREO].flags |= D_SELECTED; else record_dialog[RECORD_STEREO].flags &= ~D_SELECTED; if (!record_freq[0]) sprintf(record_freq, "%d", get_sound_input_cap_rate(((bits&16) ? 16 : 8), stereo)); retry: /* ask the user for settings */ if (do_dialog(record_dialog, sel) == RECORD_OK) { length = atof(record_length); freq = atof(record_freq); bits = (record_dialog[RECORD_16BIT].flags & D_SELECTED) ? 16 : 8; stereo = (record_dialog[RECORD_STEREO].flags & D_SELECTED) ? TRUE : FALSE; /* make sure we got a valid length */ if ((length < 0.1) || (length > 30)) { alert("Please enter a length", "between 0.1 and 30 seconds", NULL, "Understood", NULL, 13, 0); sel = RECORD_LENGTH; goto retry; } /* make sure we got a valid frequency */ if ((freq < 4000) || (freq > 50000)) { alert("Please enter a sampling frequency", "between 4000 and 50000", NULL, "Understood", NULL, 13, 0); sel = RECORD_FREQ; goto retry; } /* check that the card likes this frequency */ freq2 = get_sound_input_cap_parm(freq, bits, stereo); if (freq2 == 0) { alert("The current input driver does", "not support these settings", NULL, "Drat", NULL, 13, 0); sel = RECORD_16BIT; goto retry; } if (freq2 < 0) { sprintf(record_freq, "%d", -freq2); alert("Invalid sampling frequency", "Suggested alternative:", record_freq, "Understood", NULL, 13, 0); sel = RECORD_FREQ; goto retry; } /* set the sample source */ if (record_dialog[RECORD_MIC].flags & D_SELECTED) set_sound_input_source(SOUND_INPUT_MIC); else if (record_dialog[RECORD_LINE].flags & D_SELECTED) set_sound_input_source(SOUND_INPUT_LINE); else if (record_dialog[RECORD_CD].flags & D_SELECTED) set_sound_input_source(SOUND_INPUT_CD); /* draw progress bar */ show_mouse(NULL); rect(screen, 95, 175, 545, 273, 255); rectfill(screen, 96, 176, 544, 272, 0); rect(screen, 127, 235, 513, 257, 255); rectfill(screen, 128, 236, 512, 256, 8); textout_centre_ex(screen, font, "Recording...", 320, 200, 255, -1); /* start the input! */ buffer_size = start_sound_input(freq, bits, stereo); if (buffer_size <= 0) { alert("Error recording sample", NULL, NULL, "Drat", NULL, 13, 0); destroy_sample(the_sample); the_sample = NULL; show_mouse(screen); return D_REDRAW; } /* round the sample length to an even number of buffer transfers. * Really the recording code ought to handle odd amounts of data * left over at the end, but it is much easier like this :-) */ bytes_per_sample = ((bits==8) ? 1 : sizeof(short)) * ((stereo) ? 2 : 1); sample_len = length * freq; sample_len = MAX((((sample_len * bytes_per_sample) / buffer_size) * buffer_size), buffer_size) / bytes_per_sample; sample_todo = sample_todo_orig = sample_len * bytes_per_sample; if (the_sample) destroy_sample(the_sample); /* create a new sample to hold our data */ the_sample = create_sample(bits, stereo, freq, sample_len); sample_data = (char *)the_sample->data; /* discard the first input buffer, to prevent clicks */ do { } while (!read_sound_input(sample_data)); /* finally, record the sound */ while (sample_todo >= buffer_size) { if (read_sound_input(sample_data)) { sample_data += buffer_size; sample_todo -= buffer_size; rectfill(screen, 128, 236, 128+(sample_todo_orig-sample_todo)*384/sample_todo_orig, 256, 1); } } stop_sound_input(); show_mouse(screen); } return D_REDRAW; } return ret; } /* helper for redrawing a specific part of the keyboard object */ void redraw_keyboard(DIALOG *d, int c) { set_clip_rect(screen, d->x+(c-36)*12-6, d->y, d->x+(c-36)*12+18, d->y+d->h); object_message(d, MSG_DRAW, 0); set_clip_rect(screen, 0, 0, SCREEN_W-1, SCREEN_H-1); } /* GUI interface for playing sounds */ int piano_proc(int msg, DIALOG *d, int c) { static char blackkey[12] = { FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE }; int i, l, r; switch (msg) { case MSG_START: d->d1 = -1; break; case MSG_DRAW: /* display the keyboard graphic */ for (i=0; iw/12; i++) { if (!blackkey[i%12]) { l = i*12; r = (i+1)*12; if (blackkey[(i-1)%12]) l -= 6; if (blackkey[(i+1)%12]) r += 6; rectfill(screen, d->x+l+1, d->y+1, d->x+r-1, d->y+d->h-1, (voice[i+36] >= 0) ? 1 : d->bg); rect(screen, d->x+l, d->y, d->x+r, d->y+d->h, d->fg); } } for (i=0; iw/12; i++) { if (blackkey[i%12]) { l = i*12 + 1; r = (i+1)*12 - 1; rectfill(screen, d->x+l, d->y+d->h/4, d->x+r, d->y+d->h, (voice[i+36] >= 0) ? 1 : d->fg); } } break; case MSG_CLICK: /* handle mouse clicks (playing sounds from the GUI) */ if (!the_sample) { alert("You must read in or record a", "sample before you can play it", NULL, "Sorry. I won't do it again", NULL, 13, 0); return D_O_K; } do { poll_mouse(); i = ((mouse_x - d->x) / 12) + 36; if (i != d->d1) { if (d->d1 >= 0) { stop_note(d->d1); redraw_keyboard(d, d->d1); d->d1 = -1; } d->d1 = i; start_note(d->d1, 255); redraw_keyboard(d, d->d1); } } while (mouse_b); if (d->d1 >= 0) { stop_note(d->d1); redraw_keyboard(d, d->d1); d->d1 = -1; } break; case MSG_IDLE: /* check for MIDI input */ if (midi_waiting()) { i = get_midi(); if (i & 0x80) { /* start of a new MIDI command */ midi_cmd = i&0xF0; midi_data1 = -1; midi_data2 = -1; } else if (midi_data1 < 0) { /* got the first parameter */ midi_data1 = i; } else if (midi_data2 < 0) { /* got the second parameter */ midi_data2 = i; if (the_sample) { if ((midi_cmd == 0x80) || ((midi_cmd == 0x90) && (midi_data2 == 0))) { /* note off */ stop_note(midi_data1); redraw_keyboard(d, midi_data1); } else if (midi_cmd == 0x90) { /* note on */ start_note(midi_data1, midi_data2); redraw_keyboard(d, midi_data1); } } midi_data1 = -1; midi_data2 = -1; } } break; } return D_O_K; } DIALOG the_dialog[] = { /* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) (dp2) (dp3) */ { d_clear_proc, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, NULL, NULL, NULL }, { d_ctext_proc, 320, 4, 0, 0, 255, 8, 0, 0, 0, 0, "Audio input test program for Allegro " ALLEGRO_VERSION_STR ", " ALLEGRO_PLATFORM_STR, NULL, NULL }, { d_ctext_proc, 320, 20, 0, 0, 255, 8, 0, 0, 0, 0, "By Shawn Hargreaves, " ALLEGRO_DATE_STR, NULL, NULL }, { d_text_proc, 224, 88, 0, 0, 255, 8, 0, 0, 0, 0, "MIDI Input:", NULL, NULL }, { d_text_proc, 328, 88, 0, 0, 255, 8, 0, 0, 0, 0, NULL, NULL, NULL }, { d_text_proc, 216, 112, 0, 0, 255, 8, 0, 0, 0, 0, "Audio Input:", NULL, NULL }, { d_text_proc, 328, 112, 0, 0, 255, 8, 0, 0, 0, 0, NULL, NULL, NULL }, { d_text_proc, 208, 136, 0, 0, 255, 8, 0, 0, 0, 0, "Audio Output:", NULL, NULL }, { d_text_proc, 328, 136, 0, 0, 255, 8, 0, 0, 0, 0, NULL, NULL, NULL }, { load_proc, 160, 232, 129, 25, 255, 0, 0, D_EXIT, 0, 0, "Load Sample", NULL, NULL }, { record_proc, 352, 232, 129, 25, 255, 0, 0, D_EXIT, 0, 0, "Record Sample", NULL, NULL }, { d_button_proc, 256, 320, 129, 25, 255, 0, 0, D_EXIT, 0, 0, "Quit", NULL, NULL }, { piano_proc, 2, 440, 636, 40, 255, 0, 0, 0, 0, 0, NULL, NULL, NULL }, { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } }; int main(void) { int i; for (i=0; i<128; i++) voice[i] = -1; pitch[127] = 48; for (i=126; i>=0; i--) pitch[i] = pitch[i+1] / pow(2.0, 1.0/12.0); if (allegro_init() != 0) return 1; install_keyboard(); install_mouse(); install_timer(); if (install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL) != 0) { allegro_message("\nError initialising sound\n%s\n", allegro_error); return 1; } if (install_sound_input(DIGI_AUTODETECT, MIDI_AUTODETECT) != 0) { allegro_message("\nError initialising sound input\n%s\n\n" "Try changing the digi_input_card and midi_input_card settings in your\n" "allegro.cfg, in particular disabling (set to zero) one of those drivers.\n", allegro_error); return 1; } if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) != 0) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message("Error setting graphics mode\n%s\n", allegro_error); return 1; } set_palette(desktop_palette); LOCK_VARIABLE(midi_buffer_head); LOCK_VARIABLE(midi_buffer_tail); LOCK_VARIABLE(midi_buffer); LOCK_FUNCTION(midi_input_callback); midi_recorder = midi_input_callback; the_dialog[4].dp = (void*)midi_input_driver->name; the_dialog[6].dp = (void*)digi_input_driver->name; the_dialog[8].dp = (void*)digi_driver->name; do_dialog(the_dialog, -1); if (the_sample) destroy_sample(the_sample); return 0; } END_OF_MAIN()