CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / qt / nsSound.cpp
blobdc3fe7f2b196f366730ba1bd71109077723993f3
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
14 * License.
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.
23 * Contributor(s):
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 ***** */
39 #include <QApplication>
40 #include <QSound>
42 #include <string.h>
44 #include "nscore.h"
45 #include "plstr.h"
46 #include "prlink.h"
48 #include "nsSound.h"
49 #include "nsString.h"
51 #include "nsIURL.h"
52 #include "nsIFileURL.h"
53 #include "nsNetUtil.h"
54 #include "nsCOMPtr.h"
55 #include "nsNativeCharsetUtils.h"
56 #include "nsAutoPtr.h"
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)
84 nsSound::nsSound()
85 : mInited( PR_FALSE )
89 nsSound::~nsSound()
91 /* see above comment */
92 if (esdref != -1) {
93 EsdCloseType EsdClose = (EsdCloseType) PR_FindFunctionSymbol(elib, "esd_close");
94 if (EsdClose)
95 (*EsdClose)(esdref);
96 esdref = -1;
102 * unload esd library
104 void
105 nsSound::Shutdown()
107 if (elib) {
108 PR_UnloadLibrary(elib);
109 elib = nsnull;
113 NS_IMETHODIMP
114 nsSound::Init()
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
119 if (mInited)
120 return NS_OK;
121 if (elib)
122 return NS_OK;
124 EsdOpenSoundType EsdOpenSound;
126 elib = PR_LoadLibrary("libesd.so.0");
127 if (!elib) return NS_ERROR_NOT_AVAILABLE;
129 EsdOpenSound = (EsdOpenSoundType) PR_FindFunctionSymbol(elib, "esd_open_sound");
131 if (!EsdOpenSound)
132 return NS_ERROR_FAILURE;
134 esdref = (*EsdOpenSound)("localhost");
136 if (!esdref)
137 return NS_ERROR_FAILURE;
139 mInited = PR_TRUE;
141 return NS_OK;
144 NS_METHOD nsSound::Beep()
146 QApplication::beep();
147 return NS_OK;
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,
162 nsresult aStatus,
163 PRUint32 dataLen,
164 const PRUint8 *data)
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)) {
172 #ifdef DEBUG
173 if (aLoader) {
174 nsCOMPtr<nsIRequest> request;
175 aLoader->GetRequest(getter_AddRefs(request));
176 if (request) {
177 nsCOMPtr<nsIURI> uri;
178 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
179 if (channel) {
180 channel->GetURI(getter_AddRefs(uri));
181 if (uri) {
182 nsCAutoString uriSpec;
183 uri->GetSpec(uriSpec);
184 printf("Failed to load %s\n", uriSpec.get());
189 #endif
190 return aStatus;
193 int fd, mask = 0;
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;
199 if (dataLen < 4) {
200 NS_WARNING("Sound stream too short to determine its type");
201 return NS_ERROR_FAILURE;
204 if (memcmp(data, "RIFF", 4)) {
205 #ifdef DEBUG
206 printf("We only support WAV files currently.\n");
207 #endif
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;
216 PRUint32 i = 12;
217 while (i + 7 < dataLen) {
218 if (!memcmp(data + i, "fmt ", 4) && !chunk_len) {
219 i += 4;
221 /* length of the rest of this subblock (should be 16 for PCM data */
222 chunk_len = GET_DWORD(data, i);
223 i += 4;
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);
231 i += 2;
233 channels = GET_WORD(data, i);
234 i += 2;
236 samples_per_sec = GET_DWORD(data, i);
237 i += 4;
239 avg_bytes_per_sec = GET_DWORD(data, i);
240 i += 4;
242 // block align
243 i += 2;
245 bits_per_sample = GET_WORD(data, i);
246 i += 2;
248 /* we don't support WAVs with odd compression codes */
249 if (chunk_len != 16)
250 NS_WARNING("Extra format bits found in WAV. Ignoring");
252 i += chunk_len - 16;
253 } else if (!memcmp(data + i, "data", 4)) {
254 i += 4;
255 if (!chunk_len) {
256 NS_WARNING("Invalid WAV file: no fmt chunk found");
257 return NS_ERROR_FAILURE;
260 audio_len = GET_DWORD(data, i);
261 i += 4;
263 /* try to play truncated WAVs */
264 if (i + audio_len > dataLen)
265 audio_len = dataLen - i;
267 audio = data + i;
268 break;
269 } else {
270 i += 4;
271 i += GET_DWORD(data, i);
272 i += 4;
276 if (!audio) {
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. */
282 if (!audio_len)
283 return NS_OK;
285 #if 0
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);
288 #endif
290 /* open up connection to esd */
291 EsdPlayStreamType EsdPlayStream =
292 (EsdPlayStreamType) PR_FindFunctionSymbol(elib,
293 "esd_play_stream");
294 if (!EsdPlayStream)
295 return NS_ERROR_FAILURE;
297 mask = ESD_PLAY | ESD_STREAM;
299 if (bits_per_sample == 8)
300 mask |= ESD_BITS8;
301 else
302 mask |= ESD_BITS16;
304 if (channels == 1)
305 mask |= ESD_MONO;
306 else
307 mask |= ESD_STEREO;
309 nsAutoArrayPtr<PRUint8> buf;
311 // ESD only handle little-endian data.
312 // Swap the byte order if we're on a big-endian architecture.
313 #ifdef IS_BIG_ENDIAN
314 if (bits_per_sample != 8) {
315 buf = new PRUint8[audio_len];
316 if (!buf)
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];
323 audio = buf;
325 #endif
327 fd = (*EsdPlayStream)(mask, samples_per_sec, NULL, "mozillaSound");
329 if (fd < 0) {
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)();
344 if (fd < 0)
345 return NS_ERROR_FAILURE;
347 (*EsdAudioWrite)(audio, audio_len);
348 (*EsdAudioClose)();
349 } else {
350 while (audio_len > 0) {
351 size_t written = write(fd, audio, audio_len);
352 if (written <= 0)
353 break;
354 audio += written;
355 audio_len -= written;
357 close(fd);
360 return NS_OK;
363 NS_METHOD nsSound::Play(nsIURL *aURL)
365 nsresult rv;
367 if (!mInited)
368 Init();
370 if (!elib)
371 return NS_ERROR_NOT_AVAILABLE;
373 nsCOMPtr<nsIStreamLoader> loader;
374 rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
376 return rv;
379 NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
381 if (NS_IsMozAliasSound(aSoundAlias)) {
382 NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead");
383 if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
384 return Beep();
385 return NS_OK;
388 nsresult rv;
389 nsCOMPtr <nsIURI> fileURI;
391 // create a nsILocalFile and then a nsIFileURL from that
392 nsCOMPtr <nsILocalFile> soundFile;
393 rv = NS_NewLocalFile(aSoundAlias, PR_TRUE,
394 getter_AddRefs(soundFile));
395 NS_ENSURE_SUCCESS(rv,rv);
397 rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile);
398 NS_ENSURE_SUCCESS(rv,rv);
400 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI,&rv);
401 NS_ENSURE_SUCCESS(rv,rv);
403 rv = Play(fileURL);
404 return rv;
408 NS_IMETHODIMP nsSound::PlayEventSound(PRUint32 aEventId)
410 return aEventId == EVENT_NEW_MAIL_RECEIVED ? Beep() : NS_OK;