Move dead submodules in-tree

Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
swurl 2025-05-31 02:33:02 -04:00
parent c0cceff365
commit 6c655321e6
Signed by: crueter
GPG key ID: A5A7629F109C8FD1
4081 changed files with 1185566 additions and 45 deletions

View file

@ -0,0 +1,52 @@
**parselib**
==========
Classes for parsing and loading audio data.
## Abstract
(Oboe) **parselib** contains facilities for reading and loading audio data from streams. Streams can be wrapped around either files or memory blocks.
**parselib** is written in C++ and is intended to be called from Android native code. It is implemented as a static library.
## Supported Encodings
* Microsoft WAV format
## **parselib** project structure
* stream
Contains classes related to reading audio data from a stream abstraction
* wav
Contains classes to read/load audio data in WAV format
## **stream** Classes
### InputStream
An abstract class that defines the `InputStream` interface.
### FileInputStream
A concrete implementation of `InputStream` that reads data from a file.
### MemInputStream
A concrete implementation of `InputStream` that reads data from a memory block.
## **wav** Classes
Contains classes to read/load audio data in WAV format. WAV format files are "Microsoft Resource Interchange File Format" (RIFF) files. WAV files contain a variety of RIFF "chunks", but only a few are required (see 'Chunk' classes below)
### Utility
#### AudioEncoding
Defines constants for various audio encodings
### WavTypes
Support for **RIFF** file types and managing FOURCC data.
### WAV Data I/O
#### WavStreamReader
Parses and loads WAV data from an InputStream.
### WAV Data
#### WavChunkHeader
Defines common fields and operations for all WAV format RIFF Chunks.
#### WavFmtChunkHeader
Defines fields and operations for RIFF '`fmt `' chunks
#### WavRIFFChunkHeader
Defines fields and operations for RIFF '`data`' chunks

View file

@ -0,0 +1,33 @@
apply plugin: 'com.android.library'
android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 35
compileSdkVersion 35
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_18
targetCompatibility JavaVersion.VERSION_18
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
namespace 'com.google.oboe.samples'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.7.0'
}

View file

@ -0,0 +1 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />

View file

@ -0,0 +1,64 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
#PROJECT(wavlib C CXX)
#message("CMAKE_CURRENT_LIST_DIR = " ${CMAKE_CURRENT_LIST_DIR})
#message("HOME is " ${HOME})
# SET(NDK "")
#message("NDK is " ${NDK})
# compiler flags
# -mhard-float -D_NDK_MATH_NO_SOFTFP=1
#SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mhard-float -D_NDK_MATH_NO_SOFTFP=1" )
# Set the path to the Oboe library directory
set (OBOE_DIR ../../../../../)
message("OBOE_DIR = " + ${OBOE_DIR})
#add_subdirectory(${OBOE_DIR} ./oboe-bin)
# include folders
include_directories(
${OBOE_DIR}/include
${CMAKE_CURRENT_LIST_DIR}
../../../../shared)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
parselib
# Sets the library as a static library.
STATIC
# Provides a relative path to your source file(s).
# stream
${CMAKE_CURRENT_LIST_DIR}/stream/FileInputStream.cpp
${CMAKE_CURRENT_LIST_DIR}/stream/InputStream.cpp
${CMAKE_CURRENT_LIST_DIR}/stream/MemInputStream.cpp
# wav
${CMAKE_CURRENT_LIST_DIR}/wav/AudioEncoding.cpp
${CMAKE_CURRENT_LIST_DIR}/wav/WavChunkHeader.cpp
${CMAKE_CURRENT_LIST_DIR}/wav/WavFmtChunkHeader.cpp
${CMAKE_CURRENT_LIST_DIR}/wav/WavRIFFChunkHeader.cpp
${CMAKE_CURRENT_LIST_DIR}/wav/WavStreamReader.cpp)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
parselib
# Links the target library to the log library
# included in the NDK.
log)

View file

@ -0,0 +1,48 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unistd.h>
#include "FileInputStream.h"
namespace parselib {
int32_t FileInputStream::read(void *buff, int32_t numBytes) {
return ::read(mFH, buff, numBytes);
}
int32_t FileInputStream::peek(void *buff, int32_t numBytes) {
int32_t numRead = ::read(mFH, buff, numBytes);
::lseek(mFH, -numBytes, SEEK_CUR);
return numRead;
}
void FileInputStream::advance(int32_t numBytes) {
if (numBytes > 0) {
::lseek(mFH, numBytes, SEEK_CUR);
}
}
int32_t FileInputStream::getPos() {
return ::lseek(mFH, 0L, SEEK_CUR);
}
void FileInputStream::setPos(int32_t pos) {
if (pos > 0) {
::lseek(mFH, pos, SEEK_SET);
}
}
} /* namespace parselib */

View file

@ -0,0 +1,49 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_STREAM_FILEINPUTSTREAM_H_
#define _IO_STREAM_FILEINPUTSTREAM_H_
#include "InputStream.h"
namespace parselib {
/**
* A concrete implementation of InputStream for a file data source
*/
class FileInputStream : public InputStream {
public:
/** constructor. Caller is presumed to have opened the file with (at least) read permission */
FileInputStream(int fh) : mFH(fh) {}
virtual ~FileInputStream() {}
virtual int32_t read(void *buff, int32_t numBytes);
virtual int32_t peek(void *buff, int32_t numBytes);
virtual void advance(int32_t numBytes);
virtual int32_t getPos();
virtual void setPos(int32_t pos);
private:
/** File handle of the data file to read from */
int mFH;
};
} // namespace parselib
#endif // _IO_STREAM_FILEINPUTSTREAM_H_

View file

@ -0,0 +1,22 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "InputStream.h"
namespace parselib {
// All of the methods of InputStream are pure virtual
} /* namespace wavlib */

View file

@ -0,0 +1,63 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_STREAM_INPUTSTREAM_H_
#define _IO_STREAM_INPUTSTREAM_H_
#include <cstdint>
namespace parselib {
/**
* An interface declaration for a stream of bytes. Concrete implements for File and Memory Buffers
*/
class InputStream {
public:
InputStream() {}
virtual ~InputStream() {}
/**
* Retrieve the specified number of bytes and advance the read position.
* Returns: The number of bytes actually retrieved. May be less than requested
* if attempt to read beyond the end of the stream.
*/
virtual int32_t read(void *buff, int32_t numBytes) = 0;
/**
* Retrieve the specified number of bytes. DOES NOT advance the read position.
* Returns: The number of bytes actually retrieved. May be less than requested
* if attempt to read beyond the end of the stream.
*/
virtual int32_t peek(void *buff, int32_t numBytes) = 0;
/**
* Moves the read position forward the (positive) number of bytes specified.
*/
virtual void advance(int32_t numBytes) = 0;
/**
* Returns the read position of the stream
*/
virtual int32_t getPos() = 0;
/**
* Sets the read position of the stream to the 0 or positive position.
*/
virtual void setPos(int32_t pos) = 0;
};
} // namespace parselib
#endif // _IO_STREAM_INPUTSTREAM_H_

View file

@ -0,0 +1,60 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <string.h>
#include "MemInputStream.h"
namespace parselib {
int32_t MemInputStream::read(void *buff, int32_t numBytes) {
int32_t numAvail = mBufferLen - mPos;
numBytes = std::min(numBytes, numAvail);
peek(buff, numBytes);
mPos += numBytes;
return numBytes;
}
int32_t MemInputStream::peek(void *buff, int32_t numBytes) {
int32_t numAvail = mBufferLen - mPos;
numBytes = std::min(numBytes, numAvail);
memcpy(buff, mBuffer + mPos, numBytes);
return numBytes;
}
void MemInputStream::advance(int32_t numBytes) {
if (numBytes > 0) {
int32_t numAvail = mBufferLen - mPos;
mPos += std::min(numAvail, numBytes);
}
}
int32_t MemInputStream::getPos() {
return mPos;
}
void MemInputStream::setPos(int32_t pos) {
if (pos > 0) {
if (pos < mBufferLen) {
mPos = pos;
} else {
mPos = mBufferLen - 1;
}
}
}
} // namespace parselib

View file

@ -0,0 +1,55 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_STREAM_MEMINPUTSTREAM_H_
#define _IO_STREAM_MEMINPUTSTREAM_H_
#include "InputStream.h"
namespace parselib {
/**
* A concrete implementation of InputStream for a memory buffer data source
*/
class MemInputStream : public InputStream {
public:
/** constructor. Caller is presumed to have allocated and filled the memory buffer */
MemInputStream(unsigned char *buff, int32_t len) : mBuffer(buff), mBufferLen(len), mPos(0) {}
virtual ~MemInputStream() {}
virtual int32_t read(void *buff, int32_t numBytes);
virtual int32_t peek(void *buff, int32_t numBytes);
virtual void advance(int32_t numBytes);
virtual int32_t getPos();
virtual void setPos(int32_t pos);
private:
/** Points to the data buffer to stream from. */
unsigned char *mBuffer;
/** Total number of bytes in the memory buffer */
int32_t mBufferLen;
/** The index of the next byte to read */
int32_t mPos;
};
} // namespace parselib
#endif // _IO_STREAM_MEMINPUTSTREAM_H_

View file

@ -0,0 +1,20 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AudioEncoding.h"
namespace parselib {
} // namespace wavlib

View file

@ -0,0 +1,36 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_WAV_AUDIOFORMAT_H_
#define _IO_WAV_AUDIOFORMAT_H_
namespace parselib {
/**
* Definitions for Audio Encodings in WAV files.
*/
class AudioEncoding {
public:
static const int INVALID = -1;
static const int PCM_16 = 0;
static const int PCM_8 = 1;
static const int PCM_IEEEFLOAT = 2;
static const int PCM_24 = 3;
static const int PCM_32 = 4;
};
} // namespace parselib
#endif // _IO_WAV_AUDIOFORMAT_H_

View file

@ -0,0 +1,29 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "stream/InputStream.h"
#include "WavChunkHeader.h"
namespace parselib {
const RiffID WavChunkHeader::RIFFID_DATA = makeRiffID('d', 'a', 't', 'a');
void WavChunkHeader::read(InputStream *stream) {
stream->read(&mChunkId, sizeof(mChunkId));
stream->read(&mChunkSize, sizeof(mChunkSize));
}
} // namespace parselib

View file

@ -0,0 +1,50 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_WAV_WAVCHUNKHEADER_H_
#define _IO_WAV_WAVCHUNKHEADER_H_
#include "WavTypes.h"
namespace parselib {
class InputStream;
/**
* Superclass for all RIFF chunks. Handles the chunk ID and chunk size.
* Concrete subclasses include chunks for 'RIFF' and 'fmt ' chunks.
*/
class WavChunkHeader {
public:
static const RiffID RIFFID_DATA;
RiffID mChunkId;
RiffInt32 mChunkSize;
WavChunkHeader() : mChunkId(0), mChunkSize(0) {}
WavChunkHeader(RiffID chunkId) : mChunkId(chunkId), mChunkSize(0) {}
/**
* Reads the contents of the chunk. In this class, just the ID and size fields.
* When implemented in a concrete subclass, that implementation MUST call this (super) method
* as the first step. It may then read the fields specific to that chunk type.
*/
virtual void read(InputStream *stream);
};
} // namespace parselib
#endif // _IO_WAV_WAVCHUNKHEADER_H_

View file

@ -0,0 +1,75 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android/log.h>
#include "stream/InputStream.h"
#include "WavFmtChunkHeader.h"
static const char *TAG = "WavFmtChunkHeader";
namespace parselib {
const RiffID WavFmtChunkHeader::RIFFID_FMT = makeRiffID('f', 'm', 't', ' ');
WavFmtChunkHeader::WavFmtChunkHeader() : WavChunkHeader(RIFFID_FMT) {
mEncodingId = ENCODING_PCM;
mNumChannels = 0;
mSampleRate = 0;
mAveBytesPerSecond = 0;
mBlockAlign = 0;
mSampleSize = 0;
mExtraBytes = 0;
}
WavFmtChunkHeader::WavFmtChunkHeader(RiffID tag) : WavChunkHeader(tag) {
mEncodingId = ENCODING_PCM;
mNumChannels = 0;
mSampleRate = 0;
mAveBytesPerSecond = 0;
mBlockAlign = 0;
mSampleSize = 0;
mExtraBytes = 0;
}
void WavFmtChunkHeader::normalize() {
if (mEncodingId == ENCODING_PCM || mEncodingId == ENCODING_IEEE_FLOAT) {
mBlockAlign = (short) (mNumChannels * (mSampleSize / 8));
mAveBytesPerSecond = mSampleRate * mBlockAlign;
mExtraBytes = 0;
} else {
//hmmm....
}
}
void WavFmtChunkHeader::read(InputStream *stream) {
WavChunkHeader::read(stream);
stream->read(&mEncodingId, sizeof(mEncodingId));
stream->read(&mNumChannels, sizeof(mNumChannels));
stream->read(&mSampleRate, sizeof(mSampleRate));
stream->read(&mAveBytesPerSecond, sizeof(mAveBytesPerSecond));
stream->read(&mBlockAlign, sizeof(mBlockAlign));
stream->read(&mSampleSize, sizeof(mSampleSize));
if (mEncodingId != ENCODING_PCM && mEncodingId != ENCODING_IEEE_FLOAT) {
// only read this if NOT PCM
stream->read(&mExtraBytes, sizeof(mExtraBytes));
} else {
mExtraBytes = (short) (mChunkSize - 16);
}
}
} // namespace parselib

View file

@ -0,0 +1,56 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_WAV_WAVFMTCHUNKHEADER_H_
#define _IO_WAV_WAVFMTCHUNKHEADER_H_
#include "WavChunkHeader.h"
class InputStream;
namespace parselib {
/**
* Encapsulates a WAV file 'fmt ' chunk.
*/
class WavFmtChunkHeader : public WavChunkHeader {
public:
static const RiffID RIFFID_FMT;
// Microsoft Encoding IDs
static const short ENCODING_PCM = 1;
static const short ENCODING_ADPCM = 2; // Microsoft ADPCM Format
static const short ENCODING_IEEE_FLOAT = 3; // samples from -1.0 -> 1.0
RiffInt16 mEncodingId; /** Microsoft WAV encoding ID (see above) */
RiffInt16 mNumChannels;
RiffInt32 mSampleRate;
RiffInt32 mAveBytesPerSecond;
RiffInt16 mBlockAlign;
RiffInt16 mSampleSize;
RiffInt16 mExtraBytes;
WavFmtChunkHeader();
WavFmtChunkHeader(RiffID tag);
void normalize();
void read(InputStream *stream);
};
} // namespace parselib
#endif // _IO_WAV_WAVFMTCHUNKHEADER_H_

View file

@ -0,0 +1,37 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "WavRIFFChunkHeader.h"
#include "stream/InputStream.h"
namespace parselib {
const RiffID WavRIFFChunkHeader::RIFFID_RIFF = makeRiffID('R', 'I', 'F', 'F');
const RiffID WavRIFFChunkHeader::RIFFID_WAVE = makeRiffID('W', 'A', 'V', 'E');
WavRIFFChunkHeader::WavRIFFChunkHeader() : WavChunkHeader(RIFFID_RIFF) {
mFormatId = RIFFID_WAVE;
}
WavRIFFChunkHeader::WavRIFFChunkHeader(RiffID tag) : WavChunkHeader(tag) {
mFormatId = RIFFID_WAVE;
}
void WavRIFFChunkHeader::read(InputStream *stream) {
WavChunkHeader::read(stream);
stream->read(&mFormatId, sizeof(mFormatId));
}
} // namespace parselib

View file

@ -0,0 +1,42 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_WAV_WAVRIFFCHUNKHEADER_H_
#define _IO_WAV_WAVRIFFCHUNKHEADER_H_
#include "WavChunkHeader.h"
namespace parselib {
class InputStream;
class WavRIFFChunkHeader : public WavChunkHeader {
public:
static const RiffID RIFFID_RIFF;
static const RiffID RIFFID_WAVE;
RiffID mFormatId;
WavRIFFChunkHeader();
WavRIFFChunkHeader(RiffID tag);
virtual void read(InputStream *stream);
};
} // namespace parselib
#endif // _IO_WAV_WAVRIFFCHUNKHEADER_H_

View file

@ -0,0 +1,319 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <string.h>
#include <android/log.h>
#include "stream/InputStream.h"
#include "AudioEncoding.h"
#include "WavRIFFChunkHeader.h"
#include "WavFmtChunkHeader.h"
#include "WavChunkHeader.h"
#include "WavStreamReader.h"
static const char *TAG = "WavStreamReader";
static constexpr int kConversionBufferFrames = 16;
namespace parselib {
WavStreamReader::WavStreamReader(InputStream *stream) {
mStream = stream;
mWavChunk = nullptr;
mFmtChunk = nullptr;
mDataChunk = nullptr;
mAudioDataStartPos = -1;
}
int WavStreamReader::getSampleEncoding() {
if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
switch (mFmtChunk->mSampleSize) {
case 8:
return AudioEncoding::PCM_8;
case 16:
return AudioEncoding::PCM_16;
case 24:
return AudioEncoding::PCM_24;
case 32:
return AudioEncoding::PCM_32;
default:
return AudioEncoding::INVALID;
}
} else if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_IEEE_FLOAT) {
return AudioEncoding::PCM_IEEEFLOAT;
}
return AudioEncoding::INVALID;
}
void WavStreamReader::parse() {
RiffID tag;
while (true) {
int numRead = mStream->peek(&tag, sizeof(tag));
if (numRead <= 0) {
break; // done
}
// char *tagStr = (char *) &tag;
// __android_log_print(ANDROID_LOG_INFO, TAG, "[%c%c%c%c]",
// tagStr[0], tagStr[1], tagStr[2], tagStr[3]);
std::shared_ptr<WavChunkHeader> chunk = nullptr;
if (tag == WavRIFFChunkHeader::RIFFID_RIFF) {
chunk = mWavChunk = std::make_shared<WavRIFFChunkHeader>(WavRIFFChunkHeader(tag));
mWavChunk->read(mStream);
} else if (tag == WavFmtChunkHeader::RIFFID_FMT) {
chunk = mFmtChunk = std::make_shared<WavFmtChunkHeader>(WavFmtChunkHeader(tag));
mFmtChunk->read(mStream);
} else if (tag == WavChunkHeader::RIFFID_DATA) {
chunk = mDataChunk = std::make_shared<WavChunkHeader>(WavChunkHeader(tag));
mDataChunk->read(mStream);
// We are now positioned at the start of the audio data.
mAudioDataStartPos = mStream->getPos();
mStream->advance(mDataChunk->mChunkSize);
} else {
chunk = std::make_shared<WavChunkHeader>(WavChunkHeader(tag));
chunk->read(mStream);
mStream->advance(chunk->mChunkSize); // skip the body
}
mChunkMap[tag] = chunk;
}
if (mDataChunk != 0) {
mStream->setPos(mAudioDataStartPos);
}
}
// Data access
void WavStreamReader::positionToAudio() {
if (mDataChunk != 0) {
mStream->setPos(mAudioDataStartPos);
}
}
/**
* Read and convert samples in PCM8 format to float
*/
int WavStreamReader::getDataFloat_PCM8(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int buffOffset = 0;
int totalFramesRead = 0;
static constexpr int kSampleSize = sizeof(u_int8_t);
static constexpr float kSampleFullScale = (float)0x80;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
u_int8_t readBuff[kConversionBufferFrames * numChannels];
int framesLeft = numFrames;
while (framesLeft > 0) {
int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
//__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
int numFramesRead =
mStream->read(readBuff, framesThisRead * kSampleSize * numChannels) /
(kSampleSize * numChannels);
totalFramesRead += numFramesRead;
// Convert & Scale
for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
// PCM8 is unsigned, so we need to make it signed before scaling/converting
buff[buffOffset++] = ((float) readBuff[offset] - kSampleFullScale)
* kInverseScale;
}
if (numFramesRead < framesThisRead) {
break; // none left
}
framesLeft -= framesThisRead;
}
return totalFramesRead;
}
/**
* Read and convert samples in PCM16 format to float
*/
int WavStreamReader::getDataFloat_PCM16(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int buffOffset = 0;
int totalFramesRead = 0;
static constexpr int kSampleSize = sizeof(int16_t);
static constexpr float kSampleFullScale = (float) 0x8000;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
int16_t readBuff[kConversionBufferFrames * numChannels];
int framesLeft = numFrames;
while (framesLeft > 0) {
int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
//__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
int numFramesRead =
mStream->read(readBuff, framesThisRead * kSampleSize * numChannels) /
(kSampleSize * numChannels);
totalFramesRead += numFramesRead;
// Convert & Scale
for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
buff[buffOffset++] = (float) readBuff[offset] * kInverseScale;
}
if (numFramesRead < framesThisRead) {
break; // none left
}
framesLeft -= framesThisRead;
}
return totalFramesRead;
}
/**
* Read and convert samples in PCM24 format to float
*/
int WavStreamReader::getDataFloat_PCM24(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int numSamples = numFrames * numChannels;
static constexpr float kSampleFullScale = (float) 0x80000000;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
uint8_t buffer[3];
for(int sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) {
if (mStream->read(buffer, 3) < 3) {
break; // no more data
}
int32_t sample = (buffer[0] << 8) | (buffer[1] << 16) | (buffer[2] << 24);
buff[sampleIndex] = (float)sample * kInverseScale;
}
return numFrames;
}
/**
* Read and convert samples in Float32 format to float
*/
int WavStreamReader::getDataFloat_Float32(float *buff, int numFrames) {
// Turns out that WAV Float32 is just Android floats
int numChannels = mFmtChunk->mNumChannels;
return mStream->read(buff, numFrames * sizeof(float) * numChannels) /
(sizeof(float) * numChannels);
}
/**
* Read and convert samples in PCM32 format to float
*/
int WavStreamReader::getDataFloat_PCM32(float *buff, int numFrames) {
int numChannels = mFmtChunk->mNumChannels;
int buffOffset = 0;
int totalFramesRead = 0;
static constexpr int kSampleSize = sizeof(int32_t);
static constexpr float kSampleFullScale = (float) 0x80000000;
static constexpr float kInverseScale = 1.0f / kSampleFullScale;
int32_t readBuff[kConversionBufferFrames * numChannels];
int framesLeft = numFrames;
while (framesLeft > 0) {
int framesThisRead = std::min(framesLeft, kConversionBufferFrames);
//__android_log_print(ANDROID_LOG_INFO, TAG, "read(%d)", framesThisRead);
int numFramesRead =
mStream->read(readBuff, framesThisRead * kSampleSize* numChannels) /
(kSampleSize * numChannels);
totalFramesRead += numFramesRead;
// convert & Scale
for (int offset = 0; offset < numFramesRead * numChannels; offset++) {
buff[buffOffset++] = (float) readBuff[offset] * kInverseScale;
}
if (numFramesRead < framesThisRead) {
break; // none left
}
framesLeft -= framesThisRead;
}
return totalFramesRead;
}
int WavStreamReader::getDataFloat(float *buff, int numFrames) {
// __android_log_print(ANDROID_LOG_INFO, TAG, "getData(%d)", numFrames);
if (mDataChunk == nullptr || mFmtChunk == nullptr) {
return ERR_INVALID_STATE;
}
int numFramesRead = 0;
switch (mFmtChunk->mSampleSize) {
case 8:
numFramesRead = getDataFloat_PCM8(buff, numFrames);
break;
case 16:
numFramesRead = getDataFloat_PCM16(buff, numFrames);
break;
case 24:
if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
numFramesRead = getDataFloat_PCM24(buff, numFrames);
} else {
__android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
}
break;
case 32:
if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_PCM) {
numFramesRead = getDataFloat_PCM32(buff, numFrames);
} else if (mFmtChunk->mEncodingId == WavFmtChunkHeader::ENCODING_IEEE_FLOAT) {
numFramesRead = getDataFloat_Float32(buff, numFrames);
} else {
__android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
}
break;
default:
__android_log_print(ANDROID_LOG_INFO, TAG, "invalid encoding:%d mSampleSize:%d",
mFmtChunk->mEncodingId, mFmtChunk->mSampleSize);
return ERR_INVALID_FORMAT;
}
// Zero out any unread frames
if (numFramesRead < numFrames) {
int numChannels = getNumChannels();
memset(buff + (numFramesRead * numChannels), 0,
(numFrames - numFramesRead) * sizeof(buff[0]) * numChannels);
}
return numFramesRead;
}
} // namespace parselib

View file

@ -0,0 +1,89 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _IO_WAV_WAVSTREAMREADER_H_
#define _IO_WAV_WAVSTREAMREADER_H_
#include <map>
#include "AudioEncoding.h"
#include "WavRIFFChunkHeader.h"
#include "WavFmtChunkHeader.h"
/*
* WAV format documentation can be found:
* http://soundfile.sapp.org/doc/WaveFormat/
* https://web.archive.org/web/20090417165828/http://www.kk.iij4u.or.jp/~kondo/wave/mpidata.txt
*/
namespace parselib {
class InputStream;
class WavStreamReader {
public:
WavStreamReader(InputStream *stream);
int getSampleRate() { return mFmtChunk->mSampleRate; }
int getNumSampleFrames() {
return mDataChunk->mChunkSize / (mFmtChunk->mSampleSize / 8) / mFmtChunk->mNumChannels;
}
int getNumChannels() { return mFmtChunk != 0 ? mFmtChunk->mNumChannels : 0; }
int getSampleEncoding();
int getBitsPerSample() { return mFmtChunk->mSampleSize; }
void parse();
// Data access
void positionToAudio();
static constexpr int ERR_INVALID_FORMAT = -1;
static constexpr int ERR_INVALID_STATE = -2;
int getDataFloat(float *buff, int numFrames);
// int getData16(short *buff, int numFramees);
protected:
InputStream *mStream;
std::shared_ptr<WavRIFFChunkHeader> mWavChunk;
std::shared_ptr<WavFmtChunkHeader> mFmtChunk;
std::shared_ptr<WavChunkHeader> mDataChunk;
long mAudioDataStartPos;
std::map<RiffID, std::shared_ptr<WavChunkHeader>> mChunkMap;
private:
/*
* Individual Format Readers/Converters
*/
int getDataFloat_PCM8(float *buff, int numFrames);
int getDataFloat_PCM16(float *buff, int numFrames);
int getDataFloat_PCM24(float *buff, int numFrames);
int getDataFloat_Float32(float *buff, int numFrames);
int getDataFloat_PCM32(float *buff, int numFrames);
};
} // namespace parselib
#endif // _IO_WAV_WAVSTREAMREADER_H_

View file

@ -0,0 +1,38 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __WAVTYPES_H__
#define __WAVTYPES_H__
namespace parselib {
/*
* Declarations for various (cross-platform) WAV-specific data types.
*/
typedef unsigned int RiffID; // A "four character code" (i.e. FOURCC)
typedef int RiffInt32; // A 32-bit signed integer
typedef short RiffInt16; // A 16-bit signed integer
/*
* Packs the specified characters into a 32-bit value in accordance with the Microsoft
* FOURCC specification.
*/
inline RiffID makeRiffID(char a, char b, char c, char d) {
return ((RiffID)d << 24) | ((RiffID)c << 16) | ((RiffID)b << 8) | (RiffID)a;
}
} // namespace parselib
#endif /* __WAVTYPES_H__ */

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">wavlib</string>
</resources>