/** \fn updateVu */ bool GUIPlayback::updateVu(void) { uint64_t time= ticktock.getElapsedMS(); // Refresh vumeter every 50 ms if(!playbackAudio) return true; if(time>(vuMeterPts+50)) { uint32_t stat[6]; AVDM_getStats(stat); UI_setVUMeter(stat); vuMeterPts=time; } return true; }
/** \fn initializeAudio \brief Initialize audio */ bool GUIPlayback::initializeAudio(void) { uint32_t state,latency, preload; uint32_t small_; uint32_t channels,frequency; wavbuf = 0; uint64_t startPts=firstPts; int32_t shift=0; // unit is ms, + => delay audio, -=> advance audio // if audio shift is activated, take it into account // EditableAudioTrack *ed=video_body->getDefaultEditableAudioTrack(); if(ed->audioEncodingConfig.shiftEnabled) shift=ed->audioEncodingConfig.shiftInMs; playbackAudio = createPlaybackFilter(startPts,shift); if(!playbackAudio) { ADM_info("No audio\n"); return false; } channels= playbackAudio->getInfo()->channels; frequency=playbackAudio->getInfo()->frequency; preload= (frequency * channels)/5; // 200 ms preload // 4 sec buffer.. wavbuf = (float *) ADM_alloc((20*sizeof(float)*preload)); // 4 secs buffers ADM_assert(wavbuf); // Read a at least one block to have the proper channel mapping uint32_t fill=0; AUD_Status status; small_ = playbackAudio->fill(channels, wavbuf,&status); fill+=small_; // Call it twice to be sure it is properly setup state = AVDM_AudioSetup(frequency, channels ,playbackAudio->getChannelMapping()); AVDM_AudioClose(); state = AVDM_AudioSetup(frequency, channels ,playbackAudio->getChannelMapping()); latency=AVDM_GetLayencyMs(); printf("[Playback] Latency : %d ms\n",latency); audioLatency=latency; // ms -> us if (!state) { GUI_Error_HIG(QT_TR_NOOP("Trouble initializing audio device"), NULL); cleanupAudio(); return false; } while(fill<preload) { if (!(small_ = playbackAudio->fill(preload-fill, wavbuf+fill,&status))) { break; } fill+=small_; } nbSamplesSent = fill/channels; // In sample AVDM_AudioPlay(wavbuf, fill); // Let audio latency sets in... ticktock.reset(); uint32_t slice=(frequency * channels)/100; // 10 ms // pump data until latency is over updateVu(); #if 0 while(ticktock.getElapsedMS()<latency) { if(AVDM_getMsFullness()<AUDIO_PRELOAD) { if (!(small_ = playbackAudio->fill(slice, wavbuf,&status))) { printf("[Playback] Compensating for latency failed\n"); break; } AVDM_AudioPlay(wavbuf, slice); } ADM_usleep(10*1000); updateVu(); } #endif printf("[Playback] Latency is now %u\n",ticktock.getElapsedMS()); return true; }
/** \fn localInit \brief Take & initialize the device */ bool pulseSimpleAudioDevice::localInit(void) { ADM_info("Pulse, initiliazing channel=%d samplerate=%d\n",(int)_channels,(int)_frequency); pa_simple *s; pa_sample_spec ss; int er; pa_buffer_attr attr; pa_channel_map map,*pmap=NULL; attr.maxlength = (uint32_t) -1; attr.tlength = (uint32_t )-1; attr.prebuf =(uint32_t) -1; attr.minreq = (uint32_t) -1; attr.fragsize =(uint32_t) -1; // We want something like 20 ms latency uint64_t bufSize=_frequency; bufSize*=_channels; bufSize*=2; // 1 second worth of audio bufSize=bufSize/1000; bufSize*=ADM_PULSE_LATENCY; attr.tlength=bufSize; // Latency in bytes // Channel mapping if(_channels>2) { pmap=↦ map.channels=_channels; map.map[0]=PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1]=PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2]=PA_CHANNEL_POSITION_FRONT_CENTER; map.map[3]=PA_CHANNEL_POSITION_REAR_LEFT; map.map[4]=PA_CHANNEL_POSITION_REAR_RIGHT; map.map[5]=PA_CHANNEL_POSITION_SUBWOOFER; } ss.format = PA_SAMPLE_S16LE; ss.channels = _channels; ss.rate =_frequency; instance= pa_simple_new(NULL, // Use the default server. "Avidemux2", // Our application's name. PA_STREAM_PLAYBACK, NULL, // Use the default device. "Sound", // Description of our stream. &ss, // Our sample format. pmap, // Use default channel map &attr , // Use default buffering attributes. &er // Ignore error code. ); if(!instance) { ADM_info("[PulseSimple] open failed :%s\n",pa_strerror(er)); return 0; } #if 0 pa_usec_t l=0; // Latency... Clock ticktock; ticktock.reset(); if(0>pa_simple_write(INSTANCE,silence, sizeOf10ms,&er)) { fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(er)); } pa_simple_drain(INSTANCE,&er); latency=ticktock.getElapsedMS(); ADM_info("[Pulse] Latency :%"PRIu32", total %"PRIu32"\n",latency,pa_simple_get_latency(INSTANCE,&er)/1000); #endif ADM_info("[PulseSimple] open ok for fq=%d channels=%d\n",ss.rate,ss.channels); return 1; }
bool GUIPlayback::run(void) { uint32_t movieTime; uint32_t systemTime; uint32_t fn; int refreshCounter=0; ticktock.reset(); vuMeterPts=0; ADMImage *previewBuffer=admPreview::getBuffer(); ADM_HW_IMAGE hwImageFormat=admPreview::getPreferedHwImageFormat(); do { admPreview::displayNow();; GUI_setCurrentFrameAndTime(firstPts); if(false==videoFilter->getNextFrameAs(hwImageFormat,&fn,previewBuffer)) { printf("[Play] Cancelling playback, nextPicture failed\n"); break; } audioPump(false); lastPts=admPreview::getCurrentPts(); systemTime = ticktock.getElapsedMS(); movieTime=(uint32_t)((lastPts-firstPts*0)/1000); movieTime+=audioLatency; // printf("[Playback] systemTime: %lu movieTime : %lu \r",systemTime,movieTime); if(systemTime>movieTime) // We are late, the current PTS is after current closk { if(systemTime >movieTime+20) { refreshCounter++; } if(refreshCounter>15) { UI_purge(); UI_purge(); refreshCounter=0; } } else { int32_t delta; delta=movieTime-systemTime; // a call to whatever sleep function will last at leat 10 ms // give some time to GTK while(delta > 10) { if(delta>10) { audioPump(true); }else audioPump(false); UI_purge(); refreshCounter=0; systemTime = ticktock.getElapsedMS(); delta=movieTime-systemTime; } } } while (!stop_req); abort_play: return true; };
int main (int argc, char **argv) { // TODO: Create a separate test suit executable instead of this code block. #ifndef NDEBUG testStringTokenizer(); #endif UserSettings::load(); Game::Window window; int r = glewInit(); assert (r == GLEW_OK); // Setup an orthographic projection. glMatrixMode (GL_PROJECTION); glLoadIdentity(); gluOrtho2D (0.0, Game::Window::get_width(), 0.0, Game::Window::get_height()); // Enable color blending for transparent parts of PNG images. glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glMatrixMode (GL_MODELVIEW); Font disclaimer_text (10); Font controls_text (12); int fps; static const GLfloat PADDING = 10.0F; // in pixels #ifndef NDEBUG static const std::string FPS_LABEL = "FPS: "; // Create the FPS rectangle. static const std::string FPS_LONGEST_STR = FPS_LABEL + "000"; Rectangle fps_rect; fps_rect.w = PADDING + disclaimer_text.get_width (FPS_LONGEST_STR) + PADDING; fps_rect.h = PADDING + disclaimer_text.get_line_height() + PADDING; fps_rect.x = Window::get_width() - fps_rect.w; fps_rect.y = Window::get_height() - fps_rect.h; #endif // Create the menu. static const int ITEM_WIDTH = 200, ITEM_HEIGHT = 50; // in pixels menu = new Menu (ITEM_WIDTH, ITEM_HEIGHT); menu->consume (new MenuTextItem ("Start Game", start_game, MenuTextItem::LARGE)); menu->consume (new MenuTextItem ("Controls", display_controls, MenuTextItem::LARGE)); menu->consume (new MenuTextItem ("Options", display_options, MenuTextItem::LARGE)); menu->consume (new MenuTextItem ("Credits", display_credits, MenuTextItem::LARGE)); menu->consume (new MenuTextItem ("Exit", exit_game, MenuTextItem::LARGE)); menu_rect.w = ITEM_WIDTH; menu_rect.h = ITEM_HEIGHT * menu->get_item_count(); menu_rect.x = 400; menu_rect.y = (Window::get_height() - menu_rect.h) / 2; menu->set_position (menu_rect); UI::Manager menu_ui_manager; menu_ui_manager.consume (menu); display_menu (NULL); UserSettingsPanel *user_settings_widget = NULL; // Main game loop. const int DESIRED_FPS = 60; static const unsigned int DESIRED_FRAME_DURATION = 1000 / DESIRED_FPS; Clock clock; do { unsigned int start_tick = clock.getElapsedMS(); Controllers::Manager::update (window); // Update, draw, and eventually terminate the world. if (activity == WORLD) { if (Controllers::Manager::is_escape_pressed()) { display_menu (NULL); } else { world->update (start_tick); if (world->get_status() == World::PLAYER_WON) { delete world; world = NULL; display_credits (NULL); } else if (world->get_status() == World::PLAYER_LOST) { delete world; world = NULL; start_game (NULL); } else if (world->get_status() == World::RUNNING) { world->draw(); } } } else if (activity == CREDITS) { credits->update (start_tick); if (credits->is_done() || Controllers::Manager::is_escape_pressed()) { delete credits; credits = NULL; display_menu (NULL); } else { credits->draw (window); } } else { // Draw the backdrop. if (world) { world->draw(); // Blacken the world by 80%. glColor4f (0.0F, 0.0F, 0.0F, 0.8F); glPushMatrix(); glLoadIdentity(); glRecti (0, 0, Window::get_width(), Window::get_height()); glPopMatrix(); } else { // Draw the background. glPushMatrix(); glLoadIdentity(); static Animation background ("ui/main_menu.png"); static const GLfloat BG_SCALE = (GLfloat)Game::Window::get_width() / background.get_width(); glScalef (BG_SCALE, BG_SCALE, 0.0F); background.draw(); glPopMatrix(); glMatrixMode (GL_MODELVIEW); glColor3ub (64, 40, 196); // dark purple // Draw the disclaimer. static const std::string INFO_STR = "Contributions copyright (2011) by their " "respective authors listed in the credits. Not a finished work."; static const int INFO_X = (Game::Window::get_width() - disclaimer_text.get_width (INFO_STR)) / 2; glLoadIdentity(); glTranslatef (INFO_X, PADDING, 0.0F); disclaimer_text.draw (INFO_STR); // Draw the version. static const std::string VER_STR = "v 0.7"; static const int VER_X = Window::get_width() - PADDING - disclaimer_text.get_width (VER_STR); glLoadIdentity(); glTranslatef (VER_X, PADDING, 0.0F); disclaimer_text.draw (VER_STR); } // Draw the title. glPushMatrix(); glLoadIdentity(); glTranslatef (title_x, title_y, 0.0F); glColor3ub (64, 40, 196); // dark purple title_text.draw (title); glPopMatrix(); if (activity == CONTROLS) { if (Controllers::Manager::is_escape_pressed()) { display_menu (NULL); } else { // Draw the controls. glPushMatrix(); glLoadIdentity(); glTranslatef (menu_rect.x, menu_rect.get_top() - controls_text.get_line_height(), 0.0F); glColor3ub (156, 120, 252); // light purple controls_text.draw ( "Arrows: Move, aim\n" "z: fire weapon, select menu item\n" "a, s: Select weapon\n" "x: Jump\n" "Down: Use door, talk, etc\n" "Esc: Back\n" "\n" "F1: Editor\n" "Mouse buttons 3, 4: Select map layer\n" "Mouse wheel: Select brush\n" "Left click: Select or use brush\n" "Right click: Copy brush\n" "\n" "F2 Terminal"); glPopMatrix(); } } else if (activity == OPTIONS) { if (!user_settings_widget) { user_settings_widget = new UserSettingsPanel(); } user_settings_widget->update (start_tick); user_settings_widget->draw(); if (Controllers::Manager::is_escape_pressed()) { delete user_settings_widget; user_settings_widget = NULL; display_menu (NULL); } } else if (activity == MAIN_MENU) { // Select a menu item by hot key. static Controllers::Player pc; if (pc.is_up_pressed()) { menu->decrement_highlight(); } if (pc.is_down_pressed()) { menu->increment_highlight(); } if (pc.is_weapon_fire_pressed()) { menu->select_highlighted(); } menu_ui_manager.update (start_tick); menu_ui_manager.draw(); } } // Draw FPS if compiled for debugging. #ifndef NDEBUG // Create the FPS string. std::stringstream fps_ss; fps_ss << FPS_LABEL << fps; std::string fps_str = fps_ss.str(); // Move to the FPS label's location. glLoadIdentity(); glTranslatef (fps_rect.x, fps_rect.y, 0.0F); // Draw the FPS label's background. glColor4f (0.0F, 0.0F, 0.0F, 0.8F); // 80% black glRecti (0, 0, fps_rect.w, fps_rect.h); // Draw the FPS label. glTranslatef (PADDING, PADDING, 0.0F); glColor3ub (64, 40, 196); // dark purple disclaimer_text.draw (fps_str); #endif window.refresh(); unsigned int frame_duration = clock.getElapsedMS() - start_tick; if (frame_duration >= DESIRED_FRAME_DURATION) { fps = 1000 / frame_duration; } else { // if the frame duration is less than desired fps = DESIRED_FPS; clock.sleep (DESIRED_FRAME_DURATION - frame_duration); } } while (activity != EXIT && !Controllers::Manager::is_app_closed()); delete world; delete credits; delete user_settings_widget; return EXIT_SUCCESS; }