Exemple #1
0
int main(int argc, char **argv)
{
   ALLEGRO_AUDIO_RECORDER *r;
   ALLEGRO_AUDIO_STREAM *s;
   
   ALLEGRO_EVENT_QUEUE *q;
   ALLEGRO_DISPLAY *d;
   ALLEGRO_FILE *fp = NULL;
   ALLEGRO_PATH *tmp_path = NULL;
      
   int prev = 0;
   bool is_recording = false;
   
   int n = 0; /* number of samples written to disk */
   
   (void) argc;
   (void) argv;

   if (!al_init()) {
      abort_example("Could not init Allegro.\n");
   }
   
   if (!al_init_primitives_addon()) {
      abort_example("Unable to initialize primitives addon");
   }
      
   if (!al_install_keyboard()) {
      abort_example("Unable to install keyboard");
   }
      
   if (!al_install_audio()) {
      abort_example("Unable to initialize audio addon");
   }
   
   if (!al_init_acodec_addon()) {
      abort_example("Unable to initialize acodec addon");
   }
   
   /* Note: increasing the number of channels will break this demo. Other
    * settings can be changed by modifying the constants at the top of the
    * file.
    */
   r = al_create_audio_recorder(1000, samples_per_fragment, frequency,
      audio_depth, ALLEGRO_CHANNEL_CONF_1);
   if (!r) {
      abort_example("Unable to create audio recorder");
   }
   
   s = al_create_audio_stream(playback_fragment_count,
      playback_samples_per_fragment, frequency, audio_depth,
      ALLEGRO_CHANNEL_CONF_1);      
   if (!s) {
      abort_example("Unable to create audio stream");
   }
      
   al_reserve_samples(0);
   al_set_audio_stream_playing(s, false);
   al_attach_audio_stream_to_mixer(s, al_get_default_mixer());
      
   q = al_create_event_queue();
   
   /* Note: the following two options are referring to pixel samples, and have
    * nothing to do with audio samples. */
   al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST);
   al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST);
   
   d = al_create_display(320, 256);
   if (!d) {
      abort_example("Error creating display\n");
   }
      
   al_set_window_title(d, "SPACE to record. P to playback.");
   
   al_register_event_source(q, al_get_audio_recorder_event_source(r));
   al_register_event_source(q, al_get_audio_stream_event_source(s));
   al_register_event_source(q, al_get_display_event_source(d));
   al_register_event_source(q, al_get_keyboard_event_source());
   
   al_start_audio_recorder(r);
   
   while (true) {
      ALLEGRO_EVENT e;

      al_wait_for_event(q, &e);
       
      if (e.type == ALLEGRO_EVENT_AUDIO_RECORDER_FRAGMENT) {
         /* We received an incoming fragment from the microphone. In this
          * example, the recorder is constantly recording even when we aren't
          * saving to disk. The display is updated every time a new fragment
          * comes in, because it makes things more simple. If the fragments
          * are coming in faster than we can update the screen, then it will be
          * a problem.
          */          
         ALLEGRO_AUDIO_RECORDER_EVENT *re = al_get_audio_recorder_event(&e);
         audio_buffer_t input = (audio_buffer_t) re->buffer;
         int sample_count = re->samples; 
         const int R = sample_count / 320;
         int i, gain = 0;
         
         /* Calculate the volume, and display it regardless if we are actively
          * recording to disk. */
         for (i = 0; i < sample_count; ++i) {
            if (gain < abs(input[i] - sample_center))
               gain = abs(input[i] - sample_center);
         }
        
         al_clear_to_color(al_map_rgb(0,0,0));
        
         if (is_recording) {
            /* Save raw bytes to disk. Assumes everything is written
             * succesfully. */
            if (fp && n < frequency / (float) samples_per_fragment * 
               max_seconds_to_record) {
               al_fwrite(fp, input, sample_count * sample_size);
               ++n;
            }

            /* Draw a pathetic visualization. It draws exactly one fragment
             * per frame. This means the visualization is dependent on the 
             * various parameters. A more thorough implementation would use this
             * event to copy the new data into a circular buffer that holds a
             * few seconds of audio. The graphics routine could then always
             * draw that last second of audio, which would cause the
             * visualization to appear constant across all different settings.
             */
            for (i = 0; i < 320; ++i) {
               int j, c = 0;
               
               /* Take the average of R samples so it fits on the screen */
               for (j = i * R; j < i * R + R && j < sample_count; ++j) {
                  c += input[j] - sample_center;
               }
               c /= R;
               
               /* Draws a line from the previous sample point to the next */
               al_draw_line(i - 1, 128 + ((prev - min_sample_val) /
                  (float) sample_range) * 256 - 128, i, 128 +
                  ((c - min_sample_val) / (float) sample_range) * 256 - 128,
                  al_map_rgb(255,255,255), 1.2);
               
               prev = c;
            }
         }
         
         /* draw volume bar */
         al_draw_filled_rectangle((gain / (float) max_sample_val) * 320, 251,
            0, 256, al_map_rgba(0, 255, 0, 128));
            
         al_flip_display();
      }
      else if (e.type == ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) {
         /* This event is received when we are playing back the audio clip.
          * See ex_saw.c for an example dedicated to playing streams.
          */
         if (fp) {
            audio_buffer_t output = al_get_audio_stream_fragment(s);
            if (output) {
               /* Fill the buffer from the data we have recorded into the file.
                * If an error occurs (or end of file) then silence out the
                * remainder of the buffer and stop the playback.
                */
               const size_t bytes_to_read =
                  playback_samples_per_fragment * sample_size;
               size_t bytes_read = 0, i;
               
               do {
                  bytes_read += al_fread(fp, (uint8_t *)output + bytes_read,
                     bytes_to_read - bytes_read);                  
               } while (bytes_read < bytes_to_read && !al_feof(fp) &&
                  !al_ferror(fp));
               
               /* silence out unused part of buffer (end of file) */
               for (i = bytes_read / sample_size;
                  i < bytes_to_read / sample_size; ++i) {
                     output[i] = sample_center;
               }
               
               al_set_audio_stream_fragment(s, output);
               
               if (al_ferror(fp) || al_feof(fp)) {
                  al_drain_audio_stream(s);
                  al_fclose(fp);
                  fp = NULL;
               }
            }
         }
      }      
      else if (e.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
         break;
      }
      else if (e.type == ALLEGRO_EVENT_KEY_CHAR) {
         if (e.keyboard.unichar == 27) {
            /* pressed ESC */
            break;
         }
         else if (e.keyboard.unichar == ' ') {
            if (!is_recording) {
               /* Start the recording */
               is_recording = true;
               
               if (al_get_audio_stream_playing(s)) {
                  al_drain_audio_stream(s);
               }
               
               /* Reuse the same temp file for all recordings */
               if (!tmp_path) {
                  fp = al_make_temp_file("alrecXXX.raw", &tmp_path);
               }
               else {
                  if (fp) al_fclose(fp);
                  fp = al_fopen(al_path_cstr(tmp_path, '/'), "w");
               }
               
               n = 0;
            }
            else {
               is_recording = false;
               if (fp) {
                  al_fclose(fp);
                  fp = NULL;
               }
            }
         }
         else if (e.keyboard.unichar == 'p') {
            /* Play the previously recorded wav file */
            if (!is_recording) {
               if (tmp_path) {
                  fp = al_fopen(al_path_cstr(tmp_path, '/'), "r");
                  if (fp) {
                     al_set_audio_stream_playing(s, true);
                  }
               }
            }
         }
      }
   }
   
   /* clean up */
   al_destroy_audio_recorder(r);
   al_destroy_audio_stream(s);
      
   if (fp)
      al_fclose(fp);
      
   if (tmp_path) {
      al_remove_filename(al_path_cstr(tmp_path, '/'));
      al_destroy_path(tmp_path);
   }
   
   return 0;
}
int main(int argc, char *argv[])
{
   ALLEGRO_DISPLAY *display;
   ALLEGRO_FONT *font;
   ALLEGRO_AUDIO_RECORDER *recorder;
   ALLEGRO_EVENT_QUEUE *queue;
   ALLEGRO_TIMER *timer;
   int font_height;
   
   /* Frequency is the number of samples per second. */
   const int frequency = 44100;
   
   const int channels = 2;

   /* The latency is used to determine the size of the fragment buffer.
      More accurately, it represents approximately how many seconds will
      pass between fragment events. (There may be overhead latency from
      the OS or driver the adds a fixed amount of latency on top of 
      Allegro's.) 
      
      For this example, the latency should be kept relatively low since
      each fragment is processed in its entirety. Increasing the latency
      would increase the size of the fragment, which would decrease the
      accuracy of the code that processes the fragment.
      
      But if it's too low, then it will cut out too quickly. (If the
      example were more thoroughly written, the latency setting wouldn't
      actually change how the voice detection worked.)
    */
   const float latency = 0.10;

   const int max_seconds = 3; /* number of seconds of voice recording */
   
   int16_t *name_buffer;      /* stores up to max_seconds of audio */
   int16_t *name_buffer_pos;  /* points to the current recorded position */
   int16_t *name_buffer_end;  /* points to the end of the buffer */
   
   float gain = 0.0f;         /* 0.0 (quiet) - 1.0 (loud) */
   float begin_gain = 0.3f;   /* when to begin recording */
   
   bool is_recording = false;
   
   ALLEGRO_SAMPLE *spl = NULL;
   
   (void) argc;
   (void) argv;
   
   if (!al_init()) {
      abort_example("Could not init Allegro.\n");
   }
   
   if (!al_install_audio()) {
      abort_example("Unable to initialize audio addon\n");
   }
   
   if (!al_init_acodec_addon()) {
      abort_example("Unable to initialize acoded addon\n");
   }
   
   if (!al_init_image_addon()) {
      abort_example("Unable to initialize image addon\n");
   }
   
   if (!al_init_primitives_addon()) {
      abort_example("Unable to initialize primitives addon\n");
   }
      
   al_init_font_addon();
   al_install_keyboard();
   
   font = al_load_bitmap_font("data/bmpfont.tga");
   if (!font) {
      abort_example("Unable to load data/a4_font.tga\n");
   }
   
   font_height = al_get_font_line_height(font);
   
   /* WARNING: This demo assumes an audio depth of INT16 and two channels.
      Changing those values will break the demo. Nothing here really needs to be
      changed. If you want to fiddle with things, adjust the constants at the
      beginning of the program.
    */
   
   recorder = al_create_audio_recorder(
      5 / latency,                 /* five seconds of buffer space */
      frequency * latency,         /* configure the fragment size to give us the given
                                        latency in seconds */
      frequency,                   /* samples per second (higher => better quality) */
      ALLEGRO_AUDIO_DEPTH_INT16,   /* 2-byte sample size */
      ALLEGRO_CHANNEL_CONF_2       /* stereo */
   );
   
   if (!recorder) {
      abort_example("Unable to create audio recorder\n");
   }
   
   display = al_create_display(640, 480);
   if (!display) {
      abort_example("Unable to create display\n");
   }
   
   /* Used to play back the voice recording. */
   al_reserve_samples(1);
   
   /* store up to three seconds */
   name_buffer = al_calloc(channels * frequency * max_seconds, sizeof(int16_t));
   name_buffer_pos = name_buffer;
   name_buffer_end = name_buffer + channels * frequency * max_seconds;
   
   queue = al_create_event_queue();
   timer = al_create_timer(1 / 60.0);
   
   al_register_event_source(queue, al_get_display_event_source(display));
   al_register_event_source(queue, al_get_audio_recorder_event_source(recorder));
   al_register_event_source(queue, al_get_timer_event_source(timer));
   al_register_event_source(queue, al_get_keyboard_event_source());
   
   al_start_timer(timer);
   al_start_audio_recorder(recorder);
   
   while (true) {
      ALLEGRO_EVENT event;
      bool do_draw = false;
      
      al_wait_for_event(queue, &event);
      
      if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE || 
         (event.type == ALLEGRO_EVENT_KEY_UP && event.keyboard.keycode == ALLEGRO_KEY_ESCAPE)) {
         break;
      }
      else if (event.type == ALLEGRO_EVENT_KEY_CHAR) {
         if (spl && event.keyboard.unichar != 27) {
            al_play_sample(spl, 1.0, 0.0, 1.0, ALLEGRO_PLAYMODE_ONCE, NULL);
         }
      }
      else if (event.type == ALLEGRO_EVENT_TIMER) {
         do_draw = true;
      }
      else if (event.type == ALLEGRO_EVENT_AUDIO_RECORDER_FRAGMENT && recorder != NULL) {
         /* Because the recording happens in a different thread (and simply because we are
            queuing up events), it's quite possible to receive (process) a fragment event
            after the recorder has been stopped or destroyed. Thus, it is important to
            somehow check that the recorder is still valid, as we are doing above.
          */
         ALLEGRO_AUDIO_RECORDER_EVENT *re = al_get_audio_recorder_event(&event);
         int16_t *buffer = re->buffer;
         int16_t low = 0, high = 0;
         unsigned int i;
         
         /* Calculate the volume by comparing the highest and lowest points. This entire
            section assumes we are using fairly small fragment size (low latency). If a 
            large fragment size were used, then we'd have to inspect smaller portions 
            of it at a time to more accurately deterine when recording started and
            stopped. */
         for (i = 0; i < channels * re->samples; ++i) {
            if (buffer[i] < low)
               low = buffer[i];
            else if (buffer[i] > high)
               high = buffer[i];
         }
         
         gain = gain * 0.25 + ((float) (high - low) / 0xffff) * 0.75;
         
         /* Set arbitrary thresholds for beginning and stopping recording. This probably
            should be calibrated by determining how loud the ambient noise is.
          */
         if (!is_recording && gain >= begin_gain && name_buffer_pos == name_buffer)
            is_recording = true;
         else if (is_recording && gain <= 0.10)
            is_recording = false;
         
         if (is_recording) {
            /* Copy out of the fragment buffer into our own buffer that holds the
               name. */
            int samples_to_copy = channels * re->samples;
            
            /* Don't overfill up our name buffer... */
            if (samples_to_copy > name_buffer_end - name_buffer_pos)
               samples_to_copy = name_buffer_end - name_buffer_pos;
            
            if (samples_to_copy) {
               /* must multiply by two, since we are using 16-bit samples */
               memcpy(name_buffer_pos, re->buffer, samples_to_copy * 2);
            }
            
            name_buffer_pos += samples_to_copy;
            if (name_buffer_pos >= name_buffer_end) {
               is_recording = false;
            }
         }
         
         if (!is_recording && name_buffer_pos != name_buffer && !spl) {
            /* finished recording, but haven't created the sample yet */
            spl = al_create_sample(name_buffer, name_buffer_pos - name_buffer, frequency, 
               ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2, false);
            
            /* We no longer need the recorder. Destroying it is the only way to unlock the device. */
            al_destroy_audio_recorder(recorder);
            recorder = NULL;
         }
      }
      
      if (do_draw) {
         al_clear_to_color(al_map_rgb(0,0,0));
         if (!spl) {
            const char *msg = "Say Your Name";
            int width = al_get_text_width(font, msg);
            
            al_draw_text(font, al_map_rgb(255,255,255),
               320, 240 - font_height / 2, ALLEGRO_ALIGN_CENTRE, msg
            );
            
            /* draw volume meter */
            al_draw_filled_rectangle(320 - width / 2, 242 + font_height / 2,
               (320 - width / 2) + (gain * width), 242 + font_height, 
               al_map_rgb(0,255,0)
            );
            
            /* draw target line that triggers recording */
            al_draw_line((320 - width / 2) + (begin_gain * width), 242 + font_height / 2,
               (320 - width / 2) + (begin_gain * width), 242 + font_height,
               al_map_rgb(255,255,0), 1.0
            );
         }
         else {
            al_draw_text(font, al_map_rgb(255,255,255), 320, 240 - font_height / 2,
               ALLEGRO_ALIGN_CENTRE, "Press Any Key");
         }
         al_flip_display();
      }
   }
   
   if (recorder) {
      al_destroy_audio_recorder(recorder);
   }
   
   al_free(name_buffer);
   
   return 0;
}