backout the latest landing b=463209
[wine-gecko.git] / widget / src / gtk2 / nsSound.cpp
bloba7486b9834ca957eddbbb99463f8de4249870942
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
3 */
4 /* ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is mozilla.org code.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2000
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Stuart Parmenter <pavlov@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include <string.h>
43 #include "nscore.h"
44 #include "plstr.h"
45 #include "prlink.h"
47 #include "nsSound.h"
49 #include "nsIURL.h"
50 #include "nsIFileURL.h"
51 #include "nsNetUtil.h"
52 #include "nsCOMPtr.h"
53 #include "nsAutoPtr.h"
55 #include <stdio.h>
56 #include <unistd.h>
58 #include <gtk/gtk.h>
59 /* used with esd_open_sound */
60 static int esdref = -1;
61 static PRLibrary *elib = nsnull;
63 // the following from esd.h
65 #define ESD_BITS8 (0x0000)
66 #define ESD_BITS16 (0x0001)
67 #define ESD_MONO (0x0010)
68 #define ESD_STEREO (0x0020)
69 #define ESD_STREAM (0x0000)
70 #define ESD_PLAY (0x1000)
72 #define WAV_MIN_LENGTH 44
74 typedef int (*EsdOpenSoundType)(const char *host);
75 typedef int (*EsdCloseType)(int);
77 /* used to play the sounds from the find symbol call */
78 typedef int (*EsdPlayStreamType) (int, int, const char *, const char *);
79 typedef int (*EsdAudioOpenType) (void);
80 typedef int (*EsdAudioWriteType) (const void *, int);
81 typedef void (*EsdAudioCloseType) (void);
83 NS_IMPL_ISUPPORTS2(nsSound, nsISound, nsIStreamLoaderObserver)
85 ////////////////////////////////////////////////////////////////////////
86 nsSound::nsSound()
88 mInited = PR_FALSE;
91 nsSound::~nsSound()
93 /* see above comment */
94 if (esdref != -1) {
95 EsdCloseType EsdClose = (EsdCloseType) PR_FindFunctionSymbol(elib, "esd_close");
96 if (EsdClose)
97 (*EsdClose)(esdref);
98 esdref = -1;
102 NS_IMETHODIMP
103 nsSound::Init()
105 /* we don't need to do esd_open_sound if we are only going to play files
106 but we will if we want to do things like streams, etc
108 if (mInited)
109 return NS_OK;
110 if (elib)
111 return NS_OK;
113 EsdOpenSoundType EsdOpenSound;
115 elib = PR_LoadLibrary("libesd.so.0");
116 if (!elib) return NS_ERROR_FAILURE;
118 EsdOpenSound = (EsdOpenSoundType) PR_FindFunctionSymbol(elib, "esd_open_sound");
120 if (!EsdOpenSound)
121 return NS_ERROR_FAILURE;
123 esdref = (*EsdOpenSound)("localhost");
125 if (!esdref)
126 return NS_ERROR_FAILURE;
128 mInited = PR_TRUE;
130 return NS_OK;
133 /* static */ void
134 nsSound::Shutdown()
136 if (elib) {
137 PR_UnloadLibrary(elib);
138 elib = nsnull;
142 #define GET_WORD(s, i) (s[i+1] << 8) | s[i]
143 #define GET_DWORD(s, i) (s[i+3] << 24) | (s[i+2] << 16) | (s[i+1] << 8) | s[i]
145 NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
146 nsISupports *context,
147 nsresult aStatus,
148 PRUint32 dataLen,
149 const PRUint8 *data)
152 // print a load error on bad status, and return
153 if (NS_FAILED(aStatus)) {
154 #ifdef DEBUG
155 if (aLoader) {
156 nsCOMPtr<nsIRequest> request;
157 aLoader->GetRequest(getter_AddRefs(request));
158 if (request) {
159 nsCOMPtr<nsIURI> uri;
160 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
161 if (channel) {
162 channel->GetURI(getter_AddRefs(uri));
163 if (uri) {
164 nsCAutoString uriSpec;
165 uri->GetSpec(uriSpec);
166 printf("Failed to load %s\n", uriSpec.get());
171 #endif
172 return aStatus;
175 int fd, mask = 0;
176 PRUint32 samples_per_sec = 0, avg_bytes_per_sec = 0, chunk_len = 0;
177 PRUint16 format, channels = 1, bits_per_sample = 0;
178 const PRUint8 *audio = nsnull;
179 size_t audio_len = 0;
181 if (dataLen < 4) {
182 NS_WARNING("Sound stream too short to determine its type");
183 return NS_ERROR_FAILURE;
186 if (memcmp(data, "RIFF", 4)) {
187 #ifdef DEBUG
188 printf("We only support WAV files currently.\n");
189 #endif
190 return NS_ERROR_FAILURE;
193 if (dataLen <= WAV_MIN_LENGTH) {
194 NS_WARNING("WAV files should be longer than 44 bytes.");
195 return NS_ERROR_FAILURE;
198 PRUint32 i = 12;
199 while (i + 7 < dataLen) {
200 if (!memcmp(data + i, "fmt ", 4) && !chunk_len) {
201 i += 4;
203 /* length of the rest of this subblock (should be 16 for PCM data */
204 chunk_len = GET_DWORD(data, i);
205 i += 4;
207 if (chunk_len < 16 || i + chunk_len >= dataLen) {
208 NS_WARNING("Invalid WAV file: bad fmt chunk.");
209 return NS_ERROR_FAILURE;
212 format = GET_WORD(data, i);
213 i += 2;
215 channels = GET_WORD(data, i);
216 i += 2;
218 samples_per_sec = GET_DWORD(data, i);
219 i += 4;
221 avg_bytes_per_sec = GET_DWORD(data, i);
222 i += 4;
224 // block align
225 i += 2;
227 bits_per_sample = GET_WORD(data, i);
228 i += 2;
230 /* we don't support WAVs with odd compression codes */
231 if (chunk_len != 16)
232 NS_WARNING("Extra format bits found in WAV. Ignoring");
234 i += chunk_len - 16;
235 } else if (!memcmp(data + i, "data", 4)) {
236 i += 4;
237 if (!chunk_len) {
238 NS_WARNING("Invalid WAV file: no fmt chunk found");
239 return NS_ERROR_FAILURE;
242 audio_len = GET_DWORD(data, i);
243 i += 4;
245 /* try to play truncated WAVs */
246 if (i + audio_len > dataLen)
247 audio_len = dataLen - i;
249 audio = data + i;
250 break;
251 } else {
252 i += 4;
253 i += GET_DWORD(data, i);
254 i += 4;
258 if (!audio) {
259 NS_WARNING("Invalid WAV file: no data chunk found");
260 return NS_ERROR_FAILURE;
263 /* No audio data? well, at least the WAV was valid. */
264 if (!audio_len)
265 return NS_OK;
267 #if 0
268 printf("f: %d | c: %d | sps: %li | abps: %li | ba: %d | bps: %d | rate: %li\n",
269 format, channels, samples_per_sec, avg_bytes_per_sec, block_align, bits_per_sample, rate);
270 #endif
272 /* open up connection to esd */
273 EsdPlayStreamType EsdPlayStream =
274 (EsdPlayStreamType) PR_FindFunctionSymbol(elib,
275 "esd_play_stream");
276 if (!EsdPlayStream)
277 return NS_ERROR_FAILURE;
279 mask = ESD_PLAY | ESD_STREAM;
281 if (bits_per_sample == 8)
282 mask |= ESD_BITS8;
283 else
284 mask |= ESD_BITS16;
286 if (channels == 1)
287 mask |= ESD_MONO;
288 else
289 mask |= ESD_STEREO;
291 nsAutoArrayPtr<PRUint8> buf;
293 // ESD only handle little-endian data.
294 // Swap the byte order if we're on a big-endian architecture.
295 #ifdef IS_BIG_ENDIAN
296 if (bits_per_sample != 8) {
297 buf = new PRUint8[audio_len];
298 if (!buf)
299 return NS_ERROR_OUT_OF_MEMORY;
300 for (PRUint32 j = 0; j + 2 < audio_len; j += 2) {
301 buf[j] = audio[j + 1];
302 buf[j + 1] = audio[j];
305 audio = buf;
307 #endif
309 fd = (*EsdPlayStream)(mask, samples_per_sec, NULL, "mozillaSound");
311 if (fd < 0) {
312 int *esd_audio_format = (int *) PR_FindSymbol(elib, "esd_audio_format");
313 int *esd_audio_rate = (int *) PR_FindSymbol(elib, "esd_audio_rate");
314 EsdAudioOpenType EsdAudioOpen = (EsdAudioOpenType) PR_FindFunctionSymbol(elib, "esd_audio_open");
315 EsdAudioWriteType EsdAudioWrite = (EsdAudioWriteType) PR_FindFunctionSymbol(elib, "esd_audio_write");
316 EsdAudioCloseType EsdAudioClose = (EsdAudioCloseType) PR_FindFunctionSymbol(elib, "esd_audio_close");
318 if (!esd_audio_format || !esd_audio_rate ||
319 !EsdAudioOpen || !EsdAudioWrite || !EsdAudioClose)
320 return NS_ERROR_FAILURE;
322 *esd_audio_format = mask;
323 *esd_audio_rate = samples_per_sec;
324 fd = (*EsdAudioOpen)();
326 if (fd < 0)
327 return NS_ERROR_FAILURE;
329 (*EsdAudioWrite)(audio, audio_len);
330 (*EsdAudioClose)();
331 } else {
332 while (audio_len > 0) {
333 size_t written = write(fd, audio, audio_len);
334 if (written <= 0)
335 break;
336 audio += written;
337 audio_len -= written;
339 close(fd);
342 return NS_OK;
345 NS_METHOD nsSound::Beep()
347 ::gdk_beep();
348 return NS_OK;
351 NS_METHOD nsSound::Play(nsIURL *aURL)
353 nsresult rv;
355 if (!mInited)
356 Init();
358 if (!elib)
359 return NS_ERROR_FAILURE;
361 nsCOMPtr<nsIStreamLoader> loader;
362 rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
364 return rv;
367 NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
369 if (aSoundAlias.EqualsLiteral("_moz_mailbeep")) {
370 return Beep();
373 nsresult rv;
374 nsCOMPtr <nsIURI> fileURI;
376 // create a nsILocalFile and then a nsIFileURL from that
377 nsCOMPtr <nsILocalFile> soundFile;
378 rv = NS_NewLocalFile(aSoundAlias, PR_TRUE,
379 getter_AddRefs(soundFile));
380 NS_ENSURE_SUCCESS(rv,rv);
382 rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile);
383 NS_ENSURE_SUCCESS(rv,rv);
385 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI,&rv);
386 NS_ENSURE_SUCCESS(rv,rv);
388 rv = Play(fileURL);
390 return rv;