You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
2.8 KiB
119 lines
2.8 KiB
#pragma once
|
|
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <fstream>
|
|
|
|
#include <boost/iterator/iterator_facade.hpp>
|
|
|
|
namespace wav {
|
|
struct riff_chunk {
|
|
char chunk_id[4]; // "RIFF"
|
|
uint32_t chunk_size; // size of rest of file
|
|
char format[4]; // "WAVE"
|
|
};
|
|
|
|
//8-bit samples are stored as unsigned bytes, ranging
|
|
// from 0 to 255. 16-bit samples are stored as 2's-complement
|
|
// signed integers, ranging from -32768 to 32767. :(
|
|
struct fmt_chunk {
|
|
char chunk_id[4]; // "fmt "
|
|
uint32_t chunk_size; // size of rest of subchunk
|
|
uint16_t audio_format; // 1 == PCM
|
|
uint16_t channels; // 1 == mono, 2 = stereo
|
|
uint32_t sample_rate; // eg. 44100
|
|
uint32_t byte_rate; // == sample_rate * channels * bits_per_sample/8
|
|
uint16_t block_align; // channels * bits_per_sample/8
|
|
uint16_t bits_per_sample; // 8
|
|
};
|
|
|
|
struct data_chunk {
|
|
char chunk_id[4]; // "data"
|
|
uint32_t chunk_size; // size of actual sound
|
|
// + data
|
|
};
|
|
|
|
static_assert(sizeof(riff_chunk) == 12, "wrong size");
|
|
static_assert(sizeof(fmt_chunk) == 24, "wrong size");
|
|
static_assert(sizeof(data_chunk) == 8, "wrong size");
|
|
|
|
class handle{
|
|
struct const_iterator; // forward decl
|
|
|
|
public:
|
|
handle(std::string filename){
|
|
file.open(filename, std::ios_base::in);
|
|
read(riff);
|
|
assert(strncmp("RIFF", riff.chunk_id, 4) == 0);
|
|
assert(strncmp("WAVE", riff.format, 4) == 0);
|
|
|
|
read(fmt);
|
|
assert(strncmp("fmt ", fmt.chunk_id, 4) == 0);
|
|
assert(fmt.audio_format == 1);
|
|
assert(fmt.channels == 1);
|
|
assert(fmt.bits_per_sample == 8);
|
|
|
|
read(data);
|
|
assert(strncmp("data", data.chunk_id, 4) == 0);
|
|
}
|
|
|
|
const_iterator begin(){
|
|
return {this};
|
|
}
|
|
|
|
const_iterator end(){
|
|
return {};
|
|
}
|
|
|
|
void rewind(unsigned int sample){
|
|
auto start = sizeof(riff) + sizeof(fmt) + sizeof(data);
|
|
auto offset = sample * fmt.bits_per_sample / 8 * fmt.channels;
|
|
file.pubseekpos(start + offset, std::ios_base::in);
|
|
}
|
|
|
|
private:
|
|
struct const_iterator : public boost::iterator_facade<const_iterator, double const, boost::forward_traversal_tag>{
|
|
const_iterator() = default;
|
|
const_iterator(handle* bp)
|
|
: back_ptr(bp)
|
|
{
|
|
++*this;
|
|
}
|
|
|
|
private:
|
|
friend class boost::iterator_core_access;
|
|
|
|
void increment() {
|
|
uint8_t data = 0;
|
|
if(back_ptr->file.sgetn(reinterpret_cast<char*>(&data), 1) > 0){
|
|
value = data / 255.0 - 0.5;
|
|
} else {
|
|
back_ptr = nullptr;
|
|
}
|
|
}
|
|
|
|
bool equal(const_iterator const& other) const {
|
|
return back_ptr == other.back_ptr;
|
|
}
|
|
|
|
double const & dereference() const {
|
|
return value;
|
|
}
|
|
|
|
handle* back_ptr = nullptr;
|
|
double value;
|
|
};
|
|
|
|
template<typename T>
|
|
void read(T& thing){
|
|
file.sgetn(reinterpret_cast<char*>(&thing), sizeof(T));
|
|
}
|
|
|
|
std::filebuf file;
|
|
riff_chunk riff;
|
|
fmt_chunk fmt;
|
|
data_chunk data;
|
|
};
|
|
}
|
|
|