diff --git a/include/vlc_aout.h b/include/vlc_aout.h index 6d4508d08ff75f257b79434a38be96c4d502770d..dcf236007ccec623013cbc70cb3ac3652531a046 100644 --- a/include/vlc_aout.h +++ b/include/vlc_aout.h @@ -134,8 +134,6 @@ typedef int32_t vlc_fixed_t; /* FIXME to remove once aout.h is cleaned a bit more */ #include <vlc_block.h> -typedef int (*aout_volume_cb) (audio_output_t *, float, bool); - /** Audio output object */ struct audio_output { @@ -150,7 +148,8 @@ struct audio_output callback (optional, may be NULL) */ void (* pf_flush)( audio_output_t *, bool ); /**< Flush/drain callback (optional, may be NULL) */ - aout_volume_cb pf_volume_set; /**< Volume setter (or NULL) */ + int (*volume_set)(audio_output_t *, float); /**< Volume setter (or NULL) */ + int (*mute_set)(audio_output_t *, bool); /**< Mute setter (or NULL) */ struct { void (*time_report)(audio_output_t *, mtime_t); @@ -224,12 +223,15 @@ VLC_API void aout_FormatPrint(vlc_object_t *, const char *, VLC_API const char * aout_FormatPrintChannels( const audio_sample_format_t * ) VLC_USED; VLC_API void aout_VolumeSoftInit( audio_output_t * ); -VLC_API void aout_VolumeHardInit( audio_output_t *, aout_volume_cb, bool ); -static inline void aout_VolumeHardSet(audio_output_t *aout, float v, bool m) +static inline void aout_VolumeReport(audio_output_t *aout, float volume) +{ + aout->event.volume_report(aout, volume); +} + +static inline void aout_MuteReport(audio_output_t *aout, bool mute) { - aout->event.volume_report(aout, v); - aout->event.mute_report(aout, m); + aout->event.mute_report(aout, mute); } static inline void aout_TimeReport(audio_output_t *aout, mtime_t date) diff --git a/modules/audio_output/alsa.c b/modules/audio_output/alsa.c index 266dbe9b55a68a3a96e8f2d1906452aaa2c7a12c..df1ce604b3f2f6b59830b8f9d8a2e0f07ffb262b 100644 --- a/modules/audio_output/alsa.c +++ b/modules/audio_output/alsa.c @@ -516,7 +516,8 @@ static int Open (vlc_object_t *obj) { aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE; aout->format.i_frame_length = A52_FRAME_NB; - aout->pf_volume_set = NULL; + aout->volume_set = NULL; + aout->mute_set = NULL; } else { diff --git a/modules/audio_output/amem.c b/modules/audio_output/amem.c index 15996e3e682ca22f164a80ca01a7291c2d6bffb4..fbd0af869f77258429adaab4e8f317a53b0075bd 100644 --- a/modules/audio_output/amem.c +++ b/modules/audio_output/amem.c @@ -62,6 +62,8 @@ struct aout_sys_t void (*drain) (void *opaque); int (*set_volume) (void *opaque, float vol, bool mute); void (*cleanup) (void *opaque); + float volume; + bool mute; }; static void Play (audio_output_t *aout, block_t *block) @@ -91,11 +93,20 @@ static void Flush (audio_output_t *aout, bool wait) cb (sys->opaque); } -static int VolumeSet (audio_output_t *aout, float vol, bool mute) +static int VolumeSet (audio_output_t *aout, float vol) { aout_sys_t *sys = aout->sys; - return sys->set_volume (sys->opaque, vol, mute) ? -1 : 0; + sys->volume = vol; + return sys->set_volume (sys->opaque, vol, sys->mute) ? -1 : 0; +} + +static int MuteSet (audio_output_t *aout, bool mute) +{ + aout_sys_t *sys = aout->sys; + + sys->mute = mute; + return sys->set_volume (sys->opaque, sys->volume, mute) ? -1 : 0; } typedef int (*vlc_audio_format_cb) (void **, char *, unsigned *, unsigned *); @@ -116,6 +127,8 @@ static int Open (vlc_object_t *obj) sys->drain = var_InheritAddress (obj, "amem-drain"); sys->set_volume = var_InheritAddress (obj, "amem-set-volume"); sys->cleanup = NULL; /* defer */ + sys->volume = 1.; + sys->mute = false; if (sys->play == NULL) goto error; @@ -203,7 +216,10 @@ static int Open (vlc_object_t *obj) aout->pf_pause = Pause; aout->pf_flush = Flush; if (sys->set_volume != NULL) - aout->pf_volume_set = VolumeSet; + { + aout->volume_set = VolumeSet; + aout->mute_set = MuteSet; + } else aout_VolumeSoftInit (aout); return VLC_SUCCESS; diff --git a/modules/audio_output/auhal.c b/modules/audio_output/auhal.c index d67fc1d591f97d33d54d1146c31f156125e9115f..2928fed2f21806484a263f2bb2642c02c931946b 100644 --- a/modules/audio_output/auhal.c +++ b/modules/audio_output/auhal.c @@ -787,7 +787,8 @@ static int OpenSPDIF( audio_output_t * p_aout ) p_aout->format.i_rate = (unsigned int)p_sys->stream_format.mSampleRate; aout_FormatPrepare( &p_aout->format ); aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB ); - p_aout->pf_volume_set = NULL; + p_aout->volume_set = NULL; + p_aout->mute_set = NULL; /* Add IOProc callback */ err = AudioDeviceCreateIOProcID( p_sys->i_selected_dev, diff --git a/modules/audio_output/directx.c b/modules/audio_output/directx.c index 017a0748953c9754b22de3eabc4fc52120d2e8d2..71f41d5c606127d228f73b65719e75151d58a902 100644 --- a/modules/audio_output/directx.c +++ b/modules/audio_output/directx.c @@ -75,7 +75,12 @@ struct aout_sys_t LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound * takes care of mixing all the * secondary buffers into the primary) */ - LONG volume; + struct + { + LONG volume; + LONG mb; + bool mute; + } volume; notification_thread_t notif; /* DirectSoundThread id */ @@ -96,7 +101,8 @@ struct aout_sys_t static int OpenAudio ( vlc_object_t * ); static void CloseAudio ( vlc_object_t * ); static void Play ( audio_output_t *, block_t * ); -static int VolumeSet ( audio_output_t *, float, bool ); +static int VolumeSet ( audio_output_t *, float ); +static int MuteSet ( audio_output_t *, bool ); /* local functions */ static void Probe ( audio_output_t * ); @@ -232,8 +238,6 @@ static int OpenAudio( vlc_object_t *p_this ) } aout_PacketInit( p_aout, &p_aout->sys->packet, A52_FRAME_NB ); - p_aout->sys->volume = -1; - p_aout->pf_volume_set = NULL; } else { @@ -287,9 +291,10 @@ static int OpenAudio( vlc_object_t *p_this ) /* Calculate the frame size in bytes */ aout_FormatPrepare( &p_aout->format ); aout_PacketInit( p_aout, &p_aout->sys->packet, FRAME_SIZE ); - aout_VolumeHardInit( p_aout, VolumeSet, true ); } + p_aout->sys->volume.volume = -1; + /* Now we need to setup our DirectSound play notification structure */ vlc_atomic_set(&p_aout->sys->notif.abort, 0); p_aout->sys->notif.event = CreateEvent( 0, FALSE, FALSE, 0 ); @@ -313,6 +318,19 @@ static int OpenAudio( vlc_object_t *p_this ) p_aout->pf_pause = aout_PacketPause; p_aout->pf_flush = aout_PacketFlush; + /* Volume */ + if( val.i_int == AOUT_VAR_SPDIF ) + { + p_aout->volume_set = NULL; + p_aout->mute_set = NULL; + } + else + { + p_aout->sys->volume.mb = 0; + p_aout->sys->volume.mute = false; + p_aout->volume_set = VolumeSet; + p_aout->mute_set = MuteSet; + } return VLC_SUCCESS; error: @@ -573,12 +591,10 @@ static void Play( audio_output_t *p_aout, block_t *p_buffer ) p_aout->pf_play = aout_PacketPlay; } -/***************************************************************************** - * VolumeSet: change audio device volume - *****************************************************************************/ -static int VolumeSet( audio_output_t *p_aout, float vol, bool mute ) +static int VolumeSet( audio_output_t *p_aout, float vol ) { aout_sys_t *sys = p_aout->sys; + int ret = 0; /* Convert UI volume to linear factor (cube) */ vol = vol * vol * vol; @@ -589,15 +605,32 @@ static int VolumeSet( audio_output_t *p_aout, float vol, bool mute ) /* Clamp to allowed DirectSound range */ static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" ); if( mb >= DSBVOLUME_MAX ) + { mb = DSBVOLUME_MAX; + ret = -1; + } if( mb <= DSBVOLUME_MIN ) mb = DSBVOLUME_MIN; - InterlockedExchange(&sys->volume, mute ? DSBVOLUME_MIN : mb); + sys->volume.mb = mb; + if (!sys->volume.mute) + InterlockedExchange(&sys->volume.volume, mb); /* Convert back to UI volume */ vol = cbrtf(powf(10.f, ((float)mb) / -2000.f)); - aout_VolumeHardSet( p_aout, vol, mute ); + aout_VolumeReport( p_aout, vol ); + return ret; +} + +static int MuteSet( audio_output_t *p_aout, bool mute ) +{ + aout_sys_t *sys = p_aout->sys; + + sys->volume.mute = mute; + InterlockedExchange(&sys->volume.volume, + mute ? DSBVOLUME_MIN : sys->volume.mb); + + aout_MuteReport( p_aout, mute ); return 0; } @@ -1064,7 +1097,7 @@ static void* DirectSoundThread( void *data ) int i; /* Update volume if required */ - LONG volume = InterlockedExchange( &p_aout->sys->volume, -1 ); + LONG volume = InterlockedExchange( &p_aout->sys->volume.volume, -1 ); if( unlikely(volume != -1) ) IDirectSoundBuffer_SetVolume( p_aout->sys->p_dsbuffer, volume ); diff --git a/modules/audio_output/file.c b/modules/audio_output/file.c index d36870166177ff682757372b8c2fadf3797384b8..b5ac856c10c070ed0b1dedbfea97a410ccf4b9ed 100644 --- a/modules/audio_output/file.c +++ b/modules/audio_output/file.c @@ -194,7 +194,8 @@ static int Open( vlc_object_t * p_this ) p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE; p_aout->format.i_frame_length = A52_FRAME_NB; } - p_aout->pf_volume_set = NULL; + p_aout->volume_set = NULL; + p_aout->mute_set = NULL; /* Channels number */ i_channels = var_CreateGetInteger( p_this, "audiofile-channels" ); diff --git a/modules/audio_output/oss.c b/modules/audio_output/oss.c index c2bcce269722121e0ab0e9aafd04c9a2e1bd566f..8222893b8c91927b6930b01ffd1fe7f2c3677e38 100644 --- a/modules/audio_output/oss.c +++ b/modules/audio_output/oss.c @@ -368,7 +368,8 @@ static int Open( vlc_object_t *p_this ) p_aout->format.i_frame_length = A52_FRAME_NB; aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB ); - p_aout->pf_volume_set = NULL; + p_aout->volume_set = NULL; + p_aout->mute_set = NULL; } else { diff --git a/modules/audio_output/pulse.c b/modules/audio_output/pulse.c index bd296eeee33c47784186dc1a556f382d619aa495..c8e7c0878aceaff1fa9877970df2247902bb998a 100644 --- a/modules/audio_output/pulse.c +++ b/modules/audio_output/pulse.c @@ -481,7 +481,8 @@ static void sink_input_info_cb(pa_context *ctx, const pa_sink_input_info *i, pa_volume_t volume = pa_cvolume_max(&i->volume); volume = pa_sw_volume_divide(volume, sys->base_volume); - aout_VolumeHardSet(aout, (float)volume / PA_VOLUME_NORM, i->mute); + aout_VolumeReport(aout, (float)volume / PA_VOLUME_NORM); + aout_MuteReport(aout, i->mute); } @@ -601,7 +602,7 @@ static void Flush(audio_output_t *aout, bool wait) pa_threaded_mainloop_unlock(sys->mainloop); } -static int VolumeSet(audio_output_t *aout, float vol, bool mute) +static int VolumeSet(audio_output_t *aout, float vol) { aout_sys_t *sys = aout->sys; pa_operation *op; @@ -626,6 +627,18 @@ static int VolumeSet(audio_output_t *aout, float vol, bool mute) op = pa_context_set_sink_input_volume(sys->context, idx, &cvolume, NULL, NULL); if (likely(op != NULL)) pa_operation_unref(op); + pa_threaded_mainloop_unlock(sys->mainloop); + + return 0; +} + +static int MuteSet(audio_output_t *aout, bool mute) +{ + aout_sys_t *sys = aout->sys; + pa_operation *op; + uint32_t idx = pa_stream_get_index(sys->stream); + + pa_threaded_mainloop_lock(sys->mainloop); op = pa_context_set_sink_input_mute(sys->context, idx, mute, NULL, NULL); if (likely(op != NULL)) pa_operation_unref(op); @@ -946,7 +959,8 @@ static int Open(vlc_object_t *obj) aout->pf_play = Play; aout->pf_pause = Pause; aout->pf_flush = Flush; - aout_VolumeHardInit (aout, VolumeSet, false); + aout->volume_set = VolumeSet; + aout->mute_set = MuteSet; return VLC_SUCCESS; fail: diff --git a/modules/audio_output/wasapi.c b/modules/audio_output/wasapi.c index fb8217268fb1446e1ed829a2d7630438e738ce1f..00d755b16217a8d364a15496902eca40f0965982 100644 --- a/modules/audio_output/wasapi.c +++ b/modules/audio_output/wasapi.c @@ -179,7 +179,7 @@ static void Flush(audio_output_t *aout, bool wait) Leave(); } -static int SimpleVolumeSet(audio_output_t *aout, float vol, bool mute) +static int SimpleVolumeSet(audio_output_t *aout, float vol) { aout_sys_t *sys = aout->sys; HRESULT hr; @@ -188,26 +188,24 @@ static int SimpleVolumeSet(audio_output_t *aout, float vol, bool mute) vol = 1.; Enter(); - /* NOTE: better change volume while muted (if mute is toggled) */ - if (mute) - { - hr = ISimpleAudioVolume_SetMute(sys->volume.simple, true, NULL); - if (FAILED(hr)) - msg_Warn(aout, "cannot mute session (error 0x%lx)", hr); - } - hr = ISimpleAudioVolume_SetMasterVolume(sys->volume.simple, vol, NULL); if (FAILED(hr)) msg_Warn(aout, "cannot set session volume (error 0x%lx)", hr); + Leave(); + return FAILED(hr) ? -1 : 0; +} - if (!mute) - { - hr = ISimpleAudioVolume_SetMute(sys->volume.simple, false, NULL); - if (FAILED(hr)) - msg_Warn(aout, "cannot unmute session (error 0x%lx)", hr); - } +static int SimpleMuteSet(audio_output_t *aout, bool mute) +{ + aout_sys_t *sys = aout->sys; + HRESULT hr; + + Enter(); + hr = ISimpleAudioVolume_SetMute(sys->volume.simple, mute, NULL); + if (FAILED(hr)) + msg_Warn(aout, "cannot mute session (error 0x%lx)", hr); Leave(); - return 0; + return FAILED(hr) ? -1 : 0; } static void vlc_ToWave(WAVEFORMATEXTENSIBLE *restrict wf, @@ -477,7 +475,10 @@ static int Open(vlc_object_t *obj) aout->pf_pause = Pause; aout->pf_flush = Flush; /*if (AOUT_FMT_LINEAR(&format) && !exclusive)*/ - aout_VolumeHardInit(aout, SimpleVolumeSet, false); + { + aout->volume_set = SimpleVolumeSet; + aout->mute_set = SimpleMuteSet; + } Leave(); return VLC_SUCCESS; error: diff --git a/modules/audio_output/waveout.c b/modules/audio_output/waveout.c index cfd17d25eb0917a7173b73033e741d149bdb87a4..67a03423bb19e17ced0671587ae44fbaa886cdff 100644 --- a/modules/audio_output/waveout.c +++ b/modules/audio_output/waveout.c @@ -68,7 +68,8 @@ static int PlayWaveOut ( audio_output_t *, HWAVEOUT, WAVEHDR *, static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR ); static void* WaveOutThread( void * ); -static int VolumeSet( audio_output_t *, float, bool ); +static int VolumeSet( audio_output_t *, float ); +static int MuteSet( audio_output_t *, bool ); static int WaveOutClearDoneBuffers(aout_sys_t *p_sys); @@ -140,6 +141,9 @@ struct aout_sys_t uint8_t *p_silence_buffer; /* buffer we use to play silence */ + float volume; + bool mute; + bool b_chan_reorder; /* do we need channel reordering */ int pi_chan_table[AOUT_CHAN_MAX]; }; @@ -169,7 +173,6 @@ static int Open( vlc_object_t *p_this ) */ ReloadWaveoutDevices( p_this, "waveout-audio-device", val, val, NULL); - /* check for configured audio device! */ @@ -246,7 +249,8 @@ static int Open( vlc_object_t *p_this ) p_aout->format.i_bytes_per_frame; aout_PacketInit( p_aout, &p_aout->sys->packet, A52_FRAME_NB ); - p_aout->pf_volume_set = NULL; + p_aout->volume_set = NULL; + p_aout->mute_set = NULL; } else { @@ -293,16 +297,22 @@ static int Open( vlc_object_t *p_this ) aout_PacketInit( p_aout, &p_aout->sys->packet, FRAME_SIZE ); +#ifndef UNDER_CE /* Check for hardware volume support */ if( waveOutGetDevCaps( (UINT_PTR)p_aout->sys->h_waveout, &wocaps, sizeof(wocaps) ) == MMSYSERR_NOERROR && (wocaps.dwSupport & WAVECAPS_VOLUME) ) - aout_VolumeHardInit( p_aout, VolumeSet, false /* ?? */ ); + { + p_aout->volume_set = VolumeSet; + p_aout->mute_set = MuteSet; + p_aout->sys->volume = 0xffff.fp0; + p_aout->sys->mute = false; + } else +#endif aout_VolumeSoftInit( p_aout ); } - waveOutReset( p_aout->sys->h_waveout ); /* Allocate silence buffer */ @@ -992,26 +1002,37 @@ static void* WaveOutThread( void *data ) return NULL; } -static int VolumeSet( audio_output_t * p_aout, float volume, bool mute ) -{ #ifndef UNDER_CE - const HWAVEOUT hwo = p_aout->sys->h_waveout; -#else - const HWAVEOUT hwo = 0; -#endif +static int VolumeSet( audio_output_t *aout, float volume ) +{ + aout_sys_t *sys = aout->sys; + const HWAVEOUT hwo = sys->h_waveout; const float full = 0xffff.fp0; - if( mute ) - volume = 0.; volume *= full; if( volume >= full ) - volume = full; + return -1; + + sys->volume = volume; + if( sys->mute ) + return 0; uint16_t vol = lroundf(volume); waveOutSetVolume( hwo, vol | (vol << 16) ); return 0; } +static int MuteSet( audio_output_t * p_aout, bool mute ) +{ + aout_sys_t *sys = p_aout->sys; + const HWAVEOUT hwo = sys->h_waveout; + uint16_t vol = mute ? 0 : lroundf(sys->volume); + + sys->mute = mute; + waveOutSetVolume( hwo, vol | (vol << 16) ); + return 0; +} +#endif /* reload the configuration drop down list, of the Audio Devices diff --git a/src/audio_output/common.c b/src/audio_output/common.c index 50c494770d9e2e680511fcb91d06604d3800d8f3..0c0a12d3458eccb75bd71157216ce6b9b666c6fa 100644 --- a/src/audio_output/common.c +++ b/src/audio_output/common.c @@ -67,7 +67,8 @@ audio_output_t *aout_New( vlc_object_t * p_parent ) owner->volume.mixer = NULL; aout->pf_play = aout_DecDeleteBuffer; - aout->pf_volume_set = NULL; + aout->volume_set = NULL; + aout->mute_set = NULL; vlc_object_set_destructor (aout, aout_Destructor); /* diff --git a/src/audio_output/intf.c b/src/audio_output/intf.c index 18c654ebd1b506ac826f70b6c6cfa00b9562e283..494a7fb1aee57cfc5d3c86d5cd1bc205a87954e9 100644 --- a/src/audio_output/intf.c +++ b/src/audio_output/intf.c @@ -90,8 +90,12 @@ static int commitVolume (vlc_object_t *obj, audio_output_t *aout, { /* apply volume to the pipeline */ aout_lock (aout); - if (aout->pf_volume_set != NULL) - ret = aout->pf_volume_set (aout, vol, mute); + if (aout->mute_set != NULL) + ret = aout->mute_set (aout, mute); + else + ret = -1; + if (ret == 0 && aout->volume_set != NULL) + ret = aout->volume_set (aout, vol); aout_unlock (aout); if (ret == 0) diff --git a/src/audio_output/output.c b/src/audio_output/output.c index 5b6b48c3a6afb7a1eab2b12dd098c84deefcf935..e4a74cd7994ee5b37a7bc14012a18021087b97ec 100644 --- a/src/audio_output/output.c +++ b/src/audio_output/output.c @@ -277,7 +277,8 @@ void aout_OutputDelete (audio_output_t *aout) aout->pf_play = aout_DecDeleteBuffer; /* gruik */ aout->pf_pause = NULL; aout->pf_flush = NULL; - aout->pf_volume_set = NULL; + aout->volume_set = NULL; + aout->mute_set = NULL; owner->module = NULL; owner->volume.amp = 1.f; owner->volume.mute = false; @@ -334,15 +335,11 @@ void aout_OutputFlush( audio_output_t *aout, bool wait ) /*** Volume handling ***/ -/** - * Volume setter for software volume. - */ -static int aout_VolumeSoftSet (audio_output_t *aout, float volume, bool mute) +static int aout_SoftVolumeSet (audio_output_t *aout, float volume) { aout_owner_t *owner = aout_owner (aout); aout_assert_locked (aout); - /* Cubic mapping from software volume to amplification factor. * This provides a good tradeoff between low and high volume ranges. * @@ -350,6 +347,14 @@ static int aout_VolumeSoftSet (audio_output_t *aout, float volume, bool mute) * formula, be sure to update the aout_VolumeHardInit()-based plugins also. */ owner->volume.amp = volume * volume * volume; + return 0; +} + +static int aout_SoftMuteSet (audio_output_t *aout, bool mute) +{ + aout_owner_t *owner = aout_owner (aout); + + aout_assert_locked (aout); owner->volume.mute = mute; return 0; } @@ -366,26 +371,8 @@ void aout_VolumeSoftInit (audio_output_t *aout) bool mute = var_GetBool (aout, "mute"); aout_assert_locked (aout); - aout->pf_volume_set = aout_VolumeSoftSet; - aout_VolumeSoftSet (aout, volume / (float)AOUT_VOLUME_DEFAULT, mute); -} - -/** - * Configures a custom volume setter. This is used by audio outputs that can - * control the hardware volume directly and/or emulate it internally. - * @param setter volume setter callback - * @param restore apply volume from VLC configuration immediately - */ -void aout_VolumeHardInit (audio_output_t *aout, aout_volume_cb setter, - bool restore) -{ - aout_assert_locked (aout); - aout->pf_volume_set = setter; - - if (restore) - { - float vol = var_GetInteger (aout, "volume") - / (float)AOUT_VOLUME_DEFAULT; - setter (aout, vol, var_GetBool (aout, "mute")); - } + aout->volume_set = aout_SoftVolumeSet; + aout->mute_set = aout_SoftMuteSet; + aout_SoftVolumeSet (aout, volume / (float)AOUT_VOLUME_DEFAULT); + aout_SoftMuteSet (aout, mute); } diff --git a/src/libvlccore.sym b/src/libvlccore.sym index 60c4ebd7097cd41c367ac9b5a042faa1989f84a6..c95eafce727c7229d37e79872db01fd987466767 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -21,7 +21,6 @@ aout_MuteToggle aout_MuteSet aout_MuteGet aout_VolumeSoftInit -aout_VolumeHardInit block_Alloc block_FifoCount block_FifoEmpty