Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version

Multidevice ASIO output plugin for WinAMP

, 13 Feb 2009 CDDL
A tiny WinAMP output DLL that uses a C++ replacement of the official ASIO SDK that supports multiple ASIO devices.
#include "Writer.hpp"

#include "deinterleaver.h"

#include "tchar.h"


namespace WinAMP
{

namespace OutModule
{

////////////////////////////////////////////////////////////////////////////////
//
//  WriterForASIOSampleType
//  -----------------------
//
//  A helper wrapper for automatic definition/'creation' of a static/singleton
// Writer for the specified ASIO sample type through simple referencing of this
// class. In other words it sort of implements, or helps to implement (through
// the getOutputSampleTypeWriter() function), a kind of a 'static map' that maps
// ASIO output sample types to corresponding Writers. As all ASIO sample types
// are known at compile time and all should, ideally, be supported a static map
// (a switch statement) was a more efficient yet functionally satisfactory
// solution.
//
////////////////////////////////////////////////////////////////////////////////

template <ASIOSampleType outputSampleType>
class WriterForASIOSampleType
{
public:
    static Writer & writer              () { return writer_; }
    static BYTE     outputBytesPerSample();
    
private:
    static Writer writer_;
};


//************************************
// Method:    outputBytesPerSample
// FullName:  WinAMP::OutModule::WriterForASIOSampleType<outputSampleType>::outputBytesPerSample
// Access:    public static
//************************************
//  Yes, this gets "compile-timed" by the optimizer.
//************************************

template<ASIOSampleType outputSampleType>
BYTE WriterForASIOSampleType<outputSampleType>::outputBytesPerSample()
{
    switch( outputSampleType )
    {
        // BPS = 2
    case ASIOSTInt16LSB:
    case ASIOSTInt16MSB:
        return 2;

        // BPS = 3
    case ASIOSTInt24LSB:
    case ASIOSTInt24MSB:
        return 3;

        // BPS = 4
    case ASIOSTInt32LSB:
    case ASIOSTInt32LSB16:
    case ASIOSTInt32LSB24:
    case ASIOSTFloat32LSB:
    case ASIOSTInt32MSB:
    case ASIOSTFloat32MSB:
    case ASIOSTInt32MSB16:
    case ASIOSTInt32MSB24:
        return 4;

        // BPS = 8
    case ASIOSTFloat64LSB:
    case ASIOSTFloat64MSB:
        return 8;

    default:
        assert( !"Unsupported ASIO sample type" );
        __assume( false );
    }
}

template<ASIOSampleType outputSampleType>
__declspec( selectany ) Writer WriterForASIOSampleType<outputSampleType>::writer_( outputSampleType, WriterForASIOSampleType<outputSampleType>::outputBytesPerSample() );


////////////////////////////////////////////////////////////////////////////////
//
//  WriterImpl
//  ----------
//
//  Extends/specializes WriterForASIOSampleType<> for a particular input sample
// type, i.e. it provides the basic template infrastructure for easier writing
// /implementing new Write() callbacks (that take 'inputBitsPerSample'
// interleaved data and convert it to 'outputSampleType' data and store to their
// Writer's MultiChannelBuffer (writer().buffer()).
//
//  (todo) Switch/reverse the outputSampleType and inputBitsPerSample template
//         parameters as that will perhaps be a little less confusing.
//
////////////////////////////////////////////////////////////////////////////////

template <ASIOSampleType outputSampleType, size_t inputBitsPerSample>
class WriterImpl : public WriterForASIOSampleType<outputSampleType>
{
public:
    static int __cdecl Write( char * buf, int len );

private:
    static void                              checkInputParameters     ( char const * pBuffer, int bufferLength );
    static MultiChannelBuffer::WritePointers getWritePointers         ( int inputBufferLength                  );
    static size_t                            writeSizePerOutputChannel( int inputNumberOfBytes                 );
};


//************************************
// Method:    getWritePointers
// FullName:  WinAMP::OutModule::WriterImpl<outputSampleType, inputBitsPerSample>::getWritePointers
// Access:    private static
//************************************

template <ASIOSampleType outputSampleType, size_t inputBitsPerSample>
MultiChannelBuffer::WritePointers
WriterImpl<outputSampleType, inputBitsPerSample>::getWritePointers( int const inputBufferLength )
{
    size_t const writeSZ( writeSizePerOutputChannel( inputBufferLength ) );
    return writer().buffer().getWritePointers( writeSZ );
}


//************************************
// Method:    writeSizePerOutputChannel
// FullName:  WinAMP::OutModule::WriterImpl<outputSampleType, inputBitsPerSample>::writeSizePerOutputChannel
// Access:    private static
//************************************

template <ASIOSampleType outputSampleType, size_t inputBitsPerSample>
size_t
WriterImpl<outputSampleType, inputBitsPerSample>::writeSizePerOutputChannel( int const inputNumberOfBytes )
{
    return inputNumberOfBytes * outputBytesPerSample() / ( inputBitsPerSample / 8 ) / writer().buffer().numberOfChannels();
}


//************************************
// Method:    checkInputParameters
// FullName:  WinAMP::OutModule::WriterImpl<outputSampleType, inputBitsPerSample>::checkInputParameters
// Access:    private static
//************************************

template <ASIOSampleType outputSampleType, size_t inputBitsPerSample>
void
WriterImpl<outputSampleType, inputBitsPerSample>::checkInputParameters( char const * const pBuffer, int const bufferLength )
{
    assert( writer().buffer().numberOfChannels() == 2 );

    assert( ( pBuffer || !bufferLength ) && "Null input buffer pointer with non-zero size." ); pBuffer;
    assert( ( reinterpret_cast<unsigned int>( pBuffer ) % ( inputBitsPerSample / 8 ) == 0 ) && "Input buffer unaligned." );
    assert( bufferLength <= maxWAInputChunk && "A WinAMP in-module sent more than maxWAInputChunk bytes." ); bufferLength;
    assert( ( ( bufferLength % ( writer().buffer().numberOfChannels() * inputBitsPerSample / 8 ) ) == 0 ) && "Input buffer length not a multiple numberOfInputChannels." );

    assert( writer().buffer().canWrite() >= writeSizePerOutputChannel( bufferLength ) );
}


////////////////////////////////////////////////////////////////////////////////
//
//  Write() WA output module callbacks
//  ----------------------------------
//
//  WriterImpl<>::Write() specializations for ('someday') all input-sample type
// combinations.
//
//  (todo) Write the remaining required specializations.
//
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//  The default implementation that gets called in case a specialization for the
// particular IO sample type combination has not yet been defined.
////////////////////////////////////////////////////////////////////////////////

template<ASIOSampleType outputSampleType, size_t inputBitsPerSample>
int WriterImpl<outputSampleType, inputBitsPerSample>::Write( char *, int )
{
    Close();
    ::MessageBox( WAOutModule::singleton().WinAMPWindow(), _T("Unsupported IO sample type combination..."), _T("wasiona"), MB_OK | MB_ICONSTOP );
    return 1;
}

#pragma optimize ( "t", on ) // optimize the write callbacks for speed.
template<>
int WriterImpl<ASIOSTInt32LSB, 16>::Write( char * const buf, int const len )
{
    checkInputParameters( buf, len );

    MultiChannelBuffer::WritePointers const inBuffers( getWritePointers( len ) );
    Deinterleaver::Deinterleave_16_32( (short const *)buf, (int*)(inBuffers[ 0 ]), (int*)(inBuffers[ 1 ]), len );
    return 0;
}

template<>
int WriterImpl<ASIOSTInt32LSB, 24>::Write( char * const buf, int const len )
{
    checkInputParameters( buf, len );

    MultiChannelBuffer::WritePointers const inBuffers( getWritePointers( len ) );
    Deinterleaver::Deinterleave_24_32( buf, (int *)(inBuffers[ 0 ]), (int *)(inBuffers[ 1 ]), len );
    return 0;
}
#pragma optimize( "", on ) // end optimize for speed.


//************************************
// Method:    getReadersWriter
// FullName:  WinAMP::OutModule::getReadersWriter
// Access:    global
//************************************

Writer const & getReadersWriter( MultiChannelBuffer::Reader const & reader )
{
    // "Ughh-but-it-works"...
    Writer const & writer( reinterpret_cast<Writer const &>( reader.parent() ) );
    // Check that the above conversion is valid:
    //  - the MultiChannelBuffer must be the first variable (in the Writer class)
    assert( reinterpret_cast<void const *>( &writer ) == reinterpret_cast<void const *>( &writer.buffer() ) );
    //  - the reader's MultiChannelBuffer parent must be from a Writer (from
    //    this module), not a custom/'hand made' MultiChannelBuffer object.
    assert( &writer == &getOutputSampleTypeWriter( writer.outputSampleType() ) );
    return writer;
}


//************************************
// Method:    getOutputSampleTypeWriter
// FullName:  WinAMP::OutModule::getOutputSampleTypeWriter
// Access:    global
//************************************

Writer & getOutputSampleTypeWriter( ASIOSampleType const outputSampleType )
{
    assert( ASIOSTLastEntry < 65536 );
    #define returnFor( ASIOOutputSampleType ) case ASIOOutputSampleType: return WriterForASIOSampleType<ASIOOutputSampleType>::writer()

    switch( outputSampleType )
    {
        // BPS = 2
        returnFor( ASIOSTInt16LSB );
        returnFor( ASIOSTInt16MSB );

        // BPS = 3
        returnFor( ASIOSTInt24LSB );
        returnFor( ASIOSTInt24MSB );

        // BPS = 4
        returnFor( ASIOSTInt32LSB   );
        returnFor( ASIOSTInt32LSB16 );
        returnFor( ASIOSTInt32LSB24 );
        returnFor( ASIOSTFloat32LSB );
        returnFor( ASIOSTInt32MSB   );
        returnFor( ASIOSTFloat32MSB );
        returnFor( ASIOSTInt32MSB16 );
        returnFor( ASIOSTInt32MSB24 );

        // BPS = 8
        returnFor( ASIOSTFloat64LSB );
        returnFor( ASIOSTFloat64MSB );

        default:
            assert( !"Unsupported ASIO sample type" );
            __assume( false );
    }

    #undef returnFor
}


//************************************
// Method:    getWriteCallback
// FullName:  WinAMP::OutModule::getWriteCallback
// Access:    global
//************************************

WAOutModule::WriteCallback getWriteCallback( int const inputBitsPerSecond, ASIOSampleType const outputSampleType )
{
    assert( ASIOSTLastEntry < 65536 );
    assert( outputSampleType < ASIOSTLastEntry );
    assert( inputBitsPerSecond <= 32 );
    __assume( outputSampleType < ASIOSTLastEntry );
    __assume( inputBitsPerSecond <= 32 );

    #define IOSampleType( ASIOOutputType, WinAMPInputBitsPerSecond ) ASIOOutputType | ( WinAMPInputBitsPerSecond << 16 )
    #define returnFor( sampleType, inputBitsPerSecond ) case IOSampleType( sampleType, inputBitsPerSecond ): return &WriterImpl<sampleType, inputBitsPerSecond>::Write
    
    switch( IOSampleType( outputSampleType, inputBitsPerSecond ) )
    {
        // BPS = 2
        returnFor( ASIOSTInt16LSB, 16 );
        returnFor( ASIOSTInt16LSB, 24 );
        returnFor( ASIOSTInt16LSB, 32 );

        // BPS = 3
        returnFor( ASIOSTInt24LSB, 16 );
        returnFor( ASIOSTInt24LSB, 24 );
        returnFor( ASIOSTInt24LSB, 32 );

        // BPS = 4
        returnFor( ASIOSTInt32LSB, 16 );
        returnFor( ASIOSTInt32LSB, 24 );
        returnFor( ASIOSTInt32LSB, 32 );

        returnFor( ASIOSTInt32LSB16, 16 );
        returnFor( ASIOSTInt32LSB16, 32 );

        returnFor( ASIOSTInt32LSB24, 16 );
        returnFor( ASIOSTInt32LSB24, 32 );

        returnFor( ASIOSTFloat32LSB, 16 );
        returnFor( ASIOSTFloat32LSB, 32 );

        // BPS = 8
        returnFor( ASIOSTFloat64LSB, 16 );
        returnFor( ASIOSTFloat64LSB, 32 );

        default:
            assert( !"Unsupported IO sample type combination" );
            return NULL;
    }
}


//************************************
// Method:    Writer
// FullName:  WinAMP::OutModule::Writer::Writer
// Access:    public 
//************************************

Writer::Writer( ASIOSampleType const outputSampleType, size_t const outputBytesPerSample )
    :
    writeCallback_        ( NULL                 ),
    outputSampleType_     ( outputSampleType     ),
    outputBytesPerSample_ ( outputBytesPerSample )
{
}


//************************************
// Method:    setUpForIOParameters
// FullName:  WinAMP::OutModule::Writer::setUpForIOParameters
// Access:    public
//************************************

bool Writer::setUpForIOParameters( int const inputBitsPerSecond, size_t const outputSampleRate )
{
    outputSampleRate_ = outputSampleRate;
    writeCallback_    = getWriteCallback( inputBitsPerSecond, outputSampleType_ );
    return writeCallback_ != NULL;
}


//************************************
// Method:    writtenTimeInMilliseconds
// FullName:  WinAMP::OutModule::Writer::writtenTimeInMilliseconds
// Access:    public
//************************************

int Writer::writtenTimeInMilliseconds() const
{
    register size_t const bytesWritten             ( buffer().bytesWritten()                    );
    register size_t const outputBytesPerChPerSecond( outputBytesPerSample() * outputSampleRate_ );
    return static_cast<int>
    (
          ( ( bytesWritten / outputBytesPerChPerSecond ) * 1000 ) +
        ( ( ( bytesWritten % outputBytesPerChPerSecond ) * 1000 ) / outputBytesPerChPerSecond )
    );
}


}   // namespace OutModule

}   // namespace WinAMP

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)

Share

About the Author

Domagoj Šarić
Software Developer Little Endian Ltd.
Croatia Croatia
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 13 Feb 2009
Article Copyright 2009 by Domagoj Šarić
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid