Click here to Skip to main content
15,893,487 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.
#include "WAOutModule.hpp"

#include "debugMacros.hpp"

namespace WinAMP
{

namespace OutModule
{

//  WinAMPOutModuleSingleton.
//************************************
// Method:    singleton
// FullName:  WinAMP::OutModule::WAOutModule::singleton
// Access:    public static 
//************************************

WAOutModule WAOutModule::WinAMPOutModule;
WAOutModule & WAOutModule::singleton() { return WinAMPOutModule; }


//************************************
// Method:    WAOutModule
// FullName:  WinAMP::OutModule::WAOutModule::WAOutModule
// Access:    private 
//************************************

WAOutModule::WAOutModule()
{
    version        = OUT_VER;

    description    = const_cast<char *>( WAOutModuleDescription() );
    id             = WAOutModuleID();

    hMainWindow    = NULL;
    hDllInstance   = NULL;

    Config         = &OutModule::ConfigDialog;
    About          = &OutModule::About;
    Init           = &OutModule::Init;
    Quit           = &OutModule::Quit;
    Open           = &OutModule::Open;
    Close          = &OutModule::Close;
    Write          = NULL; // &OutModule::Write;
    CanWrite       = &OutModule::CanWrite;
    IsPlaying      = &OutModule::IsPlaying;
    Pause          = &OutModule::Pause;
    SetVolume      = NULL; // &WAOutModule::SetVolumeWaveOut;
    SetPan         = NULL; // &WAOutModule::SetPanWaveOut;
    Flush          = &OutModule::Flush;
    GetOutputTime  = &OutModule::GetOutputTime;
    GetWrittenTime = &OutModule::GetWrittenTime;
}


//************************************
// Method:    SetVolumeMixer
// FullName:  WinAMP::OutModule::WAOutModule::SetVolumeMixer
// Access:    public 
//************************************
//  SetVolume mixerAPI implementation.
//************************************

void __cdecl WAOutModule::SetVolumeMixer( int const volume )
{
    assert( (volume >= 0 && volume <= 255) || volume == -666 );
    if ( volume == -666 )
        return;

    // (todo) This is a quick implementation that makes redundant calls every
    //        time (only the final mixerSetControlDetails() should be required)
    //        only to avoid having state/becoming an object. It should be
    //        refactored to get the necessary information only once and use it
    //        afterwards.

    #pragma comment( lib, "Winmm.lib" )
    MIXERLINE xline; xline.cbStruct = sizeof( MIXERLINE ); xline.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
    VERIFY( ::mixerGetLineInfo( HMIXEROBJ(), &xline, MIXER_GETLINEINFOF_COMPONENTTYPE | MIXER_OBJECTF_MIXER ) == MMSYSERR_NOERROR );

    MIXERCONTROL xcontrol;
    MIXERLINECONTROLS xcontrols =
    {
        sizeof( MIXERLINECONTROLS ),        // xcontrols.cbStruct
        xline.dwLineID,                     // xcontrols.dwLineID
        MIXERCONTROL_CONTROLTYPE_VOLUME,    // xcontrols.dwControlType
        1,                                  // xcontrols.cControls = xline.cControls;
        sizeof( MIXERCONTROL ),             // xcontrols.cbmxctrl
        &xcontrol                           // xcontrols.pamxctrl
    };
    VERIFY( ::mixerGetLineControls( HMIXEROBJ(), &xcontrols, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_MIXER ) == MMSYSERR_NOERROR );


    static MIXERCONTROLDETAILS_UNSIGNED detail;
    static MIXERCONTROLDETAILS detalis =
    {
        sizeof( MIXERCONTROLDETAILS ),              // detalis.cbStruct
        1,                                          // detalis.dwControlID
        1,                                          // detalis.cChannels
        NULL,                                       // detalis.hwndOwner
        sizeof( MIXERCONTROLDETAILS_UNSIGNED ),     // detalis.cbDetails
        &detail                                     // detalis.paDetails
    };
    detalis.dwControlID = xcontrol.dwControlID;

    detail.dwValue = volume << 8 | 0xFF;

    VERIFY( ::mixerSetControlDetails( HMIXEROBJ(), &detalis, MIXER_OBJECTF_WAVEOUT | MIXER_SETCONTROLDETAILSF_VALUE ) == MMSYSERR_NOERROR );
}


//************************************
// Method:    SetPanMixer
// FullName:  WinAMP::OutModule::WAOutModule::SetPanMixer
// Access:    public 
//************************************
//  SetPan mixerAPI implementation.
//************************************

void WAOutModule::SetPanMixer( int const pan )
{
    //  I could not get the panning through the MixerAPI to work no matter what.
    // (todo) try to fix it.
    SetPanVoid( pan );
}


//************************************
// Method:    SetPanWaveOut
// FullName:  WinAMP::OutModule::WAOutModule::SetPanWaveOut
// Access:    public 
//************************************
//  SetPan waveOut/MME implementation.
//************************************

void WAOutModule::SetPanWaveOut( int const pan )
{
    assert( pan >= -128 && pan < 128 );

    DWORD currentGain;
    VERIFY( ::waveOutGetVolume( HWAVEOUT(), &currentGain ) == MMSYSERR_NOERROR );
    assert( currentGain < 65536 );

    DWORD leftGain, rightGain;
    if ( pan == 0 )
        leftGain = rightGain = currentGain;
    else if ( pan < 0 )
    {
        leftGain  = currentGain * pan / (-128);
        rightGain = currentGain;
    }
    else
    {
        leftGain  = currentGain;
        rightGain = currentGain * pan / 128;
    }

    // (todo) Investigate why a direct VERIFY does not work here (!?).
    MMRESULT const setResult( ::waveOutSetVolume( HWAVEOUT(), leftGain | rightGain << 16 ) );
    assert( setResult == MMSYSERR_NOERROR ); setResult;
}


//************************************
// Method:    SetVolumeWaveOut
// FullName:  WinAMP::OutModule::WAOutModule::SetVolumeWaveOut
// Access:    public 
//************************************
//  SetVolume waveOut/MME implementation.
//************************************

void __cdecl WAOutModule::SetVolumeWaveOut( int const volume )
{
    assert( (volume >= 0 && volume <= 255) || volume == -666 );
    if ( volume == -666 )
        return;

    DWORD const volume16bit( volume << 8 );
    // (todo) Investigate why a direct VERIFY does not work here (!?).
    MMRESULT const setResult( ::waveOutSetVolume( 0, volume16bit | volume16bit << 16 ) );
    assert( setResult == MMSYSERR_NOERROR ); setResult;
}

}   // namespace OutModule


HWND      WinAMPWindowHandle() { return OutModule::WAOutModule::singleton().WinAMPWindow(); }
HINSTANCE DLLInstanceHandle () { return OutModule::WAOutModule::singleton().DLLInstance (); }

}   // namespace WinAMP


//  The one exported function required by WinAMP.
extern "C"
{
    __declspec(dllexport) WinAMP::OutModule::Out_Module * __cdecl winampGetOutModule()
    {
        return &WinAMP::OutModule::WAOutModule::singleton().getWAOutModule();
    }
}

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