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

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. #define INITGUID
  21. #include "fluidsynth_priv.h"
  22. #include "fluid_synth.h"
  23. #include "fluid_sys.h"
  24. #include "fluid_adriver.h"
  25. #include "fluid_settings.h"
  26. #include <mmsystem.h>
  27. #include <dsound.h>
  28. fluid_audio_driver_t*
  29. new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth);
  30. int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* data);
  31. DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter);
  32. HWND fluid_win32_get_window(void);
  33. char* fluid_win32_error(HRESULT hr);
  34. #define FLUID_HINSTANCE  ((HINSTANCE)fluid_get_hinstance())
  35. typedef struct {
  36.   fluid_audio_driver_t driver;
  37.   LPDIRECTSOUND direct_sound;
  38.   LPDIRECTSOUNDBUFFER prim_buffer;
  39.   LPDIRECTSOUNDBUFFER sec_buffer;
  40.   WAVEFORMATEX* format;
  41.   HANDLE thread;
  42.   DWORD threadID;
  43.   fluid_synth_t* synth;
  44.   fluid_audio_callback_t write;
  45.   int cont;
  46.   DWORD buffer_byte_size;
  47.   DWORD queue_byte_size;
  48.   DWORD frame_size;
  49. } fluid_dsound_audio_driver_t;
  50. typedef struct {
  51.   LPGUID devGUID;
  52.   char* devname;
  53. } fluid_dsound_devsel_t;
  54. BOOL CALLBACK
  55. fluid_dsound_enum_callback(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
  56. {
  57.   fluid_settings_t* settings = (fluid_settings_t*) context;
  58.   fluid_settings_add_option(settings, "audio.dsound.device", (const char *)description);
  59.   return TRUE;
  60. }
  61. BOOL CALLBACK
  62. fluid_dsound_enum_callback2(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
  63. {
  64.   fluid_dsound_devsel_t* devsel = (fluid_dsound_devsel_t*) context;
  65.   FLUID_LOG(FLUID_DBG, "Testing audio device: %s", description);
  66.   if (strcasecmp(devsel->devname, description) == 0) {
  67.     devsel->devGUID = FLUID_NEW(GUID);
  68.     if(devsel->devGUID) {
  69.       memcpy(devsel->devGUID, guid, sizeof(GUID));
  70.       FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %p", devsel->devGUID);
  71.     }
  72.   }
  73.   return TRUE;
  74. }
  75. void fluid_dsound_audio_driver_settings(fluid_settings_t* settings)
  76. {
  77.   fluid_settings_register_str(settings, "audio.dsound.device", "default", 0, NULL, NULL);
  78.   fluid_settings_add_option(settings, "audio.dsound.device", "default");
  79.   DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback, settings);
  80. }
  81. /*
  82.  * new_fluid_dsound_audio_driver
  83.  */
  84. fluid_audio_driver_t*
  85. new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth)
  86. {
  87.   HRESULT hr;
  88.   DSBUFFERDESC desc;
  89.   fluid_dsound_audio_driver_t* dev = NULL;
  90.   DSCAPS caps;
  91.   char *buf1;
  92.   DWORD bytes1;
  93.   double sample_rate;
  94.   int periods, period_size;
  95.   fluid_dsound_devsel_t devsel;
  96.   /* check if the globals are initialized */
  97.   if (FLUID_HINSTANCE == NULL) {
  98.     FLUID_LOG(FLUID_ERR, "FluidSynth hinstance not set, which is needed for DirectSound");
  99.     return NULL;
  100.   }
  101. /*
  102.   if (fluid_wnd == NULL) {
  103.     if (fluid_win32_create_window() != 0) {
  104.       FLUID_LOG(FLUID_ERR, "Couldn't create window needed for DirectSound");
  105.       return NULL;
  106.     }
  107.   }
  108. */
  109.   /* create and clear the driver data */
  110.   dev = FLUID_NEW(fluid_dsound_audio_driver_t);
  111.   if (dev == NULL) {
  112.     FLUID_LOG(FLUID_ERR, "Out of memory");
  113.     return NULL;
  114.   }
  115.   FLUID_MEMSET(dev, 0, sizeof(fluid_dsound_audio_driver_t));
  116.   dev->synth = synth;
  117.   dev->cont = 1;
  118.   fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
  119.   fluid_settings_getint(settings, "audio.periods", &periods);
  120.   fluid_settings_getint(settings, "audio.period-size", &period_size);
  121.   /* check the format */
  122.   if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) {
  123.     FLUID_LOG(FLUID_ERR, "Unhandled sample format");
  124.     goto error_recovery;
  125.   }
  126.   dev->frame_size = 2 * sizeof(short);
  127.   dev->buffer_byte_size = period_size * dev->frame_size;
  128.   dev->queue_byte_size = periods * dev->buffer_byte_size;
  129.   dev->write = fluid_synth_write_s16;
  130.   /* create and initialize the buffer format */
  131.   dev->format = (WAVEFORMATEX*) FLUID_MALLOC(sizeof(WAVEFORMATEX));
  132.   if (dev->format == NULL) {
  133.     FLUID_LOG(FLUID_ERR, "Out of memory");
  134.     goto error_recovery;
  135.   }
  136.   ZeroMemory(dev->format, sizeof(WAVEFORMATEX));
  137.   dev->format->wFormatTag = WAVE_FORMAT_PCM;
  138.   dev->format->nChannels = 2;
  139.   dev->format->wBitsPerSample = 16;
  140.   dev->format->nSamplesPerSec = (DWORD) sample_rate;
  141.   dev->format->nBlockAlign = (WORD) dev->frame_size;
  142.   dev->format->nAvgBytesPerSec = dev->format->nSamplesPerSec * dev->frame_size;
  143.   dev->format->cbSize = 0;
  144.   devsel.devGUID = NULL;
  145.   /* get the selected device name. if none is specified, use NULL for the default device. */
  146.   if(fluid_settings_dupstr(settings, "audio.dsound.device", &devsel.devname)    /* ++ alloc device name */
  147.      && devsel.devname && strlen (devsel.devname) > 0) {
  148.     /* look for the GUID of the selected device */
  149.     DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback2, (void *)&devsel);
  150.   }
  151.   if (devsel.devname) FLUID_FREE (devsel.devname);      /* -- free device name */
  152.   /* open DirectSound */
  153.   hr = DirectSoundCreate(devsel.devGUID, &dev->direct_sound, NULL);
  154.   if (hr != DS_OK) {
  155.     FLUID_LOG(FLUID_ERR, "Failed to create the DirectSound object");
  156.     goto error_recovery;
  157.   }
  158.   hr = IDirectSound_SetCooperativeLevel(dev->direct_sound, fluid_win32_get_window(), DSSCL_PRIORITY);
  159.   if (hr != DS_OK) {
  160.     FLUID_LOG(FLUID_ERR, "Failed to set the cooperative level");
  161.     goto error_recovery;
  162.   }
  163.   caps.dwSize = sizeof(caps);
  164.   hr = IDirectSound_GetCaps(dev->direct_sound, &caps);
  165.   if (hr != DS_OK)  {
  166.     FLUID_LOG(FLUID_ERR, "Failed to query the device capacities");
  167.     goto error_recovery;
  168.   }
  169.   /* create primary buffer */
  170.   ZeroMemory(&desc, sizeof(DSBUFFERDESC));
  171.   desc.dwSize = sizeof(DSBUFFERDESC);
  172.   desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
  173.   if (caps.dwFreeHwMixingStreamingBuffers > 0) {
  174.     desc.dwFlags |= DSBCAPS_LOCHARDWARE;
  175.   }
  176.   hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->prim_buffer, NULL);
  177.   if (hr != DS_OK) {
  178.     FLUID_LOG(FLUID_ERR, "Failed to allocate the primary buffer");
  179.     goto error_recovery;
  180.   }
  181.   /* set the primary sound buffer to this format. if it fails, just
  182.      print a warning. */
  183.   hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, dev->format);
  184.   if (hr != DS_OK) {
  185.     FLUID_LOG(FLUID_WARN, "Can't set format of primary sound buffer", fluid_win32_error(hr));
  186.   }
  187.   /* initialize the buffer description */
  188.   ZeroMemory(&desc, sizeof(DSBUFFERDESC));
  189.   desc.dwSize = sizeof(DSBUFFERDESC);
  190.   desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
  191.   desc.lpwfxFormat = dev->format;
  192.   desc.dwBufferBytes = dev->queue_byte_size;
  193.   desc.dwReserved = 0;
  194.   if (caps.dwFreeHwMixingStreamingBuffers > 0) {
  195.     desc.dwFlags |= DSBCAPS_LOCHARDWARE;
  196.   }
  197.   /* create the secondary sound buffer */
  198.   hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->sec_buffer, NULL);
  199.   if (hr != DS_OK) {
  200.     FLUID_LOG(FLUID_ERR, "dsound: Can't create sound buffer: %s", fluid_win32_error(hr));
  201.     goto error_recovery;
  202.   }
  203.   /* Lock */
  204.   hr = IDirectSoundBuffer_Lock(dev->sec_buffer, 0, 0, (void*) &buf1, &bytes1, 0, 0, DSBLOCK_ENTIREBUFFER);
  205.   if ((hr != DS_OK) || (buf1 == NULL)) {
  206.     FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. Exiting.");
  207.     goto error_recovery;
  208.   }
  209.   /* fill the buffer with silence */
  210.   memset(buf1, 0, bytes1);
  211.   /* Unlock */
  212.   IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0);
  213.   /* start the audio thread */
  214.   dev->thread = CreateThread(NULL, 0, &fluid_dsound_audio_run, (LPVOID) dev, 0, &dev->threadID);
  215.   if (dev->thread == NULL) {
  216.     goto error_recovery;
  217.   }
  218.   return (fluid_audio_driver_t*) dev;
  219.  error_recovery:
  220.   delete_fluid_dsound_audio_driver((fluid_audio_driver_t*) dev);
  221.   return NULL;
  222. }
  223. int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* d)
  224. {
  225.   fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) d;
  226.   if (dev == NULL) {
  227.     return FLUID_OK;
  228.   }
  229.   /* tell the audio thread to stop its loop */
  230.   dev->cont = 0;
  231.   /* wait till the audio thread exits */
  232.   if (dev->thread != 0) {
  233.     if (WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0) {
  234.       /* on error kill the thread mercilessly */
  235.       FLUID_LOG(FLUID_DBG, "Couldn't join the audio thread. killing it.");
  236.       TerminateThread(dev->thread, 0);
  237.     }
  238.   }
  239.   /* release all the allocated ressources */
  240.   if (dev->format != NULL) {
  241.     FLUID_FREE(dev->format);
  242.   }
  243.   if (dev->sec_buffer != NULL) {
  244.     IDirectSoundBuffer_Stop(dev->sec_buffer);
  245.     IDirectSoundBuffer_Release(dev->sec_buffer);
  246.   }
  247.   if (dev->prim_buffer != NULL) {
  248.     IDirectSoundBuffer_Release(dev->prim_buffer);
  249.   }
  250.   if (dev->direct_sound != NULL) {
  251.     IDirectSound_Release(dev->direct_sound);
  252.   }
  253.   FLUID_FREE(dev);
  254. //  fluid_win32_destroy_window();
  255.   return 0;
  256. }
  257. DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
  258. {
  259.   fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) lpParameter;
  260.   short *buf1, *buf2;
  261.   DWORD bytes1, bytes2;
  262.   DWORD cur_position, frames, play_position, write_position, bytes;
  263.   HRESULT res;
  264.   cur_position = 0;
  265.   /* boost the priority of the audio thread */
  266.   SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  267.   IDirectSoundBuffer_Play(dev->sec_buffer, 0, 0, DSBPLAY_LOOPING);
  268.   while (dev->cont) {
  269.     IDirectSoundBuffer_GetCurrentPosition(dev->sec_buffer, &play_position, &write_position);
  270.     if (cur_position <= play_position) {
  271.       bytes = play_position - cur_position;
  272.     } else if ((play_position < cur_position) && (write_position <= cur_position)) {
  273.       bytes = dev->queue_byte_size + play_position - cur_position;
  274.     } else {
  275.       bytes = 0;
  276.     }
  277.     if (bytes >= dev->buffer_byte_size) {
  278.       /* Lock */
  279.       res = IDirectSoundBuffer_Lock(dev->sec_buffer, cur_position, bytes, (void*) &buf1, &bytes1, (void*) &buf2, &bytes2, 0);
  280.       if ((res != DS_OK) || (buf1 == NULL)) {
  281. FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. System lockup might follow. Exiting.");
  282. ExitProcess(0);
  283.       }
  284.       /* fill the first part of the buffer */
  285.       if (bytes1 > 0) {
  286. frames = bytes1 / dev->frame_size;
  287. dev->write(dev->synth, frames, buf1, 0, 2, buf1, 1, 2);
  288. cur_position += frames * dev->frame_size;
  289.       }
  290.       /* fill the second part of the buffer */
  291.       if ((buf2 != NULL) && (bytes2 > 0)) {
  292. frames = bytes2 / dev->frame_size;
  293. dev->write(dev->synth, frames, buf2, 0, 2, buf2, 1, 2);
  294. cur_position += frames * dev->frame_size;
  295.       }
  296.       /* Unlock */
  297.       IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, buf2, bytes2);
  298.       if (cur_position >= dev->queue_byte_size) {
  299. cur_position -= dev->queue_byte_size;
  300.       }
  301.     } else {
  302.       Sleep(1);
  303.     }
  304.   }
  305.   ExitThread(0);
  306.   return 0; /* never reached */
  307. }
  308. char* fluid_win32_error(HRESULT hr) {
  309.   char *s = "Don't know why";
  310.   switch (hr) {
  311.   case E_NOINTERFACE: s = "No such interface"; break;
  312.   case DSERR_GENERIC: s = "Generic error"; break;
  313.   case DSERR_ALLOCATED: s = "Required resources already allocated"; break;
  314.   case DSERR_BADFORMAT: s = "The format is not supported"; break;
  315.   case DSERR_INVALIDPARAM: s = "Invalid parameter"; break;
  316.   case DSERR_NOAGGREGATION: s = "No aggregation"; break;
  317.   case DSERR_OUTOFMEMORY: s = "Out of memory"; break;
  318.   case DSERR_UNINITIALIZED: s = "Uninitialized"; break;
  319.   case DSERR_UNSUPPORTED: s = "Function not supported"; break;
  320.   }
  321.   return s;
  322. }