2001-04-16 11:27:16 +08:00
|
|
|
/* fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp
|
2001-06-26 22:47:48 +08:00
|
|
|
|
2004-02-09 12:04:24 +08:00
|
|
|
Copyright 2001, 2002, 2003, 2004 Red Hat, Inc
|
2001-06-26 22:47:48 +08:00
|
|
|
|
2001-04-16 11:27:16 +08:00
|
|
|
Written by Andy Younger (andy@snoogie.demon.co.uk)
|
2004-03-23 19:05:56 +08:00
|
|
|
Extended by Gerd Spalink (Gerd.Spalink@t-online.de)
|
|
|
|
to support recording from the audio input
|
2001-06-26 22:47:48 +08:00
|
|
|
|
2001-04-16 11:27:16 +08:00
|
|
|
This file is part of Cygwin.
|
2001-06-26 22:47:48 +08:00
|
|
|
|
2001-04-16 11:27:16 +08:00
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
|
|
details. */
|
|
|
|
|
|
|
|
#include "winsup.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <windows.h>
|
|
|
|
#include <sys/soundcard.h>
|
|
|
|
#include <mmsystem.h>
|
2001-07-27 03:22:24 +08:00
|
|
|
#include "cygerrno.h"
|
|
|
|
#include "security.h"
|
* devices.cc: New file.
* devices.gperf: New file.
* devices.shilka: New file.
* cygwin-gperf: New file.
* cygwin-shilka: New file.
* fhandler_fifo.cc: New file.
* fhandler_nodevice.cc : New file. Reorganize headers so that path.h precedes
fhandler.h throughout. Remove device argument and unit arguments from fhandler
constructors throughout. Remove pc arguments to fhandler functions and use
internal pc element instead, throughout. Use dev element in pc throughout.
Use major/minor elements rather than units and device numbers previously in
fhandler class. Use correct methods for fhandler file names rather than
directly accessing file name variables, throughout.
* Makefile.in (DLL_OFILES): Add devices.o, fhandler_fifo.o
* dcrt0.cc (dll_crt0_1): Call device::init.
* devices.h: Renumber devices based on more Linux-like major/minor numbers.
Add more devices. Declare standard device storage.
(device): Declare struct.
* dir.cc (opendir): Use new 'build_fh_name' to construct a fhandler_* type.
* dtable.cc (dtable::get_debugger_info): Ditto.
(cygwin_attach_handle_to_fd): Ditto.
(dtable::release): Remove special FH_SOCKET case in favor of generic
"need_fixup_before" test.
(dtable::init_std_file_from_handle): Use either build_fh_dev or build_fh_name
to build standard fhandler.
(dtable::build_fh_name): Renamed from dtable::build_fhandler_from_name. Move
out of dtable class. Don't accept a path_conv argument. Just build it here
and pass it to:
(build_fh_pc): Renamed from dtable::build_fhandler. Move out of dtable class.
Use intrinsic device type in path_conv to create new fhandler.
(build_fh_dev): Renamed from dtable::build_fhandler. Move out of dtable class.
Simplify arguments to just take new 'device' type and a name. Just return
pointer to fhandler rather than trying to insert into dtable.
(dtable::dup_worker): Accommodate above build_fh name changes.
(dtable::find_fifo): New (currently broken) function.
(handle_to_fn): Use strechr for efficiency.
* dtable.h: Reflect above build_fh name changes and argument differences.
(fhandler_base *&operator []): Return self rather than copy of self.
* fhandler.cc (fhandler_base::operator =): Use pc element to set normalized
path.
(fhandler_base::set_name): Ditto.
(fhandler_base::raw_read): Use method to access name.
(fhandler_base::write): Correctly use get_output_handle rather than get_handle.
(handler_base::device_access_denied): New function.
(fhandler_base::open): Eliminate pc argument and use pc element of
fhandler_base throughout.
(fhandler_base::fstat): Detect if device is based in filesystem and use
fstat_fs to calculate stat, if so.
(fhandler_base::fhandler_base): Eliminate handling of file names and, instead,
just free appropriate component from pc.
(fhandler_base::opendir): Remove path_conv parameter.
* fhandler.h: Remove all device flags.
(fhandler_base::pc): New element.
(fhandler_base::set_name): Change argument to path_conv.
(fhandler_base::error): New function.
(fhandler_base::exists): New function.
(fhandler_base::pc_binmode): New function.
(fhandler_base::dev): New function.
(fhandler_base::open_fs): New function.
(fhandler_base::fstat_fs): New function.
(fhandler_base::fstat_by_name): New function.
(fhandler_base::fstat_by_handle): New function.
(fhandler_base::isfifo): New function.
(fhandler_base::is_slow): New function.
(fhandler_base::is_auto_device): New function.
(fhandler_base::is_fs_special): New function.
(fhandler_base::device_access_denied): New function.
(fhandler_base::operator DWORD&): New operator.
(fhandler_base::get_name): Return normalized path from pc.
(fhandler_base::get_win32_name): Return windows path from pc.
(fhandler_base::isdevice): Renamed from is_device.
(fhandler_base::get_native_name): Return device format.
(fhandler_fifo): New class.
(fhandler_nodevice): New class.
(select_stuff::device_specific): Remove array.
(select_stuff::device_specific_pipe): New class element.
(select_stuff::device_specific_socket): New class element.
(select_stuff::device_specific_serial): New class element.
(select_stuff::select_stuff): Initialize new elements.
* fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Move to base class
from fhandler_disk_file.
(fhandler_base::fstat_by_name): Ditto.
(fhandler_base::fstat_by_name): Ditto.
(fhandler_disk_file::open): Move most functionality into
fhandler_base::open_fs.
(fhandler_base::open_fs): New function.
(fhandler_disk_file::close): Move most functionality into
fhandler_base::close_fs.
(fhandler_base::close_fs): New function.
* fhandler_mem.cc (fhandler_dev_mem::open): Use device name in debugging
output.
* fhandler_socket.cc (fhandler_socket::set_connect_secret): Copy standard
urandom device into appropriate place.
(fhandler_socket::accept): Reflect change in fdsock return value.
* fhandler_tty.cc: See "throughouts" above.
* net.cc: Accommodate fdsock change throughout.
(fdsock): Return success or failure, accept fd argument and device argument.
* path.cc (symlink_info::major): New element.
(symlink_info::minor): New element.
(symlink_info::parse_device): Declare new function.
(fs_info::update): Accommodate changes in path_conv class.
(path_conv::fillin): Ditto.
(path_conv::return_and_clear_normalized_path): Eliminate.
(path_conv::set_normalized_path): New function.
(path_conv::path_conv): Set info in dev element. Use path_conv methods Check
for FH_FS rather than FH_BAD to indicate when to fill in filesystem stuff.
where appropriate rather than direct access. Use set_normalized_path to set
normalized path.
(windows_device_names): Eliminate.
(get_dev): Ditto.
(get_raw_device_number): Ditto.
(get_device_number): Ditto.
(win32_device_name): Call new device name parser to do most of the heavy
lifting.
(mount_info::conv_to_win32_path): Fill in dev field as appropriate.
(symlink_worker): Handle new device files.
(symlink_info::check): Ditto.
(symlink_info::parse_device): Define new function.
* path.h (executable_states): Move here from fhandler.h.
(fs_info): Rename variables to *_storage and create methods for accessing same.
(path_conv): Add dev element, remove devn and unit and adjust inline methods to
accommodate.
(set_normalized_path): Declare new function.
* pinfo.cc (_pinfo::commune_recv): Add broken support for handling fifos.
(_pinfo::commune_send): Ditto.
* pipe.cc (fhandler_pipe::close): check for existence of handle before closing
it.
(handler_pipe::create): Rename from make_pipe. Change arguments to accept
fhandler_pipe array. Accommodate fifos.
(pipe): Rework to deal with fhandler_pipe::create changes.
(_pipe): Ditto.
* select.cc: Use individual device_specific types throughout rather than
indexing with obsolete device number.
(set_bits): Use is_socket call rather than checking device number.
* shared_info.h (CURR_MOUNT_MAGIC): Update.
(conv_to_win32_path): Reflect addition of device argument.
* syscalls.cc (mknod_worker): New function.
(open): Use build_fh_name to build fhandler.
(chown_worker): Detect if this is an 'auto' device rather than an on-filesystem
device and handle appropriately.
(chmod_device): New function.
(chmod): Detect if this is an 'auto' device rather than an on-filesystem device
and handle appropriately. Use chmod_device to set mode of in-filesystem
devices.
(stat_worker): Eliminate path_conv argument. Call build_fh_name to construct
fhandler. Use fh->error() rather than pc->error to detect errors in fhandler
construction.
(access_worker): New function pulled from access. Accommodate in-filesystem
devices.
(access): Use access_worker.
(fpathconf): Detect if this is an 'auto' device rather than an on-filesystem
device and handle appropriately.
(mknod_worker): New function.
(mknod32): New function.
(chroot): Free normalized path -- assuming it was actually cmalloced.
* tty.cc (create_tty_master): Tweak for new device class.
(tty::common_init): Ditto.
* winsup.h (stat_worker): Remove.
(symlink_worker): Declare.
* exceptions.cc (set_process_mask): Just call sig_dispatch_pending and don't
worry about pending_signals since sig_dispatch_pending should always do the
right thing now.
(sig_handle): Reorganize SIGCONT handling to more closely conform to SUSv3.
* pinfo.h: Move __SIG enum to sigproc.h.
(PICOM_FIFO): New enum element.
(_pinfo): Remove 'thread2signal' stuff throughout class.
(_pinfo::commune_send): Make varargs.
(_pinfo::sigtodo): Eliminate.
(_pinfo::thread2signal): Ditto.
* signal.cc (kill_worker): Eliminate call to setthread2signal.
* sigproc.cc (local_sigtodo): Eliminate.
(getlocal_sigtodo): Ditto.
(sigelem): New class.
(pending_signals): New class.
(sigqueue): New variable, start of sigqueue linked list.
(sigcatch_nonmain): Eliminate.
(sigcatch_main): Eliminate.
(sigcatch_nosync): Eliminate.
(sigcomplete_nonmain): Eliminate.
(pending_signals): Eliminate.
(sig_clear): Call signal thread to clear pending signals, unless already in
signal thread.
(sigpending): Call signal thread to get pending signals.
(sig_dispatch_pending): Eliminate use of pending_signals and just check
sigqueue.
(sigproc_terminate): Eliminate all of the obsolete semaphore stuff. Close
signal pipe handle.
(sig_send): Eliminate all of the obsolete semaphore stuff and use pipe to send
signals.
(getevent): Eliminate.
(pending_signals::add): New function.
(pending_signals::del): New function.
(pending_signals::next): New function.
(wait_sig): Eliminate all of the obsolete semaphore stuff. Use pipe to
communicate and maintain a linked list of signals.
* sigproc.h: Move __SIG defines here. Add __SIGPENDING.
(sig_dispatch_pending): Remove "C" specifier.
(sig_handle): Accept a mask argument.
* thread.cc: Remove signal handling considerations throughout.
2003-09-25 08:37:18 +08:00
|
|
|
#include "path.h"
|
2001-07-27 03:22:24 +08:00
|
|
|
#include "fhandler.h"
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
/*------------------------------------------------------------------------
|
|
|
|
Simple encapsulation of the win32 audio device.
|
|
|
|
|
|
|
|
Implementation Notes
|
|
|
|
1. Audio buffers are created dynamically just before the first read or
|
|
|
|
write to /dev/dsp. The actual buffer size is determined at that time,
|
|
|
|
such that one buffer holds about 125ms of audio data.
|
|
|
|
At the time of this writing, 12 buffers are allocated,
|
|
|
|
so that up to 1.5 seconds can be buffered within Win32.
|
|
|
|
The buffer size can be queried with the ioctl SNDCTL_DSP_GETBLKSIZE,
|
|
|
|
but for this implementation only returns meaningful results if
|
|
|
|
sampling rate, number of channels and number of bits per sample
|
|
|
|
are not changed afterwards.
|
|
|
|
|
|
|
|
2. Every open call creates a new instance of the handler. To cope
|
|
|
|
with the fact that only a single wave device exists, the static
|
|
|
|
variable open_count tracks opens for one process. After a
|
|
|
|
successful open, every subsequent open from the same process
|
|
|
|
to the device fails with EBUSY.
|
|
|
|
If different processes open the audio device simultaneously,
|
|
|
|
the results are unpredictable - usually the first one wins.
|
|
|
|
|
|
|
|
3. The wave device is reserved within a process from the time that
|
|
|
|
the first read or write call has been successful until /dev/dsp
|
|
|
|
has been closed by that process. During this reservation period
|
|
|
|
child processes that use the same file descriptor cannot
|
|
|
|
do read, write or ioctls that change the device properties.
|
|
|
|
This means that a parent can open the device, do some ioctl,
|
|
|
|
spawn children, and any one of them can do the data read/write
|
|
|
|
*/
|
|
|
|
|
|
|
|
class fhandler_dev_dsp::Audio
|
|
|
|
{ // This class contains functionality common to Audio_in and Audio_out
|
|
|
|
public:
|
|
|
|
Audio ();
|
|
|
|
~Audio ();
|
|
|
|
|
|
|
|
class queue;
|
|
|
|
|
|
|
|
bool denyAccess ();
|
|
|
|
void fork_fixup (HANDLE parent);
|
|
|
|
inline DWORD getOwner () { return owner_; }
|
|
|
|
void setOwner () { owner_ = GetCurrentProcessId (); }
|
|
|
|
inline void clearOwner () { owner_ = 0L; }
|
|
|
|
void setformat (int format);
|
|
|
|
void convert_none (unsigned char *buffer, int size_bytes) { }
|
|
|
|
void convert_U8_S8 (unsigned char *buffer, int size_bytes);
|
|
|
|
void convert_S16LE_U16LE (unsigned char *buffer, int size_bytes);
|
|
|
|
void convert_S16LE_U16BE (unsigned char *buffer, int size_bytes);
|
|
|
|
void convert_S16LE_S16BE (unsigned char *buffer, int size_bytes);
|
|
|
|
void fillFormat (WAVEFORMATEX * format,
|
|
|
|
int rate, int bits, int channels);
|
|
|
|
unsigned blockSize (int rate, int bits, int channels);
|
|
|
|
|
|
|
|
void (fhandler_dev_dsp::Audio::*convert_)
|
|
|
|
(unsigned char *buffer, int size_bytes);
|
|
|
|
inline void lock () { EnterCriticalSection (&lock_); }
|
|
|
|
inline void unlock () { LeaveCriticalSection (&lock_); }
|
|
|
|
private:
|
|
|
|
DWORD owner_; /* Process ID when wave operation started, else 0 */
|
|
|
|
CRITICAL_SECTION lock_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class fhandler_dev_dsp::Audio::queue
|
|
|
|
{ // non-blocking fixed size queues for buffer management
|
|
|
|
public:
|
|
|
|
queue (int depth = 4);
|
|
|
|
~queue ();
|
|
|
|
|
|
|
|
bool send (WAVEHDR *); // queue an item, returns true if successful
|
|
|
|
bool recv (WAVEHDR **); // retrieve an item, returns true if successful
|
|
|
|
int query (); // return number of items queued
|
|
|
|
|
|
|
|
private:
|
|
|
|
int head_;
|
|
|
|
int tail_;
|
|
|
|
int depth_, depth1_;
|
|
|
|
WAVEHDR **storage_;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void CALLBACK waveOut_callback (HWAVEOUT hWave, UINT msg, DWORD instance,
|
|
|
|
DWORD param1, DWORD param2);
|
|
|
|
|
|
|
|
class fhandler_dev_dsp::Audio_out: public Audio
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Audio_out ();
|
|
|
|
~Audio_out ();
|
|
|
|
|
|
|
|
bool query (int rate, int bits, int channels);
|
|
|
|
bool start (int rate, int bits, int channels);
|
2004-04-13 17:38:32 +08:00
|
|
|
void stop (bool immediately = false);
|
2004-03-23 19:05:56 +08:00
|
|
|
bool write (const char *pSampleData, int nBytes);
|
|
|
|
void buf_info (audio_buf_info *p, int rate, int bits, int channels);
|
|
|
|
void callback_sampledone (WAVEHDR *pHdr);
|
|
|
|
bool parsewav (const char *&pData, int &nBytes,
|
|
|
|
int &rate, int &bits, int &channels);
|
|
|
|
|
|
|
|
private:
|
|
|
|
void init (unsigned blockSize);
|
|
|
|
void waitforallsent ();
|
|
|
|
void waitforspace ();
|
|
|
|
bool sendcurrent ();
|
|
|
|
int emptyblocks ();
|
|
|
|
|
|
|
|
enum { MAX_BLOCKS = 12 };
|
|
|
|
queue *Qapp2app_; // empty and unprepared blocks
|
|
|
|
HWAVEOUT dev_; // The wave device
|
|
|
|
int bufferIndex_; // offset into pHdr_->lpData
|
|
|
|
WAVEHDR *pHdr_; // data to be filled by write
|
|
|
|
WAVEHDR wavehdr_[MAX_BLOCKS];
|
|
|
|
char *bigwavebuffer_; // audio samples only
|
|
|
|
|
|
|
|
// Member variables below must be locked
|
|
|
|
queue *Qisr2app_; // empty blocks passed from wave callback
|
|
|
|
};
|
|
|
|
|
|
|
|
static void CALLBACK waveIn_callback (HWAVEIN hWave, UINT msg, DWORD instance,
|
|
|
|
DWORD param1, DWORD param2);
|
|
|
|
|
|
|
|
class fhandler_dev_dsp::Audio_in: public Audio
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
|
|
|
public:
|
2004-03-23 19:05:56 +08:00
|
|
|
Audio_in ();
|
|
|
|
~Audio_in ();
|
2001-05-24 13:20:17 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
bool query (int rate, int bits, int channels);
|
|
|
|
bool start (int rate, int bits, int channels);
|
|
|
|
void stop ();
|
|
|
|
bool read (char *pSampleData, int &nBytes);
|
2004-04-13 17:38:32 +08:00
|
|
|
void buf_info (audio_buf_info *p, int rate, int bits, int channels);
|
2004-03-23 19:05:56 +08:00
|
|
|
void callback_blockfull (WAVEHDR *pHdr);
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
private:
|
|
|
|
bool init (unsigned blockSize);
|
|
|
|
bool queueblock (WAVEHDR *pHdr);
|
|
|
|
void waitfordata (); // blocks until we have a good pHdr_
|
|
|
|
|
|
|
|
enum { MAX_BLOCKS = 12 }; // read ahead of 1.5 seconds
|
|
|
|
queue *Qapp2app_; // filled and unprepared blocks
|
|
|
|
HWAVEIN dev_;
|
|
|
|
int bufferIndex_; // offset into pHdr_->lpData
|
|
|
|
WAVEHDR *pHdr_; // successfully recorded data
|
|
|
|
WAVEHDR wavehdr_[MAX_BLOCKS];
|
|
|
|
char *bigwavebuffer_; // audio samples
|
|
|
|
|
|
|
|
// Member variables below must be locked
|
|
|
|
queue *Qisr2app_; // filled blocks passed from wave callback
|
2001-04-16 11:27:16 +08:00
|
|
|
};
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
/* --------------------------------------------------------------------
|
|
|
|
Implementation */
|
|
|
|
|
|
|
|
// Simple fixed length FIFO queue implementation for audio buffer management
|
|
|
|
fhandler_dev_dsp::Audio::queue::queue (int depth)
|
|
|
|
{
|
|
|
|
head_ = 0;
|
|
|
|
tail_ = 0;
|
|
|
|
depth_ = depth;
|
|
|
|
depth1_ = depth + 1;
|
|
|
|
// allow space for one extra object in the queue
|
|
|
|
// so we can distinguish full and empty status
|
|
|
|
storage_ = new WAVEHDR *[depth1_];
|
|
|
|
}
|
|
|
|
|
|
|
|
fhandler_dev_dsp::Audio::queue::~queue ()
|
|
|
|
{
|
|
|
|
delete[] storage_;
|
|
|
|
}
|
2001-05-24 13:20:17 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio::queue::send (WAVEHDR *x)
|
|
|
|
{
|
|
|
|
if (query () == depth_)
|
|
|
|
return false;
|
|
|
|
storage_[tail_] = x;
|
|
|
|
tail_++;
|
|
|
|
if (tail_ == depth1_)
|
|
|
|
tail_ = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio::queue::recv (WAVEHDR **x)
|
|
|
|
{
|
|
|
|
if (query () == 0)
|
|
|
|
return false;
|
|
|
|
*x = storage_[head_];
|
|
|
|
head_++;
|
|
|
|
if (head_ == depth1_)
|
|
|
|
head_ = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fhandler_dev_dsp::Audio::queue::query ()
|
|
|
|
{
|
|
|
|
int n = tail_ - head_;
|
|
|
|
if (n < 0)
|
|
|
|
n += depth1_;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Audio class implements functionality need for both read and write
|
|
|
|
fhandler_dev_dsp::Audio::Audio ()
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2001-05-21 01:31:06 +08:00
|
|
|
InitializeCriticalSection (&lock_);
|
2004-03-23 19:05:56 +08:00
|
|
|
convert_ = &fhandler_dev_dsp::Audio::convert_none;
|
|
|
|
owner_ = 0L;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio::~Audio ()
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2001-05-21 01:31:06 +08:00
|
|
|
DeleteCriticalSection (&lock_);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio::fork_fixup (HANDLE parent)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("parent=0x%08x", parent);
|
|
|
|
InitializeCriticalSection (&lock_);
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio::denyAccess ()
|
|
|
|
{
|
|
|
|
if (owner_ == 0L)
|
2001-04-16 11:27:16 +08:00
|
|
|
return false;
|
2004-03-23 19:05:56 +08:00
|
|
|
return (GetCurrentProcessId () != owner_);
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio::setformat (int format)
|
|
|
|
{
|
|
|
|
switch (format)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
case AFMT_S8:
|
|
|
|
convert_ = &fhandler_dev_dsp::Audio::convert_U8_S8;
|
|
|
|
debug_printf ("U8_S8");
|
|
|
|
break;
|
|
|
|
case AFMT_U16_LE:
|
|
|
|
convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_U16LE;
|
|
|
|
debug_printf ("S16LE_U16LE");
|
|
|
|
break;
|
|
|
|
case AFMT_U16_BE:
|
|
|
|
convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_U16BE;
|
|
|
|
debug_printf ("S16LE_U16BE");
|
|
|
|
break;
|
|
|
|
case AFMT_S16_BE:
|
|
|
|
convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_S16BE;
|
|
|
|
debug_printf ("S16LE_S16BE");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
convert_ = &fhandler_dev_dsp::Audio::convert_none;
|
|
|
|
debug_printf ("none");
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio::convert_U8_S8 (unsigned char *buffer,
|
|
|
|
int size_bytes)
|
|
|
|
{
|
|
|
|
while (size_bytes-- > 0)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
*buffer ^= (unsigned char)0x80;
|
|
|
|
buffer++;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
void
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio::convert_S16LE_U16BE (unsigned char *buffer,
|
|
|
|
int size_bytes)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
int size_samples = size_bytes / 2;
|
|
|
|
unsigned char hi, lo;
|
|
|
|
while (size_samples-- > 0)
|
2001-05-21 01:31:06 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
hi = buffer[0];
|
|
|
|
lo = buffer[1];
|
|
|
|
*buffer++ = lo;
|
|
|
|
*buffer++ = hi ^ (unsigned char)0x80;
|
|
|
|
}
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio::convert_S16LE_U16LE (unsigned char *buffer,
|
|
|
|
int size_bytes)
|
|
|
|
{
|
|
|
|
int size_samples = size_bytes / 2;
|
|
|
|
while (size_samples-- > 0)
|
|
|
|
{
|
|
|
|
buffer++;
|
|
|
|
*buffer ^= (unsigned char)0x80;
|
|
|
|
buffer++;
|
|
|
|
}
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio::convert_S16LE_S16BE (unsigned char *buffer,
|
|
|
|
int size_bytes)
|
|
|
|
{
|
|
|
|
int size_samples = size_bytes / 2;
|
|
|
|
unsigned char hi, lo;
|
|
|
|
while (size_samples-- > 0)
|
|
|
|
{
|
|
|
|
hi = buffer[0];
|
|
|
|
lo = buffer[1];
|
|
|
|
*buffer++ = lo;
|
|
|
|
*buffer++ = hi;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio::fillFormat (WAVEFORMATEX * format,
|
|
|
|
int rate, int bits, int channels)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
memset (format, 0, sizeof (*format));
|
|
|
|
format->wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
format->wBitsPerSample = bits;
|
|
|
|
format->nChannels = channels;
|
|
|
|
format->nSamplesPerSec = rate;
|
|
|
|
format->nAvgBytesPerSec = format->nSamplesPerSec * format->nChannels
|
|
|
|
* (bits / 8);
|
|
|
|
format->nBlockAlign = format->nChannels * (bits / 8);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
// calculate a good block size
|
|
|
|
unsigned
|
|
|
|
fhandler_dev_dsp::Audio::blockSize (int rate, int bits, int channels)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
unsigned blockSize;
|
|
|
|
blockSize = ((bits / 8) * channels * rate) / 8; // approx 125ms per block
|
|
|
|
// round up to multiple of 64
|
|
|
|
blockSize += 0x3f;
|
|
|
|
blockSize &= ~0x3f;
|
|
|
|
return blockSize;
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
//=======================================================================
|
|
|
|
fhandler_dev_dsp::Audio_out::Audio_out (): Audio ()
|
|
|
|
{
|
|
|
|
bigwavebuffer_ = NULL;
|
|
|
|
Qisr2app_ = new queue (MAX_BLOCKS);
|
|
|
|
Qapp2app_ = new queue (MAX_BLOCKS);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_out::~Audio_out ()
|
|
|
|
{
|
|
|
|
stop ();
|
|
|
|
delete Qapp2app_;
|
|
|
|
delete Qisr2app_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio_out::query (int rate, int bits, int channels)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
WAVEFORMATEX format;
|
|
|
|
MMRESULT rc;
|
|
|
|
|
|
|
|
fillFormat (&format, rate, bits, channels);
|
|
|
|
rc = waveOutOpen (NULL, WAVE_MAPPER, &format, 0L, 0L, WAVE_FORMAT_QUERY);
|
|
|
|
debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels,
|
|
|
|
(rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
|
|
|
|
return (rc == MMSYSERR_NOERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio_out::start (int rate, int bits, int channels)
|
|
|
|
{
|
|
|
|
WAVEFORMATEX format;
|
|
|
|
MMRESULT rc;
|
|
|
|
unsigned bSize = blockSize (rate, bits, channels);
|
|
|
|
bigwavebuffer_ = new char[MAX_BLOCKS * bSize];
|
|
|
|
if (bigwavebuffer_ == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int nDevices = waveOutGetNumDevs ();
|
|
|
|
debug_printf ("number devices=%d, blocksize=%d", nDevices, bSize);
|
|
|
|
if (nDevices <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
fillFormat (&format, rate, bits, channels);
|
|
|
|
rc = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveOut_callback,
|
|
|
|
(DWORD) this, CALLBACK_FUNCTION);
|
|
|
|
if (rc == MMSYSERR_NOERROR)
|
|
|
|
{
|
|
|
|
setOwner ();
|
|
|
|
init (bSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels,
|
|
|
|
(rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
|
|
|
|
|
|
|
|
return (rc == MMSYSERR_NOERROR);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
void
|
2004-04-13 17:38:32 +08:00
|
|
|
fhandler_dev_dsp::Audio_out::stop (bool immediately)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
MMRESULT rc;
|
|
|
|
WAVEHDR *pHdr;
|
|
|
|
bool gotblock;
|
|
|
|
|
|
|
|
debug_printf ("dev_=%08x pid=%d owner=%d", (int)dev_,
|
|
|
|
GetCurrentProcessId (), getOwner ());
|
|
|
|
if (getOwner () && !denyAccess ())
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-04-13 17:38:32 +08:00
|
|
|
if (!immediately)
|
|
|
|
{
|
|
|
|
sendcurrent (); // force out last block whatever size..
|
|
|
|
waitforallsent (); // block till finished..
|
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
|
|
|
|
rc = waveOutReset (dev_);
|
|
|
|
debug_printf ("waveOutReset rc=%d", rc);
|
|
|
|
do
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
lock ();
|
|
|
|
gotblock = Qisr2app_->recv (&pHdr);
|
|
|
|
unlock ();
|
|
|
|
if (gotblock)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
|
|
|
|
debug_printf ("waveOutUnprepareHeader Block 0x%08x %s", pHdr,
|
|
|
|
(rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
}
|
|
|
|
while (gotblock);
|
|
|
|
while (Qapp2app_->recv (&pHdr))
|
|
|
|
/* flush queue */;
|
|
|
|
|
|
|
|
rc = waveOutClose (dev_);
|
|
|
|
debug_printf ("waveOutClose rc=%d", rc);
|
|
|
|
|
|
|
|
clearOwner ();
|
|
|
|
|
|
|
|
if (bigwavebuffer_)
|
|
|
|
{
|
|
|
|
delete[] bigwavebuffer_;
|
|
|
|
bigwavebuffer_ = NULL;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio_out::init (unsigned blockSize)
|
|
|
|
{
|
|
|
|
int i;
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
// internally queue all of our buffer for later use by write
|
|
|
|
for (i = 0; i < MAX_BLOCKS; i++)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize];
|
|
|
|
(int)wavehdr_[i].dwUser = blockSize;
|
|
|
|
if (!Qapp2app_->send (&wavehdr_[i]))
|
|
|
|
{
|
|
|
|
debug_printf ("Internal Error i=%d", i);
|
|
|
|
break; // should not happen
|
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
pHdr_ = NULL;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
bool
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_out::write (const char *pSampleData, int nBytes)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
while (nBytes != 0)
|
|
|
|
{ // Block if all blocks used until at least one is free
|
|
|
|
waitforspace ();
|
|
|
|
|
|
|
|
int sizeleft = (int)pHdr_->dwUser - bufferIndex_;
|
|
|
|
if (nBytes < sizeleft)
|
|
|
|
{ // all data fits into the current block, with some space left
|
|
|
|
memcpy (&pHdr_->lpData[bufferIndex_], pSampleData, nBytes);
|
|
|
|
bufferIndex_ += nBytes;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // data will fill up the current block
|
|
|
|
memcpy (&pHdr_->lpData[bufferIndex_], pSampleData, sizeleft);
|
|
|
|
bufferIndex_ += sizeleft;
|
|
|
|
sendcurrent ();
|
|
|
|
pSampleData += sizeleft;
|
|
|
|
nBytes -= sizeleft;
|
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
return true;
|
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2004-04-13 17:38:32 +08:00
|
|
|
// return number of (completely) empty blocks back.
|
2004-03-23 19:05:56 +08:00
|
|
|
int
|
|
|
|
fhandler_dev_dsp::Audio_out::emptyblocks ()
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
lock ();
|
|
|
|
n = Qisr2app_->query ();
|
|
|
|
unlock ();
|
|
|
|
n += Qapp2app_->query ();
|
|
|
|
return n;
|
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio_out::buf_info (audio_buf_info *p,
|
|
|
|
int rate, int bits, int channels)
|
|
|
|
{
|
|
|
|
p->fragstotal = MAX_BLOCKS;
|
|
|
|
p->fragsize = blockSize (rate, bits, channels);
|
|
|
|
if (getOwner ())
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
p->fragments = emptyblocks ();
|
|
|
|
if (pHdr_ != NULL)
|
2004-04-13 17:38:32 +08:00
|
|
|
p->bytes = (int)pHdr_->dwUser - bufferIndex_
|
|
|
|
+ p->fragsize * p->fragments;
|
2004-03-23 19:05:56 +08:00
|
|
|
else
|
|
|
|
p->bytes = p->fragsize * p->fragments;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
else
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
p->fragments = MAX_BLOCKS;
|
|
|
|
p->bytes = p->fragsize * p->fragments;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
/* This is called on an interupt so use locking.. Note Qisr2app_
|
|
|
|
is used so we should wrap all references to it in locks. */
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio_out::callback_sampledone (WAVEHDR *pHdr)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
lock ();
|
|
|
|
Qisr2app_->send (pHdr);
|
|
|
|
unlock ();
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
void
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_out::waitforspace ()
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
WAVEHDR *pHdr;
|
|
|
|
bool gotblock;
|
|
|
|
MMRESULT rc = WAVERR_STILLPLAYING;
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
if (pHdr_ != NULL)
|
|
|
|
return;
|
|
|
|
while (Qapp2app_->recv (&pHdr) == false)
|
|
|
|
{
|
|
|
|
lock ();
|
|
|
|
gotblock = Qisr2app_->recv (&pHdr);
|
|
|
|
unlock ();
|
|
|
|
if (gotblock)
|
|
|
|
{
|
|
|
|
if ((pHdr->dwFlags & WHDR_DONE)
|
|
|
|
&& ((rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)))
|
|
|
|
== MMSYSERR_NOERROR))
|
|
|
|
{
|
|
|
|
Qapp2app_->send (pHdr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug_printf ("error UnprepareHeader 0x%08x, rc=%d, 100ms",
|
|
|
|
pHdr, rc);
|
|
|
|
lock ();
|
|
|
|
Qisr2app_->send (pHdr);
|
|
|
|
unlock ();
|
|
|
|
Sleep (100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug_printf ("100ms");
|
|
|
|
Sleep (100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pHdr_ = pHdr;
|
|
|
|
bufferIndex_ = 0;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_out::waitforallsent ()
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
while (emptyblocks () != MAX_BLOCKS)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("100ms Qisr=%d Qapp=%d",
|
|
|
|
Qisr2app_->query (), Qapp2app_->query ());
|
|
|
|
Sleep (100);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
// send the block described by pHdr_ and bufferIndex_ to wave device
|
2001-04-16 11:27:16 +08:00
|
|
|
bool
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_out::sendcurrent ()
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
WAVEHDR *pHdr = pHdr_;
|
|
|
|
if (pHdr_ == NULL)
|
2001-05-21 01:31:06 +08:00
|
|
|
return false;
|
2004-03-23 19:05:56 +08:00
|
|
|
pHdr_ = NULL;
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
// Sample buffer conversion
|
|
|
|
(this->*convert_) ((unsigned char *)pHdr->lpData, bufferIndex_);
|
2001-04-25 15:26:54 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
// Send internal buffer out to the soundcard
|
|
|
|
pHdr->dwBufferLength = bufferIndex_;
|
|
|
|
pHdr->dwFlags = 0;
|
|
|
|
if (waveOutPrepareHeader (dev_, pHdr, sizeof (WAVEHDR)) == MMSYSERR_NOERROR)
|
2001-04-25 15:26:54 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
if (waveOutWrite (dev_, pHdr, sizeof (WAVEHDR)) == MMSYSERR_NOERROR)
|
2001-05-21 01:31:06 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("waveOutWrite bytes=%d", bufferIndex_);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug_printf ("waveOutWrite failed");
|
|
|
|
lock ();
|
|
|
|
Qisr2app_->send (pHdr);
|
|
|
|
unlock ();
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("waveOutPrepareHeader failed");
|
|
|
|
Qapp2app_->send (pHdr);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
// Call back routine
|
2001-05-21 01:31:06 +08:00
|
|
|
static void CALLBACK
|
2004-03-23 19:05:56 +08:00
|
|
|
waveOut_callback (HWAVEOUT hWave, UINT msg, DWORD instance, DWORD param1,
|
|
|
|
DWORD param2)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
|
|
|
if (msg == WOM_DONE)
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_out *ptr =
|
|
|
|
(fhandler_dev_dsp::Audio_out *) instance;
|
|
|
|
ptr->callback_sampledone ((WAVEHDR *) param1);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2001-04-16 11:27:16 +08:00
|
|
|
//------------------------------------------------------------------------
|
|
|
|
// wav file detection..
|
|
|
|
#pragma pack(1)
|
|
|
|
struct wavchunk
|
2001-05-21 01:31:06 +08:00
|
|
|
{
|
2001-04-16 11:27:16 +08:00
|
|
|
char id[4];
|
|
|
|
unsigned int len;
|
|
|
|
};
|
|
|
|
struct wavformat
|
|
|
|
{
|
|
|
|
unsigned short wFormatTag;
|
|
|
|
unsigned short wChannels;
|
|
|
|
unsigned int dwSamplesPerSec;
|
|
|
|
unsigned int dwAvgBytesPerSec;
|
|
|
|
unsigned short wBlockAlign;
|
|
|
|
unsigned short wBitsPerSample;
|
|
|
|
};
|
|
|
|
#pragma pack()
|
|
|
|
|
|
|
|
bool
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_out::parsewav (const char * &pData, int &nBytes,
|
|
|
|
int &rate, int &bits, int &channels)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
const char *end = pData + nBytes;
|
2004-03-23 19:05:56 +08:00
|
|
|
const char *pDat;
|
|
|
|
int skip = 0;
|
2004-04-13 17:38:32 +08:00
|
|
|
// Check alignment first: A lot of the code below depends on it
|
|
|
|
if (((int)pData & 0x3) != 0)
|
|
|
|
return false;
|
2004-03-23 19:05:56 +08:00
|
|
|
if (!(pData[0] == 'R' && pData[1] == 'I'
|
|
|
|
&& pData[2] == 'F' && pData[3] == 'F'))
|
2001-04-16 11:27:16 +08:00
|
|
|
return false;
|
2004-03-23 19:05:56 +08:00
|
|
|
if (!(pData[8] == 'W' && pData[9] == 'A'
|
|
|
|
&& pData[10] == 'V' && pData[11] == 'E'))
|
2001-04-16 11:27:16 +08:00
|
|
|
return false;
|
2001-05-21 01:31:06 +08:00
|
|
|
|
|
|
|
len = *(int *) &pData[4];
|
2004-03-23 19:05:56 +08:00
|
|
|
len -= 12;
|
|
|
|
pDat = pData + 12;
|
|
|
|
skip = 12;
|
|
|
|
while ((len > 0) && (pDat + sizeof (wavchunk) < end))
|
|
|
|
{ /* We recognize two kinds of wavchunk:
|
|
|
|
"fmt " for the PCM parameters (only PCM supported here)
|
|
|
|
"data" for the start of PCM data */
|
|
|
|
wavchunk * pChunk = (wavchunk *) pDat;
|
2001-05-21 01:31:06 +08:00
|
|
|
int blklen = pChunk-> len;
|
2004-03-23 19:05:56 +08:00
|
|
|
if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm'
|
|
|
|
&& pChunk->id[2] == 't' && pChunk->id[3] == ' ')
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2001-05-21 01:31:06 +08:00
|
|
|
wavformat *format = (wavformat *) (pChunk + 1);
|
2004-03-23 19:05:56 +08:00
|
|
|
if ((char *) (format + 1) >= end)
|
2001-04-16 11:27:16 +08:00
|
|
|
return false;
|
2004-03-23 19:05:56 +08:00
|
|
|
// We have found the parameter chunk
|
|
|
|
if (format->wFormatTag == 0x0001)
|
|
|
|
{ // Micr*s*ft PCM; check if parameters work with our device
|
|
|
|
if (query (format->dwSamplesPerSec, format->wBitsPerSample,
|
|
|
|
format->wChannels))
|
|
|
|
{ // return the parameters we found
|
|
|
|
rate = format->dwSamplesPerSec;
|
|
|
|
bits = format->wBitsPerSample;
|
|
|
|
channels = format->wChannels;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pChunk->id[0] == 'd' && pChunk->id[1] == 'a'
|
|
|
|
&& pChunk->id[2] == 't' && pChunk->id[3] == 'a')
|
|
|
|
{ // throw away all the header & not output it to the soundcard.
|
|
|
|
skip += sizeof (wavchunk);
|
|
|
|
debug_printf ("Discard %d bytes wave header", skip);
|
|
|
|
pData += skip;
|
|
|
|
nBytes -= skip;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pDat += blklen + sizeof (wavchunk);
|
|
|
|
skip += blklen + sizeof (wavchunk);
|
|
|
|
len -= blklen + sizeof (wavchunk);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ========================================================================
|
|
|
|
Buffering concept for Audio_in:
|
|
|
|
On the first read, we queue all blocks of our bigwavebuffer
|
|
|
|
for reception and start the wave-in device.
|
|
|
|
We manage queues of pointers to WAVEHDR
|
|
|
|
When a block has been filled, the callback puts the corresponding
|
|
|
|
WAVEHDR pointer into a queue. We need a second queue to distinguish
|
|
|
|
blocks with data from blocks that have been unprepared and are ready
|
|
|
|
to be used by read().
|
|
|
|
The function read() blocks (polled, sigh) until at least one good buffer
|
|
|
|
has arrived, then the data is copied into the buffer provided to read().
|
|
|
|
After a buffer has been fully used by read(), it is queued again
|
|
|
|
to the wave-in device immediately.
|
|
|
|
The function read() iterates until all data requested has been
|
|
|
|
received, there is no way to interrupt it */
|
|
|
|
|
|
|
|
fhandler_dev_dsp::Audio_in::Audio_in () : Audio ()
|
|
|
|
{
|
|
|
|
bigwavebuffer_ = NULL;
|
|
|
|
Qisr2app_ = new queue (MAX_BLOCKS);
|
|
|
|
Qapp2app_ = new queue (MAX_BLOCKS);
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
fhandler_dev_dsp::Audio_in::~Audio_in ()
|
|
|
|
{
|
|
|
|
stop ();
|
|
|
|
delete Qapp2app_;
|
|
|
|
delete Qisr2app_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio_in::query (int rate, int bits, int channels)
|
|
|
|
{
|
|
|
|
WAVEFORMATEX format;
|
|
|
|
MMRESULT rc;
|
|
|
|
|
|
|
|
fillFormat (&format, rate, bits, channels);
|
|
|
|
rc = waveInOpen (NULL, WAVE_MAPPER, &format, 0L, 0L, WAVE_FORMAT_QUERY);
|
|
|
|
debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels,
|
|
|
|
(rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
|
|
|
|
return (rc == MMSYSERR_NOERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio_in::start (int rate, int bits, int channels)
|
|
|
|
{
|
|
|
|
WAVEFORMATEX format;
|
|
|
|
MMRESULT rc;
|
|
|
|
unsigned bSize = blockSize (rate, bits, channels);
|
|
|
|
bigwavebuffer_ = new char[MAX_BLOCKS * bSize];
|
|
|
|
if (bigwavebuffer_ == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int nDevices = waveInGetNumDevs ();
|
|
|
|
debug_printf ("number devices=%d, blocksize=%d", nDevices, bSize);
|
|
|
|
if (nDevices <= 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
fillFormat (&format, rate, bits, channels);
|
|
|
|
rc = waveInOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveIn_callback,
|
|
|
|
(DWORD) this, CALLBACK_FUNCTION);
|
|
|
|
if (rc == MMSYSERR_NOERROR)
|
|
|
|
{
|
|
|
|
setOwner ();
|
|
|
|
if (!init (bSize))
|
|
|
|
{
|
|
|
|
stop ();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels,
|
|
|
|
(rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
|
|
|
|
|
|
|
|
return (rc == MMSYSERR_NOERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio_in::stop ()
|
|
|
|
{
|
|
|
|
MMRESULT rc;
|
|
|
|
WAVEHDR *pHdr;
|
|
|
|
bool gotblock;
|
|
|
|
|
|
|
|
debug_printf ("dev_=%08x pid=%d owner=%d", (int)dev_,
|
|
|
|
GetCurrentProcessId (), getOwner ());
|
|
|
|
if (getOwner () && !denyAccess ())
|
|
|
|
{
|
|
|
|
rc = waveInReset (dev_);
|
|
|
|
/* Note that waveInReset calls our callback for all incomplete buffers.
|
|
|
|
Since all the win32 wave functions appear to use a common lock,
|
|
|
|
we must not call into the wave API from the callback.
|
|
|
|
Otherwise we end up in a deadlock. */
|
|
|
|
debug_printf ("waveInReset rc=%d", rc);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
lock ();
|
|
|
|
gotblock = Qisr2app_->recv (&pHdr);
|
|
|
|
unlock ();
|
|
|
|
if (gotblock)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
|
|
|
|
debug_printf ("waveInUnprepareHeader Block 0x%08x %s", pHdr,
|
|
|
|
(rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
}
|
|
|
|
while (gotblock);
|
|
|
|
while (Qapp2app_->recv (&pHdr))
|
|
|
|
/* flush queue */;
|
|
|
|
|
|
|
|
rc = waveInClose (dev_);
|
|
|
|
debug_printf ("waveInClose rc=%d", rc);
|
|
|
|
|
|
|
|
clearOwner ();
|
|
|
|
|
|
|
|
if (bigwavebuffer_)
|
|
|
|
{
|
|
|
|
delete[] bigwavebuffer_;
|
|
|
|
bigwavebuffer_ = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio_in::queueblock (WAVEHDR *pHdr)
|
|
|
|
{
|
|
|
|
MMRESULT rc;
|
|
|
|
pHdr->dwFlags = 0;
|
|
|
|
rc = waveInPrepareHeader (dev_, pHdr, sizeof (WAVEHDR));
|
|
|
|
if (rc == MMSYSERR_NOERROR)
|
|
|
|
rc = waveInAddBuffer (dev_, pHdr, sizeof (WAVEHDR));
|
|
|
|
debug_printf ("waveInAddBuffer Block 0x%08x %s", pHdr,
|
|
|
|
(rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
|
|
|
|
return (rc == MMSYSERR_NOERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio_in::init (unsigned blockSize)
|
|
|
|
{
|
|
|
|
MMRESULT rc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// try to queue all of our buffer for reception
|
|
|
|
for (i = 0; i < MAX_BLOCKS; i++)
|
|
|
|
{
|
|
|
|
wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize];
|
|
|
|
wavehdr_[i].dwBufferLength = blockSize;
|
|
|
|
if (!queueblock (&wavehdr_[i]))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pHdr_ = NULL;
|
|
|
|
rc = waveInStart (dev_);
|
|
|
|
debug_printf ("waveInStart=%d %s queued=%d",
|
|
|
|
rc, (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK", i);
|
|
|
|
return (rc == MMSYSERR_NOERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
fhandler_dev_dsp::Audio_in::read (char *pSampleData, int &nBytes)
|
|
|
|
{
|
|
|
|
int bytes_to_read = nBytes;
|
|
|
|
nBytes = 0;
|
|
|
|
debug_printf ("pSampleData=%08x nBytes=%d", pSampleData, bytes_to_read);
|
|
|
|
while (bytes_to_read != 0)
|
|
|
|
{ // Block till next sound has been read
|
|
|
|
waitfordata ();
|
|
|
|
|
|
|
|
// Handle gathering our blocks into smaller or larger buffer
|
|
|
|
int sizeleft = pHdr_->dwBytesRecorded - bufferIndex_;
|
|
|
|
if (bytes_to_read < sizeleft)
|
|
|
|
{ // The current buffer holds more data than requested
|
|
|
|
memcpy (pSampleData, &pHdr_->lpData[bufferIndex_], bytes_to_read);
|
|
|
|
(this->*convert_) ((unsigned char *)pSampleData, bytes_to_read);
|
|
|
|
nBytes += bytes_to_read;
|
|
|
|
bufferIndex_ += bytes_to_read;
|
|
|
|
debug_printf ("got %d", bytes_to_read);
|
|
|
|
break; // done; use remaining data in next call to read
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // not enough or exact amount in the current buffer
|
|
|
|
if (sizeleft)
|
|
|
|
{ // use up what we have
|
|
|
|
memcpy (pSampleData, &pHdr_->lpData[bufferIndex_], sizeleft);
|
|
|
|
(this->*convert_) ((unsigned char *)pSampleData, sizeleft);
|
|
|
|
nBytes += sizeleft;
|
|
|
|
bytes_to_read -= sizeleft;
|
|
|
|
pSampleData += sizeleft;
|
|
|
|
debug_printf ("got %d", sizeleft);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
queueblock (pHdr_); // re-queue this block to ISR
|
|
|
|
pHdr_ = NULL; // need to wait for a new block
|
|
|
|
// if more samples are needed, we need a new block now
|
|
|
|
}
|
|
|
|
}
|
|
|
|
debug_printf ("end nBytes=%d", nBytes);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio_in::waitfordata ()
|
|
|
|
{
|
|
|
|
WAVEHDR *pHdr;
|
|
|
|
bool gotblock;
|
|
|
|
MMRESULT rc;
|
|
|
|
|
|
|
|
if (pHdr_ != NULL)
|
|
|
|
return;
|
|
|
|
while (Qapp2app_->recv (&pHdr) == false)
|
|
|
|
{
|
|
|
|
lock ();
|
|
|
|
gotblock = Qisr2app_->recv (&pHdr);
|
|
|
|
unlock ();
|
|
|
|
if (gotblock)
|
|
|
|
{
|
|
|
|
rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
|
|
|
|
if (rc == MMSYSERR_NOERROR)
|
|
|
|
Qapp2app_->send (pHdr);
|
|
|
|
else
|
|
|
|
debug_printf ("error UnprepareHeader 0x%08x", pHdr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug_printf ("100ms");
|
|
|
|
Sleep (100);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
}
|
|
|
|
pHdr_ = pHdr;
|
|
|
|
bufferIndex_ = 0;
|
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2004-04-13 17:38:32 +08:00
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio_in::buf_info (audio_buf_info *p,
|
|
|
|
int rate, int bits, int channels)
|
|
|
|
{
|
|
|
|
p->fragstotal = MAX_BLOCKS;
|
|
|
|
p->fragsize = blockSize (rate, bits, channels);
|
|
|
|
if (getOwner ())
|
|
|
|
{
|
|
|
|
lock ();
|
|
|
|
p->fragments = Qisr2app_->query ();
|
|
|
|
unlock ();
|
|
|
|
p->fragments += Qapp2app_->query ();
|
|
|
|
if (pHdr_ != NULL)
|
|
|
|
p->bytes = pHdr_->dwBytesRecorded - bufferIndex_
|
|
|
|
+ p->fragsize * p->fragments;
|
|
|
|
else
|
|
|
|
p->bytes = p->fragsize * p->fragments;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p->fragments = 0;
|
|
|
|
p->bytes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
// This is called on an interrupt so use locking..
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::Audio_in::callback_blockfull (WAVEHDR *pHdr)
|
|
|
|
{
|
|
|
|
lock ();
|
|
|
|
Qisr2app_->send (pHdr);
|
|
|
|
unlock ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CALLBACK
|
|
|
|
waveIn_callback (HWAVEIN hWave, UINT msg, DWORD instance, DWORD param1,
|
|
|
|
DWORD param2)
|
|
|
|
{
|
|
|
|
if (msg == WIM_DATA)
|
|
|
|
{
|
|
|
|
fhandler_dev_dsp::Audio_in *ptr =
|
|
|
|
(fhandler_dev_dsp::Audio_in *) instance;
|
|
|
|
ptr->callback_blockfull ((WAVEHDR *) param1);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------
|
|
|
|
/dev/dsp handler
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
instances of the handler statics */
|
|
|
|
int fhandler_dev_dsp::open_count = 0;
|
|
|
|
|
2001-10-14 01:23:35 +08:00
|
|
|
fhandler_dev_dsp::fhandler_dev_dsp ():
|
* devices.cc: New file.
* devices.gperf: New file.
* devices.shilka: New file.
* cygwin-gperf: New file.
* cygwin-shilka: New file.
* fhandler_fifo.cc: New file.
* fhandler_nodevice.cc : New file. Reorganize headers so that path.h precedes
fhandler.h throughout. Remove device argument and unit arguments from fhandler
constructors throughout. Remove pc arguments to fhandler functions and use
internal pc element instead, throughout. Use dev element in pc throughout.
Use major/minor elements rather than units and device numbers previously in
fhandler class. Use correct methods for fhandler file names rather than
directly accessing file name variables, throughout.
* Makefile.in (DLL_OFILES): Add devices.o, fhandler_fifo.o
* dcrt0.cc (dll_crt0_1): Call device::init.
* devices.h: Renumber devices based on more Linux-like major/minor numbers.
Add more devices. Declare standard device storage.
(device): Declare struct.
* dir.cc (opendir): Use new 'build_fh_name' to construct a fhandler_* type.
* dtable.cc (dtable::get_debugger_info): Ditto.
(cygwin_attach_handle_to_fd): Ditto.
(dtable::release): Remove special FH_SOCKET case in favor of generic
"need_fixup_before" test.
(dtable::init_std_file_from_handle): Use either build_fh_dev or build_fh_name
to build standard fhandler.
(dtable::build_fh_name): Renamed from dtable::build_fhandler_from_name. Move
out of dtable class. Don't accept a path_conv argument. Just build it here
and pass it to:
(build_fh_pc): Renamed from dtable::build_fhandler. Move out of dtable class.
Use intrinsic device type in path_conv to create new fhandler.
(build_fh_dev): Renamed from dtable::build_fhandler. Move out of dtable class.
Simplify arguments to just take new 'device' type and a name. Just return
pointer to fhandler rather than trying to insert into dtable.
(dtable::dup_worker): Accommodate above build_fh name changes.
(dtable::find_fifo): New (currently broken) function.
(handle_to_fn): Use strechr for efficiency.
* dtable.h: Reflect above build_fh name changes and argument differences.
(fhandler_base *&operator []): Return self rather than copy of self.
* fhandler.cc (fhandler_base::operator =): Use pc element to set normalized
path.
(fhandler_base::set_name): Ditto.
(fhandler_base::raw_read): Use method to access name.
(fhandler_base::write): Correctly use get_output_handle rather than get_handle.
(handler_base::device_access_denied): New function.
(fhandler_base::open): Eliminate pc argument and use pc element of
fhandler_base throughout.
(fhandler_base::fstat): Detect if device is based in filesystem and use
fstat_fs to calculate stat, if so.
(fhandler_base::fhandler_base): Eliminate handling of file names and, instead,
just free appropriate component from pc.
(fhandler_base::opendir): Remove path_conv parameter.
* fhandler.h: Remove all device flags.
(fhandler_base::pc): New element.
(fhandler_base::set_name): Change argument to path_conv.
(fhandler_base::error): New function.
(fhandler_base::exists): New function.
(fhandler_base::pc_binmode): New function.
(fhandler_base::dev): New function.
(fhandler_base::open_fs): New function.
(fhandler_base::fstat_fs): New function.
(fhandler_base::fstat_by_name): New function.
(fhandler_base::fstat_by_handle): New function.
(fhandler_base::isfifo): New function.
(fhandler_base::is_slow): New function.
(fhandler_base::is_auto_device): New function.
(fhandler_base::is_fs_special): New function.
(fhandler_base::device_access_denied): New function.
(fhandler_base::operator DWORD&): New operator.
(fhandler_base::get_name): Return normalized path from pc.
(fhandler_base::get_win32_name): Return windows path from pc.
(fhandler_base::isdevice): Renamed from is_device.
(fhandler_base::get_native_name): Return device format.
(fhandler_fifo): New class.
(fhandler_nodevice): New class.
(select_stuff::device_specific): Remove array.
(select_stuff::device_specific_pipe): New class element.
(select_stuff::device_specific_socket): New class element.
(select_stuff::device_specific_serial): New class element.
(select_stuff::select_stuff): Initialize new elements.
* fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Move to base class
from fhandler_disk_file.
(fhandler_base::fstat_by_name): Ditto.
(fhandler_base::fstat_by_name): Ditto.
(fhandler_disk_file::open): Move most functionality into
fhandler_base::open_fs.
(fhandler_base::open_fs): New function.
(fhandler_disk_file::close): Move most functionality into
fhandler_base::close_fs.
(fhandler_base::close_fs): New function.
* fhandler_mem.cc (fhandler_dev_mem::open): Use device name in debugging
output.
* fhandler_socket.cc (fhandler_socket::set_connect_secret): Copy standard
urandom device into appropriate place.
(fhandler_socket::accept): Reflect change in fdsock return value.
* fhandler_tty.cc: See "throughouts" above.
* net.cc: Accommodate fdsock change throughout.
(fdsock): Return success or failure, accept fd argument and device argument.
* path.cc (symlink_info::major): New element.
(symlink_info::minor): New element.
(symlink_info::parse_device): Declare new function.
(fs_info::update): Accommodate changes in path_conv class.
(path_conv::fillin): Ditto.
(path_conv::return_and_clear_normalized_path): Eliminate.
(path_conv::set_normalized_path): New function.
(path_conv::path_conv): Set info in dev element. Use path_conv methods Check
for FH_FS rather than FH_BAD to indicate when to fill in filesystem stuff.
where appropriate rather than direct access. Use set_normalized_path to set
normalized path.
(windows_device_names): Eliminate.
(get_dev): Ditto.
(get_raw_device_number): Ditto.
(get_device_number): Ditto.
(win32_device_name): Call new device name parser to do most of the heavy
lifting.
(mount_info::conv_to_win32_path): Fill in dev field as appropriate.
(symlink_worker): Handle new device files.
(symlink_info::check): Ditto.
(symlink_info::parse_device): Define new function.
* path.h (executable_states): Move here from fhandler.h.
(fs_info): Rename variables to *_storage and create methods for accessing same.
(path_conv): Add dev element, remove devn and unit and adjust inline methods to
accommodate.
(set_normalized_path): Declare new function.
* pinfo.cc (_pinfo::commune_recv): Add broken support for handling fifos.
(_pinfo::commune_send): Ditto.
* pipe.cc (fhandler_pipe::close): check for existence of handle before closing
it.
(handler_pipe::create): Rename from make_pipe. Change arguments to accept
fhandler_pipe array. Accommodate fifos.
(pipe): Rework to deal with fhandler_pipe::create changes.
(_pipe): Ditto.
* select.cc: Use individual device_specific types throughout rather than
indexing with obsolete device number.
(set_bits): Use is_socket call rather than checking device number.
* shared_info.h (CURR_MOUNT_MAGIC): Update.
(conv_to_win32_path): Reflect addition of device argument.
* syscalls.cc (mknod_worker): New function.
(open): Use build_fh_name to build fhandler.
(chown_worker): Detect if this is an 'auto' device rather than an on-filesystem
device and handle appropriately.
(chmod_device): New function.
(chmod): Detect if this is an 'auto' device rather than an on-filesystem device
and handle appropriately. Use chmod_device to set mode of in-filesystem
devices.
(stat_worker): Eliminate path_conv argument. Call build_fh_name to construct
fhandler. Use fh->error() rather than pc->error to detect errors in fhandler
construction.
(access_worker): New function pulled from access. Accommodate in-filesystem
devices.
(access): Use access_worker.
(fpathconf): Detect if this is an 'auto' device rather than an on-filesystem
device and handle appropriately.
(mknod_worker): New function.
(mknod32): New function.
(chroot): Free normalized path -- assuming it was actually cmalloced.
* tty.cc (create_tty_master): Tweak for new device class.
(tty::common_init): Ditto.
* winsup.h (stat_worker): Remove.
(symlink_worker): Declare.
* exceptions.cc (set_process_mask): Just call sig_dispatch_pending and don't
worry about pending_signals since sig_dispatch_pending should always do the
right thing now.
(sig_handle): Reorganize SIGCONT handling to more closely conform to SUSv3.
* pinfo.h: Move __SIG enum to sigproc.h.
(PICOM_FIFO): New enum element.
(_pinfo): Remove 'thread2signal' stuff throughout class.
(_pinfo::commune_send): Make varargs.
(_pinfo::sigtodo): Eliminate.
(_pinfo::thread2signal): Ditto.
* signal.cc (kill_worker): Eliminate call to setthread2signal.
* sigproc.cc (local_sigtodo): Eliminate.
(getlocal_sigtodo): Ditto.
(sigelem): New class.
(pending_signals): New class.
(sigqueue): New variable, start of sigqueue linked list.
(sigcatch_nonmain): Eliminate.
(sigcatch_main): Eliminate.
(sigcatch_nosync): Eliminate.
(sigcomplete_nonmain): Eliminate.
(pending_signals): Eliminate.
(sig_clear): Call signal thread to clear pending signals, unless already in
signal thread.
(sigpending): Call signal thread to get pending signals.
(sig_dispatch_pending): Eliminate use of pending_signals and just check
sigqueue.
(sigproc_terminate): Eliminate all of the obsolete semaphore stuff. Close
signal pipe handle.
(sig_send): Eliminate all of the obsolete semaphore stuff and use pipe to send
signals.
(getevent): Eliminate.
(pending_signals::add): New function.
(pending_signals::del): New function.
(pending_signals::next): New function.
(wait_sig): Eliminate all of the obsolete semaphore stuff. Use pipe to
communicate and maintain a linked list of signals.
* sigproc.h: Move __SIG defines here. Add __SIGPENDING.
(sig_dispatch_pending): Remove "C" specifier.
(sig_handle): Accept a mask argument.
* thread.cc: Remove signal handling considerations throughout.
2003-09-25 08:37:18 +08:00
|
|
|
fhandler_base ()
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("0x%08x", (int)this);
|
|
|
|
audio_in_ = NULL;
|
|
|
|
audio_out_ = NULL;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
fhandler_dev_dsp::~fhandler_dev_dsp ()
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
close ();
|
|
|
|
debug_printf ("0x%08x end", (int)this);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
* devices.cc: New file.
* devices.gperf: New file.
* devices.shilka: New file.
* cygwin-gperf: New file.
* cygwin-shilka: New file.
* fhandler_fifo.cc: New file.
* fhandler_nodevice.cc : New file. Reorganize headers so that path.h precedes
fhandler.h throughout. Remove device argument and unit arguments from fhandler
constructors throughout. Remove pc arguments to fhandler functions and use
internal pc element instead, throughout. Use dev element in pc throughout.
Use major/minor elements rather than units and device numbers previously in
fhandler class. Use correct methods for fhandler file names rather than
directly accessing file name variables, throughout.
* Makefile.in (DLL_OFILES): Add devices.o, fhandler_fifo.o
* dcrt0.cc (dll_crt0_1): Call device::init.
* devices.h: Renumber devices based on more Linux-like major/minor numbers.
Add more devices. Declare standard device storage.
(device): Declare struct.
* dir.cc (opendir): Use new 'build_fh_name' to construct a fhandler_* type.
* dtable.cc (dtable::get_debugger_info): Ditto.
(cygwin_attach_handle_to_fd): Ditto.
(dtable::release): Remove special FH_SOCKET case in favor of generic
"need_fixup_before" test.
(dtable::init_std_file_from_handle): Use either build_fh_dev or build_fh_name
to build standard fhandler.
(dtable::build_fh_name): Renamed from dtable::build_fhandler_from_name. Move
out of dtable class. Don't accept a path_conv argument. Just build it here
and pass it to:
(build_fh_pc): Renamed from dtable::build_fhandler. Move out of dtable class.
Use intrinsic device type in path_conv to create new fhandler.
(build_fh_dev): Renamed from dtable::build_fhandler. Move out of dtable class.
Simplify arguments to just take new 'device' type and a name. Just return
pointer to fhandler rather than trying to insert into dtable.
(dtable::dup_worker): Accommodate above build_fh name changes.
(dtable::find_fifo): New (currently broken) function.
(handle_to_fn): Use strechr for efficiency.
* dtable.h: Reflect above build_fh name changes and argument differences.
(fhandler_base *&operator []): Return self rather than copy of self.
* fhandler.cc (fhandler_base::operator =): Use pc element to set normalized
path.
(fhandler_base::set_name): Ditto.
(fhandler_base::raw_read): Use method to access name.
(fhandler_base::write): Correctly use get_output_handle rather than get_handle.
(handler_base::device_access_denied): New function.
(fhandler_base::open): Eliminate pc argument and use pc element of
fhandler_base throughout.
(fhandler_base::fstat): Detect if device is based in filesystem and use
fstat_fs to calculate stat, if so.
(fhandler_base::fhandler_base): Eliminate handling of file names and, instead,
just free appropriate component from pc.
(fhandler_base::opendir): Remove path_conv parameter.
* fhandler.h: Remove all device flags.
(fhandler_base::pc): New element.
(fhandler_base::set_name): Change argument to path_conv.
(fhandler_base::error): New function.
(fhandler_base::exists): New function.
(fhandler_base::pc_binmode): New function.
(fhandler_base::dev): New function.
(fhandler_base::open_fs): New function.
(fhandler_base::fstat_fs): New function.
(fhandler_base::fstat_by_name): New function.
(fhandler_base::fstat_by_handle): New function.
(fhandler_base::isfifo): New function.
(fhandler_base::is_slow): New function.
(fhandler_base::is_auto_device): New function.
(fhandler_base::is_fs_special): New function.
(fhandler_base::device_access_denied): New function.
(fhandler_base::operator DWORD&): New operator.
(fhandler_base::get_name): Return normalized path from pc.
(fhandler_base::get_win32_name): Return windows path from pc.
(fhandler_base::isdevice): Renamed from is_device.
(fhandler_base::get_native_name): Return device format.
(fhandler_fifo): New class.
(fhandler_nodevice): New class.
(select_stuff::device_specific): Remove array.
(select_stuff::device_specific_pipe): New class element.
(select_stuff::device_specific_socket): New class element.
(select_stuff::device_specific_serial): New class element.
(select_stuff::select_stuff): Initialize new elements.
* fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Move to base class
from fhandler_disk_file.
(fhandler_base::fstat_by_name): Ditto.
(fhandler_base::fstat_by_name): Ditto.
(fhandler_disk_file::open): Move most functionality into
fhandler_base::open_fs.
(fhandler_base::open_fs): New function.
(fhandler_disk_file::close): Move most functionality into
fhandler_base::close_fs.
(fhandler_base::close_fs): New function.
* fhandler_mem.cc (fhandler_dev_mem::open): Use device name in debugging
output.
* fhandler_socket.cc (fhandler_socket::set_connect_secret): Copy standard
urandom device into appropriate place.
(fhandler_socket::accept): Reflect change in fdsock return value.
* fhandler_tty.cc: See "throughouts" above.
* net.cc: Accommodate fdsock change throughout.
(fdsock): Return success or failure, accept fd argument and device argument.
* path.cc (symlink_info::major): New element.
(symlink_info::minor): New element.
(symlink_info::parse_device): Declare new function.
(fs_info::update): Accommodate changes in path_conv class.
(path_conv::fillin): Ditto.
(path_conv::return_and_clear_normalized_path): Eliminate.
(path_conv::set_normalized_path): New function.
(path_conv::path_conv): Set info in dev element. Use path_conv methods Check
for FH_FS rather than FH_BAD to indicate when to fill in filesystem stuff.
where appropriate rather than direct access. Use set_normalized_path to set
normalized path.
(windows_device_names): Eliminate.
(get_dev): Ditto.
(get_raw_device_number): Ditto.
(get_device_number): Ditto.
(win32_device_name): Call new device name parser to do most of the heavy
lifting.
(mount_info::conv_to_win32_path): Fill in dev field as appropriate.
(symlink_worker): Handle new device files.
(symlink_info::check): Ditto.
(symlink_info::parse_device): Define new function.
* path.h (executable_states): Move here from fhandler.h.
(fs_info): Rename variables to *_storage and create methods for accessing same.
(path_conv): Add dev element, remove devn and unit and adjust inline methods to
accommodate.
(set_normalized_path): Declare new function.
* pinfo.cc (_pinfo::commune_recv): Add broken support for handling fifos.
(_pinfo::commune_send): Ditto.
* pipe.cc (fhandler_pipe::close): check for existence of handle before closing
it.
(handler_pipe::create): Rename from make_pipe. Change arguments to accept
fhandler_pipe array. Accommodate fifos.
(pipe): Rework to deal with fhandler_pipe::create changes.
(_pipe): Ditto.
* select.cc: Use individual device_specific types throughout rather than
indexing with obsolete device number.
(set_bits): Use is_socket call rather than checking device number.
* shared_info.h (CURR_MOUNT_MAGIC): Update.
(conv_to_win32_path): Reflect addition of device argument.
* syscalls.cc (mknod_worker): New function.
(open): Use build_fh_name to build fhandler.
(chown_worker): Detect if this is an 'auto' device rather than an on-filesystem
device and handle appropriately.
(chmod_device): New function.
(chmod): Detect if this is an 'auto' device rather than an on-filesystem device
and handle appropriately. Use chmod_device to set mode of in-filesystem
devices.
(stat_worker): Eliminate path_conv argument. Call build_fh_name to construct
fhandler. Use fh->error() rather than pc->error to detect errors in fhandler
construction.
(access_worker): New function pulled from access. Accommodate in-filesystem
devices.
(access): Use access_worker.
(fpathconf): Detect if this is an 'auto' device rather than an on-filesystem
device and handle appropriately.
(mknod_worker): New function.
(mknod32): New function.
(chroot): Free normalized path -- assuming it was actually cmalloced.
* tty.cc (create_tty_master): Tweak for new device class.
(tty::common_init): Ditto.
* winsup.h (stat_worker): Remove.
(symlink_worker): Declare.
* exceptions.cc (set_process_mask): Just call sig_dispatch_pending and don't
worry about pending_signals since sig_dispatch_pending should always do the
right thing now.
(sig_handle): Reorganize SIGCONT handling to more closely conform to SUSv3.
* pinfo.h: Move __SIG enum to sigproc.h.
(PICOM_FIFO): New enum element.
(_pinfo): Remove 'thread2signal' stuff throughout class.
(_pinfo::commune_send): Make varargs.
(_pinfo::sigtodo): Eliminate.
(_pinfo::thread2signal): Ditto.
* signal.cc (kill_worker): Eliminate call to setthread2signal.
* sigproc.cc (local_sigtodo): Eliminate.
(getlocal_sigtodo): Ditto.
(sigelem): New class.
(pending_signals): New class.
(sigqueue): New variable, start of sigqueue linked list.
(sigcatch_nonmain): Eliminate.
(sigcatch_main): Eliminate.
(sigcatch_nosync): Eliminate.
(sigcomplete_nonmain): Eliminate.
(pending_signals): Eliminate.
(sig_clear): Call signal thread to clear pending signals, unless already in
signal thread.
(sigpending): Call signal thread to get pending signals.
(sig_dispatch_pending): Eliminate use of pending_signals and just check
sigqueue.
(sigproc_terminate): Eliminate all of the obsolete semaphore stuff. Close
signal pipe handle.
(sig_send): Eliminate all of the obsolete semaphore stuff and use pipe to send
signals.
(getevent): Eliminate.
(pending_signals::add): New function.
(pending_signals::del): New function.
(pending_signals::next): New function.
(wait_sig): Eliminate all of the obsolete semaphore stuff. Use pipe to
communicate and maintain a linked list of signals.
* sigproc.h: Move __SIG defines here. Add __SIGPENDING.
(sig_dispatch_pending): Remove "C" specifier.
(sig_handle): Accept a mask argument.
* thread.cc: Remove signal handling considerations throughout.
2003-09-25 08:37:18 +08:00
|
|
|
fhandler_dev_dsp::open (int flags, mode_t mode)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
open_count++;
|
|
|
|
if (open_count > 1)
|
2002-06-04 19:18:46 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
set_errno (EBUSY);
|
2002-06-04 19:18:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2002-07-03 11:20:50 +08:00
|
|
|
set_flags ((flags & ~O_TEXT) | O_BINARY);
|
2004-03-23 19:05:56 +08:00
|
|
|
// Work out initial sample format & frequency, /dev/dsp defaults
|
|
|
|
audioformat_ = AFMT_U8;
|
2001-10-04 10:34:20 +08:00
|
|
|
audiofreq_ = 8000;
|
|
|
|
audiobits_ = 8;
|
|
|
|
audiochannels_ = 1;
|
2004-03-23 19:05:56 +08:00
|
|
|
switch (flags & O_ACCMODE)
|
2001-04-24 10:07:58 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
case O_WRONLY:
|
|
|
|
audio_out_ = new Audio_out;
|
|
|
|
if (!audio_out_->query (audiofreq_, audiobits_, audiochannels_))
|
|
|
|
{
|
|
|
|
delete audio_out_;
|
|
|
|
audio_out_ = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case O_RDONLY:
|
|
|
|
audio_in_ = new Audio_in;
|
|
|
|
if (!audio_in_->query (audiofreq_, audiobits_, audiochannels_))
|
|
|
|
{
|
|
|
|
delete audio_in_;
|
|
|
|
audio_in_ = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case O_RDWR:
|
|
|
|
audio_out_ = new Audio_out;
|
|
|
|
if (audio_out_->query (audiofreq_, audiobits_, audiochannels_))
|
|
|
|
{
|
|
|
|
audio_in_ = new Audio_in;
|
|
|
|
if (!audio_in_->query (audiofreq_, audiobits_, audiochannels_))
|
|
|
|
{
|
|
|
|
delete audio_in_;
|
|
|
|
audio_in_ = NULL;
|
|
|
|
audio_out_->stop ();
|
|
|
|
delete audio_out_;
|
|
|
|
audio_out_ = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete audio_out_;
|
|
|
|
audio_out_ = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
set_errno (EINVAL);
|
|
|
|
return 0;
|
|
|
|
} // switch (flags & O_ACCMODE)
|
|
|
|
int rc;
|
|
|
|
if (audio_in_ || audio_out_)
|
|
|
|
{ /* All tried query () succeeded */
|
|
|
|
rc = 1;
|
2001-04-24 10:07:58 +08:00
|
|
|
set_open_status ();
|
2004-04-10 21:45:10 +08:00
|
|
|
need_fork_fixup (true);
|
|
|
|
close_on_exec (true);
|
2001-04-24 10:07:58 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
else
|
|
|
|
{ /* One of the tried query () failed */
|
|
|
|
rc = 0;
|
|
|
|
set_errno (EIO);
|
|
|
|
}
|
|
|
|
debug_printf ("ACCMODE=0x%08x audio_in=%08x audio_out=%08x, rc=%d",
|
|
|
|
flags & O_ACCMODE, (int)audio_in_, (int)audio_out_, rc);
|
|
|
|
return rc;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
#define RETURN_ERROR_WHEN_BUSY(audio)\
|
|
|
|
if ((audio)->denyAccess ()) \
|
|
|
|
{\
|
|
|
|
set_errno (EBUSY);\
|
|
|
|
return -1;\
|
|
|
|
}
|
|
|
|
|
2001-04-16 11:27:16 +08:00
|
|
|
int
|
|
|
|
fhandler_dev_dsp::write (const void *ptr, size_t len)
|
|
|
|
{
|
2004-03-24 16:57:17 +08:00
|
|
|
int len_s = len;
|
|
|
|
const char *ptr_s = static_cast <const char *> (ptr);
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("ptr=%08x len=%d", ptr, len);
|
|
|
|
if (!audio_out_)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
set_errno (EACCES); // device was opened for read?
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
|
|
|
if (audio_out_->getOwner () == 0L)
|
|
|
|
{ // No owner yet, lets do it
|
|
|
|
// check for wave file & get parameters & skip header if possible.
|
2004-03-24 16:57:17 +08:00
|
|
|
if (audio_out_->parsewav (ptr_s, len_s,
|
2004-03-23 19:05:56 +08:00
|
|
|
audiofreq_, audiobits_, audiochannels_))
|
|
|
|
{ // update our format conversion
|
2004-03-24 16:57:17 +08:00
|
|
|
debug_printf ("=> ptr_s=%08x len_s=%d", ptr_s, len_s);
|
2004-03-23 19:05:56 +08:00
|
|
|
audioformat_ = ((audiobits_ == 8) ? AFMT_U8 : AFMT_S16_LE);
|
|
|
|
audio_out_->setformat (audioformat_);
|
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
// Open audio device properly with callbacks.
|
2004-03-23 19:05:56 +08:00
|
|
|
if (!audio_out_->start (audiofreq_, audiobits_, audiochannels_))
|
|
|
|
{
|
|
|
|
set_errno (EIO);
|
|
|
|
return -1;
|
|
|
|
}
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
|
2004-03-24 16:57:17 +08:00
|
|
|
audio_out_->write (ptr_s, len_s);
|
2001-04-16 11:27:16 +08:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2002-12-14 12:01:32 +08:00
|
|
|
void __stdcall
|
|
|
|
fhandler_dev_dsp::read (void *ptr, size_t& len)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("ptr=%08x len=%d", ptr, len);
|
|
|
|
if (!audio_in_)
|
|
|
|
{
|
|
|
|
len = (size_t)-1;
|
|
|
|
set_errno (EACCES); // device was opened for write?
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (audio_in_->denyAccess ())
|
|
|
|
{
|
|
|
|
len = (size_t)-1;
|
|
|
|
set_errno (EBUSY);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (audio_in_->getOwner () == 0L)
|
|
|
|
{ // No owner yet. Let's take it
|
|
|
|
// Open audio device properly with callbacks.
|
|
|
|
if (!audio_in_->start (audiofreq_, audiobits_, audiochannels_))
|
|
|
|
{
|
|
|
|
len = (size_t)-1;
|
|
|
|
set_errno (EIO);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
audio_in_->read ((char *)ptr, (int&)len);
|
2002-12-14 12:01:32 +08:00
|
|
|
return;
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
|
|
|
|
2003-04-02 00:11:41 +08:00
|
|
|
_off64_t
|
|
|
|
fhandler_dev_dsp::lseek (_off64_t offset, int whence)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fhandler_dev_dsp::close (void)
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("audio_in=%08x audio_out=%08x",
|
|
|
|
(int)audio_in_, (int)audio_out_);
|
|
|
|
if (audio_in_)
|
|
|
|
{
|
|
|
|
delete audio_in_;
|
|
|
|
audio_in_ = NULL;
|
|
|
|
}
|
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
delete audio_out_;
|
|
|
|
audio_out_ = NULL;
|
|
|
|
}
|
|
|
|
if (open_count > 0)
|
|
|
|
open_count--;
|
2001-04-16 11:27:16 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fhandler_dev_dsp::dup (fhandler_base * child)
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("");
|
2001-05-21 01:31:06 +08:00
|
|
|
fhandler_dev_dsp *fhc = (fhandler_dev_dsp *) child;
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
fhc->set_flags (get_flags ());
|
2001-04-16 11:27:16 +08:00
|
|
|
fhc->audiochannels_ = audiochannels_;
|
|
|
|
fhc->audiobits_ = audiobits_;
|
|
|
|
fhc->audiofreq_ = audiofreq_;
|
|
|
|
fhc->audioformat_ = audioformat_;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
int
|
|
|
|
fhandler_dev_dsp::ioctl (unsigned int cmd, void *ptr)
|
2001-04-16 11:27:16 +08:00
|
|
|
{
|
2001-05-21 01:31:06 +08:00
|
|
|
int *intptr = (int *) ptr;
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("audio_in=%08x audio_out=%08x",
|
|
|
|
(int)audio_in_, (int)audio_out_);
|
2001-04-16 11:27:16 +08:00
|
|
|
switch (cmd)
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
#define CASE(a) case a : debug_printf ("/dev/dsp: ioctl %s", #a);
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
CASE (SNDCTL_DSP_RESET)
|
2004-03-23 19:05:56 +08:00
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
2004-04-13 17:38:32 +08:00
|
|
|
audio_out_->stop (true);
|
2004-03-23 19:05:56 +08:00
|
|
|
}
|
|
|
|
if (audio_in_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_in_);
|
|
|
|
audio_in_->stop ();
|
|
|
|
}
|
2001-06-29 10:20:01 +08:00
|
|
|
return 0;
|
2004-03-23 19:05:56 +08:00
|
|
|
break;
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
CASE (SNDCTL_DSP_GETBLKSIZE)
|
2004-03-23 19:05:56 +08:00
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
*intptr = audio_out_->blockSize (audiofreq_,
|
|
|
|
audiobits_,
|
|
|
|
audiochannels_);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // I am very sure that audio_in_ is valid
|
|
|
|
*intptr = audio_in_->blockSize (audiofreq_,
|
|
|
|
audiobits_,
|
|
|
|
audiochannels_);
|
|
|
|
}
|
2001-11-05 14:09:15 +08:00
|
|
|
return 0;
|
2004-03-23 19:05:56 +08:00
|
|
|
break;
|
2001-04-16 11:27:16 +08:00
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
CASE (SNDCTL_DSP_SETFMT)
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
int nBits;
|
|
|
|
switch (*intptr)
|
|
|
|
{
|
|
|
|
case AFMT_QUERY:
|
|
|
|
*intptr = audioformat_;
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case AFMT_U16_BE:
|
|
|
|
case AFMT_U16_LE:
|
|
|
|
case AFMT_S16_BE:
|
|
|
|
case AFMT_S16_LE:
|
|
|
|
nBits = 16;
|
|
|
|
break;
|
|
|
|
case AFMT_U8:
|
|
|
|
case AFMT_S8:
|
|
|
|
nBits = 8;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nBits = 0;
|
|
|
|
}
|
|
|
|
if (nBits && audio_out_)
|
2001-05-21 01:31:06 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
|
|
|
audio_out_->stop ();
|
|
|
|
audio_out_->setformat (*intptr);
|
|
|
|
if (audio_out_->query (audiofreq_, nBits, audiochannels_))
|
2001-05-21 01:31:06 +08:00
|
|
|
{
|
|
|
|
audiobits_ = nBits;
|
2004-03-23 19:05:56 +08:00
|
|
|
audioformat_ = *intptr;
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
*intptr = audiobits_;
|
2001-05-21 01:31:06 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
if (nBits && audio_in_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_in_);
|
|
|
|
audio_in_->stop ();
|
|
|
|
audio_in_->setformat (*intptr);
|
|
|
|
if (audio_in_->query (audiofreq_, nBits, audiochannels_))
|
|
|
|
{
|
|
|
|
audiobits_ = nBits;
|
|
|
|
audioformat_ = *intptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*intptr = audiobits_;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
CASE (SNDCTL_DSP_SPEED)
|
2004-03-23 19:05:56 +08:00
|
|
|
{
|
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
|
|
|
audio_out_->stop ();
|
|
|
|
if (audio_out_->query (*intptr, audiobits_, audiochannels_))
|
|
|
|
audiofreq_ = *intptr;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*intptr = audiofreq_;
|
|
|
|
return -1;
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
if (audio_in_)
|
2001-05-21 01:31:06 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_in_);
|
|
|
|
audio_in_->stop ();
|
|
|
|
if (audio_in_->query (*intptr, audiobits_, audiochannels_))
|
|
|
|
audiofreq_ = *intptr;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*intptr = audiofreq_;
|
|
|
|
return -1;
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
2001-05-21 01:31:06 +08:00
|
|
|
|
|
|
|
CASE (SNDCTL_DSP_STEREO)
|
|
|
|
{
|
|
|
|
int nChannels = *intptr + 1;
|
|
|
|
|
2004-03-23 19:05:56 +08:00
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
|
|
|
audio_out_->stop ();
|
|
|
|
if (audio_out_->query (audiofreq_, audiobits_, nChannels))
|
|
|
|
audiochannels_ = nChannels;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*intptr = audiochannels_ - 1;
|
|
|
|
return -1;
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
if (audio_in_)
|
2001-05-21 01:31:06 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_in_);
|
|
|
|
audio_in_->stop ();
|
|
|
|
if (audio_in_->query (audiofreq_, audiobits_, nChannels))
|
|
|
|
audiochannels_ = nChannels;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*intptr = audiochannels_ - 1;
|
|
|
|
return -1;
|
|
|
|
}
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
return 0;
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-04-05 16:30:41 +08:00
|
|
|
CASE (SNDCTL_DSP_CHANNELS)
|
|
|
|
{
|
|
|
|
int nChannels = *intptr;
|
|
|
|
|
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
|
|
|
audio_out_->stop ();
|
|
|
|
if (audio_out_->query (audiofreq_, audiobits_, nChannels))
|
|
|
|
audiochannels_ = nChannels;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*intptr = audiochannels_;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (audio_in_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_in_);
|
|
|
|
audio_in_->stop ();
|
|
|
|
if (audio_in_->query (audiofreq_, audiobits_, nChannels))
|
|
|
|
audiochannels_ = nChannels;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*intptr = audiochannels_;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
CASE (SNDCTL_DSP_GETOSPACE)
|
|
|
|
{
|
|
|
|
audio_buf_info *p = (audio_buf_info *) ptr;
|
2004-03-23 19:05:56 +08:00
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
|
|
|
audio_out_->buf_info (p, audiofreq_, audiobits_, audiochannels_);
|
|
|
|
debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d",
|
|
|
|
ptr, p->fragments, p->fragsize, p->bytes);
|
|
|
|
}
|
2001-06-29 10:20:01 +08:00
|
|
|
return 0;
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2004-04-13 17:38:32 +08:00
|
|
|
CASE (SNDCTL_DSP_GETISPACE)
|
|
|
|
{
|
|
|
|
audio_buf_info *p = (audio_buf_info *) ptr;
|
|
|
|
if (audio_in_)
|
|
|
|
{
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_in_);
|
|
|
|
audio_in_->buf_info (p, audiofreq_, audiobits_, audiochannels_);
|
|
|
|
debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d",
|
|
|
|
ptr, p->fragments, p->fragsize, p->bytes);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
CASE (SNDCTL_DSP_SETFRAGMENT)
|
|
|
|
{
|
|
|
|
// Fake!! esound & mikmod require this on non PowerPC platforms.
|
|
|
|
//
|
2001-06-29 10:20:01 +08:00
|
|
|
return 0;
|
2001-05-21 01:31:06 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2003-01-14 10:08:35 +08:00
|
|
|
CASE (SNDCTL_DSP_GETFMTS)
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
*intptr = AFMT_S16_LE | AFMT_U8; // only native formats returned here
|
2004-04-05 16:30:41 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
CASE (SNDCTL_DSP_GETCAPS)
|
|
|
|
{
|
|
|
|
*intptr = DSP_CAP_BATCH | DSP_CAP_DUPLEX;
|
2004-03-23 19:05:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
CASE (SNDCTL_DSP_POST)
|
|
|
|
CASE (SNDCTL_DSP_SYNC)
|
|
|
|
{
|
|
|
|
if (audio_out_)
|
|
|
|
{
|
|
|
|
// Stop audio out device
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_out_);
|
|
|
|
audio_out_->stop ();
|
|
|
|
}
|
|
|
|
if (audio_in_)
|
|
|
|
{
|
|
|
|
// Stop audio in device
|
|
|
|
RETURN_ERROR_WHEN_BUSY (audio_in_);
|
|
|
|
audio_in_->stop ();
|
|
|
|
}
|
2003-01-14 10:08:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2001-05-21 01:31:06 +08:00
|
|
|
default:
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("/dev/dsp: ioctl 0x%08x not handled yet! FIXME:", cmd);
|
2001-05-21 01:31:06 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
#undef CASE
|
2001-04-16 11:27:16 +08:00
|
|
|
};
|
2004-03-23 19:05:56 +08:00
|
|
|
set_errno (EINVAL);
|
2001-04-16 11:27:16 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::dump ()
|
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
paranoid_printf ("here");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
fhandler_dev_dsp::fixup_after_fork (HANDLE parent)
|
|
|
|
{ // called from new child process
|
|
|
|
debug_printf ("audio_in=%08x audio_out=%08x",
|
|
|
|
(int)audio_in_, (int)audio_out_);
|
|
|
|
if (audio_in_ )
|
|
|
|
audio_in_ ->fork_fixup (parent);
|
|
|
|
if (audio_out_)
|
|
|
|
audio_out_->fork_fixup (parent);
|
2001-04-16 11:27:16 +08:00
|
|
|
}
|
2001-05-24 13:20:17 +08:00
|
|
|
|
|
|
|
void
|
2004-02-03 05:00:07 +08:00
|
|
|
fhandler_dev_dsp::fixup_after_exec ()
|
2001-05-24 13:20:17 +08:00
|
|
|
{
|
2004-03-23 19:05:56 +08:00
|
|
|
debug_printf ("audio_in=%08x audio_out=%08x",
|
|
|
|
(int)audio_in_, (int)audio_out_);
|
2001-05-24 13:20:17 +08:00
|
|
|
}
|
2004-03-23 19:05:56 +08:00
|
|
|
|
|
|
|
|