DEADSOFTWARE

portability: avoid errors on some compilers
[flatwaifu.git] / src / openal / sound.c
index af75aa381eeef080d04619e3f3a3cd4d2ce0888a..bcea104d3e4167b94fae261ad3c5e822473681af 100644 (file)
@@ -1,14 +1,35 @@
+/* Copyright (C) 2020 SovietPony
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License ONLY.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
 #include "sound.h"
 #include "music.h"
 
 #include "files.h" // F_findres F_getreslen
 #include "memory.h" // M_lock M_unlock
-#include "misc.h" // int2host
 #include "error.h" // logo
 
-#include <OpenAL/al.h>
-#include <OpenAL/alc.h>
-#include <SDL.h> // SDL_BuildAudioCVT SDL_ConvertAudio
+#include "common/endianness.h"
+
+#ifdef __APPLE__
+#  include <OpenAL/al.h>
+#  include <OpenAL/alc.h>
+#else
+#  include <AL/al.h>
+#  include <AL/alc.h>
+#endif
+
 #include <assert.h>
 #include <stdlib.h> // malloc
 #include <string.h> // memcpy
 #define TAG_OAL1 0x4F414C31
 #define MAX_CHANNELS 8
 
+#define OAL_FORMAT AL_FORMAT_MONO8
+
 #pragma pack(1)
 typedef struct dmi {
-  dword len;
-  dword rate;
-  dword lstart;
-  dword llen;
+  word len;
+  word rate;
+  word lstart;
+  word llen;
   byte data[];
 } dmi;
 #pragma pack()
@@ -35,11 +58,7 @@ typedef struct openal_channel {
   ALuint source;
 } openal_channel;
 
-short snd_vol;
-short mus_vol;
-char music_random;
-int music_time;
-int music_fade;
+static short snd_vol;
 
 static ALCdevice *device;
 static ALCcontext *context;
@@ -47,75 +66,174 @@ static ALuint sources[MAX_CHANNELS];
 
 /* Music */
 
-void S_initmusic (void) {
+const cfg_t *MUS_args (void) {
+  return NULL;
+}
+
+const cfg_t *MUS_conf (void) {
+  return NULL;
+}
+
+const menu_t *MUS_menu (void) {
+  return NULL;
+}
+
+void MUS_init (void) {
 
 }
 
-void S_donemusic (void) {
+void MUS_done (void) {
 
 }
 
-void S_startmusic (int time) {
+void MUS_start (int time) {
 
 }
 
-void S_stopmusic (void) {
+void MUS_stop (void) {
 
 }
 
-void S_volumemusic (int v) {
+void MUS_volume (int v) {
 
 }
 
-void F_loadmus (char n[8]) {
+void MUS_load (char n[8]) {
 
 }
 
-void F_freemus (void) {
+void MUS_free (void) {
 
 }
 
-void S_updatemusic (void) {
+void MUS_update (void) {
 
 }
 
 /* Sound */
 
-static void convert_this_ext (Uint32 src_format, int src_chan, int src_rate, Uint32 dst_format, int dst_chan, int dst_rate, const void *buf, int len, void **maxbuf, int *maxlen) {
-  SDL_AudioCVT cvt;
-  *maxlen = 0;
-  *maxbuf = NULL;
-  if (SDL_BuildAudioCVT(&cvt, src_format, src_chan, src_rate, dst_format, dst_chan, dst_rate) != -1) {
-    *maxlen = len * cvt.len_mult;
-    *maxbuf = malloc(*maxlen);
-    if (*maxbuf != NULL) {
-      memcpy(*maxbuf, buf, len);
-      cvt.buf = *maxbuf;
-      cvt.len = len;
-      if (SDL_ConvertAudio(&cvt) == 0) {
-        *maxlen = len * cvt.len_ratio;
-      } else {
-        free(*maxbuf);
-        *maxbuf = NULL;
-        *maxlen = 0;
-      }
+static int sound_menu_handler (menu_msg_t *msg, const menu_t *m, int i) {
+  static int cur;
+  enum { VOLUME, __NUM__ };
+  static const simple_menu_t sm = {
+    GM_BIG, "Sound", NULL,
+    {
+      { "Volume", NULL },
+    }
+  };
+  if (i == VOLUME) {
+    switch (msg->type) {
+      case GM_GETENTRY: return GM_init_int0(msg, GM_SCROLLER, 0, 0, 0);
+      case GM_GETINT: return GM_init_int(msg, snd_vol, 0, 128, 8);
+      case GM_SETINT: S_volume(msg->integer.i); return 1;
     }
   }
+  return simple_menu_handler(msg, i, __NUM__, &sm, &cur);
+}
+
+const menu_t *S_menu (void) {
+  static const menu_t m = { &sound_menu_handler };
+  return &m;
+}
+
+const cfg_t *S_args (void) {
+  static const cfg_t args[] = {
+    { "sndvol", &snd_vol, Y_WORD },
+    { NULL, NULL, 0 }
+  };
+  return args;
+}
+
+const cfg_t *S_conf (void) {
+  static const cfg_t conf[] = {
+    { "sound_volume", &snd_vol, Y_WORD },
+    { NULL, NULL, 0 }
+  };
+  return conf;
+}
+
+// Convert mono signed/unsigned 8bit sound to openal format
+//   AL_FORMAT_MONO8    -> mono unsigned 8bit
+//   AL_FORMAT_STEREO8  -> stereo unsigned 8bit
+//   AL_FORMAT_MONO16   -> mono signed 16bit
+//   AL_FORMAT_STEREO16 -> stereo signed 16bit
+static void cvt_this (int sign, ALint format, const void *data, size_t len, void **outdata, size_t *outlen) {
+  *outdata = NULL;
+  *outlen = 0;
+  switch (format) {
+    case AL_FORMAT_MONO8:
+      {
+        int8_t *y = malloc(len);
+        if (y != NULL) {
+          memcpy(y, data, len);
+          if (sign) {
+            for (size_t i = 0; i < len; i++) {
+              y[i] ^= 0x80;
+            }
+          }
+          *outdata = y;
+          *outlen = len;
+        }
+      }
+      break;
+    case AL_FORMAT_MONO16:
+      {
+        int16_t *y = malloc(2 * len);
+        if (y != NULL) {
+          for (size_t i = 0; i < len; ++i) {
+            int32_t c = ((int8_t*)data)[i] ^ (sign ? 0x00 : 0x80);
+            y[i] = c >= 0 ? c * 32767 / 127 : c * -32767 / -128;
+          }
+        }
+        *outdata = y;
+        *outlen = 2 * len;
+      }
+      break;
+    case AL_FORMAT_STEREO8:
+      {
+        int8_t *y = malloc(2 * len);
+        if (y != NULL) {
+          int8_t *yy = y;
+          for (size_t i = 0; i < len; i++) {
+            int8_t xx = ((int8_t*)data)[i] ^ (sign ? 0x80 : 0x00);
+            *yy = xx; yy++;
+            *yy = xx; yy++;
+          }
+          *outdata = y;
+          *outlen = 2 * len;
+        }
+      }
+      break;
+    case AL_FORMAT_STEREO16:
+      {
+        int16_t *y = malloc(2 * 2 * len);
+        if (y != NULL) {
+          int16_t *yy = y;
+          for (size_t i = 0; i < len; ++i) {
+            int32_t x = ((int8_t*)data)[i] ^ (sign ? 0x00 : 0x80);
+            int16_t xx = x >= 0 ? x * 32767 / 127 : x * -32767 / -128;
+            *yy = xx; yy++;
+            *yy = xx; yy++;
+          }
+        }
+        *outdata = y;
+        *outlen = 2 * 2* len;
+      }
+      break;
+  }
 }
 
-static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen) {
+static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
   assert(data);
   ALuint buffer = 0;
   openal_snd *snd = NULL;
   void *newdata = NULL;
-  int newlen = 0;
-  // for some reason 8bit formats makes psshshshsh
-  // TODO do this without SDL
-  convert_this_ext(AUDIO_S8, 1, rate, AUDIO_S16SYS, 1, rate, data, len, &newdata, &newlen);
+  size_t newlen = 0;
+  cvt_this(sign, OAL_FORMAT, data, len, &newdata, &newlen);
   if (newdata != NULL) {
     alGenBuffers(1, &buffer);
     if (alGetError() == AL_NO_ERROR) {
-      alBufferData(buffer, AL_FORMAT_MONO16, newdata, newlen, rate);
+      alBufferData(buffer, OAL_FORMAT, newdata, newlen, rate);
       if (alGetError() == AL_NO_ERROR) {
         snd = malloc(sizeof(openal_snd));
         if (snd != NULL) {
@@ -135,30 +253,35 @@ static openal_snd *new_openal_snd (const void *data, dword len, dword rate, dwor
 }
 
 snd_t *S_get (int id) {
+  void *handle;
   openal_snd *snd = NULL;
-  void *handle = M_lock(id);
-  if (context != NULL && handle != NULL) {
-    byte *data = handle;
-    dword len = F_getreslen(id);
-    dword rate = 8000;
-    dword lstart = 0;
-    dword llen = 0;
-    if (len > 16) {
-      dmi *hdr = handle;
-      dword hdr_len = int2host(hdr->len);
-      dword hdr_rate = int2host(hdr->rate);
-      dword hdr_lstart = int2host(hdr->lstart);
-      dword hdr_llen = int2host(hdr->llen);
-      if (hdr_len <= len - 8 && hdr_lstart + hdr_llen <= len - 16) {
-        data = hdr->data;
-        len = hdr_len;
-        rate = hdr_rate;
-        lstart = hdr_lstart;
-        llen = hdr_llen;
+  if (context != NULL) {
+    handle = M_lock(id);
+    if (handle != NULL) {
+      void *data = handle;
+      word len = F_getreslen(id);
+      word rate = 11025;
+      word lstart = 0;
+      word llen = 0;
+      int sign = 0;
+      if (len > 8) {
+        dmi *hdr = handle;
+        word hdr_len = short2host(hdr->len);
+        word hdr_rate = short2host(hdr->rate);
+        word hdr_lstart = short2host(hdr->lstart);
+        word hdr_llen = short2host(hdr->llen);
+        if (hdr_len <= len - 4 && hdr_lstart + hdr_llen <= len - 8) {
+          data = hdr->data;
+          len = hdr_len;
+          rate = hdr_rate;
+          lstart = hdr_lstart;
+          llen = hdr_llen;
+          sign = 1;
+        }
       }
+      snd = new_openal_snd(data, len, rate, lstart, llen, sign);
+      M_unlock(handle);
     }
-    snd = new_openal_snd(data, len, rate, lstart, llen);
-    M_unlock(handle);
   }
   return (snd_t*)snd;
 }
@@ -167,6 +290,28 @@ snd_t *S_load (const char name[8]) {
   return S_get(F_findres(name));
 }
 
+void S_free (snd_t *s) {
+  int i;
+  ALint h;
+  openal_snd *snd = (openal_snd*)s;
+  if (snd != NULL) {
+    assert(snd->base.tag == TAG_OAL1);
+    if (context != NULL) {
+      for (i = 0; i < MAX_CHANNELS; i++) {
+        alGetSourcei(sources[i], AL_BUFFER, &h);
+        if (h == snd->buffer) {
+          alSourceStop(sources[i]);
+          alSourcei(sources[i], AL_BUFFER, 0);
+        }
+      }
+      alDeleteBuffers(1, &snd->buffer);
+      assert(alGetError() == AL_NO_ERROR);
+    }
+    snd->base.tag = 0;
+    free(s);
+  }
+}
+
 void S_init (void) {
   assert(device == NULL && context == NULL);
   const ALCint attrs[] = {ALC_MONO_SOURCES, MAX_CHANNELS, 0};