Created
September 3, 2016 11:50
-
-
Save bit-hack/436f7145caafbc5af2c628cfd247e648 to your computer and use it in GitHub Desktop.
Simple Wave file to AIFF converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "file.h" | |
#include <memory> | |
#if !defined(_MSC_VER) | |
#define PACK__ __attribute__((__packed__)) | |
#else | |
#define PACK__ | |
#pragma pack(push, 1) | |
#endif | |
struct sound_t { | |
uint32_t length_; | |
std::unique_ptr<int8_t[]> samples_; | |
uint32_t sample_rate_; | |
uint32_t bit_depth_; | |
uint32_t channels_; | |
}; | |
namespace { | |
constexpr uint32_t fourcc(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | |
return (d<<24)|(c<<16)|(b<<8)|a; | |
} | |
template <typename type_t> | |
type_t endian(type_t in) { | |
uint8_t * p = reinterpret_cast<uint8_t*>(&in); | |
const size_t size = sizeof(type_t); | |
for (int i = 0; i<size/2; ++i) { | |
uint8_t & a = p[i]; | |
uint8_t & b = p[(size-1)-i]; | |
uint8_t temp = a; | |
a = b; | |
b = temp; | |
} | |
return in; | |
} | |
} // namespace {} | |
bool load_wav(const char * path, sound_t & out) { | |
file_reader_t file; | |
if (!file.open(path)) { | |
return false; | |
} | |
struct PACK__ { | |
uint32_t chunk_id_; | |
uint32_t chunk_size_; | |
uint32_t format_; | |
} riff; | |
struct PACK__ { | |
uint32_t chunk_id_; | |
uint32_t chunk_size_; | |
uint16_t format_; | |
uint16_t channels_; | |
uint32_t sample_rate_; | |
uint32_t byte_rate_; | |
uint16_t block_align_; | |
uint16_t bit_depth_; | |
} fmt; | |
struct PACK__ { | |
uint32_t chunk_id_; | |
uint32_t chunk_size_; | |
/* after starts sample data */ | |
} data; | |
// read riff header | |
if (!file.read(riff)) { | |
return false; | |
} | |
if (riff.chunk_id_!=fourcc('R', 'I', 'F', 'F')) { | |
return false; | |
} | |
if (riff.format_!=fourcc('W', 'A', 'V', 'E')) { | |
return false; | |
} | |
// read format chunk | |
if (!file.read(fmt)) { | |
return false; | |
} | |
if (fmt.chunk_id_!=fourcc('f', 'm', 't', ' ')) { | |
return false; | |
} | |
// read data chunk | |
if (!file.read(data)) { | |
return false; | |
} | |
if (data.chunk_id_!=fourcc('d', 'a', 't', 'a')) { | |
return false; | |
} | |
// sanity check format | |
if (fmt.bit_depth_%8 /* multiple of 8 */) { | |
return false; | |
} | |
if (fmt.channels_>2) { | |
return false; | |
} | |
// sanity check on file size | |
size_t file_size = 0; | |
if (!file.size(file_size)) { | |
return false; | |
} | |
if (data.chunk_size_>=file_size) { | |
return false; | |
} | |
// read sample data | |
std::unique_ptr<int8_t[]> buffer; | |
buffer.reset(new int8_t[data.chunk_size_]); | |
if (!file.read(buffer.get(), data.chunk_size_)) { | |
return false; | |
} | |
// copy into output structure | |
out.bit_depth_ = fmt.bit_depth_; | |
out.sample_rate_ = fmt.sample_rate_; | |
out.length_ = data.chunk_size_; | |
out.channels_ = fmt.channels_; | |
out.samples_.reset(buffer.release()); | |
/* success */ | |
return true; | |
} | |
bool save_aif(const char * path, const sound_t & in) { | |
struct PACK__ { | |
uint32_t a_; | |
uint32_t size_; | |
uint32_t b_; | |
} header; | |
struct PACK__ { | |
uint32_t id_; | |
uint32_t size_; | |
uint16_t channels_; | |
uint32_t frames_; | |
uint16_t bit_depth_; | |
uint8_t sample_rate_[10]; | |
} comm; | |
struct PACK__ { | |
uint32_t id_; | |
uint32_t size_; | |
uint16_t offset_; | |
uint16_t blocksize_; | |
} ssnd; | |
/* open file for writing */ | |
file_writer_t file; | |
if (!file.open(path)) { | |
return false; | |
} | |
/* write aiff header */ | |
header.a_ = fourcc('F', 'O', 'R', 'M'); | |
header.size_ = endian<uint32_t>(4 + sizeof(comm) + sizeof(ssnd) + in.length_); | |
header.b_ = fourcc('A', 'I', 'F', 'F'); | |
if (!file.write(header)) { | |
return false; | |
} | |
/* write common header */ | |
comm.id_ = fourcc('C', 'O', 'M', 'M'); | |
comm.size_ = endian<uint32_t>(sizeof(comm)-8); | |
comm.channels_ = endian<uint16_t>(in.channels_); | |
comm.frames_ = endian<uint32_t>(in.length_ / ((in.bit_depth_ / 8) * in.channels_)); | |
comm.bit_depth_ = endian<uint16_t>(in.bit_depth_); | |
const uint8_t C_44100[] = {0x40, 0x0E, 0xAC, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | |
memcpy_s(&comm.sample_rate_, 10, C_44100, sizeof(C_44100)); | |
if (!file.write(comm)) { | |
return false; | |
} | |
/* write sound chunk header */ | |
ssnd.id_ = fourcc('S', 'S', 'N', 'D'); | |
ssnd.size_ = endian<uint32_t>(sizeof(comm) + in.length_ - 8); | |
ssnd.offset_ = 0; | |
ssnd.blocksize_ = 0; | |
if (!file.write(ssnd)) { | |
return false; | |
} | |
#if 0 | |
/* write sample data */ | |
if (!file.write(in.samples_.get(), in.length_)) { | |
return false; | |
} | |
#else | |
switch (in.bit_depth_) { | |
case(8): | |
for (uint32_t i = 0; i<in.length_; ++i) { | |
uint8_t sample = in.samples_.get()[i]; | |
sample += 127; | |
file.write<uint8_t>(sample); | |
} | |
break; | |
default: | |
return false; | |
} | |
#endif | |
/* success */ | |
return true; | |
} | |
#if defined(_MSC_VER) | |
#pragma pack(pop) | |
#endif | |
int main(const int argc, char *args[]) { | |
if (argc<3) { | |
printf("%s input.wav output.aiff\n", args[0]); | |
return 1; | |
} | |
sound_t sound; | |
if (!load_wav(args[1], sound)) { | |
printf("error loading wave\n"); | |
return 1; | |
} | |
if (!save_aif(args[2], sound)) { | |
printf("error saving aiff\n"); | |
return 1; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment