/ source / audiolib / src / flac.cpp
flac.cpp
  1  /*
  2   Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
  3  
  4   This program is free software; you can redistribute it and/or
  5   modify it under the terms of the GNU General Public License
  6   as published by the Free Software Foundation; either version 2
  7   of the License, or (at your option) any later version.
  8  
  9   This program is distributed in the hope that it will be useful,
 10   but WITHOUT ANY WARRANTY; without even the implied warranty of
 11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 12  
 13   See the GNU General Public License for more details.
 14  
 15   You should have received a copy of the GNU General Public License
 16   along with this program; if not, write to the Free Software
 17   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 18  
 19   */
 20  
 21  /**
 22   * FLAC source support for MultiVoc
 23   */
 24  
 25  #include "compat.h"
 26  
 27  #ifdef HAVE_FLAC
 28  
 29  #define FLAC__NO_DLL
 30  
 31  #if defined(__APPLE__) || defined(__linux__)
 32  #include <FLAC/all.h>
 33  #else
 34  #include "FLAC/all.h"
 35  #endif
 36  
 37  #include "_multivc.h"
 38  #include "multivoc.h"
 39  #include "pitch.h"
 40  #include "pragmas.h"
 41  
 42  typedef struct
 43  {
 44      void *ptr;
 45      size_t length;
 46      size_t pos;
 47  
 48      FLAC__StreamDecoder *stream;
 49      FLAC__uint64 sample_pos;
 50  
 51      char *block;
 52      size_t blocksize;
 53  
 54      VoiceNode *owner;
 55  } flac_data;
 56  
 57  // callbacks, round 1
 58  
 59  static size_t read_flac(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle datasource)
 60  {
 61      flac_data *flac = (flac_data *)datasource;
 62      size_t nread = 0;
 63      size_t bytes;
 64  
 65      errno = 0;
 66  
 67      if (flac->length == flac->pos)
 68      {
 69          return 0;
 70      }
 71  
 72      for (; nmemb > 0; nmemb--, nread++)
 73      {
 74          bytes = flac->length - flac->pos;
 75          if (size < bytes)
 76          {
 77              bytes = size;
 78          }
 79  
 80          memcpy(ptr, (uint8_t *)flac->ptr + flac->pos, bytes);
 81          flac->pos += bytes;
 82          ptr = (uint8_t *)ptr + bytes;
 83  
 84          if (flac->length == flac->pos)
 85          {
 86              nread++;
 87              break;
 88          }
 89      }
 90  
 91      return nread;
 92  }
 93  
 94  static size_t write_flac(const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle datasource)
 95  {
 96      UNREFERENCED_PARAMETER(ptr);
 97      UNREFERENCED_PARAMETER(size);
 98      UNREFERENCED_PARAMETER(nmemb);
 99      UNREFERENCED_PARAMETER(datasource);
100  
101      return 0;
102  }
103  
104  static int seek_flac(FLAC__IOHandle datasource, FLAC__int64 offset, int whence)
105  {
106      flac_data *flac = (flac_data *)datasource;
107  
108      switch (whence)
109      {
110          case SEEK_SET: flac->pos = 0; break;
111          case SEEK_CUR: break;
112          case SEEK_END: flac->pos = flac->length; break;
113      }
114  
115      flac->pos += offset;
116  
117      if (flac->pos > flac->length)
118      {
119          flac->pos = flac->length;
120      }
121  
122      return 0;
123  }
124  
125  static FLAC__int64 tell_flac(FLAC__IOHandle datasource)
126  {
127      flac_data *flac = (flac_data *)datasource;
128  
129      return flac->pos;
130  }
131  
132  static FLAC__int64 length_flac(FLAC__IOHandle datasource)
133  {
134      flac_data *flac = (flac_data *)datasource;
135  
136      return flac->length;
137  }
138  
139  static int eof_flac(FLAC__IOHandle datasource)
140  {
141      flac_data *flac = (flac_data *)datasource;
142  
143      return (flac->pos == flac->length);
144  }
145  
146  static int close_flac(FLAC__IOHandle datasource)
147  {
148      UNREFERENCED_PARAMETER(datasource);
149      return 0;
150  }
151  
152  static FLAC__IOCallbacks flac_callbacks = {
153      read_flac, write_flac, seek_flac, tell_flac, eof_flac, close_flac,
154  };
155  
156  
157  // callbacks, round 2
158  
159  FLAC__StreamDecoderReadStatus read_flac_stream(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes,
160                                                 void *client_data)
161  {
162      UNREFERENCED_PARAMETER(decoder);
163      if (*bytes > 0)
164      {
165          *bytes = read_flac(buffer, sizeof(FLAC__byte), *bytes, client_data);
166          if (errno)
167              return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
168          else if (*bytes == 0)
169              return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
170          else
171              return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
172      }
173      else
174          return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
175  }
176  
177  FLAC__StreamDecoderSeekStatus seek_flac_stream(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset,
178                                                 void *client_data)
179  {
180      UNREFERENCED_PARAMETER(decoder);
181      if (seek_flac(client_data, absolute_byte_offset, SEEK_SET) < 0)
182          return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
183      else
184          return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
185  }
186  
187  FLAC__StreamDecoderTellStatus tell_flac_stream(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset,
188                                                 void *client_data)
189  {
190      UNREFERENCED_PARAMETER(decoder);
191      *absolute_byte_offset = tell_flac(client_data);
192      return FLAC__STREAM_DECODER_TELL_STATUS_OK;
193  }
194  
195  FLAC__StreamDecoderLengthStatus length_flac_stream(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length,
196                                                     void *client_data)
197  {
198      UNREFERENCED_PARAMETER(decoder);
199      *stream_length = length_flac(client_data);
200      return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
201  }
202  
203  FLAC__bool eof_flac_stream(const FLAC__StreamDecoder *decoder, void *client_data)
204  {
205      UNREFERENCED_PARAMETER(decoder);
206      return eof_flac(client_data);
207  }
208  
209  FLAC__StreamDecoderWriteStatus write_flac_stream(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
210                                                   const FLAC__int32 *const ibuffer[], void *client_data)
211  {
212      flac_data *fd = (flac_data *)client_data;
213      VoiceNode *voice = fd->owner;
214      FLAC__uint64 samples = frame->header.blocksize;
215  
216      UNREFERENCED_PARAMETER(decoder);
217  
218      voice->channels = frame->header.channels;
219      voice->bits = frame->header.bits_per_sample;
220      voice->SamplingRate = frame->header.sample_rate;
221  
222      if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER)
223          fd->sample_pos = frame->header.number.frame_number;
224      else if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER)
225          fd->sample_pos = frame->header.number.sample_number;
226  
227      if ((FLAC__uint64)(uintptr_t)voice->Loop.End > 0 &&
228          fd->sample_pos + samples >= (FLAC__uint64)(uintptr_t)voice->Loop.End)
229      {
230          samples = (FLAC__uint64)(uintptr_t)voice->Loop.End - fd->sample_pos;
231          if (!FLAC__stream_decoder_seek_absolute(fd->stream, (FLAC__uint64)(uintptr_t)voice->Loop.Start))
232              LOG_F(ERROR, "write_flac_stream: error in FLAC__stream_decoder_seek_absolute (LOOP_START %" PRIu64 ", LOOP_END %" PRIu64 ")",
233                        (FLAC__uint64)(uintptr_t)voice->Loop.Start, (FLAC__uint64)(uintptr_t)voice->Loop.End);
234      }
235  
236      size_t const size = samples * voice->channels * (voice->bits >> 3);
237  
238      voice->position = 0;
239      // CODEDUP multivoc.c MV_SetVoicePitch
240      voice->RateScale = divideu64((uint64_t)voice->SamplingRate * voice->PitchScale, MV_MixRate);
241      voice->FixedPointBufferSize = (voice->RateScale * MV_MIXBUFFERSIZE) - voice->RateScale;
242      MV_SetVoiceMixMode(voice);
243  
244      char * block = fd->block;
245  
246      if (size > fd->blocksize)
247      {
248          block = (char *)Xaligned_alloc(16, size);
249  
250          if (block == nullptr)
251              return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
252      }
253  
254      {
255          char *obuffer = block;
256          FLAC__uint64 sample;
257          uint8_t channel;
258  
259          // this loop is adapted from code in ov_read_filter() in vorbisfile.c in libvorbis
260          for (sample = 0; sample < samples; ++sample)
261              for (channel = 0; channel < frame->header.channels; ++channel)
262              {
263                  int8_t byte;
264                  FLAC__int32 val = ibuffer[channel][sample];
265                  if (val > (1 << (voice->bits - 1)) - 1)
266                      val = (1 << (voice->bits - 1)) - 1;
267                  else if (val < -(1 << (voice->bits - 1)))
268                      val = -(1 << (voice->bits - 1));
269                  for (byte = 0; byte < voice->bits; byte += 8) *obuffer++ = ((val >> byte) & 0x000000FF);
270              }
271      }
272  
273      voice->sound = block;
274      voice->length = samples << 16;
275  
276      if (block != fd->block)
277      {
278          char * oldblock = fd->block;
279          fd->block = block;
280          fd->blocksize = size;
281          ALIGNED_FREE_AND_NULL(oldblock);
282      }
283  
284      return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
285  }
286  
287  void error_flac_stream(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
288  {
289      // flac_data * fd = (flac_data *) client_data;
290      UNREFERENCED_PARAMETER(client_data);
291      UNREFERENCED_PARAMETER(decoder);
292      LOG_F(ERROR, "%s", FLAC__StreamDecoderErrorStatusString[status]);
293      // FLAC__stream_decoder_flush(fd->stream);
294  }
295  
296  int MV_GetFLACPosition(VoiceNode *voice)
297  {
298      FLAC__uint64 position = 0;
299      flac_data *fd = (flac_data *)voice->rawdataptr;
300  
301      FLAC__stream_decoder_get_decode_position(fd->stream, &position);
302  
303      return position;
304  }
305  
306  void MV_SetFLACPosition(VoiceNode *voice, int position)
307  {
308      flac_data *fd = (flac_data *)voice->rawdataptr;
309  
310      FLAC__stream_decoder_seek_absolute(fd->stream, position);
311  }
312  
313  /*---------------------------------------------------------------------
314  Function: MV_GetNextFLACBlock
315  
316  Controls playback of FLAC data
317  ---------------------------------------------------------------------*/
318  
319  static playbackstatus MV_GetNextFLACBlock(VoiceNode *voice)
320  
321  {
322      flac_data *fd = (flac_data *)voice->rawdataptr;
323      FLAC__StreamDecoderState decode_state;
324      // FLAC__bool decode_status;
325  
326      if ((FLAC__uint64)(uintptr_t)voice->Loop.End > 0 && fd->sample_pos >= (FLAC__uint64)(uintptr_t)voice->Loop.End)
327          if (!FLAC__stream_decoder_seek_absolute(fd->stream, (FLAC__uint64)(uintptr_t)voice->Loop.Start))
328              LOG_F(ERROR, "MV_GetNextFLACBlock: error in FLAC__stream_decoder_seek_absolute (LOOP_START %" PRIu64 ", LOOP_END %" PRIu64 ")",
329                        (FLAC__uint64)(uintptr_t)voice->Loop.Start, (FLAC__uint64)(uintptr_t)voice->Loop.End);
330  
331      /*decode_status =*/FLAC__stream_decoder_process_single(fd->stream);
332      decode_state = FLAC__stream_decoder_get_state(fd->stream);
333  
334      /*
335          if (!decode_status)
336          {
337              LOG_F(INFO, "MV_GetNextFLACBlock: %s", FLAC__StreamDecoderStateString[decode_state]);
338              return NoMoreData;
339          }
340      */
341  
342      if (decode_state == FLAC__STREAM_DECODER_SEEK_ERROR)
343      {
344          FLAC__stream_decoder_flush(fd->stream);
345          decode_state = FLAC__stream_decoder_get_state(fd->stream);
346      }
347  
348      if (decode_state == FLAC__STREAM_DECODER_END_OF_STREAM)
349      {
350          if (voice->Loop.Size > 0)
351          {
352              if (!FLAC__stream_decoder_seek_absolute(fd->stream, (FLAC__uint64)(uintptr_t)voice->Loop.Start))
353                  LOG_F(ERROR, "MV_GetNextFLACBlock: error in FLAC__stream_decoder_seek_absolute (LOOP_START %" PRIu64 ")",
354                            (FLAC__uint64)(uintptr_t)voice->Loop.Start);
355          }
356          else
357              return NoMoreData;
358      }
359  
360  #if 0
361      // unnecessary: duplicated in write_flac_stream()
362      voice->channels     = FLAC__stream_decoder_get_channels(fd->stream);
363      voice->bits         = FLAC__stream_decoder_get_bits_per_sample(fd->stream);
364      voice->SamplingRate = FLAC__stream_decoder_get_sample_rate(fd->stream);
365      // CODEDUP multivoc.c MV_SetVoicePitch
366      voice->RateScale    = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate;
367      voice->FixedPointBufferSize = ( voice->RateScale * MV_MIXBUFFERSIZE ) - voice->RateScale;
368      MV_SetVoiceMixMode( voice );
369  #endif
370  
371      return KeepPlaying;
372  }
373  
374  
375  /*---------------------------------------------------------------------
376  Function: MV_PlayFLAC3D
377  
378  Begin playback of sound data at specified angle and distance
379  from listener.
380  ---------------------------------------------------------------------*/
381  
382  int MV_PlayFLAC3D(char *ptr, uint32_t length, int loophow, int pitchoffset, int angle, int distance, int priority, fix16_t volume, intptr_t callbackval)
383  {
384      int left;
385      int right;
386      int mid;
387      int vol;
388      int status;
389  
390      if (!MV_Installed)
391          return MV_SetErrorCode(MV_NotInstalled);
392  
393      if (distance < 0)
394      {
395          distance = -distance;
396          angle += MV_NUMPANPOSITIONS / 2;
397      }
398  
399      vol = MIX_VOLUME(distance);
400  
401      // Ensure angle is within 0 - 127
402      angle &= MV_MAXPANPOSITION;
403  
404      left = MV_PanTable[angle][vol].left;
405      right = MV_PanTable[angle][vol].right;
406      mid = max(0, 255 - distance);
407  
408      status = MV_PlayFLAC(ptr, length, loophow, -1, pitchoffset, mid, left, right, priority, volume, callbackval);
409  
410      return status;
411  }
412  
413  
414  /*---------------------------------------------------------------------
415  Function: MV_PlayFLAC
416  
417  Begin playback of sound data with the given sound levels and
418  priority.
419  ---------------------------------------------------------------------*/
420  
421  int MV_PlayFLAC(char *ptr, uint32_t length, int loopstart, int loopend, int pitchoffset, int vol, int left, int right, int priority, fix16_t volume, intptr_t callbackval)
422  {
423      UNREFERENCED_PARAMETER(loopend);
424  
425      if (!MV_Installed)
426          return MV_SetErrorCode(MV_NotInstalled);
427  
428      // Request a voice from the voice pool
429      auto voice = MV_AllocVoice(priority, sizeof(flac_data));
430      if (voice == nullptr)
431          return MV_SetErrorCode(MV_NoVoices);
432  
433      auto fd = (flac_data *)voice->rawdataptr;
434  
435      fd->owner      = voice;
436      fd->ptr        = ptr;
437      fd->pos        = 0;
438      fd->blocksize  = 0;
439      fd->length     = length;
440      fd->block      = nullptr;
441      fd->stream     = FLAC__stream_decoder_new();
442      fd->sample_pos = 0;
443  
444      FLAC__stream_decoder_set_metadata_ignore_all(fd->stream);
445  
446      if (FLAC__stream_decoder_init_stream(fd->stream, read_flac_stream, seek_flac_stream, tell_flac_stream,
447                                           length_flac_stream, eof_flac_stream, write_flac_stream,
448                                           /*metadata_flac_stream*/ nullptr, error_flac_stream,
449                                           (void *)fd) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
450      {
451          LOG_F(ERROR, "MV_PlayFLAC: error in FLAC__stream_decoder_init_stream: %s", FLAC__stream_decoder_get_resolved_state_string(fd->stream));
452          ALIGNED_FREE_AND_NULL(fd);
453          return MV_SetErrorCode(MV_InvalidFile);
454      }
455  
456      voice->wavetype = FMT_FLAC;
457      voice->GetSound = MV_GetNextFLACBlock;
458      voice->NextBlock = fd->block;
459      voice->PitchScale = PITCH_GetScale(pitchoffset);
460      voice->priority = priority;
461      voice->callbackval = callbackval;
462  
463      voice->Loop = { nullptr, nullptr, 0, loopstart >= 0 };
464  
465      // parse metadata
466      // loop parsing designed with multiple repetitions in mind
467      // In retrospect, it may be possible to MV_GetVorbisCommentLoops(voice, (vorbis_comment *)
468      // &tags->data.vorbis_comment)
469      // but libvorbisfile may be confused by the signedness of char* vs FLAC__byte* and this code does not depend on
470      // HAVE_VORBIS.
471      auto metadata_chain = FLAC__metadata_chain_new();
472      if (metadata_chain != nullptr)
473      {
474          if (FLAC__metadata_chain_read_with_callbacks(metadata_chain, fd, flac_callbacks))
475          {
476              FLAC__Metadata_Iterator *metadata_iterator = FLAC__metadata_iterator_new();
477              if (metadata_iterator != nullptr)
478              {
479                  char *vc_loopstart = nullptr;
480                  char *vc_loopend = nullptr;
481                  char *vc_looplength = nullptr;
482  
483                  FLAC__metadata_iterator_init(metadata_iterator, metadata_chain);
484  
485                  do
486                  {
487                      FLAC__StreamMetadata *tags = FLAC__metadata_iterator_get_block(metadata_iterator);
488  
489                      if (tags->type == FLAC__METADATA_TYPE_STREAMINFO)
490                      {
491                          const FLAC__StreamMetadata_StreamInfo *info = &tags->data.stream_info;
492  
493                          if (info->channels != 1 && info->channels != 2)
494                          {
495                              FLAC__metadata_object_delete(tags);
496                              FLAC__metadata_iterator_delete(metadata_iterator);
497                              // FLAC__metadata_chain_delete(metadata_chain);
498                              FLAC__stream_decoder_finish(fd->stream);
499                              FLAC__stream_decoder_delete(fd->stream);
500                              ALIGNED_FREE_AND_NULL(fd);
501                              return MV_SetErrorCode(MV_InvalidFile);
502                          }
503  
504                          voice->channels = info->channels;
505                          voice->bits = info->bits_per_sample;
506                          voice->SamplingRate = info->sample_rate;
507                      }
508  
509                      // load loop tags from metadata
510                      if (tags->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
511                      {
512                          FLAC__uint32 comment;
513                          for (comment = 0; comment < tags->data.vorbis_comment.num_comments; ++comment)
514                          {
515                              const char *entry = (const char *)tags->data.vorbis_comment.comments[comment].entry;
516                              if (entry != nullptr && entry[0] != '\0')
517                              {
518                                  const char *value = strchr(entry, '=');
519                                  const size_t field = value - entry;
520                                  value += 1;
521  
522                                  for (int t = 0; t < loopStartTagCount && vc_loopstart == nullptr; ++t)
523                                  {
524                                      char const * const tag = loopStartTags[t];
525                                      if (field == strlen(tag) && Bstrncasecmp(entry, tag, field) == 0)
526                                          vc_loopstart = Xstrdup(value);
527                                  }
528  
529                                  for (int t = 0; t < loopEndTagCount && vc_loopend == nullptr; ++t)
530                                  {
531                                      char const * const tag = loopEndTags[t];
532                                      if (field == strlen(tag) && Bstrncasecmp(entry, tag, field) == 0)
533                                          vc_loopend = Xstrdup(value);
534                                  }
535  
536                                  for (int t = 0; t < loopLengthTagCount && vc_looplength == nullptr; ++t)
537                                  {
538                                      char const * const tag = loopLengthTags[t];
539                                      if (field == strlen(tag) && Bstrncasecmp(entry, tag, field) == 0)
540                                          vc_looplength = Xstrdup(value);
541                                  }
542                              }
543                          }
544                      }
545  
546                      FLAC__metadata_object_delete(
547                      tags);  // If it were not for this, I would assign pointers instead of strdup().
548                  } while (FLAC__metadata_iterator_next(metadata_iterator));
549  
550                  if (vc_loopstart != nullptr)
551                  {
552                      {
553                          const FLAC__int64 flac_loopstart = atol(vc_loopstart);
554                          if (flac_loopstart >= 0)  // a loop starting at 0 is valid
555                          {
556                              voice->Loop.Start = (const char *)(intptr_t)flac_loopstart;
557                              voice->Loop.Size = 1;
558                          }
559                      }
560                      Xfree(vc_loopstart);
561                  }
562                  if (vc_loopend != nullptr)
563                  {
564                      if (voice->Loop.Size > 0)
565                      {
566                          const FLAC__int64 flac_loopend = atol(vc_loopend);
567                          if (flac_loopend > 0)  // a loop ending at 0 is invalid
568                              voice->Loop.End = (const char *)(intptr_t)flac_loopend;
569                      }
570                      Xfree(vc_loopend);
571                  }
572                  if (vc_looplength != nullptr)
573                  {
574                      if (voice->Loop.Size > 0 && voice->Loop.End == 0)
575                      {
576                          const FLAC__int64 flac_looplength = atol(vc_looplength);
577                          if (flac_looplength > 0)  // a loop of length 0 is invalid
578                              voice->Loop.End = (const char *)((intptr_t)flac_looplength + (intptr_t)voice->Loop.Start);
579                      }
580                      Xfree(vc_looplength);
581                  }
582  
583                  FLAC__metadata_iterator_delete(metadata_iterator);
584              }
585              else
586                  LOG_F(ERROR, "MV_PlayFLAC: error in FLAC__metadata_iterator_new");
587          }
588          else
589              LOG_F(ERROR, "MV_PlayFLAC: error in FLAC__metadata_chain_read_with_callbacks: %s", FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(metadata_chain)]);
590  
591          // FLAC__metadata_chain_delete(metadata_chain); // when run with GDB, this throws SIGTRAP about freed heap
592          // memory being modified
593      }
594      else
595          LOG_F(ERROR, "MV_PlayFLAC: error in FLAC__metadata_chain_new");
596  
597      // CODEDUP multivoc.c MV_SetVoicePitch
598      voice->RateScale = divideu64((uint64_t)voice->SamplingRate * voice->PitchScale, MV_MixRate);
599      voice->FixedPointBufferSize = (voice->RateScale * MV_MIXBUFFERSIZE) - voice->RateScale;
600      MV_SetVoiceMixMode(voice);
601  
602      MV_SetVoiceVolume(voice, vol, left, right, volume);
603      MV_PlayVoice(voice);
604  
605      return voice->handle;
606  }
607  
608  
609  void MV_ReleaseFLACVoice(VoiceNode *voice)
610  {
611      Bassert(voice->wavetype == FMT_FLAC && voice->rawdataptr != nullptr && voice->rawdatasiz == sizeof(flac_data));
612  
613      flac_data *fd = (flac_data *)voice->rawdataptr;
614      voice->rawdataptr = nullptr;
615      voice->rawdatasiz = 0;
616  
617      if (fd->stream != nullptr)
618      {
619          auto stream = fd->stream;
620          fd->stream = nullptr;
621  
622          FLAC__stream_decoder_finish(stream);
623          FLAC__stream_decoder_delete(stream);
624      }
625  
626      auto block = fd->block;
627      fd->block = nullptr;
628  
629      ALIGNED_FREE_AND_NULL(block);
630      ALIGNED_FREE_AND_NULL(fd);
631  }
632  #else
633  #include "_multivc.h"
634  
635  int MV_PlayFLAC(char *, uint32_t, int, int, int, int, int, int, int, fix16_t, intptr_t)
636  {
637      LOG_F(ERROR, "MV_PlayFLAC: FLAC support not included in this binary.");
638      return -1;
639  }
640  
641  int MV_PlayFLAC3D(char *, uint32_t, int, int, int, int, int, fix16_t, intptr_t)
642  {
643      LOG_F(ERROR, "MV_PlayFLAC: FLAC support not included in this binary.");
644      return -1;
645  }
646  #endif  // HAVE_FLAC