void cdda_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { get_audio_data(&outputs[0][0], &outputs[1][0], samples); m_audio_volume[0] = (int16_t)outputs[0][0]; m_audio_volume[1] = (int16_t)outputs[1][0]; }
static void get_audio_data(cdda_info *info, stream_sample_t *bufL, stream_sample_t *bufR, UINT32 samples_wanted) { int i, sectoread, remaining; INT16 *audio_cache = (INT16 *) info->audio_cache; /* if no file, audio not playing, audio paused, or out of disc data, just zero fill */ if (!info->disc || !info->audio_playing || info->audio_pause || (!info->audio_length && !info->audio_samples)) { if( info->disc && info->audio_playing && !info->audio_pause && !info->audio_length ) { info->audio_playing = FALSE; info->audio_ended_normally = TRUE; } memset(bufL, 0, sizeof(stream_sample_t)*samples_wanted); memset(bufR, 0, sizeof(stream_sample_t)*samples_wanted); return; } /* if we've got enough samples, just feed 'em out */ if (samples_wanted <= info->audio_samples) { for (i = 0; i < samples_wanted; i++) { *bufL++ = audio_cache[ info->audio_bptr++ ]; *bufR++ = audio_cache[ info->audio_bptr++ ]; } info->audio_samples -= samples_wanted; return; } /* we don't have enough, so first feed what we've got */ for (i = 0; i < info->audio_samples; i++) { *bufL++ = audio_cache[ info->audio_bptr++ ]; *bufR++ = audio_cache[ info->audio_bptr++ ]; } /* remember how much left for later */ remaining = samples_wanted - info->audio_samples; /* reset the buffer and get what we can from the disc */ info->audio_samples = 0; if (info->audio_length >= MAX_SECTORS) { sectoread = MAX_SECTORS; } else { sectoread = info->audio_length; } for (i = 0; i < sectoread; i++) { cdrom_read_data(info->disc, info->audio_lba, &info->audio_cache[CD_MAX_SECTOR_DATA*i], CD_TRACK_AUDIO); info->audio_lba++; } info->audio_samples = (CD_MAX_SECTOR_DATA*sectoread)/4; info->audio_length -= sectoread; /* CD-DA data on the disc is big-endian, flip if we're not */ if (ENDIANNESS_NATIVE == ENDIANNESS_LITTLE) { for( i = 0; i < info->audio_samples * 2; i++ ) { audio_cache[ i ] = BIG_ENDIANIZE_INT16( audio_cache[ i ] ); } } /* reset feedout ptr */ info->audio_bptr = 0; /* we've got data, feed it out by calling ourselves recursively */ get_audio_data(info, bufL, bufR, remaining); }
static STREAM_UPDATE( cdda_update ) { cdda_info *info = (cdda_info *)param; get_audio_data(info, &outputs[0][0], &outputs[1][0], samples); }
int main(int argc, char **argv) { if(argc != 2) exit(0); if(!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); return -1; } glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); window = glfwCreateWindow(1920, 1080, "Audio Visualization", glfwGetPrimaryMonitor(), NULL); if(window == NULL) { fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n"); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glewExperimental = true; //Needed for core profile if(glewInit() != GLEW_OK) { fprintf(stderr, "Failed to initialize GLEW\n"); return -1; } glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); glfwSetInputMode(window, GLFW_CURSOR_DISABLED, GL_TRUE); // glClearColor(0.f / 255.f, 63.f / 255.f, 0.f / 255.f, 1.0f); glClearColor(0.1, 0.1, 0.1, 1.0); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); GLuint VertexArrayID; glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); /* ShaderInfo shaders[] = { {GL_VERTEX_SHADER, "VertexShader.vert"}, {GL_FRAGMENT_SHADER, "FragmentShader.frag"}, {GL_GEOMETRY_SHADER, "GeometryShader.geom"}, {GL_NONE, NULL}}; GLuint programID = LoadShaders(shaders);*/ GLuint programID = LoadShaders("shaders/VertexShader.vert", "shaders/FragmentShader.frag", NULL);//"shaders/GeometryShader.geom"); GLuint MatrixID = glGetUniformLocation(programID, "MVP"); GLuint objectID = glGetUniformLocation(programID, "object"); GLuint topID = glGetUniformLocation(programID, "top"); glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f); glm::mat4 View = glm::lookAt( glm::vec3(10,15,20), //Camera is at (4,3,-3), in World Space glm::vec3(0,0,0), //and looks at the origin glm::vec3(0,1,0)); //Head is up (set to 0,-1,0 to look upside-down) glm::mat4 Model = glm::mat4(1.0f); glm::mat4 PV = Projection * View; glm::mat4 MVP = PV * Model; static const GLfloat g_vertex_buffer_data1[] = { -1.0f,-1.0f,-1.0f, //left -1.0f,-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f,-1.0f, -1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, //right 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, -1.0f,-1.0f, 1.0f, //front 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, //top 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,-1.0f,-1.0f, //back 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f,-1.0f, 1.0f, 1.0f,-1.0f, -1.0f,-1.0f,-1.0f, //bottom 1.0f,-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, -1.0f,-1.0f,-1.0f, -1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f }; static const GLfloat g_vertex_buffer_data2[] = { -1.0f,-1.0f,-1.0f, //left -1.0f,-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f,-1.0f, -1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, //right 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, -1.0f,-1.0f, 1.0f, //front 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, //top 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,-1.0f,-1.0f, //back 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f,-1.0f, 1.0f, 1.0f,-1.0f, -1.0f,-1.0f,-1.0f, //bottom 1.0f,-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, -1.0f,-1.0f,-1.0f, -1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f }; static const GLfloat g_color_buffer_data1[] = { 1.0f, 0.2f, 0.2f, //left 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, //right 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, //front 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, //top 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, //back 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.5f, 0.5f, 1.0f, 0.2f, 0.2f, //bottom 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.5f, 0.5f }; static const GLfloat g_color_buffer_data2[] = { 0.2f, 0.2f, 1.0f, //left 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, 1.0f, //right 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, //front 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, //top 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.2f, 0.2f, 1.0f, //back 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.5f, 0.5f, 1.0f, 0.2f, 0.2f, 1.0f, //bottom 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.2f, 0.2f, 1.0f, 0.5f, 0.5f, 1.0f, 0.5f, 0.5f, 1.0f }; GLuint vertexbuffer1; //left column glGenBuffers(1, &vertexbuffer1); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer1); glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data1), g_vertex_buffer_data1, GL_STATIC_DRAW); GLuint vertexbuffer2; //right column glGenBuffers(1, &vertexbuffer2); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer2); glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data2), g_vertex_buffer_data2, GL_STATIC_DRAW); GLuint vertexbuffer3; //waveform glGenBuffers(1, &vertexbuffer3); GLuint vertexbuffer4; //z glGenBuffers(1, &vertexbuffer4); GLuint vertexbuffer5; //spectrum glGenBuffers(1, &vertexbuffer5); GLuint colorbuffer1; glGenBuffers(1, &colorbuffer1); glBindBuffer(GL_ARRAY_BUFFER, colorbuffer1); glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data1), g_color_buffer_data1, GL_STATIC_DRAW); GLuint colorbuffer2; glGenBuffers(1, &colorbuffer2); glBindBuffer(GL_ARRAY_BUFFER, colorbuffer2); glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data2), g_color_buffer_data2, GL_STATIC_DRAW); audio_data data = get_audio_data(argv[1]); int bpf = data.sampling_rate / fps; int data_index = 0; double current_time; double last_time; double total_time = data.size / data.sampling_rate / 4; float z[bpf * 2]; for(int i = 0; i < bpf; i++) z[i * 2] = z[i * 2 + 1] = waveform_length / bpf * i - waveform_length / 2; glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer4); glBufferData(GL_ARRAY_BUFFER, bpf * 8, z, GL_STATIC_DRAW); int res; pthread_t a_thread; void *thread_result; res = pthread_create(&a_thread, NULL, play_wav_d, argv[1]); if(res != 0) { perror("Thread creation failed!"); exit(EXIT_FAILURE); } FFT fft; fft.setDataSize(bpf); fft.setSampleRate(data.sampling_rate); float max_l = 0, max_r = 0; glfwSetTime(0); do { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(programID); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); computeMatricesFromInputs(); glm::mat4 Projection = getProjectionMatrix(); glm::mat4 View = getViewMatrix(); glm::mat4 ModelMatrix = glm::mat4(1.0); PV = Projection * View; MVP = PV * ModelMatrix; glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); float *FFTdata = fft.calculateFFT((short *)data.data + data_index); int spectrum_interval = fps / 2; for(int i = 0; i < bpf; i++) { for(int j = 1; j < spectrum_interval; j++) FFTdata[i * spectrum_interval] += FFTdata[i * spectrum_interval + j]; FFTdata[i * spectrum_interval] /= spectrum_interval * 10; FFTdata[i * spectrum_interval + spectrum_interval / 2] = 0; } glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer4); //z glVertexAttribPointer( 2, //attribute. No particular reason for 0, but must match the layout in the shader. 1, //size GL_FLOAT, //type GL_FALSE, //normalized? 0, //stride (void *)0 //array buffer offset ); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer5); //spectrum glBufferData(GL_ARRAY_BUFFER, spectrum_interval * bpf * 4, FFTdata, GL_STATIC_DRAW); glVertexAttribPointer( 0, //attribute. No particular reason for 0, but must match the layout in the shader. 1, //size GL_FLOAT, //type GL_FALSE, //normalized? spectrum_interval * 2, //stride (void *)0 //array buffer offset ); glUniform1i(objectID, 6); glDrawArrays(GL_LINES, 0, bpf * 2); //draw spectrum glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer4); //z glVertexAttribPointer( 2, //attribute. No particular reason for 0, but must match the layout in the shader. 1, //size GL_FLOAT, //type GL_FALSE, //normalized? (waveform_interval + 1) * 4, //stride (void *)0 //array buffer offset ); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer3); //waveform glBufferData(GL_ARRAY_BUFFER, bpf * 4, (short *)data.data + data_index, GL_DYNAMIC_DRAW); glVertexAttribPointer( 0, //attribute. No particular reason for 0, but must match the layout in the shader. 1, //size GL_SHORT, //type GL_FALSE, //normalized? waveform_interval * 4, //stride (void *)0 //array buffer offset ); glUniform1i(objectID, 2); glDrawArrays(GL_LINE_STRIP, 0, bpf / waveform_interval); //draw left waveform glVertexAttribPointer( 0, //attribute. No particular reason for 0, but must match the layout in the shader. 1, //size GL_SHORT, //type GL_FALSE, //normalized? waveform_interval * 4, //stride (void *)2 //array buffer offset ); glUniform1i(objectID, 3); glDrawArrays(GL_LINE_STRIP, 0, bpf / waveform_interval); //draw right waveform float sum_l = 0, sum_r = 0; for(int i = 0; i < bpf; i++) { sum_l = max(sum_l, abs(((short*)data.data)[data_index++])); //max sum_r = max(sum_r, abs(((short*)data.data)[data_index++])); // sum_l += abs(((short*)data.data)[data_index++]); //avg // sum_r += abs(((short*)data.data)[data_index++]); } // sum_l /= bpf; //avg // sum_r /= bpf; float scale_l = sum_l / 32768 * column_height; Model = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.0, 1.0, 0.0, 1.0); glm::mat4 scale1 = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, scale_l, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); MVP = PV * scale1 * Model; glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer1); //left column vertex glVertexAttribPointer( 0, // attribute. No particular reason for 0, but must match the layout in the shader. 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void *)0 // array buffer offset ); glBindBuffer(GL_ARRAY_BUFFER, colorbuffer1); //left column color glVertexAttribPointer( 1, // attribute. No particular reason for 1, but must match the layout in the shader. 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void *)0 // array buffer offset ); glUniform1i(objectID, 0); glDrawArrays(GL_TRIANGLES, 0, 12*3); //draw left column if(scale_l> max_l) max_l = scale_l; else max_l -= top_speed; glm::mat4 translate1 = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.0, max_l * 2, 0.0, 1.0); scale1 = glm::mat4( max_l / 2, 0.0, 0.0, 0.0, 0.0, max_l / 2 * top_height, 0.0, 0.0, 0.0, 0.0, max_l / 2, 0.0, 0.0, 0.0, 0.0, 1.0); MVP = PV * translate1 * scale1; glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); glUniform1i(objectID, 4); glUniform1f(topID, max_l); glDrawArrays(GL_TRIANGLES, 0, 12*3); //draw left upper top translate1 = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.0, scale_l * 2, 0.0, 1.0); scale1 = glm::mat4( scale_l / 2, 0.0, 0.0, 0.0, 0.0, scale_l / 2 * top_height, 0.0, 0.0, 0.0, 0.0, scale_l / 2, 0.0, 0.0, 0.0, 0.0, 1.0); MVP = PV * translate1 * scale1; glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); glUniform1i(objectID, 4); glUniform1f(topID, scale_l); glDrawArrays(GL_TRIANGLES, 0, 12*3); //draw left lower top float scale_r = sum_r / 32768 * column_height; Model = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, 1.0, 0.0, 1.0); glm::mat4 scale2 = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, scale_r, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); MVP = PV * scale2 * Model; glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer2); //right column vertex glVertexAttribPointer( 0, // attribute. No particular reason for 0, but must match the layout in the shader. 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); glBindBuffer(GL_ARRAY_BUFFER, colorbuffer2); //right column vertex glVertexAttribPointer( 1, // attribute. No particular reason for 1, but must match the layout in the shader. 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); glUniform1i(objectID, 1); glDrawArrays(GL_TRIANGLES, 0, 12*3); //draw right column if(scale_r > max_r) max_r = scale_r; else max_r -= top_speed; glm::mat4 translate2 = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, max_r * 2, 0.0, 1.0); scale2 = glm::mat4( max_r / 2, 0.0, 0.0, 0.0, 0.0, max_r / 2 * top_height, 0.0, 0.0, 0.0, 0.0, max_r / 2, 0.0, 0.0, 0.0, 0.0, 1.0);//0.25 / max_r);//(4.0 - max_r) * (4.0 - max_r)); MVP = PV * translate2 * scale2; glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); glUniform1i(objectID, 5); glUniform1f(topID, max_r); glDrawArrays(GL_TRIANGLES, 0, 12*3); //draw right upper top translate2 = glm::mat4( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, scale_r * 2, 0.0, 1.0); scale2 = glm::mat4( scale_r / 2, 0.0, 0.0, 0.0, 0.0, scale_r / 2 * top_height, 0.0, 0.0, 0.0, 0.0, scale_r / 2, 0.0, 0.0, 0.0, 0.0, 1.0);//0.25 / max_r);//(4.0 - max_r) * (4.0 - max_r)); MVP = PV * translate2 * scale2; glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); glUniform1i(objectID, 5); glUniform1f(topID, scale_r); glDrawArrays(GL_TRIANGLES, 0, 12*3); //draw right lower top glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); if(data_index >= data.size / 2) break; current_time = glfwGetTime(); double left_time = total_time - current_time; if(left_time <= 0) break; double accurate_time = data_index / 2.0 / bpf / fps; double delta = accurate_time - current_time; printf("%lf %lf %lf %lf %lf\n", current_time - last_time, accurate_time, current_time, delta, left_time); delta = delta > 0 ? delta : 0; last_time = current_time; usleep(delta * 1000000); glfwSwapBuffers(window); glfwPollEvents(); } while(glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && !glfwWindowShouldClose(window)); pthread_cancel(a_thread); res = pthread_join(a_thread, &thread_result); if(res != 0) { perror("Thread join failed!"); exit(EXIT_FAILURE); } glDeleteBuffers(1, &vertexbuffer1); glDeleteBuffers(1, &vertexbuffer2); glDeleteBuffers(1, &vertexbuffer3); glDeleteBuffers(1, &vertexbuffer4); glDeleteBuffers(1, &vertexbuffer5); glDeleteBuffers(1, &colorbuffer1); glDeleteBuffers(1, &colorbuffer2); glDeleteProgram(programID); glDeleteVertexArrays(1, &VertexArrayID); glfwTerminate(); return 0; }