shithub: mp3dec

Download patch

ref: 29f6dca321808736a6f671692e647b95fda2d288
parent: f3efc075704f05d344e628307b07821ab1c568fe
author: lieff <[email protected]>
date: Mon Feb 5 13:15:07 EST 2018

player: add threaded decoder WIP

--- a/player/build.sh
+++ b/player/build.sh
@@ -1,1 +1,1 @@
-gcc -O2 -o player player.cpp glad.c -lstdc++ -lglfw -lGL -lm -ldl
\ No newline at end of file
+gcc -O2 -o player *.cpp *.c -lstdc++ -lglfw -lGL -lpthread -lm -ldl
\ No newline at end of file
--- /dev/null
+++ b/player/decode.c
@@ -1,0 +1,122 @@
+#include <string.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#define MINIMP3_IMPLEMENTATION
+#include "../minimp3.h"
+#include "decode.h"
+
+static void add_audio(decoder *dec, short *buf, int bytes)
+{
+    char *buf_new = 0;
+    if (dec->mp3_allocated < (dec->mp3_size + bytes))
+    {
+        int allocated = dec->mp3_allocated;
+        dec->mp3_allocated = dec->mp3_allocated ? dec->mp3_allocated*2 : 4*1024*1024;
+        buf_new = (char *)malloc(dec->mp3_allocated);
+        memcpy(buf_new, dec->mp3_buf, allocated);
+    }
+    EnterCriticalSection(&dec->mp3_lock);
+    if (buf_new)
+    {
+        if (dec->mp3_buf)
+            free(dec->mp3_buf);
+        dec->mp3_buf = (short *)buf_new;
+        dec->mp3_size += bytes;
+    }
+    LeaveCriticalSection(&dec->mp3_lock);
+}
+
+static void *load_mp3_thread(void *lpThreadParameter)
+{
+    static mp3dec_t mp3d;
+    mp3dec_frame_info_t info = { 0, };
+    struct stat st;
+    decoder *dec = (decoder *)lpThreadParameter;
+    unsigned char *inputBuffer, *buf_mp3;
+    int file, mp3_size, samples;
+retry_open:
+    file = open(dec->file_name, O_RDONLY);
+    if (file < 0 && (errno == EAGAIN || errno == EINTR))
+        goto retry_open;
+    free((void*)dec->file_name);
+    dec->file_name = 0;
+    if (file < 0 || fstat(file, &st) < 0)
+        return (void*)(size_t)errno;
+
+    dec->mp3_file_size = st.st_size;
+retry_mmap:
+    inputBuffer = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, file, 0);
+    if (MAP_FAILED == inputBuffer && (errno == EAGAIN || errno == EINTR))
+        goto retry_mmap;
+    if (MAP_FAILED == inputBuffer)
+    {
+        close(file);
+        return (void*)(size_t)errno;
+    }
+    buf_mp3 = inputBuffer;
+    mp3_size = dec->mp3_file_size;
+    mp3dec_init(&mp3d);
+    do
+    {
+        short pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
+        samples = mp3dec_decode_frame(&mp3d, buf_mp3, mp3_size, pcm, &info);
+        if (samples)
+        {
+            add_audio(dec, pcm, samples*2*info.channels);
+            if (!dec->mp3_rate)
+                dec->mp3_rate = info.hz;
+            if (!dec->mp3_channels)
+                dec->mp3_channels = info.channels;
+            if (dec->mp3_rate != info.hz || dec->mp3_channels != info.channels)
+                break;
+        }
+        buf_mp3  += info.frame_bytes;
+        mp3_size -= info.frame_bytes;
+    } while (info.frame_bytes);
+
+    munmap(inputBuffer, st.st_size);
+    close(file);
+    return 0;
+}
+
+int preload_mp3(decoder *dec, const char *file_name)
+{
+    if (dec->mp3_open_thread)
+    {
+        thread_wait(dec->mp3_open_thread);
+        thread_close(dec->mp3_open_thread);
+        dec->mp3_open_thread = 0;
+    }
+    EnterCriticalSection(&dec->mp3_lock);
+    if (dec->mp3_buf)
+    {
+        free(dec->mp3_buf);
+        dec->mp3_buf = 0;
+        dec->mp3_size = 0;
+        dec->mp3_allocated = 0;
+        dec->mp3_rate = 0;
+        dec->mp3_channels = 0;
+        dec->mp3_duration = 0;
+    }
+    LeaveCriticalSection(&dec->mp3_lock);
+
+    if (!file_name || !*file_name)
+        return 0;
+
+    dec->file_name = strdup(file_name);
+    if (!dec->file_name)
+        return 0;
+    dec->mp3_open_thread = thread_create(load_mp3_thread, dec);
+    if (!dec->mp3_open_thread)
+    {
+        free((void*)dec->file_name);
+        dec->file_name = 0;
+        return 0;
+    }
+    return 1;
+}
--- /dev/null
+++ b/player/decode.h
@@ -1,0 +1,14 @@
+#pragma once
+#include "system.h"
+
+typedef struct decoder
+{
+    CRITICAL_SECTION mp3_lock;
+    const char *file_name;
+    short *mp3_buf;
+    int mp3_size, mp3_allocated, mp3_file_size, mp3_rate, mp3_channels;
+    float mp3_duration;
+    HANDLE mp3_open_thread;
+} decoder;
+
+int preload_mp3(decoder *dec, const char *file_name);
--- a/player/player.cpp
+++ b/player/player.cpp
@@ -10,7 +10,6 @@
 #include <OpenGL/gl3.h>
 #endif
 #include "FreeSans.h"
-#include "../minimp3.h"
 
 #define STB_IMAGE_IMPLEMENTATION
 #include "stb_image.h"
--- /dev/null
+++ b/player/system.cpp
@@ -1,0 +1,473 @@
+#include "system.h"
+
+#ifndef _WIN32
+
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#if defined(__linux) || defined(__linux__)
+#include <sys/prctl.h>
+#endif
+
+#define nullptr 0
+
+struct Event
+{
+    Event * volatile pMultipleCond;
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    volatile bool signaled;
+    bool manual_reset;
+};
+
+static bool InitEvent(Event *e)
+{
+#if (defined(ANDROID) && !defined(__LP64__)) || defined(__APPLE__)
+    if (pthread_cond_init(&e->cond, NULL))
+        return false;
+#else
+    pthread_condattr_t attr;
+    if (pthread_condattr_init(&attr))
+        return false;
+    if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC))
+    {
+        pthread_condattr_destroy(&attr);
+        return false;
+    }
+    if (pthread_cond_init(&e->cond, &attr))
+    {
+        pthread_condattr_destroy(&attr);
+        return false;
+    }
+    pthread_condattr_destroy(&attr);
+#endif
+    if (pthread_mutex_init(&e->mutex, NULL))
+    {
+        pthread_cond_destroy(&e->cond);
+        return false;
+    }
+    e->pMultipleCond = NULL;
+    return true;
+}
+
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+static inline uint64_t GetAbsTimeInNanoseconds()
+{
+    static mach_timebase_info_data_t g_timebase_info;
+    if (g_timebase_info.denom == 0)
+        mach_timebase_info(&g_timebase_info);
+    return mach_absolute_time()*g_timebase_info.numer/g_timebase_info.denom;
+}
+#endif
+
+static inline void GetAbsTime(timespec *ts, uint32_t timeout)
+{
+#if defined(__APPLE__)
+    uint64_t cur_time = GetAbsTimeInNanoseconds();
+    ts->tv_sec  = cur_time/1000000000u + timeout/1000u;
+    ts->tv_nsec = (cur_time % 1000000000u) + (timeout % 1000u)*1000000u;
+#else
+    clock_gettime(CLOCK_MONOTONIC, ts);
+    ts->tv_sec  += timeout/1000u;
+    ts->tv_nsec += (timeout % 1000u)*1000000u;
+#endif
+    if (ts->tv_nsec >= 1000000000)
+    {
+        ts->tv_nsec -= 1000000000;
+        ts->tv_sec++;
+    }
+}
+
+static inline int CondTimedWait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+#if defined(ANDROID) && !defined(__LP64__)
+    return pthread_cond_timedwait_monotonic_np(cond, mutex, abstime);
+#elif defined(__APPLE__)
+    timespec reltime;
+    uint64_t cur_time = GetAbsTimeInNanoseconds();
+    reltime.tv_sec  = abstime->tv_sec  - cur_time/1000000000u;
+    reltime.tv_nsec = abstime->tv_nsec - (cur_time % 1000000000u);
+    if (reltime.tv_nsec < 0)
+    {
+        reltime.tv_nsec += 1000000000;
+        reltime.tv_sec--;
+    }
+    if ((reltime.tv_sec < 0) || ((reltime.tv_sec == 0) && (reltime.tv_nsec == 0)))
+        return ETIMEDOUT;
+    return pthread_cond_timedwait_relative_np(cond, mutex, &reltime);
+#else
+    return pthread_cond_timedwait(cond, mutex, abstime);
+#endif
+}
+
+static bool WaitForEvent(Event *e, uint32_t timeout, bool *signaled)
+{
+    if (pthread_mutex_lock(&e->mutex))
+        return false;
+
+    if (timeout == INFINITE)
+    {
+        while (!e->signaled)
+            pthread_cond_wait(&e->cond, &e->mutex);
+    } else if (timeout != 0)
+    {
+        timespec t;
+        GetAbsTime(&t, timeout);
+        while (!e->signaled)
+        {
+            if (CondTimedWait(&e->cond, &e->mutex, &t))
+                break;
+        }
+    }
+    *signaled = e->signaled;
+    if (!e->manual_reset)
+        e->signaled = false;
+
+    if (pthread_mutex_unlock(&e->mutex))
+        return false;
+    return true;
+}
+
+static bool WaitForMultipleEvents(Event **e, uint32_t count, uint32_t timeout, bool waitAll, int *signaled_num)
+{
+    uint32_t i;
+#define PTHR(func, num) for (i = num; i < count; i++)\
+        if (func(&e[i]->mutex))\
+            return false;
+    PTHR(pthread_mutex_lock, 0);
+
+    int sig_num = -1;
+    if (timeout == 0)
+    {
+#define CHECK_SIGNALED \
+        if (waitAll)\
+        {\
+            for (i = 0; i < count; i++)\
+                if (!e[i]->signaled)\
+                    break;\
+            if (i == count)\
+                for (i = 0; i < count; i++)\
+                {\
+                    if (sig_num < 0 && e[i]->signaled)\
+                        sig_num = (int)i;\
+                    if (!e[i]->manual_reset)\
+                        e[i]->signaled = false;\
+                }\
+        } else\
+        {\
+            for (i = 0; i < count; i++)\
+                if (e[i]->signaled)\
+                {\
+                    sig_num = (int)i;\
+                    if (!e[i]->manual_reset)\
+                        e[i]->signaled = false;\
+                    break;\
+                }\
+        }
+        CHECK_SIGNALED;
+    } else
+    if (timeout == INFINITE)
+    {
+#define SET_MULTIPLE(val) for (i = 1; i < count; i++)\
+            e[i]->pMultipleCond = val;
+        SET_MULTIPLE(e[0]);
+        for (;;)
+        {
+            CHECK_SIGNALED;
+            if (sig_num >= 0)
+                break;
+            PTHR(pthread_mutex_unlock, 1);
+            pthread_cond_wait(&e[0]->cond, &e[0]->mutex);
+            PTHR(pthread_mutex_lock, 1);
+        }
+        SET_MULTIPLE(0);
+    } else
+    {
+        SET_MULTIPLE(e[0]);
+        timespec t;
+        GetAbsTime(&t, timeout);
+        for (;;)
+        {
+            CHECK_SIGNALED;
+            if (sig_num >= 0)
+                break;
+            PTHR(pthread_mutex_unlock, 1);
+            int res = CondTimedWait(&e[0]->cond, &e[0]->mutex, &t);
+            PTHR(pthread_mutex_lock, 1);
+            if (res)
+                break;
+        }
+        SET_MULTIPLE(0);
+    }
+    PTHR(pthread_mutex_unlock, 0);
+    *signaled_num = sig_num;
+    return true;
+}
+
+static bool SignalEvent(Event *e)
+{
+    if (pthread_mutex_lock(&e->mutex))
+        return false;
+
+    Event *pMultipleCond = e->pMultipleCond;
+    e->signaled = true;
+    if (pthread_cond_signal(&e->cond))
+        return false;
+
+    if (pthread_mutex_unlock(&e->mutex))
+        return false;
+
+    if (pMultipleCond && pMultipleCond != e)
+    {
+        if (pthread_mutex_lock(&pMultipleCond->mutex))
+            return false;
+        if (pthread_cond_signal(&pMultipleCond->cond))
+            return false;
+        if (pthread_mutex_unlock(&pMultipleCond->mutex))
+            return false;
+    }
+    return true;
+}
+
+static bool ResetEvent(Event *e)
+{
+    if (pthread_mutex_lock(&e->mutex))
+        return false;
+    e->signaled = false;
+    if (pthread_mutex_unlock(&e->mutex))
+        return false;
+    return true;
+}
+
+HANDLE event_create(bool manualReset, bool initialState)
+{
+    Event *e = (Event *)malloc(sizeof(*e));
+    if (!e)
+        return NULL;
+    if (!InitEvent(e))
+    {
+        free(e);
+        return NULL;
+    }
+    e->manual_reset = manualReset;
+    e->signaled     = initialState;
+    return (HANDLE)e;
+}
+
+bool event_destroy(HANDLE event)
+{
+    Event *e = (Event *)event;
+    if (!e)
+        return false;
+    if (pthread_cond_destroy(&e->cond))
+        return false;
+    if (pthread_mutex_destroy(&e->mutex))
+        return false;
+    free((void *)e);
+    return true;
+}
+
+bool event_set(HANDLE event)
+{
+    return SignalEvent((Event *)event);
+}
+
+bool event_reset(HANDLE event)
+{
+    return ResetEvent((Event *)event);
+}
+
+int event_wait(HANDLE event, uint32_t milliseconds)
+{
+    bool signaled;
+    if (!WaitForEvent((Event *)event, milliseconds, &signaled))
+        return WAIT_FAILED;
+    return signaled ? WAIT_OBJECT : WAIT_TIMEOUT;
+}
+
+int event_wait_multiple(uint32_t count, const HANDLE *events, bool waitAll, uint32_t milliseconds)
+{
+    if (count == 1)
+        return event_wait(events[0], milliseconds);
+    int signaled_num = -1;
+    if (!WaitForMultipleEvents((Event **)events, count, milliseconds, waitAll, &signaled_num))
+        return WAIT_FAILED;
+    return (signaled_num < 0) ? WAIT_TIMEOUT : (WAIT_OBJECT_0 + signaled_num);
+}
+
+bool InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+    pthread_mutexattr_t ma;
+    if (pthread_mutexattr_init(&ma))
+        return false;
+    if (pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE))
+    {
+        pthread_mutexattr_destroy(&ma);
+        return false;
+    }
+    if (pthread_mutex_init((pthread_mutex_t *)lpCriticalSection, &ma))
+    {
+        pthread_mutexattr_destroy(&ma);
+        return false;
+    }
+    if (pthread_mutexattr_destroy(&ma))
+        return false;
+    return true;
+}
+
+bool DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+    if (pthread_mutex_destroy((pthread_mutex_t *)lpCriticalSection))
+        return false;
+    return true;
+}
+
+bool EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+    if (pthread_mutex_lock((pthread_mutex_t *)lpCriticalSection))
+        return false;
+    return true;
+}
+
+bool LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 
+{
+    if (pthread_mutex_unlock((pthread_mutex_t *)lpCriticalSection))
+        return false;
+    return true;
+}
+
+HANDLE thread_create(LPTHREAD_START_ROUTINE lpStartAddress, void *lpParameter)
+{
+    pthread_t *t = (pthread_t *)malloc(sizeof(*t));
+    if (!t)
+        return nullptr;
+    if (pthread_create(t, 0, lpStartAddress, lpParameter))
+    {
+        free(t);
+        return nullptr;
+    }
+    //if (lpThreadId)
+    //    *lpThreadId = (uint32_t)*t;
+    return (HANDLE)t;
+}
+
+bool thread_close(HANDLE thread)
+{
+    if (!thread)
+        return false;
+    pthread_t *t = (pthread_t *)thread;
+    if (*t)
+        pthread_detach(*t);
+    free(t);
+    return true;
+}
+
+void *thread_wait(HANDLE thread)
+{
+    if (!thread)
+        return (void*)-1;
+    void *ret = 0;
+    pthread_t *t = (pthread_t *)thread;
+    if (!*t)
+        return ret;
+    int res = pthread_join(*t, &ret);
+    if (res)
+        return (void*)-1;
+#if 0
+    if (timeout == 0)
+    {
+        int res = pthread_tryjoin_np(*t, &ret);
+        if (res)
+            return false;
+    } else
+    if (timeout == INFINITE)
+    {
+        int res = pthread_join(*t, &ret);
+        if (res)
+            return false;
+    } else
+    {
+        timespec ts;
+        GetAbsTime(&ts, timeout);
+        int res = pthread_timedjoin_np(*t, &ret, &ts);
+        if (res)
+            return false;
+    }
+#endif
+    *t = 0; // thread joined - no need to detach
+    return ret;
+}
+
+#else  //_WIN32
+
+bool event_destroy(HANDLE event)
+{
+    CloseHandle(event);
+    return true;
+}
+
+bool thread_close(HANDLE thread)
+{
+    CloseHandle(thread);
+    return true;
+}
+
+bool thread_wait(HANDLE thread)
+{
+    return WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0;
+}
+
+#endif //_WIN32
+
+bool thread_name(const char *name)
+{
+#ifdef _WIN32
+    struct tagTHREADNAME_INFO
+    {
+        DWORD dwType;
+        LPCSTR szName;
+        DWORD dwThreadID;
+        DWORD dwFlags;
+    } info = { 0x1000, name, (DWORD)-1, 0 };
+    __try
+    {
+        RaiseException(0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+    }
+    __except(EXCEPTION_EXECUTE_HANDLER)
+    {
+    }
+    return true;
+#elif defined(__linux) || defined(__linux__)
+    return (0 == prctl(PR_SET_NAME, name, 0, 0, 0));
+    //return (0 == pthread_setname_np(pthread_self(), name));
+#else // macos, ios
+    return (0 == pthread_setname_np(name));
+#endif
+}
+
+bool thread_sleep(uint32_t milliseconds)
+{
+#ifdef _WIN32
+    Sleep(milliseconds);
+#else
+    usleep((useconds_t)milliseconds*1000);
+#endif
+}
+
+uint64_t GetTime()
+{
+    uint64_t time;
+#ifdef _WIN32
+    QueryPerformanceCounter((LARGE_INTEGER*)&time);
+#elif defined(__APPLE__)
+    time = GetAbsTimeInNanoseconds() / 1000u;
+#else
+    timespec ts;
+    // CLOCK_PROCESS_CPUTIME_ID CLOCK_THREAD_CPUTIME_ID
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    time = (uint64_t)ts.tv_sec * 1000000u + ts.tv_nsec / 1000u;
+#endif
+    return time;
+}
--- /dev/null
+++ b/player/system.h
@@ -1,0 +1,107 @@
+#pragma once
+#ifndef __LGE_SYSTEM_H__
+#define __LGE_SYSTEM_H__
+
+#ifdef _WIN32
+
+#include <windows.h>
+typedef DWORD THREAD_RET;
+#define THRAPI __stdcall
+#include <stdint.h>
+
+#else  //_WIN32
+
+#include <stdint.h>
+#include <pthread.h>
+
+typedef void * THREAD_RET;
+typedef THREAD_RET (*PTHREAD_START_ROUTINE)(void *lpThreadParameter);
+typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
+
+typedef pthread_mutex_t CRITICAL_SECTION, *PCRITICAL_SECTION, *LPCRITICAL_SECTION;
+
+#define THRAPI
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+typedef void * HANDLE;
+#define MAXIMUM_WAIT_OBJECTS 64
+#define INFINITE       (-1)
+#define WAIT_FAILED    (-1)
+#define WAIT_TIMEOUT   0x102
+#define WAIT_OBJECT    0
+#define WAIT_OBJECT_0  0
+#define WAIT_ABANDONED   128
+#define WAIT_ABANDONED_0 128
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#ifndef bool
+#define bool int
+#endif
+#endif
+
+HANDLE event_create(bool manualReset
+#ifdef __cplusplus
+    = false
+#endif
+    , bool initialState
+#ifdef __cplusplus
+    = false
+#endif
+        );
+bool event_set(HANDLE event);
+bool event_reset(HANDLE event);
+int event_wait(HANDLE event, uint32_t milliseconds
+#ifdef __cplusplus
+    = INFINITE
+#endif
+        );
+int event_wait_multiple(uint32_t count, const HANDLE *events, bool waitAll
+#ifdef __cplusplus
+    = false
+#endif
+    , uint32_t milliseconds
+#ifdef __cplusplus
+    = INFINITE
+#endif
+        );
+
+bool InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+bool DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+bool EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+bool LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
+
+HANDLE thread_create(LPTHREAD_START_ROUTINE lpStartAddress, void *lpParameter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_WIN32
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool event_destroy(HANDLE event);
+
+bool thread_close(HANDLE thread);
+void *thread_wait(HANDLE thread);
+bool thread_name(const char *name);
+bool thread_sleep(uint32_t milliseconds);
+
+uint64_t GetTime();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__LGE_SYSTEM_H__