Merge branch 'master' into file-callback

This commit is contained in:
Tom M 2017-11-27 14:52:20 +01:00 committed by GitHub
commit 621c84f6a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 205 additions and 242 deletions

View file

@ -364,7 +364,7 @@ https://stackoverflow.com/a/6251757
<type>str</type>
<def>PortAudio Default</def>
<desc>
Device to use for PortAudio driver output. Note that 'PortAudio Default' is a special value which outputs to the default PortAudio device.
Device to use for PortAudio driver output. Note that 'PortAudio Default' is a special value which outputs to the default PortAudio device. The format of the device name is: "&lt;device_index&gt;:&lt;host_api_name&gt;:&lt;host_device_name&gt;" e.g. "11:Windows DirectSound:SB PCI"
</desc>
</setting>
<setting>

View file

@ -90,6 +90,7 @@ Changes in FluidSynth 2.0.0 concerning developers:
- all public delete_* functions return void and are safe when called with NULL
- the shell command handler was decoupled internally, as a consequence the param list of new_fluid_server() and new_fluid_cmd_handler() was adapted
- reverb: roomsize is now limited to an upper threshold of 1.0
- use unique device names for the "audio.portaudio.device" setting
<br /><br />
- add "synth.volenv" a setting for volume envelope processing
- add "midi.autoconnect" a setting for automatically connecting fluidsynth to available MIDI input ports

View file

@ -61,10 +61,63 @@ void delete_fluid_portaudio_driver (fluid_audio_driver_t *p);
#define PORTAUDIO_DEFAULT_DEVICE "PortAudio Default"
/**
* Checks if device_num is a valid device and returns the name of the portaudio device.
* A device is valid if it is an output device with at least 2 channels.
*
* @param device_num index of the portaudio device to check.
* @param name_ptr if device_num is valid, set to a unique device name, ignored otherwise
*
* The name returned is unique for each num_device index, so this
* name is useful to identify any available host audio device.
* This name is convenient for audio.portaudio.device setting.
*
* The format of the name is: device_index:host_api_name:host_device_name
*
* example: 5:MME:SB PCI
*
* 5: is the portaudio device index.
* MME: is the host API name.
* SB PCI: is the host device name.
*
* @return #FLUID_OK if device_num is a valid output device, #FLUID_FAILED otherwise.
* When #FLUID_OK, the name is returned in allocated memory. The caller must check
* the name pointer for a valid memory allocation and should free the memory.
*/
static int fluid_portaudio_get_device_name(int device_num, char **name_ptr)
{
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo (device_num);
if( deviceInfo->maxOutputChannels >= 2 )
{
const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
/* The size of the buffer name for the following format:
device_index:host_api_name:host_device_name.
*/
int i = device_num;
int size = 0;
do {size++; i = i/10 ;} while(i); /* index size */
/* host API size + host device size + 2 separators + zero termination */
size += strlen(hostInfo->name) + strlen(deviceInfo->name) + 3;
*name_ptr = FLUID_MALLOC (size);
if (*name_ptr)
{ /* the name is filled if allocation is successful */
FLUID_SPRINTF(*name_ptr,"%d:%s:%s",device_num,
hostInfo->name, deviceInfo->name);
}
return FLUID_OK; /* device_num is a valid device */
}
else return FLUID_FAILED; /* device_num is an invalid device */
}
/**
* Initializes "audio.portaudio.device" setting with an options list of unique device names
* of available sound card devices.
* @param settings pointer to settings.
*/
void
fluid_portaudio_driver_settings (fluid_settings_t *settings)
{
const PaDeviceInfo *deviceInfo;
int numDevices;
PaError err;
int i;
@ -85,17 +138,27 @@ fluid_portaudio_driver_settings (fluid_settings_t *settings)
if (numDevices < 0)
{
FLUID_LOG (FLUID_ERR, "PortAudio returned unexpected device count %d", numDevices);
return;
FLUID_LOG (FLUID_ERR, "PortAudio returned unexpected device count %d", numDevices);
}
for (i = 0; i < numDevices; i++)
else for (i = 0; i < numDevices; i++)
{
deviceInfo = Pa_GetDeviceInfo (i);
if ( deviceInfo->maxOutputChannels >= 2 )
fluid_settings_add_option (settings, "audio.portaudio.device",
deviceInfo->name);
}
char * name;
if(fluid_portaudio_get_device_name(i, &name) == FLUID_OK)
{
/* the device i is a valid output device */
if(name)
{
/* registers this name in the option list */
fluid_settings_add_option (settings, "audio.portaudio.device", name);
FLUID_FREE (name);
}
else
{
FLUID_LOG (FLUID_ERR, "Out of memory");
break;
}
}
}
/* done with PortAudio for now, may get reopened later */
err = Pa_Terminate();
@ -104,14 +167,22 @@ fluid_portaudio_driver_settings (fluid_settings_t *settings)
printf ("PortAudio termination error: %s\n", Pa_GetErrorText (err) );
}
/**
* Creates the portaudio driver and opens the portaudio device
* indicated by audio.portaudio.device setting.
*
* @param settings pointer to settings
* @param synth the synthesizer instance
* @return pointer to the driver on success, NULL otherwise.
*/
fluid_audio_driver_t *
new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth)
{
fluid_portaudio_driver_t *dev = NULL;
PaStreamParameters outputParams;
char *device = NULL;
double sample_rate;
int period_size;
char *device = NULL; /* the portaudio device name to work with */
double sample_rate; /* intended sample rate */
int period_size; /* intended buffer size */
PaError err;
dev = FLUID_NEW (fluid_portaudio_driver_t);
@ -135,19 +206,20 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth)
FLUID_MEMSET (dev, 0, sizeof (fluid_portaudio_driver_t));
dev->synth = synth;
/* gets audio parameters from the settings */
fluid_settings_getint (settings, "audio.period-size", &period_size);
fluid_settings_getnum (settings, "synth.sample-rate", &sample_rate);
fluid_settings_dupstr(settings, "audio.portaudio.device", &device); /* ++ alloc device name */
memset (&outputParams, 0, sizeof (outputParams));
outputParams.channelCount = 2;
outputParams.channelCount = 2; /* For stereo output */
outputParams.suggestedLatency = (PaTime)period_size / sample_rate;
/* Locate the device if specified */
if (strcmp (device, PORTAUDIO_DEFAULT_DEVICE) != 0)
{
const PaDeviceInfo *deviceInfo;
{ /* The intended device is not the default device name, so we search
a device among available devices */
int numDevices;
int i;
@ -161,23 +233,46 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth)
for (i = 0; i < numDevices; i++)
{
deviceInfo = Pa_GetDeviceInfo (i);
char * name;
if(fluid_portaudio_get_device_name(i, &name) == FLUID_OK )
{
/* the device i is a valid output device */
if(name)
{
/* We see if the name corresponds to audio.portaudio.device */
char found = (strcmp (device, name) == 0);
FLUID_FREE (name);
if (strcmp (device, deviceInfo->name) == 0)
{
outputParams.device = i;
break;
}
if(found)
{
/* the device index is found */
outputParams.device = i;
/* The search is finished */
break;
}
}
else
{
FLUID_LOG (FLUID_ERR, "Out of memory");
goto error_recovery;
}
}
}
if (i == numDevices)
{
FLUID_LOG (FLUID_ERR, "PortAudio device '%s' was not found", device);
goto error_recovery;
}
}
else outputParams.device = Pa_GetDefaultOutputDevice();
else
{ /* the default device will be used */
outputParams.device = Pa_GetDefaultOutputDevice();
}
/* The device is found. We set the sample format and the audio rendering
function suited to this format.
*/
if (fluid_settings_str_equal (settings, "audio.sample-format", "16bits"))
{
outputParams.sampleFormat = paInt16;
@ -199,11 +294,11 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth)
/* Open an audio I/O stream. */
err = Pa_OpenStream (&dev->stream,
NULL, /* Input parameters */
&outputParams,
&outputParams, /* Output parameters */
sample_rate,
period_size,
paNoFlag,
fluid_portaudio_run,
fluid_portaudio_run, /* callback */
dev);
if (err != paNoError)
@ -213,7 +308,7 @@ new_fluid_portaudio_driver (fluid_settings_t *settings, fluid_synth_t *synth)
goto error_recovery;
}
err = Pa_StartStream (dev->stream);
err = Pa_StartStream (dev->stream); /* starts the I/O stream */
if (err != paNoError)
{

View file

@ -2752,10 +2752,10 @@ fluid_synth_nwrite_float(fluid_synth_t* synth, int len,
{
#ifdef WITH_FLOAT
if(fx_left != NULL)
FLUID_MEMCPY(fx_left[i + count], fx_left_in[i], bytes);
FLUID_MEMCPY(fx_left[i] + count, fx_left_in[i], bytes);
if(fx_right != NULL)
FLUID_MEMCPY(fx_right[i + count], fx_right_in[i], bytes);
FLUID_MEMCPY(fx_right[i] + count, fx_right_in[i], bytes);
#else //WITH_FLOAT
int j;
if(fx_left != NULL) {

View file

@ -32,7 +32,13 @@
/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket.
* Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */
#define WIN32_SOCKET_FLAG 0x40000000
#ifdef _WIN32
#define FLUID_SOCKET_FLAG 0x40000000
#else
#define FLUID_SOCKET_FLAG 0x00000000
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#endif
/* SCHED_FIFO priority for high priority timer threads */
#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10
@ -854,14 +860,14 @@ fluid_istream_gets (fluid_istream_t in, char* buf, int len)
if (n == -1) return -1;
#else
/* Handle read differently depending on if its a socket or file descriptor */
if (!(in & WIN32_SOCKET_FLAG))
if (!(in & FLUID_SOCKET_FLAG))
{
n = read(in, &c, 1);
if (n == -1) return -1;
}
else
{
n = recv(in & ~WIN32_SOCKET_FLAG, &c, 1, 0);
n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0);
if (n == SOCKET_ERROR) return -1;
}
#endif
@ -923,11 +929,11 @@ fluid_ostream_printf (fluid_ostream_t out, char* format, ...)
int retval;
/* Handle write differently depending on if its a socket or file descriptor */
if (!(out & WIN32_SOCKET_FLAG))
if (!(out & FLUID_SOCKET_FLAG))
return write(out, buf, strlen (buf));
/* Socket */
retval = send (out & ~WIN32_SOCKET_FLAG, buf, strlen (buf), 0);
retval = send (out & ~FLUID_SOCKET_FLAG, buf, strlen (buf), 0);
return retval != SOCKET_ERROR ? retval : -1;
}
@ -939,191 +945,55 @@ int fluid_server_socket_join(fluid_server_socket_t *server_socket)
return fluid_thread_join (server_socket->thread);
}
static int fluid_socket_init(void)
{
#ifdef _WIN32
WSADATA wsaData;
int res = WSAStartup(MAKEWORD(2,2), &wsaData);
#ifndef WIN32 // Not win32?
if (res != 0) {
FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res);
return FLUID_FAILED;
}
#endif
#define SOCKET_ERROR -1
return FLUID_OK;
}
static void fluid_socket_cleanup(void)
{
#ifdef _WIN32
WSACleanup();
#endif
}
static int fluid_socket_get_error(void)
{
#ifdef _WIN32
return (int)WSAGetLastError();
#else
return errno;
#endif
}
fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
{
return sock;
return sock | FLUID_SOCKET_FLAG;
}
fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
{
return sock;
}
void fluid_socket_close(fluid_socket_t sock)
{
if (sock != INVALID_SOCKET)
close (sock);
}
static fluid_thread_return_t
fluid_server_socket_run (void *data)
{
fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
fluid_socket_t client_socket;
#ifdef IPV6_SUPPORT
struct sockaddr_in6 addr;
char straddr[INET6_ADDRSTRLEN];
#else
struct sockaddr_in addr;
char straddr[INET_ADDRSTRLEN];
#endif
socklen_t addrlen = sizeof (addr);
int retval;
FLUID_MEMSET((char *)&addr, 0, 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 FLUID_THREAD_RETURN_VALUE;
} else {
#ifdef HAVE_INETNTOP
#ifdef IPV6_SUPPORT
inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
#else
inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
#endif
#endif
#ifdef HAVE_INETNTOP
retval = server_socket->func (server_socket->data, client_socket,
straddr);
#else
retval = server_socket->func (server_socket->data, client_socket,
inet_ntoa (addr.sin_addr));
#endif
if (retval != 0)
fluid_socket_close(client_socket);
}
}
FLUID_LOG(FLUID_DBG, "Server closing");
return FLUID_THREAD_RETURN_VALUE;
}
fluid_server_socket_t*
new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
{
fluid_server_socket_t* server_socket;
#ifdef IPV6_SUPPORT
struct sockaddr_in6 addr;
#else
struct sockaddr_in addr;
#endif
fluid_socket_t sock;
g_return_val_if_fail (func != NULL, NULL);
#ifdef IPV6_SUPPORT
sock = socket(AF_INET6, 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_in6));
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any;
addr.sin6_port = htons(port);
#else
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);
#endif
if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == 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("server", fluid_server_socket_run, server_socket,
0, FALSE);
if (server_socket->thread == NULL) {
FLUID_FREE(server_socket);
fluid_socket_close(sock);
return NULL;
}
return server_socket;
}
void delete_fluid_server_socket(fluid_server_socket_t* server_socket)
{
fluid_return_if_fail(server_socket != NULL);
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);
}
#else // Win32 is "special"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
{
return sock | WIN32_SOCKET_FLAG;
}
fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
{
return sock | WIN32_SOCKET_FLAG;
return sock | FLUID_SOCKET_FLAG;
}
void fluid_socket_close (fluid_socket_t sock)
{
if (sock != INVALID_SOCKET)
closesocket (sock);
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
static fluid_thread_return_t fluid_server_socket_run (void *data)
@ -1132,13 +1002,18 @@ static fluid_thread_return_t fluid_server_socket_run (void *data)
fluid_socket_t client_socket;
#ifdef IPV6_SUPPORT
struct sockaddr_in6 addr;
char straddr[INET6_ADDRSTRLEN];
#else
struct sockaddr_in addr;
#endif
#ifdef HAVE_INETNTOP
#ifdef IPV6_SUPPORT
char straddr[INET6_ADDRSTRLEN];
#else
char straddr[INET_ADDRSTRLEN];
#endif
#endif
#endif /* IPV6_SUPPORT */
#endif /* HAVE_INETNTOP */
socklen_t addrlen = sizeof (addr);
int r;
FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
@ -1154,7 +1029,7 @@ static fluid_thread_return_t fluid_server_socket_run (void *data)
if (client_socket == INVALID_SOCKET)
{
if (server_socket->cont)
FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", WSAGetLastError ());
FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", fluid_socket_get_error());
server_socket->cont = 0;
return FLUID_THREAD_RETURN_VALUE;
@ -1162,13 +1037,13 @@ static fluid_thread_return_t fluid_server_socket_run (void *data)
else
{
#ifdef HAVE_INETNTOP
#ifdef IPV6_SUPPORT
inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
#else
inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
#endif
#endif
#ifdef HAVE_INETNTOP
r = server_socket->func (server_socket->data, client_socket,
straddr);
#else
@ -1182,7 +1057,7 @@ static fluid_thread_return_t fluid_server_socket_run (void *data)
FLUID_LOG (FLUID_DBG, "Server closing");
return FLUID_THREAD_RETURN_VALUE;
return FLUID_THREAD_RETURN_VALUE;
}
fluid_server_socket_t*
@ -1196,29 +1071,25 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
#endif
fluid_socket_t sock;
WSADATA wsaData;
int retval;
g_return_val_if_fail (func != NULL, NULL);
fluid_return_val_if_fail (func != NULL, NULL);
// Win32 requires initialization of winsock
retval = WSAStartup (MAKEWORD (2,2), &wsaData);
if (retval != 0)
if (fluid_socket_init() != FLUID_OK)
{
FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", retval);
return NULL;
}
#ifdef IPV6_SUPPORT
sock = socket (AF_INET6, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
{
FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
WSACleanup ();
FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", fluid_socket_get_error());
fluid_socket_cleanup();
return NULL;
}
FLUID_MEMSET(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons (port);
addr.sin6_port = htons ((uint16_t)port);
addr.sin6_addr = in6addr_any;
#else
@ -1226,30 +1097,30 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
if (sock == INVALID_SOCKET)
{
FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
WSACleanup ();
FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", fluid_socket_get_error());
fluid_socket_cleanup();
return NULL;
}
FLUID_MEMSET(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
addr.sin_port = htons ((uint16_t)port);
addr.sin_addr.s_addr = htonl (INADDR_ANY);
#endif
retval = bind (sock, (struct sockaddr *)&addr, sizeof (addr));
if (retval == SOCKET_ERROR)
if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR)
{
FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", WSAGetLastError ());
FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", fluid_socket_get_error());
fluid_socket_close (sock);
WSACleanup ();
fluid_socket_cleanup();
return NULL;
}
if (listen (sock, SOMAXCONN) == SOCKET_ERROR)
{
FLUID_LOG (FLUID_ERR, "Failed to listen on server socket: %ld", WSAGetLastError ());
FLUID_LOG (FLUID_ERR, "Failed to listen on server socket: %ld", fluid_socket_get_error());
fluid_socket_close (sock);
WSACleanup ();
fluid_socket_cleanup();
return NULL;
}
@ -1259,7 +1130,7 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
{
FLUID_LOG (FLUID_ERR, "Out of memory");
fluid_socket_close (sock);
WSACleanup ();
fluid_socket_cleanup();
return NULL;
}
@ -1274,7 +1145,7 @@ new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
{
FLUID_FREE (server_socket);
fluid_socket_close (sock);
WSACleanup ();
fluid_socket_cleanup();
return NULL;
}
@ -1295,7 +1166,6 @@ void delete_fluid_server_socket(fluid_server_socket_t *server_socket)
FLUID_FREE (server_socket);
WSACleanup (); // Should be called the same number of times as WSAStartup
// Should be called the same number of times as fluid_socket_init()
fluid_socket_cleanup();
}
#endif

View file

@ -36,7 +36,6 @@
#ifndef _FLUID_SYS_H
#define _FLUID_SYS_H
#include <glib.h>
#include "fluidsynth_priv.h"
#ifdef LADSPA
@ -356,7 +355,6 @@ fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock);
fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock);
/* Profiling */

View file

@ -174,7 +174,6 @@ typedef double fluid_real_t;
typedef SOCKET fluid_socket_t;
#else
typedef int fluid_socket_t;
#define INVALID_SOCKET -1
#endif
#if defined(SUPPORTS_VLA)