ni-media

An open source audi/o stream library

Marc Boucek

Native Instruments

https://github.com/NativeInstruments/ni-media

Design goals

  • Generic
  • Modern
  • Performant
  • Modular
  • Extensible
  • Etc etc ... :)

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

The Boost Iostreams Library

Boost Iostreams Aims

  • To make it easy to create standard C++ streams and stream buffers for accessing new Sources and Sinks
  • To provide a framework for defining Filters and attaching them to standard streams and stream buffers.
  • To provide a collection of ready-to-use Filters, Sources and Sinks.

Boost Iostreams Concepts

  • Source: provides read access to a sequence of characters
  • Sink: provides write access to a sequence of characters.
  • InputFilter: filters input read from a Source.
  • OutputFilter: filters output written to a Sink.

Boost Iostreams Concepts

  • Sources, Sinks and their refinements are called Devices.
  • InputFilters, OutputFilters and their refinements are called Filters

Source Concept


class source
{
public:
    using char_type = char;
    struct category : boost::iostreams::input
                    , boost::iostreams::device_tag{};

    std::streamsize read( char_type* s, std::streamsize n );

}
                        

Source Concept


class seekable_source
{
public:
    using char_type = char;
    struct category : boost::iostreams::input_seekable
                    , boost::iostreams::device_tag{};

    std::streamsize read( char_type* s, std::streamsize n );
    std::streampos  seek( boost::iostreams::stream_offset off, BOOST_IOS::seekdir way );
}
						

Sink Concept


class sink
{
public:
    using char_type = char;
    struct category : boost::iostreams::output
                    , boost::iostreams::device_tag{};

    std::streamsize write( char_type* s, std::streamsize n );

}
						

Sink Concept


class seekable_sink
{
public:
    using char_type = char;
    struct category : boost::iostreams::output_seekable
                    , boost::iostreams::device_tag{};

    std::streamsize write( char_type* s, std::streamsize n );
    std::streampos  seek( boost::iostreams::stream_offset off, BOOST_IOS::seekdir way );
}
						

Creating A Container Source


template <class Container>
class container_source
{
public:
    using char_type = typename Container::value_type;
    struct category : boost::iostreams::input_seekable
                    , boost::iostreams::device_tag{};

    container_device( Container container )
    : m_data( std::move( container ) )
    {
    }

    std::streamsize read( char_type* s, std::streamsize n )
    {
        auto result = std::min( n, std::streamsize( m_data.size() - m_pos ) );
        if ( result != 0 )
        {
            std::copy( m_data.begin() + m_pos, m_data.begin() + m_pos + result, s );
            m_pos += result;
            return result;
        }
        else
        {
            return -1; // EOF
        }
    }

    std::streampos seek( boost::iostreams::stream_offset off, BOOST_IOS::seekdir way )
    {
        auto beg = std::streampos( 0 );
        auto pos = std::streampos( m_pos );
        auto end = std::streampos( m_data.size() );

        m_pos = size_t( absolute_position( pos, beg, end, off, way ) );
        return m_pos;
    }

private:
    Container m_data;
    size_t    m_pos = 0;
};

                        

Creating A Container Sink


template <class Container>
class container_sink
{
public:
    using char_type = typename Container::value_type;
    struct category : boost::iostreams::output_seekable
                    , boost::iostreams::device_tag{};

    container_device( Container container )
    : m_data( std::move( container ) )
    {
    }

    std::streamsize write( const char_type* s, std::streamsize n )
    {
        std::streamsize result = 0;
        if ( m_pos != m_data.size() )
        {
            result = std::min( n, std::streamsize( m_data.size() - m_pos ) );
            std::copy( s, s + result, m_data.begin() + m_pos );
            m_pos += result;
        }
        if ( result < n )
        {
            m_data.insert( m_data.end(), s, s + n );
            m_pos = m_data.size();
        }
        return n;
    }

    std::streampos seek( boost::iostreams::stream_offset off, BOOST_IOS::seekdir way )
    {
        auto beg = std::streampos( 0 );
        auto pos = std::streampos( m_pos );
        auto end = std::streampos( m_data.size() );

        m_pos = size_t( absolute_position( pos, beg, end, off, way ) );
        return m_pos;
    }

private:
    Container m_data;
    size_t    m_pos = 0;
};

                        

Creating A Container Device


template <class Container>
class container_device
{
public:
    using char_type = typename Container::value_type;
    using category  = boost::iostreams::seekable_device_tag;


    container_device( Container container )
    : m_data( std::move( container ) )
    {
    }

    std::streamsize read( char_type* s, std::streamsize n )
    {
        auto result = std::min( n, std::streamsize( m_data.size() - m_pos ) );
        if ( result != 0 )
        {
            std::copy( m_data.begin() + m_pos, m_data.begin() + m_pos + result, s );
            m_pos += result;
            return result;
        }
        else
        {
            return -1; // EOF
        }
    }

    std::streamsize write( const char_type* s, std::streamsize n )
    {
        std::streamsize result = 0;
        if ( m_pos != m_data.size() )
        {
            result = std::min( n, std::streamsize( m_data.size() - m_pos ) );
            std::copy( s, s + result, m_data.begin() + m_pos );
            m_pos += result;
        }
        if ( result < n )
        {
            m_data.insert( m_data.end(), s, s + n );
            m_pos = m_data.size();
        }
        return n;
    }

    std::streampos seek( boost::iostreams::stream_offset off, BOOST_IOS::seekdir way )
    {
        auto beg = std::streampos( 0 );
        auto pos = std::streampos( m_pos );
        auto end = std::streampos( m_data.size() );

        m_pos = size_t( absolute_position( pos, beg, end, off, way ) );
        return m_pos;
    }

private:
    Container m_data;
    size_t    m_pos = 0;
};

						

Creating A Container Source


template <class Container>
class container_source : private container_device<Container>
{
    using base_type = container_device<Container>;

public:
    using char_type = typename base_type::char_type;

    struct category : boost::iostreams::input_seekable
                    , boost::iostreams::device_tag{};

    using base_type::base_type;
    using base_type::read;
    using base_type::seek;
};
						

Creating A Container Sink


template <class Container>
class container_sink : private container_device<Container>
{
    using base_type = container_device<Container>;

public:
    using char_type = typename base_type::char_type;

    struct category : boost::iostreams::output_seekable
                    , boost::iostreams::device_tag{};

    using base_type::base_type;
    using base_type::write;
    using base_type::seek;
};
						

NI Audiostream Library

Extending boost::iostreams concepts for audio

  • Samplerate
  • Number of channels
  • Channel layout? Interleaved!
  • Data format? Pcm Format!

Extending boost::iostreams concepts for audio

  • Audio Source: provides read access to a sequence of interleaved audio pcm data
  • Sink: provides write access to a sequence of interleaved audio pcm data.
  • Audio Sources, Sinks and their refinements are called Audio Devices.
  • Audio Devices must provide additional audio specific information.

Audio Source Concept


class audio_source
{
public:
    using char_type = char;
    struct category : boost::iostreams::input
                    , boost::iostreams::device_tag{};

    using info_type = audio::stream_info;

    info_type info() const;

    std::streamsize read( char_type* s, std::streamsize n );

}
						

Audio Source Concept


class seekable_audio_source
{
public:
    using char_type = char;
    struct category : boost::iostreams::input_seekable
                    , boost::iostreams::device_tag{};

    using info_type = audio::stream_info;

    info_type info() const;

    std::streamsize read( char_type* s, std::streamsize n );
    std::streampos seek( boost::iostreams::stream_offset off, BOOST_IOS::seekdir way );
}
						

Audio Sink Concept


class audio_sink
{
public:
    using char_type = char;
    struct category : boost::iostreams::output
                    , boost::iostreams::device_tag{};

    using info_type = audio::stream_info;

    audio_sink( info_type info );

    std::streamsize write( char_type* s, std::streamsize n );

}
						

Audio Sink Concept


class seekable_audio_sink
{
public:
    using char_type = char;
    struct category : boost::iostreams::output_seekable
                    , boost::iostreams::device_tag{};

    using info_type = audio::stream_info;

    audio_sink( info_type info );

    std::streamsize write( char_type* s, std::streamsize n );
    std::streampos seek( boost::iostreams::stream_offset off, BOOST_IOS::seekdir way );
}
						

Audio Container Source


template <class Container, class Info>
class container_source : private boostext::iostreams::container_source<Container>
{
    using base_type = boostext::iostreams::container_source<Container>;

public:
    using base_type::char_type;
    using base_type::category;
    using info_type = Info;

    container_source( Container container, const info_type& info );

    info_type info() const { return m_info; }

    using base_type::read;
    using base_type::seek;

private:
    info_type m_info;
};
						

Audio Container Sink


template <class Container, class Info>
class container_sink : private boostext::iostreams::container_sink<Container>
{
    using base_type = boostext::iostreams::container_sink<Container>;

public:
    using base_type::char_type;
    using base_type::category;
    using info_type = Info;

    container_sink( Container container, const info_type& info );

    info_type info() const { return m_info; }

    using base_type::write;
    using base_type::seek;

private:
    info_type m_info;
};

						

NI Audiostream Library

From Audio Devices to Audio Streams

From Audio Source to audio::istream


namespace audio
{

class istream_info
{

public:
    virtual ~istream_info() = default;

    using format_type = ::pcm::format;

    void format( format_type format );
    auto format() const -> format_type;

    void   sample_rate( size_t val );
    size_t sample_rate() const;

    void   num_channels( size_t num_channels );
    size_t num_channels() const;

    size_t bits_per_sample() const;
    size_t bytes_per_sample() const;
    size_t bytes_per_frame() const;

    friend bool operator==( const istream_info& lhs, const istream_info& rhs );
    friend bool operator!=( const istream_info& lhs, const istream_info& rhs );

private:
    // Members
    size_t      m_sample_rate  = 44100; //!< sample rate
    size_t      m_num_channels = 1;     //!< number of channels
    format_type m_format;               //!< pcm format
};

} // namespace audio
                        

From Audio Source to audio::istream


namespace audio
{

class istream : protected std::istream
{
public:
    istream();

    template <class AudioSource>
    istream( AudioSource source );

    istream( const istream& ) = delete;
    istream( istream&& );

    istream& operator=( istream& ) = delete;
    istream& operator              =( istream&& );

    // - std::istream

    using char_type   = std::istream::char_type;
    using traits_type = std::istream::traits_type;
    using int_type    = std::istream::int_type;
    using pos_type    = std::istream::pos_type;
    using off_type    = std::istream::off_type;

    using std::istream::operator bool;
    using std::istream::operator!;
    using std::istream::bad;
    using std::istream::clear;
    using std::istream::eof;
    using std::istream::fail;
    using std::istream::good;
    using std::istream::rdstate;
    using std::istream::setstate;
    using std::istream::rdbuf;

    auto read( char_type* s, std::streamsize n ) -> istream&;

    template <class Value>
    auto operator>>( Value& val ) -> std::enable_if_t<std::is_arithmetic<Value>::value, istream&>;

    template <class Range>
    auto operator>>( Range&& rng ) -> std::enable_if_t<boost::has_range_iterator<Range>::value, istream&>;

    auto seekg( pos_type pos ) -> istream&;
    auto sample_seekg( pos_type pos ) -> istream&;
    auto frame_seekg( pos_type pos ) -> istream&;

    auto seekg( off_type off, std::ios_base::seekdir dir ) -> istream&;
    auto sample_seekg( off_type off, std::ios_base::seekdir dir ) -> istream&;
    auto frame_seekg( off_type off, std::ios_base::seekdir dir ) -> istream&;

    using std::istream::gcount;
    auto sample_gcount() const -> std::streamsize;
    auto frame_gcount() const -> std::streamsize;

    using std::istream::tellg;
    auto frame_tellg() -> pos_type;
    auto sample_tellg() -> pos_type;

    // - stream_info

    using info_type = istream_info;

    virtual auto info() const -> const info_type&;

private:
    std::unique_ptr<info_type> m_info;
    std::unique_ptr<streambuf> m_streambuf;
};

} // audio

						

From AudioFile Source to audio::ifstream


namespace audio
{

class ifstream_info : public istream_info
{
public:
    enum class container_type {aiff, flac, mp3, mp4, ogg, wav, wma};

    enum class codec_type {aac, alac, aiff, flac, mp3, vorbis, wav, wma};

    void   num_frames( size_t num_frames );
    size_t num_frames() const;

    void codec( codec_type value );
    auto codec() const -> codec_type;

    void container( container_type value );
    auto container() const -> container_type;

    friend bool operator==( const ifstream_info& lhs, const ifstream_info& rhs );
    friend bool operator!=( const ifstream_info& lhs, const ifstream_info& rhs );

private:
    size_t         m_num_frames = 0;
    container_type m_container;
    codec_type     m_codec;
};

} // namespace audio
                        

From AudioFile Source to audio::ifstream


// ifstream.h

namespace audio
{

class ifstream : public istream
{
public:
    using info_type = ifstream_info;

    ifstream() = default;
    ifstream( const std::string& file );
    ifstream( const std::string& file, info_type::container_type container, size_t stream_index = 0 );

    ifstream( ifstream&& );
    ifstream& operator=( ifstream&& );

    const info_type& info() const override;
};

} // audio


						

From AudioFile Source to audio::ifstream


// ifstream.cpp

istream make_istream( const std::string& file
                    , ifstream_info::container_type container, size_t stream_index = 0 )
{
    using container_type = ifstream_info::container_type;

    if ( container != container_type::mp4 && stream_index != 0 )
    {
        throw std::runtime_error( "Unsupported stream index" );
    }

    switch ( container )
    {

#if NIMEDIA_ENABLE_AIFF_DECODING
        case container_type::aiff:
            return { aiff_file_source( file ) };
#endif

#if NIMEDIA_ENABLE_FLAC_DECODING
        case container_type::flac:
            return { flac_file_source( file ) };
#endif

#if NIMEDIA_ENABLE_MP3_DECODING
        case container_type::mp3:
            return { mp3_file_source( file ) };
#endif

#if NIMEDIA_ENABLE_MP4_DECODING
        case container_type::mp4:
            return { mp4_file_source( file, stream_index ) };
#endif

#if NIMEDIA_ENABLE_OGG_DECODING
        case container_type::ogg:
            return { ogg_file_source( file ) };
#endif

#if NIMEDIA_ENABLE_WAV_DECODING
        case container_type::wav:
            return { wav_file_source( file ) };
#endif

#if NIMEDIA_ENABLE_WMA_DECODING
        case container_type::wma:
            return { wma_file_source( file ) };
#endif

        default:
            break;
    }

    throw std::runtime_error( "Unsupported container_type" );
}


istream make_istream( const std::string& file )
{
    if ( auto container = ifstream_container( file ) )
        return make_istream( file, *container );

    throw std::runtime_error( "Unsupported file extension" );
}

} // namespace


//----------------------------------------------------------------

ifstream::ifstream( const std::string& file )
{
    istream::operator=( make_istream( file ) );
}

} // audio
						

Supported formats

Format Windows Osx Ios Linux
wav native native native native
aiff native native native native
flac libFlac libFlac libFlac libFlac
ogg/vorbis libVorbis libVorbis libVorbis libVorbis
mp3 WMF CoreAudio CoreAudio No :(
mp4 WMF CoreAudio CoreAudio No :(

Supported formats

Format CMake Option
wav NIMEDIA_ENABLE_WAV_DECODING
aiff NIMEDIA_ENABLE_AIFF_DECODING
flac NIMEDIA_ENABLE_FLAC_DECODING
ogg/vorbis NIMEDIA_ENABLE_OGG_DECODING
mp3 NIMEDIA_ENABLE_MP3_DECODING
mp4 NIMEDIA_ENABLE_MP4_DECODING

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
						

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.aiff");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.flac");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.ogg");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.mp3");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.mp4");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.mp4");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
						

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.mp4");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
						

Pcm library

Pcm format description


namespace pcm
{

enum number_type : uint8_t
{
    signed_integer = 0,
    unsigned_integer,
    floating_point
};

enum bitwidth_type : uint8_t
{
    _8bit  = 8,
    _16bit = 16,
    _24bit = 24,
    _32bit = 32,
    _64bit = 64
};

enum endian_type : uint8_t
{
    big_endian = 0,
    little_endian,
#if defined( BOOST_BIG_ENDIAN )
    native_endian = big_endian,
#elif defined( BOOST_LITTLE_ENDIAN )
    native_endian = little_endian,
#else
#error "unable to determine system endianness."
#endif
};

} // pcm

						

Compiletime Pcm Format


namespace pcm
{

template <number_type n = signed_integer, bitwidth_type b = _8bit, endian_type e = native_endian>
struct compiletime_format
{
    constexpr auto number() const { return n; }
    constexpr auto bitwidth() const { return b; }
    constexpr auto endian() const { return e; }
};

template <number_type ln, bitwidth_type lb, endian_type le, number_type rn, bitwidth_type rb, endian_type re>
constexpr auto operator==( compiletime_format<ln, lb, le>, compiletime_format<rn, rb, re> )
{
    return std::is_same<compiletime_format<ln, lb, le>, compiletime_format<rn, rb, re>>::value;
}

template <number_type ln, bitwidth_type lb, endian_type le, number_type rn, bitwidth_type rb, endian_type re>
constexpr auto operator!=( compiletime_format<ln, lb, le>, compiletime_format<rn, rb, re> )
{
    return !std::is_same<compiletime_format<ln, lb, le>, compiletime_format<rn, rb, re>>::value;
}

} // namespace pcm
                        

Runtime Pcm Format


namespace pcm
{

struct runtime_format
{
    runtime_format( const runtime_format& ) = default;

    template <class Format = compiletime_format<>, class = enable_if_supported<Format>>
    constexpr runtime_format( const Format& fmt = {} )
    : m_number( fmt.number() )
    , m_bitwidth( fmt.bitwidth() )
    , m_endian( fmt.endian() )
    , m_index( uint8_t( detail::tuple_find<Format, decltype( compiletime_formats() )>::value ) )
    {
    }

    runtime_format( number_type n, bitwidth_type b, endian_type e = native_endian )
    {
        static auto const formats = detail::tuple_to_array<runtime_format>( compiletime_formats() );

        auto it = boost::find_if( formats, [n, b, e]( const auto& fmt ) {
            return fmt.number() == n && fmt.bitwidth() == b && fmt.endian() == e;
        } );

        if ( it == formats.end() )
            throw std::runtime_error( "Invalid format" );

        *this /*is*/ = *it;
    }

    auto number()   const { return m_number;   }
    auto bitwidth() const { return m_bitwidth; }
    auto endian()   const { return m_endian;   }
    auto index()    const { return m_index;    }

private:
    number_type   m_number;
    bitwidth_type m_bitwidth;
    endian_type   m_endian;
    uint8_t       m_index;
};

inline bool operator==( const runtime_format& lhs, const runtime_format& rhs )
{
    return lhs.index() == rhs.index();
}

inline bool operator!=( const runtime_format& lhs, const runtime_format& rhs )
{
    return lhs.index() != rhs.index();
}

} // namespace pcm
                        

Pcm Formats


namespace pcm
{

inline constexpr auto compiletime_formats()
{
    return std::tuple<compiletime_format<signed_integer, _8bit, big_endian>,
                      compiletime_format<signed_integer, _8bit, little_endian>,
                      compiletime_format<signed_integer, _16bit, big_endian>,
                      compiletime_format<signed_integer, _16bit, little_endian>,
                      compiletime_format<signed_integer, _24bit, big_endian>,
                      compiletime_format<signed_integer, _24bit, little_endian>,
                      compiletime_format<signed_integer, _32bit, big_endian>,
                      compiletime_format<signed_integer, _32bit, little_endian>,
                      compiletime_format<signed_integer, _64bit, big_endian>,
                      compiletime_format<signed_integer, _64bit, little_endian>,
                      compiletime_format<unsigned_integer, _8bit, big_endian>,
                      compiletime_format<unsigned_integer, _8bit, little_endian>,
                      compiletime_format<unsigned_integer, _16bit, big_endian>,
                      compiletime_format<unsigned_integer, _16bit, little_endian>,
                      compiletime_format<unsigned_integer, _24bit, big_endian>,
                      compiletime_format<unsigned_integer, _24bit, little_endian>,
                      compiletime_format<unsigned_integer, _32bit, big_endian>,
                      compiletime_format<unsigned_integer, _32bit, little_endian>,
                      compiletime_format<unsigned_integer, _64bit, big_endian>,
                      compiletime_format<unsigned_integer, _64bit, little_endian>,
                      compiletime_format<floating_point, _32bit, big_endian>,
                      compiletime_format<floating_point, _32bit, little_endian>,
                      compiletime_format<floating_point, _64bit, big_endian>,
                      compiletime_format<floating_point, _64bit, little_endian>>();
}

inline auto runtime_formats()
{
    static auto const fs = detail::tuple_to_array<runtime_format>( compiletime_formats() );
    return fs;
}

} // namespace pcm
                        

    => 24 formats

    => 576 conversions!

Pcm data read


namespace pcm {

namespace detail {
                            
template <typename Value, typename Iterator, typename Format>
Value read_impl( Iterator iter )
{
    auto data = intermediate<Format>( iter );
    return convert_to<Value>( data.value() );
}

template <typename Value, typename Iterator, typename... Ts>
auto make_read_impls( const std::tuple<Ts...>& ) -> std::array<Value ( * )( Iterator ), sizeof...( Ts )>
{
    return {{&read_impl<Value, Iterator, Ts>...}};
}

} // namespace detail

template <typename Value, typename Iterator, number_type n, bitwidth_type b, endian_type e>
Value read( Iterator iter, const compiletime_format<n, b, e>& )
{
    return detail::read_impl<Value, Iterator, compiletime_format<n, b, e>>( iter );
}

template <typename Value, typename Iterator, typename... Ts>
Value read( Iterator iter, const runtime_format& fmt )
{
    static auto const impls = detail::make_read_impls<Value, Iterator>( std::tuple<Ts...>{} );
    return impls.at( fmt.index() )( iter );
}

} // namespace pcm
						

Pcm data write



namespace pcm {

namespace detail {

template <typename Value, typename Iterator, typename Format>
void write_impl( Iterator iter, Value val )
{
    auto data = intermediate<Format>{convert_to<typename intermediate<Format>::value_type>( val )};
    std::copy( data.begin(), data.end(), iter );
}

template <typename Value, typename Iterator, typename... Ts>
auto make_write_impls( const std::tuple<Ts...>& ) -> std::array<void ( * )( Iterator, Value ), sizeof...( Ts )>
{
    return {{&write_impl<Value, Iterator, Ts>...}};
}

} // namespace detail

template <typename Value, typename Iterator, number_type n, bitwidth_type b, endian_type e>
void write( Iterator iter, Value val, const compiletime_format<n, b, e>& )
{
    detail::write_impl<Value, Iterator, compiletime_format<n, b, e>>( iter, val );
}

template <typename Value, typename Iterator, typename... Ts>
void write( Iterator iter, Value val, const runtime_format& fmt )
{
    static auto const impls = detail::make_write_impls<Value, Iterator>( std::tuple<Ts...>{} );
    impls.at( fmt.index() )( iter, val );
}

} // namespace pcm
						

Pcm format conversions


// floating point <- floating point
// static_cast
template <typename Target, typename Source>
Target convert_to( Source src )
{
    return static_cast<Target>( src );
}

// fixed point <- floating point
// clamp, upscale, round_cast, sign_cast
template <typename Target, typename Source>
Target convert_to( Source src )
{
    using boost::algorithm::clamp;

    using SignedTarget   = std::make_signed_t<Target>;
    using PromotedSource = promote_t<Source, sizeof( Target ) * 8>;

    static constexpr auto scale = PromotedSource{1ull << ( sizeof( Target ) * 8 - 1 )};
    static constexpr auto lo    = PromotedSource{-1};
    static constexpr auto hi    = ( scale - PromotedSource{1} ) / scale;

    return sign_cast<Target>( round_cast<SignedTarget>( scale * clamp( PromotedSource{src}, lo, hi ) ) );
}

// floating point <- fixed point
// sign_cast + downscale
template <typename Target, typename Source>
Target convert_to( Source src )
{
    using SignedSource   = std::make_signed_t<Source>;
    using PromotedTarget = promote_t<Target, sizeof( Source ) * 8>;

    static constexpr auto scale = PromotedTarget{1} / ( 1ull << ( sizeof( Source ) * 8 - 1 ) );

    return static_cast<Target>( scale * sign_cast<SignedSource>( src ) );
}

// fixed point <- fixed point
// sign_cast, shift_cast
template <typename Target, typename Source>
Target convert_to( Source src )
{
    using SignAdjustedSource = std::conditional_t<std::is_signed<Target>::value, //
                                                  std::make_signed_t<Source>,
                                                  std::make_unsigned_t<Source>>;

    return shift_cast<Target>( sign_cast<SignAdjustedSource>( src ) );
}
                        

Pcm iterator


template <typename Value,
          typename Iterator,
          typename Format,
          typename Category = typename std::iterator_traits<Iterator>::iterator_category>
class iterator : public boost::iterator_facade<iterator<Value, Iterator, Format, Category>,
                                               Value,
                                               Category,
                                               iterator_reference_t<Value, Iterator, Format>>
{

public:

    iterator() = default;

    template <class OtherIterator,
              class OtherFormat,
              class = std::enable_if_t<std::is_convertible<OtherIterator, Iterator>::value
                                       && std::is_convertible<OtherFormat, Format>::value>>
    iterator( const iterator<Value, OtherIterator, OtherFormat>& other )
    : m_proxy( other.m_proxy )
    {
    }

    iterator( Iterator iter, const Format& fmt )
    : m_proxy( fmt, iter )
    {
    }

private:
    void increment()
    {
        std::advance( m_proxy.iter, m_proxy.step() );
    }

    void decrement()
    {
        std::advance( m_proxy.iter, -m_proxy.step() );
    }

    void advance( difference_type offset )
    {
        std::advance( m_proxy.iter, offset * m_proxy.step() );
    }

    difference_type distance_to( const iterator& other ) const
    {
        return std::distance( m_proxy.iter, other.m_proxy.iter ) / m_proxy.step();
    }

    template <class OtherIterator,
              class OtherFormat,
              class = std::enable_if_t<std::is_convertible<OtherIterator, Iterator>::value
                                       && std::is_convertible<OtherFormat, Format>::value>>
    bool equal( const iterator<Value, OtherIterator, OtherFormat>& other ) const
    {
        return equal_format( m_proxy.format(), other.m_proxy.format() ) && m_proxy.iter == other.m_proxy.iter;
    }

    reference dereference() const
    {
        return m_proxy;
    }

    friend class ::boost::iterator_core_access;

    proxy_t m_proxy;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <class Value, class Iterator, class Format>
struct proxy : Format
{
    proxy() = default;

    proxy& operator=( Value val )
    {
        write( iter, val, format() );
        return *this;
    }

    operator Value() const
    {
        return read<Value>( iter, format() );
    }

private:

    template <class OtherIterator,
              class OtherFormat,
              class = std::enable_if_t<std::is_convertible<OtherIterator, Iterator>::value
                                       && std::is_convertible<OtherFormat, Format>::value>>
    proxy( const proxy<Value, OtherIterator, OtherFormat>& other )
    : Format( other.format() )
    , iter( other.iter )
    {
    }

    proxy( const Format& fmt, Iterator it )
    : Format( fmt )
    , iter( it )
    {
    }

    auto step() const -> typename std::iterator_traits<Iterator>::difference_type
    {
        return format().bitwidth() / 8;
    }

    const Format& format() const
    {
        return static_cast<const Format&>( *this );
    }

    Iterator iter;
};
                        

Pcm iterator usage


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    std::copy( pcm::make_iterator<float>( in.begin(), format ) 
             , pcm::make_iterator<float>( in.end(), format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    std::copy( in.begin()
             , in.end()
             , pcm::make_iterator<float>( out.begin(), format ) );
}
                        

Pcm iterator usage


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    std::copy( pcm::make_iterator<float>( in.begin(), format ) 
             , pcm::make_iterator<float>( in.end(), format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    std::copy( in.begin()
             , in.end()
             , pcm::make_iterator<float>( out.begin(), format ) );
}
                        

Pcm range adaptor usage


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    boost::copy( in | pcm::converted_to<float>( format )
               , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    boost::copy( in
               , std::begin( out | pcm::converted_to<float>( format ) ) );
}
                        

Pcm range adaptor usage


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    boost::copy( in | pcm::converted_to<float>( format )
               , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    boost::copy( in
               , std::begin( out | pcm::converted_to<float>( format ) ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    std::copy( pcm::make_iterator<float>( in.begin(), format ) 
             , pcm::make_iterator<float>( in.end(), format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    std::copy( in.begin()
             , in.end()
             , pcm::make_iterator<float>( out.begin(), format ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    std::copy( pcm::make_iterator<float>( in.begin(), format ) 
             , pcm::make_iterator<float>( in.end(), format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    std::copy( in.begin()
             , in.end()
             , pcm::make_iterator<float>( out.begin(), format ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    pcm::copy( pcm::make_iterator<float>( in.begin(), format ) 
             , pcm::make_iterator<float>( in.end(), format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    pcm::copy( in.begin()
             , in.end()
             , pcm::make_iterator<float>( out.begin(), format ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    pcm::copy( pcm::make_iterator<float>( in.begin(), format ) 
             , pcm::make_iterator<float>( in.end(), format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    pcm::copy( in.begin()
             , in.end()
             , pcm::make_iterator<float>( out.begin(), format ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    boost::copy( in | pcm::converted_to<float>( format )
               , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    boost::copy( in
               , std::begin( out | pcm::converted_to<float>( format ) ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    boost::copy( in | pcm::converted_to<float>( format )
               , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    boost::copy( in
               , std::begin( out | pcm::converted_to<float>( format ) ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    pcm::copy( in | pcm::converted_to<float>( format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format(pcm::signed_integer, pcm::_16bit, pcm::little_endian);

    pcm::copy( in
             , std::begin( out | pcm::converted_to<float>( format ) ) );
}
                        

Efficient pcm algorithms


{ // read pcm data
    std::vector<char> in;
    std::vector<float> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    pcm::copy( in | pcm::converted_to<float>( format )
             , out.begin() );
}

{ // write pcm data
    std::vector<float> in;
    std::vector<char> out;

    auto format = pcm::make_format<pcm::signed_integer, pcm::_16bit, pcm::little_endian>();

    pcm::copy( in
             , std::begin( out | pcm::converted_to<float>( format ) ) );
}
                        

Algorithm dispatching by Hand


namespace pcm
{

template <class OutputIt, class Value, class Iterator>
auto copy( iterator<Value,Iterator,runtime_format> beg
         , iterator<Value,Iterator,runtime_format> end
         , OutputIt out )
{
    assert ( beg.format() == end.format() );

    auto fmt = beg.format();

    if ( fmt == compiletime_format<signed_integer, _8bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _8bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _8bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _8bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _8bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _8bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _16bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _16bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _16bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _16bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _16bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _16bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _24bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _24bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _24bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _24bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _24bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _24bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _32bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _32bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _32bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _32bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _32bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _32bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _64bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _64bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _64bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<signed_integer, _64bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<signed_integer, _64bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<signed_integer, _64bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _8bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _8bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _8bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _8bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _8bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _8bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _16bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _16bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _16bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _16bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _16bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _16bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _24bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _24bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _24bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _24bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _24bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _24bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _32bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _32bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _32bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _32bit, little_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _32bit, little_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _32bit, little_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _64bit, big_endian>{} )
        return std::copy( make_iterator<Value>( beg.base(), compiletime_format<unsigned_integer, _64bit, big_endian>{} )
                        , make_iterator<Value>( end.base(), compiletime_format<unsigned_integer, _64bit, big_endian>{} )
                        , out );

    else if ( fmt == compiletime_format<unsigned_integer, _64bit, little_endian>{} )


    .
    .
    .
    
}
} // namespace pcm
                        

Algorithm dispatching by Hand


namespace pcm
{

template <class InputIt, class Value, class Iterator>
auto copy( InputIt beg
         , InputIt end
         , iterator<Value,Iterator, runtime_format> out )
{
    auto out = beg.format();

    if ( fmt == compiletime_format<signed_integer, _8bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _8bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _8bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _8bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _16bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _16bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _16bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _16bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _24bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _24bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _24bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _24bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _32bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _32bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _32bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _32bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _64bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _64bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<signed_integer, _64bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<signed_integer, _64bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _8bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _8bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _8bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _8bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _16bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _16bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _16bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _16bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _24bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _24bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _24bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _24bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _32bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _32bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _32bit, little_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _32bit, little_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _64bit, big_endian>{} )
        return std::copy( beg
                        , end
                        , make_iterator<Value>( out.base(), compiletime_format<unsigned_integer, _64bit, big_endian>{} ) );

    else if ( fmt == compiletime_format<unsigned_integer, _64bit, little_endian>{} )


    .
    .
    .
    
}
} // namespace pcm
                        

Generic Algorithm dispatching


namespace pcm
{
namespace detail
{

struct copy_impl
{
    template <class InputIt, class OutputIt>
    OutputIt operator()( InputIt beg, InputIt end, OutputIt out ) const
    {
        return std::copy( beg, end, out );
    }
};

} // namespace detail

// iterator based
template <class InputIt, class OutputIt>
auto copy( InputIt beg, InputIt end, OutputIt out )
{
    return dispatch( detail::copy_impl{}, beg, end, out );
}

// range based
template <class InputRange, class OutputIt>
auto copy( const InputRange& range, OutputIt out )
{
    return ::pcm::copy( std::begin( range ), std::end( range ), out );
}

} // namespace pcm
                        

Generic Algorithm dispatching


namespace pcm
{
namespace detail
{

struct copy_n_impl
{
    template <class InputIt, class Size, class OutputIt>
    OutputIt operator()( InputIt first, Size count, OutputIt result ) const
    {
        return std::copy_n( first, count, result );
    }
};

} // namespace detail

template <class InputIt, class Size, class OutputIt>
OutputIt copy_n( InputIt first, Size count, OutputIt result )
{
    return dispatch( detail::copy_n_impl{}, first, count, result );
}

} // namespace pcm
                        

Generic Algorithm dispatching


namespace pcm { namespace detail {

struct catch_t{};
struct try_t : catch_t{};

template <class Result, class F>
auto invoke( try_t, F f, Ts... ts ) -> decltype( f( ts... ), Result() )
{
    return f( ts... );
}

template <class Result, class F>
Result invoke( catch_t, F, Ts... )
{
    throw std::runtime_error( "runtime argument mismatch" );
}

template <class...>
struct dispatch_impl{};

// Base case
template <class R, class F, class... Bs, class... Fs>
struct dispatch_impl<R, F, std::tuple<>, std::tuple<Bs...>, std::tuple<Fs...>>
{
    auto operator()( F f, Bs... bs )
    {
        return invoke<R>( try_t{}, f, bs... );
    }
};

// Recursive case without dispatch
template <class R, class F, class A, class... As, class... Bs, class... Fs>
struct dispatch_impl<R, F, std::tuple<A, As...>, std::tuple<Bs...>, std::tuple<Fs...>>
{
    auto operator()( F f, A a, As... as, Bs... bs )
    {
        auto impl = dispatch_impl<R, F, std::tuple<As...>, std::tuple<Bs..., A>, std::tuple<Fs...>>();
        return impl( f, as..., bs..., a );
    }
};

// Recursive case, with dispatch, with return type
template <class R, class F, class V, class I, class... As, class... Bs, class... Fs>
struct dispatch_impl<R, F, std::tuple<iterator<V, I>, As...>, std::tuple<Bs...>, std::tuple<Fs...>>
{
    auto operator()( F f, iterator<V, I> it, As... as, Bs... bs )
    {
        R r;
        []( ... ) {}( it.format() == Fs{}
                      && ( r = ( dispatch_impl<R,
                                               F,
                                               std::tuple<As...>,
                                               std::tuple<Bs..., iterator<V, I, Fs>>,
                                               std::tuple<Fs...>>()(
                               f, as..., bs..., make_iterator<V>( it.base(), Fs{} ) ) ),
                           false )... );
        return r;
    }
};

// Recursive case, with dispatch, without return type
template <class F, class V, class I, class... As, class... Bs, class... Fs>
struct dispatch_impl<void, F, std::tuple<iterator<V, I>, As...>, std::tuple<Bs...>, std::tuple<Fs...>>
{
    auto operator()( F f, iterator<V, I> it, As... as, Bs... bs )
    {
        []( ... ) {}( it.format() == Fs{}
                      && ( dispatch_impl<void,
                                         F,
                                         std::tuple<As...>,
                                         std::tuple<Bs..., iterator<V, I, Fs>>,
                                         std::tuple<Fs...>>()(
                               f, as..., bs..., make_iterator<V>( it.base(), Fs{} ) ),
                           false )... );
    }
};

} // namespace detail

template <class F, class... Ts>
auto dispatch( F f, Ts... ts )
{
    using Result  = decltype( f( ts... ) );
    using Formats = decltype( compiletime_formats() );
    auto impl     = detail::dispatch_impl<Result, F, std::tuple<Ts...>, std::tuple<>, Formats>();
    return impl( f, ts... );
}

} // namespace pcm
                        

Usage in audio::istream


template <class Range>
auto istream::operator>>( Range&& rng ) -> std::enable_if_t<boost::has_range_iterator<Range>::value, istream&>
{
    using Value      = typename boost::range_value<Range>::type;
    using Difference = typename boost::range_difference<Range>::type;

    auto beg = std::begin( rng );
    auto end = std::end( rng );

    const auto samples_requested = std::distance( beg, end );
    const auto samples_available = Difference( m_info->num_samples() - sample_tellg() );

    auto iter = pcm::make_iterator<Value>( std::istreambuf_iterator<char>( rdbuf() ), m_info->format() );
    std::fill( pcm::copy_n( iter, std::min( samples_requested, samples_available ), beg ), end, Value{} );

    if ( samples_available < samples_requested )
        setstate( rdstate() | eofbit );

    return *this;
}
                        

Usage in audio::istream


template <class Range>
auto istream::operator>>( Range&& rng ) -> std::enable_if_t<boost::has_range_iterator<Range>::value, istream&>
{
    using Value      = typename boost::range_value<Range>::type;
    using Difference = typename boost::range_difference<Range>::type;

    auto beg = std::begin( rng );
    auto end = std::end( rng );

    const auto samples_requested = std::distance( beg, end );
    const auto samples_available = Difference( m_info->num_samples() - sample_tellg() );

    auto iter = pcm::make_iterator<Value>( std::istreambuf_iterator<char>( rdbuf() ), m_info->format() );
    std::fill( pcm::copy_n( iter, std::min( samples_requested, samples_available ), beg ), end, Value{} );

    if ( samples_available < samples_requested )
        setstate( rdstate() | eofbit );

    return *this;
}
                        

Usage in audio::istream


template <class Range>
auto istream::operator>>( Range&& rng ) -> std::enable_if_t<boost::has_range_iterator<Range>::value, istream&>
{
    using Value      = typename boost::range_value<Range>::type;
    using Difference = typename boost::range_difference<Range>::type;

    auto beg = std::begin( rng );
    auto end = std::end( rng );

    const auto samples_requested = std::distance( beg, end );
    const auto samples_available = Difference( m_info->num_samples() - sample_tellg() );

    auto iter = pcm::make_iterator<Value>( std::istreambuf_iterator<char>( rdbuf() ), m_info->format() );
    std::fill( pcm::copy_n( iter, std::min( samples_requested, samples_available ), beg ), end, Value{} );

    if ( samples_available < samples_requested )
        setstate( rdstate() | eofbit );

    return *this;
}
                        

Usage in audio::istream


template <class Range>
auto istream::operator>>( Range&& rng ) -> std::enable_if_t<boost::has_range_iterator<Range>::value, istream&>
{
    using Value      = typename boost::range_value<Range>::type;
    using Difference = typename boost::range_difference<Range>::type;

    auto beg = std::begin( rng );
    auto end = std::end( rng );

    const auto samples_requested = std::distance( beg, end );
    const auto samples_available = Difference( m_info->num_samples() - sample_tellg() );

    auto iter = pcm::make_iterator<Value>( std::istreambuf_iterator<char>( rdbuf() ), m_info->format() );
    std::fill( pcm::copy_n( iter, std::min( samples_requested, samples_available ), beg ), end, Value{} );

    if ( samples_available < samples_requested )
        setstate( rdstate() | eofbit );

    return *this;
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.aiff");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.flac");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.ogg");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.mp3");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.mp4");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<float>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<double>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<int8_t>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<uint8_t>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<int16_t>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<uint16_t>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<int32_t>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<uint32_t>(1024 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<uint32_t>(256 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::vector<uint32_t>(42 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = std::list<uint32_t>(42 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?


#include <ni/media/audio/ifstream.h>
#include <vector>

int main()
{
    auto stream = audio::ifstream("hello.wav");
    auto samples = my::array<uint32_t>(42 * stream.info().num_channels());
    while(stream >> samples)
        process(samples);
}
                        

std stream for audio?

std stream for audio!

Contributors!

  • Nathan Kozlowski
  • Moritz Heppner
  • Matthew Goldsmith
  • Kevin Dixon
  • Peter Vasil
  • Andrea Savio
  • David Terry
  • Eike Verdenhalven

Special Thanks!

  • AndrĂ© Bergner
  • Tobias Baumbach

https://github.com/NativeInstruments/ni-media