1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 code.
18 * The Initial Developer of the Original Code is the Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
23 * Chris Double <chris.double@double.co.nz>
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 ***** */
42 #include "nsAutoPtr.h"
43 #include "nsAudioStream.h"
44 #include "nsAlgorithm.h"
46 #include "sydneyaudio/sydney_audio.h"
50 PRLogModuleInfo
* gAudioStreamLog
= nsnull
;
53 #define FAKE_BUFFER_SIZE 176400
55 static float CurrentTimeInSeconds()
57 return PR_IntervalToMilliseconds(PR_IntervalNow()) / 1000.0;
60 void nsAudioStream::InitLibrary()
63 gAudioStreamLog
= PR_NewLogModule("nsAudioStream");
67 void nsAudioStream::ShutdownLibrary()
71 nsAudioStream::nsAudioStream() :
80 mFormat(FORMAT_S16_LE
),
85 void nsAudioStream::Init(PRInt32 aNumChannels
, PRInt32 aRate
, SampleFormat aFormat
)
88 mChannels
= aNumChannels
;
90 mStartTime
= CurrentTimeInSeconds();
91 if (sa_stream_create_pcm(reinterpret_cast<sa_stream_t
**>(&mAudioHandle
),
96 aNumChannels
) != SA_SUCCESS
) {
97 mAudioHandle
= nsnull
;
98 PR_LOG(gAudioStreamLog
, PR_LOG_ERROR
, ("nsAudioStream: sa_stream_create_pcm error"));
102 if (sa_stream_open(static_cast<sa_stream_t
*>(mAudioHandle
)) != SA_SUCCESS
) {
103 sa_stream_destroy(static_cast<sa_stream_t
*>(mAudioHandle
));
104 mAudioHandle
= nsnull
;
105 PR_LOG(gAudioStreamLog
, PR_LOG_ERROR
, ("nsAudioStream: sa_stream_open error"));
110 void nsAudioStream::Shutdown()
115 sa_stream_destroy(static_cast<sa_stream_t
*>(mAudioHandle
));
116 mAudioHandle
= nsnull
;
119 void nsAudioStream::Write(const void* aBuf
, PRUint32 aCount
)
121 NS_ABORT_IF_FALSE(aCount
% mChannels
== 0,
122 "Buffer size must be divisible by channel count");
124 mSamplesBuffered
+= aCount
;
125 PRUint32 offset
= mBufferOverflow
.Length();
126 PRInt32 count
= aCount
+ offset
;
131 nsAutoArrayPtr
<short> s_data(new short[count
]);
134 for (PRUint32 i
=0; i
< offset
; ++i
) {
135 s_data
[i
] = mBufferOverflow
.ElementAt(i
);
137 mBufferOverflow
.Clear();
141 const PRUint8
* buf
= static_cast<const PRUint8
*>(aBuf
);
142 PRInt32 volume
= PRInt32((1 << 16) * mVolume
);
143 for (PRUint32 i
= 0; i
< aCount
; ++i
) {
144 s_data
[i
+ offset
] = short(((PRInt32(buf
[i
]) - 128) * volume
) >> 8);
148 case FORMAT_S16_LE
: {
149 const short* buf
= static_cast<const short*>(aBuf
);
150 PRInt32 volume
= PRInt32((1 << 16) * mVolume
);
151 for (PRUint32 i
= 0; i
< aCount
; ++i
) {
152 s_data
[i
+ offset
] = short((PRInt32(buf
[i
]) * volume
) >> 16);
156 case FORMAT_FLOAT32_LE
: {
157 const float* buf
= static_cast<const float*>(aBuf
);
158 for (PRUint32 i
= 0; i
< aCount
; ++i
) {
159 float scaled_value
= floorf(0.5 + 32768 * buf
[i
] * mVolume
);
161 s_data
[i
+ offset
] = (scaled_value
< -32768.0) ?
165 s_data
[i
+offset
] = (scaled_value
> 32767.0) ?
174 PRInt32 available
= Available();
175 if (available
< count
) {
176 mBufferOverflow
.AppendElements(s_data
.get() + available
, (count
- available
));
180 if (sa_stream_write(static_cast<sa_stream_t
*>(mAudioHandle
),
181 s_data
.get(), count
* sizeof(short)) != SA_SUCCESS
) {
182 PR_LOG(gAudioStreamLog
, PR_LOG_ERROR
, ("nsAudioStream: sa_stream_write error"));
188 PRInt32
nsAudioStream::Available()
190 // If the audio backend failed to open, lie and say we'll accept some
193 return FAKE_BUFFER_SIZE
;
196 sa_stream_get_write_size(static_cast<sa_stream_t
*>(mAudioHandle
), &s
);
197 return s
/ sizeof(short);
200 float nsAudioStream::GetVolume()
205 void nsAudioStream::SetVolume(float aVolume
)
207 NS_ASSERTION(aVolume
>= 0.0 && aVolume
<= 1.0, "Invalid volume");
211 void nsAudioStream::Drain()
214 // mSamplesBuffered already accounts for the data in the
215 // mBufferOverflow array.
216 PRUint32 drainTime
= (float(mSamplesBuffered
) / mRate
/ mChannels
- GetTime()) * 1000.0;
217 PR_Sleep(PR_MillisecondsToInterval(drainTime
));
221 // Write any remaining unwritten sound data in the overflow buffer
222 if (!mBufferOverflow
.IsEmpty()) {
223 if (sa_stream_write(static_cast<sa_stream_t
*>(mAudioHandle
),
224 mBufferOverflow
.Elements(),
225 mBufferOverflow
.Length() * sizeof(short)) != SA_SUCCESS
)
229 if (sa_stream_drain(static_cast<sa_stream_t
*>(mAudioHandle
)) != SA_SUCCESS
) {
230 PR_LOG(gAudioStreamLog
, PR_LOG_ERROR
, ("nsAudioStream: sa_stream_drain error"));
235 void nsAudioStream::Pause()
240 // Save the elapsed playback time. Used to offset the wall-clock time
242 mPauseTime
= CurrentTimeInSeconds() - mStartTime
;
251 sa_stream_get_position(static_cast<sa_stream_t
*>(mAudioHandle
), SA_POSITION_WRITE_SOFTWARE
, &bytes
);
253 mSavedPauseBytes
= bytes
;
255 sa_stream_pause(static_cast<sa_stream_t
*>(mAudioHandle
));
258 void nsAudioStream::Resume()
263 // Reset the start time to the current time offset backwards by the
264 // elapsed time saved when the stream paused.
265 mStartTime
= CurrentTimeInSeconds() - mPauseTime
;
272 sa_stream_resume(static_cast<sa_stream_t
*>(mAudioHandle
));
275 mPauseBytes
+= mSavedPauseBytes
;
279 double nsAudioStream::GetTime()
281 // If the audio backend failed to open, emulate the current playback
282 // position using the system clock.
287 float curTime
= CurrentTimeInSeconds() - mStartTime
;
288 float maxTime
= float(mSamplesBuffered
) / mRate
/ mChannels
;
289 return NS_MIN(curTime
, maxTime
);
294 sa_stream_get_position(static_cast<sa_stream_t
*>(mAudioHandle
), SA_POSITION_WRITE_HARDWARE
, &bytes
);
296 sa_stream_get_position(static_cast<sa_stream_t
*>(mAudioHandle
), SA_POSITION_WRITE_SOFTWARE
, &bytes
);
298 return double(bytes
+ mPauseBytes
) / (sizeof(short) * mChannels
* mRate
);