diff options
Diffstat (limited to 'source/io/SampleSourceAudiofile.c')
| -rw-r--r-- | source/io/SampleSourceAudiofile.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/source/io/SampleSourceAudiofile.c b/source/io/SampleSourceAudiofile.c new file mode 100644 index 0000000..17fd1b9 --- /dev/null +++ b/source/io/SampleSourceAudiofile.c @@ -0,0 +1,204 @@ +// +// SampleSourceAudiofile.c - MrsWatson +// Created by Nik Reiman on 1/22/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#if USE_AUDIOFILE + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "audio/AudioSettings.h" +#include "io/SampleSourceAudiofile.h" +#include "io/SampleSourcePcm.h" +#include "logging/EventLogger.h" + +static boolByte _openSampleSourceAudiofile(void *sampleSourcePtr, const SampleSourceOpenAs openAs) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourceAudiofileData extraData = sampleSource->extraData; + + if (openAs == SAMPLE_SOURCE_OPEN_READ) { + extraData->fileHandle = afOpenFile(sampleSource->sourceName->data, "r", NULL); + + if (extraData->fileHandle != NULL) { + setNumChannels((const unsigned int)afGetVirtualChannels(extraData->fileHandle, AF_DEFAULT_TRACK)); + setSampleRate((float)afGetRate(extraData->fileHandle, AF_DEFAULT_TRACK)); + } + } else if (openAs == SAMPLE_SOURCE_OPEN_WRITE) { + int byteOrder = AF_BYTEORDER_LITTLEENDIAN; + int outfileFormat; + switch (sampleSource->sampleSourceType) { + case SAMPLE_SOURCE_TYPE_AIFF: + // AIFF is the only file format we support which is big-endian. That is, + // even on big-endian platforms (which are untested), raw PCM should still + // write little-endian data. + byteOrder = AF_BYTEORDER_BIGENDIAN; + outfileFormat = AF_FILE_AIFF; + break; + case SAMPLE_SOURCE_TYPE_WAVE: + outfileFormat = AF_FILE_WAVE; + break; + case SAMPLE_SOURCE_TYPE_FLAC: + outfileFormat = AF_FILE_FLAC; + break; + default: + logInternalError("Unsupported audiofile type %d", sampleSource->sampleSourceType); + return false; + } + + AFfilesetup outfileSetup = afNewFileSetup(); + afInitFileFormat(outfileSetup, outfileFormat); + afInitByteOrder(outfileSetup, AF_DEFAULT_TRACK, byteOrder); + afInitChannels(outfileSetup, AF_DEFAULT_TRACK, getNumChannels()); + afInitRate(outfileSetup, AF_DEFAULT_TRACK, getSampleRate()); + afInitSampleFormat(outfileSetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, DEFAULT_BITRATE); + extraData->fileHandle = afOpenFile(sampleSource->sourceName->data, "w", outfileSetup); + } else { + logInternalError("Invalid type for openAs in audiofile source"); + return false; + } + + if (extraData->fileHandle == NULL) { + logError("File '%s' could not be opened for %s", + sampleSource->sourceName->data, + openAs == SAMPLE_SOURCE_OPEN_READ ? "reading" : "writing"); + return false; + } + + sampleSource->openedAs = openAs; + return true; +} + +boolByte _readBlockFromAudiofile(void *sampleSourcePtr, SampleBuffer sampleBuffer) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourceAudiofileData extraData = (SampleSourceAudiofileData)(sampleSource->extraData); + const size_t bufferByteSize = sizeof(short) * getNumChannels() * getBlocksize(); + AFframecount numFramesRead = 0; + + if (extraData->pcmBuffer == NULL) { + extraData->pcmBuffer = (short *)malloc(bufferByteSize); + } + + memset(extraData->pcmBuffer, 0, bufferByteSize); + numFramesRead = afReadFrames(extraData->fileHandle, AF_DEFAULT_TRACK, + extraData->pcmBuffer, (int)getBlocksize()); + sampleBufferCopyPcmSamples(sampleBuffer, extraData->pcmBuffer); + + // Set the blocksize of the sample buffer to be the number of frames read + sampleBuffer->blocksize = (unsigned long)numFramesRead; + sampleSource->numSamplesProcessed += numFramesRead; + + if (numFramesRead == 0) { + logDebug("End of audio file reached"); + return false; + } else if (numFramesRead < 0) { + logError("Error reading audio file"); + return false; + } else { + return true; + } +} + +boolByte _writeBlockToAudiofile(void *sampleSourcePtr, const SampleBuffer sampleBuffer) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourceAudiofileData extraData = (SampleSourceAudiofileData)(sampleSource->extraData); + const AFframecount numSamplesToWrite = sampleBuffer->blocksize; + const size_t bufferByteSize = sizeof(short) * getNumChannels() * getBlocksize(); + AFframecount numFramesWritten = 0; + + if (extraData->pcmBuffer == NULL) { + extraData->pcmBuffer = (short *)malloc(bufferByteSize); + } + + memset(extraData->pcmBuffer, 0, bufferByteSize); + // TODO: flip endian argument is probably wrong for some file formats (namely AIFF)!! + sampleBufferGetPcmSamples(sampleBuffer, extraData->pcmBuffer, false); + + numFramesWritten = afWriteFrames(extraData->fileHandle, AF_DEFAULT_TRACK, + extraData->pcmBuffer, (int)getBlocksize()); + sampleSource->numSamplesProcessed += getBlocksize() * getNumChannels(); + + if (numFramesWritten == -1) { + logWarn("audiofile encountered an error when writing to file"); + return false; + } else if (numFramesWritten == numSamplesToWrite) { + return true; + } else { + logWarn("Short write occurred while writing samples"); + return false; + } +} + +void _closeSampleSourceAudiofile(void *sampleSourcePtr) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourceAudiofileData extraData = (SampleSourceAudiofileData)sampleSource->extraData; + + if (extraData->fileHandle != NULL) { + afCloseFile(extraData->fileHandle); + } +} + +void _freeSampleSourceDataAudiofile(void *sampleSourceDataPtr) +{ + SampleSourceAudiofileData extraData = (SampleSourceAudiofileData)sampleSourceDataPtr; + + if (extraData->pcmBuffer != NULL) { + free(extraData->pcmBuffer); + } + + free(extraData); +} + +SampleSource _newSampleSourceAudiofile(const CharString sampleSourceName, + const SampleSourceType sampleSourceType) +{ + SampleSource sampleSource = (SampleSource)malloc(sizeof(SampleSourceMembers)); + SampleSourceAudiofileData extraData = (SampleSourceAudiofileData)malloc(sizeof(SampleSourceAudiofileDataMembers)); + + sampleSource->sampleSourceType = sampleSourceType; + sampleSource->openedAs = SAMPLE_SOURCE_OPEN_NOT_OPENED; + sampleSource->sourceName = newCharString(); + charStringCopy(sampleSource->sourceName, sampleSourceName); + sampleSource->numSamplesProcessed = 0; + + sampleSource->openSampleSource = _openSampleSourceAudiofile; + sampleSource->readSampleBlock = _readBlockFromAudiofile; + sampleSource->writeSampleBlock = _writeBlockToAudiofile; + sampleSource->closeSampleSource = _closeSampleSourceAudiofile; + sampleSource->freeSampleSourceData = _freeSampleSourceDataAudiofile; + + extraData->fileHandle = NULL; + extraData->pcmBuffer = NULL; + + sampleSource->extraData = extraData; + return sampleSource; +} + +#endif |
