4 * Sound driver implementation.
6 * Portable Windows Library
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
30 * Revision 1.3 2002/02/09 00:52:01 robertj
31 * Slight adjustment to API and documentation for volume functions.
33 * Revision 1.2 2002/02/07 20:57:21 dereks
34 * add SetVolume and GetVolume methods to PSoundChannel
36 * Revision 1.1 2001/08/11 15:38:43 rogerh
37 * Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
42 #pragma implementation "sound.h"
46 // goddamn apple headers
50 //#include "macosaudio/SoundInput.h"
51 //#include "macosaudio/SoundPlayer.h"
52 #include "macosaudio/MacMain.h" // interface to the Main Thread for Carbon
53 #include "macosaudio/ringbuffer.h"
55 #define DEFAULTSAMPLESIZE 16
58 PSound::PSound(unsigned channels
,
59 unsigned samplesPerSecond
,
60 unsigned bitsPerSample
,
65 SetFormat(channels
, samplesPerSecond
, DEFAULTSAMPLESIZE
);
70 memcpy(GetPointer(), buffer
, bufferSize
);
74 PSound::PSound(const PFilePath
& filename
)
77 SetFormat(1, 8000, DEFAULTSAMPLESIZE
);
85 PSound
& PSound::operator=(const PBYTEArray
& data
)
87 PBYTEArray::operator=(data
);
92 void PSound::SetFormat(unsigned channels
,
93 unsigned samplesPerSecond
,
94 unsigned bitsPerSample
)
96 formatInfo
.SetSize( 0 ); // media format inside
98 // Build our format definition
99 sampleSize
= bitsPerSample
;
100 sampleRate
= samplesPerSecond
;
101 numChannels
= channels
;
104 BOOL
PSound::Load(const PFilePath
& filename
)
110 BOOL
PSound::Save(const PFilePath
& filename
)
116 PSoundChannel::PSoundChannel() :
118 mNumChannels(1), mSampleRate(8000), mBitsPerSample(16)
124 PSoundChannel::PSoundChannel(const PString
& device
,
126 unsigned numChannels
,
128 unsigned bitsPerSample
) :
130 mNumChannels(numChannels
), mSampleRate(sampleRate
), mBitsPerSample(bitsPerSample
)
134 Open(device
, dir
, numChannels
, sampleRate
, bitsPerSample
);
138 void PSoundChannel::Construct()
143 PSoundChannel::~PSoundChannel()
147 if( direction
== Recorder
&& mpInput
)
152 PStringArray
PSoundChannel::GetDeviceNames(Directions
/*dir*/)
156 array
[0]= "built-in audio";
161 PString
PSoundChannel::GetDefaultDevice(Directions
/*dir*/)
163 return "built-in audio";
167 BOOL
PSoundChannel::Open(const PString
& dev
,
169 unsigned numChannels
,
171 unsigned bitsPerSample
)
174 PTRACE(1, "PSoundChannel::Open: " << dev
<< "," << (int)dir
);
179 SetFormat(numChannels
, sampleRate
, bitsPerSample
);
181 soundParams
sp(numChannels
, bitsPerSample
, sampleRate
);
183 if( direction
== Player
)
187 // XXX PString only has a const unsigned char * operator
188 // XXX if HAS_UNICODE is not defined -- yet they don't provide
189 // XXX any decent operators for portably extracting into a
191 commandRequest
r(kOpenPlayer
,
192 (const unsigned char *)device
, &sp
);
193 err
= r
.CarbonQueue();
194 if (err
== 0 && r
.m_status
== 0) {
195 os_handle
= r
.m_result
;
198 fprintf(stderr
,"OpenPlayer failed! %d\n",err
);
202 r
= commandRequest(kSetFormatPlayer
, os_handle
, &sp
);
203 err
= r
.CarbonQueue();
204 if (err
!= 0 || r
.m_status
)
205 goto bail
; // destructor will close channel
207 PTRACE(1, "PSoundChannel::Open(p): returning TRUE");
211 if( direction
== Recorder
)
213 mpInput
= new JRingBuffer(4096);
214 if (!mpInput
) goto bail
;
217 commandRequest
r(kOpenRecorder
, (const unsigned char *)dev
, &sp
);
218 err
= r
.CarbonQueue();
219 if (err
== 0 && r
.m_status
== 0) {
220 os_handle
= r
.m_result
;
223 r
= commandRequest(kSetFormatRecorder
, os_handle
, &sp
);
224 err
= r
.CarbonQueue();
225 if (err
|| r
.m_status
) goto bail
;
227 r
= commandRequest(kStartRecorder
,
228 os_handle
, (void *)mpInput
);
229 err
= r
.CarbonQueue();
230 if (err
|| r
.m_status
) goto bail
;
232 PTRACE(1, "PSoundChannel::Open(r): returning TRUE");
234 } else assert(0); // bad direction type
241 BOOL
PSoundChannel::SetFormat(unsigned numChannels
,
243 unsigned bitsPerSample
)
245 PTRACE(1, "PSoundChannel::SetFormat: " << numChannels
<< "," << sampleRate
<< "," << bitsPerSample
);
247 PAssert(numChannels
>= 1 && numChannels
<= 2, PInvalidParameter
);
248 PAssert( bitsPerSample
== 0 ||
249 bitsPerSample
== 8 ||
250 bitsPerSample
== 16 ||
251 bitsPerSample
== 32, PInvalidParameter
);
254 PError
<< "\tWarning: sample bits parameter is zero. Float?" << endl
;
256 mNumChannels
= numChannels
;
257 mSampleRate
= sampleRate
;
258 mBitsPerSample
= bitsPerSample
;
260 soundParams
sp(numChannels
, bitsPerSample
, sampleRate
);
262 if( direction
== Player
)
267 commandRequest
r(kSetFormatPlayer
, os_handle
, &sp
);
268 int err
= r
.CarbonQueue();
269 if (err
|| r
.m_status
)
274 if( direction
== Recorder
)
279 commandRequest
r(kSetFormatRecorder
, os_handle
, &sp
);
280 int err
= r
.CarbonQueue();
281 if (err
|| r
.m_status
)
289 BOOL
PSoundChannel::Read( void * buf
, PINDEX len
)
291 if( direction
== Player
)
296 if( direction
== Recorder
)
298 static int total_read
= 0;
299 static int message_level
= 0;
304 int rlen
= mpInput
->Read(buf
, len
);
305 PAssert(rlen
== -1 || rlen
== len
, "huh?");
307 static Nanoseconds gStartTime
, gMostRecentTime
;
308 if (total_read
== 0) gStartTime
= AbsoluteToNanoseconds(UpTime());
309 else gMostRecentTime
= AbsoluteToNanoseconds(UpTime());
311 if (total_read
>= 80000 && message_level
== 0) {
312 fprintf(stderr
,"80000 bytes read\n");
315 else if (total_read
>= 240000 && message_level
== 1) {
316 fprintf(stderr
,"240000 bytes read at %d samples per sec\n",
318 (long)( (*(long long *)&gMostRecentTime
-
319 *(long long *)&gStartTime
) / 100000000LL));
324 return rlen
== len
? TRUE
: FALSE
;
330 BOOL
PSoundChannel::Write( const void * buf
, PINDEX len
)
332 if( direction
== Player
)
334 commandRequest
r(kPlaySample
, (unsigned long)os_handle
,
335 (const unsigned char *)buf
,
337 int err
= r
.CarbonQueue();
338 if (err
|| r
.m_status
) {
339 fprintf(stderr
,"Write failed: err %d status %d\n", err
, r
.m_status
);
346 if( direction
== Recorder
)
354 BOOL
PSoundChannel::Close()
356 if( direction
== Player
)
358 if (os_handle
>= 0) {
359 commandRequest
r(kClosePlayer
, os_handle
);
360 (void) r
.CarbonQueue();
362 isInitialised
= false;
367 if( direction
== Recorder
)
370 commandRequest
r(kCloseRecorder
, os_handle
);
371 (void) r
.CarbonQueue();
374 isInitialised
= false;
382 BOOL
PSoundChannel::SetBuffers(PINDEX size
, PINDEX count
)
384 PAssert(size
> 0 && count
> 0 && count
< 65536, PInvalidParameter
);
390 BOOL
PSoundChannel::GetBuffers(PINDEX
& size
, PINDEX
& count
)
396 BOOL
PSoundChannel::PlaySound(const PSound
& sound
, BOOL wait
)
398 if (!Write((const BYTE
*)sound
, sound
.GetSize()))
402 return WaitForPlayCompletion();
407 BOOL
PSoundChannel::PlayFile(const PFilePath
& filename
, BOOL wait
)
413 BOOL
PSoundChannel::HasPlayCompleted()
415 commandRequest
r(kIsPlaying
, os_handle
);
416 (void)r
.CarbonQueue();
417 return r
.m_result
? FALSE
: TRUE
;
421 BOOL
PSoundChannel::WaitForPlayCompletion()
423 commandRequest
r(kWaitForPlayCompletion
, os_handle
);
424 int err
= r
.CarbonQueue();
425 if (err
== 0) err
= r
.m_status
;
430 BOOL
PSoundChannel::RecordSound(PSound
& sound
)
436 BOOL
PSoundChannel::RecordFile(const PFilePath
& filename
)
442 BOOL
PSoundChannel::StartRecording()
444 if( direction
== Player
)
449 if( direction
== Recorder
)
451 PAssertNULL(mpInput
);
453 commandRequest
r(kStartRecorder
, os_handle
, mpInput
);
454 int err
= r
.CarbonQueue();
455 if (err
== 0) err
= r
.m_status
;
458 PError
<< "Recording started" << endl
;
459 return err
== 0 ? TRUE
: FALSE
;
466 // Is there any record data to read?
467 BOOL
PSoundChannel::IsRecordBufferFull()
469 if( direction
== Recorder
)
471 PAssertNULL(mpInput
);
472 return !mpInput
->IsEmpty();
478 BOOL
PSoundChannel::AreAllRecordBuffersFull()
480 if( direction
== Recorder
)
482 PAssertNULL(mpInput
);
483 return mpInput
->IsFull();
489 BOOL
PSoundChannel::WaitForRecordBufferFull()
491 if( direction
== Recorder
)
493 PAssertNULL(mpInput
);
494 return mpInput
->WaitForData();
500 BOOL
PSoundChannel::WaitForAllRecordBuffersFull()
506 BOOL
PSoundChannel::Abort()
508 if( direction
== Player
)
510 commandRequest
r(kStopPlayer
, os_handle
);
511 int err
= r
.CarbonQueue();
512 if (err
== 0) err
= r
.m_status
;
516 if( direction
== Recorder
)
521 commandRequest
r(kStopRecorder
, os_handle
);
522 int err
= r
.CarbonQueue();
523 if (err
== 0) err
= r
.m_status
;
530 BOOL
PSoundChannel::SetVolume(unsigned newVolume
)
532 cerr
<< __FILE__
<< "PSoundChannel :: SetVolume called in error. Please fix"<<endl
;
536 BOOL
PSoundChannel::GetVolume(unsigned & volume
)
538 cerr
<< __FILE__
<< "PSoundChannel :: GetVolume called in error. Please fix"<<endl
;