/ source / audiolib / src / formats.cpp
formats.cpp
  1  /*
  2   Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
  3   Copyright (C) 2015 EDuke32 developers
  4   Copyright (C) 2015 Voidpoint, LLC
  5  
  6   This program is free software; you can redistribute it and/or
  7   modify it under the terms of the GNU General Public License
  8   as published by the Free Software Foundation; either version 2
  9   of the License, or (at your option) any later version.
 10  
 11   This program is distributed in the hope that it will be useful,
 12   but WITHOUT ANY WARRANTY; without even the implied warranty of
 13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 14  
 15   See the GNU General Public License for more details.
 16  
 17   You should have received a copy of the GNU General Public License
 18   along with this program; if not, write to the Free Software
 19   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 20  
 21   */
 22  
 23  /**
 24   * Raw, WAV, and VOC source support for MultiVoc
 25   */
 26  
 27  #include "_multivc.h"
 28  #include "compat.h"
 29  #include "multivoc.h"
 30  #include "pitch.h"
 31  #include "pragmas.h"
 32  
 33  static playbackstatus MV_GetNextWAVBlock(VoiceNode *voice)
 34  {
 35      if (voice->BlockLength == 0)
 36      {
 37          if (voice->Loop.Start == nullptr)
 38              return NoMoreData;
 39  
 40          voice->BlockLength = voice->Loop.Size;
 41          voice->NextBlock   = voice->Loop.Start;
 42          voice->length      = 0;
 43          voice->position    = 0;
 44      }
 45  
 46      voice->sound        = voice->NextBlock;
 47      voice->position    -= voice->length;
 48      voice->length       = min(voice->BlockLength, 0x8000u);
 49      voice->NextBlock   += voice->length * ((voice->channels * voice->bits) >> 3);
 50      voice->BlockLength -= voice->length;
 51      voice->length     <<= 16;
 52  
 53      return KeepPlaying;
 54  }
 55  
 56  static playbackstatus MV_GetNextVOCBlock(VoiceNode *voice)
 57  {
 58      size_t   blocklength = 0;
 59      uint32_t samplespeed = 0;  // XXX: compiler-happy on synthesis
 60      uint32_t tc          = 0;
 61      unsigned BitsPerSample;
 62      unsigned Channels;
 63      unsigned Format;
 64  
 65      if (voice->BlockLength > 0)
 66      {
 67          voice->position    -= voice->length;
 68          voice->sound       += (voice->length >> 16) * ((voice->channels * voice->bits) >> 3);
 69          voice->length       = min(voice->BlockLength, 0x8000u);
 70          voice->BlockLength -= voice->length;
 71          voice->length     <<= 16;
 72          return KeepPlaying;
 73      }
 74  
 75      auto ptr = (uint8_t const *)voice->NextBlock;
 76  
 77      voice->Paused = FALSE;
 78  
 79      int voicemode = 0;
 80      int blocktype = 0;
 81      int lastblocktype = 0;
 82      int packtype = 0;
 83  
 84      int done = FALSE;
 85  
 86      do
 87      {
 88          // Stop playing if we get a null pointer
 89          if (ptr == nullptr)
 90          {
 91              done = 2;
 92              break;
 93          }
 94  
 95          // terminator is not mandatory according to
 96          // http://wiki.multimedia.cx/index.php?title=Creative_Voice
 97  
 98          if ((uint32_t)(ptr - (uint8_t *)voice->rawdataptr) >= voice->rawdatasiz)
 99              blocktype = 0;  // fake a terminator
100          else
101              blocktype = *ptr;
102  
103          if (blocktype != 0)
104              blocklength = ptr[1]|(ptr[2]<<8)|(ptr[3]<<16);
105          else
106              blocklength = 0;
107          // would need one byte pad at end of alloc'd region:
108  //        blocklength = B_LITTLE32(*(uint32_t *)(ptr + 1)) & 0x00ffffff;
109  
110          ptr += 4;
111  
112          switch (blocktype)
113          {
114          case 0 :
115  end_of_data:
116              // End of data
117              if ((voice->Loop.Start == nullptr) ||
118                      ((intptr_t) voice->Loop.Start >= ((intptr_t) ptr - 4)))
119              {
120                  done = 2;
121              }
122              else
123              {
124                  voice->NextBlock    = voice->Loop.Start;
125                  voice->BlockLength  = 0;
126                  voice->position     = 0;
127                  return MV_GetNextVOCBlock(voice);
128              }
129              break;
130  
131          case 1 :
132              // Sound data block
133              voice->bits  = 8;
134              voice->channels = voicemode + 1;
135              if (lastblocktype != 8)
136              {
137                  tc = (uint32_t)*ptr << 8;
138                  packtype = *(ptr + 1);
139              }
140  
141              ptr += 2;
142              blocklength -= 2;
143  
144              samplespeed = 256000000L / (voice->channels * (65536 - tc));
145  
146              // Skip packed or stereo data
147              if ((packtype != 0) || (voicemode != 0 && voicemode != 1))
148                  ptr += blocklength;
149              else
150                  done = TRUE;
151  
152              if ((uint32_t)(ptr - (uint8_t *)voice->rawdataptr) >= voice->rawdatasiz)
153                  goto end_of_data;
154  
155              voicemode = 0;
156              break;
157  
158          case 2 :
159              // Sound continuation block
160              samplespeed = voice->SamplingRate;
161              done = TRUE;
162              break;
163  
164          case 3 :
165              // Silence
166          case 4 :
167              // Marker
168          case 5 :
169              // ASCII string
170              // All not implemented.
171              ptr += blocklength;
172              break;
173  
174          case 6 :
175              // Repeat begin
176              if (voice->Loop.End == nullptr)
177              {
178                  voice->Loop.Count = B_LITTLE16(*(uint16_t const *)ptr);
179                  voice->Loop.Start = (char *)((intptr_t) ptr + blocklength);
180              }
181              ptr += blocklength;
182              break;
183  
184          case 7 :
185              // Repeat end
186              ptr += blocklength;
187              if (lastblocktype == 6)
188                  voice->Loop.Count = 0;
189              else
190              {
191                  if ((voice->Loop.Count > 0) && (voice->Loop.Start != nullptr))
192                  {
193                      ptr = (uint8_t const *) voice->Loop.Start;
194  
195                      if (voice->Loop.Count < 0xffff)
196                      {
197                          if (--voice->Loop.Count == 0)
198                              voice->Loop.Start = nullptr;
199                      }
200                  }
201              }
202              break;
203  
204          case 8 :
205              // Extended block
206              voice->bits  = 8;
207              voice->channels = 1;
208              tc = B_LITTLE16(*(uint16_t const *)ptr);
209              packtype = *(ptr + 2);
210              voicemode = *(ptr + 3);
211              ptr += blocklength;
212              break;
213  
214          case 9 :
215              // New sound data block
216              samplespeed = B_LITTLE32(*(uint32_t const *)ptr);
217              BitsPerSample = (unsigned)*(ptr + 4);
218              Channels = (unsigned)*(ptr + 5);
219              Format = (unsigned)B_LITTLE16(*(uint16_t const *)(ptr + 6));
220  
221              if ((BitsPerSample == 8) && (Channels == 1 || Channels == 2) && (Format == VOC_8BIT))
222              {
223                  ptr         += 12;
224                  blocklength -= 12;
225                  voice->bits  = 8;
226                  voice->channels = Channels;
227                  done         = TRUE;
228              }
229              else if ((BitsPerSample == 16) && (Channels == 1 || Channels == 2) && (Format == VOC_16BIT))
230              {
231                  ptr         += 12;
232                  blocklength -= 12;
233                  voice->bits  = 16;
234                  voice->channels = Channels;
235                  done         = TRUE;
236              }
237              else
238              {
239                  ptr += blocklength;
240              }
241  
242              // CAUTION:
243              //  SNAKRM.VOC is corrupt!  blocklength gets us beyond the
244              //  end of the file.
245              if ((uint32_t)(ptr - (uint8_t *)voice->rawdataptr) >= voice->rawdatasiz)
246                  goto end_of_data;
247  
248              break;
249  
250          default :
251              // Unknown data.  Probably not a VOC file.
252              done = 2;
253              break;
254          }
255  
256          lastblocktype = blocktype;
257      }
258      while (!done);
259  
260      if (done != 2)
261      {
262          voice->NextBlock    = (char const *)ptr + blocklength;
263          voice->sound        = (char const *)ptr;
264  
265          // CODEDUP multivoc.c MV_SetVoicePitch
266          voice->SamplingRate = samplespeed;
267          voice->RateScale    = divideu64((uint64_t)voice->SamplingRate * voice->PitchScale, MV_MixRate);
268  
269          // Multiply by MV_MIXBUFFERSIZE - 1
270          voice->FixedPointBufferSize = (voice->RateScale * MV_MIXBUFFERSIZE) -
271                                        voice->RateScale;
272  
273          if (voice->Loop.End != nullptr)
274          {
275              if (blocklength > (uintptr_t)voice->Loop.End)
276                  blocklength = (uintptr_t)voice->Loop.End;
277              else
278                  voice->Loop.End = (char *)blocklength;
279  
280              voice->Loop.Start = voice->sound + (uintptr_t)voice->Loop.Start;
281              voice->Loop.End   = voice->sound + (uintptr_t)voice->Loop.End;
282              voice->Loop.Size  = voice->Loop.End - voice->Loop.Start;
283          }
284  
285          if (voice->bits == 16)
286              blocklength /= 2;
287  
288          if (voice->channels == 2)
289              blocklength /= 2;
290  
291          voice->position     = 0;
292          voice->length       = min<uint32_t>(blocklength, 0x8000u);
293          voice->BlockLength  = blocklength - voice->length;
294          voice->length     <<= 16;
295  
296          MV_SetVoiceMixMode(voice);
297  
298          return KeepPlaying;
299      }
300  
301      return NoMoreData;
302  }
303  
304  static playbackstatus MV_GetNextRAWBlock(VoiceNode *voice)
305  {
306      if (voice->BlockLength == 0)
307      {
308          if (voice->Loop.Start == NULL)
309              return NoMoreData;
310  
311          voice->BlockLength = voice->Loop.Size;
312          voice->NextBlock   = voice->Loop.Start;
313          voice->length      = 0;
314          voice->position    = 0;
315      }
316  
317      voice->sound        = voice->NextBlock;
318      voice->position    -= voice->length;
319      voice->length       = min(voice->BlockLength, 0x8000u);
320      voice->NextBlock   += voice->length * (voice->channels * voice->bits / 8);
321      voice->BlockLength -= voice->length;
322      voice->length     <<= 16;
323  
324      return KeepPlaying;
325  }
326  
327  int MV_PlayWAV3D(char *ptr, uint32_t length, int loophow, int pitchoffset, int angle, int distance,
328                       int priority, fix16_t volume, intptr_t callbackval)
329  {
330      if (!MV_Installed)
331          return MV_Error;
332  
333      if (distance < 0)
334      {
335          distance  = -distance;
336          angle    += MV_NUMPANPOSITIONS / 2;
337      }
338  
339      int const vol = MIX_VOLUME(distance);
340  
341      // Ensure angle is within 0 - 127
342      angle &= MV_MAXPANPOSITION;
343  
344      return MV_PlayWAV(ptr, length, loophow, -1, pitchoffset, max(0, 255 - distance),
345          MV_PanTable[angle][vol].left, MV_PanTable[angle][vol].right, priority, volume, callbackval);
346  }
347  
348  int MV_PlayWAV(char *ptr, uint32_t length, int loopstart, int loopend, int pitchoffset, int vol,
349                     int left, int right, int priority, fix16_t volume, intptr_t callbackval)
350  {
351      if (!MV_Installed)
352          return MV_Error;
353  
354      riff_header   riff;
355      memcpy(&riff, ptr, sizeof(riff_header));
356      riff.file_size   = B_LITTLE32(riff.file_size);
357      riff.format_size = B_LITTLE32(riff.format_size);
358  
359      if ((memcmp(riff.RIFF, "RIFF", 4) != 0) || (memcmp(riff.WAVE, "WAVE", 4) != 0) || (memcmp(riff.fmt, "fmt ", 4) != 0))
360          return MV_SetErrorCode(MV_InvalidFile);
361  
362      format_header format;
363      memcpy(&format, ptr + sizeof(riff_header), sizeof(format_header));
364      format.wFormatTag      = B_LITTLE16(format.wFormatTag);
365      format.nChannels       = B_LITTLE16(format.nChannels);
366      format.nSamplesPerSec  = B_LITTLE32(format.nSamplesPerSec);
367      format.nAvgBytesPerSec = B_LITTLE32(format.nAvgBytesPerSec);
368      format.nBlockAlign     = B_LITTLE16(format.nBlockAlign);
369      format.nBitsPerSample  = B_LITTLE16(format.nBitsPerSample);
370  
371      data_header   data;
372      memcpy(&data, ptr + sizeof(riff_header) + riff.format_size, sizeof(data_header));
373      data.size = B_LITTLE32(data.size);
374  
375      // Check if it's PCM data.
376      if (format.wFormatTag != 1 || (format.nChannels != 1 && format.nChannels != 2) ||
377          ((format.nBitsPerSample != 8) && (format.nBitsPerSample != 16)) || memcmp(data.DATA, "data", 4) != 0)
378          return MV_SetErrorCode(MV_InvalidFile);
379  
380      // Request a voice from the voice pool
381  
382      auto voice = MV_AllocVoice(priority);
383  
384      if (voice == nullptr)
385          return MV_SetErrorCode(MV_NoVoices);
386  
387      voice->wavetype    = FMT_WAV;
388      voice->bits        = format.nBitsPerSample;
389      voice->channels    = format.nChannels;
390      voice->GetSound    = MV_GetNextWAVBlock;
391  
392      int blocklen = data.size;
393  
394      if (voice->bits == 16)
395      {
396          data.size  &= ~1;
397          blocklen     /= 2;
398      }
399  
400      if (voice->channels == 2)
401      {
402          data.size &= ~1;
403          blocklen    /= 2;
404      }
405  
406      voice->rawdataptr   = (uint8_t *)ptr;
407      voice->rawdatasiz   = length;
408      voice->position     = 0;
409      voice->BlockLength  = blocklen;
410      voice->NextBlock    = (char *)((intptr_t)ptr + sizeof(riff_header) + riff.format_size + sizeof(data_header));
411      voice->priority     = priority;
412      voice->callbackval  = callbackval;
413      voice->Loop.Start   = loopstart >= 0 ? voice->NextBlock : nullptr;
414      voice->Loop.End     = nullptr;
415      voice->Loop.Count   = 0;
416      voice->Loop.Size    = loopend > 0 ? loopend - loopstart + 1 : blocklen;
417  
418      MV_SetVoicePitch(voice, format.nSamplesPerSec, pitchoffset);
419      MV_SetVoiceVolume(voice, vol, left, right, volume);
420      MV_PlayVoice(voice);
421  
422      return voice->handle;
423  }
424  
425  int MV_PlayVOC3D(char *ptr, uint32_t length, int loophow, int pitchoffset, int angle,
426                       int distance, int priority, fix16_t volume, intptr_t callbackval)
427  {
428      if (!MV_Installed)
429          return MV_Error;
430  
431      if (distance < 0)
432      {
433          distance  = -distance;
434          angle    += MV_NUMPANPOSITIONS / 2;
435      }
436  
437      int const vol = MIX_VOLUME(distance);
438  
439      // Ensure angle is within 0 - 127
440      angle &= MV_MAXPANPOSITION;
441  
442      return MV_PlayVOC(ptr, length, loophow, -1, pitchoffset, max(0, 255 - distance),
443          MV_PanTable[angle][vol].left, MV_PanTable[angle][vol].right, priority, volume, callbackval);
444  }
445  
446  int MV_PlayVOC(char *ptr, uint32_t length, int loopstart, int loopend, int pitchoffset, int vol,
447                     int left, int right, int priority, fix16_t volume, intptr_t callbackval)
448  {
449      if (!MV_Installed)
450          return MV_Error;
451  
452      // Make sure it looks like a valid VOC file.
453      if (memcmp(ptr, "Creative Voice File", 19) != 0)
454          return MV_SetErrorCode(MV_InvalidFile);
455  
456      // Request a voice from the voice pool
457      auto voice = MV_AllocVoice(priority);
458  
459      if (voice == nullptr)
460          return MV_SetErrorCode(MV_NoVoices);
461  
462      voice->rawdataptr  = (uint8_t *)ptr;
463      voice->rawdatasiz  = length;
464      voice->wavetype    = FMT_VOC;
465      voice->bits        = 8;
466      voice->channels    = 1;
467      voice->GetSound    = MV_GetNextVOCBlock;
468      voice->NextBlock   = ptr + B_LITTLE16(*(uint16_t *)(ptr + 0x14));
469      voice->PitchScale  = PITCH_GetScale(pitchoffset);
470      voice->priority    = priority;
471      voice->callbackval = callbackval;
472      voice->Loop        = { loopstart >= 0 ? voice->NextBlock : nullptr, nullptr, 0, (uint32_t)(loopend - loopstart + 1) };
473  
474      MV_SetVoiceVolume(voice, vol, left, right, volume);
475      MV_PlayVoice(voice);
476  
477      return voice->handle;
478  }
479  
480  int MV_PlayRAW(char *ptr, uint32_t length, int rate, char *loopstart, char *loopend, int pitchoffset, int vol,
481                     int left, int right, int priority, fix16_t volume, intptr_t callbackval)
482  {
483      if (!MV_Installed)
484          return MV_Error;
485  
486      // Request a voice from the voice pool
487      auto voice = MV_AllocVoice(priority);
488  
489      if (voice == nullptr)
490          return MV_SetErrorCode(MV_NoVoices);
491  
492      voice->rawdataptr  = (uint8_t *)ptr;
493      voice->rawdatasiz  = length;
494      voice->wavetype    = FMT_RAW;
495      voice->bits        = 8;
496      voice->channels    = 1;
497      voice->GetSound    = MV_GetNextRAWBlock;
498      voice->NextBlock   = ptr;
499      voice->position    = 0;
500      voice->BlockLength = length;
501      voice->PitchScale  = PITCH_GetScale(pitchoffset);
502      voice->priority    = priority;
503      voice->callbackval = callbackval;
504      voice->Loop        = { loopstart, loopend, 0, (uint32_t)(loopend - loopstart + 1) };
505      voice->volume      = volume;
506  
507      MV_SetVoicePitch(voice, rate, pitchoffset);
508      MV_SetVoiceVolume(voice, vol, left, right, volume);
509      MV_PlayVoice(voice);
510  
511      return voice->handle;
512  }