Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dr_wav append write? #211

Open
Youlean opened this issue Oct 16, 2021 · 8 comments
Open

dr_wav append write? #211

Youlean opened this issue Oct 16, 2021 · 8 comments

Comments

@Youlean
Copy link

Youlean commented Oct 16, 2021

Hi, is there a way for dr_wav to append data to a wav file when writing instead of rewriting the file?

@mackron
Copy link
Owner

mackron commented Oct 17, 2021

No, unfortunately appending is not supported. The writing API was only really designed for basic use cases.

@Youlean
Copy link
Author

Youlean commented Oct 17, 2021

Thanks. It might be complex to implement it to dr_wav. I have looked and I will have to have a deep understanding of the lib to be able to implement it.

The way to do it:

open file
seek to the end of the file
write new data
read old wav header
create a new wav header and overwrite the old one

This lib is doing exactly that: https://github.com/justinfrankel/WDL/blob/master/WDL/wavwrite.h

@mackron
Copy link
Owner

mackron commented Oct 17, 2021

Yeah I'm not against adding support or anything. I'll mark this as a feature request and get to it when I can, but it could be a ways away.

@Youlean
Copy link
Author

Youlean commented Oct 17, 2021

I qm already working on it. Might have it ready in a couple of hours

@Youlean
Copy link
Author

Youlean commented Oct 17, 2021

I think I got it. I will need to do more testing, but so far it works great.

Here are the changes/additions:

typedef enum
{
    drwav_seek_origin_start,
    drwav_seek_origin_current,
    drwav_seek_origin_end
} drwav_seek_origin;
DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
{
  drwav_uint32 seekOrigin = SEEK_SET;
  
  if (origin == drwav_seek_origin_current)
    seekOrigin = SEEK_CUR;
  
  if (origin == drwav_seek_origin_end)
    seekOrigin = SEEK_END;
  
    return fseek((FILE*)pUserData, offset, seekOrigin) == 0;
}
DRWAV_API drwav_bool32 drwav_init_file_write_append(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
{
  return drwav_init_file_write_append__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
}
DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_append__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
{
  /* Open the file and get current data chunk size */
  drwav_uint64 dataChunkDataSize = 0;
  drwav_uint64 dataChunkDataPos = 0;
  
  drwav_bool32 appendToFile = DRWAV_FALSE;
  
  /* Open file for writing */
  FILE* pFile;
  if (drwav_fopen(&pFile, filename, "r+b") == DRWAV_SUCCESS)
  {
    if (drwav_init_file(pWav, filename, NULL)) {
      dataChunkDataSize = pWav->dataChunkDataSize;
      dataChunkDataPos = pWav->dataChunkDataPos;
      
      appendToFile = DRWAV_TRUE;
    }
    
    drwav_uninit(pWav);
        
    drwav_bool32 result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
    
    if (result != DRWAV_TRUE) {
      fclose(pFile);
      return result;
    }
    
    pWav->container = pFormat->container;
    pWav->channels = (drwav_uint16)pFormat->channels;
    pWav->sampleRate = pFormat->sampleRate;
    pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
    pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
    
    if (appendToFile)
    {
      pWav->dataChunkDataSize = dataChunkDataSize;
      pWav->dataChunkDataPos = dataChunkDataPos;
      
      if (pWav->onSeek) {
        pWav->onSeek(pWav->pUserData, 0, drwav_seek_origin::drwav_seek_origin_end);
      }
    }
    
    return result;
  }
  else if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS)
  {
    return DRWAV_FALSE;
  }
  
  /* This takes ownership of the FILE* object. */
  return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
}

@Youlean
Copy link
Author

Youlean commented Oct 17, 2021

drwav_init_file_write_append__internal was made a bit more complicated but in this way falling to call drwav_uninit won't render the file unreadable.

That basically means, if you create and write 3 seconds successfully, then close the file and open it again to append audio to it, but for some reason app crashes while writing data, and you don't call drwav_uninit (header data is not updated), the file will be still readable, and you will be able to play the first 3 seconds just fine. That is a pretty super feature to have. :)

@Youlean
Copy link
Author

Youlean commented Oct 18, 2021

I have run some tests today, and it seems it's working as expected.

@mackron
Copy link
Owner

mackron commented Oct 22, 2021

Thanks for that sample code. I'll review this when I get a chance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants