Skip to content

Instantly share code, notes, and snippets.

@notmasteryet
Created May 11, 2011 01:33
Show Gist options
  • Save notmasteryet/965743 to your computer and use it in GitHub Desktop.
Save notmasteryet/965743 to your computer and use it in GitHub Desktop.
Audio for Worker v1
diff --git a/content/base/public/Makefile.in b/content/base/public/Makefile.in
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -119,12 +119,13 @@ XPIDLSRCS = \
nsIScriptEventManager.idl \
nsIImageLoadingContent.idl \
nsIObjectLoadingContent.idl \
nsIFrameLoader.idl \
nsIXMLHttpRequest.idl \
nsIContentSecurityPolicy.idl \
nsIFrameMessageManager.idl \
nsIWebSocket.idl \
+ nsIAudioDataDestination.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk
diff --git a/content/base/public/nsIAudioDataDestination.idl b/content/base/public/nsIAudioDataDestination.idl
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsIAudioDataDestination.idl
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+%{C++
+// for jsval
+#include "jsapi.h"
+%}
+
+
+[scriptable, uuid(cd1a6a6b-bc4c-4e5b-b7da-53dccc878ab8)]
+interface nsIAudioDataDestination : nsISupports
+{
+ // Setup the audio stream for writing
+ void mozSetup(in PRUint32 channels, in PRUint32 rate);
+
+ // Write audio to the audio stream
+ [implicit_jscontext]
+ unsigned long mozWriteAudio(in jsval data);
+
+ // Get the current offset (measured in samples since the start) of the audio
+ // stream created using mozWriteAudio().
+ unsigned long long mozCurrentSampleOffset();
+
+ // Shutdown the audio stream
+ void mozShutdown();
+};
+
diff --git a/dom/src/threads/Makefile.in b/dom/src/threads/Makefile.in
--- a/dom/src/threads/Makefile.in
+++ b/dom/src/threads/Makefile.in
@@ -56,16 +56,17 @@ CPPSRCS = \
nsDOMWorkerMessageHandler.cpp \
nsDOMWorkerNavigator.cpp \
nsDOMWorkerPool.cpp \
nsDOMWorkerScriptLoader.cpp \
nsDOMWorkerSecurityManager.cpp \
nsDOMWorkerTimeout.cpp \
nsDOMWorkerXHR.cpp \
nsDOMWorkerXHRProxy.cpp \
+ nsDOMWorkerAudio.cpp \
$(NULL)
LOCAL_INCLUDES = \
-I$(topsrcdir)/content/base/src \
-I$(topsrcdir)/content/events/src \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/dom/src/json \
-I$(topsrcdir)/js/src/xpconnect/src \
diff --git a/dom/src/threads/nsDOMWorker.cpp b/dom/src/threads/nsDOMWorker.cpp
--- a/dom/src/threads/nsDOMWorker.cpp
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -63,16 +63,17 @@
#include "nsDOMThreadService.h"
#include "nsDOMWorkerEvents.h"
#include "nsDOMWorkerLocation.h"
#include "nsDOMWorkerNavigator.h"
#include "nsDOMWorkerPool.h"
#include "nsDOMWorkerScriptLoader.h"
#include "nsDOMWorkerTimeout.h"
#include "nsDOMWorkerXHR.h"
+#include "nsDOMWorkerAudio.h"
using namespace mozilla;
class TestComponentThreadsafetyRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
@@ -149,16 +150,19 @@ public:
static JSBool
LoadScripts(JSContext* aCx, uintN aArgc, jsval* aVp);
static JSBool
NewXMLHttpRequest(JSContext* aCx, uintN aArgc, jsval* aVp);
static JSBool
+ NewAudio(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+ static JSBool
NewWorker(JSContext* aCx, uintN aArgc, jsval* aVp) {
return MakeNewWorker(aCx, aArgc, aVp, nsDOMWorker::CONTENT);
}
static JSBool
AtoB(JSContext* aCx, uintN aArgc, jsval* aVp);
static JSBool
@@ -423,16 +427,65 @@ nsDOMWorkerFunctions::NewXMLHttpRequest(
return JS_FALSE;
}
JS_SET_RVAL(aCs, aVp, v);
return JS_TRUE;
}
JSBool
+nsDOMWorkerFunctions::NewAudio(JSContext* aCx,
+ uintN aArgc,
+ jsval* aVp)
+{
+ nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+ NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+ if (worker->IsCanceled()) {
+ return JS_FALSE;
+ }
+
+ if (aArgc) {
+ JS_ReportError(aCx, "Audio constructor takes no arguments!");
+ return JS_FALSE;
+ }
+
+ nsRefPtr<nsDOMWorkerAudio> audio = new nsDOMWorkerAudio(worker);
+ if (!audio) {
+ JS_ReportOutOfMemory(aCx);
+ return JS_FALSE;
+ }
+
+ nsresult rv = audio->Init();
+ if (NS_FAILED(rv)) {
+ JS_ReportError(aCx, "Failed to construct Audio!");
+ return JS_FALSE;
+ }
+
+ rv = worker->AddFeature(audio, aCx);
+ if (NS_FAILED(rv)) {
+ JS_ReportOutOfMemory(aCx);
+ return JS_FALSE;
+ }
+
+ nsCOMPtr<nsIXPConnectJSObjectHolder> audioWrapped;
+ jsval v;
+ rv = nsContentUtils::WrapNative(aCx, JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
+ static_cast<nsIAudioDataDestination*>(audio), &v,
+ getter_AddRefs(audioWrapped));
+ if (NS_FAILED(rv)) {
+ JS_ReportError(aCx, "Failed to wrap Audio!");
+ return JS_FALSE;
+ }
+
+ JS_SET_RVAL(aCs, aVp, v);
+ return JS_TRUE;
+}
+
+JSBool
nsDOMWorkerFunctions::AtoB(JSContext* aCx,
uintN aArgc,
jsval* aVp)
{
nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
if (worker->IsCanceled()) {
@@ -762,16 +815,17 @@ nsDOMWorkerFunctions::CTypesLazyGetter(J
JSFunctionSpec gDOMWorkerFunctions[] = {
{ "dump", nsDOMWorkerFunctions::Dump, 1, 0 },
{ "setTimeout", nsDOMWorkerFunctions::SetTimeout, 1, 0 },
{ "clearTimeout", nsDOMWorkerFunctions::KillTimeout, 1, 0 },
{ "setInterval", nsDOMWorkerFunctions::SetInterval, 1, 0 },
{ "clearInterval", nsDOMWorkerFunctions::KillTimeout, 1, 0 },
{ "importScripts", nsDOMWorkerFunctions::LoadScripts, 1, 0 },
{ "XMLHttpRequest", nsDOMWorkerFunctions::NewXMLHttpRequest, 0, JSFUN_CONSTRUCTOR },
+ { "Audio", nsDOMWorkerFunctions::NewAudio, 0, JSFUN_CONSTRUCTOR },
{ "Worker", nsDOMWorkerFunctions::NewWorker, 1, JSFUN_CONSTRUCTOR },
{ "atob", nsDOMWorkerFunctions::AtoB, 1, 0 },
{ "btoa", nsDOMWorkerFunctions::BtoA, 1, 0 },
{ nsnull, nsnull, 0, 0 }
};
JSFunctionSpec gDOMWorkerChromeFunctions[] = {
{ "ChromeWorker", nsDOMWorkerFunctions::NewChromeWorker, 1, JSFUN_CONSTRUCTOR },
{ nsnull, nsnull, 0, 0 }
diff --git a/dom/src/threads/nsDOMWorkerAudio.cpp b/dom/src/threads/nsDOMWorkerAudio.cpp
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerAudio.cpp
@@ -0,0 +1,254 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMWorkerAudio.h"
+
+// Interfaces
+#include "nsIDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIThread.h"
+#include "nsIXPConnect.h"
+
+// Other includes
+#include "jscntxt.h"
+#include "jstypedarray.h"
+#include "nsAXPCNativeCallContext.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsIClassInfoImpl.h"
+#include "nsJSUtils.h"
+#include "nsThreadUtils.h"
+
+// DOMWorker includes
+#include "nsDOMThreadService.h"
+#include "nsDOMWorkerEvents.h"
+#include "nsDOMWorkerPool.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS_INHERITED3(nsDOMWorkerAudio, nsDOMWorkerFeature,
+ nsIAudioDataDestination,
+ nsIClassInfo,
+ nsIXPCScriptable)
+
+NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerAudio, nsIAudioDataDestination)
+
+NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerAudio)
+
+#define XPC_MAP_CLASSNAME nsDOMWorkerAudio
+#define XPC_MAP_QUOTED_CLASSNAME "Audio"
+#define XPC_MAP_WANT_POSTCREATE
+#define XPC_MAP_WANT_FINALIZE
+
+#define XPC_MAP_FLAGS \
+ nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \
+ nsIXPCScriptable::CLASSINFO_INTERFACES_ONLY | \
+ nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
+
+#include "xpc_map_end.h"
+
+nsDOMWorkerAudio::nsDOMWorkerAudio(nsDOMWorker* aWorker)
+: nsDOMWorkerFeature(aWorker),
+ mAudioStream(nsnull),
+ mWrappedNative(nsnull),
+ mCanceled(PR_FALSE),
+ mChannels(0),
+ mRate(0)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aWorker, "Must have a worker!");
+}
+
+nsDOMWorkerAudio::~nsDOMWorkerAudio()
+{
+ if (mAudioStream) {
+ mAudioStream->Shutdown();
+ mAudioStream = nsnull;
+ }
+}
+
+NS_IMETHODIMP
+nsDOMWorkerAudio::Finalize(nsIXPConnectWrappedNative* /* aWrapper */,
+ JSContext* /* aCx */,
+ JSObject* /* aObj */)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ Shutdown();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerAudio::PostCreate(nsIXPConnectWrappedNative* aWrapper,
+ JSContext* /* aCx */,
+ JSObject* /* aObj */)
+{
+ mWrappedNative = aWrapper;
+ return NS_OK;
+}
+
+nsresult
+nsDOMWorkerAudio::Init()
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ return NS_OK;
+}
+
+void
+nsDOMWorkerAudio::Cancel()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ Shutdown();
+ mWorker = nsnull;
+}
+
+void
+nsDOMWorkerAudio::Shutdown()
+{
+ if (mAudioStream) {
+ mAudioStream->Shutdown();
+ mAudioStream = nsnull;
+ }
+}
+
+NS_IMETHODIMP
+nsDOMWorkerAudio::MozSetup(PRUint32 aChannels, PRUint32 aRate)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ // MozWriteAudio divides by mChannels, so validate now.
+ if (0 == aChannels) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mAudioStream) {
+ mAudioStream->Shutdown();
+ }
+
+ mAudioStream = nsAudioStream::AllocateStream();
+ nsresult rv = mAudioStream->Init(aChannels, aRate,
+ nsAudioStream::FORMAT_FLOAT32);
+ if (NS_FAILED(rv)) {
+ mAudioStream->Shutdown();
+ mAudioStream = nsnull;
+ return rv;
+ }
+
+ mChannels = aChannels;
+ mRate = aRate;
+ // mAudioStream->SetVolume(mVolume);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerAudio::MozWriteAudio(const jsval &aData, JSContext *aCx, PRUint32 *aRetVal)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ JSAutoRequest ar(aCx);
+
+ if (!mAudioStream) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ if (JSVAL_IS_PRIMITIVE(aData)) {
+ return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
+ }
+
+ JSObject *darray = JSVAL_TO_OBJECT(aData);
+ js::AutoValueRooter tsrc_tvr(aCx);
+ js::TypedArray *tsrc = NULL;
+
+ // Allow either Float32Array or plain JS Array
+ if (darray->getClass() == &js::TypedArray::fastClasses[js::TypedArray::TYPE_FLOAT32])
+ {
+ tsrc = js::TypedArray::fromJSObject(darray);
+ } else if (JS_IsArrayObject(aCx, darray)) {
+ JSObject *nobj = js_CreateTypedArrayWithArray(aCx, js::TypedArray::TYPE_FLOAT32, darray);
+ if (!nobj) {
+ return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
+ }
+ *tsrc_tvr.jsval_addr() = OBJECT_TO_JSVAL(nobj);
+ tsrc = js::TypedArray::fromJSObject(nobj);
+ } else {
+ return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
+ }
+
+ PRUint32 dataLength = tsrc->length;
+
+ // Make sure that we are going to write the correct amount of data based
+ // on number of channels.
+ if (dataLength % mChannels != 0) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ // Don't write more than can be written without blocking.
+ PRUint32 writeLen = NS_MIN(mAudioStream->Available(), dataLength);
+
+ nsresult rv = mAudioStream->Write(tsrc->data, writeLen, PR_TRUE);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Return the actual amount written.
+ *aRetVal = writeLen;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerAudio::MozCurrentSampleOffset(PRUint64 *aRetVal)
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ if (!mAudioStream) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ *aRetVal = mAudioStream->GetSampleOffset();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWorkerAudio::MozShutdown()
+{
+ NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+ Shutdown();
+ return NS_OK;
+}
diff --git a/dom/src/threads/nsDOMWorkerAudio.h b/dom/src/threads/nsDOMWorkerAudio.h
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/nsDOMWorkerAudio.h
@@ -0,0 +1,101 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is worker threads.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NSDOMWORKERAUDIO_H__
+#define __NSDOMWORKERAUDIO_H__
+
+// Bases
+#include "nsIClassInfo.h"
+#include "nsIAudioDataDestination.h"
+#include "nsIXPCScriptable.h"
+
+// Other includes
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsAudioStream.h"
+
+// DOMWorker includes
+#include "nsDOMWorker.h"
+#include "nsDOMWorkerMacros.h"
+
+class nsIXPConnectWrappedNative;
+
+class nsDOMWorkerAudio : public nsDOMWorkerFeature,
+ public nsIAudioDataDestination,
+ public nsIClassInfo,
+ public nsIXPCScriptable
+{
+ typedef mozilla::Mutex Mutex;
+
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIAUDIODATADESTINATION
+ NS_DECL_NSICLASSINFO
+ NS_DECL_NSIXPCSCRIPTABLE
+
+ nsDOMWorkerAudio(nsDOMWorker* aWorker);
+
+ nsresult Init();
+
+ virtual void Cancel();
+
+private:
+ virtual ~nsDOMWorkerAudio();
+ void Shutdown();
+
+ Mutex& GetLock() {
+ return mWorker->GetLock();
+ }
+
+ already_AddRefed<nsIXPConnectWrappedNative> GetWrappedNative() {
+ nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative(mWrappedNative);
+ return wrappedNative.forget();
+ }
+
+ nsRefPtr<nsAudioStream> mAudioStream;
+
+ nsIXPConnectWrappedNative* mWrappedNative;
+
+ volatile PRBool mCanceled;
+
+ PRUint32 mChannels;
+
+ PRUint32 mRate;
+};
+
+#endif /* __NSDOMWORKERAUDIO_H__ */
<!DOCTYPE html>
<html>
<head>
<script>
var worker;
function setup() {
worker = new Worker("workerPlay.js");
}
function play() {
worker.postMessage("*");
}
document.addEventListener("DOMContentLoaded", setup, false);
</script>
</head>
<body>
<button onclick="play()">Play</button>
</body></html>
var audio = new Audio();
audio.mozSetup(1, 44100);
var ar = new Array(22050); // new Float32Array(22050);
for (var i = 0; i < 22050; i++) {
ar[i] = Math.sin(2 * Math.PI * i / 44100 * 440);
}
function play() {
audio.mozWriteAudio(ar);
}
addEventListener("message", play, false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment