예제 #1
0
int osd_update_audio_stream(INT16 *buffer)
{
	// Soundcard switch off?
	if (Machine->sample_rate == 0) return samples_per_frame;

	Uint32 unBytesQueued = SDL_GetQueuedAudioSize(audio_device);
	if (unBytesQueued == 0) {
		++fifo_underrun;

		// Queue some silence to start buffering
		uint8_t silence[bytes_per_frame];
		memset(silence, 0, sizeof(silence));
		SDL_QueueAudio(audio_device, silence, sizeof(silence));
	}
	if (unBytesQueued > 10*bytes_per_frame) {
		++fifo_overrun;

		SDL_ClearQueuedAudio(audio_device);
	}

	profiler_mark(PROFILER_USER1);
	if (attenuation < 0) {
		/* FIXME: Need to attenuate the audio stream? */
	}
	SDL_QueueAudio(audio_device, buffer, bytes_per_frame);
	profiler_mark(PROFILER_END);

	return samples_per_frame;
}
예제 #2
0
void SoundSystemImpl::queueAudio()
{
#ifdef IMGUI_ENABLE
    const int max_samples = mAudioSpecObtained.samples * mAudioSpecObtained.channels;
    const uint32_t bytePerSample = sizeof(float);
    SDL_AudioDeviceID audioDeviceID = mAudioDeviceId;
    uint32_t alreadyQueuedByte = SDL_GetQueuedAudioSize(audioDeviceID);
    int16_t alreadyQueued = numeric_cast<int16_t>(alreadyQueuedByte / bytePerSample);
    int16_t toQueue = max_samples - alreadyQueued;
    while (g_debug_sample_buffer.max_size() <= g_debug_sample_buffer.size())
    {
        g_debug_sample_buffer.read();
    }
    g_debug_sample_buffer.write(mFrameMixer.size());
    while (g_debug_required_buffer.max_size() <= g_debug_required_buffer.size())
    {
        g_debug_required_buffer.read();
    }
    g_debug_required_buffer.write(toQueue);
#endif

    int audioError = SDL_QueueAudio(audioDeviceID, (const void*)mFrameMixer.data(), mFrameMixer.size() * bytePerSample);
    if (0 != audioError)
    {
        printf("Audio queue error %s\n", SDL_GetError());
    }
    mFrameMixer.clear();
}
예제 #3
0
static void sdl_renderer_decode_and_play_sample(char* data, int length) {
  int decodeLen = opus_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
  if (decodeLen > 0) {
    SDL_QueueAudio(dev, pcmBuffer, decodeLen * CHANNEL_COUNT * sizeof(short));
  } else {
    printf("Opus error from decode: %d\n", decodeLen);
  }
}
예제 #4
0
파일: audio.cpp 프로젝트: TaylanUB/nestopia
void audio_play() {
	
	if (paused) { updateok = true; return; }
	
	bufsize = 2 * channels * (conf.audio_sample_rate / framerate);
	
	if (conf.audio_api == 0) { // SDL
		#if SDL_VERSION_ATLEAST(2,0,4)
		SDL_QueueAudio(dev, (const void*)audiobuf, bufsize);
		// Clear the audio queue arbitrarily to avoid it backing up too far
		if (SDL_GetQueuedAudioSize(dev) > (Uint32)(bufsize * 3)) { SDL_ClearQueuedAudio(dev); }
		#endif
	}
#ifndef _MINGW
	else if (conf.audio_api == 1) { // libao
		ao_play(aodevice, (char*)audiobuf, bufsize);
	}
#endif
	updateok = true;
}
예제 #5
0
void
loop()
{
#ifdef __EMSCRIPTEN__
    if (done || (SDL_GetAudioStatus() != SDL_AUDIO_PLAYING)) {
        emscripten_cancel_main_loop();
    }
    else
#endif
    {
        /* The device from SDL_OpenAudio() is always device #1. */
        const Uint32 queued = SDL_GetQueuedAudioSize(1);
        SDL_Log("Device has %u bytes queued.\n", (unsigned int) queued);
        if (queued <= 8192) {  /* time to requeue the whole thing? */
            if (SDL_QueueAudio(1, wave.sound, wave.soundlen) == 0) {
                SDL_Log("Device queued %u more bytes.\n", (unsigned int) wave.soundlen);
            } else {
                SDL_Log("Device FAILED to queue %u more bytes: %s\n", (unsigned int) wave.soundlen, SDL_GetError());
            }
        }
    }
}
예제 #6
0
int main(int argc, char *argv[]) {
    sdl_state SDLState = {};

    platform_work_queue HighPriorityQueue = {};
    SDLMakeQueue(&HighPriorityQueue, 6);

    platform_work_queue LowPriorityQueue = {};
    SDLMakeQueue(&LowPriorityQueue, 2);


    GlobalPerfCountFrequency = SDL_GetPerformanceFrequency();

    SDLGetEXEFileName(&SDLState);

    char SourceGameCodeDLLFullpath[SDL_STATE_FILE_NAME_COUNT];
    SDLBuildEXEPathFileName(&SDLState, "handmade.dylib",
                            sizeof(SourceGameCodeDLLFullpath), SourceGameCodeDLLFullpath);

    char TempGameCodeDLLFullpath[SDL_STATE_FILE_NAME_COUNT];
    SDLBuildEXEPathFileName(&SDLState, "handmade_temp.dylib",
                            sizeof(TempGameCodeDLLFullpath), TempGameCodeDLLFullpath);

    char GameCodeLockFullpath[SDL_STATE_FILE_NAME_COUNT];
    SDLBuildEXEPathFileName(&SDLState, "lock.tmp",
                            sizeof(GameCodeLockFullpath), GameCodeLockFullpath);

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
        printf("Failed to initialize SDL: %s\n", SDL_GetError());
        return -1;
    }

    SDL_Window *Window = SDL_CreateWindow("Handmade Hero",
                                          SDL_WINDOWPOS_CENTERED,
                                          SDL_WINDOWPOS_CENTERED,
                                          960, 540,
                                          SDL_WINDOW_OPENGL);
    if (!Window) {
        printf("Failed to create window: %s\n", SDL_GetError());
        return -1;
    }

    SDLResizeDIBSection(Window, &GlobalBackBuffer, 960, 540);

    // TODO: Set GameUpdateHz by monitor refresh HZ
    real32 GameUpdateHz = 60.0f;
    real32 TargetSecondsPerFrame = 1.0f / GameUpdateHz;

    sdl_sound_output SoundOutput = {};
    SoundOutput.SamplesPerSecond = 48000;
    SoundOutput.BytesPerSample = sizeof(int16) * 2;
    SoundOutput.BufferSize = SoundOutput.SamplesPerSecond * SoundOutput.BytesPerSample;

    SDL_AudioDeviceID Audio = SDLInitSound(SoundOutput.SamplesPerSecond);

    u32 MaxPossibleOverrun = 2 * 4 * sizeof(u16);
    int16 *Samples = (int16 *)mmap(0, (size_t)(SoundOutput.BufferSize + MaxPossibleOverrun),
                                   PROT_READ | PROT_WRITE,
                                   MAP_PRIVATE | MAP_ANON, -1, 0);

    GlobalRunning = true;

#if HANDMADE_INTERNAL
    void *BaseAddress = (void *)Terabytes(2);
#else
    void *BaseAddress = 0;
#endif

    game_memory GameMemory = {};
    GameMemory.PermanentStorageSize = Megabytes(64);
    GameMemory.TransientStorageSize = Gigabytes(256);
    GameMemory.HighPriorityQueue = &HighPriorityQueue;
    GameMemory.LowPriorityQueue = &LowPriorityQueue;

    GameMemory.PlatformAPI.AddEntry = SDLAddEntry;
    GameMemory.PlatformAPI.CompleteAllWork = SDLCompleteAllWork;

    GameMemory.PlatformAPI.GetAllFilesOfTypeBegin = SDLGetAllFilesOfTypeBegin;
    GameMemory.PlatformAPI.GetAllFilesOfTypeEnd = SDLGetAllFilesOfTypeEnd;
    GameMemory.PlatformAPI.OpenNextFile = SDLOpenNextFile;
    GameMemory.PlatformAPI.ReadDataFromFile = SDLReadDataFromFile;
    GameMemory.PlatformAPI.FileError = SDLFileError;

    GameMemory.PlatformAPI.AllocateMemory = SDLAllocateMemory;
    GameMemory.PlatformAPI.DeallocateMemory = SDLDeallocateMemory;

    GameMemory.PlatformAPI.DEBUGFreeFileMemory = DEBUGPlatformFreeFileMemory;
    GameMemory.PlatformAPI.DEBUGReadEntireFile = DEBUGPlatformReadEntireFile;
    GameMemory.PlatformAPI.DEBUGWriteEntireFile = DEBUGPlatformWriteEntireFile;

    SDLState.TotalSize = GameMemory.PermanentStorageSize + GameMemory.TransientStorageSize;

    SDLState.GameMemoryBlock = mmap(BaseAddress,
                                    (size_t) SDLState.TotalSize,
                                    PROT_READ | PROT_WRITE,
                                    MAP_PRIVATE | MAP_ANON, -1, 0);
    GameMemory.PermanentStorage = SDLState.GameMemoryBlock;
    GameMemory.TransientStorage = ((uint8 *) GameMemory.PermanentStorage + GameMemory.PermanentStorageSize);

    if (!(GameMemory.PermanentStorage && GameMemory.TransientStorage)) {
        printf("Failed to allocate game memory\n");
        return -1;
    }

    // TODO: Add game replay support here

    game_input Input[2] = {};
    game_input *NewInput = &Input[0];
    game_input *OldInput = &Input[1];

    uint64 LastCounter = SDL_GetPerformanceCounter();
    uint64 FlipWallClock = SDL_GetPerformanceCounter();

    sdl_game_code Game = SDLLoadGameCode(SourceGameCodeDLLFullpath,
                                         TempGameCodeDLLFullpath,
                                         GameCodeLockFullpath);

    uint64 LastCycleCount = _rdtsc();

    while (GlobalRunning) {
        NewInput->dtForFrame = TargetSecondsPerFrame;

        NewInput->ExecutableReloaded = false;
        time_t NewDLLWriteTime = SDLGetLastWriteTime(SourceGameCodeDLLFullpath);
        if (difftime(NewDLLWriteTime, Game.DLLLastWriteTime) > 0) {
            SDLCompleteAllWork(&HighPriorityQueue);
            SDLCompleteAllWork(&LowPriorityQueue);

            SDLUnloadGameCode(&Game);
            Game = SDLLoadGameCode(SourceGameCodeDLLFullpath,
                                   TempGameCodeDLLFullpath,
                                   GameCodeLockFullpath);
            NewInput->ExecutableReloaded = true;
        }

        game_controller_input *OldKeyboardController = GetController(OldInput, 0);
        game_controller_input *NewKeyboardController = GetController(NewInput, 0);
        *NewKeyboardController = {};
        NewKeyboardController->IsConnected = true;
        for (size_t ButtonIndex = 0; ButtonIndex < ArrayCount(NewKeyboardController->Buttons); ++ButtonIndex) {
            NewKeyboardController->Buttons[ButtonIndex].EndedDown = OldKeyboardController->Buttons[ButtonIndex].EndedDown;
        }

        SDLProcessPendingMessage(&SDLState, NewKeyboardController);

        if (!GlobalPause) {
            Uint32 MouseButtons = SDL_GetMouseState(&NewInput->MouseX, &NewInput->MouseY);
            NewInput->MouseZ = 0;
            SDLProcessKeyboardMessage(&NewInput->MouseButtons[0],
                                      SDL_BUTTON(SDL_BUTTON_LEFT));
            SDLProcessKeyboardMessage(&NewInput->MouseButtons[1],
                                      SDL_BUTTON(SDL_BUTTON_MIDDLE));
            SDLProcessKeyboardMessage(&NewInput->MouseButtons[2],
                                      SDL_BUTTON(SDL_BUTTON_RIGHT));
            SDLProcessKeyboardMessage(&NewInput->MouseButtons[3],
                                      SDL_BUTTON(SDL_BUTTON_X1));
            SDLProcessKeyboardMessage(&NewInput->MouseButtons[4],
                                      SDL_BUTTON(SDL_BUTTON_X2));

            // TODO: Handle Mouse button here

            // TODO: Game controller support here

            game_offscreen_buffer Buffer = {};
            Buffer.Memory = GlobalBackBuffer.Memory;
            Buffer.Width = GlobalBackBuffer.Width;
            Buffer.Height = GlobalBackBuffer.Height;
            Buffer.Pitch = GlobalBackBuffer.Pitch;

            if (Game.UpdateAndRender) {
                Game.UpdateAndRender(&GameMemory, NewInput, &Buffer);
                HandleDebugCycleCounters(&GameMemory);
            }

            // TODO: Game audio support here
            game_sound_output_buffer SoundBuffer = {};
            SoundBuffer.SamplesPerSecond = SoundOutput.SamplesPerSecond;
            SoundBuffer.SampleCount = Align8((u32)(SoundOutput.SamplesPerSecond * TargetSecondsPerFrame));
            SoundBuffer.Samples = Samples;
            if (Game.GetSoundSamples) {
                Game.GetSoundSamples(&GameMemory, &SoundBuffer);
                SDL_QueueAudio(Audio, SoundBuffer.Samples, SoundBuffer.SampleCount * SoundOutput.BytesPerSample);
            }

            SDLDisplayBufferInWindow(&GlobalBackBuffer);

            game_input *Temp = NewInput;
            NewInput = OldInput;
            OldInput = Temp;
        }
    }

    return 0;
}
예제 #7
0
static void
loop()
{
    SDL_bool please_quit = SDL_FALSE;
    SDL_Event e;

    while (SDL_PollEvent(&e)) {
        if (e.type == SDL_QUIT) {
            please_quit = SDL_TRUE;
        } else if (e.type == SDL_KEYDOWN) {
            if (e.key.keysym.sym == SDLK_ESCAPE) {
                please_quit = SDL_TRUE;
            }
        } else if (e.type == SDL_MOUSEBUTTONDOWN) {
            if (e.button.button == 1) {
                SDL_PauseAudioDevice(devid_out, SDL_TRUE);
                SDL_PauseAudioDevice(devid_in, SDL_FALSE);
            }
        } else if (e.type == SDL_MOUSEBUTTONUP) {
            if (e.button.button == 1) {
                SDL_PauseAudioDevice(devid_in, SDL_TRUE);
                SDL_PauseAudioDevice(devid_out, SDL_FALSE);
            }
        }
    }

    if (SDL_GetAudioDeviceStatus(devid_in) == SDL_AUDIO_PLAYING) {
        SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
    } else {
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    }
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);

    if (please_quit) {
        /* stop playing back, quit. */
        SDL_Log("Shutting down.\n");
        SDL_PauseAudioDevice(devid_in, 1);
        SDL_CloseAudioDevice(devid_in);
        SDL_PauseAudioDevice(devid_out, 1);
        SDL_CloseAudioDevice(devid_out);
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        SDL_Quit();
        #ifdef __EMSCRIPTEN__
        emscripten_cancel_main_loop();
        #endif
        exit(0);
    }

    /* Note that it would be easier to just have a one-line function that
        calls SDL_QueueAudio() as a capture device callback, but we're
        trying to test the API, so we use SDL_DequeueAudio() here. */
    while (SDL_TRUE) {
        Uint8 buf[1024];
        const Uint32 br = SDL_DequeueAudio(devid_in, buf, sizeof (buf));
        SDL_QueueAudio(devid_out, buf, br);
        if (br < sizeof (buf)) {
            break;
        }
    }
}
예제 #8
0
int
main(int argc, char *argv[])
{
    int i;
    char filename[4096];

	/* Enable standard application logging */
	SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);

    /* Load the SDL library */
    if (SDL_Init(SDL_INIT_AUDIO) < 0) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
        return (1);
    }

    if (argc > 1) {
        SDL_strlcpy(filename, argv[1], sizeof(filename));
    } else {
        SDL_strlcpy(filename, "sample.wav", sizeof(filename));
    }
    /* Load the wave file into memory */
    if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError());
        quit(1);
    }

    wave.spec.callback = NULL;  /* we'll push audio. */

#if HAVE_SIGNAL_H
    /* Set the signals */
#ifdef SIGHUP
    signal(SIGHUP, poked);
#endif
    signal(SIGINT, poked);
#ifdef SIGQUIT
    signal(SIGQUIT, poked);
#endif
    signal(SIGTERM, poked);
#endif /* HAVE_SIGNAL_H */

    /* Initialize fillerup() variables */
    if (SDL_OpenAudio(&wave.spec, NULL) < 0) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError());
        SDL_FreeWAV(wave.sound);
        quit(2);
    }

    /*static x[99999]; SDL_QueueAudio(1, x, sizeof (x));*/

    /* Let the audio run */
    SDL_PauseAudio(0);

    /* Note that we stuff the entire audio buffer into the queue in one
       shot. Most apps would want to feed it a little at a time, as it
       plays, but we're going for simplicity here. */
    
    while (!done && (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING))
    {
        /* The device from SDL_OpenAudio() is always device #1. */
        const Uint32 queued = SDL_GetQueuedAudioSize(1);
        SDL_Log("Device has %u bytes queued.\n", (unsigned int) queued);
        if (queued <= 8192) {  /* time to requeue the whole thing? */
            if (SDL_QueueAudio(1, wave.sound, wave.soundlen) == 0) {
                SDL_Log("Device queued %u more bytes.\n", (unsigned int) wave.soundlen);
            } else {
                SDL_Log("Device FAILED to queue %u more bytes: %s\n", (unsigned int) wave.soundlen, SDL_GetError());
            }
        }

        SDL_Delay(100);  /* let it play for awhile. */
    }

    /* Clean up on signal */
    SDL_CloseAudio();
    SDL_FreeWAV(wave.sound);
    SDL_Quit();
    return 0;
}
예제 #9
0
//******************************************************* MAIN *******************************
int main(int argc, char* argv[]){
    //set up sdl
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

    SDL_Window *Window = SDL_CreateWindow("Handmade Hero",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        640, 480, SDL_WINDOW_RESIZABLE);
    if(Window){
        SDL_Renderer *Renderer = SDL_CreateRenderer(Window, -1, 0);
    if(Renderer){

    // VIDEO
    sdl_offscreen_buffer Buffer = {}; // if non-initialized, declaring variables in a loop fails on SDLResizeTexture with a pointer error - im blaming the compiler
    sdl_window_dimension Dimension = SDLGetWindowDimension(Window);
    SDLResizeTexture(&Buffer, Renderer, Dimension.Width, Dimension.Height);
    keystates Keys = {};

    //AUDIO
    sdl_sound_output sound_output = sdl_sound_outputH(48000);
    //open audio
    SDLInitAudio(sound_output.SamplesPerSecond, sound_output.SamplesPerSecond * sound_output.BytesPerSample / 60);
    SDL_PauseAudio(0);

    //BW: state area allocation
    void *new_state_area = malloc(128*1024*1024);//need ~2 MB at least; give it 128 MiB -- 2MB for video; dont know audio
    void *prev_state_area = malloc(1024);// should be sizeof(state0), or sizeof(biggest statetype) later
    //apparently we didnt have enough memory, but only crashed sometimes? this fixed it
    void *state;
    
    {
        uint8 *next_ptr = (uint8*)prev_state_area;
        anim_comp *animation = (anim_comp*)next_ptr;
        next_ptr += sizeof(anim_comp);

        init_anim_comp(animation, 0,0);

        state0 *stateptr = (state0*)next_ptr;
        printf("state0 size %ld\n", sizeof(state0));
        uint64 stateptrn = (uint64)stateptr;
        uint64 sizeptr = (uint64)&(stateptr->size);
        uint64 deepc_ptr = (uint64)&(stateptr->deep_count);
        uint64 anim_ptr = (uint64)&(stateptr->animation);
        uint64 tsine_ptr = (uint64)&(stateptr->tSine);
        uint64 tvol_ptr = (uint64)&(stateptr->ToneVolume);
        uint64 thz_ptr = (uint64)&(stateptr->ToneHz);
        uint64 pu_ptr = (uint64)&(stateptr->pitch_up_was_pressed);
        printf("offset begin    %lu\n", sizeptr - stateptrn);
        printf("width of size   %lu\n", deepc_ptr - sizeptr);
        printf("width of deepc  %lu\n", anim_ptr - deepc_ptr);
        printf("width of animpt %lu\n", tsine_ptr - anim_ptr);
        printf("width of tsine  %lu\n", tvol_ptr - tsine_ptr);
        printf("width of tvol   %lu\n", thz_ptr - tvol_ptr);
        printf("width of thz    %lu\n", pu_ptr - thz_ptr);

        next_ptr += sizeof(state0);

        init_state0(stateptr, animation, 3000, 256, 0);

        state = stateptr;
        //return 0;
    }

    uint64 LastCounter = SDL_GetPerformanceCounter();
    uint64 LastCycleCount = _rdtsc();
    uint64 PerfCountFrequency = SDL_GetPerformanceFrequency();

    bool running = true;
    //main loop
    printf("enter main event loop\n");
    while(running){
        ///////NP_UPDATE/////////////
        //event capturing
        event_return events = eventHandler(&Keys);
        if(events.shouldQuit) running = false;
        //setup for p
        state_window_info Wi = {}; Wi.Height = Buffer.Height; Wi.Width = Buffer.Width; Wi.Pitch = Buffer.Pitch;
        int TargetQueueBytes = sound_output.LatencySampleCount * sound_output.BytesPerSample;
        state_sound_info Si = {}; Si.BytesToGet = TargetQueueBytes - SDL_GetQueuedAudioSize(1); Si.BytesPerSample = sound_output.BytesPerSample; Si.SamplesPerSecond = sound_output.SamplesPerSecond;
        uint64 state_size = 0;
        uint64 vbuffer_size = Buffer.Height * Buffer.Pitch;
        uint64 abuffer_size = Si.BytesToGet;
        state_return next;
        { //in case(statetype) or similar
            next = P_update_state0(*(state0*)state, new_state_area, Keys, Wi, Si);
            //p should return state_size? and also, what statetype we are in -> later
            state_size = sizeof(state0);
        }

        //GARBAGE COLLECTOR
        //move this state to previous state
        //fmemcpy(prev_state_area, new_state_area, state_size); //shallow copy, as supposed
        //printf("hi\n");
        deepcopy(prev_state_area, next.state, new_state_area, (uint8*)new_state_area + 128*1024*1024);
        //TODO(md): DEEPCPY

        //queue audio
        if (Si.BytesToGet > 0) SDL_QueueAudio(1, next.abuffer, abuffer_size);

        //render
        SDLUpdateWindow(Window, Renderer, Buffer, next.vbuffer);


        uint64 EndCycleCount = _rdtsc();
        uint64 EndCounter = SDL_GetPerformanceCounter();
        uint64 CounterElapsed = EndCounter - LastCounter;
        uint64 CyclesElapsed = EndCycleCount - LastCycleCount;

        real64 MSPerFrame = (((1000.0f * (real64)CounterElapsed) / (real64)PerfCountFrequency));
        real64 FPS = (real64)PerfCountFrequency / (real64)CounterElapsed;
        real64 MCPF = ((real64)CyclesElapsed / (1000.0f * 1000.0f));

        printf("%.02fms/f, %.02f/s, %.02fmc/f\n", MSPerFrame, FPS, MCPF);

        LastCycleCount = EndCycleCount;
        LastCounter = EndCounter;
    }
    } } //if(Renderer, Window)
    SDL_Quit();
    return 0;
}
예제 #10
0
void loop(NES nesSystem, const char * fileLoc) {

    if (!init_sdl(fileLoc)) {
        std::cerr << "SDL did not initialize, quitting" << std::endl;
        return;
    }

    Debugger debugger(&nesSystem);

    //game loop variables
    double frequency = SDL_GetPerformanceFrequency();
    uintmax_t startTime = SDL_GetPerformanceCounter();

    bool paused = true;

    SDL_Event event;

    for (int x = 0; x < 256 * 240; x++) localPixels[x] = 0;
    draw(nesSystem.m_nesCPU.m_nesPPU.m_pixels);       //draw screen black
    SDL_PauseAudioDevice(sdlAudioDevice, 0);    //unpause audio

    for (;;) {

        //1 process events
        if (!process_events(&event, &nesSystem.m_nesCPU.m_controllerByte, &paused)) {
            break;
        }

        //2 logic
        if (!paused) {
            do {

                enum DebuggerEventStatus debugEventStatus = debugger.pre_instr_events();
                if (debugEventStatus == BREAK_HIT || debugEventStatus == CRASH_IMMINENT) {
                    paused = true;
                    break;
                } else if (debugEventStatus == DONE_LOGGED_EXECUTION) {
                    paused = true;
                }

                //execute one cpu opcode
                nesSystem.m_nesCPU.execute_next_opcode();

                debugger.post_instr_events();



                //ppu catches up
                nesSystem.m_nesCPU.m_nesPPU.tick(&nesSystem.m_nesCPU.m_NMI, &nesSystem.m_nesCPU.m_cpuClock);
            } while (!nesSystem.m_nesCPU.m_nesPPU.m_draw && !paused);

            //3.1 audio
            nesSystem.m_nesCPU.m_nesAPU.fill_buffer(&nesSystem, &nesSystem.m_nesCPU.m_IRQ);
            if (SDL_GetQueuedAudioSize(sdlAudioDevice) > (unsigned int) nesSystem.m_nesCPU.m_nesAPU.m_audioBufferSize * 10) {
                //prevents audio from becoming too out of sync
                SDL_ClearQueuedAudio(sdlAudioDevice);
            }
            SDL_QueueAudio(sdlAudioDevice, (void *) nesSystem.m_nesCPU.m_nesAPU.m_audioBuffer, nesSystem.m_nesCPU.m_nesAPU.m_audioBufferSize);
            
        } else {

            enum DebuggerCommandStatus commandStatus;

            do {
                commandStatus = debugger.cmd();
            } while (commandStatus == CONTINUE_DEBUG);

            //SDL_FlushEvents(SDL_USEREVENT, SDL_LASTEVENT);

            if (commandStatus == RUN_NO_FOCUS) {
                paused = false;
            } else if (commandStatus == QUIT) {
                break;
            } else if (commandStatus == RUN_RETURN_FOCUS) {
                paused = false;
                SDL_RaiseWindow(window);
            } else {
                break;
            }
        }

        //3.2 video
        draw(nesSystem.m_nesCPU.m_nesPPU.m_pixels);
        
        //4 sync framerate
        double delay = MILLISECONDS_PER_FRAME - (((SDL_GetPerformanceCounter() - startTime) / frequency) * 1000) - 0.5;
        if (delay > 0) {
            std::this_thread::sleep_for(std::chrono::microseconds((int) (delay * 1000)));
        }
        while ((((SDL_GetPerformanceCounter() - startTime) / frequency) * 1000)  < MILLISECONDS_PER_FRAME) {

        }
        startTime = SDL_GetPerformanceCounter();
    }

    SDL_PauseAudioDevice(sdlAudioDevice, 1);    //pause
    SDL_ClearQueuedAudio(sdlAudioDevice);       //clear audio queue
    close_sdl();

    return;
}