Initial support for WAV parsing

This commit is contained in:
Joshua Granick
2014-07-28 21:27:21 -07:00
parent d8a0eed472
commit b55c191e04
10 changed files with 408 additions and 215 deletions

72
lime/audio/Sound.hx Normal file
View File

@@ -0,0 +1,72 @@
package lime.audio;
import lime.system.System;
import lime.utils.ByteArray;
class Sound {
public var bitsPerSample:Int;
public var channels:Int;
public var data:ByteArray;
public var sampleRate:Int;
public function new () {
}
public static function loadFromBytes (bytes:ByteArray):Sound {
#if (cpp || neko)
var data = lime_sound_load_bytes (bytes);
var sound = new Sound ();
sound.bitsPerSample = data.bitsPerSample;
sound.channels = data.channels;
sound.data = data.data;
sound.sampleRate = data.sampleRate;
return sound;
#else
return null;
#end
}
public static function loadFromFile (path:String):Sound {
#if (cpp || neko)
var data = lime_sound_load (path);
var sound = new Sound ();
sound.bitsPerSample = data.bitsPerSample;
sound.channels = data.channels;
sound.data = data.data;
sound.sampleRate = data.sampleRate;
return sound;
#else
return null;
#end
}
#if (cpp || neko)
private static var lime_sound_load = System.load ("lime", "lime_sound_load", 1);
private static var lime_sound_load_bytes = System.load ("lime", "lime_sound_load_bytes", 1);
#end
}

View File

@@ -31,6 +31,10 @@ class System {
element = cast Browser.document.getElementById (elementName);
} else {
element = cast Browser.document.createElement ("div");
}
var color = null;

View File

@@ -105,6 +105,8 @@
<file name="src/system/ios/System.mm" if="ios" />
<file name="src/app/UpdateEvent.cpp" />
<file name="src/audio/Sound.cpp" />
<file name="src/format/WAV.cpp" />
<file name="src/graphics/Image.cpp" />
<file name="src/graphics/RenderEvent.cpp" />
<file name="src/system/System.cpp" />

View File

@@ -2,6 +2,30 @@
#define LIME_AUDIO_SOUND_H
#include <hx/CFFI.h>
#include <utils/ByteArray.h>
#ifdef ANDROID
#include <android/log.h>
#endif
#ifdef ANDROID
#define LOG_SOUND(args,...) ELOG(args, ##__VA_ARGS__)
#else
#ifdef IPHONE
//#define LOG_SOUND(args,...) printf(args, ##__VA_ARGS__)
#define LOG_SOUND(args...) { }
#elif defined(TIZEN)
#include <FBase.h>
#define LOG_SOUND(args,...) AppLog(args, ##__VA_ARGS__)
#else
#define LOG_SOUND(args,...) printf(args, ##__VA_ARGS__)
#endif
#endif
//#define LOG_SOUND(args...) { }
namespace lime {
@@ -10,6 +34,20 @@ namespace lime {
public:
Sound ();
~Sound ();
value Value ();
int bitsPerSample;
int channels;
int sampleRate;
ByteArray *data;
private:
value mValue;
};

View File

@@ -9,6 +9,37 @@
namespace lime {
struct RIFF_Header {
char chunkID[4];
unsigned int chunkSize; //size not including chunkSize or chunkID
char format[4];
};
struct WAVE_Format {
char subChunkID[4];
unsigned int subChunkSize;
short audioFormat;
short numChannels;
unsigned int sampleRate;
unsigned int byteRate;
short blockAlign;
short bitsPerSample;
};
struct WAVE_Data {
char subChunkID[4]; //should contain the word data
unsigned int subChunkSize; //Stores the size of the data block
};
class WAV {

View File

@@ -17,15 +17,14 @@ namespace lime {
Image ();
~Image ();
void Resize (int width, int height, int bpp = 4);
void Blit (const unsigned char *data, int x, int y, int width, int height);
void Resize (int width, int height, int bpp = 4);
value Value ();
int width;
int height;
int bpp; // bytes per pixel
int bpp;
ByteArray *data;
int height;
int width;
private:

View File

@@ -10,11 +10,13 @@
#include <hx/CFFI.h>
#include <app/Application.h>
#include <app/UpdateEvent.h>
#include <audio/Sound.h>
#ifdef LIME_FREETYPE
#include <graphics/Font.h>
#endif
#include <format/JPEG.h>
#include <format/PNG.h>
#include <format/WAV.h>
#include <graphics/Image.h>
#include <graphics/Renderer.h>
#include <graphics/RenderEvent.h>
@@ -115,10 +117,10 @@ namespace lime {
#else
return alloc_null ();
#endif
}
value lime_font_load_glyphs (value fontHandle, value size, value glyphs) {
#ifdef LIME_FREETYPE
@@ -133,8 +135,8 @@ namespace lime {
#endif
}
value lime_key_event_manager_register (value callback, value eventObject) {
KeyEvent::callback = new AutoGCRoot (callback);
@@ -214,6 +216,39 @@ namespace lime {
}
value lime_sound_load (value path) {
Sound sound;
Resource resource (val_string (path));
if (WAV::Decode (&resource, &sound)) {
return sound.Value ();
}
return alloc_null ();
}
value lime_sound_load_bytes (value bytes) {
Sound sound;
ByteArray data (bytes);
Resource resource (&data);
if (WAV::Decode (&resource, &sound)) {
return sound.Value ();
}
return alloc_null ();
}
value lime_system_get_timestamp () {
return alloc_float (System::GetTimestamp ());
@@ -289,6 +324,8 @@ namespace lime {
DEFINE_PRIM (lime_renderer_create, 1);
DEFINE_PRIM (lime_renderer_flip, 1);
DEFINE_PRIM (lime_render_event_manager_register, 2);
DEFINE_PRIM (lime_sound_load, 1);
DEFINE_PRIM (lime_sound_load_bytes, 1);
DEFINE_PRIM (lime_system_get_timestamp, 0);
DEFINE_PRIM (lime_touch_event_manager_register, 2);
DEFINE_PRIM (lime_update_event_manager_register, 2);

View File

@@ -0,0 +1,53 @@
#include <audio/Sound.h>
namespace lime {
static int id_bitsPerSample;
static int id_channels;
static int id_data;
static int id_sampleRate;
static bool init = false;
Sound::Sound () {
bitsPerSample = 0;
channels = 0;
data = new ByteArray ();
sampleRate = 0;
}
Sound::~Sound () {
delete data;
}
value Sound::Value () {
if (!init) {
id_bitsPerSample = val_id ("bitsPerSample");
id_channels = val_id ("channels");
id_data = val_id ("data");
id_sampleRate = val_id ("sampleRate");
init = true;
}
mValue = alloc_empty_object ();
alloc_field (mValue, id_bitsPerSample, alloc_int (bitsPerSample));
alloc_field (mValue, id_channels, alloc_int (channels));
alloc_field (mValue, id_data, data->mValue);
alloc_field (mValue, id_sampleRate, alloc_int (sampleRate));
return mValue;
}
}

View File

@@ -1,9 +1,47 @@
#include <format/WAV.h>
#include <utils/FileIO.h>
namespace lime {
template<typename T>
inline const char* readStruct (T& dest, const char*& ptr) {
const char* ret;
memcpy (&dest, ptr, sizeof (T));
ptr += sizeof (WAVE_Data);
ret = ptr;
ptr += dest.subChunkSize;
return ret;
}
const char* find_chunk (const char* start, const char* end, const char* chunkID) {
WAVE_Data chunk;
const char* ptr = start;
while (ptr < (end - sizeof(WAVE_Data))) {
memcpy (&chunk, ptr, sizeof (WAVE_Data));
if (chunk.subChunkID[0] == chunkID[0] && chunk.subChunkID[1] == chunkID[1] && chunk.subChunkID[2] == chunkID[2] && chunk.subChunkID[3] == chunkID[3]) {
return ptr;
}
ptr += sizeof (WAVE_Data) + chunk.subChunkSize;
}
return 0;
}
bool WAV::Decode (Resource *resource, Sound *sound) {
WAVE_Format wave_format;
@@ -11,254 +49,174 @@ namespace lime {
WAVE_Data wave_data;
unsigned char* data;
FILE *f = NULL;
FILE *file = NULL;
if (resource->path) {
//http://www.dunsanyinteractive.com/blogs/oliver/?p=72
//Local Declarations
#ifdef ANDROID
FileInfo info = AndroidGetAssetFD(inFileURL);
f = fdopen(info.fd, "rb");
fseek(f, info.offset, 0);
FileInfo info = AndroidGetAssetFD (resource->path);
file = fdopen (info.fd, "rb");
lime::fseek (file, info.offset, 0);
#else
f = fopen(inFileURL, "rb");
file = lime::fopen (resource->path, "rb");
#endif
if (!f)
{
LOG_SOUND("FAILED to read sound file, file pointer as null?\n");
if (!file) {
LOG_SOUND ("FAILED to read sound file, file pointer as null?\n");
return false;
}
// Read in the first chunk into the struct
int result = fread(&riff_header, sizeof(RIFF_Header), 1, f);
//check for RIFF and WAVE tag in memeory
if ((riff_header.chunkID[0] != 'R' ||
riff_header.chunkID[1] != 'I' ||
riff_header.chunkID[2] != 'F' ||
riff_header.chunkID[3] != 'F') ||
(riff_header.format[0] != 'W' ||
riff_header.format[1] != 'A' ||
riff_header.format[2] != 'V' ||
riff_header.format[3] != 'E'))
{
LOG_SOUND("Invalid RIFF or WAVE Header!\n");
int result = lime::fread (&riff_header, sizeof (RIFF_Header), 1, file);
if ((riff_header.chunkID[0] != 'R' || riff_header.chunkID[1] != 'I' || riff_header.chunkID[2] != 'F' || riff_header.chunkID[3] != 'F') || (riff_header.format[0] != 'W' || riff_header.format[1] != 'A' || riff_header.format[2] != 'V' || riff_header.format[3] != 'E')) {
LOG_SOUND ("Invalid RIFF or WAVE Header!\n");
return false;
}
long int currentHead = 0;
bool foundFormat = false;
while (!foundFormat)
{
// Save the current position indicator of the stream
currentHead = ftell(f);
while (!foundFormat) {
//Read in the 2nd chunk for the wave info
result = fread(&wave_format, sizeof(WAVE_Format), 1, f);
currentHead = lime::ftell (file);
if (result != 1)
{
LOG_SOUND("Invalid Wave Format!\n");
result = lime::fread (&wave_format, sizeof (WAVE_Format), 1, file);
if (result != 1) {
LOG_SOUND ("Invalid Wave Format!\n");
return false;
}
//check for fmt tag in memory
if (wave_format.subChunkID[0] != 'f' ||
wave_format.subChunkID[1] != 'm' ||
wave_format.subChunkID[2] != 't' ||
wave_format.subChunkID[3] != ' ')
{
fseek(f, wave_data.subChunkSize, SEEK_CUR);
}
else
{
if (wave_format.subChunkID[0] != 'f' || wave_format.subChunkID[1] != 'm' || wave_format.subChunkID[2] != 't' || wave_format.subChunkID[3] != ' ') {
lime::fseek (file, wave_data.subChunkSize, SEEK_CUR);
} else {
foundFormat = true;
}
}
//check for extra parameters;
if (wave_format.subChunkSize > 16)
{
fseek(f, sizeof(short), SEEK_CUR);
if (wave_format.subChunkSize > 16) {
lime::fseek (file, sizeof (short), SEEK_CUR);
}
bool foundData = false;
while (!foundData)
{
//Read in the the last byte of data before the sound file
result = fread(&wave_data, sizeof(WAVE_Data), 1, f);
while (!foundData) {
if (result != 1)
{
LOG_SOUND("Invalid Wav Data Header!\n");
result = lime::fread (&wave_data, sizeof (WAVE_Data), 1, file);
if (result != 1) {
LOG_SOUND ("Invalid Wav Data Header!\n");
return false;
}
if (wave_data.subChunkID[0] != 'd' ||
wave_data.subChunkID[1] != 'a' ||
wave_data.subChunkID[2] != 't' ||
wave_data.subChunkID[3] != 'a')
{
//fseek(f, wave_data.subChunkSize, SEEK_CUR);
//fseek(f, wave_data.subChunkSize, SEEK_CUR);
// Goto next chunk.
fseek(f, currentHead + sizeof(WAVE_Data) + wave_format.subChunkSize, SEEK_SET);
}
else
{
if (wave_data.subChunkID[0] != 'd' || wave_data.subChunkID[1] != 'a' || wave_data.subChunkID[2] != 't' || wave_data.subChunkID[3] != 'a') {
lime::fseek (file, currentHead + sizeof (WAVE_Data) + wave_format.subChunkSize, SEEK_SET);
} else {
foundData = true;
}
}
//Allocate memory for data
data = new unsigned char[wave_data.subChunkSize];
sound->data->Resize (wave_data.subChunkSize);
// Read in the sound data into the soundData variable
if (!fread(data, wave_data.subChunkSize, 1, f))
{
LOG_SOUND("error loading WAVE data into struct!\n");
if (!lime::fread (sound->data->Bytes (), wave_data.subChunkSize, 1, file)) {
LOG_SOUND ("error loading WAVE data into struct!\n");
return false;
}
}
//Store in the outbuffer
outBuffer.Set(data, wave_data.subChunkSize);
//Now we set the variables that we passed in with the
//data from the structs
*outSampleRate = (int)wave_format.sampleRate;
//The format is worked out by looking at the number of
//channels and the bits per sample.
*channels = wave_format.numChannels;
*bitsPerSample = wave_format.bitsPerSample;
//clean up and return true if successful
fclose(f);
delete[] data;
return true;
lime::fclose (file);
} else {
const char* start = resource->data->Bytes ();
const char* start = (const char*)resource->data->Bytes ();
const char* end = start + resource->data->Size ();
const char* ptr = start;
// Read in the first chunk into the struct
memcpy (&riff_header, ptr, sizeof (RIFF_Header));
ptr += sizeof (RIFF_Header);
//check for RIFF and WAVE tag in memeory
if ((riff_header.chunkID[0] != 'R' ||
riff_header.chunkID[1] != 'I' ||
riff_header.chunkID[2] != 'F' ||
riff_header.chunkID[3] != 'F') ||
(riff_header.format[0] != 'W' ||
riff_header.format[1] != 'A' ||
riff_header.format[2] != 'V' ||
riff_header.format[3] != 'E'))
{
LOG_SOUND("Invalid RIFF or WAVE Header!\n");
if ((riff_header.chunkID[0] != 'R' || riff_header.chunkID[1] != 'I' || riff_header.chunkID[2] != 'F' || riff_header.chunkID[3] != 'F') || (riff_header.format[0] != 'W' || riff_header.format[1] != 'A' || riff_header.format[2] != 'V' || riff_header.format[3] != 'E')) {
LOG_SOUND ("Invalid RIFF or WAVE Header!\n");
return false;
}
//Read in the 2nd chunk for the wave info
ptr = find_chunk(ptr, end, "fmt ");
ptr = find_chunk (ptr, end, "fmt ");
if (!ptr) {
return false;
}
readStruct(wave_format, ptr);
//check for fmt tag in memory
if (wave_format.subChunkID[0] != 'f' ||
wave_format.subChunkID[1] != 'm' ||
wave_format.subChunkID[2] != 't' ||
wave_format.subChunkID[3] != ' ')
{
LOG_SOUND("Invalid Wave Format!\n");
return false;
}
ptr = find_chunk(ptr, end, "data");
readStruct (wave_format, ptr);
if (wave_format.subChunkID[0] != 'f' || wave_format.subChunkID[1] != 'm' || wave_format.subChunkID[2] != 't' || wave_format.subChunkID[3] != ' ') {
LOG_SOUND ("Invalid Wave Format!\n");
return false;
}
ptr = find_chunk (ptr, end, "data");
if (!ptr) {
return false;
}
const char* base = readStruct(wave_data, ptr);
const char* base = readStruct (wave_data, ptr);
//check for data tag in memory
if (wave_data.subChunkID[0] != 'd' ||
wave_data.subChunkID[1] != 'a' ||
wave_data.subChunkID[2] != 't' ||
wave_data.subChunkID[3] != 'a')
{
LOG_SOUND("Invalid Wav Data Header!\n");
if (wave_data.subChunkID[0] != 'd' || wave_data.subChunkID[1] != 'a' || wave_data.subChunkID[2] != 't' || wave_data.subChunkID[3] != 'a') {
LOG_SOUND ("Invalid Wav Data Header!\n");
return false;
}
//Allocate memory for data
//data = new unsigned char[wave_data.subChunk2Size];
sound->data->Resize (wave_data.subChunkSize);
// Read in the sound data into the soundData variable
size_t size = wave_data.subChunkSize;
if (size > (end - base)) {
return false;
}
/*mlChannels = wave_format.numChannels;
if (mlChannels == 2)
{
if (wave_format.bitsPerSample == 8)
{
mFormat = AL_FORMAT_STEREO8;
mlSamples = size / 2;
}
else //if (wave_format.bitsPerSample == 16)
{
mlSamples = size / 4;
mFormat = AL_FORMAT_STEREO16;
}
} else //if (mlChannels == 1)
{
if (wave_format.bitsPerSample == 8)
{
mlSamples = size;
mFormat = AL_FORMAT_MONO8;
}
else //if (wave_format.bitsPerSample == 16)
{
mlSamples = size / 2;
mFormat = AL_FORMAT_MONO16;
}
}
mlFrequency = wave_format.sampleRate;
mfTotalTime = float(mlSamples) / float(mlFrequency);*/
//Store in the outbuffer
outBuffer.Set((unsigned char*)base, size);
//Now we set the variables that we passed in with the
//data from the structs
*outSampleRate = (int)wave_format.sampleRate;
//The format is worked out by looking at the number of
//channels and the bits per sample.
*channels = wave_format.numChannels;
*bitsPerSample = wave_format.bitsPerSample;
//clean up and return true if successful
//fclose(f);
//delete[] data;
return true;
unsigned char* bytes = sound->data->Bytes ();
memcpy (bytes, base, size);
}
sound->sampleRate = (int)wave_format.sampleRate;
sound->channels = wave_format.numChannels;
sound->bitsPerSample = wave_format.bitsPerSample;
return true;
}

View File

@@ -28,49 +28,48 @@ namespace lime {
}
void Image::Blit (const unsigned char *data, int x, int y, int width, int height) {
if (x < 0 || x + width > this->width || y < 0 || y + height > this->height) {
return;
}
unsigned char *bytes = this->data->Bytes ();
for (int i = 0; i < height; i++) {
memcpy (&bytes[(i + y) * this->width + x], &data[i * width], width * bpp);
}
}
void Image::Resize (int width, int height, int bpp) {
this->bpp = bpp;
this->width = width;
this->height = height;
if (this->data) delete this->data;
this->data = new ByteArray (width * height * bpp);
}
void Image::Blit (const unsigned char *data, int x, int y, int width, int height) {
if (x < 0 || x + width > this->width ||
y < 0 || y + height > this->height) {
return;
}
unsigned char *bytes = this->data->Bytes ();
for (int i = 0; i < height; i++) {
memcpy (&bytes[(i + y) * this->width + x], &data[i * width], width * bpp);
}
}
value Image::Value() {
value Image::Value () {
if (!init) {
id_width = val_id ("width");
id_height = val_id ("height");
id_data = val_id ("data");
id_bpp = val_id ("bpp");
init = true;
}
mValue = alloc_empty_object ();
alloc_field (mValue, id_width, alloc_int (width));
alloc_field (mValue, id_height, alloc_int (height));