DEADSOFTWARE

sound: fix voices in menu
[flatwaifu.git] / src / sdlmixer / sound.c
index 026e01fec9486d79a499196d596c412ccc2e618a..93c1b6350f3cd9ae3935a1c4dde5071fcf2391e8 100644 (file)
 #define TAG_MIX1 0x4d495831
 
 #pragma pack(1)
-struct dmi {
+typedef struct dmi {
   Uint32 len;    // length [bytes]
   Uint32 rate;   // freq [Hz]
   Uint32 lstart; // loop start offset [bytes]
   Uint32 llen;   // loop length [bytes]
   Uint8 data[];  // sound data
-};
+} dmi;
 #pragma pack()
 
-struct sound {
+typedef struct sdlmixer_snd {
   snd_t base;
   Mix_Chunk *c;
-};
+} sdlmixer_snd;
 
 short snd_vol; // public 0..128
 
@@ -75,7 +75,7 @@ void S_updatemusic (void) {
 
 }
 
-/* sound */
+/* Sound */
 
 void S_init (void) {
   assert(devinit == 0);
@@ -86,16 +86,17 @@ void S_init (void) {
       devinit = 1;
     } else {
       logo("S_init: Mix_OpenAudio: %s\n", Mix_GetError());
+      SDL_QuitSubSystem(SDL_INIT_AUDIO);
     }
   } else {
     logo("S_init: SDL_InitSubSytem: %s\n", SDL_GetError());
   }
 }
 
-static Mix_Chunk *convert_this (int rate, const Uint8 *buf, int len) {
+static Mix_Chunk *convert_this (int rate, int sign, const Uint8 *buf, int len) {
   SDL_AudioCVT cvt;
   Mix_Chunk *c = NULL;
-  if (SDL_BuildAudioCVT(&cvt, AUDIO_S8, 1, rate, devformat, devchannels, devfreq) != -1) {
+  if (SDL_BuildAudioCVT(&cvt, sign ? AUDIO_S8 : AUDIO_U8, 1, rate, devformat, devchannels, devfreq) != -1) {
     int maxlen = len * cvt.len_mult;
     Uint8 *maxbuf = malloc(maxlen);
     if (maxbuf != NULL) {
@@ -120,44 +121,55 @@ static Mix_Chunk *convert_this (int rate, const Uint8 *buf, int len) {
   return c;
 }
 
-// TODO load raw sounds with voices
+static sdlmixer_snd *new_sdlmixer_snd (const void *data, dword len, dword rate, dword lstart, dword llen, int sign) {
+  Mix_Chunk *c = NULL;
+  sdlmixer_snd *snd = NULL;
+  c = convert_this(rate, sign, data, len);
+  if (c != NULL) {
+    snd = malloc(sizeof(sdlmixer_snd));
+    if (snd != NULL) {
+      snd->base.tag = TAG_MIX1;
+      snd->c = c;
+    } else {
+      free(c->abuf);
+      free(c);
+    }
+  }
+  return snd;
+}
 
 snd_t *S_get (int id) {
-  if (!devinit) {
-    return NULL;
-  }
-  struct dmi *snd = M_lock(id);
-  struct sound *res = NULL;
-  if (snd != NULL) {
-    int rlen = F_getreslen(id);
-    if (rlen > 16) {
-      Uint32 len = int2host(snd->len);
-      Uint32 rate = int2host(snd->rate);
-      Uint32 lstart = int2host(snd->lstart);
-      Uint32 llen = int2host(snd->llen);
-      if (len <= rlen - 8 && lstart + llen <= rlen - 16) {
-        Mix_Chunk *c = convert_this(rate, snd->data, len);
-        if (c != NULL) {
-          res = malloc(sizeof(struct sound));
-          if (res != NULL) {
-            res->base.tag = TAG_MIX1;
-            res->c = c;
-            // TODO loops
-          } else {
-            free(c);
-          }
+  void *handle;
+  sdlmixer_snd *snd = NULL;
+  if (devinit != NULL) {
+    handle = M_lock(id);
+    if (handle != NULL) {
+      void *data = handle;
+      dword len = F_getreslen(id);
+      dword rate = 11025;
+      dword lstart = 0;
+      dword llen = 0;
+      int sign = 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;
+          sign = 1;
         }
-      } else {
-        logo("S_get(%i): invalid header {len=%u:rate=%u:lstart=%u:llen=%u:rlen=%i}\n", id, len, rate, lstart, llen, rlen);
       }
-    } else {
-      logo("S_load(%i): too short\n", id);
+      snd = new_sdlmixer_snd(data, len, rate, lstart, llen, sign);
+      M_unlock(handle);
     }
-    M_unlock(snd);
-  } else {
-    logo("S_load(%i): not found\n", id);
   }
-  return (snd_t*)res;
+  return (snd_t*)snd;
 }
 
 snd_t *S_load (const char name[8]) {
@@ -165,29 +177,43 @@ snd_t *S_load (const char name[8]) {
   return S_get(id);
 }
 
+void S_free (snd_t *s) {
+  int i;
+  sdlmixer_snd *snd = (sdlmixer_snd*)s;
+  if (snd != NULL) {
+    assert(snd->base.tag == TAG_MIX1);
+    if (devinit) {
+      for (i = 0; i < devchunkchannels; i++) {
+        if (Mix_GetChunk(i) == snd->c) {
+          Mix_HaltChannel(i);
+        }
+      }
+    }
+    free(snd->c->abuf);
+    free(snd->c);
+    free(snd);
+  }
+}
+
 short S_play (snd_t *s, short c, short v) {
+  short channel = 0;
+  sdlmixer_snd *snd = (sdlmixer_snd*)s;
   assert(c >= 0 && c <= 8);
   assert(v >= 0 && v <= 255);
-  short channel = 0;
-  if (devinit) {
-    if (s) {
-      struct sound *snd = (struct sound *)s;
-      assert(snd->base.tag == TAG_MIX1);
-      // TODO care about global volume level
-      snd->c->volume = v * MIX_MAX_VOLUME / 255;
-      channel = Mix_PlayChannel(c <= 0 ? -1 : c - 1, snd->c, 0);
-      channel = channel == -1 ? 0 : channel + 1;
-    }
+  if (devinit && snd != NULL) {
+    assert(snd->base.tag == TAG_MIX1);
+    // TODO care about global volume level
+    snd->c->volume = v * MIX_MAX_VOLUME / 255;
+    channel = Mix_PlayChannel(c <= 0 ? -1 : c - 1, snd->c, 0);
+    channel = channel == -1 ? 0 : channel + 1;
   }
   return channel;
 }
 
 void S_stop (short c) {
   assert(c >= 0 && c <= 8);
-  if (devinit) {
-    if (c > 0) {
-      Mix_HaltChannel(c - 1);
-    }
+  if (devinit && c > 0) {
+    Mix_HaltChannel(c - 1);
   }
 }