media-glfw.c (6489B)
1 #include <assert.h> 2 #include <stdio.h> 3 #include <string.h> 4 5 #define GLFW_INCLUDE_ES2 6 #include <GLFW/glfw3.h> 7 8 #define MA_NO_DECODING 9 #define MINIAUDIO_IMPLEMENTATION 10 #include "ext/miniaudio.h" 11 12 #include "media.h" 13 14 #define PIXEL_RADIUS 16 15 16 #define SCREEN_WIDTH (64*PIXEL_RADIUS) 17 #define SCREEN_HEIGHT (32*PIXEL_RADIUS) 18 19 #define WINDOW_NAME "CHIP-8 emulator" 20 21 #define NUM_PIXELS (32*64) 22 23 #define SOUND_DEV_FREQ 48000 24 #define SOUND_DEV_FORMAT ma_format_f32 25 #define SOUND_DEV_CHANNELS 1 26 27 #define BUZZER_WAVE_TYPE ma_waveform_type_sawtooth 28 #define BUZZER_FREQ 440 29 #define BUZZER_VOL .05 30 31 unsigned char pixels[NUM_PIXELS]; 32 33 GLFWwindow *window; 34 35 const float vertices[] = { 36 -1.0f, -1.0f, 37 -1.0f, +1.0f, 38 +1.0f, -1.0f, 39 +1.0f, +1.0f, 40 }; 41 42 const char *v_shader_src = 43 "#version 100\n" 44 "in vec2 coords;\n" 45 "varying vec2 tex_coords;\n" 46 "void main(){\n" 47 " gl_Position = vec4(coords.x, coords.y, 0.0, 1.0);\n" 48 " tex_coords.x = (1.0 + coords.x)/2.0;\n" 49 " tex_coords.y = (1.0 - coords.y)/2.0;\n" 50 "}"; 51 52 const char *f_shader_src = 53 "#version 100\n" 54 "in lowp vec2 tex_coords;\n" 55 "uniform sampler2D tex;\n" 56 "void main(){\n" 57 " gl_FragColor = vec4(vec3(texture2D(tex, tex_coords).a), 1.0);\n" 58 "}"; 59 60 61 unsigned short input; 62 63 64 /* sound state*/ 65 ma_waveform wave; 66 ma_device device; 67 68 /* 69 ┌───┬───┬───┬───┐ 70 │ 1 │ 2 │ 3 │ C │ 71 ├───┼───┼───┼───┤ 72 │ 4 │ 5 │ 6 │ D │ 73 ├───┼───┼───┼───┤ 74 │ 7 │ 8 │ 9 │ E │ 75 ├───┼───┼───┼───┤ 76 │ A │ 0 │ B │ F │ 77 └───┴───┴───┴───┘ 78 */ 79 80 int keys[16] = { 81 GLFW_KEY_V, 82 GLFW_KEY_3, GLFW_KEY_4, GLFW_KEY_5, 83 GLFW_KEY_E, GLFW_KEY_R, GLFW_KEY_T, 84 GLFW_KEY_D, GLFW_KEY_F, GLFW_KEY_G, 85 GLFW_KEY_C, GLFW_KEY_B, 86 GLFW_KEY_6, GLFW_KEY_Y, GLFW_KEY_H, GLFW_KEY_N 87 }; 88 89 static void buzzer_callback(ma_device *d, void *out, const void *in, ma_uint32 frame){ 90 (void)in; (void)d; 91 ma_waveform_read_pcm_frames(&wave, out, frame); 92 } 93 94 static void key_callback(GLFWwindow* window, int key, int s, int action, int m){ 95 (void) s, (void)m, (void) window; 96 for (int i=0 ; i<16 ; i++) { 97 if (key == keys[i]){ 98 if (action == GLFW_PRESS) 99 input |= 1<<i; 100 else if (action == GLFW_RELEASE) 101 input ^= 1<<i; 102 } 103 } 104 } 105 106 void framebuffer_size_callback(GLFWwindow* window, int width, int height){ 107 (void) window; 108 glViewport(0, 0, width, height); 109 } 110 111 static void check_shader_log(unsigned shader){ 112 int success; 113 glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 114 if (!success){ 115 char log[512]; 116 glGetShaderInfoLog(shader, 512, NULL, log); 117 puts("Shader compile log:"); 118 puts(log); 119 } 120 } 121 122 static void setup_shaders(void){ 123 /* Create vertex shader */ 124 unsigned v_shader = glCreateShader(GL_VERTEX_SHADER); 125 glShaderSource(v_shader, 1, &v_shader_src, NULL); 126 glCompileShader(v_shader); 127 check_shader_log(v_shader); 128 129 /* Create fragment shader */ 130 unsigned f_shader = glCreateShader(GL_FRAGMENT_SHADER); 131 glShaderSource(f_shader, 1, &f_shader_src, NULL); 132 glCompileShader(f_shader); 133 check_shader_log(f_shader); 134 135 /* Create shader program */ 136 unsigned program = glCreateProgram(); 137 glAttachShader(program, v_shader); 138 glAttachShader(program, f_shader); 139 glLinkProgram(program); 140 glUseProgram(program); 141 142 /* Remove shaders */ 143 glDeleteShader(v_shader); 144 glDeleteShader(f_shader); 145 146 /* Create vertex buffer */ 147 unsigned VBO; 148 glGenBuffers(1, &VBO); 149 glBindBuffer(GL_ARRAY_BUFFER, VBO); 150 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 151 152 /* Setup vertex input array */ 153 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); 154 glEnableVertexAttribArray(0); 155 } 156 157 static void setup_texture(void){ 158 unsigned texture; 159 glGenTextures(1, &texture); 160 glBindTexture(GL_TEXTURE_2D, texture); 161 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 162 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 163 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 164 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 165 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 64, 32, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); 166 } 167 168 static void init_graphics(void){ 169 /* Init GLFW */ 170 glfwInit(); 171 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 172 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 173 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 174 175 window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_NAME, NULL, NULL); 176 glfwMakeContextCurrent(window); 177 178 glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 179 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 180 181 glfwSwapInterval(1); 182 183 /* Init OpenGL */ 184 setup_shaders(); 185 setup_texture(); 186 187 glClear(GL_COLOR_BUFFER_BIT); 188 } 189 190 static void init_audio(void){ 191 ma_waveform_config wave_config = ma_waveform_config_init( 192 SOUND_DEV_FORMAT, SOUND_DEV_CHANNELS, SOUND_DEV_FREQ, 193 BUZZER_WAVE_TYPE, BUZZER_VOL, BUZZER_FREQ); 194 195 ma_waveform_init(&wave_config, &wave); 196 197 ma_device_config device_config = ma_device_config_init(ma_device_type_playback); 198 199 device_config.playback.format = SOUND_DEV_FORMAT; 200 device_config.playback.channels = SOUND_DEV_CHANNELS; 201 device_config.sampleRate = SOUND_DEV_FREQ; 202 device_config.dataCallback = buzzer_callback; 203 204 ma_device_init(NULL, &device_config, &device); 205 ma_device_start(&device); 206 207 ma_device_set_master_volume(&device, 0); 208 } 209 210 int m_init(int argc, char **argv){ 211 (void)argc, (void)argv; 212 /*TODO: check every init */ 213 214 init_graphics(); 215 216 init_audio(); 217 218 glfwSetKeyCallback(window, key_callback); 219 220 return 0; 221 } 222 223 void m_quit(void){ 224 glfwDestroyWindow(window); 225 226 glfwTerminate(); 227 228 ma_device_uninit(&device); 229 } 230 231 unsigned short get_input(unsigned short old_input){ 232 (void) old_input; 233 if (glfwWindowShouldClose(window)) 234 return -1; 235 236 return input; 237 } 238 unsigned short wait_input(unsigned short input){ 239 int new_input; 240 241 while((new_input = get_input(input)) == input) 242 frame(); 243 244 return new_input; 245 } 246 247 void clear_screen(void){ 248 memset(pixels,0,NUM_PIXELS); 249 glClear(GL_COLOR_BUFFER_BIT); 250 } 251 252 void draw(int x, int y, int value){ 253 assert(0<=x && x<64); 254 assert(0<=y && y<32); 255 256 pixels[y*64+x] = value ? 255 : 0; 257 } 258 259 void frame(void){ 260 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 64, 32, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); 261 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 262 263 glfwSwapBuffers(window); 264 glfwPollEvents(); 265 } 266 267 void set_buzzer_state(int state){ 268 static int old_state = 0; 269 if(state != old_state){ 270 ma_device_set_master_volume(&device, state); 271 old_state = state; 272 } 273 }