Back to index

lightning-sunbird  0.9+nobinonly
nsSound.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include <string.h>
00041 
00042 #include "nscore.h"
00043 #include "plstr.h"
00044 #include "prlink.h"
00045 
00046 #include "nsSound.h"
00047 
00048 #include "nsIURL.h"
00049 #include "nsIFileURL.h"
00050 #include "nsNetUtil.h"
00051 #include "nsCOMPtr.h"
00052 
00053 #include <stdio.h>
00054 #include <unistd.h>
00055 
00056 #include <gtk/gtk.h>
00057 /* used with esd_open_sound */
00058 static int esdref = -1;
00059 static PRLibrary *elib = nsnull;
00060 
00061 // the following from esd.h
00062 
00063 #define ESD_BITS8  (0x0000)
00064 #define ESD_BITS16 (0x0001) 
00065 #define ESD_MONO (0x0010)
00066 #define ESD_STEREO (0x0020) 
00067 #define ESD_STREAM (0x0000)
00068 #define ESD_PLAY (0x1000)
00069 
00070 #define WAV_MIN_LENGTH 44
00071 
00072 typedef int (PR_CALLBACK *EsdOpenSoundType)(const char *host);
00073 typedef int (PR_CALLBACK *EsdCloseType)(int);
00074 
00075 /* used to play the sounds from the find symbol call */
00076 typedef int (PR_CALLBACK *EsdPlayStreamFallbackType)(int, int, const char *, const char *);
00077 
00078 NS_IMPL_ISUPPORTS2(nsSound, nsISound, nsIStreamLoaderObserver)
00079 
00080 
00081 nsSound::nsSound()
00082 {
00083   mInited = PR_FALSE;
00084 }
00085 
00086 nsSound::~nsSound()
00087 {
00088   /* see above comment */
00089 
00090   if (esdref != -1) {
00091     EsdCloseType EsdClose = (EsdCloseType) PR_FindSymbol(elib, "esd_close");
00092     (*EsdClose)(esdref);
00093     esdref = -1;
00094   }
00095 }
00096 
00097 NS_IMETHODIMP
00098 nsSound::Init()
00099 {
00100   /* we don't need to do esd_open_sound if we are only going to play files
00101      but we will if we want to do things like streams, etc
00102   */
00103   if (mInited) return NS_OK;
00104   if (elib) return NS_OK;
00105   
00106 
00107   EsdOpenSoundType EsdOpenSound;
00108 
00109   elib = PR_LoadLibrary("libesd.so.0");
00110   if (!elib) return NS_ERROR_FAILURE;
00111 
00112   EsdOpenSound = (EsdOpenSoundType) PR_FindSymbol(elib, "esd_open_sound");
00113 
00114   if (!EsdOpenSound)
00115     return NS_ERROR_FAILURE;
00116 
00117   esdref = (*EsdOpenSound)("localhost");
00118 
00119   if (!esdref)
00120     return NS_ERROR_FAILURE;
00121 
00122   mInited = PR_TRUE;
00123 
00124   return NS_OK;
00125 }
00126 
00127 #define GET_WORD(s, i) (s[i+1] << 8) | s[i]
00128 #define GET_DWORD(s, i) (s[i+3] << 24) | (s[i+2] << 16) | (s[i+1] << 8) | s[i]
00129 
00130 NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
00131                                         nsISupports *context,
00132                                         nsresult aStatus,
00133                                         PRUint32 dataLen,
00134                                         const PRUint8 *data)
00135 {
00136 
00137   // print a load error on bad status
00138   if (NS_FAILED(aStatus)) {
00139 #ifdef DEBUG
00140     if (aLoader) {
00141       nsCOMPtr<nsIRequest> request;
00142       aLoader->GetRequest(getter_AddRefs(request));
00143       if (request) {
00144         nsCOMPtr<nsIURI> uri;
00145         nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
00146         if (channel) {
00147             channel->GetURI(getter_AddRefs(uri));
00148             if (uri) {
00149                 nsCAutoString uriSpec;
00150                 uri->GetSpec(uriSpec);
00151                 printf("Failed to load %s\n", uriSpec.get());
00152             }
00153         }
00154       }
00155     }
00156 #endif
00157     return aStatus;
00158   }
00159 
00160   int fd, mask = 0;
00161 
00162   unsigned long samples_per_sec=0, avg_bytes_per_sec=0;
00163   unsigned long rate=0;
00164   unsigned short format, channels = 1, block_align, bits_per_sample=0;
00165 
00166 
00167   if (memcmp(data, "RIFF", 4)) {
00168 #ifdef DEBUG
00169     printf("We only support WAV files currently.\n");
00170 #endif
00171     return NS_ERROR_FAILURE;
00172   }
00173 
00174   if (dataLen <= WAV_MIN_LENGTH) {
00175       NS_WARNING("WAV files should be longer than 44 bytes.");
00176       return NS_ERROR_FAILURE;
00177   }
00178 
00179   PRUint32 i;
00180   for (i= 0; i < dataLen; i++) {
00181 
00182     if (i+3 <= dataLen) 
00183       if ((data[i] == 'f') &&
00184           (data[i+1] == 'm') &&
00185           (data[i+2] == 't') &&
00186           (data[i+3] == ' ')) {
00187         i += 4;
00188 
00189         /* length of the rest of this subblock (should be 16 for PCM data */
00190         //        long len = GET_DWORD(data, i);
00191         i+=4;
00192 
00193         format = GET_WORD(data, i);
00194         i+=2;
00195 
00196         channels = GET_WORD(data, i);
00197         i+=2;
00198 
00199         samples_per_sec = GET_DWORD(data, i);
00200         i+=4;
00201 
00202         avg_bytes_per_sec = GET_DWORD(data, i);
00203         i+=4;
00204 
00205         block_align = GET_WORD(data, i);
00206         i+=2;
00207 
00208         bits_per_sample = GET_WORD(data, i);
00209         i+=2;
00210 
00211         rate = samples_per_sec;
00212 
00213         break;
00214       }
00215   }
00216 
00217 #ifdef DEBUG
00218   printf("f: %d | c: %d | sps: %li | abps: %li | ba: %d | bps: %d | rate: %li\n",
00219          format, channels, samples_per_sec, avg_bytes_per_sec, block_align, bits_per_sample, rate);
00220 #endif
00221 
00222   /* open up conneciton to esd */  
00223   EsdPlayStreamFallbackType EsdPlayStreamFallback = (EsdPlayStreamFallbackType) PR_FindSymbol(elib, "esd_play_stream_fallback");
00224   
00225   mask = ESD_PLAY | ESD_STREAM;
00226 
00227   if (bits_per_sample == 8)
00228     mask |= ESD_BITS8;
00229   else 
00230     mask |= ESD_BITS16;
00231 
00232   if (channels == 1)
00233     mask |= ESD_MONO;
00234   else 
00235     mask |= ESD_STEREO;
00236 
00237   fd = (*EsdPlayStreamFallback)(mask, rate, NULL, "mozillaSound"); 
00238   
00239   if (fd < 0) {
00240     return NS_ERROR_FAILURE;
00241   }
00242 
00243   /* write data out */
00244   // ESD only handle little-endian data. 
00245   // Swap the byte order if we're on a big-endian architecture.
00246 #ifdef IS_BIG_ENDIAN
00247   if (bits_per_sample == 8)
00248     write(fd, data, dataLen);
00249   else {
00250     PRUint8 *buf = new PRUint8[dataLen - WAV_MIN_LENGTH];
00251     // According to the wav file format, the first 44 bytes are headers.
00252     // We don't really need to send them.
00253     if (!buf)
00254       return NS_ERROR_OUT_OF_MEMORY;
00255     for (PRUint32 j = 0; j < dataLen - WAV_MIN_LENGTH - 1; j += 2) {
00256       buf[j] = data[j + WAV_MIN_LENGTH + 1];
00257       buf[j + 1] = data[j + WAV_MIN_LENGTH];
00258     }
00259     write(fd, buf, (dataLen - WAV_MIN_LENGTH));
00260     delete [] buf;
00261   }
00262 #else
00263   write(fd, data, dataLen);
00264 #endif
00265 
00266   close(fd);
00267 
00268   return NS_OK;
00269 }
00270 
00271 NS_METHOD nsSound::Beep()
00272 {
00273   ::gdk_beep();
00274 
00275   return NS_OK;
00276 }
00277 
00278 NS_METHOD nsSound::Play(nsIURL *aURL)
00279 {
00280   nsresult rv;
00281 
00282   if (!mInited)
00283     Init();
00284 
00285   if (!elib) return NS_ERROR_FAILURE;
00286 
00287   nsCOMPtr<nsIStreamLoader> loader;
00288   rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
00289 
00290   return rv;
00291 }
00292 
00293 NS_IMETHODIMP nsSound::PlaySystemSound(const char *aSoundAlias)
00294 {
00295   if (!aSoundAlias) return NS_ERROR_FAILURE;
00296   if (strcmp(aSoundAlias, "_moz_mailbeep") == 0)
00297   {
00298     return Beep();
00299   }
00300   nsresult rv;
00301   nsCOMPtr <nsIURI> fileURI;
00302 
00303   // create a nsILocalFile and then a nsIFileURL from that
00304   nsCOMPtr <nsILocalFile> soundFile;
00305   rv = NS_NewNativeLocalFile(nsDependentCString(aSoundAlias), PR_TRUE, 
00306                              getter_AddRefs(soundFile));
00307   NS_ENSURE_SUCCESS(rv,rv);
00308 
00309   rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile);
00310   NS_ENSURE_SUCCESS(rv,rv);
00311 
00312   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI,&rv);
00313   NS_ENSURE_SUCCESS(rv,rv);
00314   rv = Play(fileURL);
00315   return rv;
00316 }