Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/fluidsynth/src/utils/fluid_sys.c
4396 views
1
/* FluidSynth - A Software Synthesizer
2
*
3
* Copyright (C) 2003 Peter Hanappe and others.
4
*
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public License
7
* as published by the Free Software Foundation; either version 2.1 of
8
* the License, or (at your option) any later version.
9
*
10
* This library is distributed in the hope that it will be useful, but
11
* WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
14
*
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free
17
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
* 02110-1301, USA
19
*/
20
21
#include "fluid_sys.h"
22
23
24
#if READLINE_SUPPORT
25
#include <readline/readline.h>
26
#include <readline/history.h>
27
#endif
28
29
#ifdef DBUS_SUPPORT
30
#include "fluid_rtkit.h"
31
#endif
32
33
#if HAVE_PTHREAD_H && !defined(_WIN32)
34
// Do not include pthread on windows. It includes winsock.h, which collides with ws2tcpip.h from fluid_sys.h
35
// It isn't need on Windows anyway.
36
#include <pthread.h>
37
#endif
38
39
/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket.
40
* Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */
41
#ifdef _WIN32
42
#define FLUID_SOCKET_FLAG 0x40000000
43
#else
44
#define FLUID_SOCKET_FLAG 0x00000000
45
#define SOCKET_ERROR -1
46
#define INVALID_SOCKET -1
47
#endif
48
49
/* SCHED_FIFO priority for high priority timer threads */
50
#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10
51
52
53
typedef struct
54
{
55
fluid_thread_func_t func;
56
void *data;
57
int prio_level;
58
} fluid_thread_info_t;
59
60
struct _fluid_timer_t
61
{
62
long msec;
63
64
// Pointer to a function to be executed by the timer.
65
// This field is set to NULL once the timer is finished to indicate completion.
66
// This allows for timed waits, rather than waiting forever as fluid_timer_join() does.
67
fluid_timer_callback_t callback;
68
void *data;
69
fluid_thread_t *thread;
70
int cont;
71
int auto_destroy;
72
};
73
74
struct _fluid_server_socket_t
75
{
76
fluid_socket_t socket;
77
fluid_thread_t *thread;
78
int cont;
79
fluid_server_func_t func;
80
void *data;
81
};
82
83
84
static int fluid_istream_gets(fluid_istream_t in, char *buf, int len);
85
86
static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] =
87
{
88
fluid_default_log_function,
89
fluid_default_log_function,
90
fluid_default_log_function,
91
fluid_default_log_function,
92
#ifdef DEBUG
93
fluid_default_log_function
94
#else
95
NULL
96
#endif
97
};
98
static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL };
99
100
static const char fluid_libname[] = "fluidsynth";
101
102
/**
103
* Installs a new log function for a specified log level.
104
* @param level Log level to install handler for.
105
* @param fun Callback function handler to call for logged messages
106
* @param data User supplied data pointer to pass to log function
107
* @return The previously installed function.
108
*/
109
fluid_log_function_t
110
fluid_set_log_function(int level, fluid_log_function_t fun, void *data)
111
{
112
fluid_log_function_t old = NULL;
113
114
if((level >= 0) && (level < LAST_LOG_LEVEL))
115
{
116
old = fluid_log_function[level];
117
fluid_log_function[level] = fun;
118
fluid_log_user_data[level] = data;
119
}
120
121
return old;
122
}
123
124
/**
125
* Default log function which prints to the stderr.
126
* @param level Log level
127
* @param message Log message
128
* @param data User supplied data (not used)
129
*/
130
void
131
fluid_default_log_function(int level, const char *message, void *data)
132
{
133
FILE *out;
134
135
#if defined(_WIN32)
136
out = stdout;
137
#else
138
out = stderr;
139
#endif
140
141
switch(level)
142
{
143
case FLUID_PANIC:
144
FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
145
break;
146
147
case FLUID_ERR:
148
FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
149
break;
150
151
case FLUID_WARN:
152
FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
153
break;
154
155
case FLUID_INFO:
156
FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
157
break;
158
159
case FLUID_DBG:
160
FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
161
break;
162
163
default:
164
FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
165
break;
166
}
167
168
fflush(out);
169
}
170
171
/**
172
* Print a message to the log.
173
* @param level Log level (#fluid_log_level).
174
* @param fmt Printf style format string for log message
175
* @param ... Arguments for printf 'fmt' message string
176
* @return Always returns #FLUID_FAILED
177
*/
178
int
179
fluid_log(int level, const char *fmt, ...)
180
{
181
if((level >= 0) && (level < LAST_LOG_LEVEL))
182
{
183
fluid_log_function_t fun = fluid_log_function[level];
184
185
if(fun != NULL)
186
{
187
char errbuf[1024];
188
189
va_list args;
190
va_start(args, fmt);
191
FLUID_VSNPRINTF(errbuf, sizeof(errbuf), fmt, args);
192
va_end(args);
193
194
(*fun)(level, errbuf, fluid_log_user_data[level]);
195
}
196
}
197
198
return FLUID_FAILED;
199
}
200
201
void* fluid_alloc(size_t len)
202
{
203
void* ptr = malloc(len);
204
205
#if defined(DEBUG) && !defined(_MSC_VER)
206
// garbage initialize allocated memory for debug builds to ease reproducing
207
// bugs like 44453ff23281b3318abbe432fda90888c373022b .
208
//
209
// MSVC++ already garbage initializes allocated memory by itself (debug-heap).
210
//
211
// 0xCC because
212
// * it makes pointers reliably crash when dereferencing them,
213
// * floating points are still some valid but insanely huge negative number, and
214
// * if for whatever reason this allocated memory is executed, it'll trigger
215
// INT3 (...at least on x86)
216
if(ptr != NULL)
217
{
218
memset(ptr, 0xCC, len);
219
}
220
#endif
221
return ptr;
222
}
223
224
/**
225
* Open a file with a UTF-8 string, even in Windows
226
* @param filename The name of the file to open
227
* @param mode The mode to open the file in
228
*/
229
FILE *fluid_fopen(const char *filename, const char *mode)
230
{
231
#if defined(_WIN32)
232
wchar_t *wpath = NULL, *wmode = NULL;
233
FILE *file = NULL;
234
int length;
235
if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, NULL, 0)) == 0)
236
{
237
FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for filename '%s'. Error was: '%s'", filename, fluid_get_windows_error());
238
errno = EINVAL;
239
goto error_recovery;
240
}
241
242
wpath = FLUID_MALLOC(length * sizeof(wchar_t));
243
if (wpath == NULL)
244
{
245
FLUID_LOG(FLUID_PANIC, "Out of memory.");
246
errno = EINVAL;
247
goto error_recovery;
248
}
249
250
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, wpath, length);
251
252
if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, NULL, 0)) == 0)
253
{
254
FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for mode '%s'. Error was: '%s'", mode, fluid_get_windows_error());
255
errno = EINVAL;
256
goto error_recovery;
257
}
258
259
wmode = FLUID_MALLOC(length * sizeof(wchar_t));
260
if (wmode == NULL)
261
{
262
FLUID_LOG(FLUID_PANIC, "Out of memory.");
263
errno = EINVAL;
264
goto error_recovery;
265
}
266
267
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, wmode, length);
268
269
file = _wfopen(wpath, wmode);
270
271
error_recovery:
272
FLUID_FREE(wpath);
273
FLUID_FREE(wmode);
274
275
return file;
276
#else
277
return fopen(filename, mode);
278
#endif
279
}
280
281
/**
282
* Wrapper for free() that satisfies at least C90 requirements.
283
*
284
* @param ptr Pointer to memory region that should be freed
285
*
286
* @note Only use this function when the API documentation explicitly says so. Otherwise use
287
* adequate \c delete_fluid_* functions.
288
*
289
* @warning Calling ::free() on memory that is advised to be freed with fluid_free() results in undefined behaviour!
290
* (cf.: "Potential Errors Passing CRT Objects Across DLL Boundaries" found in MS Docs)
291
*
292
* @since 2.0.7
293
*/
294
void fluid_free(void* ptr)
295
{
296
free(ptr);
297
}
298
299
/**
300
* An improved strtok, still trashes the input string, but is portable and
301
* thread safe. Also skips token chars at beginning of token string and never
302
* returns an empty token (will return NULL if source ends in token chars though).
303
* NOTE: NOT part of public API
304
* @internal
305
* @param str Pointer to a string pointer of source to tokenize. Pointer gets
306
* updated on each invocation to point to beginning of next token. Note that
307
* token char gets overwritten with a 0 byte. String pointer is set to NULL
308
* when final token is returned.
309
* @param delim String of delimiter chars.
310
* @return Pointer to the next token or NULL if no more tokens.
311
*/
312
char *fluid_strtok(char **str, char *delim)
313
{
314
char *s, *d, *token;
315
char c;
316
317
if(str == NULL || delim == NULL || !*delim)
318
{
319
FLUID_LOG(FLUID_ERR, "Null pointer");
320
return NULL;
321
}
322
323
s = *str;
324
325
if(!s)
326
{
327
return NULL; /* str points to a NULL pointer? (tokenize already ended) */
328
}
329
330
/* skip delimiter chars at beginning of token */
331
do
332
{
333
c = *s;
334
335
if(!c) /* end of source string? */
336
{
337
*str = NULL;
338
return NULL;
339
}
340
341
for(d = delim; *d; d++) /* is source char a token char? */
342
{
343
if(c == *d) /* token char match? */
344
{
345
s++; /* advance to next source char */
346
break;
347
}
348
}
349
}
350
while(*d); /* while token char match */
351
352
token = s; /* start of token found */
353
354
/* search for next token char or end of source string */
355
for(s = s + 1; *s; s++)
356
{
357
c = *s;
358
359
for(d = delim; *d; d++) /* is source char a token char? */
360
{
361
if(c == *d) /* token char match? */
362
{
363
*s = '\0'; /* overwrite token char with zero byte to terminate token */
364
*str = s + 1; /* update str to point to beginning of next token */
365
return token;
366
}
367
}
368
}
369
370
/* we get here only if source string ended */
371
*str = NULL;
372
return token;
373
}
374
375
/**
376
* Suspend the execution of the current thread for the specified amount of time.
377
* @param milliseconds to wait.
378
*/
379
void fluid_msleep(unsigned int msecs)
380
{
381
g_usleep(msecs * 1000);
382
}
383
384
/**
385
* Get time in milliseconds to be used in relative timing operations.
386
* @return Monotonic time in milliseconds.
387
*/
388
unsigned int fluid_curtime(void)
389
{
390
double now;
391
static double initial_time = 0;
392
393
if(initial_time == 0)
394
{
395
initial_time = fluid_utime();
396
}
397
398
now = fluid_utime();
399
400
return (unsigned int)((now - initial_time) / 1000.0);
401
}
402
403
/**
404
* Get time in microseconds to be used in relative timing operations.
405
* @return time in microseconds.
406
* Note: When used for profiling we need high precision clock given
407
* by g_get_monotonic_time()if available (glib version >= 2.53.3).
408
* If glib version is too old and in the case of Windows the function
409
* uses high precision performance counter instead of g_getmonotic_time().
410
*/
411
double
412
fluid_utime(void)
413
{
414
double utime;
415
416
#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28
417
/* use high precision monotonic clock if available (g_monotonic_time().
418
* For Windows, if this clock is actually implemented as low prec. clock
419
* (i.e. in case glib is too old), high precision performance counter are
420
* used instead.
421
* see: https://bugzilla.gnome.org/show_bug.cgi?id=783340
422
*/
423
#if defined(WITH_PROFILING) && defined(_WIN32) &&\
424
/* glib < 2.53.3 */\
425
(GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3))
426
/* use high precision performance counter. */
427
static LARGE_INTEGER freq_cache = {0, 0}; /* Performance Frequency */
428
LARGE_INTEGER perf_cpt;
429
430
if(! freq_cache.QuadPart)
431
{
432
QueryPerformanceFrequency(&freq_cache); /* Frequency value */
433
}
434
435
QueryPerformanceCounter(&perf_cpt); /* Counter value */
436
utime = perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */
437
#else
438
utime = g_get_monotonic_time();
439
#endif
440
#else
441
/* fallback to less precise clock */
442
GTimeVal timeval;
443
g_get_current_time(&timeval);
444
utime = (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
445
#endif
446
447
return utime;
448
}
449
450
451
452
#if defined(_WIN32) /* Windoze specific stuff */
453
454
void
455
fluid_thread_self_set_prio(int prio_level)
456
{
457
if(prio_level > 0)
458
{
459
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
460
}
461
}
462
463
464
#elif defined(__OS2__) /* OS/2 specific stuff */
465
466
void
467
fluid_thread_self_set_prio(int prio_level)
468
{
469
if(prio_level > 0)
470
{
471
DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
472
}
473
}
474
475
#else /* POSIX stuff.. Nice POSIX.. Good POSIX. */
476
477
void
478
fluid_thread_self_set_prio(int prio_level)
479
{
480
struct sched_param priority;
481
482
if(prio_level > 0)
483
{
484
485
memset(&priority, 0, sizeof(priority));
486
priority.sched_priority = prio_level;
487
488
if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &priority) == 0)
489
{
490
return;
491
}
492
493
#ifdef DBUS_SUPPORT
494
/* Try to gain high priority via rtkit */
495
496
if(fluid_rtkit_make_realtime(0, prio_level) == 0)
497
{
498
return;
499
}
500
501
#endif
502
FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority");
503
}
504
}
505
506
#ifdef FPE_CHECK
507
508
/***************************************************************
509
*
510
* Floating point exceptions
511
*
512
* The floating point exception functions were taken from Ircam's
513
* jMax source code. https://www.ircam.fr/jmax
514
*
515
* FIXME: check in config for i386 machine
516
*
517
* Currently not used. I leave the code here in case we want to pick
518
* this up again some time later.
519
*/
520
521
/* Exception flags */
522
#define _FPU_STATUS_IE 0x001 /* Invalid Operation */
523
#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */
524
#define _FPU_STATUS_ZE 0x004 /* Zero Divide */
525
#define _FPU_STATUS_OE 0x008 /* Overflow */
526
#define _FPU_STATUS_UE 0x010 /* Underflow */
527
#define _FPU_STATUS_PE 0x020 /* Precision */
528
#define _FPU_STATUS_SF 0x040 /* Stack Fault */
529
#define _FPU_STATUS_ES 0x080 /* Error Summary Status */
530
531
/* Macros for accessing the FPU status word. */
532
533
/* get the FPU status */
534
#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw))
535
536
/* clear the FPU status */
537
#define _FPU_CLR_SW() __asm__ ("fnclex" : : )
538
539
/* Purpose:
540
* Checks, if the floating point unit has produced an exception, print a message
541
* if so and clear the exception.
542
*/
543
unsigned int fluid_check_fpe_i386(char *explanation)
544
{
545
unsigned int s;
546
547
_FPU_GET_SW(s);
548
_FPU_CLR_SW();
549
550
s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE;
551
552
if(s)
553
{
554
FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation,
555
(s & _FPU_STATUS_IE) ? "Invalid operation " : "",
556
(s & _FPU_STATUS_DE) ? "Denormal number " : "",
557
(s & _FPU_STATUS_ZE) ? "Zero divide " : "",
558
(s & _FPU_STATUS_OE) ? "Overflow " : "",
559
(s & _FPU_STATUS_UE) ? "Underflow " : "");
560
}
561
562
return s;
563
}
564
565
/* Purpose:
566
* Clear floating point exception.
567
*/
568
void fluid_clear_fpe_i386(void)
569
{
570
_FPU_CLR_SW();
571
}
572
573
#endif // ifdef FPE_CHECK
574
575
576
#endif // #else (its POSIX)
577
578
579
/***************************************************************
580
*
581
* Profiling (Linux, i586 only)
582
*
583
*/
584
585
#if WITH_PROFILING
586
/* Profiling interface between profiling command shell and audio rendering API
587
(FluidProfile_0004.pdf- 3.2.2).
588
Macros are in defined in fluid_sys.h.
589
*/
590
591
/*
592
-----------------------------------------------------------------------------
593
Shell task side | Profiling interface | Audio task side
594
-----------------------------------------------------------------------------
595
profiling | Internal | | | Audio
596
command <---> |<-- profiling -->| Data |<--macros -->| <--> rendering
597
shell | API | | | API
598
599
*/
600
/* default parameters for shell command "prof_start" in fluid_sys.c */
601
unsigned short fluid_profile_notes = 0; /* number of generated notes */
602
/* preset bank:0 prog:16 (organ) */
603
unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK;
604
unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG;
605
606
/* print mode */
607
unsigned char fluid_profile_print = FLUID_PROFILE_DEFAULT_PRINT;
608
/* number of measures */
609
unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF;
610
/* measure duration in ms */
611
unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION;
612
/* lock between multiple-shell */
613
fluid_atomic_int_t fluid_profile_lock = 0;
614
/**/
615
616
/*----------------------------------------------
617
Profiling Data
618
-----------------------------------------------*/
619
unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */
620
unsigned int fluid_profile_end_ticks = 0; /* ending position (in ticks) */
621
fluid_profile_data_t fluid_profile_data[] = /* Data duration */
622
{
623
{"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0},
624
{"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0},
625
{"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0},
626
{"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0},
627
{"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0},
628
{"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0},
629
{"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0},
630
{"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0},
631
{"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0}
632
};
633
634
635
/*----------------------------------------------
636
Internal profiling API
637
-----------------------------------------------*/
638
/* logging profiling data (used on synthesizer instance deletion) */
639
void fluid_profiling_print(void)
640
{
641
int i;
642
643
FLUID_LOG(FLUID_INFO, "fluid_profiling_print\n");
644
645
FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");
646
647
for(i = 0; i < FLUID_PROFILE_NBR; i++)
648
{
649
if(fluid_profile_data[i].count > 0)
650
{
651
FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f",
652
fluid_profile_data[i].description,
653
fluid_profile_data[i].min,
654
fluid_profile_data[i].total / fluid_profile_data[i].count,
655
fluid_profile_data[i].max);
656
}
657
else
658
{
659
FLUID_LOG(FLUID_DBG, "%s: no profiling available",
660
fluid_profile_data[i].description);
661
}
662
}
663
}
664
665
/* Macro that returns cpu load in percent (%)
666
* @dur: duration (micro second).
667
* @sample_rate: sample_rate used in audio driver (Hz).
668
* @n_amples: number of samples collected during 'dur' duration.
669
*/
670
#define fluid_profile_load(dur,sample_rate,n_samples) \
671
(dur * sample_rate / n_samples / 10000.0)
672
673
674
/* prints cpu loads only
675
*
676
* @param sample_rate the sample rate of audio output.
677
* @param out output stream device.
678
*
679
* ------------------------------------------------------------------------------
680
* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices
681
* ------------------------------------------------------------------------------
682
* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices
683
* -------|---------|---------|----------|---------|---------|-------------------
684
* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612
685
*/
686
static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out)
687
{
688
unsigned int n_voices; /* voices number */
689
static const char max_voices_not_available[] = " not available";
690
const char *pmax_voices;
691
char max_voices_available[20];
692
693
/* First computes data to be printed */
694
double total, voices, reverb, chorus, all_voices, voice;
695
/* voices number */
696
n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?
697
fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices /
698
fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count : 0;
699
700
/* total load (%) */
701
total = fluid_profile_data[FLUID_PROF_WRITE].count ?
702
fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total, sample_rate,
703
fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0;
704
705
/* reverb load (%) */
706
reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ?
707
fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total,
708
sample_rate,
709
fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0;
710
711
/* chorus load (%) */
712
chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ?
713
fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total,
714
sample_rate,
715
fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0;
716
717
/* total voices load: total - reverb - chorus (%) */
718
voices = total - reverb - chorus;
719
720
/* One voice load (%): all_voices / n_voices. */
721
all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?
722
fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total,
723
sample_rate,
724
fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0;
725
726
voice = n_voices ? all_voices / n_voices : 0;
727
728
/* estimated maximum voices number */
729
if(voice > 0)
730
{
731
FLUID_SNPRINTF(max_voices_available, sizeof(max_voices_available),
732
"%17d", (unsigned int)((100.0 - reverb - chorus) / voice));
733
pmax_voices = max_voices_available;
734
}
735
else
736
{
737
pmax_voices = max_voices_not_available;
738
}
739
740
/* Now prints data */
741
fluid_ostream_printf(out,
742
" ------------------------------------------------------------------------------\n");
743
fluid_ostream_printf(out,
744
" Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n",
745
sample_rate, 1000000.0 / sample_rate);
746
fluid_ostream_printf(out,
747
" ------------------------------------------------------------------------------\n");
748
fluid_ostream_printf(out,
749
" nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n");
750
fluid_ostream_printf(out,
751
" -------|---------|---------|----------|---------|---------|-------------------\n");
752
fluid_ostream_printf(out,
753
"%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices,
754
reverb, chorus, voice, pmax_voices);
755
}
756
757
/*
758
* prints profiling data (used by profile shell command: prof_start).
759
* The function is an internal profiling API between the "profile" command
760
* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
761
*
762
* @param sample_rate the sample rate of audio output.
763
* @param out output stream device.
764
*
765
* When print mode is 1, the function prints all the information (see below).
766
* When print mode is 0, the function prints only the cpu loads.
767
*
768
* ------------------------------------------------------------------------------
769
* Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond)
770
* ------------------------------------------------------------------------------
771
* Code under profiling |Voices| Duration (microsecond) | Load(%)
772
* | nbr| min| avg| max|
773
* ---------------------------|------|--------------------------------|----------
774
* synth_write_* ------------>| 250| 3.91| 2188.82| 3275.00| 41.544
775
* synth_one_block ---------->| 250| 1150.70| 2273.56| 3241.47| 41.100
776
* synth_one_block:clear ---->| 250| 3.07| 4.62| 61.18| 0.084
777
* synth_one_block:one voice->| 1| 4.19| 9.02| 1044.27| 0.163
778
* synth_one_block:all voices>| 250| 1138.41| 2259.11| 3217.73| 40.839
779
* synth_one_block:reverb --->| no profiling available
780
* synth_one_block:chorus --->| no profiling available
781
* voice:note --------------->| no profiling available
782
* voice:release ------------>| no profiling available
783
* ------------------------------------------------------------------------------
784
* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices
785
* ------------------------------------------------------------------------------
786
* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices
787
* -------|---------|---------|----------|---------|---------|-------------------
788
* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612
789
*/
790
void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out)
791
{
792
int i;
793
794
if(fluid_profile_print)
795
{
796
/* print all details: Duration(microsecond) and cpu loads(%) */
797
fluid_ostream_printf(out,
798
" ------------------------------------------------------------------------------\n");
799
fluid_ostream_printf(out,
800
" Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n",
801
sample_rate, 1000000.0 / sample_rate);
802
fluid_ostream_printf(out,
803
" ------------------------------------------------------------------------------\n");
804
fluid_ostream_printf(out,
805
" Code under profiling |Voices| Duration (microsecond) | Load(%%)\n");
806
fluid_ostream_printf(out,
807
" | nbr| min| avg| max|\n");
808
fluid_ostream_printf(out,
809
" ---------------------------|------|--------------------------------|----------\n");
810
811
for(i = 0; i < FLUID_PROFILE_NBR; i++)
812
{
813
unsigned int count = fluid_profile_data[i].count;
814
815
if(count > 0)
816
{
817
/* data are available */
818
819
if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS)
820
{
821
double load = fluid_profile_load(fluid_profile_data[i].total, sample_rate,
822
fluid_profile_data[i].n_samples);
823
fluid_ostream_printf(out, " %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n",
824
fluid_profile_data[i].description, /* code under profiling */
825
fluid_profile_data[i].n_voices / count, /* voices number */
826
fluid_profile_data[i].min, /* minimum duration */
827
fluid_profile_data[i].total / count, /* average duration */
828
fluid_profile_data[i].max, /* maximum duration */
829
load); /* cpu load */
830
}
831
else
832
{
833
/* note and release duration */
834
fluid_ostream_printf(out, " %s|%6d|%10.0f|%10.0f|%10.0f|\n",
835
fluid_profile_data[i].description, /* code under profiling */
836
fluid_profile_data[i].n_voices / count,
837
fluid_profile_data[i].min, /* minimum duration */
838
fluid_profile_data[i].total / count, /* average duration */
839
fluid_profile_data[i].max); /* maximum duration */
840
}
841
}
842
else
843
{
844
/* data aren't available */
845
fluid_ostream_printf(out,
846
" %s| no profiling available\n", fluid_profile_data[i].description);
847
}
848
}
849
}
850
851
/* prints cpu loads only */
852
fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */
853
}
854
855
/*
856
Returns true if the user cancels the current profiling measurement.
857
Actually this is implemented using the <ENTER> key. To add this functionality:
858
1) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h.
859
2) Adds the necessary code inside fluid_profile_is_cancel().
860
861
When FLUID_PROFILE_CANCEL is not defined, the function return FALSE.
862
*/
863
int fluid_profile_is_cancel_req(void)
864
{
865
#ifdef FLUID_PROFILE_CANCEL
866
867
#if defined(_WIN32) /* Windows specific stuff */
868
/* Profile cancellation is supported for Windows */
869
/* returns TRUE if key <ENTER> is depressed */
870
return(GetAsyncKeyState(VK_RETURN) & 0x1);
871
872
#elif defined(__OS2__) /* OS/2 specific stuff */
873
/* Profile cancellation isn't yet supported for OS2 */
874
/* For OS2, replaces the following line with the function that returns
875
true when the keyboard key <ENTER> is depressed */
876
return FALSE; /* default value */
877
878
#else /* POSIX stuff */
879
/* Profile cancellation is supported for Linux */
880
/* returns true is <ENTER> is depressed */
881
{
882
/* Here select() is used to poll the standard input to see if an input
883
is ready. As the standard input is usually buffered, the user
884
needs to depress <ENTER> to set the input to a "ready" state.
885
*/
886
struct timeval tv;
887
fd_set fds; /* just one fds need to be polled */
888
tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */
889
tv.tv_usec = 0;
890
FD_ZERO(&fds); /* reset fds */
891
FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */
892
select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */
893
return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */
894
}
895
#endif /* OS stuff */
896
897
#else /* FLUID_PROFILE_CANCEL not defined */
898
return FALSE; /* default value */
899
#endif /* FLUID_PROFILE_CANCEL */
900
}
901
902
/**
903
* Returns status used in shell command "prof_start".
904
* The function is an internal profiling API between the "profile" command
905
* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
906
*
907
* @return status
908
* - PROFILE_READY profiling data are ready.
909
* - PROFILE_RUNNING, profiling data are still under acquisition.
910
* - PROFILE_CANCELED, acquisition has been cancelled by the user.
911
* - PROFILE_STOP, no acquisition in progress.
912
*
913
* When status is PROFILE_RUNNING, the caller can do passive waiting, or other
914
* work before recalling the function later.
915
*/
916
int fluid_profile_get_status(void)
917
{
918
/* Checks if user has requested to cancel the current measurement */
919
/* Cancellation must have precedence over other status */
920
if(fluid_profile_is_cancel_req())
921
{
922
fluid_profile_start_stop(0, 0); /* stops the measurement */
923
return PROFILE_CANCELED;
924
}
925
926
switch(fluid_profile_status)
927
{
928
case PROFILE_READY:
929
return PROFILE_READY; /* profiling data are ready */
930
931
case PROFILE_START:
932
return PROFILE_RUNNING;/* profiling data are under acquisition */
933
934
default:
935
return PROFILE_STOP;
936
}
937
}
938
939
/**
940
* Starts or stops profiling measurement.
941
* The function is an internal profiling API between the "profile" command
942
* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).
943
*
944
* @param end_tick end position of the measure (in ticks).
945
* - If end_tick is greater then 0, the function starts a measure if a measure
946
* isn't running. If a measure is already running, the function does nothing
947
* and returns.
948
* - If end_tick is 0, the function stops a measure.
949
* @param clear_data,
950
* - If clear_data is 0, the function clears fluid_profile_data before starting
951
* a measure, otherwise, the data from the started measure will be accumulated
952
* within fluid_profile_data.
953
*/
954
void fluid_profile_start_stop(unsigned int end_ticks, short clear_data)
955
{
956
if(end_ticks) /* This is a "start" request */
957
{
958
/* Checks if a measure is already running */
959
if(fluid_profile_status != PROFILE_START)
960
{
961
short i;
962
fluid_profile_end_ticks = end_ticks;
963
964
/* Clears profile data */
965
if(clear_data == 0)
966
{
967
for(i = 0; i < FLUID_PROFILE_NBR; i++)
968
{
969
fluid_profile_data[i].min = 1e10;/* min sets to max value */
970
fluid_profile_data[i].max = 0; /* maximum sets to min value */
971
fluid_profile_data[i].total = 0; /* total duration microsecond */
972
fluid_profile_data[i].count = 0; /* data count */
973
fluid_profile_data[i].n_voices = 0; /* voices number */
974
fluid_profile_data[i].n_samples = 0;/* audio samples number */
975
}
976
}
977
978
fluid_profile_status = PROFILE_START; /* starts profiling */
979
}
980
981
/* else do nothing when profiling is already started */
982
}
983
else /* This is a "stop" request */
984
{
985
/* forces the current running profile (if any) to stop */
986
fluid_profile_status = PROFILE_STOP; /* stops profiling */
987
}
988
}
989
990
#endif /* WITH_PROFILING */
991
992
/***************************************************************
993
*
994
* Threads
995
*
996
*/
997
998
#if OLD_GLIB_THREAD_API
999
1000
/* Rather than inline this one, we just declare it as a function, to prevent
1001
* GCC warning about inline failure. */
1002
fluid_cond_t *
1003
new_fluid_cond(void)
1004
{
1005
if(!g_thread_supported())
1006
{
1007
g_thread_init(NULL);
1008
}
1009
1010
return g_cond_new();
1011
}
1012
1013
#endif
1014
1015
static gpointer
1016
fluid_thread_high_prio(gpointer data)
1017
{
1018
fluid_thread_info_t *info = data;
1019
1020
fluid_thread_self_set_prio(info->prio_level);
1021
1022
info->func(info->data);
1023
FLUID_FREE(info);
1024
1025
return NULL;
1026
}
1027
1028
/**
1029
* Create a new thread.
1030
* @param func Function to execute in new thread context
1031
* @param data User defined data to pass to func
1032
* @param prio_level Priority level. If greater than 0 then high priority scheduling will
1033
* be used, with the given priority level (used by pthreads only). 0 uses normal scheduling.
1034
* @param detach If TRUE, 'join' does not work and the thread destroys itself when finished.
1035
* @return New thread pointer or NULL on error
1036
*/
1037
fluid_thread_t *
1038
new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach)
1039
{
1040
GThread *thread;
1041
fluid_thread_info_t *info = NULL;
1042
GError *err = NULL;
1043
1044
g_return_val_if_fail(func != NULL, NULL);
1045
1046
#if OLD_GLIB_THREAD_API
1047
1048
/* Make sure g_thread_init has been called.
1049
* Probably not a good idea in a shared library,
1050
* but what can we do *and* remain backwards compatible? */
1051
if(!g_thread_supported())
1052
{
1053
g_thread_init(NULL);
1054
}
1055
1056
#endif
1057
1058
if(prio_level > 0)
1059
{
1060
info = FLUID_NEW(fluid_thread_info_t);
1061
1062
if(!info)
1063
{
1064
FLUID_LOG(FLUID_ERR, "Out of memory");
1065
return NULL;
1066
}
1067
1068
info->func = func;
1069
info->data = data;
1070
info->prio_level = prio_level;
1071
#if NEW_GLIB_THREAD_API
1072
thread = g_thread_try_new(name, fluid_thread_high_prio, info, &err);
1073
#else
1074
thread = g_thread_create(fluid_thread_high_prio, info, detach == FALSE, &err);
1075
#endif
1076
}
1077
1078
else
1079
{
1080
#if NEW_GLIB_THREAD_API
1081
thread = g_thread_try_new(name, (GThreadFunc)func, data, &err);
1082
#else
1083
thread = g_thread_create((GThreadFunc)func, data, detach == FALSE, &err);
1084
#endif
1085
}
1086
1087
if(!thread)
1088
{
1089
FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s",
1090
fluid_gerror_message(err));
1091
g_clear_error(&err);
1092
FLUID_FREE(info);
1093
return NULL;
1094
}
1095
1096
#if NEW_GLIB_THREAD_API
1097
1098
if(detach)
1099
{
1100
g_thread_unref(thread); // Release thread reference, if caller wants to detach
1101
}
1102
1103
#endif
1104
1105
return thread;
1106
}
1107
1108
/**
1109
* Frees data associated with a thread (does not actually stop thread).
1110
* @param thread Thread to free
1111
*/
1112
void
1113
delete_fluid_thread(fluid_thread_t *thread)
1114
{
1115
/* Threads free themselves when they quit, nothing to do */
1116
}
1117
1118
/**
1119
* Join a thread (wait for it to terminate).
1120
* @param thread Thread to join
1121
* @return FLUID_OK
1122
*/
1123
int
1124
fluid_thread_join(fluid_thread_t *thread)
1125
{
1126
g_thread_join(thread);
1127
1128
return FLUID_OK;
1129
}
1130
1131
1132
static fluid_thread_return_t
1133
fluid_timer_run(void *data)
1134
{
1135
fluid_timer_t *timer;
1136
int count = 0;
1137
int cont;
1138
long start;
1139
long delay;
1140
1141
timer = (fluid_timer_t *)data;
1142
1143
/* keep track of the start time for absolute positioning */
1144
start = fluid_curtime();
1145
1146
while(timer->cont)
1147
{
1148
cont = (*timer->callback)(timer->data, fluid_curtime() - start);
1149
1150
count++;
1151
1152
if(!cont)
1153
{
1154
break;
1155
}
1156
1157
/* to avoid incremental time errors, calculate the delay between
1158
two callbacks bringing in the "absolute" time (count *
1159
timer->msec) */
1160
delay = (count * timer->msec) - (fluid_curtime() - start);
1161
1162
if(delay > 0)
1163
{
1164
fluid_msleep(delay);
1165
}
1166
}
1167
1168
FLUID_LOG(FLUID_DBG, "Timer thread finished");
1169
timer->callback = NULL;
1170
1171
if(timer->auto_destroy)
1172
{
1173
FLUID_FREE(timer);
1174
}
1175
1176
return FLUID_THREAD_RETURN_VALUE;
1177
}
1178
1179
fluid_timer_t *
1180
new_fluid_timer(int msec, fluid_timer_callback_t callback, void *data,
1181
int new_thread, int auto_destroy, int high_priority)
1182
{
1183
fluid_timer_t *timer;
1184
1185
timer = FLUID_NEW(fluid_timer_t);
1186
1187
if(timer == NULL)
1188
{
1189
FLUID_LOG(FLUID_ERR, "Out of memory");
1190
return NULL;
1191
}
1192
1193
timer->msec = msec;
1194
timer->callback = callback;
1195
timer->data = data;
1196
timer->cont = TRUE ;
1197
timer->thread = NULL;
1198
timer->auto_destroy = auto_destroy;
1199
1200
if(new_thread)
1201
{
1202
timer->thread = new_fluid_thread("timer", fluid_timer_run, timer, high_priority
1203
? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE);
1204
1205
if(!timer->thread)
1206
{
1207
FLUID_FREE(timer);
1208
return NULL;
1209
}
1210
}
1211
else
1212
{
1213
fluid_timer_run(timer); /* Run directly, instead of as a separate thread */
1214
1215
if(auto_destroy)
1216
{
1217
/* do NOT return freed memory */
1218
return NULL;
1219
}
1220
}
1221
1222
return timer;
1223
}
1224
1225
void
1226
delete_fluid_timer(fluid_timer_t *timer)
1227
{
1228
int auto_destroy;
1229
fluid_return_if_fail(timer != NULL);
1230
1231
auto_destroy = timer->auto_destroy;
1232
1233
timer->cont = 0;
1234
fluid_timer_join(timer);
1235
1236
/* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */
1237
1238
if(!auto_destroy)
1239
{
1240
FLUID_FREE(timer);
1241
}
1242
}
1243
1244
int
1245
fluid_timer_join(fluid_timer_t *timer)
1246
{
1247
int auto_destroy;
1248
1249
if(timer->thread)
1250
{
1251
auto_destroy = timer->auto_destroy;
1252
fluid_thread_join(timer->thread);
1253
1254
if(!auto_destroy)
1255
{
1256
timer->thread = NULL;
1257
}
1258
}
1259
1260
return FLUID_OK;
1261
}
1262
1263
int
1264
fluid_timer_is_running(const fluid_timer_t *timer)
1265
{
1266
// for unit test usage only
1267
return timer->callback != NULL;
1268
}
1269
1270
long fluid_timer_get_interval(const fluid_timer_t * timer)
1271
{
1272
// for unit test usage only
1273
return timer->msec;
1274
}
1275
1276
1277
/***************************************************************
1278
*
1279
* Sockets and I/O
1280
*
1281
*/
1282
1283
/**
1284
* Get standard in stream handle.
1285
* @return Standard in stream.
1286
*/
1287
fluid_istream_t
1288
fluid_get_stdin(void)
1289
{
1290
return STDIN_FILENO;
1291
}
1292
1293
/**
1294
* Get standard output stream handle.
1295
* @return Standard out stream.
1296
*/
1297
fluid_ostream_t
1298
fluid_get_stdout(void)
1299
{
1300
return STDOUT_FILENO;
1301
}
1302
1303
/**
1304
* Read a line from an input stream.
1305
* @return 0 if end-of-stream, -1 if error, non zero otherwise
1306
*/
1307
int
1308
fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt,
1309
char *buf, int len)
1310
{
1311
#if READLINE_SUPPORT
1312
1313
if(in == fluid_get_stdin())
1314
{
1315
char *line;
1316
1317
line = readline(prompt);
1318
1319
if(line == NULL)
1320
{
1321
return -1;
1322
}
1323
1324
FLUID_SNPRINTF(buf, len, "%s", line);
1325
buf[len - 1] = 0;
1326
1327
if(buf[0] != '\0')
1328
{
1329
add_history(buf);
1330
}
1331
1332
free(line);
1333
return 1;
1334
}
1335
else
1336
#endif
1337
{
1338
fluid_ostream_printf(out, "%s", prompt);
1339
return fluid_istream_gets(in, buf, len);
1340
}
1341
}
1342
1343
/**
1344
* Reads a line from an input stream (socket).
1345
* @param in The input socket
1346
* @param buf Buffer to store data to
1347
* @param len Maximum length to store to buf
1348
* @return 1 if a line was read, 0 on end of stream, -1 on error
1349
*/
1350
static int
1351
fluid_istream_gets(fluid_istream_t in, char *buf, int len)
1352
{
1353
char c;
1354
int n;
1355
1356
buf[len - 1] = 0;
1357
1358
while(--len > 0)
1359
{
1360
#ifndef _WIN32
1361
n = read(in, &c, 1);
1362
1363
if(n == -1)
1364
{
1365
return -1;
1366
}
1367
1368
#else
1369
1370
/* Handle read differently depending on if its a socket or file descriptor */
1371
if(!(in & FLUID_SOCKET_FLAG))
1372
{
1373
// usually read() is supposed to return '\n' as last valid character of the user input
1374
// when compiled with compatibility for WinXP however, read() may return 0 (EOF) rather than '\n'
1375
// this would cause the shell to exit early
1376
n = read(in, &c, 1);
1377
1378
if(n == -1)
1379
{
1380
return -1;
1381
}
1382
}
1383
else
1384
{
1385
#ifdef NETWORK_SUPPORT
1386
n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0);
1387
if(n == SOCKET_ERROR)
1388
#endif
1389
{
1390
return -1;
1391
}
1392
}
1393
1394
#endif
1395
1396
if(n == 0)
1397
{
1398
*buf = 0;
1399
// return 1 if read from stdin, else 0, to fix early exit of shell
1400
return (in == STDIN_FILENO);
1401
}
1402
1403
if(c == '\n')
1404
{
1405
*buf = 0;
1406
return 1;
1407
}
1408
1409
/* Store all characters excluding CR */
1410
if(c != '\r')
1411
{
1412
*buf++ = c;
1413
}
1414
}
1415
1416
return -1;
1417
}
1418
1419
/**
1420
* Send a printf style string with arguments to an output stream (socket).
1421
* @param out Output stream
1422
* @param format printf style format string
1423
* @param ... Arguments for the printf format string
1424
* @return Number of bytes written or -1 on error
1425
*/
1426
int
1427
fluid_ostream_printf(fluid_ostream_t out, const char *format, ...)
1428
{
1429
char buf[4096];
1430
va_list args;
1431
int len;
1432
1433
va_start(args, format);
1434
len = FLUID_VSNPRINTF(buf, 4095, format, args);
1435
va_end(args);
1436
1437
if(len == 0)
1438
{
1439
return 0;
1440
}
1441
1442
if(len < 0)
1443
{
1444
printf("fluid_ostream_printf: buffer overflow");
1445
return -1;
1446
}
1447
1448
buf[4095] = 0;
1449
1450
#ifndef _WIN32
1451
return write(out, buf, FLUID_STRLEN(buf));
1452
#else
1453
{
1454
int retval;
1455
1456
/* Handle write differently depending on if its a socket or file descriptor */
1457
if(!(out & FLUID_SOCKET_FLAG))
1458
{
1459
return write(out, buf, (unsigned int)FLUID_STRLEN(buf));
1460
}
1461
1462
#ifdef NETWORK_SUPPORT
1463
/* Socket */
1464
retval = send(out & ~FLUID_SOCKET_FLAG, buf, (int)FLUID_STRLEN(buf), 0);
1465
return retval != SOCKET_ERROR ? retval : -1;
1466
#else
1467
return -1;
1468
#endif
1469
}
1470
#endif
1471
}
1472
1473
#ifdef NETWORK_SUPPORT
1474
1475
int fluid_server_socket_join(fluid_server_socket_t *server_socket)
1476
{
1477
return fluid_thread_join(server_socket->thread);
1478
}
1479
1480
static int fluid_socket_init(void)
1481
{
1482
#ifdef _WIN32
1483
WSADATA wsaData;
1484
int res = WSAStartup(MAKEWORD(2, 2), &wsaData);
1485
1486
if(res != 0)
1487
{
1488
FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res);
1489
return FLUID_FAILED;
1490
}
1491
1492
#endif
1493
1494
return FLUID_OK;
1495
}
1496
1497
static void fluid_socket_cleanup(void)
1498
{
1499
#ifdef _WIN32
1500
WSACleanup();
1501
#endif
1502
}
1503
1504
static int fluid_socket_get_error(void)
1505
{
1506
#ifdef _WIN32
1507
return (int)WSAGetLastError();
1508
#else
1509
return errno;
1510
#endif
1511
}
1512
1513
fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock)
1514
{
1515
return sock | FLUID_SOCKET_FLAG;
1516
}
1517
1518
fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock)
1519
{
1520
return sock | FLUID_SOCKET_FLAG;
1521
}
1522
1523
void fluid_socket_close(fluid_socket_t sock)
1524
{
1525
if(sock != INVALID_SOCKET)
1526
{
1527
#ifdef _WIN32
1528
closesocket(sock);
1529
1530
#else
1531
close(sock);
1532
#endif
1533
}
1534
}
1535
1536
static fluid_thread_return_t fluid_server_socket_run(void *data)
1537
{
1538
fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
1539
fluid_socket_t client_socket;
1540
#ifdef IPV6_SUPPORT
1541
struct sockaddr_in6 addr;
1542
#else
1543
struct sockaddr_in addr;
1544
#endif
1545
1546
#ifdef HAVE_INETNTOP
1547
#ifdef IPV6_SUPPORT
1548
char straddr[INET6_ADDRSTRLEN];
1549
#else
1550
char straddr[INET_ADDRSTRLEN];
1551
#endif /* IPV6_SUPPORT */
1552
#endif /* HAVE_INETNTOP */
1553
1554
socklen_t addrlen = sizeof(addr);
1555
int r;
1556
FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
1557
1558
FLUID_LOG(FLUID_DBG, "Server listening for connections");
1559
1560
while(server_socket->cont)
1561
{
1562
client_socket = accept(server_socket->socket, (struct sockaddr *)&addr, &addrlen);
1563
1564
FLUID_LOG(FLUID_DBG, "New client connection");
1565
1566
if(client_socket == INVALID_SOCKET)
1567
{
1568
if(server_socket->cont)
1569
{
1570
FLUID_LOG(FLUID_ERR, "Failed to accept connection: %d", fluid_socket_get_error());
1571
}
1572
1573
server_socket->cont = 0;
1574
return FLUID_THREAD_RETURN_VALUE;
1575
}
1576
else
1577
{
1578
#ifdef HAVE_INETNTOP
1579
1580
#ifdef IPV6_SUPPORT
1581
inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
1582
#else
1583
inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
1584
#endif
1585
1586
r = server_socket->func(server_socket->data, client_socket,
1587
straddr);
1588
#else
1589
r = server_socket->func(server_socket->data, client_socket,
1590
inet_ntoa(addr.sin_addr));
1591
#endif
1592
1593
if(r != 0)
1594
{
1595
fluid_socket_close(client_socket);
1596
}
1597
}
1598
}
1599
1600
FLUID_LOG(FLUID_DBG, "Server closing");
1601
1602
return FLUID_THREAD_RETURN_VALUE;
1603
}
1604
1605
fluid_server_socket_t *
1606
new_fluid_server_socket(int port, fluid_server_func_t func, void *data)
1607
{
1608
fluid_server_socket_t *server_socket;
1609
struct sockaddr_in addr4;
1610
#ifdef IPV6_SUPPORT
1611
struct sockaddr_in6 addr6;
1612
#endif
1613
const struct sockaddr *addr;
1614
size_t addr_size;
1615
fluid_socket_t sock;
1616
1617
fluid_return_val_if_fail(func != NULL, NULL);
1618
1619
if(fluid_socket_init() != FLUID_OK)
1620
{
1621
return NULL;
1622
}
1623
1624
FLUID_MEMSET(&addr4, 0, sizeof(addr4));
1625
addr4.sin_family = AF_INET;
1626
addr4.sin_port = htons((uint16_t)port);
1627
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
1628
1629
#ifdef IPV6_SUPPORT
1630
FLUID_MEMSET(&addr6, 0, sizeof(addr6));
1631
addr6.sin6_family = AF_INET6;
1632
addr6.sin6_port = htons((uint16_t)port);
1633
addr6.sin6_addr = in6addr_any;
1634
#endif
1635
1636
#ifdef IPV6_SUPPORT
1637
sock = socket(AF_INET6, SOCK_STREAM, 0);
1638
addr = (const struct sockaddr *) &addr6;
1639
addr_size = sizeof(addr6);
1640
1641
if(sock == INVALID_SOCKET)
1642
{
1643
FLUID_LOG(FLUID_WARN, "Failed to create IPv6 server socket: %d (will try with IPv4)", fluid_socket_get_error());
1644
1645
sock = socket(AF_INET, SOCK_STREAM, 0);
1646
addr = (const struct sockaddr *) &addr4;
1647
addr_size = sizeof(addr4);
1648
}
1649
1650
#else
1651
sock = socket(AF_INET, SOCK_STREAM, 0);
1652
addr = (const struct sockaddr *) &addr4;
1653
addr_size = sizeof(addr4);
1654
#endif
1655
1656
if(sock == INVALID_SOCKET)
1657
{
1658
FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error());
1659
fluid_socket_cleanup();
1660
return NULL;
1661
}
1662
1663
if(bind(sock, addr, addr_size) == SOCKET_ERROR)
1664
{
1665
FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %d", fluid_socket_get_error());
1666
fluid_socket_close(sock);
1667
fluid_socket_cleanup();
1668
return NULL;
1669
}
1670
1671
if(listen(sock, SOMAXCONN) == SOCKET_ERROR)
1672
{
1673
FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %d", fluid_socket_get_error());
1674
fluid_socket_close(sock);
1675
fluid_socket_cleanup();
1676
return NULL;
1677
}
1678
1679
server_socket = FLUID_NEW(fluid_server_socket_t);
1680
1681
if(server_socket == NULL)
1682
{
1683
FLUID_LOG(FLUID_ERR, "Out of memory");
1684
fluid_socket_close(sock);
1685
fluid_socket_cleanup();
1686
return NULL;
1687
}
1688
1689
server_socket->socket = sock;
1690
server_socket->func = func;
1691
server_socket->data = data;
1692
server_socket->cont = 1;
1693
1694
server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
1695
0, FALSE);
1696
1697
if(server_socket->thread == NULL)
1698
{
1699
FLUID_FREE(server_socket);
1700
fluid_socket_close(sock);
1701
fluid_socket_cleanup();
1702
return NULL;
1703
}
1704
1705
return server_socket;
1706
}
1707
1708
void delete_fluid_server_socket(fluid_server_socket_t *server_socket)
1709
{
1710
fluid_return_if_fail(server_socket != NULL);
1711
1712
server_socket->cont = 0;
1713
1714
if(server_socket->socket != INVALID_SOCKET)
1715
{
1716
fluid_socket_close(server_socket->socket);
1717
}
1718
1719
if(server_socket->thread)
1720
{
1721
fluid_thread_join(server_socket->thread);
1722
delete_fluid_thread(server_socket->thread);
1723
}
1724
1725
FLUID_FREE(server_socket);
1726
1727
// Should be called the same number of times as fluid_socket_init()
1728
fluid_socket_cleanup();
1729
}
1730
1731
#endif // NETWORK_SUPPORT
1732
1733
FILE* fluid_file_open(const char* path, const char** errMsg)
1734
{
1735
static const char ErrExist[] = "File does not exist.";
1736
static const char ErrRegular[] = "File is not regular, refusing to open it.";
1737
static const char ErrNull[] = "File does not exists or insufficient permissions to open it.";
1738
1739
FILE* handle = NULL;
1740
1741
if(!fluid_file_test(path, FLUID_FILE_TEST_EXISTS))
1742
{
1743
if(errMsg != NULL)
1744
{
1745
*errMsg = ErrExist;
1746
}
1747
}
1748
else if(!fluid_file_test(path, FLUID_FILE_TEST_IS_REGULAR))
1749
{
1750
if(errMsg != NULL)
1751
{
1752
*errMsg = ErrRegular;
1753
}
1754
}
1755
else if((handle = FLUID_FOPEN(path, "rb")) == NULL)
1756
{
1757
if(errMsg != NULL)
1758
{
1759
*errMsg = ErrNull;
1760
}
1761
}
1762
1763
return handle;
1764
}
1765
1766
fluid_long_long_t fluid_file_tell(FILE* f)
1767
{
1768
#ifdef _WIN32
1769
// On Windows, long is only a 32 bit integer. Thus ftell() does not support to handle files >2GiB.
1770
// We should use _ftelli64() in this case, however its availability depends on MS CRT and might not be
1771
// available on WindowsXP, Win98, etc.
1772
//
1773
// The web recommends to fallback to _telli64() in this case. However, it's return value differs from
1774
// _ftelli64() on Win10: https://github.com/FluidSynth/fluidsynth/pull/629#issuecomment-602238436
1775
//
1776
// Thus, we use fgetpos().
1777
fpos_t pos;
1778
if(fgetpos(f, &pos) != 0)
1779
{
1780
return (fluid_long_long_t)-1L;
1781
}
1782
return pos;
1783
#else
1784
return ftell(f);
1785
#endif
1786
}
1787
1788
#if defined(_WIN32) || defined(__CYGWIN__)
1789
// not thread-safe!
1790
char* fluid_get_windows_error(void)
1791
{
1792
static TCHAR err[1024];
1793
1794
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
1795
NULL,
1796
GetLastError(),
1797
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
1798
err,
1799
sizeof(err)/sizeof(err[0]),
1800
NULL);
1801
1802
#ifdef _UNICODE
1803
static char ascii_err[sizeof(err)];
1804
1805
WideCharToMultiByte(CP_UTF8, 0, err, -1, ascii_err, sizeof(ascii_err)/sizeof(ascii_err[0]), 0, 0);
1806
return ascii_err;
1807
#else
1808
return err;
1809
#endif
1810
}
1811
#endif
1812
1813