Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "../include/kit_sdl2/kit_acodec.h"
- #include "../_private/include/_kit_privmacro.h"
- #define _WAVE_FORMAT_PCM 0x0001
- #define _WAVE_FORMAT_IEEE_FLOAT 0x0003
- #define _WAVE_FORMAT_EXTENSIBLE 0xFFFE
- enum _kit_acodecWAV_ids {
- wid_RIFF = 0x46464952, //="RIFF"
- wid_WAVE = 0x45564157, //="WAVE"
- wid_fmt = 0x20746D66, //="fmt "
- wid_fact = 0x74636166, //="fact" (ignored when reading; numSamples is already calculated)
- //(might need to include PEAK when writing,
- //if programs refuse to read float sample wavs without it)
- //wid_PEAK = 0x4B414550, //="PEAK" (ignored when reading; no info useful to me)
- wid_data = 0x61746164, //="data"
- };
- #define _PEAKPOS_SIZE (sizeof(_kit_acodecWAVPeakPos))
- #define _PEAK_SIZE (sizeof(_kit_acodecWAVSubchunk_PEAK))
- #define _FMTEX_SIZE (sizeof(_kit_acodecWAVSubchunk_fmtEx))
- #define _FMT_SIZE (sizeof(_kit_acodecWAVSubchunk_fmt))
- #define _CHUNK_HEADER_SIZE (sizeof(Uint32)+sizeof(Uint32))
- typedef struct _kit_acodecWAVPeakPos {
- float value; //value of peak (-1.0 -> 1.0)
- Uint32 position; //sample frame of peak
- } _kit_acodecWAVPeakPos;
- typedef struct _kit_acodecWAVSubchunk_PEAK {
- Uint32 version; //version of the PEAK subchunk
- Uint32 timeStamp; //seconds since 1970-1-1
- //_kit_acodecWAVPeakPos peak[1/*(# of channels)*/]; //peak info array
- } _kit_acodecWAVSubchunk_PEAK;
- typedef struct _kit_acodecWAVSubchunk_fmtEx {
- Uint16 format; //1 for pcm, 3 for float (anything else is unsupported)
- Uint16 channels; //number of interleaved channels; L&R for stereo (2)
- Uint32 sampleRate; //number of samples per second, in hertz
- Uint32 byteRate; //=blockAlign*sampleRate
- Uint16 blockAlign; //=(channels*bitsPerSample)/8
- Uint16 bitsPerSample;
- //the actual (Ex)tension part
- Uint16 ext_size; //size of the extension (= 0 or 22)
- Uint16 validBits; //actual number of bits used (must be <= bitsPerSample)
- Uint32 channelMask; //speaker bitmask
- union {
- Uint16 format; //1 for pcm, 3 for float (same as the other .format)
- char GUID_s[16];
- Uint64 GUID_n[2];
- } /*----------*/ sub; //guid, including data format code
- } _kit_acodecWAVSubchunk_fmtEx;
- typedef struct _kit_acodecWAVSubchunk_fmt {
- Uint16 format; //1 for pcm, 3 for float (anything else is unsupported)
- Uint16 channels; //number of interleaved channels; L&R for stereo (2)
- Uint32 sampleRate; //number of samples per second, in hertz
- Uint32 byteRate; //=blockAlign*sampleRate
- Uint16 blockAlign; //=(channels*bitsPerSample)/8
- Uint16 bitsPerSample;
- } _kit_acodecWAVSubchunk_fmt;
- typedef struct _kit_acodecWAVSubchunk {
- Uint32 id;
- Uint32 size;
- union {
- Uint32 waveID; //part of wave header chunk
- _kit_acodecWAVSubchunk_fmt fmt; //contains most relevant stream info
- _kit_acodecWAVSubchunk_fmtEx fmtEx; //fmt, where format = _WAVE_FORMAT_EXTENSIBLE
- Uint32 sampleLength; //only element of "fact" subchunk
- _kit_acodecWAVSubchunk_PEAK PEAK; //contains info about amplitude peaks
- Uint8 data[1]; //only element of "data" subchunk
- };
- } _kit_acodecWAVSubchunk;
- static inline int _kit_acodecWAVLoad_fmt(kit_acodecPCM* pcm, _kit_acodecWAVSubchunk* subchunk){
- //format
- Uint16 bitsPerSample = subchunk->fmtEx.bitsPerSample;
- _IF_SDLERR((bitsPerSample%8)!=0,;,"(bitsPerSample%%8)!=0")
- Uint16 format=subchunk->fmtEx.format;
- switch(format){
- case _WAVE_FORMAT_PCM: _format_pcm: //format 0x0001
- if( bitsPerSample== 8) pcm->format = AUDIO_U8 ;
- else if(bitsPerSample==16) pcm->format = AUDIO_S16;
- else if(bitsPerSample==32) pcm->format = AUDIO_S32;
- else _IS_SDLERR(;,"PCM && bitsPerSample!=<8,16,32>")
- break;
- case _WAVE_FORMAT_IEEE_FLOAT: _format_float: //format 0x0003
- _IF_SDLERR(bitsPerSample!=32,;,"float && bitsPerSample!=32")
- pcm->format = AUDIO_F32;
- break;
- case _WAVE_FORMAT_EXTENSIBLE: //format 0xFFFE
- _IF_SDLERR(subchunk->size<18,;,"extensible && fmt size<18")
- _IF_SDLERR(subchunk->fmtEx.ext_size<22,;,"extension size<22")
- format=subchunk->fmtEx.sub.format; //instead of subchunk->fmtEx.format
- if(format == _WAVE_FORMAT_PCM) goto _format_pcm;
- if(format == _WAVE_FORMAT_IEEE_FLOAT) goto _format_float;
- SDL_FALLTHROUGH; //go to default when format is not recognized
- default: _IS_SDLERR(;,"unknown format 0x%04X",subchunk->fmtEx.format) }
- //channels
- _IF_SDLERR(!subchunk->fmtEx.channels,;,"channels=0")
- _IF_SDLERR(subchunk->fmtEx.channels>2,;,"channels>2")
- pcm->channels = subchunk->fmtEx.channels;
- //sampleRate
- _IF_SDLERR(subchunk->fmtEx.sampleRate<1000,;,"sampleRate<1kHz")
- _IF_SDLERR(subchunk->fmtEx.sampleRate>384000,;,"sampleRate>384kHz")
- pcm->sampleRate = subchunk->fmtEx.sampleRate;
- //byteRate
- Uint16 bytesPerSample = bitsPerSample/8;
- Uint32 correctByteRate = bytesPerSample*pcm->sampleRate*pcm->channels;
- _IF_SDLERR(subchunk->fmtEx.byteRate!=correctByteRate,;,"incorrect byteRate")
- pcm->bitRate = subchunk->fmtEx.byteRate*8;
- /*!err*/ return 0;
- _error_: return -1;
- }
- kit_acodecPCM* kit_acodecWAVRead(const char* filePath){
- kit_acodecPCM* pcm = NULL;
- void* fileDataStart = NULL;
- SDL_bool success = SDL_FALSE;
- size_t fileSize = kit_coreFileReadBin(filePath,&fileDataStart,0);
- _IF_SDLERR(!fileSize,;,"!file")
- _IF_SDLERR(fileSize<44,;,"fileSize<44")
- void* fileDataEnd = fileDataStart+fileSize;
- //allocate memory for pcm struct, before setting constant values
- void* data = fileDataStart;
- _IF_GOTO_ERROR(kit_coreRealloc(&pcm,0,sizeof(kit_acodecPCM)),;)
- _IF_SDLERR(pcm==NULL,;,"!pcm")
- //these members specifically are constants, and shouldn't be touched
- pcm->magic = KPCM_MAGIC; //="kPCM" (no terminator)
- pcm->headerSize = sizeof(kit_acodecPCM);
- //verify wave header
- _kit_acodecWAVSubchunk* subchunk = data;
- _IF_SDLERR(subchunk->id!=wid_RIFF,;,"chunk ID!=\"RIFF\"")
- _IF_SDLERR(subchunk->size!=(fileSize-8),;,"chunkSize!=(fileSize-8)")
- _IF_SDLERR(subchunk->waveID!=wid_WAVE,;,"waveID!=\"WAVE\"")
- data += _CHUNK_HEADER_SIZE;
- data += sizeof(Uint32);
- _IF_SDLERR(data>=fileDataEnd,;,"buffer overflow") //just in case
- //process subchunks
- SDL_bool has_fmt = SDL_FALSE;
- SDL_bool has_data = SDL_FALSE;
- while(data < fileDataEnd){
- subchunk = data;
- switch(subchunk->id){
- case wid_fmt:; //contains most pcm info
- _IF_GOTO_ERROR(_kit_acodecWAVLoad_fmt(pcm,subchunk),;)
- has_fmt = SDL_TRUE; break;
- case wid_data:; //contains sample data (contiguous with pcm struct itself)
- _IF_GOTO_ERROR(kit_coreRealloc(&pcm, NO_MEMSET,sizeof(kit_acodecPCM)+subchunk->size),;)
- _IF_SDLERR(pcm==NULL,;,"!pcm->data")
- pcm->data = (void*)pcm+sizeof(kit_acodecPCM);
- kit_coreMemcpy(pcm->data, subchunk->data, subchunk->size);
- pcm->dataSize = subchunk->size;
- has_data = SDL_TRUE; break;
- default:; //other subchunks are ignored
- }
- data += _CHUNK_HEADER_SIZE;
- data += subchunk->size;
- }
- _IF_SDLERR(!has_fmt,;,"fmt subchunk not found")
- _IF_SDLERR(!has_data,;,"data subchunk not found")
- //calculate numSamples
- pcm->numSamples = pcm->dataSize;
- pcm->numSamples /= SDL_AUDIO_BITSIZE(pcm->format)/8;
- pcm->numSamples /= pcm->channels;
- pcm->loopEnd = pcm->numSamples; //now, if loopCount>0, the whole thing is looped
- success = SDL_TRUE;
- _error_:
- if(fileDataStart != NULL) SDL_free(fileDataStart);
- if(pcm!=NULL && !success){
- SDL_free(pcm);
- pcm = NULL;
- }
- return pcm;
- }
- #define _INCREASE_FILEDATA(_amount) { \
- fileDataSize += (_amount); \
- _IF_GOTO_ERROR(kit_coreRealloc(&fileDataStart, NO_MEMSET,fileDataSize),;) \
- data = fileDataStart+offset; }
- #define _INCREASE_OFFSET(_amount) { data+=(_amount); offset+=(_amount); }
- int kit_acodecWAVWrite(kit_acodecPCM* pcm, const char* filePath){
- _kit_acodecWAVSubchunk_fmt fmt; //fmt subchunk (not fmtEx)
- _kit_acodecWAVSubchunk* subchunk;
- void *data = NULL, *fileDataStart = NULL;
- size_t offset = 0, fileDataSize = 0;
- _IF_SDLERR(pcm==NULL,;,"!pcm")
- _IF_SDLERR(filePath==NULL,;,"!filePath")
- _IF_SDLERR(pcm->data==NULL,;,"!pcm->data")
- //verify some pcm struct info
- //grab stuff from pcm struct
- Uint64 dataSize = pcm->dataSize;
- Uint16 channels = pcm->channels;
- Uint32 sampleRate = pcm->sampleRate;
- Uint16 bitsPerSample = SDL_AUDIO_BITSIZE(pcm->format);
- Uint16 blockAlign = (channels*bitsPerSample)/8;
- Uint32 byteRate = blockAlign*sampleRate;
- Uint32 numSamples = dataSize/blockAlign;
- //then audit the retrieved values
- _IF_SDLERR(dataSize>0xffffffff,;,"pcm->dataSize>4GiB")
- _IF_SDLERR(channels==0,;,"pcm->channels=0")
- _IF_SDLERR(sampleRate==0,;,"pcm->sampleRate=0")
- _IF_SDLERR(bitsPerSample==0,;,"bitsPerSample==0")
- _IF_SDLERR(bitsPerSample%8,;,"bitsPerSample%%8)!=0")
- _IF_SDLERR(pcm->bitRate!=(byteRate*8),;,"pcm->bitRate is incorrect")
- _IF_SDLERR(pcm->numSamples!=numSamples,;,"pcm->numSamples is incorrect")
- _IF_SDLERR(pcm->data!=((void*)pcm)+sizeof(kit_acodecPCM),;,"pcm->data is incorrect")
- //fill in fmt struct
- switch(pcm->format){
- case AUDIO_U8 : SDL_FALLTHROUGH;
- case AUDIO_S16: SDL_FALLTHROUGH;
- case AUDIO_S32: fmt.format = _WAVE_FORMAT_PCM; break;
- case AUDIO_F32: fmt.format = _WAVE_FORMAT_IEEE_FLOAT; break;
- default: _IS_SDLERR(;,"unknown pcm format 0x%04X",pcm->format) }
- fmt.channels = channels;
- fmt.sampleRate = sampleRate;
- fmt.bitsPerSample = bitsPerSample;
- fmt.blockAlign = blockAlign;
- fmt.byteRate = byteRate;
- //wave header
- _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + sizeof(Uint32))
- subchunk = data;
- subchunk->id = wid_RIFF; //="RIFF"
- //subchunk->size = <file size - 8; done later>;
- subchunk->waveID = wid_WAVE; //="WAVE"
- _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + sizeof(Uint32))
- //fmt subchunk
- _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + _FMT_SIZE)
- subchunk = data;
- subchunk->id = wid_fmt; //="fmt "
- subchunk->size = _FMT_SIZE; //=16
- subchunk->fmt = fmt;
- _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + _FMT_SIZE)
- //fact subchunk
- _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + sizeof(Uint32))
- subchunk = data;
- subchunk->id = wid_fact;
- subchunk->size = sizeof(Uint32);
- subchunk->sampleLength = numSamples;
- _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + sizeof(Uint32))
- //data subchunk
- _INCREASE_FILEDATA(_CHUNK_HEADER_SIZE + dataSize + (dataSize%2))
- subchunk = data;
- subchunk->id = wid_data;
- subchunk->size = dataSize;
- kit_coreMemcpy(subchunk->data, pcm->data, dataSize);
- _INCREASE_OFFSET(_CHUNK_HEADER_SIZE + dataSize)
- if(dataSize%2){ //if dataSize is odd, insert a padding byte
- *(Uint8*)data = 0; //data should be pointing to the very last byte
- _INCREASE_OFFSET(sizeof(Uint8))
- }
- //correct wave header's chunk size, before writing to file
- subchunk = fileDataStart;
- subchunk->size = offset-_CHUNK_HEADER_SIZE;
- _IF_GOTO_ERROR(kit_coreFileWriteBin(filePath, fileDataStart, offset, 0),;)
- return 0;
- _error_:
- if(fileDataStart != NULL) SDL_free(fileDataStart);
- return -1;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement