зеркало из https://github.com/mozilla/gecko-dev.git
Bug 793294 - Implement AudioBuffer; r=bzbarsky,smaug
This is the full implementation of the AudioBuffer object. There are two ways to create these objects from an audio context and this patch implements only one of them. The construction of the AudioBuffer object is a two step process: the object should be created with operator new first, and then InitializeBuffers should be called on it. InitializeBuffers is fallible, because it uses the JS API to create the underlying typed arrays, but that's fine, since the length of the buffers comes from web content, and we don't want to use infallible allocations for those anyways. We hold on to the JS objects from the C++ implementation, and trace through all of those objects, so that a GC does not kill those object without us knowing. The buffer should be possible to manipulate from both C++ and JS, and the C++ object probably needs to support a set of methods for the C++ callers at some point.
This commit is contained in:
Родитель
1623100c46
Коммит
23e1abde57
|
@ -0,0 +1,101 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AudioBuffer.h"
|
||||
#include "mozilla/dom/AudioBufferBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "AudioContext.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mChannels)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_DROP_JS_OBJECTS(tmp, AudioBuffer);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AudioBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
for (uint32_t i = 0; i < tmp->mChannels.Length(); ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mChannels[i])
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioBuffer)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioBuffer)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aLength,
|
||||
uint32_t aSampleRate)
|
||||
: mContext(aContext),
|
||||
mLength(aLength),
|
||||
mSampleRate(aSampleRate)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
AudioBuffer::~AudioBuffer()
|
||||
{
|
||||
// Drop the JS object reference if we're still holding to the channels
|
||||
if (mChannels.Length()) {
|
||||
NS_DROP_JS_OBJECTS(this, AudioBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AudioBuffer::InitializeBuffers(uint32_t aNumberOfChannels, JSContext* aJSContext)
|
||||
{
|
||||
if (!mChannels.SetCapacity(aNumberOfChannels)) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
|
||||
JSObject* array = JS_NewFloat32Array(aJSContext, mLength);
|
||||
if (!array) {
|
||||
return false;
|
||||
}
|
||||
mChannels.AppendElement(array);
|
||||
}
|
||||
|
||||
NS_HOLD_JS_OBJECTS(this, AudioBuffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
AudioBuffer::WrapObject(JSContext* aCx, JSObject* aScope,
|
||||
bool* aTriedToWrap)
|
||||
{
|
||||
return AudioBufferBinding::Wrap(aCx, aScope, this, aTriedToWrap);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
if (aChannel >= mChannels.Length()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
return mChannels[aChannel];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "EnableWebAudioCheck.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "AudioContext.h"
|
||||
|
||||
struct JSContext;
|
||||
struct JSObject;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AudioBuffer MOZ_FINAL : public nsISupports,
|
||||
public nsWrapperCache,
|
||||
public EnableWebAudioCheck
|
||||
{
|
||||
public:
|
||||
AudioBuffer(AudioContext* aContext, uint32_t aLength,
|
||||
uint32_t aSampleRate);
|
||||
~AudioBuffer();
|
||||
|
||||
// This function needs to be called in order to allocate
|
||||
// all of the channels. It is fallible!
|
||||
bool InitializeBuffers(uint32_t aNumberOfChannels,
|
||||
JSContext* aJSContext);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AudioBuffer)
|
||||
|
||||
AudioContext* GetParentObject() const
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
|
||||
bool* aTriedToWrap);
|
||||
|
||||
float SampleRate() const
|
||||
{
|
||||
return mSampleRate;
|
||||
}
|
||||
|
||||
uint32_t Length() const
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
|
||||
float Duration() const
|
||||
{
|
||||
return mLength / mSampleRate;
|
||||
}
|
||||
|
||||
uint32_t NumberOfChannels() const
|
||||
{
|
||||
return mChannels.Length();
|
||||
}
|
||||
|
||||
JSObject* GetChannelData(JSContext* aJSContext, uint32_t aChannel,
|
||||
ErrorResult& aRv) const;
|
||||
|
||||
private:
|
||||
nsRefPtr<AudioContext> mContext;
|
||||
FallibleTArray<JSObject*> mChannels;
|
||||
uint32_t mLength;
|
||||
float mSampleRate;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/dom/AudioContextBinding.h"
|
||||
#include "AudioDestinationNode.h"
|
||||
#include "AudioBufferSourceNode.h"
|
||||
#include "AudioBuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -63,6 +64,19 @@ AudioContext::CreateBufferSource()
|
|||
return bufferNode.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<AudioBuffer>
|
||||
AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
|
||||
uint32_t aLength, float aSampleRate,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<AudioBuffer> buffer = new AudioBuffer(this, aLength, aSampleRate);
|
||||
if (!buffer->InitializeBuffers(aNumberOfChannels, aJSContext)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
return buffer.forget();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace dom {
|
|||
|
||||
class AudioDestinationNode;
|
||||
class AudioBufferSourceNode;
|
||||
class AudioBuffer;
|
||||
|
||||
class AudioContext MOZ_FINAL : public nsISupports,
|
||||
public nsWrapperCache,
|
||||
|
@ -55,6 +56,11 @@ public:
|
|||
|
||||
already_AddRefed<AudioBufferSourceNode> CreateBufferSource();
|
||||
|
||||
already_AddRefed<AudioBuffer>
|
||||
CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
|
||||
uint32_t aLength, float aSampleRate,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMWindow> mWindow;
|
||||
nsRefPtr<AudioDestinationNode> mDestination;
|
||||
|
|
|
@ -15,6 +15,7 @@ LIBRARY_NAME := gkconwebaudio_s
|
|||
LIBXUL_LIBRARY := 1
|
||||
|
||||
CPPSRCS := \
|
||||
AudioBuffer.cpp \
|
||||
AudioBufferSourceNode.cpp \
|
||||
AudioContext.cpp \
|
||||
AudioDestinationNode.cpp \
|
||||
|
@ -25,6 +26,7 @@ CPPSRCS := \
|
|||
|
||||
EXPORTS_NAMESPACES := mozilla/dom
|
||||
EXPORTS_mozilla/dom := \
|
||||
AudioBuffer.h \
|
||||
AudioBufferSourceNode.h \
|
||||
AudioDestinationNode.h \
|
||||
AudioNode.h \
|
||||
|
|
|
@ -11,6 +11,7 @@ relativesrcdir := @relativesrcdir@
|
|||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MOCHITEST_FILES := \
|
||||
test_AudioBuffer.html \
|
||||
test_AudioContext.html \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test whether we can create an AudioContext interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
var ac = new mozAudioContext();
|
||||
var buffer = ac.createBuffer(2, 2048, 44100);
|
||||
SpecialPowers.gc(); // Make sure that our channels are accessible after GC
|
||||
ok(buffer, "Buffer was allocated successfully");
|
||||
is(buffer.sampleRate, 44100, "Correct sample rate");
|
||||
is(buffer.length, 2048, "Correct length");
|
||||
ok(Math.abs(buffer.duration - 2048 / 44100) < 0.0001, "Correct duration");
|
||||
is(buffer.numberOfChannels, 2, "Correct number of channels");
|
||||
for (var i = 0; i < buffer.numberOfChannels; ++i) {
|
||||
var buf = buffer.getChannelData(i);
|
||||
ok(buf, "Buffer index " + i + " exists");
|
||||
ok(buf instanceof Float32Array, "Result is a typed array");
|
||||
is(buf.length, buffer.length, "Correct length");
|
||||
var foundNonZero = false;
|
||||
for (var j = 0; j < buf.length; ++j) {
|
||||
if (buf[j] != 0) {
|
||||
foundNonZero = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok(!foundNonZero, "Buffer " + i + " should be initialized to 0");
|
||||
}
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -66,8 +66,12 @@
|
|||
|
||||
DOMInterfaces = {
|
||||
|
||||
'AudioBuffer' : {
|
||||
},
|
||||
|
||||
'mozAudioContext': {
|
||||
'nativeType': 'AudioContext',
|
||||
'implicitJSContext': [ 'createBuffer' ],
|
||||
},
|
||||
|
||||
'AudioNode' : {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "Skeleton.h"
|
||||
#include "mozilla/dom/SkeletonBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
|
||||
*
|
||||
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[PrefControlled]
|
||||
interface AudioBuffer {
|
||||
|
||||
readonly attribute float sampleRate;
|
||||
readonly attribute long length;
|
||||
|
||||
// in seconds
|
||||
readonly attribute float duration;
|
||||
|
||||
readonly attribute long numberOfChannels;
|
||||
|
||||
[Throws]
|
||||
Float32Array getChannelData(unsigned long channel);
|
||||
|
||||
};
|
||||
|
|
@ -15,6 +15,12 @@ interface mozAudioContext {
|
|||
|
||||
readonly attribute AudioDestinationNode destination;
|
||||
|
||||
[Creator, Throws]
|
||||
AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long length, float sampleRate);
|
||||
|
||||
// [Creator, Throws]
|
||||
// AudioBuffer createBuffer(ArrayBuffer buffer, boolean mixToMono);
|
||||
|
||||
// AudioNode creation
|
||||
AudioBufferSourceNode createBufferSource();
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ generated_webidl_files = \
|
|||
$(NULL)
|
||||
|
||||
webidl_files = \
|
||||
AudioBuffer.webidl \
|
||||
AudioBufferSourceNode.webidl \
|
||||
AudioContext.webidl \
|
||||
AudioDestinationNode.webidl \
|
||||
|
|
|
@ -1024,6 +1024,8 @@ typedef uint32_t JSArrayBufferViewType;
|
|||
|
||||
/*
|
||||
* Create a new typed array with nelements elements.
|
||||
*
|
||||
* These functions (except the WithBuffer variants) fill in the array with zeros.
|
||||
*/
|
||||
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
|
|
Загрузка…
Ссылка в новой задаче