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
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)

About the Author

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

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