示例#1
0
static uint32_t delay() {
  if (alsa_handle == NULL) {
    return 0;
  } else {
    pthread_mutex_lock(&alsa_mutex);
    snd_pcm_sframes_t current_avail, current_delay = 0;
    int derr, ignore;
    if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING) {
      derr = snd_pcm_avail_delay(alsa_handle, &current_avail, &current_delay);
      // current_avail not used
      if (derr != 0) {
        ignore = snd_pcm_recover(alsa_handle, derr, 0);
        debug(1, "Error %d in delay(): %s. Delay reported is %d frames.", derr, snd_strerror(derr),
              current_delay);
        current_delay = -1;
      }
    } else if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) {
      current_delay = 0;
    } else {
      if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_XRUN)
        current_delay = 0;
      else {
        current_delay = -1;
        debug(1, "Error -- ALSA delay(): bad state: %d.", snd_pcm_state(alsa_handle));
      }
      if ((derr = snd_pcm_prepare(alsa_handle))) {
        ignore = snd_pcm_recover(alsa_handle, derr, 0);
        debug(1, "Error preparing after delay error: %s.", snd_strerror(derr));
        current_delay = -1;
      }
    }
    pthread_mutex_unlock(&alsa_mutex);
    return current_delay;
  }
}
示例#2
0
bool SoundProcessor::SetUpPlayer(){
/* Open PCM device for playback. */
  rc_s = snd_pcm_open(&handle_s, "default",
                    SND_PCM_STREAM_PLAYBACK, 0);
  if (rc_s < 0) {
    fprintf(stderr,
            "unable to open pcm device: %s\n",
            snd_strerror(rc_s));
    exit(1);
  }

  /* Allocate a hardware parameters object. */
  snd_pcm_hw_params_alloca(&params_s);

  /* Fill it in with default values. */
  snd_pcm_hw_params_any(handle_s, params_s);

  /* Set the desired hardware parameters. */

  /* Interleaved mode */
  snd_pcm_hw_params_set_access(handle_s, params_s,
                      SND_PCM_ACCESS_RW_INTERLEAVED);

  /* Signed 16-bit little-endian format */
  snd_pcm_hw_params_set_format(handle_s, params_s,
                              SND_PCM_FORMAT_U8);

  /* Two channels (stereo) */
  snd_pcm_hw_params_set_channels(handle_s, params_s, 1);

  /* 44100 bits/second sampling rate (CD quality) */
  val_s = 8000;
  snd_pcm_hw_params_set_rate_near(handle_s, params_s,
                                  &val_s, &dir_s);

  
  //frames = 32;
  snd_pcm_hw_params_set_period_size_near(handle_s,
                              params_s, &frames, &dir_s);

  /* Write the parameters to the driver */
  rc_s = snd_pcm_hw_params(handle_s, params_s);
  if (rc_s < 0) {
    fprintf(stderr,
            "unable to set hw parameters: %s\n",
            snd_strerror(rc_s));
    exit(1);
  }

  /* Use a buffer large enough to hold one period */
  snd_pcm_hw_params_get_period_size(params_s, &frames,
                                    &dir_s);
  size = frames ; /* 2 bytes/sample, 2 channels */
  buffer_s = (char *) malloc(size);

  /* We want to loop for 5 seconds */
  snd_pcm_hw_params_get_period_time(params_s,
                                    &val_s, &dir_s);
  snd_pcm_sframes_t availp;
  snd_pcm_sframes_t delayp;
  snd_pcm_avail_delay(handle_s,&availp,&delayp);
  printf("%l %l\n",availp,delayp);
  return true;
}
示例#3
0
int
main()
{

    hhxcb_state state = {};
    hhxcb_get_binary_name(&state);

    char source_game_code_library_path[HHXCB_STATE_FILE_NAME_LENGTH];
    char *game_code_filename = (char *) _HHXCB_QUOTE(GAME_CODE_FILENAME);
    hhxcb_build_full_filename(&state, game_code_filename,
            sizeof(source_game_code_library_path),
            source_game_code_library_path);
    hhxcb_game_code game_code = {};
    hhxcb_load_game(&game_code, source_game_code_library_path);

    hhxcb_context context = {};

    /* Open the connection to the X server. Use the DISPLAY environment variable */
    int screenNum;
    context.connection = xcb_connect (NULL, &screenNum);

    context.key_symbols = xcb_key_symbols_alloc(context.connection);

    /*
     * TODO(nbm): This is X-wide, so it really isn't a good option in reality.
     * We have to be careful and clean up at the end.  If we crash, auto-repeat
     * is left off.
     */
    {
        uint32_t values[1] = {XCB_AUTO_REPEAT_MODE_OFF};
        xcb_change_keyboard_control(context.connection, XCB_KB_AUTO_REPEAT_MODE, values);
    }

    load_atoms(&context);
    context.setup = xcb_get_setup(context.connection);
    xcb_screen_iterator_t iter = xcb_setup_roots_iterator(context.setup);
    xcb_screen_t *screen = iter.data;
    context.fmt = hhxcb_find_format(&context, 32, 24, 32);

    int monitor_refresh_hz = 60;
    real32 game_update_hz = (monitor_refresh_hz / 2.0f); // Should almost always be an int...
    long target_nanoseconds_per_frame = (1000 * 1000 * 1000) / game_update_hz;


    uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
    uint32_t values[2] =
    {
        0x0000ffff, //screen->black_pixel,
        0
            | XCB_EVENT_MASK_POINTER_MOTION
            | XCB_EVENT_MASK_KEY_PRESS
            | XCB_EVENT_MASK_KEY_RELEASE
            ,
    };

#define START_WIDTH 960
#define START_HEIGHT 540

    context.window = xcb_generate_id(context.connection);
	// NOTE: changed to not have a border width, so the min/max/close
	// buttons align on compiz, maybe other window managers
    xcb_create_window(context.connection, XCB_COPY_FROM_PARENT, context.window,
            screen->root, 0, 0, START_WIDTH, START_HEIGHT, 0,
            XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values);

    xcb_icccm_set_wm_name(context.connection, context.window, XCB_ATOM_STRING,
            8, strlen("hello"), "hello");

    load_and_set_cursor(&context);

    xcb_map_window(context.connection, context.window);
    xcb_atom_t protocols[] =
    {
        context.wm_delete_window,
    };
    xcb_icccm_set_wm_protocols(context.connection, context.window,
            context.wm_protocols, 1, protocols);

    xcb_size_hints_t hints = {};
    xcb_icccm_size_hints_set_max_size(&hints, START_WIDTH, START_HEIGHT);
    xcb_icccm_size_hints_set_min_size(&hints, START_WIDTH, START_HEIGHT);
    xcb_icccm_set_wm_size_hints(context.connection, context.window,
            XCB_ICCCM_WM_STATE_NORMAL, &hints);

    hhxcb_offscreen_buffer buffer = {};
    hhxcb_resize_backbuffer(&context, &buffer, START_WIDTH, START_HEIGHT);

    xcb_flush(context.connection);

    hhxcb_sound_output sound_output = {};
    sound_output.samples_per_second = 48000;
    sound_output.bytes_per_sample = sizeof(int16) * 2;
    sound_output.secondary_buffer_size = sound_output.samples_per_second * sound_output.bytes_per_sample;
    hhxcb_init_alsa(&context, &sound_output);

    int16 *sample_buffer = (int16 *)malloc(sound_output.secondary_buffer_size);

    thread_context t = {};

    game_memory m = {};
    m.PermanentStorageSize = 256 * 1024 * 1024;
    m.TransientStorageSize = 256 * 1024 * 1024;
    state.total_size = m.PermanentStorageSize + m.TransientStorageSize;
    state.game_memory_block = calloc(state.total_size, sizeof(uint8));
    m.PermanentStorage = (uint8 *)state.game_memory_block;
    m.TransientStorage =
        (uint8_t *)m.PermanentStorage + m.TransientStorageSize;
#ifdef HANDMADE_INTERNAL
    m.DEBUGPlatformFreeFileMemory = debug_xcb_free_file_memory;
    m.DEBUGPlatformReadEntireFile = debug_xcb_read_entire_file;
    m.DEBUGPlatformWriteEntireFile = debug_xcb_write_entire_file;
#endif

    hhxcb_init_replays(&state);

    bool ending = 0;
    timespec last_counter = {};
    timespec flip_wall_clock = {}; // Actually monotonic clock
    clock_gettime(HHXCB_CLOCK, &last_counter);
    clock_gettime(HHXCB_CLOCK, &flip_wall_clock);

    game_input input[2] = {};
    game_input *new_input = &input[0];
    game_input *old_input = &input[1];

    int64_t next_controller_refresh = 0;

    while(!ending)
    {
        if (last_counter.tv_sec >= next_controller_refresh)
        {
            hhxcb_refresh_controllers(&context);
            next_controller_refresh = last_counter.tv_sec + 1;
        }

        struct stat library_statbuf = {};
        stat(source_game_code_library_path, &library_statbuf);
        if (library_statbuf.st_mtime != game_code.library_mtime)
        {
            hhxcb_unload_game(&game_code);
            hhxcb_load_game(&game_code, source_game_code_library_path);
        }

        new_input->dtForFrame = target_nanoseconds_per_frame / (1024.0 * 1024 * 1024);

        hhxcb_process_events(&context, &state, new_input, old_input);

        if (context.ending_flag)
        {
            break;
        }

		// NOTE: setup game_buffer.Memory upside down and set
		// game_buffer.pitch negative, so the game would fill the
		// backbuffer upside down. XCB doesn't seem to have an
		// option to flip the image.
		
        game_offscreen_buffer game_buffer = {};
        game_buffer.Memory = ((uint8*)buffer.xcb_image->data)+
			(buffer.width*(buffer.height-1)*buffer.bytes_per_pixel);
        game_buffer.Width = buffer.width;
        game_buffer.Height = buffer.height;
        game_buffer.Pitch = -buffer.pitch;

        if (state.recording_index)
        {
            hhxcb_record_input(&state, new_input);
        }
        if (state.playback_index)
        {
            hhxcb_playback_input(&state, new_input);
        }
        if (game_code.UpdateAndRender)
        {
            game_code.UpdateAndRender(&t, &m, new_input, &game_buffer);
            HandleDebugCycleCounter(&m);
        }
	
        game_sound_output_buffer sound_buffer;
        sound_buffer.SamplesPerSecond = sound_output.samples_per_second;
        sound_buffer.SampleCount = sound_output.samples_per_second / 30;
        sound_buffer.Samples = sample_buffer;

        int err, frames;
        snd_pcm_sframes_t delay, avail;
        snd_pcm_avail_delay(context.handle, &avail, &delay);
        if (avail == sound_output.secondary_buffer_size)
        {
            // NOTE(nbm): Full available buffer, starting with ~60ms of silence
            bzero(sample_buffer, sound_buffer.SampleCount * sound_output.bytes_per_sample);
            snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount);
            snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount);
            snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount);
            snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount);
        }
        else
        {
            uint32 target_available_frames = sound_output.secondary_buffer_size;
            target_available_frames -= (sound_buffer.SampleCount * 1);
            if (avail - target_available_frames < sound_buffer.SampleCount)
            {
                sound_buffer.SampleCount += avail - target_available_frames;
            }
        }
        game_code.GetSoundSamples(&t, &m, &sound_buffer);
        if (sound_buffer.SampleCount > 0) {
            frames = snd_pcm_writei(context.handle, sample_buffer, sound_buffer.SampleCount);

            if (frames < 0)
            {
                frames = snd_pcm_recover(context.handle, frames, 0);
            }
            if (frames < 0) {
                printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
                break;
            }
            if (frames > 0 && frames < sound_buffer.SampleCount)
            {
                printf("Short write (expected %i, wrote %i)\n", sound_buffer.SampleCount, frames);
            }
        }

        xcb_image_put(context.connection, buffer.xcb_pixmap_id,
                buffer.xcb_gcontext_id, buffer.xcb_image, 0, 0, 0);
        xcb_flush(context.connection);
		
        timespec target_counter = {};
        target_counter.tv_sec = last_counter.tv_sec;
        target_counter.tv_nsec = last_counter.tv_nsec + target_nanoseconds_per_frame;
        if (target_counter.tv_nsec > (1000 * 1000 * 1000))
        {
            target_counter.tv_sec++;
            target_counter.tv_nsec %= (1000 * 1000 * 1000);
        }

        timespec work_counter = {};
        clock_gettime(HHXCB_CLOCK, &work_counter);

        bool32 might_need_sleep = 0;
        if (work_counter.tv_sec < target_counter.tv_sec)
        {
            might_need_sleep = 1;
        }
        else if ((work_counter.tv_sec == target_counter.tv_sec) && (work_counter.tv_nsec < target_counter.tv_nsec))
        {
            might_need_sleep = 1;
        }
        if (might_need_sleep) {
            timespec sleep_counter = {};
            sleep_counter.tv_nsec = target_counter.tv_nsec - work_counter.tv_nsec;
            if (sleep_counter.tv_nsec < 0) {
                sleep_counter.tv_nsec += (1000 * 1000 * 1000);
            }
            // To closest ms
            sleep_counter.tv_nsec -= sleep_counter.tv_nsec % (1000 * 1000);
            if (sleep_counter.tv_nsec > 0) {
                timespec remaining_sleep_counter = {};
                nanosleep(&sleep_counter, &remaining_sleep_counter);
            }
            else
            {
                // TODO(nbm): Log missed sleep
            }
        }

        timespec spin_counter = {};
        clock_gettime(HHXCB_CLOCK, &spin_counter);
        while (spin_counter.tv_sec <= target_counter.tv_sec && spin_counter.tv_nsec < target_counter.tv_nsec) {
            clock_gettime(HHXCB_CLOCK, &spin_counter);
        }

        timespec end_counter = {};
        clock_gettime(HHXCB_CLOCK, &end_counter);

        long ns_per_frame = end_counter.tv_nsec - last_counter.tv_nsec;
        if (ns_per_frame < 0)
        {
            ns_per_frame += (1000 * 1000 * 1000) * (end_counter.tv_sec - last_counter.tv_sec);
        }
        last_counter = end_counter;
        real32 ms_per_frame = ns_per_frame / (1000 * 1000.0);

        xcb_copy_area(context.connection, buffer.xcb_pixmap_id, context.window,
                buffer.xcb_gcontext_id, 0,0, 0, 0, buffer.xcb_image->width,
                buffer.xcb_image->height);
        xcb_flush(context.connection);

        game_input *temp_input = new_input;
        new_input = old_input;
        old_input = temp_input;
    }

    snd_pcm_close(context.handle);

    // NOTE(nbm): Since auto-repeat seems to be a X-wide thing, let's be nice
    // and return it to where it was before?
    {
        uint32_t values[1] = {XCB_AUTO_REPEAT_MODE_DEFAULT};
        xcb_change_keyboard_control(context.connection, XCB_KB_AUTO_REPEAT_MODE, values);
    }

    xcb_flush(context.connection);
    xcb_disconnect(context.connection);
}