📄 beaudio.cxx
字号:
/*
* beaudio.cxx
*
* Sound driver implementation.
*
* Portable Windows Library
*
* Copyright (c) 1993-2001 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s):
* Yuri Kiryanov, ykiryanov at users.sourceforge.net,
* Jac Goudsmit <jac@be.com>.
*
* $Log: beaudio.cxx,v $
* Revision 1.1 2006/06/29 04:18:41 joegenbaclor
* *** empty log message ***
*
* Revision 1.16 2004/10/26 18:08:54 ykiryanov
* Added code for old Media Kit, to be backwards compatible with R5, and Zeta ifdef
*
* Revision 1.15 2004/06/16 01:55:10 ykiryanov
* Added usage of lastReadCount - sound capture now works
*
* Revision 1.14 2004/05/30 04:48:45 ykiryanov
* Stable version
*
* Revision 1.12 2004/05/14 05:26:57 ykiryanov
* Fixed dynamic cast bug
*
* Revision 1.11 2004/04/18 00:32:26 ykiryanov
* Fized compiler choking on <dynamic_cast>.
*
* Revision 1.10 2004/04/02 03:29:07 ykiryanov
* New improved code
*
* Revision 1.9 2002/02/09 00:52:01 robertj
* Slight adjustment to API and documentation for volume functions.
*
* Revision 1.8 2002/02/07 20:57:21 dereks
* add SetVolume and GetVolume methods to PSoundChannelBeOS
*
* Revision 1.7 2001/07/09 06:16:15 yurik
* Jac Goudsmit's BeOS changes of July,6th. Cleaning up media subsystem etc.
*
* Revision 1.6 2000/12/16 13:08:56 rogerh
* BeOS changes from Yuri Kiryanov <openh323@kiryanov.com>
*
* Revision 1.5 2000/04/19 00:13:52 robertj
* BeOS port changes.
*
* Revision 1.4 1999/09/21 00:56:29 robertj
* Added more sound support for BeOS (thanks again Yuri!)
*
* Revision 1.3 1999/06/28 09:28:02 robertj
* Portability issues, especially n BeOS (thanks Yuri!)
*
* Revision 1.2 1999/03/05 07:03:27 robertj
* Some more BeOS port changes.
*
* Revision 1.1 1999/03/02 05:41:59 robertj
* More BeOS changes
*
*/
#include <ptlib.h>
#include <ptlib/unix/ptlib/beaudio.h>
PCREATE_SOUND_PLUGIN(BeOS, PSoundChannelBeOS);
/////////////// Debugging stuff ///////////////
#define TL (7)
#define PRINT(x) //do { printf(__FILE__ ":%d %s ", __LINE__, __FUNCTION__); printf x; printf("\n"); } while(0)
#define STATUS(x) // PRINT((x "=%ld", (long)dwLastError))
#define PRINTCB(x) // PRINT(x)
//#define SOUNDDETECT 1 define this for printed output of first pb/rec audio
//#define FILEDUMP 1 define this for dumping audio to wav file
// Macros and global vars for debugging
#ifdef SOUNDDETECT
#define DETECTVARS(buffer,numframes) short *detbuf=(short*)buffer; size_t detframes=numframes;
#define DETECTSOUND() \
do { \
static bool silence=true; \
if (silence) \
{ \
for (size_t i=0; i<detframes; i++) \
{ \
if (detbuf[i]>=255) \
{ \
PRINT(("SOUND DETECTED at %p",detbuf)); \
for (size_t j=0; j<detframes && j<30; j++) \
{ \
char *x; \
asprintf(&x,"%%%ds\n",(detbuf[j]>>10)+32); \
printf(x,"."); \
free(x); \
} \
silence=false; \
break; \
} \
} \
} \
} while(0)
#else
#define DETECTVARS(buffer,numframes)
#define DETECTSOUND()
#endif
#ifdef FILEDUMP
#include "beaudio/AudioFileWriter.h"
BAudioFileWriter *playwriter=NULL;
BAudioFileWriter *recwriter=NULL;
#endif
////////////////////////////////////////////////////////////////////////////////
// PSound
PSound::PSound(unsigned channels,
unsigned samplesPerSecond,
unsigned bitsPerSample,
PINDEX bufferSize,
const BYTE * buffer)
{
encoding = 0;
SetFormat(channels, samplesPerSecond, bitsPerSample);
if (buffer != NULL)
{
memcpy(GetPointer(bufferSize), buffer, bufferSize);
}
}
PSound::PSound(const PFilePath & filename)
{
encoding = 0;
// Set the default format
SetFormat(1, 8000, 16);
// The format is changed if the file is succesfully loaded.
Load(filename);
}
PSound & PSound::operator=(const PBYTEArray & data)
{
PBYTEArray::operator=(data);
return *this;
}
void PSound::SetFormat(unsigned channels,
unsigned samplesPerSecond,
unsigned bitsPerSample)
{
// NOTE: all constructors should call this to initialize
// the local members, especially formatInfo.
// Do NOT call the function with any parameter set to 0!
sampleSize=bitsPerSample;
sampleRate=samplesPerSecond;
numChannels = channels;
// We don't use the encoding member (although we could probably set it to 0=PCM)
// Let the application know it shouldn't assume anything
encoding = 1;
// The formatInfo member to us is a media_format structure.
BOOL setsize_formatInfo=formatInfo.SetSize(sizeof(media_format));
PAssert(setsize_formatInfo, "Unable to set size for sound info array");
// Initialize the media_format struct
// The numbers of bits that we support here are 8, 16 or 32 bits (signed),
// results for other sizes are not defined.
media_format &format=*(media_format*)(const BYTE *)formatInfo;
format.type = B_MEDIA_RAW_AUDIO;
format.u.raw_audio = media_raw_audio_format::wildcard;
format.u.raw_audio.frame_rate=(float)sampleRate;
format.u.raw_audio.channel_count=numChannels;
format.u.raw_audio.format=(sampleSize / 8) & 0xF;
format.u.raw_audio.byte_order=B_MEDIA_HOST_ENDIAN;
format.u.raw_audio.buffer_size=(channels * samplesPerSecond * (bitsPerSample/8))/10; // 1/10 sec buffer
}
BOOL PSound::Load(const PFilePath & filename)
{
// format is a reference to the formatInfo member which stores info
// about the media format. This is needed for writing the data back
// or for playing the sound.
media_format &format=*(media_format *)(const BYTE *)formatInfo;
// Create BEntry from file name
BEntry entry(filename, true);
if ((dwLastError=entry.InitCheck())!=B_OK)
{
STATUS("entry.InitCheck()");
return FALSE;
}
// Create entry_ref from BEntry
entry_ref ref;
if ((dwLastError=entry.GetRef(&ref))!=B_OK)
{
STATUS("entry.GetRef()");
return FALSE;
}
// Create BMediaFile for read access from the entry_ref
BMediaFile file(&ref);
if ((dwLastError=file.InitCheck())!=B_OK)
{
STATUS("file.InitCheck()");
return FALSE;
}
// Search for the first media track that can be decoded
BMediaTrack *ptrack = NULL;
for (int index=0; (index<file.CountTracks()) && (dwLastError==B_OK); index++)
{
ptrack = file.TrackAt(index);
if (ptrack)
{
dwLastError = ptrack->InitCheck();
}
else
{
dwLastError = B_ERROR; //todo: change error code
}
if (dwLastError==B_OK)
{
// Get media format; we're looking for a raw audio track.
format.type = B_MEDIA_RAW_AUDIO;
format.u.raw_audio = media_raw_audio_format::wildcard;
dwLastError = ptrack->DecodedFormat(&format);
if ((dwLastError==B_OK) && (format.type==B_MEDIA_RAW_AUDIO))
{
break; // found a decodable track
}
}
else
{
STATUS("TrackAt() failed, error");
}
// if we found a track and arrived at this point, the track we found
// was not decodable
if (ptrack)
{
dwLastError=file.ReleaseTrack(ptrack); // destroys ptrack
}
}
// if an error occurred during track scanning, leave now
if (dwLastError!=B_OK)
{
return FALSE;
}
// Get a reference to the raw output format
media_raw_audio_format &rawformat = format.u.raw_audio;
// Fill in our fields from the format
sampleSize = (rawformat.format & 0xF) * 8;
numChannels = rawformat.channel_count;
if (rawformat.frame_rate>0.0 && rawformat.frame_rate<=(float)0xFFFFFFFFU)
{
sampleRate = (unsigned)(rawformat.frame_rate);
}
else
{
// unknown or unrepresentable sample rate.
// It's not really documented what we should do in this case but
// it probably doesn't matter either...
sampleRate = 0;
}
// Get the number of frames for the track and determine how much
// memory we need to store the file's data
// The multiplication might overflow for huge files but we don't
// want to read them into memory anyway so I guess it's ok...
int64 numframes = ptrack->CountFrames();
int64 framesize = numChannels * (sampleSize/8);
int64 numbytes = numframes * framesize;
// Set the size of the object's data area
if (!SetSize(numbytes))
{
PRINT(("Can't set size of sound to %Ld", numbytes));
dwLastError = B_ERROR; //todo replace by better error code
return FALSE; // BMediaFile will destroy ptrack
}
// Read all frames into memory. NOTE: not thread safe!
BYTE* dest = GetPointer(); // destination pointer
int64 framecount = numframes; // number of frames left to read
int64 framesread; // number of actual frames done
while ((framecount!=0) && (dwLastError==B_OK))
{
framesread = framecount;
dwLastError = ptrack->ReadFrames(dest, &framesread);
dest += framesread * framesize;
framecount -= framesread;
}
// return true for success
return (dwLastError==B_OK); // BMediaFile will destroy ptrack
}
BOOL PSound::Save(const PFilePath & filename)
{
// format is a reference to the formatInfo member which stores info
// about the media format. This is needed for writing the data back
// or for playing the sound.
media_format &format=*(media_format *)(const BYTE *)formatInfo;
// Get the file type from the file name's extension; if none, use wav
PFilePathString filetype=filename.GetType(); // e.g. ".wav"
if (filetype=="")
{
filetype="wav";
}
else
{
filetype=filetype.Mid(1); // cut off the '.'
}
// Try to find the file format in BeOS's list of formats
media_file_format mfi;
int32 cookie=0;
while ((dwLastError=get_next_file_format(&cookie, &mfi))==B_OK)
{
if (!strcasecmp(mfi.file_extension, (const char *)filetype))
{
break;
}
}
if (dwLastError!=B_OK)
{
// didn't find file format
PRINT(("Couldn't find media_file_format for \"%s\"", (const char *)filetype));
return FALSE;
}
// Create BEntry from file name
BEntry entry(filename, true);
if ((dwLastError=entry.InitCheck())!=B_OK)
{
STATUS("entry.InitCheck()");
return FALSE;
}
// Create entry_ref from BEntry
entry_ref ref;
if ((dwLastError=entry.GetRef(&ref))!=B_OK)
{
STATUS("entry.GetRef()");
return FALSE;
}
// Create BMediaFile for write access from the entry_ref
BMediaFile file(&ref, &mfi, B_MEDIA_FILE_REPLACE_MODE);
if ((dwLastError=file.InitCheck())!=B_OK)
{
STATUS("file.InitCheck()");
return FALSE;
}
// Find an encoder. The input format is the format we have stored in
// our formatInfo member.
cookie=0;
media_format outformat;
media_codec_info mci,validmci,rawmci, *pmci;
bool found_encoder = false;
bool found_raw_encoder = false;
while (get_next_encoder(&cookie, &mfi, &format, &outformat, &mci)==B_OK)
{
found_encoder=true;
if (outformat.type==B_MEDIA_RAW_AUDIO)
{
rawmci=mci;
found_raw_encoder=true;
}
else
{
validmci=mci;
}
}
// Choose an encoder:
// If a raw-output encoder was found, use it.
// Else, use the last found encoded-output encoder, if any.
// This method of choosing will make sure that most file formats
// will get the most common encoding (PCM) whereas it's still possible
// to choose another output format like MP3, if so dictated by the
// file format.
// BeOS is smart enough not to return an encoder that produces raw audio
// for e.g. the MP3 file format, but it knows that there are many ways
// to encode e.g. a WAV file and we don't want to put anything
// unexpected into a WAV file, do we?
BMediaTrack *ptrack = NULL;
if (found_encoder)
{
if (found_raw_encoder)
{
PRINT(("Using raw encoder"));
pmci=&rawmci;
}
else
{
// don't use mci instead of validmci,
// it could be unreliable after the last call to get_next_encoder
PRINT(("Using non-raw encoder"));
pmci=&validmci;
}
// Create a BMediaTrack in the file using the selected encoder
ptrack = file.CreateTrack(&format, pmci);
if (ptrack)
{
dwLastError = ptrack->InitCheck();
}
else
{
dwLastError = B_ERROR; //todo: change error code
}
}
else
{
dwLastError=B_ERROR; //todo: change error code
}
if (dwLastError!=B_OK)
{
STATUS("Encoder not found or file.CreateTrack() error");
return FALSE; // BMediaFile will destroy ptrack
}
// We're only creating one track so commit the header now
if ((dwLastError = file.CommitHeader())!=B_OK)
{
STATUS("file.CommitHeader()");
return FALSE;
}
// Determine how many frames we have to write
// There is a small possibility of a divide by zero but this only
// happens if the object is not properly initialized.
PINDEX numbytes = GetSize();
int32 framesize = numChannels * (sampleSize/8);
int32 numframes = numbytes / framesize; // divide by zero possibility ignored.
if ((dwLastError=ptrack->WriteFrames((const BYTE *)*this, numframes))!=B_OK)
{
STATUS("ptrack->WriteFrames()");
return FALSE; // BMediaFile will destroy ptrack
}
return (file.CloseFile()==B_OK); // BMediaFile will destroy ptrack
}
BOOL PSound::Play()
{
PSoundChannelBeOS player(PSoundChannelBeOS::GetDefaultDevice(PSoundChannelBeOS::Player), PSoundChannelBeOS::Player, numChannels, sampleRate, sampleSize);
if (!player.IsOpen())
{
PRINT(("PSoundChannelBeOS constructor failed to open"));
return FALSE;
}
return player.PlaySound(*this, TRUE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -