summaryrefslogtreecommitdiff
path: root/source/io/SampleSourcePcm.c
diff options
context:
space:
mode:
authorpepper <peppersclothescult@gmail.com>2015-01-10 21:32:32 -0800
committerpepper <peppersclothescult@gmail.com>2015-01-10 21:32:32 -0800
commitd53fa8a169832563c62262078b8d2ffe5cab8473 (patch)
treeb911d06d357d009c976709780f10e92ce915228a /source/io/SampleSourcePcm.c
first
Diffstat (limited to 'source/io/SampleSourcePcm.c')
-rw-r--r--source/io/SampleSourcePcm.c221
1 files changed, 221 insertions, 0 deletions
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;
+}