fluid_jack.c
上传用户:tjmskj2
上传日期:2020-08-17
资源大小:577k
文件大小:19k
源码类别:

midi

开发平台:

C/C++

  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 Library General Public License
  7.  * as published by the Free Software Foundation; either version 2 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.  * Library General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Library General Public
  16.  * License along with this library; if not, write to the Free
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  18.  * 02111-1307, USA
  19.  */
  20. /* fluid_jack.c
  21.  *
  22.  * Driver for the JACK
  23.  *
  24.  * This code is derived from the simple_client example in the JACK
  25.  * source distribution. Many thanks to Paul Davis.
  26.  *
  27.  */
  28. #include "fluidsynth_priv.h"
  29. #include "fluid_sys.h"
  30. #include "fluid_synth.h"
  31. #include "fluid_adriver.h"
  32. #include "fluid_mdriver.h"
  33. #include "fluid_settings.h"
  34. #include <jack/jack.h>
  35. #include <jack/midiport.h>
  36. #include "config.h"
  37. #include "fluid_lash.h"
  38. typedef struct _fluid_jack_audio_driver_t fluid_jack_audio_driver_t;
  39. typedef struct _fluid_jack_midi_driver_t fluid_jack_midi_driver_t;
  40. /* Clients are shared for drivers using the same server. */
  41. typedef struct
  42. {
  43.   jack_client_t *client;
  44.   char *server;                 /* Jack server name used */
  45.   fluid_jack_audio_driver_t *audio_driver;
  46.   fluid_jack_midi_driver_t *midi_driver;
  47. } fluid_jack_client_t;
  48. /* Jack audio driver instance */
  49. struct _fluid_jack_audio_driver_t
  50. {
  51.   fluid_audio_driver_t driver;
  52.   fluid_jack_client_t *client_ref;
  53.   int audio_channels;
  54.   jack_port_t **output_ports;
  55.   int num_output_ports;
  56.   float **output_bufs;
  57.   fluid_audio_func_t callback;
  58.   void* data;
  59. };
  60. /* Jack MIDI driver instance */
  61. struct _fluid_jack_midi_driver_t
  62. {
  63.   fluid_midi_driver_t driver;
  64.   fluid_jack_client_t *client_ref;
  65.   jack_port_t *midi_port;
  66.   fluid_midi_parser_t *parser;
  67. };
  68. static fluid_jack_client_t *new_fluid_jack_client (fluid_settings_t *settings,
  69.                                                    int isaudio, void *driver);
  70. static int fluid_jack_client_register_ports (void *driver, int isaudio,
  71.                                              jack_client_t *client,
  72.                                              fluid_settings_t *settings);
  73. fluid_audio_driver_t*
  74. new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data);
  75. int delete_fluid_jack_audio_driver(fluid_audio_driver_t* p);
  76. void fluid_jack_driver_shutdown(void *arg);
  77. int fluid_jack_driver_srate(jack_nframes_t nframes, void *arg);
  78. int fluid_jack_driver_bufsize(jack_nframes_t nframes, void *arg);
  79. int fluid_jack_driver_process(jack_nframes_t nframes, void *arg);
  80. int delete_fluid_jack_midi_driver(fluid_midi_driver_t *p);
  81. static fluid_mutex_t last_client_mutex = G_STATIC_MUTEX_INIT;     /* Probably not necessary, but just in case drivers are created by multiple threads */
  82. static fluid_jack_client_t *last_client = NULL;       /* Last unpaired client. For audio/MIDI driver pairing. */
  83. void
  84. fluid_jack_audio_driver_settings(fluid_settings_t* settings)
  85. {
  86.   fluid_settings_register_str(settings, "audio.jack.id", "fluidsynth", 0, NULL, NULL);
  87.   fluid_settings_register_int(settings, "audio.jack.multi", 0, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL);
  88.   fluid_settings_register_int(settings, "audio.jack.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED, NULL, NULL);
  89.   fluid_settings_register_str(settings, "audio.jack.server", "", 0, NULL, NULL);
  90. }
  91. /*
  92.  * Create Jack client as necessary, share clients of the same server.
  93.  * @param settings Settings object
  94.  * @param isaudio TRUE if audio driver, FALSE if MIDI
  95.  * @param driver fluid_jack_audio_driver_t or fluid_jack_midi_driver_t
  96.  * @param data The user data instance associated with the driver (fluid_synth_t for example)
  97.  * @return New or paired Audio/MIDI Jack client
  98.  */
  99. static fluid_jack_client_t *
  100. new_fluid_jack_client (fluid_settings_t *settings, int isaudio, void *driver)
  101. {
  102.   fluid_jack_client_t *client_ref = NULL;
  103.   char *server = NULL;
  104.   char* client_name;
  105.   char name[64];
  106.   fluid_settings_dupstr (settings, isaudio ? "audio.jack.server"        /* ++ alloc server name */
  107.                          : "midi.jack.server", &server);
  108.   fluid_mutex_lock (last_client_mutex);     /* ++ lock last_client */
  109.   /* If the last client uses the same server and is not the same type (audio or MIDI),
  110.    * then re-use the client. */
  111.   if (last_client && ((!last_client->server && !server)
  112.                       || FLUID_STRCMP (last_client->server, server) == 0)
  113.       && ((!isaudio && !last_client->midi_driver) || (isaudio && !last_client->audio_driver)))
  114.   {
  115.     client_ref = last_client;
  116.     last_client = NULL;         /* No more pairing for this client */
  117.     /* Register ports */
  118.     if (fluid_jack_client_register_ports (driver, isaudio, client_ref->client, settings) != FLUID_OK)
  119.       goto error_recovery;
  120.     if (isaudio) fluid_atomic_pointer_set (&client_ref->audio_driver, driver);
  121.     else fluid_atomic_pointer_set (&client_ref->midi_driver, driver);
  122.     fluid_mutex_unlock (last_client_mutex);       /* -- unlock last_client */
  123.     if (server) FLUID_FREE (server);
  124.     return client_ref;
  125.   }
  126.   /* No existing client for this Jack server */
  127.   client_ref = FLUID_NEW (fluid_jack_client_t);
  128.   if (!client_ref)
  129.   {
  130.     FLUID_LOG (FLUID_ERR, "Out of memory");
  131.     goto error_recovery;
  132.   }
  133.   FLUID_MEMSET (client_ref, 0, sizeof (fluid_jack_client_t));
  134.   fluid_settings_dupstr (settings, isaudio ? "audio.jack.id"   /* ++ alloc client name */
  135.                          : "midi.jack.id", &client_name);
  136.   if (client_name != NULL && client_name[0] != 0)
  137.     snprintf(name, 64, "%s", client_name);
  138.   else strcpy (name, "fluidsynth");
  139.   name[63] = '';
  140.   if (client_name) FLUID_FREE (client_name);    /* -- free client name */
  141.   /* Open a connection to the Jack server and use the server name if specified */
  142.   if (server && server[0] != '')
  143.     client_ref->client = jack_client_open (name, JackServerName, NULL, server);
  144.   else client_ref->client = jack_client_open (name, JackNullOption, NULL);
  145.   if (!client_ref->client)
  146.   {
  147.     FLUID_LOG (FLUID_ERR, "Failed to connect to Jack server.");
  148.     goto error_recovery;
  149.   }
  150.   jack_set_process_callback (client_ref->client, fluid_jack_driver_process, client_ref);
  151.   jack_set_buffer_size_callback (client_ref->client, fluid_jack_driver_bufsize, client_ref);
  152.   jack_set_sample_rate_callback (client_ref->client, fluid_jack_driver_srate, client_ref);
  153.   jack_on_shutdown (client_ref->client, fluid_jack_driver_shutdown, client_ref);
  154.   /* Register ports */
  155.   if (fluid_jack_client_register_ports (driver, isaudio, client_ref->client, settings) != FLUID_OK)
  156.     goto error_recovery;
  157.   /* tell the JACK server that we are ready to roll */
  158.   if (jack_activate (client_ref->client))
  159.   {
  160.     FLUID_LOG (FLUID_ERR, "Failed to activate Jack client");
  161.     goto error_recovery;
  162.   }
  163.   /* tell the lash server our client name */
  164. #ifdef LASH_ENABLED
  165.   {
  166.     int enable_lash = 0;
  167.     fluid_settings_getint (settings, "lash.enable", &enable_lash);
  168.     if (enable_lash)
  169.       fluid_lash_jack_client_name (fluid_lash_client, name);
  170.   }
  171. #endif /* LASH_ENABLED */
  172.   client_ref->server = server;        /* !! takes over allocation */
  173.   server = NULL;      /* Set to NULL so it doesn't get freed below */
  174.   last_client = client_ref;
  175.   if (isaudio) fluid_atomic_pointer_set (&client_ref->audio_driver, driver);
  176.   else fluid_atomic_pointer_set (&client_ref->midi_driver, driver);
  177.   fluid_mutex_unlock (last_client_mutex);       /* -- unlock last_client */
  178.   if (server) FLUID_FREE (server);
  179.   return client_ref;
  180. error_recovery:
  181.   fluid_mutex_unlock (last_client_mutex);       /* -- unlock clients list */
  182.   if (server) FLUID_FREE (server);  /* -- free server name */
  183.   if (client_ref)
  184.   {
  185.     if (client_ref->client)
  186.       jack_client_close (client_ref->client);
  187.     FLUID_FREE (client_ref);
  188.   }
  189.   return NULL;
  190. }
  191. static int
  192. fluid_jack_client_register_ports (void *driver, int isaudio, jack_client_t *client,
  193.                                   fluid_settings_t *settings)
  194. {
  195.   fluid_jack_audio_driver_t *dev;
  196.   char name[64];
  197.   int multi;
  198.   int i;
  199.   if (!isaudio)
  200.   {
  201.     fluid_jack_midi_driver_t *dev = driver;
  202.     dev->midi_port = jack_port_register (client, "midi", JACK_DEFAULT_MIDI_TYPE,
  203.          JackPortIsInput | JackPortIsTerminal, 0);
  204.     if (!dev->midi_port)
  205.     {
  206.       FLUID_LOG (FLUID_ERR, "Failed to create Jack MIDI port");
  207.       return FLUID_FAILED;
  208.     }
  209.     return FLUID_OK;
  210.   }
  211.   dev = driver;
  212.   fluid_settings_getint (settings, "audio.jack.multi", &multi);
  213.   if (multi)
  214.   {
  215.     /* create the two audio output ports */
  216.     dev->num_output_ports = 1;
  217.     dev->output_ports = FLUID_ARRAY (jack_port_t*, 2 * dev->num_output_ports);
  218.     if (dev->output_ports == NULL)
  219.     {
  220.       FLUID_LOG (FLUID_PANIC, "Jack server not running?");
  221.       return FLUID_FAILED;
  222.     }
  223.     dev->output_bufs = FLUID_ARRAY (float*, 2 * dev->num_output_ports);
  224.     FLUID_MEMSET (dev->output_ports, 0, 2 * dev->num_output_ports * sizeof(jack_port_t*));
  225.     dev->output_ports[0]
  226.       = jack_port_register (client, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  227.     dev->output_ports[1]
  228.       = jack_port_register (client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  229.   }
  230.   else
  231.   {
  232.     fluid_settings_getint (settings, "synth.audio-channels", &dev->num_output_ports);
  233.     dev->output_ports = FLUID_ARRAY (jack_port_t *, 2 * dev->num_output_ports);
  234.     if (dev->output_ports == NULL)
  235.     {
  236.       FLUID_LOG (FLUID_PANIC, "Out of memory");
  237.       return FLUID_FAILED;
  238.     }
  239.     dev->output_bufs = FLUID_ARRAY (float *, 2 * dev->num_output_ports);
  240.     if (dev->output_bufs == NULL)
  241.     {
  242.       FLUID_LOG (FLUID_PANIC, "Out of memory");
  243.       return FLUID_FAILED;
  244.     }
  245.     FLUID_MEMSET (dev->output_ports, 0, 2 * dev->num_output_ports * sizeof (jack_port_t*));
  246.     for (i = 0; i < dev->num_output_ports; i++)
  247.     {
  248.       sprintf(name, "l_%02d", i);
  249.       dev->output_ports[2 * i]
  250.         = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  251.       sprintf(name, "r_%02d", i);
  252.       dev->output_ports[2 * i + 1]
  253.         = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  254.     }
  255.   }
  256.   return FLUID_OK;
  257. }
  258. static void
  259. fluid_jack_client_close (fluid_jack_client_t *client_ref, void *driver)
  260. {
  261.   if (client_ref->audio_driver == driver)
  262.     fluid_atomic_pointer_set (&client_ref->audio_driver, NULL);
  263.   else if (client_ref->midi_driver == driver)
  264.     fluid_atomic_pointer_set (&client_ref->midi_driver, NULL);
  265.   if (client_ref->audio_driver || client_ref->midi_driver)
  266.   {
  267.     g_usleep (100000);  /* FIXME - Hack to make sure that resources don't get freed while Jack callback is active */
  268.     return;
  269.   }
  270.   fluid_mutex_lock (last_client_mutex);
  271.   if (client_ref == last_client)
  272.     last_client = NULL;
  273.   fluid_mutex_unlock (last_client_mutex);
  274.   if (client_ref->client)
  275.     jack_client_close (client_ref->client);
  276.   if (client_ref->server)
  277.     FLUID_FREE (client_ref->server);
  278.   FLUID_FREE (client_ref);
  279. }
  280. fluid_audio_driver_t*
  281. new_fluid_jack_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth)
  282. {
  283.   return new_fluid_jack_audio_driver2(settings, NULL, synth);
  284. }
  285. fluid_audio_driver_t*
  286. new_fluid_jack_audio_driver2(fluid_settings_t* settings, fluid_audio_func_t func, void* data)
  287. {
  288.   fluid_jack_audio_driver_t* dev = NULL;
  289.   jack_client_t *client;
  290.   const char ** jack_ports;     /* for looking up ports */
  291.   int autoconnect = 0;
  292.   int jack_srate;
  293.   double sample_rate;
  294.   int i;
  295.   dev = FLUID_NEW(fluid_jack_audio_driver_t);
  296.   if (dev == NULL) {
  297.     FLUID_LOG(FLUID_ERR, "Out of memory");
  298.     return NULL;
  299.   }
  300.   FLUID_MEMSET(dev, 0, sizeof(fluid_jack_audio_driver_t));
  301.   dev->callback = func;
  302.   dev->data = data;
  303.   dev->client_ref = new_fluid_jack_client (settings, TRUE, dev);
  304.   if (!dev->client_ref)
  305.   {
  306.     FLUID_FREE (dev);
  307.     return NULL;
  308.   }
  309.   client = dev->client_ref->client;
  310.   /* display the current sample rate. once the client is activated
  311.      (see below), you should rely on your own sample rate
  312.      callback (see above) for this value.
  313.   */
  314.   jack_srate = jack_get_sample_rate (client);
  315.   FLUID_LOG (FLUID_DBG, "Jack engine sample rate: %lu", jack_srate);
  316.   fluid_settings_getnum (settings, "synth.sample-rate", &sample_rate);
  317.   if ((int)sample_rate != jack_srate) {
  318.     /* There's currently no way to change the sampling rate of the
  319.        synthesizer after it's been created. */
  320.     FLUID_LOG(FLUID_WARN, "Jack sample rate mismatch, expect tuning issues"
  321.       " (synth.sample-rate=%lu, jackd=%lu)", (int)sample_rate, jack_srate);
  322.   }
  323.   /* connect the ports. */
  324.   /* FIXME: should be done by a patchbay application */
  325.   /* find some physical ports and connect to them */
  326.   fluid_settings_getint (settings, "audio.jack.autoconnect", &autoconnect);
  327.   if (autoconnect) {
  328.     jack_ports = jack_get_ports (client, NULL, NULL, JackPortIsInput|JackPortIsPhysical);
  329.     if (jack_ports) {
  330.       int err;
  331.       int connected = 0;
  332.       for (i = 0; jack_ports[i] && i<2 * dev->num_output_ports; ++i) {
  333.         err = jack_connect (client, jack_port_name(dev->output_ports[i]), jack_ports[i]);
  334.         if (err) {
  335.           FLUID_LOG(FLUID_ERR, "Error connecting jack port");
  336.         } else {
  337.           connected++;
  338.         }
  339.       }
  340.       free (jack_ports);  /* free jack ports array (not the port values!) */
  341.     } else {
  342.       FLUID_LOG(FLUID_WARN, "Could not connect to any physical jack ports; fluidsynth is unconnected");
  343.     }
  344.   }
  345.   return (fluid_audio_driver_t*) dev;
  346. }
  347. /*
  348.  * delete_fluid_jack_audio_driver
  349.  */
  350. int
  351. delete_fluid_jack_audio_driver(fluid_audio_driver_t* p)
  352. {
  353.   fluid_jack_audio_driver_t* dev = (fluid_jack_audio_driver_t*) p;
  354.   if (dev == NULL) return 0;
  355.   if (dev->client_ref != NULL)
  356.     fluid_jack_client_close (dev->client_ref, dev);
  357.   if (dev->output_bufs)
  358.     FLUID_FREE (dev->output_bufs);
  359.   if (dev->output_ports)
  360.     FLUID_FREE (dev->output_ports);
  361.   FLUID_FREE(dev);
  362.   return 0;
  363. }
  364. /* Process function for audio and MIDI Jack drivers */
  365. int
  366. fluid_jack_driver_process (jack_nframes_t nframes, void *arg)
  367. {
  368.   fluid_jack_client_t *client = (fluid_jack_client_t *)arg;
  369.   fluid_jack_audio_driver_t *audio_driver;
  370.   fluid_jack_midi_driver_t *midi_driver;
  371.   float *left, *right;
  372.   int i, k;
  373.   jack_midi_event_t midi_event;
  374.   fluid_midi_event_t *evt;
  375.   void *midi_buffer;
  376.   jack_nframes_t event_count;
  377.   jack_nframes_t event_index;
  378.   unsigned int u;
  379.   /* Process MIDI events first, so that they take effect before audio synthesis */
  380.   midi_driver = fluid_atomic_pointer_get (&client->midi_driver);
  381.   if (midi_driver)
  382.   {
  383.     midi_buffer = jack_port_get_buffer (midi_driver->midi_port, 0);
  384.     event_count = jack_midi_get_event_count (midi_buffer);
  385.     for (event_index = 0; event_index < event_count; event_index++)
  386.     {
  387.       jack_midi_event_get (&midi_event, midi_buffer, event_index);
  388.       /* let the parser convert the data into events */
  389.       for (u = 0; u < midi_event.size; u++)
  390.       {
  391.         evt = fluid_midi_parser_parse (midi_driver->parser, midi_event.buffer[u]);
  392.         /* send the event to the next link in the chain */
  393.         if (evt != NULL) midi_driver->driver.handler (midi_driver->driver.data, evt);
  394.       }
  395.     }
  396.   }
  397.   audio_driver = client->audio_driver;
  398.   if (!audio_driver) return 0;
  399.   if (audio_driver->callback != NULL)
  400.   {
  401.     for (i = 0; i < audio_driver->num_output_ports * 2; i++)
  402.       audio_driver->output_bufs[i] = (float *)jack_port_get_buffer (audio_driver->output_ports[i], nframes);
  403.     return (*audio_driver->callback)(audio_driver->data, nframes, 0, NULL,
  404.                                      2 * audio_driver->num_output_ports,
  405.                                      audio_driver->output_bufs);
  406.   }
  407.   else if (audio_driver->num_output_ports == 1)
  408.   {
  409.     left = (float*) jack_port_get_buffer (audio_driver->output_ports[0], nframes);
  410.     right = (float*) jack_port_get_buffer (audio_driver->output_ports[1], nframes);
  411.     fluid_synth_write_float (audio_driver->data, nframes, left, 0, 1, right, 0, 1);
  412.   }
  413.   else
  414.   {
  415.     for (i = 0, k = audio_driver->num_output_ports; i < audio_driver->num_output_ports; i++, k++) {
  416.       audio_driver->output_bufs[i] = (float *)jack_port_get_buffer (audio_driver->output_ports[2*i], nframes);
  417.       audio_driver->output_bufs[k] = (float *)jack_port_get_buffer (audio_driver->output_ports[2*i+1], nframes);
  418.     }
  419.     fluid_synth_nwrite_float (audio_driver->data, nframes, audio_driver->output_bufs,
  420.                               audio_driver->output_bufs + audio_driver->num_output_ports, NULL, NULL);
  421.   }
  422.   return 0;
  423. }
  424. int
  425. fluid_jack_driver_bufsize(jack_nframes_t nframes, void *arg)
  426. {
  427. /*   printf("the maximum buffer size is now %lun", nframes); */
  428.   return 0;
  429. }
  430. int
  431. fluid_jack_driver_srate(jack_nframes_t nframes, void *arg)
  432. {
  433. /*   printf("the sample rate is now %lu/secn", nframes); */
  434.   /* FIXME: change the sample rate of the synthesizer! */
  435.   return 0;
  436. }
  437. void
  438. fluid_jack_driver_shutdown(void *arg)
  439. {
  440. //  fluid_jack_audio_driver_t* dev = (fluid_jack_audio_driver_t*) arg;
  441.   FLUID_LOG(FLUID_ERR, "Help! Lost the connection to the JACK server");
  442. /*   exit (1); */
  443. }
  444. void fluid_jack_midi_driver_settings (fluid_settings_t *settings)
  445. {
  446.   fluid_settings_register_str (settings, "midi.jack.id", "fluidsynth-midi", 0, NULL, NULL);
  447.   fluid_settings_register_str (settings, "midi.jack.server", "", 0, NULL, NULL);
  448. }
  449. /*
  450.  * new_fluid_jack_midi_driver
  451.  */
  452. fluid_midi_driver_t *
  453. new_fluid_jack_midi_driver (fluid_settings_t *settings,
  454.     handle_midi_event_func_t handler, void *data)
  455. {
  456.   fluid_jack_midi_driver_t* dev;
  457.   fluid_return_val_if_fail (handler != NULL, NULL);
  458.   /* allocate the device */
  459.   dev = FLUID_NEW (fluid_jack_midi_driver_t);
  460.   if (dev == NULL)
  461.   {
  462.     FLUID_LOG (FLUID_ERR, "Out of memory");
  463.     return NULL;
  464.   }
  465.   FLUID_MEMSET (dev, 0, sizeof (fluid_jack_midi_driver_t));
  466.   dev->driver.handler = handler;
  467.   dev->driver.data = data;
  468.   /* allocate one event to store the input data */
  469.   dev->parser = new_fluid_midi_parser ();
  470.   if (dev->parser == NULL)
  471.   {
  472.     FLUID_LOG (FLUID_ERR, "Out of memory");
  473.     FLUID_FREE (dev);
  474.     return NULL;
  475.   }
  476.   dev->client_ref = new_fluid_jack_client (settings, FALSE, dev);
  477.   if (!dev->client_ref)
  478.   {
  479.     FLUID_FREE (dev);
  480.     return NULL;
  481.   }
  482.   return (fluid_midi_driver_t *)dev;
  483. }
  484. int
  485. delete_fluid_jack_midi_driver(fluid_midi_driver_t *p)
  486. {
  487.   fluid_jack_midi_driver_t *dev = (fluid_jack_midi_driver_t *)p;
  488.   if (dev == NULL) return FLUID_OK;
  489.   if (dev->client_ref != NULL)
  490.     fluid_jack_client_close (dev->client_ref, dev);
  491.   if (dev->parser != NULL)
  492.     delete_fluid_midi_parser (dev->parser);
  493.   FLUID_FREE (dev);
  494.   return FLUID_OK;
  495. }