diff options
Diffstat (limited to 'source/io')
| -rw-r--r-- | source/io/RiffFile.c | 93 | ||||
| -rw-r--r-- | source/io/RiffFile.h | 75 | ||||
| -rw-r--r-- | source/io/SampleSource.c | 165 | ||||
| -rw-r--r-- | source/io/SampleSource.h | 94 | ||||
| -rw-r--r-- | source/io/SampleSourceAudiofile.c | 204 | ||||
| -rw-r--r-- | source/io/SampleSourceAudiofile.h | 47 | ||||
| -rw-r--r-- | source/io/SampleSourcePcm.c | 221 | ||||
| -rw-r--r-- | source/io/SampleSourcePcm.h | 92 | ||||
| -rw-r--r-- | source/io/SampleSourceSilence.c | 84 | ||||
| -rw-r--r-- | source/io/SampleSourceSilence.h | 33 | ||||
| -rw-r--r-- | source/io/SampleSourceWave.c | 499 | ||||
| -rw-r--r-- | source/io/SampleSourceWave.h | 33 |
12 files changed, 1640 insertions, 0 deletions
diff --git a/source/io/RiffFile.c b/source/io/RiffFile.c new file mode 100644 index 0000000..269780c --- /dev/null +++ b/source/io/RiffFile.c @@ -0,0 +1,93 @@ +// +// RiffFile.c - MrsWatson +// Created by Nik Reiman on 8/13/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. +// + +#include <stdlib.h> +#include <string.h> + +#include "base/Endian.h" +#include "io/RiffFile.h" + +RiffChunk newRiffChunk(void) +{ + //anything weird about this? nope, lets get back to sourcewave + RiffChunk chunk = (RiffChunk)malloc(sizeof(RiffChunkMembers)); + memset(chunk->id, 0, 5); + chunk->size = 0; + chunk->data = NULL; + return chunk; +} + +boolByte riffChunkReadNext(RiffChunk self, FILE *fileHandle, boolByte readData) +{ + size_t itemsRead = 0; + byte *chunkSize; + + if (fileHandle != NULL) { + itemsRead = fread(self->id, 1, 4, fileHandle); + + if (itemsRead != 4) { + return false; + } + + chunkSize = (byte *)malloc(sizeof(byte) * 4); + memset(chunkSize, 0, 4); + itemsRead = fread(chunkSize, 1, 4, fileHandle); + + if (itemsRead != 4) { + free(chunkSize); + return false; + } + + self->size = convertByteArrayToUnsignedInt(chunkSize); + free(chunkSize); + + if (self->size > 0 && readData) { + self->data = (byte *)malloc(self->size); + itemsRead = fread(self->data, 1, self->size, fileHandle); + + if (itemsRead != self->size) { + return false; + } + } + } + + return (boolByte)!feof(fileHandle); +} + +boolByte riffChunkIsIdEqualTo(const RiffChunk self, const char *id) +{ + return (boolByte)(strncmp(self->id, id, 4) == 0); +} + +void freeRiffChunk(RiffChunk self) +{ + if (self->data) { + free(self->data); + } + + free(self); +} diff --git a/source/io/RiffFile.h b/source/io/RiffFile.h new file mode 100644 index 0000000..5945d58 --- /dev/null +++ b/source/io/RiffFile.h @@ -0,0 +1,75 @@ +// +// RiffFile.h - MrsWatson +// Created by Nik Reiman on 8/13/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. +// + +#ifndef MrsWatson_RiffFile_h +#define MrsWatson_RiffFile_h + +#include <stdio.h> + +#include "base/CharString.h" +#include "base/Types.h" + +typedef struct { + char id[5]; + unsigned int size; + byte *data; +} RiffChunkMembers; +typedef RiffChunkMembers *RiffChunk; + +/** + * Create a new RIFF chunk object + * @return RiffChunk object + */ +RiffChunk newRiffChunk(void); + +/** + * Read the contents of the next chunk of a RIFF file into this object + * @param self + * @param fileHandle RIFF file, which should be opened for reading + * @param readData If true, save the contents of the chunk in the RiffChunk's + * data field. This is not always appropriate, for instance in the case of a + * PCM file body. In this case, one usually wants to know the size of the data + * chunk, but then to read bites from it in smaller blocks. + * @return True if the chunk was successfully read + */ +boolByte riffChunkReadNext(RiffChunk self, FILE *fileHandle, boolByte readData); + +/** + * Test to see if this chunk's ID is equal to the given four character sequence + * @param self + * @param id String to compare to, should be exactly 4 characters + * @return True if the ID's are equal, false otherwise + */ +boolByte riffChunkIsIdEqualTo(const RiffChunk self, const char *id); + +/** + * Free a RiffChunk object and its associated memory. + * @param self + */ +void freeRiffChunk(RiffChunk self); + +#endif diff --git a/source/io/SampleSource.c b/source/io/SampleSource.c new file mode 100644 index 0000000..06ba016 --- /dev/null +++ b/source/io/SampleSource.c @@ -0,0 +1,165 @@ +// +// SampleSource.c - MrsWatson +// Created by Nik Reiman on 1/2/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. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "base/File.h" +#include "io/SampleSource.h" +#include "logging/EventLogger.h" + +void sampleSourcePrintSupportedTypes(void) +{ + logInfo("Supported audio file types:"); + // We can theoretically support more formats, pretty much anything audiofile supports + // would work here. However, most of those file types are rather uncommon, and require + // special setup when writing, so we only choose the most common ones. +#if USE_AUDIOFILE + logInfo("- AIFF (via libaudiofile)"); +#endif +#if USE_FLAC + logInfo("- FLAC (via libaudiofile)"); +#endif + + // Always supported + logInfo("- PCM"); + +#if USE_AUDIOFILE + logInfo("- WAV (via libaudiofile)"); +#else + logInfo("- WAV (internal)"); +#endif +} + +static SampleSourceType _sampleSourceGuess(const CharString sampleSourceName) +{ + File sourceFile = NULL; + CharString sourceFileExtension = NULL; + SampleSourceType result = SAMPLE_SOURCE_TYPE_PCM; + + if (sampleSourceName == NULL || charStringIsEmpty(sampleSourceName)) { + result = SAMPLE_SOURCE_TYPE_SILENCE; + } else { + // Look for stdin/stdout + if (strlen(sampleSourceName->data) == 1 && sampleSourceName->data[0] == '-') { + result = SAMPLE_SOURCE_TYPE_PCM; + } else { + sourceFile = newFileWithPath(sampleSourceName); + sourceFileExtension = fileGetExtension(sourceFile); + freeFile(sourceFile); + + // If there is no file extension, then automatically assume raw PCM data. Deal with it! + if (charStringIsEmpty(sourceFileExtension)) { + result = SAMPLE_SOURCE_TYPE_PCM; + } + // Possible file extensions for raw PCM data + else if (charStringIsEqualToCString(sourceFileExtension, "pcm", true) || + charStringIsEqualToCString(sourceFileExtension, "raw", true) || + charStringIsEqualToCString(sourceFileExtension, "dat", true)) { + result = SAMPLE_SOURCE_TYPE_PCM; + } + +#if USE_AUDIOFILE + else if (charStringIsEqualToCString(sourceFileExtension, "aif", true) || + charStringIsEqualToCString(sourceFileExtension, "aiff", true)) { + result = SAMPLE_SOURCE_TYPE_AIFF; + } + +#endif + +#if USE_FLAC + else if (charStringIsEqualToCString(sourceFileExtension, "flac", true)) { + result = SAMPLE_SOURCE_TYPE_FLAC; + } + +#endif + + else if (charStringIsEqualToCString(sourceFileExtension, "wav", true) || + charStringIsEqualToCString(sourceFileExtension, "wave", true)) { + result = SAMPLE_SOURCE_TYPE_WAVE; + } else { + logCritical("Sample source '%s' does not match any supported type", sampleSourceName->data); + result = SAMPLE_SOURCE_TYPE_INVALID; + } + } + } + + freeCharString(sourceFileExtension); + return result; +} + +extern SampleSource _newSampleSourceAudiofile(const CharString sampleSourceName, + const SampleSourceType sampleSourceType); +extern SampleSource _newSampleSourcePcm(const CharString sampleSourceName); +extern SampleSource _newSampleSourceSilence(); +extern SampleSource _newSampleSourceWave(const CharString sampleSourceName); + +SampleSource sampleSourceFactory(const CharString sampleSourceName) +{ + SampleSourceType sampleSourceType = _sampleSourceGuess(sampleSourceName); + + switch (sampleSourceType) { + case SAMPLE_SOURCE_TYPE_SILENCE: + return _newSampleSourceSilence(); + + case SAMPLE_SOURCE_TYPE_PCM: + return _newSampleSourcePcm(sampleSourceName); + +#if USE_AUDIOFILE + + case SAMPLE_SOURCE_TYPE_AIFF: + return _newSampleSourceAudiofile(sampleSourceName, sampleSourceType); +#endif + +#if USE_FLAC + + case SAMPLE_SOURCE_TYPE_FLAC: + return _newSampleSourceAudiofile(sampleSourceName, sampleSourceType); +#endif + +#if USE_AUDIOFILE + + case SAMPLE_SOURCE_TYPE_WAVE: + return _newSampleSourceAudiofile(sampleSourceName, sampleSourceType); +#else + + case SAMPLE_SOURCE_TYPE_WAVE: + return _newSampleSourceWave(sampleSourceName); +#endif + + default: + return NULL; + } +} + +void freeSampleSource(SampleSource self) +{ + self->freeSampleSourceData(self->extraData); + freeCharString(self->sourceName); + free(self); +} diff --git a/source/io/SampleSource.h b/source/io/SampleSource.h new file mode 100644 index 0000000..f4ef0f4 --- /dev/null +++ b/source/io/SampleSource.h @@ -0,0 +1,94 @@ +// +// SampleSource.h - MrsWatson +// Created by Nik Reiman on 1/2/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. +// + +#ifndef MrsWatson_SampleSource_h +#define MrsWatson_SampleSource_h + +#include "audio/SampleBuffer.h" +#include "base/CharString.h" +#include "base/Types.h" + +typedef enum { + SAMPLE_SOURCE_TYPE_INVALID, + SAMPLE_SOURCE_TYPE_SILENCE, + SAMPLE_SOURCE_TYPE_PCM, + SAMPLE_SOURCE_TYPE_AIFF, + SAMPLE_SOURCE_TYPE_FLAC, + SAMPLE_SOURCE_TYPE_MP3, + SAMPLE_SOURCE_TYPE_OGG, + SAMPLE_SOURCE_TYPE_WAVE, + NUM_SAMPLE_SOURCES +} SampleSourceType; + +typedef enum { + SAMPLE_SOURCE_OPEN_NOT_OPENED, + SAMPLE_SOURCE_OPEN_READ, + SAMPLE_SOURCE_OPEN_WRITE, + NUM_SAMPLE_SOURCE_OPEN_AS +} SampleSourceOpenAs; + +typedef boolByte (*OpenSampleSourceFunc)(void *, const SampleSourceOpenAs); +typedef boolByte (*ReadSampleBlockFunc)(void *, SampleBuffer); +typedef boolByte (*WriteSampleBlockFunc)(void *, const SampleBuffer); +typedef void (*CloseSampleSourceFunc)(void *); +typedef void (*FreeSampleSourceDataFunc)(void *); + +typedef struct { + SampleSourceType sampleSourceType; + SampleSourceOpenAs openedAs; + CharString sourceName; + unsigned long numSamplesProcessed; + + OpenSampleSourceFunc openSampleSource; + ReadSampleBlockFunc readSampleBlock; + WriteSampleBlockFunc writeSampleBlock; + CloseSampleSourceFunc closeSampleSource; + FreeSampleSourceDataFunc freeSampleSourceData; + + void *extraData; +} SampleSourceMembers; +typedef SampleSourceMembers *SampleSource; + +/** + * Factory method to create a new sample source + * @param sampleSourceName Source name. If NULL, then a silent sample source is created. + * @return Initialized sample source, or NULL if none could be created + */ +SampleSource sampleSourceFactory(const CharString sampleSourceName); + +/** + * Print a list of all supported sample source pipes to the log + */ +void sampleSourcePrintSupportedTypes(void); + +/** + * Release a sample source and associated resources + * @param self + */ +void freeSampleSource(SampleSource self); + +#endif 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 diff --git a/source/io/SampleSourceAudiofile.h b/source/io/SampleSourceAudiofile.h new file mode 100644 index 0000000..3dcf146 --- /dev/null +++ b/source/io/SampleSourceAudiofile.h @@ -0,0 +1,47 @@ +// +// SampleSourceAudiofile.h - 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. +// + +#ifndef MrsWatson_SampleSourceAudiofile_h +#define MrsWatson_SampleSourceAudiofile_h + +#if USE_AUDIOFILE +#include <audiofile.h> + +// This isn't a real SampleSource class, but rather a base class to facilitate +// in reading and writing any file supported via the audiofile library. As each +// format has slightly different methods for opening and configuring them, those +// are defined in the individual subclasses. + +typedef struct { + AFfilehandle fileHandle; + short *pcmBuffer; +} SampleSourceAudiofileDataMembers; + +typedef SampleSourceAudiofileDataMembers *SampleSourceAudiofileData; + +#endif +#endif diff --git a/source/io/SampleSourcePcm.c b/source/io/SampleSourcePcm.c new file mode 100644 index 0000000..f0be1e8 --- /dev/null +++ b/source/io/SampleSourcePcm.c @@ -0,0 +1,221 @@ +// +// SampleSourcePcm.c - MrsWatson +// Created by Nik Reiman on 1/2/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. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "audio/AudioSettings.h" +#include "base/PlatformInfo.h" +#include "io/SampleSourcePcm.h" +#include "logging/EventLogger.h" + +static boolByte openSampleSourcePcm(void *sampleSourcePtr, const SampleSourceOpenAs openAs) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)(sampleSource->extraData); + + extraData->dataBufferNumItems = 0; + + if (openAs == SAMPLE_SOURCE_OPEN_READ) { + if (charStringIsEqualToCString(sampleSource->sourceName, "-", false)) { + extraData->fileHandle = stdin; + charStringCopyCString(sampleSource->sourceName, "stdin"); + extraData->isStream = true; + } else { + extraData->fileHandle = fopen(sampleSource->sourceName->data, "rb"); + } + } else if (openAs == SAMPLE_SOURCE_OPEN_WRITE) { + if (charStringIsEqualToCString(sampleSource->sourceName, "-", false)) { + extraData->fileHandle = stdout; + charStringCopyCString(sampleSource->sourceName, "stdout"); + extraData->isStream = true; + } else { + extraData->fileHandle = fopen(sampleSource->sourceName->data, "wb"); + } + } else { + logInternalError("Invalid type for openAs in PCM file"); + return false; + } + + if (extraData->fileHandle == NULL) { + logError("PCM 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; +} + +size_t sampleSourcePcmRead(SampleSourcePcmData self, SampleBuffer sampleBuffer) +{ + size_t pcmSamplesRead = 0; + + if (self == NULL || self->fileHandle == NULL) { + logCritical("Corrupt PCM data structure"); + return 0; + } + + if (self->dataBufferNumItems < (size_t)(sampleBuffer->numChannels * sampleBuffer->blocksize)) { + self->dataBufferNumItems = (size_t)(sampleBuffer->numChannels * sampleBuffer->blocksize); + self->interlacedPcmDataBuffer = (short *)realloc(self->interlacedPcmDataBuffer, sizeof(short) * self->dataBufferNumItems); + } + + // Clear the PCM data buffer, or else the last block will have dirty samples in the end + memset(self->interlacedPcmDataBuffer, 0, sizeof(short) * self->dataBufferNumItems); + + pcmSamplesRead = fread(self->interlacedPcmDataBuffer, sizeof(short), self->dataBufferNumItems, self->fileHandle); + + if (pcmSamplesRead < self->dataBufferNumItems) { + logDebug("End of PCM file reached"); + // Set the blocksize of the sample buffer to be the number of frames read + sampleBuffer->blocksize = pcmSamplesRead / sampleBuffer->numChannels; + } + + logDebug("Read %d samples from PCM file", pcmSamplesRead); + + sampleBufferCopyPcmSamples(sampleBuffer, self->interlacedPcmDataBuffer); + return pcmSamplesRead; +} + +static boolByte readBlockFromPcmFile(void *sampleSourcePtr, SampleBuffer sampleBuffer) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)(sampleSource->extraData); + SampleCount originalBlocksize = sampleBuffer->blocksize; + size_t samplesRead = sampleSourcePcmRead(extraData, sampleBuffer); + sampleSource->numSamplesProcessed += samplesRead; + return (boolByte)(originalBlocksize == sampleBuffer->blocksize); +} + +size_t sampleSourcePcmWrite(SampleSourcePcmData self, const SampleBuffer sampleBuffer) +{ + size_t pcmSamplesWritten = 0; + size_t numSamplesToWrite = (size_t)(sampleBuffer->numChannels * sampleBuffer->blocksize); + + if (self == NULL || self->fileHandle == NULL) { + logCritical("Corrupt PCM data structure"); + return false; + } + + if (self->dataBufferNumItems < (size_t)(sampleBuffer->numChannels * sampleBuffer->blocksize)) { + self->dataBufferNumItems = (size_t)(sampleBuffer->numChannels * sampleBuffer->blocksize); + self->interlacedPcmDataBuffer = (short *)realloc(self->interlacedPcmDataBuffer, sizeof(short) * self->dataBufferNumItems); + } + + // Clear the PCM data buffer just to be safe + memset(self->interlacedPcmDataBuffer, 0, sizeof(short) * self->dataBufferNumItems); + + boolByte isLittleEndian = (boolByte)(self->isLittleEndian != platformInfoIsLittleEndian()); + sampleBufferGetPcmSamples(sampleBuffer, self->interlacedPcmDataBuffer, isLittleEndian); + pcmSamplesWritten = fwrite(self->interlacedPcmDataBuffer, sizeof(short), numSamplesToWrite, self->fileHandle); + + if (pcmSamplesWritten < numSamplesToWrite) { + logWarn("Short write to PCM file"); + return pcmSamplesWritten; + } + + logDebug("Wrote %d samples to PCM file", pcmSamplesWritten); + return pcmSamplesWritten; +} + +static boolByte writeBlockToPcmFile(void *sampleSourcePtr, const SampleBuffer sampleBuffer) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)(sampleSource->extraData); + unsigned int samplesWritten = (int)sampleSourcePcmWrite(extraData, sampleBuffer); + sampleSource->numSamplesProcessed += samplesWritten; + return (boolByte)(samplesWritten == sampleBuffer->blocksize); +} + +static void _closeSampleSourcePcm(void *sampleSourcePtr) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; + + if (extraData->fileHandle != NULL) { + fclose(extraData->fileHandle); + } +} + +void sampleSourcePcmSetSampleRate(void *sampleSourcePtr, SampleRate sampleRate) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; + extraData->sampleRate = (unsigned int)sampleRate; +} + +void sampleSourcePcmSetNumChannels(void *sampleSourcePtr, int numChannels) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; + extraData->numChannels = (unsigned short)numChannels; +} + +void freeSampleSourceDataPcm(void *sampleSourceDataPtr) +{ + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSourceDataPtr; + + if (extraData->interlacedPcmDataBuffer != NULL) { + free(extraData->interlacedPcmDataBuffer); + extraData->interlacedPcmDataBuffer = NULL; + } + + free(extraData); +} + +SampleSource _newSampleSourcePcm(const CharString sampleSourceName) +{ + SampleSource sampleSource = (SampleSource)malloc(sizeof(SampleSourceMembers)); + SampleSourcePcmData extraData = (SampleSourcePcmData)malloc(sizeof(SampleSourcePcmDataMembers)); + + sampleSource->sampleSourceType = SAMPLE_SOURCE_TYPE_PCM; + sampleSource->openedAs = SAMPLE_SOURCE_OPEN_NOT_OPENED; + sampleSource->sourceName = newCharString(); + charStringCopy(sampleSource->sourceName, sampleSourceName); + sampleSource->numSamplesProcessed = 0; + + sampleSource->openSampleSource = openSampleSourcePcm; + sampleSource->readSampleBlock = readBlockFromPcmFile; + sampleSource->writeSampleBlock = writeBlockToPcmFile; + sampleSource->closeSampleSource = _closeSampleSourcePcm; + sampleSource->freeSampleSourceData = freeSampleSourceDataPcm; + + extraData->isStream = false; + extraData->isLittleEndian = true; + extraData->fileHandle = NULL; + extraData->dataBufferNumItems = 0; + extraData->interlacedPcmDataBuffer = NULL; + + extraData->numChannels = (unsigned short)getNumChannels(); + extraData->sampleRate = (unsigned int)getSampleRate(); + extraData->bitsPerSample = 16; + sampleSource->extraData = extraData; + + return sampleSource; +} diff --git a/source/io/SampleSourcePcm.h b/source/io/SampleSourcePcm.h new file mode 100644 index 0000000..0dfe789 --- /dev/null +++ b/source/io/SampleSourcePcm.h @@ -0,0 +1,92 @@ +// +// SampleSourcePcm.h - MrsWatson +// Created by Nik Reiman on 1/2/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. +// + +#ifndef MrsWatson_InputSourcePcm_h +#define MrsWatson_InputSourcePcm_h + +#include <stdio.h> + +#include "io/SampleSource.h" + +typedef struct { + boolByte isStream; + boolByte isLittleEndian; + FILE *fileHandle; + size_t dataBufferNumItems; + short *interlacedPcmDataBuffer; + + ChannelCount numChannels; + SampleRate sampleRate; + unsigned short bitsPerSample; +} SampleSourcePcmDataMembers; +typedef SampleSourcePcmDataMembers *SampleSourcePcmData; +// this struct is actually the structure we need, looks like name just redefined later. sampleRate, numChannels, need to check those. +// ok awesome...so basically in order to check classes/structs, sometimes you have to look in the header file? yeah all structs and enums will be in header file +// and what is an enum (compared to a struct?) enumeration of possible values for one variable. like TYPE_MP3 TYPE_WAV etc. weird ok no worries + +/** + * Read raw PCM data to a floating-point sample buffer + * @param self + * @param sampleBuffer + * @return Number of samples read + * http://ghghgh.us/VST/ + * this documentation is still a bit tough for me to read + */ +size_t sampleSourcePcmRead(SampleSourcePcmData self, SampleBuffer sampleBuffer); + +/** + * Writes data from a sample buffer to a PCM output + * @param self + * @param sampleBuffer + * @return Number of samples written + */ +size_t sampleSourcePcmWrite(SampleSourcePcmData self, const SampleBuffer sampleBuffer); + +/** + * Set the sample rate to be used for raw PCM file operations. This is most + * relevant when writing a WAVE or a AIFF file, as the sample rate must be given + * in the file header. + * @param sampleSourcePtr + * @param sampleRate Sample rate, in Hertz + */ +void sampleSourcePcmSetSampleRate(void *sampleSourcePtr, double sampleRate); + +/** + * Set the number of channels to be used for raw PCM file operations. Like the + * sample, this is most relevant when writing a WAVE or a AIFF file. + * @param sampleSourcePtr + * @param numChannels Number of channels + */ +void sampleSourcePcmSetNumChannels(void *sampleSourcePtr, int numChannels); + +/** + * Free a PCM sample source and all associated data + * @param sampleSourceDataPtr Pointer to sample source data + */ +void freeSampleSourceDataPcm(void *sampleSourceDataPtr); + +#endif diff --git a/source/io/SampleSourceSilence.c b/source/io/SampleSourceSilence.c new file mode 100644 index 0000000..37136e2 --- /dev/null +++ b/source/io/SampleSourceSilence.c @@ -0,0 +1,84 @@ +// +// SampleSourceSilence.c - MrsWatson +// Created by Nik Reiman on 1/5/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. +// + +#include <stdio.h> +#include <stdlib.h> + +#include "audio/AudioSettings.h" +#include "io/SampleSource.h" +#include "logging/EventLogger.h" + +static boolByte _openSampleSourceSilence(void *sampleSourcePtr, const SampleSourceOpenAs openAs) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + sampleSource->openedAs = openAs; + return true; +} + +static void _closeSampleSourceSilence(void *sampleSourcePtr) +{ +} + +static boolByte _readBlockFromSilence(void *sampleSourcePtr, SampleBuffer sampleBuffer) +{ + unsigned long samplesRead = sampleBuffer->blocksize * sampleBuffer->numChannels; + sampleBufferClear(sampleBuffer); + ((SampleSource)sampleSourcePtr)->numSamplesProcessed += sampleBuffer->blocksize * sampleBuffer->numChannels; + logDebug("Read %d samples from silence source", samplesRead); + return true; +} + +static boolByte _writeBlockToSilence(void *sampleSourcePtr, const SampleBuffer sampleBuffer) +{ + unsigned long samplesWritten = sampleBuffer->blocksize * sampleBuffer->numChannels; + ((SampleSource)sampleSourcePtr)->numSamplesProcessed += samplesWritten; + logDebug("Wrote %d samples to silence source", samplesWritten); + return true; +} + +static void _freeInputSourceDataSilence(void *sampleSourceDataPtr) +{ +} + +SampleSource _newSampleSourceSilence(void) +{ + SampleSource sampleSource = (SampleSource)malloc(sizeof(SampleSourceMembers)); + + sampleSource->sampleSourceType = SAMPLE_SOURCE_TYPE_SILENCE; + sampleSource->openedAs = SAMPLE_SOURCE_OPEN_NOT_OPENED; + sampleSource->sourceName = newCharString(); + charStringCopyCString(sampleSource->sourceName, "(silence)"); + sampleSource->numSamplesProcessed = 0; + + sampleSource->openSampleSource = _openSampleSourceSilence; + sampleSource->closeSampleSource = _closeSampleSourceSilence; + sampleSource->readSampleBlock = _readBlockFromSilence; + sampleSource->writeSampleBlock = _writeBlockToSilence; + sampleSource->freeSampleSourceData = _freeInputSourceDataSilence; + + return sampleSource; +} diff --git a/source/io/SampleSourceSilence.h b/source/io/SampleSourceSilence.h new file mode 100644 index 0000000..d4b793f --- /dev/null +++ b/source/io/SampleSourceSilence.h @@ -0,0 +1,33 @@ +// +// SampleSourceSilence.h - MrsWatson +// Created by Nik Reiman on 1/5/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. +// + +#ifndef MrsWatson_SampleSourceSilence_h +#define MrsWatson_SampleSourceSilence_h + +// SampleSourceSilent contains only private functions + +#endif diff --git a/source/io/SampleSourceWave.c b/source/io/SampleSourceWave.c new file mode 100644 index 0000000..6e1442d --- /dev/null +++ b/source/io/SampleSourceWave.c @@ -0,0 +1,499 @@ +// +// SampleSourceWave.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. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "audio/AudioSettings.h" +#include "base/Endian.h" +#include "io/RiffFile.h" +#include "io/SampleSource.h" +#include "io/SampleSourcePcm.h" +#include "logging/EventLogger.h" + +static boolByte _readWaveFileInfo(const char *filename, SampleSourcePcmData extraData) +{ + int chunkOffset = 0; + RiffChunk chunk = newRiffChunk(); + char format[4]; + size_t itemsRead; + unsigned int audioFormat; + unsigned int byteRate; + unsigned int expectedByteRate; + unsigned int blockAlign; + unsigned int expectedBlockAlign; + + if (riffChunkReadNext(chunk, extraData->fileHandle, false)) { + if (!riffChunkIsIdEqualTo(chunk, "RIFF")) { + logFileError(filename, "Invalid RIFF chunk descriptor"); + freeRiffChunk(chunk); + return false; + } + + // The WAVE file format has two sub-chunks, with the size of both calculated in the size field. Before + // either of the subchunks, there are an extra 4 bytes which indicate the format type. We need to read + // that before either of the subchunks can be parsed. + itemsRead = fread(format, sizeof(byte), 4, extraData->fileHandle); + + if (itemsRead != 4 || strncmp(format, "WAVE", 4)) { + logFileError(filename, "Invalid format description"); + freeRiffChunk(chunk); + return false; + } + } else { + logFileError(filename, "No chunks following descriptor"); + freeRiffChunk(chunk); + return false; + } + + if (riffChunkReadNext(chunk, extraData->fileHandle, true)) { + if (!riffChunkIsIdEqualTo(chunk, "fmt ")) { + logError(filename, "Invalid format chunk header"); + freeRiffChunk(chunk); + return false; + } + + audioFormat = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); + chunkOffset += 2; + if (audioFormat != 1) { + logError("WAVE file with audio format %d is not supported", audioFormat); + freeRiffChunk(chunk); + return false; + } + + extraData->numChannels = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); + chunkOffset += 2; + setNumChannels(extraData->numChannels); + + extraData->sampleRate = convertByteArrayToUnsignedInt(chunk->data + chunkOffset); + chunkOffset += 4; + setSampleRate(extraData->sampleRate); + + byteRate = convertByteArrayToUnsignedInt(chunk->data + chunkOffset); + chunkOffset += 4; + + blockAlign = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); + chunkOffset += 2; + + extraData->bitsPerSample = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); + + if (extraData->bitsPerSample > 16) { + logUnsupportedFeature("Bitrates greater than 16"); + freeRiffChunk(chunk); + return false; + } else if (extraData->bitsPerSample < 16) { + logUnsupportedFeature("Bitrates lower than 16"); + freeRiffChunk(chunk); + return false; + } + + expectedByteRate = extraData->sampleRate * extraData->numChannels * extraData->bitsPerSample / 8; + + if (expectedByteRate != byteRate) { + logWarn("Possibly invalid bitrate %d, expected %d", byteRate, expectedByteRate); + } + + expectedBlockAlign = (unsigned int)(extraData->numChannels * extraData->bitsPerSample / 8); + + if (expectedBlockAlign != blockAlign) { + logWarn("Possibly invalid block align %d, expected %d", blockAlign, expectedBlockAlign); + } + } else { + logFileError(filename, "WAVE file has no chunks following format"); + freeRiffChunk(chunk); + return false; + } + + // We don't need the format data anymore, so free and re-alloc the chunk to avoid a small memory leak + freeRiffChunk(chunk); + chunk = newRiffChunk(); + + if (riffChunkReadNext(chunk, extraData->fileHandle, false)) { + if (!riffChunkIsIdEqualTo(chunk, "data")) { + logFileError(filename, "WAVE file has invalid data chunk header"); + freeRiffChunk(chunk); + return false; + } + + logDebug("WAVE file has %d bytes", chunk->size); + } + + freeRiffChunk(chunk); + return true; +} + +// here it writes wav, sample data is rom SampleSourcePcmData +//any idea what that is? should be filled somewhere in read or init sections. basically one class to hold any audio data, regardless of format wav\mp3 etc. +//ok it's basically a struct, right? yes +//ok how would you debug something like that in C? we can add couple of printf with data from this struct, to see what' going into wav file well we +//can try it, but better to ask you, how did you find this section of the script? I'm having trouble reading this code because I don't +//entirely understand how C stuff is organized vs other scripting languages +//well it's same everywhere, generally each section of program is located in separate file, so for example input output function will be in one file, +//audio specific will be in another etc. i just checked whole folder structure to get overall picture of program and then can go fro one place to another to +//see how it works.and mrswatson.c? is that sort of going to be the file that ties all modules together? yep usally called as main.c, but here it's name of program. . +//and that's common too, right? to name it after the program? yes especially then there is several binaries. ahh like the 64bit version, vs 32bit? yep +//ok and should I expect some of the modules to pull in modules of their own? yeors ok so first look in main.c, then how did you find this particular function/method? +//so in main file there was very generic function like writeoutput, but i knew that it should write wav structure somehow, so there should be another module to do that, +//checked "io" folder and found SOurceSampleWav.c, and then this function in particular. did you see a reference to the io folder in main.c? no just a guess +//ok so the one thing that makes C a little trickier than python/perl or something is the header files, do you ever look in those? nope, they just help compile, +//not have much useful ssto uff so the headers can actually be stored in this file as well, it's just an option to keep them separate and make the code shorter? +//not really, they used in other files, so we don't have to write everytime prototpe of used function in every file which use them....so you're saying the compiler +//won't compile things if it sees a keyword it doesn't recognize, and that's why it needs to load all headers first? right, either prototype or whole function +//so the header files are necessary because the script is broken up into different sections, if it was all in one file, it would compile fine? yeah, bt still some issues +//with order of functions, because functon should be before call to the function. so there would still need to be headers for each keyword at the top of the file? yeah +//ok this is not as complicated as I thought well lets look briefly at that, and then I think I've learned enough to fiddle with this on my own. Not sure omnisphere itself +//is worth my time, but mrswatson seems to be good +//so how do we find SampleSourcePcmData's struct definition? just with grep looks like its all in this file? +//so we need to find where SampleSourcePcmData is loaded no need, print oh yeah, we know that f write correct data to it terminal so it's onlt in ths function right yes, I remember now. +// +// +// +//It seems that part of the problem with mrswatson is that the midi files I was feeding it were missing parameters, and mrswatson +//doesn't have defaults for those ( like tempo, and sampleRate)? yeah ok c couoold bel +// +//there is a standard for midi called General Midi, it's a bit weird, I guess I'll read about it some more +// +// +// +static boolByte _writeWaveFileInfo(SampleSourcePcmData extraData) +{ + //so how do we know whether samplerate is something we can print with printf? couldn't that be a struct too? need to check definition too + ////how would we access SampleRate here from extraData? + // extraData->sampleRate * extraData->numChannels * extraData->bitsPerSample + // ok so similar syntax to perl? yep and because you cannot multiply structs, we should just go straight to printf? right + printf("SampleRate: %f", extraData->sampleRate); //like that? why can't I cast the value as a string? there is no casts in C for strings and any other types, + printf("NumChannels: %d", extraData->numChannels); //like that? why can't I cast the value as a string? there is no casts in C for strings and any other types, + //so we should build this now? yep + + RiffChunk chunk = newRiffChunk(); + unsigned short audioFormat = 1; + unsigned int byteRate = extraData->sampleRate * extraData->numChannels * extraData->bitsPerSample / 8; + printf("byteRate: %d\n", byteRate); //I should test this next? i guess we need to compare original file from mrswatson and sox fixed file + unsigned short blockAlign = (unsigned short)(extraData->numChannels * extraData->bitsPerSample / 8); + unsigned int extraParams = 0; + + memcpy(chunk->id, "RIFF", 4); //ok so the 4 here stands for 4 bytes, right? yep what is memcpy doing here exactly? copy from one memory region to another + //so it is copying "RIFF" to chunk->id ? yes but it doesn't take the memory address or pointer as an argument? chunk->id is pointer. ok is it a pointer + //to the location of the chunk? most likely it's allocated inside newRiffChunk function. code here is a bit junky, could be written better + //what if it were memcpy(chunk->id, "RIFF", sizeof("RIFF")); would that be the same? should be, but size of string is larger by one byte for \0, not sure + //oh in this case, would it copy only the first 4 bytes of "RIFF"? yes ok + //writes the letters "RIFF" + if (fwrite(chunk->id, sizeof(byte), 4, extraData->fileHandle) != 4) { + logError("Could not write RIFF header"); + freeRiffChunk(chunk); + return false; + } + + // Write the size, but this will need to be set again when the file is finished writing + //chunk->size here is wrong!! FIXME + if (fwrite(&(chunk->size), sizeof(unsigned int), 1, extraData->fileHandle) != 1) { + logError("Could not write RIFF chunk size"); + freeRiffChunk(chunk); + return false; + } + + memcpy(chunk->id, "WAVE", 4); + //writes the letters "WAVE" + if (fwrite(chunk->id, sizeof(byte), 4, extraData->fileHandle) != 4) { + logError("Could not WAVE format"); + freeRiffChunk(chunk); + return false; + } + + // Write the format header + memcpy(chunk->id, "fmt ", 4); + chunk->size = 20; + + //writes the letters "fmt " + //0 + if (fwrite(chunk->id, sizeof(byte), 4, extraData->fileHandle) != 4) { + logError("Could not write format header"); + freeRiffChunk(chunk); + return false; + } + + //This is wrong!! FIXME this didn't line up with that document, but I was a bit confused because there are 6 of these one byte statements, should be four I thought + //it's not one byte, siz efof(unsigned int) should be 4 anyway structure of wav is correct, we only need to check sampleRate output well this doesn't line up with + //what we got from sox + //0 + if (fwrite(&(chunk->size), sizeof(unsigned int), 1, extraData->fileHandle) != 1) { + logError("Could not write format chunk size"); + freeRiffChunk(chunk); + return false; + } + + // NOTE: These calls will not work on big-endian platforms + //4 + if (fwrite(&audioFormat, sizeof(unsigned short), 1, extraData->fileHandle) != 1) { + logError("Could not write audio format"); + freeRiffChunk(chunk); + return false; + } + + //6 + if (fwrite(&(extraData->numChannels), sizeof(unsigned short), 1, extraData->fileHandle) != 1) { + logError("Could not write channel count"); + freeRiffChunk(chunk); + return false; + } + + //8 + int sampleRateInt = (int)extraData->sampleRate; //should we compile it again? not yet + if (fwrite(&sampleRateInt, sizeof(unsigned int), 1, extraData->fileHandle) != 1) { + logError("Could not write sample rate"); + freeRiffChunk(chunk); + return false; + } + + //12 + if (fwrite(&(byteRate), sizeof(unsigned int), 1, extraData->fileHandle) != 1) { + logError("Could not write byte rate"); + freeRiffChunk(chunk); + return false; + } + + //16 + if (fwrite(&(blockAlign), sizeof(unsigned short), 1, extraData->fileHandle) != 1) { + logError("Could not write block align"); + freeRiffChunk(chunk); + return false; + } + + //18 + if (fwrite(&(extraData->bitsPerSample), sizeof(unsigned short), 1, extraData->fileHandle) != 1) { + logError("Could not write bits per sample"); + freeRiffChunk(chunk); + return false; + } + + if (fwrite(&(extraParams), sizeof(byte), 4, extraData->fileHandle) != 4) { + logError("Could not write extra PCM parameters"); + freeRiffChunk(chunk); + return false; + } + + memcpy(chunk->id, "data", 4);// don't see how can data not be written into fe, weird zeroes. + + if (fwrite(chunk->id, sizeof(byte), 4, extraData->fileHandle) != 4) { + logError("Could not write format header"); + freeRiffChunk(chunk); + return false; + } + + if (fwrite(&(chunk->size), sizeof(unsigned int), 1, extraData->fileHandle) != 1) { + logError("Could not write data chunk size"); + freeRiffChunk(chunk); + return false; + } + + freeRiffChunk(chunk); + return true; +} + +static boolByte _openSampleSourceWave(void *sampleSourcePtr, const SampleSourceOpenAs openAs) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; + + if (openAs == SAMPLE_SOURCE_OPEN_READ) { + extraData->fileHandle = fopen(sampleSource->sourceName->data, "rb"); + + if (extraData->fileHandle != NULL) { + if (_readWaveFileInfo(sampleSource->sourceName->data, extraData)) { + setNumChannels(extraData->numChannels); + setSampleRate(extraData->sampleRate); + } else { + fclose(extraData->fileHandle); + extraData->fileHandle = NULL; + } + } + } else if (openAs == SAMPLE_SOURCE_OPEN_WRITE) { + extraData->fileHandle = fopen(sampleSource->sourceName->data, "wb"); + + if (extraData->fileHandle != NULL) { + extraData->numChannels = (unsigned short)getNumChannels(); + extraData->sampleRate = (unsigned int)getSampleRate(); + extraData->bitsPerSample = 16; + + if (!_writeWaveFileInfo(extraData)) { + fclose(extraData->fileHandle); + extraData->fileHandle = NULL; + } + } + } else { + logInternalError("Invalid type for openAs in WAVE file"); + return false; + } + + if (extraData->fileHandle == NULL) { + logError("WAVE 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; +} + +static boolByte _readBlockFromWaveFile(void *sampleSourcePtr, SampleBuffer sampleBuffer) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; + unsigned long originalBlocksize = sampleBuffer->blocksize; + size_t samplesRead = sampleSourcePcmRead(extraData, sampleBuffer); + sampleSource->numSamplesProcessed += samplesRead; + return (boolByte)(originalBlocksize == sampleBuffer->blocksize); +} + +static boolByte _writeBlockToWaveFile(void *sampleSourcePtr, const SampleBuffer sampleBuffer) +{ + SampleSource sampleSource = (SampleSource)sampleSourcePtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; + unsigned int samplesWritten = (int)sampleSourcePcmWrite(extraData, sampleBuffer); + sampleSource->numSamplesProcessed += samplesWritten; + return (boolByte)(samplesWritten == sampleBuffer->blocksize); +} + +void _closeSampleSourceWave(void *sampleSourceDataPtr) +{ + SampleSource sampleSource = (SampleSource)sampleSourceDataPtr; + SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; + size_t numBytesWritten; + RiffChunk chunk; + + if (sampleSource->openedAs == SAMPLE_SOURCE_OPEN_WRITE) { + // Re-open the file for editing + fflush(extraData->fileHandle); + + if (fclose(extraData->fileHandle) != 0) { + logError("Could not close WAVE file for finalization"); + return; + } + + extraData->fileHandle = fopen(sampleSource->sourceName->data, "rb+"); + + if (extraData->fileHandle == NULL) { + logError("Could not reopen WAVE file for finalization"); + return; + } + + // First go to the second chunk in the file and re-read the chunk length //is it possible that it is altered again at the end? yep + if (fseek(extraData->fileHandle, 12, SEEK_SET) != 0) { + logError("Could not seek to second chunk during WAVE file finalization"); + fclose(extraData->fileHandle); + return; + } + + chunk = newRiffChunk(); + + if (!riffChunkReadNext(chunk, extraData->fileHandle, false)) { + logError("Could not read RIFF chunk during WAVE file finalization"); + fclose(extraData->fileHandle); + freeRiffChunk(chunk); + return; + } + + // Go to the next chunk, and then skip the type and write the new length //should I test it again? not yet, still need to fix this function code + // should we go back and undo some of the things from before? no need, it looks closer to sox output each time + // need to find out which line of code writes 000000 instead of data, try commenting out fwrites here and compile + // so this one fix 1400 0000 part anything else we need? + if (fseek(extraData->fileHandle, (long)(chunk->size + 4), SEEK_CUR) != 0) { + logError("Could not seek to next chunk during WAVE file finalization"); + fclose(extraData->fileHandle); + freeRiffChunk(chunk); + return; + } + + numBytesWritten = sampleSource->numSamplesProcessed * extraData->bitsPerSample / 8; + + if (fwrite(&numBytesWritten, sizeof(unsigned int), 1, extraData->fileHandle) != 1) { //somwhere here it get overwritten + logError("Could not write WAVE file size during finalization"); + fclose(extraData->fileHandle); + freeRiffChunk(chunk); + return; + } +//ok should be fine now + // ok, I should also write a shorter midi file for us to use for testing. I should be able to modifiy this one and take out a lot of the note events.ok +// should I do that now? (in a different screen?) yeah + // if I write this comment, should it work? might not, depends on how cmake is smart + + // Add 40 bytes for fmt chunk size and write the RIFF chunk size + numBytesWritten += ftell(extraData->fileHandle) - 8; + + if (fseek(extraData->fileHandle, 4, SEEK_SET) != 0) { + logError("Could not seek to fmt chunk during WAVE file finalization"); + fclose(extraData->fileHandle); + freeRiffChunk(chunk); + return; + } + + if (fwrite(&numBytesWritten, sizeof(unsigned int), 1, extraData->fileHandle) != 1) { + logError("Could not write WAVE file size in fmt chunk during finalization"); + fclose(extraData->fileHandle); + freeRiffChunk(chunk); + return; + } + + fflush(extraData->fileHandle); + fclose(extraData->fileHandle); + freeRiffChunk(chunk); + } else if (sampleSource->openedAs == SAMPLE_SOURCE_OPEN_READ && extraData->fileHandle != NULL) { + fclose(extraData->fileHandle); + } +} + +SampleSource _newSampleSourceWave(const CharString sampleSourceName) +{ + SampleSource sampleSource = (SampleSource)malloc(sizeof(SampleSourceMembers)); + SampleSourcePcmData extraData = (SampleSourcePcmData)malloc(sizeof(SampleSourcePcmDataMembers)); + + sampleSource->sampleSourceType = SAMPLE_SOURCE_TYPE_WAVE; + sampleSource->openedAs = SAMPLE_SOURCE_OPEN_NOT_OPENED; + sampleSource->sourceName = newCharString(); + charStringCopy(sampleSource->sourceName, sampleSourceName); + sampleSource->numSamplesProcessed = 0; + + sampleSource->openSampleSource = _openSampleSourceWave; + sampleSource->readSampleBlock = _readBlockFromWaveFile; + sampleSource->writeSampleBlock = _writeBlockToWaveFile; + sampleSource->closeSampleSource = _closeSampleSourceWave; + sampleSource->freeSampleSourceData = freeSampleSourceDataPcm; + + extraData->isStream = false; + extraData->isLittleEndian = true; + extraData->fileHandle = NULL; + extraData->dataBufferNumItems = 0; + extraData->interlacedPcmDataBuffer = NULL; + + extraData->numChannels = (unsigned short)getNumChannels(); + extraData->sampleRate = (unsigned int)getSampleRate(); + extraData->bitsPerSample = 16; + + sampleSource->extraData = extraData; + return sampleSource; +} diff --git a/source/io/SampleSourceWave.h b/source/io/SampleSourceWave.h new file mode 100644 index 0000000..102fd5e --- /dev/null +++ b/source/io/SampleSourceWave.h @@ -0,0 +1,33 @@ +// +// SampleSourceWave.h - 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. +// + +#ifndef MrsWatson_SampleSourceWave_h +#define MrsWatson_SampleSourceWave_h + +// SampleSourceWave has only private functions + +#endif |
