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 ***** */
49 #include "nsIFileURL.h"
50 #include "nsNetUtil.h"
52 #include "nsNativeCharsetUtils.h"
53 #include "nsAutoPtr.h"
55 #include <QApplication>
58 /* used with esd_open_sound */
59 static int esdref
= -1;
60 static PRLibrary
*elib
= nsnull
;
62 // the following from esd.h
64 #define ESD_BITS8 (0x0000)
65 #define ESD_BITS16 (0x0001)
66 #define ESD_MONO (0x0010)
67 #define ESD_STEREO (0x0020)
68 #define ESD_STREAM (0x0000)
69 #define ESD_PLAY (0x1000)
71 #define WAV_MIN_LENGTH 44
73 typedef int (*EsdOpenSoundType
)(const char *host
);
74 typedef int (*EsdCloseType
)(int);
76 /* used to play the sounds from the find symbol call */
77 typedef int (*EsdPlayStreamType
) (int, int, const char *, const char *);
78 typedef int (*EsdAudioOpenType
) (void);
79 typedef int (*EsdAudioWriteType
) (const void *, int);
80 typedef void (*EsdAudioCloseType
) (void);
82 NS_IMPL_ISUPPORTS2(nsSound
, nsISound
, nsIStreamLoaderObserver
)
91 /* see above comment */
93 EsdCloseType EsdClose
= (EsdCloseType
) PR_FindFunctionSymbol(elib
, "esd_close");
108 PR_UnloadLibrary(elib
);
116 /* we don't need to do esd_open_sound if we are only going to play files
117 but we will if we want to do things like streams, etc
124 EsdOpenSoundType EsdOpenSound
;
126 elib
= PR_LoadLibrary("libesd.so.0");
127 if (!elib
) return NS_ERROR_FAILURE
;
129 EsdOpenSound
= (EsdOpenSoundType
) PR_FindFunctionSymbol(elib
, "esd_open_sound");
132 return NS_ERROR_FAILURE
;
134 esdref
= (*EsdOpenSound
)("localhost");
137 return NS_ERROR_FAILURE
;
144 NS_METHOD
nsSound::Beep()
146 QApplication::beep();
152 * This can't be implemented directly with QT.
153 * (We can use QSound to play local files but that was not enough.
154 * Also support of media formats is limited)
156 * Current implementation is copied from GTK side and implementation uses ESD interface.
158 * If we have Qtopia then we can drop ESD implementation and use Qtopia "Multimedia API"
160 NS_IMETHODIMP
nsSound::OnStreamComplete(nsIStreamLoader
*aLoader
,
161 nsISupports
*context
,
167 #define GET_WORD(s, i) (s[i+1] << 8) | s[i]
168 #define GET_DWORD(s, i) (s[i+3] << 24) | (s[i+2] << 16) | (s[i+1] << 8) | s[i]
170 // print a load error on bad status, and return
171 if (NS_FAILED(aStatus
)) {
174 nsCOMPtr
<nsIRequest
> request
;
175 aLoader
->GetRequest(getter_AddRefs(request
));
177 nsCOMPtr
<nsIURI
> uri
;
178 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
180 channel
->GetURI(getter_AddRefs(uri
));
182 nsCAutoString uriSpec
;
183 uri
->GetSpec(uriSpec
);
184 printf("Failed to load %s\n", uriSpec
.get());
194 PRUint32 samples_per_sec
= 0, avg_bytes_per_sec
= 0, chunk_len
= 0;
195 PRUint16 format
, channels
= 1, bits_per_sample
= 0;
196 const PRUint8
*audio
= nsnull
;
197 size_t audio_len
= 0;
200 NS_WARNING("Sound stream too short to determine its type");
201 return NS_ERROR_FAILURE
;
204 if (memcmp(data
, "RIFF", 4)) {
206 printf("We only support WAV files currently.\n");
208 return NS_ERROR_FAILURE
;
211 if (dataLen
<= WAV_MIN_LENGTH
) {
212 NS_WARNING("WAV files should be longer than 44 bytes.");
213 return NS_ERROR_FAILURE
;
217 while (i
+ 7 < dataLen
) {
218 if (!memcmp(data
+ i
, "fmt ", 4) && !chunk_len
) {
221 /* length of the rest of this subblock (should be 16 for PCM data */
222 chunk_len
= GET_DWORD(data
, i
);
225 if (chunk_len
< 16 || i
+ chunk_len
>= dataLen
) {
226 NS_WARNING("Invalid WAV file: bad fmt chunk.");
227 return NS_ERROR_FAILURE
;
230 format
= GET_WORD(data
, i
);
233 channels
= GET_WORD(data
, i
);
236 samples_per_sec
= GET_DWORD(data
, i
);
239 avg_bytes_per_sec
= GET_DWORD(data
, i
);
245 bits_per_sample
= GET_WORD(data
, i
);
248 /* we don't support WAVs with odd compression codes */
250 NS_WARNING("Extra format bits found in WAV. Ignoring");
253 } else if (!memcmp(data
+ i
, "data", 4)) {
256 NS_WARNING("Invalid WAV file: no fmt chunk found");
257 return NS_ERROR_FAILURE
;
260 audio_len
= GET_DWORD(data
, i
);
263 /* try to play truncated WAVs */
264 if (i
+ audio_len
> dataLen
)
265 audio_len
= dataLen
- i
;
271 i
+= GET_DWORD(data
, i
);
277 NS_WARNING("Invalid WAV file: no data chunk found");
278 return NS_ERROR_FAILURE
;
281 /* No audio data? well, at least the WAV was valid. */
286 printf("f: %d | c: %d | sps: %li | abps: %li | ba: %d | bps: %d | rate: %li\n",
287 format
, channels
, samples_per_sec
, avg_bytes_per_sec
, block_align
, bits_per_sample
, rate
);
290 /* open up connection to esd */
291 EsdPlayStreamType EsdPlayStream
=
292 (EsdPlayStreamType
) PR_FindFunctionSymbol(elib
,
295 return NS_ERROR_FAILURE
;
297 mask
= ESD_PLAY
| ESD_STREAM
;
299 if (bits_per_sample
== 8)
309 nsAutoArrayPtr
<PRUint8
> buf
;
311 // ESD only handle little-endian data.
312 // Swap the byte order if we're on a big-endian architecture.
314 if (bits_per_sample
!= 8) {
315 buf
= new PRUint8
[audio_len
];
317 return NS_ERROR_OUT_OF_MEMORY
;
318 for (PRUint32 j
= 0; j
+ 2 < audio_len
; j
+= 2) {
319 buf
[j
] = audio
[j
+ 1];
320 buf
[j
+ 1] = audio
[j
];
327 fd
= (*EsdPlayStream
)(mask
, samples_per_sec
, NULL
, "mozillaSound");
330 int *esd_audio_format
= (int *) PR_FindSymbol(elib
, "esd_audio_format");
331 int *esd_audio_rate
= (int *) PR_FindSymbol(elib
, "esd_audio_rate");
332 EsdAudioOpenType EsdAudioOpen
= (EsdAudioOpenType
) PR_FindFunctionSymbol(elib
, "esd_audio_open");
333 EsdAudioWriteType EsdAudioWrite
= (EsdAudioWriteType
) PR_FindFunctionSymbol(elib
, "esd_audio_write");
334 EsdAudioCloseType EsdAudioClose
= (EsdAudioCloseType
) PR_FindFunctionSymbol(elib
, "esd_audio_close");
336 if (!esd_audio_format
|| !esd_audio_rate
||
337 !EsdAudioOpen
|| !EsdAudioWrite
|| !EsdAudioClose
)
338 return NS_ERROR_FAILURE
;
340 *esd_audio_format
= mask
;
341 *esd_audio_rate
= samples_per_sec
;
342 fd
= (*EsdAudioOpen
)();
345 return NS_ERROR_FAILURE
;
347 (*EsdAudioWrite
)(audio
, audio_len
);
350 while (audio_len
> 0) {
351 size_t written
= write(fd
, audio
, audio_len
);
355 audio_len
-= written
;
363 NS_METHOD
nsSound::Play(nsIURL
*aURL
)
371 return NS_ERROR_FAILURE
;
373 nsCOMPtr
<nsIStreamLoader
> loader
;
374 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), aURL
, this);
379 NS_IMETHODIMP
nsSound::PlaySystemSound(const nsAString
&aSoundAlias
)
381 if (NS_IsMozAliasSound(aSoundAlias
)) {
382 if (aSoundAlias
.Equals(NS_SYSSOUND_MAIL_BEEP
))
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
);