pocplay/main.c
2026-07-20 05:03:19 +09:00

342 lines
7.6 KiB
C

#include <Mw/Milsko.h>
#include <Mw/Widget/OpenGL.h>
#include "pl_mpeg.h"
#include "miniaudio.h"
#include "stb_ds.h"
#define MULTIPLE_BUFFER
#define PERIOD PLM_AUDIO_SAMPLES_PER_FRAME
#define BUFFERS 3
typedef struct buffer {
float buffer[PERIOD * 2];
} buffer_t;
static ma_device device;
static buffer_t buffers[BUFFERS];
static int buf_rseek = 0;
static int buf_wseek = 0;
static int buf_rframe = 0;
static int buf_wframe = 0;
static ma_mutex buf_mutex;
static MwWidget box, opengl, progress;
static plm_t* plm = NULL;
static GLuint tex = 0;
static unsigned char* vid = NULL;
static int vw, vh;
static int ww, wh;
static double last;
static int progress_move = 1;
static int paused = 0;
static int sample_rate = 0;
static void camera(int w, int h){
MwOpenGLMakeCurrent(opengl);
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, h, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
static void MWAPI resize(MwWidget widget, void* user, void* call){
ww = MwGetInteger(widget, MwNwidth);
wh = MwGetInteger(widget, MwNheight);
MwVaApply(box,
MwNwidth, ww,
MwNheight, wh,
NULL);
}
static void MWAPI opengl_resize(MwWidget widget, void* user, void* call){
camera(MwGetInteger(widget, MwNwidth), MwGetInteger(widget, MwNheight));
}
static void MWAPI progress_mouseDown(MwWidget widget, void* user, void* call){
MwMouse* m = call;
if(m->button == MwMOUSE_LEFT) progress_move = 0;
}
static void MWAPI progress_mouseUp(MwWidget widget, void* user, void* call){
MwMouse* m = call;
if(m->button == MwMOUSE_LEFT) progress_move = 1;
plm_seek(plm, (double)MwGetInteger(widget, MwNvalue) / 1000, 1);
}
static void MWAPI play_activate(MwWidget widget, void* user, void* call){
plm_rewind(plm);
}
static void MWAPI pause_activate(MwWidget widget, void* user, void* call){
paused = !paused;
}
static void on_audio(plm_t *mpeg, plm_samples_t *samples, void *user) {
buffer_t buf;
int i;
int total;
total = samples->count;
for(i = 0; i < total; i++){
int b = i * 2;
buf.buffer[i * 2 + 0] = samples->interleaved[b + 0];
buf.buffer[i * 2 + 1] = samples->interleaved[b + 1];
}
ma_mutex_lock(&buf_mutex);
buffers[buf_wseek++] = buf;
if(buf_wseek == BUFFERS) buf_wseek = 0;
buf_wframe++;
ma_mutex_unlock(&buf_mutex);
}
static void on_video(plm_t* mpeg, plm_frame_t* frame, void* user){
plm_frame_to_rgb(frame, vid, vw * 3);
MwOpenGLMakeCurrent(opengl);
glBindTexture(GL_TEXTURE_2D, tex);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, vw, vh, GL_RGB, GL_UNSIGNED_BYTE, vid);
glBindTexture(GL_TEXTURE_2D, 0);
}
static void MWAPI tick(MwWidget widget, void* user, void* call){
double sx = 0;
double sy = 0;
double sw = 0;
double sh = 0;
if(((double)ww / vw) < ((double)wh / vh)){
sw = ww;
sh = (double)ww / vw * vh;
}else{
sh = wh;
sw = (double)wh / vh * vw;
}
sx = ((double)MwGetInteger(opengl, MwNwidth) - sw) / 2;
sy = ((double)MwGetInteger(opengl, MwNheight) - sh) / 2;
MwOpenGLMakeCurrent(opengl);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glColor3f(1, 1, 1);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(sx, sy);
glTexCoord2f(0, 1); glVertex2f(sx, sy + sh);
glTexCoord2f(1, 1); glVertex2f(sx + sw, sy + sh);
glTexCoord2f(1, 0); glVertex2f(sx + sw, sy);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
MwOpenGLSwapBuffer(opengl);
}
static int load(const char* input){
int i;
buf_wframe = buf_wseek = 0;
memset(&buffers, 0, sizeof(buffers));
if(plm != NULL) plm_destroy(plm);
if((plm = plm_create_with_filename(input)) == NULL || !plm_probe(plm, 5000 * 1024)){
if(plm != NULL){
plm_destroy(plm);
plm = NULL;
}
return 1;
}
plm_set_audio_decode_callback(plm, on_audio, NULL);
plm_set_video_decode_callback(plm, on_video, NULL);
plm_set_loop(plm, 1);
sample_rate = plm_get_samplerate(plm);
MwOpenGLMakeCurrent(opengl);
vw = plm_get_width(plm);
vh = plm_get_height(plm);
MwVaApply(progress,
MwNmaxValue, (int)(plm_get_duration(plm) * 1000),
MwNvalue, 0,
MwNareaShown, 1000 * 10,
NULL);
if(vid != NULL) free(vid);
vid = malloc(vw * vh * 3);
memset(vid, 0, vw * vh * 3);
for(i = 0; i < vw * vh; i++){
vid[i * 3 + 0] = i / vw * 255 / vh;
vid[i * 3 + 2] = (i % vw) * 255 / vw;
}
if(tex != 0) glDeleteTextures(1, &tex);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, vw, vh, 0, GL_RGB, GL_UNSIGNED_BYTE, vid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
last = (double)MwTimeGetTick() / 1000;
return 0;
}
static void data_callback(ma_device* device, void* output, const void* input, ma_uint32 frame){
short* o = output;
int i;
memset(o, 0, frame * sizeof(short) * 2);
ma_mutex_lock(&buf_mutex);
for(i = 0; i < frame * 2; i++){
o[i] = buffers[buf_rseek].buffer[i] * 32767;
}
ma_mutex_unlock(&buf_mutex);
buf_rseek++;
if(buf_rseek == BUFFERS) buf_rseek = 0;
buf_rframe++;
if(buf_rframe > buf_wframe){
buf_rframe = buf_wframe;
buf_rseek = buf_wseek;
}
}
static int init(void){
ma_device_config config;
ma_mutex_init(&buf_mutex);
config = ma_device_config_init(ma_device_type_playback);
config.playback.format = ma_format_s16;
config.playback.channels = 2;
config.sampleRate = 48000;
config.dataCallback = data_callback;
config.pUserData = NULL;
config.periodSizeInFrames = PERIOD;
if(ma_device_init(NULL, &config, &device) != MA_SUCCESS){
return 1;
}
if(ma_device_start(&device) != MA_SUCCESS){
ma_device_uninit(&device);
return 1;
}
MwOpenGLMakeCurrent(opengl);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
return 0;
}
int main(int argc, char** argv){
MwWidget w, bar, play, pause;
int st;
MwLibraryInit();
w = MwVaCreateWidget(MwWindowClass, "main", NULL, MwDEFAULT, MwDEFAULT, 800, 630,
MwNtitle, "poc player",
NULL);
box = MwVaCreateWidget(MwBoxClass, "box", w, 0, 0, 0, 0,
MwNorientation, MwVERTICAL,
NULL);
opengl = MwCreateWidget(MwOpenGLClass, "opengl", box, 0, 0, 0, 0);
bar = MwVaCreateWidget(MwBoxClass, "bar", box, 0, 0, 0, 0,
MwNfixedSize, 30,
MwNorientation, MwHORIZONTAL,
NULL);
play = MwVaCreateWidget(MwButtonClass, "play", bar, 0, 0, 0, 0,
MwNtext, "Play",
MwNfixedSize, 60,
NULL);
pause = MwVaCreateWidget(MwButtonClass, "pause", bar, 0, 0, 0, 0,
MwNtext, "Pause",
MwNfixedSize, 60,
NULL);
progress = MwVaCreateWidget(MwScrollBarClass, "progress", bar, 0, 0, 0, 0,
MwNshowArrows, 0,
MwNorientation, MwHORIZONTAL,
NULL);
MwAddUserHandler(w, MwNresizeHandler, resize, NULL);
MwAddUserHandler(w, MwNtickHandler, tick, NULL);
MwAddUserHandler(opengl, MwNresizeHandler, opengl_resize, NULL);
MwAddUserHandler(progress, MwNmouseDownHandler, progress_mouseDown, NULL);
MwAddUserHandler(progress, MwNmouseUpHandler, progress_mouseUp, NULL);
MwAddUserHandler(play, MwNactivateHandler, play_activate, NULL);
MwAddUserHandler(pause, MwNactivateHandler, pause_activate, NULL);
if((st = init()) != 0) return st;
resize(w, NULL, NULL);
if((st = load(argv[1])) != 0) return st;
while(1){
double l2, elapsed;
while(MwPending(w)) MwStep(w);
elapsed = (l2 = (double)MwTimeGetTick() / 1000) - last;
if(elapsed > 1.0 / 30) elapsed = 1.0 / 30;
if(paused){
buffer_t buf;
memset(&buf, 0, sizeof(buf));
ma_mutex_lock(&buf_mutex);
buffers[buf_wseek] = buf;
if(buf_wseek == BUFFERS) buf_wseek = 0;
ma_mutex_unlock(&buf_mutex);
}else{
plm_decode(plm, elapsed);
last = l2;
if(progress_move){
MwVaApply(progress,
MwNvalue, (int)(plm_get_time(plm) * 1000),
NULL);
}
}
}
}