1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 2000
21 * the Initial Developer. All Rights Reserved.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
48 #include "nsIFileURL.h"
49 #include "nsNetUtil.h"
51 #include "nsNativeCharsetUtils.h"
52 #include "nsAutoPtr.h"
54 #include <QApplication>
57 /* used with esd_open_sound */
58 static int esdref
= -1;
59 static PRLibrary
*elib
= nsnull
;
61 // the following from esd.h
63 #define ESD_BITS8 (0x0000)
64 #define ESD_BITS16 (0x0001)
65 #define ESD_MONO (0x0010)
66 #define ESD_STEREO (0x0020)
67 #define ESD_STREAM (0x0000)
68 #define ESD_PLAY (0x1000)
70 #define WAV_MIN_LENGTH 44
72 typedef int (PR_CALLBACK
*EsdOpenSoundType
)(const char *host
);
73 typedef int (PR_CALLBACK
*EsdCloseType
)(int);
75 /* used to play the sounds from the find symbol call */
76 typedef int (PR_CALLBACK
*EsdPlayStreamType
) (int,
80 typedef int (PR_CALLBACK
*EsdAudioOpenType
) (void);
81 typedef int (PR_CALLBACK
*EsdAudioWriteType
) (const void *, int);
82 typedef void (PR_CALLBACK
*EsdAudioCloseType
) (void);
84 NS_IMPL_ISUPPORTS2(nsSound
, nsISound
, nsIStreamLoaderObserver
)
93 /* see above comment */
95 EsdCloseType EsdClose
= (EsdCloseType
) PR_FindFunctionSymbol(elib
, "esd_close");
110 PR_UnloadLibrary(elib
);
118 /* we don't need to do esd_open_sound if we are only going to play files
119 but we will if we want to do things like streams, etc
126 EsdOpenSoundType EsdOpenSound
;
128 elib
= PR_LoadLibrary("libesd.so.0");
129 if (!elib
) return NS_ERROR_FAILURE
;
131 EsdOpenSound
= (EsdOpenSoundType
) PR_FindFunctionSymbol(elib
, "esd_open_sound");
134 return NS_ERROR_FAILURE
;
136 esdref
= (*EsdOpenSound
)("localhost");
139 return NS_ERROR_FAILURE
;
146 NS_METHOD
nsSound::Beep()
148 QApplication::beep();
154 * This can't be implemented directly with QT.
155 * (We can use QSound to play local files but that was not enough.
156 * Also support of media formats is limited)
158 * Current implementation is copied from GTK side and implementation uses ESD interface.
160 * If we have Qtopia then we can drop ESD implementation and use Qtopia "Multimedia API"
162 NS_IMETHODIMP
nsSound::OnStreamComplete(nsIStreamLoader
*aLoader
,
163 nsISupports
*context
,
169 #define GET_WORD(s, i) (s[i+1] << 8) | s[i]
170 #define GET_DWORD(s, i) (s[i+3] << 24) | (s[i+2] << 16) | (s[i+1] << 8) | s[i]
172 // print a load error on bad status, and return
173 if (NS_FAILED(aStatus
)) {
176 nsCOMPtr
<nsIRequest
> request
;
177 aLoader
->GetRequest(getter_AddRefs(request
));
179 nsCOMPtr
<nsIURI
> uri
;
180 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
182 channel
->GetURI(getter_AddRefs(uri
));
184 nsCAutoString uriSpec
;
185 uri
->GetSpec(uriSpec
);
186 printf("Failed to load %s\n", uriSpec
.get());
196 PRUint32 samples_per_sec
= 0, avg_bytes_per_sec
= 0, chunk_len
= 0;
197 PRUint16 format
, channels
= 1, bits_per_sample
= 0;
198 const PRUint8
*audio
= nsnull
;
199 size_t audio_len
= 0;
202 NS_WARNING("Sound stream too short to determine its type");
203 return NS_ERROR_FAILURE
;
206 if (memcmp(data
, "RIFF", 4)) {
208 printf("We only support WAV files currently.\n");
210 return NS_ERROR_FAILURE
;
213 if (dataLen
<= WAV_MIN_LENGTH
) {
214 NS_WARNING("WAV files should be longer than 44 bytes.");
215 return NS_ERROR_FAILURE
;
219 while (i
+ 7 < dataLen
) {
220 if (!memcmp(data
+ i
, "fmt ", 4) && !chunk_len
) {
223 /* length of the rest of this subblock (should be 16 for PCM data */
224 chunk_len
= GET_DWORD(data
, i
);
227 if (chunk_len
< 16 || i
+ chunk_len
>= dataLen
) {
228 NS_WARNING("Invalid WAV file: bad fmt chunk.");
229 return NS_ERROR_FAILURE
;
232 format
= GET_WORD(data
, i
);
235 channels
= GET_WORD(data
, i
);
238 samples_per_sec
= GET_DWORD(data
, i
);
241 avg_bytes_per_sec
= GET_DWORD(data
, i
);
247 bits_per_sample
= GET_WORD(data
, i
);
250 /* we don't support WAVs with odd compression codes */
252 NS_WARNING("Extra format bits found in WAV. Ignoring");
255 } else if (!memcmp(data
+ i
, "data", 4)) {
258 NS_WARNING("Invalid WAV file: no fmt chunk found");
259 return NS_ERROR_FAILURE
;
262 audio_len
= GET_DWORD(data
, i
);
265 /* try to play truncated WAVs */
266 if (i
+ audio_len
> dataLen
)
267 audio_len
= dataLen
- i
;
273 i
+= GET_DWORD(data
, i
);
279 NS_WARNING("Invalid WAV file: no data chunk found");
280 return NS_ERROR_FAILURE
;
283 /* No audio data? well, at least the WAV was valid. */
288 printf("f: %d | c: %d | sps: %li | abps: %li | ba: %d | bps: %d | rate: %li\n",
289 format
, channels
, samples_per_sec
, avg_bytes_per_sec
, block_align
, bits_per_sample
, rate
);
292 /* open up connection to esd */
293 EsdPlayStreamType EsdPlayStream
=
294 (EsdPlayStreamType
) PR_FindFunctionSymbol(elib
,
297 return NS_ERROR_FAILURE
;
299 mask
= ESD_PLAY
| ESD_STREAM
;
301 if (bits_per_sample
== 8)
311 nsAutoArrayPtr
<PRUint8
> buf
;
313 // ESD only handle little-endian data.
314 // Swap the byte order if we're on a big-endian architecture.
316 if (bits_per_sample
!= 8) {
317 buf
= new PRUint8
[audio_len
];
319 return NS_ERROR_OUT_OF_MEMORY
;
320 for (PRUint32 j
= 0; j
+ 2 < audio_len
; j
+= 2) {
321 buf
[j
] = audio
[j
+ 1];
322 buf
[j
+ 1] = audio
[j
];
329 fd
= (*EsdPlayStream
)(mask
, samples_per_sec
, NULL
, "mozillaSound");
332 int *esd_audio_format
= (int *) PR_FindSymbol(elib
, "esd_audio_format");
333 int *esd_audio_rate
= (int *) PR_FindSymbol(elib
, "esd_audio_rate");
334 EsdAudioOpenType EsdAudioOpen
= (EsdAudioOpenType
) PR_FindFunctionSymbol(elib
, "esd_audio_open");
335 EsdAudioWriteType EsdAudioWrite
= (EsdAudioWriteType
) PR_FindFunctionSymbol(elib
, "esd_audio_write");
336 EsdAudioCloseType EsdAudioClose
= (EsdAudioCloseType
) PR_FindFunctionSymbol(elib
, "esd_audio_close");
338 if (!esd_audio_format
|| !esd_audio_rate
||
339 !EsdAudioOpen
|| !EsdAudioWrite
|| !EsdAudioClose
)
340 return NS_ERROR_FAILURE
;
342 *esd_audio_format
= mask
;
343 *esd_audio_rate
= samples_per_sec
;
344 fd
= (*EsdAudioOpen
)();
347 return NS_ERROR_FAILURE
;
349 (*EsdAudioWrite
)(audio
, audio_len
);
352 while (audio_len
> 0) {
353 size_t written
= write(fd
, audio
, audio_len
);
357 audio_len
-= written
;
365 NS_METHOD
nsSound::Play(nsIURL
*aURL
)
373 return NS_ERROR_FAILURE
;
375 nsCOMPtr
<nsIStreamLoader
> loader
;
376 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), aURL
, this);
381 NS_IMETHODIMP
nsSound::PlaySystemSound(const nsAString
&aSoundAlias
)
383 if (aSoundAlias
.EqualsLiteral("_moz_mailbeep")) {
388 nsCOMPtr
<nsIURI
> fileURI
;
390 // create a nsILocalFile and then a nsIFileURL from that
391 nsCOMPtr
<nsILocalFile
> soundFile
;
392 rv
= NS_NewLocalFile(aSoundAlias
, PR_TRUE
,
393 getter_AddRefs(soundFile
));
394 NS_ENSURE_SUCCESS(rv
,rv
);
396 rv
= NS_NewFileURI(getter_AddRefs(fileURI
), soundFile
);
397 NS_ENSURE_SUCCESS(rv
,rv
);
399 nsCOMPtr
<nsIFileURL
> fileURL
= do_QueryInterface(fileURI
,&rv
);
400 NS_ENSURE_SUCCESS(rv
,rv
);