Advertisement
Kitomas

kit_sdl2_acodecWAV.c as of 2023-9-28

Sep 29th, 2023
758
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.22 KB | None | 0 0
  1. #include "../include/kit_sdl2/kit_acodec.h"
  2. #include "../_private/include/_kit_privmacro.h"
  3.  
  4.  
  5.  
  6.  
  7. #define _WAVE_FORMAT_PCM 0x0001
  8. #define _WAVE_FORMAT_IEEE_FLOAT 0x0003
  9. #define _WAVE_FORMAT_EXTENSIBLE 0xFFFE
  10. enum _kit_acodecWAV_ids {
  11.   wid_RIFF = 0x46464952, //="RIFF"
  12.   wid_WAVE = 0x45564157, //="WAVE"
  13.   wid_fmt  = 0x20746D66, //="fmt "
  14.   wid_fact = 0x74636166, //="fact" (ignored when reading; numSamples is already calculated)
  15.   //(might need to include PEAK when writing,
  16.    //if programs refuse to read float sample wavs without it)
  17.   //wid_PEAK = 0x4B414550, //="PEAK" (ignored when reading; no info useful to me)
  18.   wid_data = 0x61746164, //="data"
  19. };
  20.  
  21.  
  22.  
  23.  
  24. #define _PEAKPOS_SIZE      (sizeof(_kit_acodecWAVPeakPos))
  25. #define _PEAK_SIZE         (sizeof(_kit_acodecWAVSubchunk_PEAK))
  26. #define _FMTEX_SIZE        (sizeof(_kit_acodecWAVSubchunk_fmtEx))
  27. #define _FMT_SIZE          (sizeof(_kit_acodecWAVSubchunk_fmt))
  28. #define _CHUNK_HEADER_SIZE (sizeof(Uint32)+sizeof(Uint32))
  29.  
  30.  
  31.  
  32. typedef struct _kit_acodecWAVPeakPos {
  33.   float     value; //value of peak (-1.0 -> 1.0)
  34.   Uint32 position; //sample frame of peak
  35. } _kit_acodecWAVPeakPos;
  36. typedef struct _kit_acodecWAVSubchunk_PEAK {
  37.   Uint32                version; //version of the PEAK subchunk
  38.   Uint32              timeStamp; //seconds since 1970-1-1
  39.   //_kit_acodecWAVPeakPos peak[1/*(# of channels)*/]; //peak info array
  40. } _kit_acodecWAVSubchunk_PEAK;
  41.  
  42.  
  43.  
  44. typedef struct _kit_acodecWAVSubchunk_fmtEx {
  45.   Uint16        format; //1 for pcm, 3 for float (anything else is unsupported)
  46.   Uint16      channels; //number of interleaved channels; L&R for stereo (2)
  47.   Uint32    sampleRate; //number of samples per second, in hertz
  48.   Uint32      byteRate; //=blockAlign*sampleRate
  49.   Uint16    blockAlign; //=(channels*bitsPerSample)/8
  50.   Uint16 bitsPerSample;
  51.   //the actual (Ex)tension part
  52.   Uint16      ext_size; //size of the extension (= 0 or 22)
  53.   Uint16     validBits; //actual number of bits used (must be <= bitsPerSample)
  54.   Uint32   channelMask; //speaker bitmask
  55.   union {
  56.     Uint16    format; //1 for pcm, 3 for float (same as the other .format)
  57.     char  GUID_s[16];
  58.     Uint64 GUID_n[2];
  59.   } /*----------*/ sub; //guid, including data format code
  60. } _kit_acodecWAVSubchunk_fmtEx;
  61.  
  62.  
  63.  
  64. typedef struct _kit_acodecWAVSubchunk_fmt {
  65.   Uint16        format; //1 for pcm, 3 for float (anything else is unsupported)
  66.   Uint16      channels; //number of interleaved channels; L&R for stereo (2)
  67.   Uint32    sampleRate; //number of samples per second, in hertz
  68.   Uint32      byteRate; //=blockAlign*sampleRate
  69.   Uint16    blockAlign; //=(channels*bitsPerSample)/8
  70.   Uint16 bitsPerSample;
  71. } _kit_acodecWAVSubchunk_fmt;
  72.  
  73.  
  74.  
  75. typedef struct _kit_acodecWAVSubchunk {
  76.   Uint32   id;
  77.   Uint32 size;
  78.   union {
  79.     Uint32                      waveID; //part of wave header chunk
  80.     _kit_acodecWAVSubchunk_fmt     fmt; //contains most relevant stream info
  81.     _kit_acodecWAVSubchunk_fmtEx fmtEx; //fmt, where format = _WAVE_FORMAT_EXTENSIBLE
  82.     Uint32                sampleLength; //only element of "fact" subchunk
  83.     _kit_acodecWAVSubchunk_PEAK   PEAK; //contains info about amplitude peaks
  84.     Uint8                      data[1]; //only element of "data" subchunk
  85.   };
  86. } _kit_acodecWAVSubchunk;
  87.  
  88.  
  89.  
  90.  
  91. static inline int _kit_acodecWAVLoad_fmt(kit_acodecPCM* pcm, _kit_acodecWAVSubchunk* subchunk){
  92.    //format
  93.   Uint16 bitsPerSample = subchunk->fmtEx.bitsPerSample;
  94.   _IF_SDLERR((bitsPerSample%8)!=0,;,"(bitsPerSample%%8)!=0")
  95.   Uint16 format=subchunk->fmtEx.format;
  96.   switch(format){
  97.   case _WAVE_FORMAT_PCM: _format_pcm: //format 0x0001
  98.     if(     bitsPerSample== 8) pcm->format = AUDIO_U8 ;
  99.     else if(bitsPerSample==16) pcm->format = AUDIO_S16;
  100.     else if(bitsPerSample==32) pcm->format = AUDIO_S32;
  101.     else _IS_SDLERR(;,"PCM && bitsPerSample!=<8,16,32>")
  102.     break;
  103.   case _WAVE_FORMAT_IEEE_FLOAT: _format_float: //format 0x0003
  104.     _IF_SDLERR(bitsPerSample!=32,;,"float && bitsPerSample!=32")
  105.     pcm->format = AUDIO_F32;
  106.     break;
  107.   case _WAVE_FORMAT_EXTENSIBLE: //format 0xFFFE
  108.     _IF_SDLERR(subchunk->size<18,;,"extensible && fmt size<18")
  109.     _IF_SDLERR(subchunk->fmtEx.ext_size<22,;,"extension size<22")
  110.     format=subchunk->fmtEx.sub.format; //instead of subchunk->fmtEx.format
  111.     if(format == _WAVE_FORMAT_PCM) goto _format_pcm;
  112.     if(format == _WAVE_FORMAT_IEEE_FLOAT) goto _format_float;
  113.     SDL_FALLTHROUGH; //go to default when format is not recognized
  114.   default: _IS_SDLERR(;,"unknown format 0x%04X",subchunk->fmtEx.format) }
  115.  
  116.    //channels
  117.   _IF_SDLERR(!subchunk->fmtEx.channels,;,"channels=0")
  118.   _IF_SDLERR(subchunk->fmtEx.channels>2,;,"channels>2")
  119.   pcm->channels = subchunk->fmtEx.channels;
  120.  
  121.    //sampleRate
  122.   _IF_SDLERR(subchunk->fmtEx.sampleRate<1000,;,"sampleRate<1kHz")
  123.   _IF_SDLERR(subchunk->fmtEx.sampleRate>384000,;,"sampleRate>384kHz")
  124.   pcm->sampleRate = subchunk->fmtEx.sampleRate;
  125.  
  126.    //byteRate
  127.   Uint16 bytesPerSample = bitsPerSample/8;
  128.   Uint32 correctByteRate = bytesPerSample*pcm->sampleRate*pcm->channels;
  129.   _IF_SDLERR(subchunk->fmtEx.byteRate!=correctByteRate,;,"incorrect byteRate")
  130.   pcm->bitRate = subchunk->fmtEx.byteRate*8;
  131.  
  132.   /*!err*/ return  0;
  133.   _error_: return -1;
  134. }
  135.  
  136.  
  137.  
  138. kit_acodecPCM* kit_acodecWAVRead(const char* filePath){
  139.   kit_acodecPCM* pcm = NULL;
  140.   void* fileDataStart = NULL;
  141.   SDL_bool success = SDL_FALSE;
  142.   size_t fileSize = kit_coreFileReadBin(filePath,&fileDataStart,0);
  143.   _IF_SDLERR(!fileSize,;,"!file")
  144.   _IF_SDLERR(fileSize<44,;,"fileSize<44")
  145.   void* fileDataEnd = fileDataStart+fileSize;
  146.  
  147.  
  148.   //allocate memory for pcm struct, before setting constant values
  149.   void* data = fileDataStart;
  150.   _IF_GOTO_ERROR(kit_coreRealloc(&pcm,0,sizeof(kit_acodecPCM)),;)
  151.   _IF_SDLERR(pcm==NULL,;,"!pcm")
  152.    //these members specifically are constants, and shouldn't be touched
  153.   pcm->magic      = KPCM_MAGIC; //="kPCM" (no terminator)
  154.   pcm->headerSize = sizeof(kit_acodecPCM);
  155.  
  156.  
  157.   //verify wave header
  158.   _kit_acodecWAVSubchunk* subchunk = data;
  159.   _IF_SDLERR(subchunk->id!=wid_RIFF,;,"chunk ID!=\"RIFF\"")
  160.   _IF_SDLERR(subchunk->size!=(fileSize-8),;,"chunkSize!=(fileSize-8)")
  161.   _IF_SDLERR(subchunk->waveID!=wid_WAVE,;,"waveID!=\"WAVE\"")
  162.   data += _CHUNK_HEADER_SIZE;
  163.   data += sizeof(Uint32);
  164.   _IF_SDLERR(data>=fileDataEnd,;,"buffer overflow") //just in case
  165.  
  166.  
  167.   //process subchunks
  168.   SDL_bool has_fmt  = SDL_FALSE;
  169.   SDL_bool has_data = SDL_FALSE;
  170.   while(data < fileDataEnd){
  171.     subchunk = data;
  172.     switch(subchunk->id){
  173.     case wid_fmt:; //contains most pcm info
  174.       _IF_GOTO_ERROR(_kit_acodecWAVLoad_fmt(pcm,subchunk),;)
  175.       has_fmt = SDL_TRUE; break;
  176.  
  177.     case wid_data:; //contains sample data (contiguous with pcm struct itself)
  178.       _IF_GOTO_ERROR(kit_coreRealloc(&pcm, NO_MEMSET,sizeof(kit_acodecPCM)+subchunk->size),;)
  179.       _IF_SDLERR(pcm==NULL,;,"!pcm->data")
  180.       pcm->data = (void*)pcm+sizeof(kit_acodecPCM);
  181.       kit_coreMemcpy(pcm->data, subchunk->data, subchunk->size);
  182.       pcm->dataSize = subchunk->size;
  183.       has_data = SDL_TRUE; break;
  184.  
  185.     default:; //other subchunks are ignored
  186.     }
  187.     data += _CHUNK_HEADER_SIZE;
  188.     data += subchunk->size;
  189.   }
  190.  
  191.  
  192.   _IF_SDLERR(!has_fmt,;,"fmt subchunk not found")
  193.   _IF_SDLERR(!has_data,;,"data subchunk not found")
  194.   //calculate numSamples
  195.   pcm->numSamples  = pcm->dataSize;
  196.   pcm->numSamples /= SDL_AUDIO_BITSIZE(pcm->format)/8;
  197.   pcm->numSamples /= pcm->channels;
  198.  
  199.   pcm->loopEnd = pcm->numSamples; //now, if loopCount>0, the whole thing is looped
  200.  
  201.   success = SDL_TRUE;
  202.   _error_:
  203.   if(fileDataStart != NULL) SDL_free(fileDataStart);
  204.   if(pcm!=NULL && !success){
  205.     SDL_free(pcm);
  206.     pcm = NULL;
  207.   }
  208.   return pcm;
  209. }
  210.  
  211.  
  212.  
  213.  
  214. #define _INCREASE_FILEDATA(_amount) { \
  215.   fileDataSize += (_amount); \
  216.   _IF_GOTO_ERROR(kit_coreRealloc(&fileDataStart, NO_MEMSET,fileDataSize),;) \
  217.   data = fileDataStart+offset; }
  218. #define _INCREASE_OFFSET(_amount) { data+=(_amount); offset+=(_amount); }
  219. int kit_acodecWAVWrite(kit_acodecPCM* pcm, const char* filePath){
  220.   _kit_acodecWAVSubchunk_fmt fmt; //fmt subchunk (not fmtEx)
  221.   _kit_acodecWAVSubchunk* subchunk;
  222.  
  223.   void *data = NULL,  *fileDataStart = NULL;
  224.   size_t offset = 0,  fileDataSize = 0;
  225.  
  226.   _IF_SDLERR(pcm==NULL,;,"!pcm")
  227.   _IF_SDLERR(filePath==NULL,;,"!filePath")
  228.   _IF_SDLERR(pcm->data==NULL,;,"!pcm->data")
  229.  
  230.  
  231.   //verify some pcm struct info
  232.    //grab stuff from pcm struct
  233.   Uint64 dataSize      = pcm->dataSize;
  234.   Uint16 channels      = pcm->channels;
  235.   Uint32 sampleRate    = pcm->sampleRate;
  236.   Uint16 bitsPerSample = SDL_AUDIO_BITSIZE(pcm->format);
  237.   Uint16 blockAlign    = (channels*bitsPerSample)/8;
  238.   Uint32 byteRate      = blockAlign*sampleRate;
  239.   Uint32 numSamples    = dataSize/blockAlign;
  240.    //then audit the retrieved values
  241.   _IF_SDLERR(dataSize>0xffffffff,;,"pcm->dataSize>4GiB")
  242.   _IF_SDLERR(channels==0,;,"pcm->channels=0")
  243.   _IF_SDLERR(sampleRate==0,;,"pcm->sampleRate=0")
  244.   _IF_SDLERR(bitsPerSample==0,;,"bitsPerSample==0")
  245.     _IF_SDLERR(bitsPerSample%8,;,"bitsPerSample%%8)!=0")
  246.   _IF_SDLERR(pcm->bitRate!=(byteRate*8),;,"pcm->bitRate is incorrect")
  247.   _IF_SDLERR(pcm->numSamples!=numSamples,;,"pcm->numSamples is incorrect")
  248.   _IF_SDLERR(pcm->data!=((void*)pcm)+sizeof(kit_acodecPCM),;,"pcm->data is incorrect")
  249.  
  250.  
  251.   //fill in fmt struct
  252.   switch(pcm->format){
  253.   case AUDIO_U8 : SDL_FALLTHROUGH;
  254.   case AUDIO_S16: SDL_FALLTHROUGH;
  255.   case AUDIO_S32: fmt.format = _WAVE_FORMAT_PCM;        break;
  256.   case AUDIO_F32: fmt.format = _WAVE_FORMAT_IEEE_FLOAT; break;
  257.   default: _IS_SDLERR(;,"unknown pcm format 0x%04X",pcm->format) }
  258.   fmt.channels      = channels;
  259.   fmt.sampleRate    = sampleRate;
  260.   fmt.bitsPerSample = bitsPerSample;
  261.   fmt.blockAlign    = blockAlign;
  262.   fmt.byteRate      = byteRate;
  263.  
  264.  
  265.   //wave header
  266.   _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + sizeof(Uint32))
  267.   subchunk = data;
  268.   subchunk->id     = wid_RIFF; //="RIFF"
  269.   //subchunk->size = <file size - 8; done later>;
  270.   subchunk->waveID = wid_WAVE; //="WAVE"
  271.   _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + sizeof(Uint32))
  272.  
  273.  
  274.   //fmt subchunk
  275.   _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + _FMT_SIZE)
  276.   subchunk = data;
  277.   subchunk->id   = wid_fmt;   //="fmt "
  278.   subchunk->size = _FMT_SIZE; //=16
  279.   subchunk->fmt  = fmt;
  280.   _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + _FMT_SIZE)
  281.  
  282.  
  283.   //fact subchunk
  284.   _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + sizeof(Uint32))
  285.   subchunk = data;
  286.   subchunk->id           = wid_fact;
  287.   subchunk->size         = sizeof(Uint32);
  288.   subchunk->sampleLength = numSamples;
  289.   _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + sizeof(Uint32))
  290.  
  291.  
  292.   //data subchunk
  293.   _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + dataSize + (dataSize%2))
  294.   subchunk = data;
  295.   subchunk->id   = wid_data;
  296.   subchunk->size = dataSize;
  297.   kit_coreMemcpy(subchunk->data, pcm->data, dataSize);
  298.   _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + dataSize)
  299.   if(dataSize%2){ //if dataSize is odd, insert a padding byte
  300.     *(Uint8*)data = 0; //data should be pointing to the very last byte
  301.     _INCREASE_OFFSET(sizeof(Uint8))
  302.   }
  303.  
  304.  
  305.   //correct wave header's chunk size, before writing to file
  306.   subchunk = fileDataStart;
  307.   subchunk->size = offset-_CHUNK_HEADER_SIZE;
  308.   _IF_GOTO_ERROR(kit_coreFileWriteBin(filePath, fileDataStart, offset, 0),;)
  309.  
  310.  
  311.   return 0;
  312.   _error_:
  313.   if(fileDataStart != NULL) SDL_free(fileDataStart);
  314.   return -1;
  315. }
  316.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement