Bug 1175447 - mono audio support. r=padenot, r=sotaro
This patch is to enable mono audio option for those who has full hearing loss in one ear.
With this feature, they can get the complete audio with using one ear.
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -73,16 +73,22 @@ var SettingsListener = {
}));
this._callbacks[name] = callback;
}
};
SettingsListener.init();
+// =================== Mono Audio ======================
+
+SettingsListener.observe('accessibility.monoaudio.enable', false, function(value) {
+ Services.prefs.setBoolPref('accessibility.monoaudio.enable', value);
+});
+
// =================== Console ======================
SettingsListener.observe('debug.console.enabled', true, function(value) {
Services.prefs.setBoolPref('consoleservice.enabled', value);
Services.prefs.setBoolPref('layout.css.report_errors', value);
});
SettingsListener.observe('homescreen.manifestURL', 'Sentinel Value' , function(value) {
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -12,16 +12,17 @@
#include "VideoUtils.h"
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include "mozilla/Snprintf.h"
#include <algorithm>
#include "mozilla/Telemetry.h"
#include "CubebUtils.h"
#include "nsPrintfCString.h"
+#include "gfxPrefs.h"
namespace mozilla {
#ifdef LOG
#undef LOG
#endif
PRLogModuleInfo* gAudioStreamLog = nullptr;
@@ -125,16 +126,17 @@ AudioStream::AudioStream()
, mOutChannels(0)
, mWritten(0)
, mAudioClock(this)
, mTimeStretcher(nullptr)
, mDumpFile(nullptr)
, mBytesPerFrame(0)
, mState(INITIALIZED)
, mLastGoodPosition(0)
+ , mIsMonoAudioEnabled(gfxPrefs::MonoAudio())
{
}
AudioStream::~AudioStream()
{
LOG(("AudioStream: delete %p, state %d", this, mState));
MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream,
"Should've called Shutdown() before deleting an AudioStream");
@@ -416,19 +418,22 @@ AudioStream::Write(const AudioDataValue*
return NS_ERROR_FAILURE;
}
NS_ASSERTION(mState == INITIALIZED || mState == STARTED || mState == RUNNING,
"Stream write in unexpected state.");
// Downmix to Stereo.
if (mChannels > 2 && mChannels <= 8) {
DownmixAudioToStereo(const_cast<AudioDataValue*> (aBuf), mChannels, aFrames);
+ } else if (mChannels > 8) {
+ return NS_ERROR_FAILURE;
}
- else if (mChannels > 8) {
- return NS_ERROR_FAILURE;
+
+ if (mChannels >= 2 && mIsMonoAudioEnabled) {
+ DownmixStereoToMono(const_cast<AudioDataValue*> (aBuf), aFrames);
}
const uint8_t* src = reinterpret_cast<const uint8_t*>(aBuf);
uint32_t bytesToCopy = FramesToBytes(aFrames);
while (bytesToCopy > 0) {
uint32_t available = std::min(bytesToCopy, mBuffer.Available());
MOZ_ASSERT(available % mBytesPerFrame == 0,
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -320,13 +320,15 @@ private:
SHUTDOWN // Shutdown has been called
};
StreamState mState;
bool mIsFirst;
// The last good position returned by cubeb_stream_get_position(). Used to
// check if the cubeb position is going backward.
uint64_t mLastGoodPosition;
+ // Get this value from the preferece, if true, we would downmix the stereo.
+ bool mIsMonoAudioEnabled;
};
} // namespace mozilla
#endif
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -183,16 +183,33 @@ int DownmixAudioToStereo(mozilla::AudioD
buffer[i*outChannels] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampL));
sampR = (sampR + 8192)>>14;
buffer[i*outChannels+1] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampR));
}
#endif
return outChannels;
}
+void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
+ uint32_t aFrames)
+{
+ MOZ_ASSERT(aBuffer);
+ const int channels = 2;
+ for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) {
+#ifdef MOZ_SAMPLE_TYPE_FLOAT32
+ float sample = 0.0;
+#else
+ int sample = 0;
+#endif
+ // The sample of the buffer would be interleaved.
+ sample = (aBuffer[fIdx*channels] + aBuffer[fIdx*channels + 1]) * 0.5;
+ aBuffer[fIdx*channels] = aBuffer[fIdx*channels + 1] = sample;
+ }
+}
+
bool
IsVideoContentType(const nsCString& aContentType)
{
NS_NAMED_LITERAL_CSTRING(video, "video");
if (FindInReadable(video, aContentType)) {
return true;
}
return false;
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -163,16 +163,21 @@ void ScaleDisplayByAspectRatio(nsIntSize
// Downmix multichannel Audio samples to Stereo.
// Input are the buffer contains multichannel data,
// the number of channels and the number of frames.
int DownmixAudioToStereo(mozilla::AudioDataValue* buffer,
int channels,
uint32_t frames);
+// Downmix Stereo audio samples to Mono.
+// Input are the buffer contains stereo data and the number of frames.
+void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
+ uint32_t aFrames);
+
bool IsVideoContentType(const nsCString& aContentType);
// Returns true if it's safe to use aPicture as the picture to be
// extracted inside a frame of size aFrame, and scaled up to and displayed
// at a size of aDisplay. You should validate the frame, picture, and
// display regions before using them to display video frames.
bool IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
const nsIntSize& aDisplay);
--- a/dom/media/omx/MediaOmxCommonReader.cpp
+++ b/dom/media/omx/MediaOmxCommonReader.cpp
@@ -6,16 +6,17 @@
#include "MediaOmxCommonReader.h"
#include <stagefright/MediaSource.h>
#include "AbstractMediaDecoder.h"
#include "AudioChannelService.h"
#include "MediaStreamSource.h"
+#include "gfxPrefs.h"
#ifdef MOZ_AUDIO_OFFLOAD
#include <stagefright/Utils.h>
#include <cutils/properties.h>
#include <stagefright/MetaData.h>
#endif
using namespace android;
@@ -31,16 +32,21 @@ MediaOmxCommonReader::MediaOmxCommonRead
{
if (!gMediaDecoderLog) {
gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
}
mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
}
+bool MediaOmxCommonReader::IsMonoAudioEnabled()
+{
+ return gfxPrefs::MonoAudio();
+}
+
#ifdef MOZ_AUDIO_OFFLOAD
void MediaOmxCommonReader::CheckAudioOffload()
{
MOZ_ASSERT(OnTaskQueue());
char offloadProp[128];
property_get("audio.offload.disable", offloadProp, "0");
bool offloadDisable = atoi(offloadProp) != 0;
@@ -61,16 +67,17 @@ void MediaOmxCommonReader::CheckAudioOff
// aren't supported and also duration would be less than a minute
bool isTypeMusic = mAudioChannel == dom::AudioChannel::Content;
DECODER_LOG(LogLevel::Debug, ("%s meta %p, no video %d, no streaming %d,"
" channel type %d", __FUNCTION__, meta.get(), hasNoVideo,
isNotStreaming, mAudioChannel));
if ((meta.get()) && hasNoVideo && isNotStreaming && isTypeMusic &&
- canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC)) {
+ canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC) &&
+ !IsMonoAudioEnabled()) {
DECODER_LOG(LogLevel::Debug, ("Can offload this audio stream"));
mDecoder->SetPlatformCanOffloadAudio(true);
}
}
#endif
} // namespace mozilla
--- a/dom/media/omx/MediaOmxCommonReader.h
+++ b/dom/media/omx/MediaOmxCommonReader.h
@@ -42,13 +42,15 @@ public:
void CheckAudioOffload();
#endif
protected:
dom::AudioChannel mAudioChannel;
// Weak reference to the MediaStreamSource that will be created by either
// MediaOmxReader or MediaCodecReader.
android::MediaStreamSource* mStreamSource;
+ // Get value from the preferece, if true, we stop the audio offload.
+ bool IsMonoAudioEnabled();
};
} // namespace mozilla
#endif // MEDIA_OMX_COMMON_READER_H
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -1097,17 +1097,16 @@ AudioManager::AppendProfileToVolumeSetti
topic.Append(".");
topic.Append(kAudioOutputProfilesTable[idx].tag);
break;
}
}
return topic;
}
-
void
AudioManager::InitVolumeFromDatabase()
{
nsresult rv;
nsCOMPtr<nsISettingsService> service = do_GetService(SETTINGS_SERVICE, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -128,16 +128,19 @@ private:
}
T mValue;
};
// This is where DECL_GFX_PREF for each of the preferences should go.
// We will keep these in an alphabetical order to make it easier to see if
// a method accessing a pref already exists. Just add yours in the list.
+ // It's a short time fix, and will be removed after landing bug 1206637.
+ DECL_GFX_PREF(Live, "accessibility.monoaudio.enable", MonoAudio, bool, false);
+
// The apz prefs are explained in AsyncPanZoomController.cpp
DECL_GFX_PREF(Live, "apz.allow_checkerboarding", APZAllowCheckerboarding, bool, true);
DECL_GFX_PREF(Live, "apz.allow_zooming", APZAllowZooming, bool, false);
DECL_GFX_PREF(Live, "apz.asyncscroll.throttle", APZAsyncScrollThrottleTime, int32_t, 100);
DECL_GFX_PREF(Live, "apz.asyncscroll.timeout", APZAsyncScrollTimeout, int32_t, 300);
DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle", APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold", APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle", APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);