/* FluidSynth - A Software Synthesizer * * Copyright (C) 2003 Peter Hanappe and others. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307, USA */ #include "fluid_sys.h" static char fluid_errbuf[512]; /* buffer for error message */ static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL]; static void* fluid_log_user_data[LAST_LOG_LEVEL]; static int fluid_log_initialized = 0; static char* fluid_libname = "fluidsynth"; void fluid_sys_config() { fluid_log_config(); fluid_time_config(); } unsigned int fluid_debug_flags = 0; #if DEBUG /* * fluid_debug */ int fluid_debug(int level, char * fmt, ...) { if (fluid_debug_flags & level) { fluid_log_function_t fun; va_list args; va_start (args, fmt); vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); va_end (args); fun = fluid_log_function[FLUID_DBG]; if (fun != NULL) { (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]); } } return 0; } #endif /** * Installs a new log function for a specified log level. * @param level Log level to install handler for. * @param fun Callback function handler to call for logged messages * @param data User supplied data pointer to pass to log function * @return The previously installed function. */ fluid_log_function_t fluid_set_log_function(int level, fluid_log_function_t fun, void* data) { fluid_log_function_t old = NULL; if ((level >= 0) && (level < LAST_LOG_LEVEL)) { old = fluid_log_function[level]; fluid_log_function[level] = fun; fluid_log_user_data[level] = data; } return old; } /** * Default log function which prints to the stderr. * @param level Log level * @param message Log message * @param data User supplied data (not used) */ void fluid_default_log_function(int level, char* message, void* data) { FILE* out; #if defined(WIN32) out = stdout; #else out = stderr; #endif if (fluid_log_initialized == 0) { fluid_log_config(); } switch (level) { case FLUID_PANIC: FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message); break; case FLUID_ERR: FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message); break; case FLUID_WARN: FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message); break; case FLUID_INFO: FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); break; case FLUID_DBG: #if DEBUG FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message); #endif break; default: FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message); break; } fflush(out); } /* * fluid_init_log */ void fluid_log_config(void) { if (fluid_log_initialized == 0) { fluid_log_initialized = 1; if (fluid_log_function[FLUID_PANIC] == NULL) { fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_ERR] == NULL) { fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_WARN] == NULL) { fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_INFO] == NULL) { fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL); } if (fluid_log_function[FLUID_DBG] == NULL) { fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL); } } } /** * Print a message to the log. * @param level Log level (#fluid_log_level). * @param fmt Printf style format string for log message * @param ... Arguments for printf 'fmt' message string * @return Always returns -1 */ int fluid_log(int level, char* fmt, ...) { fluid_log_function_t fun = NULL; va_list args; va_start (args, fmt); vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args); va_end (args); if ((level >= 0) && (level < LAST_LOG_LEVEL)) { fun = fluid_log_function[level]; if (fun != NULL) { (*fun)(level, fluid_errbuf, fluid_log_user_data[level]); } } return FLUID_FAILED; } /** * An improved strtok, still trashes the input string, but is portable and * thread safe. Also skips token chars at beginning of token string and never * returns an empty token (will return NULL if source ends in token chars though). * NOTE: NOT part of public API * @internal * @param str Pointer to a string pointer of source to tokenize. Pointer gets * updated on each invocation to point to beginning of next token. Note that * token char get's overwritten with a 0 byte. String pointer is set to NULL * when final token is returned. * @param delim String of delimiter chars. * @return Pointer to the next token or NULL if no more tokens. */ char *fluid_strtok (char **str, char *delim) { char *s, *d, *token; char c; if (str == NULL || delim == NULL || !*delim) { FLUID_LOG(FLUID_ERR, "Null pointer"); return NULL; } s = *str; if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */ /* skip delimiter chars at beginning of token */ do { c = *s; if (!c) /* end of source string? */ { *str = NULL; return NULL; } for (d = delim; *d; d++) /* is source char a token char? */ { if (c == *d) /* token char match? */ { s++; /* advance to next source char */ break; } } } while (*d); /* while token char match */ token = s; /* start of token found */ /* search for next token char or end of source string */ for (s = s+1; *s; s++) { c = *s; for (d = delim; *d; d++) /* is source char a token char? */ { if (c == *d) /* token char match? */ { *s = '\0'; /* overwrite token char with zero byte to terminate token */ *str = s+1; /* update str to point to beginning of next token */ return token; } } } /* we get here only if source string ended */ *str = NULL; return token; } /* * fluid_error */ char* fluid_error() { return fluid_errbuf; } /* * * fluid_is_midifile */ int fluid_is_midifile(char* filename) { FILE* fp = fopen(filename, "rb"); char id[4]; if (fp == NULL) { return 0; } if (fread((void*) id, 1, 4, fp) != 4) { fclose(fp); return 0; } fclose(fp); return strncmp(id, "MThd", 4) == 0; } /* * fluid_is_soundfont * */ int fluid_is_soundfont(char* filename) { FILE* fp = fopen(filename, "rb"); char id[4]; if (fp == NULL) { return 0; } if (fread((void*) id, 1, 4, fp) != 4) { fclose(fp); return 0; } fclose(fp); return strncmp(id, "RIFF", 4) == 0; } #if defined(WIN32) /*=============================================================*/ /* */ /* Win32 */ /* */ /*=============================================================*/ /*************************************************************** * * Timer * */ struct _fluid_timer_t { long msec; fluid_timer_callback_t callback; void* data; HANDLE thread; DWORD thread_id; int cont; int auto_destroy; }; static int fluid_timer_count = 0; DWORD WINAPI fluid_timer_run(LPVOID data); fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, int new_thread, int auto_destroy) { fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); if (timer == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } timer->cont = 1; timer->msec = msec; timer->callback = callback; timer->data = data; timer->thread = 0; timer->auto_destroy = auto_destroy; if (new_thread) { timer->thread = CreateThread(NULL, 0, fluid_timer_run, (LPVOID) timer, 0, &timer->thread_id); if (timer->thread == NULL) { FLUID_LOG(FLUID_ERR, "Couldn't create timer thread"); FLUID_FREE(timer); return NULL; } SetThreadPriority(timer->thread, THREAD_PRIORITY_TIME_CRITICAL); } else { fluid_timer_run((LPVOID) timer); } return timer; } DWORD WINAPI fluid_timer_run(LPVOID data) { int count = 0; int cont = 1; long start; long delay; fluid_timer_t* timer; timer = (fluid_timer_t*) data; if ((timer == NULL) || (timer->callback == NULL)) { return 0; } SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); /* keep track of the start time for absolute positioning */ start = fluid_curtime(); while (cont) { /* do whatever we have to do */ cont = (*timer->callback)(timer->data, fluid_curtime() - start); count++; /* to avoid incremental time errors, I calculate the delay between two callbacks bringing in the "absolute" time (count * timer->msec) */ delay = (count * timer->msec) - (fluid_curtime() - start); if (delay > 0) { Sleep(delay); } cont &= timer->cont; } FLUID_LOG(FLUID_DBG, "Timer thread finished"); if (timer->auto_destroy) { FLUID_FREE(timer); } ExitThread(0); return 0; } int delete_fluid_timer(fluid_timer_t* timer) { timer->cont = 0; fluid_timer_join(timer); FLUID_FREE(timer); return FLUID_OK; } int fluid_timer_join(fluid_timer_t* timer) { DWORD wait_result; if (timer->thread == 0) { return FLUID_OK; } wait_result = WaitForSingleObject(timer->thread, INFINITE); return (wait_result == WAIT_OBJECT_0)? FLUID_OK : FLUID_FAILED; } /*************************************************************** * * Time */ double rdtsc(void); double fluid_estimate_cpu_frequency(void); static double fluid_cpu_frequency = -1.0; void fluid_time_config(void) { if (fluid_cpu_frequency < 0.0) { fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0; } } double fluid_utime(void) { return (rdtsc() / fluid_cpu_frequency); } double rdtsc(void) { LARGE_INTEGER t; QueryPerformanceCounter(&t); return (double) t.QuadPart; } double fluid_estimate_cpu_frequency(void) { #if 0 LONGLONG start, stop, ticks; unsigned int before, after, delta; double freq; start = rdtsc(); stop = start; before = fluid_curtime(); after = before; while (1) { if (after - before > 1000) { break; } after = fluid_curtime(); stop = rdtsc(); } delta = after - before; ticks = stop - start; freq = 1000 * ticks / delta; return freq; #else unsigned int before, after; LARGE_INTEGER start, stop; before = fluid_curtime(); QueryPerformanceCounter(&start); Sleep(1000); after = fluid_curtime(); QueryPerformanceCounter(&stop); return (double) 1000 * (stop.QuadPart - start.QuadPart) / (after - before); #endif } #elif defined(MACOS9) /*=============================================================*/ /* */ /* MacOS 9 */ /* */ /*=============================================================*/ /*************************************************************** * * Timer */ struct _fluid_timer_t { TMTask myTmTask; long msec; unsigned int start; unsigned int count; int isInstalled; fluid_timer_callback_t callback; void* data; int auto_destroy; }; static TimerUPP myTimerUPP; void _timerCallback(fluid_timer_t *timer) { int cont; cont = (*timer->callback)(timer->data, fluid_curtime() - timer->start); if (cont) { PrimeTime((QElemPtr)timer, timer->msec); } else { timer->isInstalled = 0; } timer->count++; } fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, int new_thread, int auto_destroy) { fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); if (timer == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } if (!myTimerUPP) myTimerUPP = NewTimerProc(_timerCallback); /* setup tmtask */ timer->myTmTask.tmAddr = myTimerUPP; timer->myTmTask.qLink = NULL; timer->myTmTask.qType = 0; timer->myTmTask.tmCount = 0L; timer->myTmTask.tmWakeUp = 0L; timer->myTmTask.tmReserved = 0L; timer->callback = callback; timer->msec = msec; timer->data = data; timer->start = fluid_curtime(); timer->isInstalled = 1; timer->count = 0; timer->auto_destroy = auto_destroy; InsXTime((QElemPtr)timer); PrimeTime((QElemPtr)timer, msec); return timer; } int delete_fluid_timer(fluid_timer_t* timer) { if (timer->isInstalled) { RmvTime((QElemPtr)timer); } FLUID_FREE(timer); return FLUID_OK; } int fluid_timer_join(fluid_timer_t* timer) { if (timer->isInstalled) { int count = timer->count; /* wait until count has incremented */ while (count == timer->count) {} } return FLUID_OK; } /*************************************************************** * * Time */ #define kTwoPower32 (4294967296.0) /* 2^32 */ void fluid_time_config(void) { } unsigned int fluid_curtime() { /* could be optimized by not going though a double */ UnsignedWide uS; double mSf; unsigned int ms; Microseconds(&uS); mSf = ((((double) uS.hi) * kTwoPower32) + uS.lo)/1000.0f; ms = mSf; return (ms); } #elif defined(__OS2__) /*=============================================================*/ /* */ /* OS2 */ /* */ /*=============================================================*/ /*************************************************************** * * Timer * */ struct _fluid_timer_t { long msec; fluid_timer_callback_t callback; void* data; int thread_id; int cont; int auto_destroy; }; static int fluid_timer_count = 0; void fluid_timer_run(void *data); fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, int new_thread, int auto_destroy) { fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); if (timer == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } timer->cont = 1; timer->msec = msec; timer->callback = callback; timer->data = data; timer->thread_id =-1; timer->auto_destroy = auto_destroy; if (new_thread) { timer->thread_id = _beginthread( fluid_timer_run, NULL, 256 * 1024, ( void * )timer ); if (timer->thread_id == -1) { FLUID_LOG(FLUID_ERR, "Couldn't create timer thread"); FLUID_FREE(timer); return NULL; } DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, timer->thread_id); } else { fluid_timer_run(( void * )timer); } return timer; } void fluid_timer_run(void *data) { int count = 0; int cont = 1; long start; long delay; fluid_timer_t* timer; timer = (fluid_timer_t*) data; if ((timer == NULL) || (timer->callback == NULL)) { return; } DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0 ); /* keep track of the start time for absolute positioning */ start = fluid_curtime(); while (cont) { /* do whatever we have to do */ cont = (*timer->callback)(timer->data, fluid_curtime() - start); count++; /* to avoid incremental time errors, I calculate the delay between two callbacks bringing in the "absolute" time (count * timer->msec) */ delay = (count * timer->msec) - (fluid_curtime() - start); if (delay > 0) { DosSleep(delay); } cont &= timer->cont; } FLUID_LOG(FLUID_DBG, "Timer thread finished"); if (timer->auto_destroy) { FLUID_FREE(timer); } return; } int delete_fluid_timer(fluid_timer_t* timer) { timer->cont = 0; fluid_timer_join(timer); FLUID_FREE(timer); return FLUID_OK; } int fluid_timer_join(fluid_timer_t* timer) { ULONG wait_result; if (timer->thread_id == -1) { return FLUID_OK; } wait_result = DosWaitThread(&timer->thread_id, DCWW_WAIT); return (wait_result == 0)? FLUID_OK : FLUID_FAILED; } /*************************************************************** * * Time */ double rdtsc(void); double fluid_estimate_cpu_frequency(void); static double fluid_cpu_frequency = -1.0; void fluid_time_config(void) { if (fluid_cpu_frequency < 0.0) { fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0; } } unsigned int fluid_curtime(void) { ULONG ulMS; DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &ulMS, sizeof( ULONG )); return ulMS; } double fluid_utime(void) { return (rdtsc() / fluid_cpu_frequency); } #define Q2ULL( q ) (*(unsigned long long *)&q) double rdtsc(void) { QWORD t; DosTmrQueryTime(&t); return (double)Q2ULL(t); } double fluid_estimate_cpu_frequency(void) { unsigned int before, after; QWORD start, stop; before = fluid_curtime(); DosTmrQueryTime(&start); DosSleep(1000); after = fluid_curtime(); DosTmrQueryTime(&stop); return (double) 1000 * (Q2ULL(stop) - Q2ULL(start)) / (after - before); } #else /*=============================================================*/ /* */ /* POSIX */ /* */ /*=============================================================*/ /*************************************************************** * * Timer */ struct _fluid_timer_t { long msec; fluid_timer_callback_t callback; void* data; pthread_t thread; int cont; int auto_destroy; }; void* fluid_timer_start(void *data) { int count = 0; int cont = 1; long start; long delay; fluid_timer_t* timer; timer = (fluid_timer_t*) data; /* keep track of the start time for absolute positioning */ start = fluid_curtime(); while (cont) { /* do whatever we have to do */ cont = (*timer->callback)(timer->data, fluid_curtime() - start); count++; /* to avoid incremental time errors, calculate the delay between two callbacks bringing in the "absolute" time (count * timer->msec) */ delay = (count * timer->msec) - (fluid_curtime() - start); if (delay > 0) { usleep(delay * 1000); } cont &= timer->cont; } FLUID_LOG(FLUID_DBG, "Timer thread finished"); if (timer->thread != 0) { pthread_exit(NULL); } if (timer->auto_destroy) { FLUID_FREE(timer); } return NULL; } fluid_timer_t* new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data, int new_thread, int auto_destroy) { pthread_attr_t *attr = NULL; pthread_attr_t rt_attr; int sched = SCHED_FIFO; struct sched_param priority; int err; fluid_timer_t* timer = FLUID_NEW(fluid_timer_t); if (timer == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } timer->msec = msec; timer->callback = callback; timer->data = data; timer->cont = 1; timer->thread = 0; timer->auto_destroy = auto_destroy; err = pthread_attr_init(&rt_attr); if (err == 0) { err = pthread_attr_setschedpolicy(&rt_attr, SCHED_FIFO); if (err == 0) { priority.sched_priority = 10; err = pthread_attr_setschedparam(&rt_attr, &priority); if (err == 0) { attr = &rt_attr; } } } if (new_thread) { err = pthread_create(&timer->thread, attr, fluid_timer_start, (void*) timer); if (err == 0) { FLUID_LOG(FLUID_DBG, "The timer thread was created with real-time priority"); } else { /* Create the thread with default attributes */ err = pthread_create(&timer->thread, NULL, fluid_timer_start, (void*) timer); if (err != 0) { FLUID_LOG(FLUID_ERR, "Failed to create the timer thread"); FLUID_FREE(timer); return NULL; } else { FLUID_LOG(FLUID_DBG, "The timer thread does not have real-time priority"); } } } else { fluid_timer_start((void*) timer); } return timer; } int delete_fluid_timer(fluid_timer_t* timer) { timer->cont = 0; fluid_timer_join(timer); FLUID_LOG(FLUID_DBG, "Joined player thread"); FLUID_FREE(timer); return FLUID_OK; } int fluid_timer_join(fluid_timer_t* timer) { int err = 0; if (timer->thread != 0) { err = pthread_join(timer->thread, NULL); } FLUID_LOG(FLUID_DBG, "Joined player thread"); return (err == 0)? FLUID_OK : FLUID_FAILED; } /*************************************************************** * * Time */ static double fluid_cpu_frequency = -1.0; double rdtsc(void); double fluid_estimate_cpu_frequency(void); void fluid_time_config(void) { if (fluid_cpu_frequency < 0.0) { fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0; if (fluid_cpu_frequency == 0.0) fluid_cpu_frequency = 1.0; } } unsigned int fluid_curtime() { struct timeval now; gettimeofday(&now, NULL); return now.tv_sec * 1000 + now.tv_usec / 1000; } double fluid_utime(void) { return (rdtsc() / fluid_cpu_frequency); } #if !defined(__i386__) double rdtsc(void) { return 0.0; } double fluid_estimate_cpu_frequency(void) { return 1.0; } #else double rdtsc(void) { unsigned int a, b; __asm__ ("rdtsc" : "=a" (a), "=d" (b)); return (double)b * (double)0x10000 * (double)0x10000 + a; } double fluid_estimate_cpu_frequency(void) { double start, stop; unsigned int a0, b0, a1, b1; unsigned int before, after; before = fluid_curtime(); __asm__ ("rdtsc" : "=a" (a0), "=d" (b0)); sleep(1); after = fluid_curtime(); __asm__ ("rdtsc" : "=a" (a1), "=d" (b1)); start = (double)b0 * (double)0x10000 * (double)0x10000 + a0; stop = (double)b1 * (double)0x10000 * (double)0x10000 + a1; return 1000 * (stop - start) / (after - before); } #endif #ifdef FPE_CHECK /*************************************************************** * * Floating point exceptions * * The floating point exception functions were taken from Ircam's * jMax source code. http://www.ircam.fr/jmax * * FIXME: check in config for i386 machine * * Currently not used. I leave the code here in case we want to pick * this up again some time later. */ /* Exception flags */ #define _FPU_STATUS_IE 0x001 /* Invalid Operation */ #define _FPU_STATUS_DE 0x002 /* Denormalized Operand */ #define _FPU_STATUS_ZE 0x004 /* Zero Divide */ #define _FPU_STATUS_OE 0x008 /* Overflow */ #define _FPU_STATUS_UE 0x010 /* Underflow */ #define _FPU_STATUS_PE 0x020 /* Precision */ #define _FPU_STATUS_SF 0x040 /* Stack Fault */ #define _FPU_STATUS_ES 0x080 /* Error Summary Status */ /* Macros for accessing the FPU status word. */ /* get the FPU status */ #define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw)) /* clear the FPU status */ #define _FPU_CLR_SW() __asm__ ("fnclex" : : ) /* Purpose: * Checks, if the floating point unit has produced an exception, print a message * if so and clear the exception. */ unsigned int fluid_check_fpe_i386(char* explanation) { unsigned int s; _FPU_GET_SW(s); _FPU_CLR_SW(); s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE; if (s) { FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation, (s & _FPU_STATUS_IE) ? "Invalid operation " : "", (s & _FPU_STATUS_DE) ? "Denormal number " : "", (s & _FPU_STATUS_ZE) ? "Zero divide " : "", (s & _FPU_STATUS_OE) ? "Overflow " : "", (s & _FPU_STATUS_UE) ? "Underflow " : ""); } return s; } /* Purpose: * Clear floating point exception. */ void fluid_clear_fpe_i386 (void) { _FPU_CLR_SW(); } #endif // ifdef FPE_CHECK #endif // #else (its POSIX) /*************************************************************** * * Profiling (Linux, i586 only) * */ #if WITH_PROFILING fluid_profile_data_t fluid_profile_data[] = { { FLUID_PROF_WRITE_S16, "fluid_synth_write_s16 ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0}, { FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0} }; void fluid_profiling_print(void) { int i; printf("fluid_profiling_print\n"); FLUID_LOG(FLUID_INFO, "Estimated CPU frequency: %.0f MHz", fluid_cpu_frequency); FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)"); for (i = 0; i < FLUID_PROF_LAST; i++) { if (fluid_profile_data[i].count > 0) { FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f", fluid_profile_data[i].description, fluid_profile_data[i].min, fluid_profile_data[i].total / fluid_profile_data[i].count, fluid_profile_data[i].max); } else { FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description); } } } #endif /* WITH_PROFILING */ /*************************************************************** * * Threads * */ #if defined(MACOS9) /* Not implemented */ fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) { return NULL; } int delete_fluid_thread(fluid_thread_t* thread) { return 0; } int fluid_thread_join(fluid_thread_t* thread) { return 0; } #elif defined(WIN32) struct _fluid_thread_t { HANDLE thread; DWORD thread_id; fluid_thread_func_t func; void* data; int detached; }; static DWORD WINAPI fluid_thread_start(LPVOID data) { fluid_thread_t* thread = (fluid_thread_t*) data; thread->func(thread->data); if (thread->detached) { FLUID_FREE(thread); } return 0; } fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) { fluid_thread_t* thread; if (func == NULL) { FLUID_LOG(FLUID_ERR, "Invalid thread function"); return NULL; } thread = FLUID_NEW(fluid_thread_t); if (thread == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } thread->data = data; thread->func = func; thread->detached = detach; thread->thread = CreateThread(NULL, 0, fluid_thread_start, (LPVOID) thread, 0, &thread->thread_id); if (thread->thread == NULL) { FLUID_LOG(FLUID_ERR, "Couldn't create the thread"); FLUID_FREE(thread); return NULL; } return thread; } int delete_fluid_thread(fluid_thread_t* thread) { FLUID_FREE(thread); return FLUID_OK; } int fluid_thread_join(fluid_thread_t* thread) { DWORD wait_result; if (thread->thread == 0) { return FLUID_OK; } wait_result = WaitForSingleObject(thread->thread, INFINITE); return (wait_result == WAIT_OBJECT_0)? FLUID_OK : FLUID_FAILED; } #elif defined(__OS2__) struct _fluid_thread_t { int thread_id; fluid_thread_func_t func; void* data; int detached; }; static void fluid_thread_start(void *data) { fluid_thread_t* thread = (fluid_thread_t*) data; thread->func(thread->data); if (thread->detached) { FLUID_FREE(thread); } return 0; } fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) { fluid_thread_t* thread; if (func == NULL) { FLUID_LOG(FLUID_ERR, "Invalid thread function"); return NULL; } thread = FLUID_NEW(fluid_thread_t); if (thread == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } thread->data = data; thread->func = func; thread->detached = detach; thread->thread_id = _beginthread(fluid_thread_start, NULL, 256 * 1024, (void *) thread); if (thread->thread_id == -1) { FLUID_LOG(FLUID_ERR, "Couldn't create the thread"); FLUID_FREE(thread); return NULL; } return thread; } int delete_fluid_thread(fluid_thread_t* thread) { FLUID_FREE(thread); return FLUID_OK; } int fluid_thread_join(fluid_thread_t* thread) { ULONG wait_result; if (thread->thread_id == -1) { return FLUID_OK; } wait_result = DosWaitThread(&thread->thread_id, DCWW_WAIT); return (wait_result == 0)? FLUID_OK : FLUID_FAILED; } #else struct _fluid_thread_t { pthread_t pthread; fluid_thread_func_t func; void* data; int detached; }; static void* fluid_thread_start(void *data) { fluid_thread_t* thread = (fluid_thread_t*) data; thread->func(thread->data); if (thread->detached) { FLUID_FREE(thread); } return NULL; } fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) { fluid_thread_t* thread; pthread_attr_t attr; if (func == NULL) { FLUID_LOG(FLUID_ERR, "Invalid thread function"); return NULL; } thread = FLUID_NEW(fluid_thread_t); if (thread == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); return NULL; } thread->data = data; thread->func = func; thread->detached = detach; pthread_attr_init(&attr); if (detach) { pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); } if (pthread_create(&thread->pthread, &attr, fluid_thread_start, thread)) { FLUID_LOG(FLUID_ERR, "Failed to create the thread"); FLUID_FREE(thread); return NULL; } return thread; } int delete_fluid_thread(fluid_thread_t* thread) { FLUID_FREE(thread); return FLUID_OK; } int fluid_thread_join(fluid_thread_t* thread) { int err = 0; if (thread->pthread != 0) { err = pthread_join(thread->pthread, NULL); } return (err == 0)? FLUID_OK : FLUID_FAILED; } #endif /*************************************************************** * * Sockets * */ #if defined(MACINTOSH) /* Not implemented */ #elif defined(WIN32) #if 0 typedef unsigned int socklen_t; #define fluid_socket_read(_S,_B,_L) recv(_S,_B,_L,0) #define fluid_socket_write(_S,_B,_L) send(_S,_B,_L,0) void fluid_socket_close(fluid_socket_t sock) { int r; char buf[1024]; if (sock != INVALID_SOCKET) { shutdown(sock, 0x02); while (1) { r = recv(sock, buf, 1024, 0); if ((r == 0) || (r == SOCKET_ERROR)) { break; } } closesocket(sock); } } #endif #else #define fluid_socket_read(_S,_B,_L) read(_S,_B,_L) #define fluid_socket_write(_S,_B,_L) write(_S,_B,_L) #define SOCKET_ERROR -1 void fluid_socket_close(fluid_socket_t sock) { if (sock != INVALID_SOCKET) { close(sock); } } #endif #if !defined(MACINTOSH) && !defined(WIN32) fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock) { return sock; } fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock) { return sock; } struct _fluid_server_socket_t { fluid_socket_t socket; fluid_thread_t* thread; int cont; fluid_server_func_t func; void* data; }; static void fluid_server_socket_run(void* data) { fluid_server_socket_t* server_socket = (fluid_server_socket_t*) data; fluid_socket_t client_socket; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); FLUID_LOG(FLUID_DBG, "Server listening for connections"); while (server_socket->cont) { client_socket = accept(server_socket->socket, (struct sockaddr*) &addr, &addrlen); FLUID_LOG(FLUID_DBG, "New client connection"); if (client_socket == INVALID_SOCKET) { if (server_socket->cont) { FLUID_LOG(FLUID_ERR, "Failed to accept connection"); } server_socket->cont = 0; return; } else { int r; r = (*server_socket->func)(server_socket->data, client_socket, inet_ntoa(addr.sin_addr)); if (r != 0) { fluid_socket_close(client_socket); } } } FLUID_LOG(FLUID_DBG, "Server closing"); } fluid_server_socket_t* new_fluid_server_socket(int port, fluid_server_func_t func, void* data) { fluid_server_socket_t* server_socket; struct sockaddr_in addr; fluid_socket_t sock; if (func == NULL) { FLUID_LOG(FLUID_ERR, "Invalid callback function"); return NULL; } sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { FLUID_LOG(FLUID_ERR, "Failed to create server socket"); return NULL; } FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); if (bind(sock, (const struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { FLUID_LOG(FLUID_ERR, "Failed to bind server socket"); fluid_socket_close(sock); return NULL; } if (listen(sock, 10) == SOCKET_ERROR) { FLUID_LOG(FLUID_ERR, "Failed listen on server socket"); fluid_socket_close(sock); return NULL; } server_socket = FLUID_NEW(fluid_server_socket_t); if (server_socket == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); fluid_socket_close(sock); return NULL; } server_socket->socket = sock; server_socket->func = func; server_socket->data = data; server_socket->cont = 1; server_socket->thread = new_fluid_thread(fluid_server_socket_run, server_socket, 0); if (server_socket->thread == NULL) { FLUID_FREE(server_socket); fluid_socket_close(sock); return NULL; } return server_socket; } int delete_fluid_server_socket(fluid_server_socket_t* server_socket) { server_socket->cont = 0; if (server_socket->socket != INVALID_SOCKET) { fluid_socket_close(server_socket->socket); } if (server_socket->thread) { delete_fluid_thread(server_socket->thread); } FLUID_FREE(server_socket); return FLUID_OK; } int fluid_server_socket_join(fluid_server_socket_t* server_socket) { return fluid_thread_join(server_socket->thread); } #endif