Click here to Skip to main content
15,896,727 members
Articles / Desktop Programming / WTL

Multidevice ASIO output plugin for WinAMP

Rate me:
Please Sign up or sign in to vote.
4.80/5 (9 votes)
13 Feb 2009CDDL27 min read 48.3K   728   23  
A tiny WinAMP output DLL that uses a C++ replacement of the official ASIO SDK that supports multiple ASIO devices.
#pragma once

#include "tlsObject.hpp"

#include <cassert>
#include <list>
#include <vector>


typedef unsigned char     BYTE;
typedef std::vector<BYTE> Buffer;


////////////////////////////////////////////////////////////////////////////////
//
//  MultiChannelBuffer
//  ------------------
//
//  Models a noninterleaved multi channel buffer that uses/presupposes the fact
// that all channels are equal in size and writing/reading is always done
// to/from all channels.
//  Supports a single writer and multiple readers/consumers from different
// threads. The writer and the readers are generally completely independent so
// they must use the query interface functions to see if/how much can they
// read/write.
//
//  (todo) rename to QueryMultiChannelBuffer or some nicer name that will better
//         emphasize its usage.
//
////////////////////////////////////////////////////////////////////////////////

class MultiChannelBuffer
{
public:
    MultiChannelBuffer();

    size_t channelSize     () const { return channelSize_; }
    size_t numberOfChannels() const { assert( channelSize() ); return totalSize() / channelSize(); }
    size_t totalSize       () const { return buffer_.size(); }

    bool hasUnreadInput() const;

    void reset();
    void resize( size_t numberOfChannels, size_t bytesPerChannel, size_t maximumExpectedWriteChunk );
    
    class Reader;
    Reader createNewReader() const;

    class WritePointers;
    size_t        canWrite() const;
    WritePointers getWritePointers( size_t sz );

    size_t bytesWritten() const { return bytesWritten_; }

private:  // WritePointers private interface.
    void writeCompletedCallback( size_t sizeOfWrittenChunk );

private:
    //typedef std::vector<Reader *> Readers;
    typedef std::list<Reader *> Readers;

    Readers & readers() { return readers_; }

    bool isReaderRegistered( Reader const & reader ) const;
    void registerReader    ( Reader       & reader ) const;
    void unregisterReader  ( Reader       & reader ) const;

    Buffer::const_iterator MultiChannelBuffer::slowestReaderPosition() const;

    bool isInResetState() const;
    bool allReadersHaveTheSameEndMarker() const;

    Buffer::iterator writeCursor      () const { return writePosition_                 ; }
    size_t           writeCursorOffset() const { return writeCursor() - buffer_.begin(); }

private:
	mutable Buffer  buffer_ ;
    mutable Readers readers_;
    mutable Buffer::iterator writePosition_;
    size_t channelSize_;
    size_t bytesWritten_;
    size_t minimumSpaceThatMustRemain_;
};


////////////////////////////////////////////////////////////////////////////////
//
//  MultiChannelBuffer::Reader
//  --------------------------
//
//  Models a single reader of a MultiChannelBuffer.
//
////////////////////////////////////////////////////////////////////////////////

class MultiChannelBuffer::Reader
{
public:
    Reader( Reader const & );
    ~Reader() { parent_.unregisterReader( *this ); }

    class ChannelIterator;
    ChannelIterator getChunk( size_t desiredChunkSize );

    size_t availableDataPerChannel() const { return availableDataPerChannel_; }    

    MultiChannelBuffer const & parent() const { return parent_; }

private:
    void operator=( Reader const & );

private: // MultiChannelBuffer private interface.
    friend class MultiChannelBuffer;
    Reader( Buffer::const_iterator const & firstChannelBeginning, MultiChannelBuffer const & parent )
        :
        firstChannelBeginning_  ( firstChannelBeginning ),
        availableDataPerChannel_( 0                     ),
        parent_                 ( parent                )
        { parent.registerReader( *this ); }

private:
    Buffer::const_iterator firstChannelBeginning_  ;
    size_t                 availableDataPerChannel_;

    MultiChannelBuffer const & parent_;
};


////////////////////////////////////////////////////////////////////////////////
//
//  MultiChannelBuffer::Reader::ChannelIterator
//  -------------------------------------------
//
//  Models an iterator over channels (i.e. over, to the corresponding reader,
// available/readable chunks of those channels) of a Reader's
// MultiChannelBuffer.
//
// (todo) make it atleast resemble an STL-like iterator.
//
////////////////////////////////////////////////////////////////////////////////

class MultiChannelBuffer::Reader::ChannelIterator
{
public:
    typedef Buffer::const_iterator const_iterator;
    const_iterator const & begin() const { return currentBegin_; }
    const_iterator         end  () const { return begin() + availableDataPerChannel_; }

    size_t size() const { return availableDataPerChannel_; }

    BYTE const * operator*() const { return &*begin(); }

    ChannelIterator & operator++();
    bool operator<( ChannelIterator const & other ) const { return currentBegin_ < other.currentBegin_; }

private:
    void operator=( ChannelIterator const & );

private: // Reader private interface.
    friend class Reader;
    ChannelIterator( const_iterator const & firstChannel, size_t const availableDataPerChannel, size_t const channelSize )
        :
        currentBegin_           ( firstChannel            ),
        availableDataPerChannel_( availableDataPerChannel ),
        channelSize_            ( channelSize             )
    {}

private:
    const_iterator       currentBegin_           ;
    size_t               availableDataPerChannel_;
    size_t         const channelSize_            ;
};


////////////////////////////////////////////////////////////////////////////////
//
//  MultiChannelBuffer::WritePointers
//  ---------------------------------
//
//  Used by a MultiChannelBuffer to provide a writer with pointers/locations
// where to write the data and to report back to the MultiChannelBuffer when the
// write has finished (and thus new data is available).
//
////////////////////////////////////////////////////////////////////////////////

class MultiChannelBuffer::WritePointers
{
public:
    BYTE * operator[]( size_t const index ) const { return &firstChannelWritePosition_[ index * channelSize_ ]; }

    ~WritePointers();

private: // MultiChannelBuffer private interface.
    friend MultiChannelBuffer;
    WritePointers( Buffer::iterator firstChannelWritePosition, size_t channelSize, size_t writeChunkSize, MultiChannelBuffer & parent );

private: // Non copyable.
    void operator=( WritePointers const & );
public: // RVO does not seem to work for this class (only NVRO in release).
    WritePointers ( WritePointers const & );

private:
    Buffer::iterator  const   firstChannelWritePosition_;
    size_t            const   channelSize_              ;
    size_t            const   writeChunkSize_           ;
    MultiChannelBuffer      & parent_                   ;
};

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer Little Endian Ltd.
Croatia Croatia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions