1401 lines
53 KiB
Plaintext
1401 lines
53 KiB
Plaintext
/*
|
|
** Copyright (c) 1985-1994 Microsoft Corporation
|
|
**
|
|
** Title: mwrec.c - Multimedia Systems Media Control Interface
|
|
** waveform digital audio driver for RIFF wave files.
|
|
** Routines for recording wave files
|
|
*/
|
|
|
|
/*
|
|
** Change log:
|
|
**
|
|
** DATE REV DESCRIPTION
|
|
** ----------- ----- ------------------------------------------
|
|
** 18-APR-1990 ROBWI Original
|
|
** 19-JUN-1990 ROBWI Added wave in
|
|
** 13-Jan-1992 MikeTri Ported to NT
|
|
** Aug-1994 Lauriegr This is all out of date
|
|
*/
|
|
|
|
|
|
/*******************************************************************************
|
|
** !!READ THIS!! *
|
|
** !!READ THIS!! *
|
|
** !!READ THIS!! *
|
|
** *
|
|
** As far as I can make out, this code was never finished.
|
|
** The scheme (which I tried to start writing up in MCIWAVE.H) is that there are
|
|
** a series of NODEs which describe a wave file. As long as there is in fact
|
|
** only one NODE for the file (which is probably the only common case) then this
|
|
** all works fine. If there are multiple NODEs (which you arrive at by inserting
|
|
** bits or deleting bits from the middle) then it all falls apart.
|
|
**
|
|
** We're pretty sure nobody's ever used this stuff as it's been broken for years
|
|
** in 16 and 32 bit. We've discovered it just as Daytona is about to ship (that's
|
|
** Windows/NT version 3.5). Maybe NMM wil replace it all anyway.
|
|
**
|
|
** This is a half-patched up version with several questions left outstanding.
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define UNICODE
|
|
|
|
#define NOGDICAPMASKS
|
|
#define NOVIRTUALKEYCODES
|
|
#define NOWINSTYLES
|
|
#define NOSYSMETRICS
|
|
#define NOMENUS
|
|
#define NOICONS
|
|
#define NOKEYSTATES
|
|
#define NOSYSCOMMANDS
|
|
#define NORASTEROPS
|
|
#define NOSHOWWINDOW
|
|
#define OEMRESOURCE
|
|
#define NOATOM
|
|
#define NOCLIPBOARD
|
|
#define NOCOLOR
|
|
#define NOCTLMGR
|
|
#define NODRAWTEXT
|
|
#define NOGDI
|
|
#define NOKERNEL
|
|
#define NONLS
|
|
#define NOMB
|
|
#define NOMEMMGR
|
|
#define NOMETAFILE
|
|
#define NOOPENFILE
|
|
#define NOSCROLL
|
|
#define NOTEXTMETRIC
|
|
#define NOWH
|
|
#define NOWINOFFSETS
|
|
#define NOCOMM
|
|
#define NOKANJI
|
|
#define NOHELP
|
|
#define NOPROFILER
|
|
#define NODEFERWINDOWPOS
|
|
|
|
#include <windows.h>
|
|
#include "mciwave.h"
|
|
#include <mmddk.h>
|
|
#include <gmem.h> // 'cos of GAllocPtrF etc.
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func int | abs |
|
|
This macro returns the absolute value of the signed integer passed to
|
|
it.
|
|
|
|
@parm int | x |
|
|
Contains the integer whose absolute value is to be returned.
|
|
|
|
@rdesc Returns the absolute value of the signed parameter passed.
|
|
*/
|
|
|
|
#define abs(x) ((x) > 0 ? (x) : -(x))
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func DWORD | mwFindThisFreeDataNode |
|
|
Attempts to locate a free wave data node whose temporary data points to
|
|
<p>dDataStart<d>. This allows data from one node to be expanded to
|
|
adjacent free data of another node. Note that this depends upon any
|
|
free data nodes that previously pointed to original data to have their
|
|
total length zeroed when freed.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm DWORD | dDataStart |
|
|
Indicates the data start position to match.
|
|
|
|
@rdesc Returns the free data node with adjacent free temporary data, else -1
|
|
if there is none.
|
|
*/
|
|
|
|
PRIVATE DWORD PASCAL NEAR mwFindThisFreeDataNode(
|
|
PWAVEDESC pwd,
|
|
DWORD dDataStart)
|
|
{
|
|
LPWAVEDATANODE lpwdn;
|
|
DWORD dBlockNode;
|
|
|
|
for (lpwdn = LPWDN(pwd, 0), dBlockNode = 0; dBlockNode < pwd->dWaveDataNodes; lpwdn++, dBlockNode++)
|
|
if (ISFREEBLOCKNODE(lpwdn) && lpwdn->dTotalLength && (UNMASKDATASTART(lpwdn) == dDataStart))
|
|
return dBlockNode;
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func DWORD | mwFindAnyFreeBlockNode |
|
|
Locates a free node with no data attached. If there is none, it forces
|
|
more to be allocated.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@rdesc Returns a node with no data attached, else -1 if no memory is available.
|
|
The node returned is marked as a free node, and need not be discarded if
|
|
not used.
|
|
*/
|
|
|
|
PRIVATE DWORD PASCAL NEAR mwFindAnyFreeBlockNode(
|
|
PWAVEDESC pwd)
|
|
{
|
|
LPWAVEDATANODE lpwdn;
|
|
DWORD dCurBlockNode;
|
|
|
|
for (lpwdn = LPWDN(pwd, 0), dCurBlockNode = 0; dCurBlockNode < pwd->dWaveDataNodes; lpwdn++, dCurBlockNode++)
|
|
if (ISFREEBLOCKNODE(lpwdn) && !lpwdn->dTotalLength)
|
|
return dCurBlockNode;
|
|
return mwAllocMoreBlockNodes(pwd);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func BOOL | CopyBlockData |
|
|
Copies <p>wLength<d> bytes of data pointed to by the <p>lpwdnSrc<d>
|
|
node to the data pointed to by the <p>lpwdnDst<d> node, starting at
|
|
<p>dSrc<d> to <p>dDst<d>.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm <t>LPWAVEDATANODE<d> | lpwdnSrc |
|
|
Points to the source node.
|
|
|
|
@parm <t>LPWAVEDATANODE<d> | lpwdnDst |
|
|
Points to the destination node.
|
|
|
|
@parm DWORD | dSrc |
|
|
Indicates the starting offset at which the data is located.
|
|
|
|
@parm DWORD | dDst |
|
|
Indicates the starting offset at which to place the data.
|
|
|
|
@parm DWORD | dLength |
|
|
Indicates the number of bytes of data to move.
|
|
|
|
@rdesc Returns TRUE if the data was copied, else FALSE if no memory is
|
|
available, or if a read or write error occured. If an error occurs,
|
|
the task error state is set.
|
|
|
|
@comm Note that this function will not compile with C 6.00A -Ox.
|
|
*/
|
|
|
|
PRIVATE BOOL PASCAL NEAR CopyBlockData(
|
|
PWAVEDESC pwd,
|
|
LPWAVEDATANODE lpwdnSrc,
|
|
LPWAVEDATANODE lpwdnDst,
|
|
DWORD dSrc,
|
|
DWORD dDst,
|
|
DWORD dLength)
|
|
{
|
|
LPBYTE lpbBuffer;
|
|
UINT wError;
|
|
|
|
if (0 != (lpbBuffer = GlobalAlloc(GMEM_FIXED, dLength))) {
|
|
if ((_llseek(pwd->hfTempBuffers, UNMASKDATASTART(lpwdnSrc) + dSrc, SEEK_SET) == -1)
|
|
|| ((DWORD)_lread(pwd->hfTempBuffers, lpbBuffer, (LONG)dLength) != dLength)
|
|
|| (_llseek(pwd->hfTempBuffers, UNMASKDATASTART(lpwdnDst) + dDst, SEEK_SET) == -1))
|
|
wError = MCIERR_FILE_READ;
|
|
else
|
|
wError = ((DWORD)_lwrite(pwd->hfTempBuffers, lpbBuffer, (LONG)dLength) == dLength) ? 0 : MCIERR_FILE_WRITE;
|
|
GlobalFree(lpbBuffer);
|
|
} else
|
|
wError = MCIERR_OUT_OF_MEMORY;
|
|
|
|
if (wError) {
|
|
pwd->wTaskError = wError;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func DWORD | mwSplitCurrentDataNode |
|
|
Splits the current node at the current position, creating a new node
|
|
to contain the rest of the data, and possibly creating a second node
|
|
to hold data not aligned on a block boundary, in the case of temporary
|
|
data. The new node returned will have free temporary data space
|
|
attached that is at least <p>wMinDataLength<d> bytes in length.
|
|
|
|
If the split point is at the start of the current node, then the new
|
|
node is just inserted in front of the current node.
|
|
|
|
If the split point is at the end of the data of the current node, then
|
|
the new node is just inserted after the current node.
|
|
|
|
Else the current node must actually be split. This means that a new
|
|
block to point to the data after the split point is created. If the
|
|
current node points to temporary data and the split point is not block
|
|
aligned, then any extra data needs to be copied over to the new node
|
|
that is being inserted. This is because all starting points for
|
|
temporary data are block aligned. If this is not temporary data,
|
|
then the starting and ending points can just be adjusted to the exact
|
|
split point, instead of having to be block aligned.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm DWORD | dMinDataLength |
|
|
Indicates the minimum size of temporary data space that is to be
|
|
available to the new data node returned.
|
|
|
|
@rdesc Returns the new node after the split, which is linked to the point
|
|
after the current position in the current node. This node becomes the
|
|
current node. Returns -1 if no memory was available, or a file error
|
|
occurred, in which case the task error code is set.
|
|
*/
|
|
|
|
PRIVATE DWORD PASCAL NEAR mwSplitCurrentDataNode(
|
|
PWAVEDESC pwd,
|
|
DWORD dMinDataLength)
|
|
{
|
|
LPWAVEDATANODE lpwdn;
|
|
LPWAVEDATANODE lpwdnNew;
|
|
DWORD dNewDataNode;
|
|
DWORD dSplitAtData;
|
|
BOOL fTempData;
|
|
|
|
dSplitAtData = pwd->dCur - pwd->dVirtualWaveDataStart;
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataCurrentNode);
|
|
fTempData = ISTEMPDATA(lpwdn);
|
|
if (fTempData)
|
|
dMinDataLength += pwd->dAudioBufferLen;
|
|
dNewDataNode = mwFindAnyFreeDataNode(pwd, dMinDataLength);
|
|
if (dNewDataNode == -1)
|
|
return (DWORD)-1;
|
|
lpwdnNew = LPWDN(pwd, dNewDataNode);
|
|
if (!dSplitAtData) {
|
|
if (pwd->dWaveDataCurrentNode == pwd->dWaveDataStartNode)
|
|
pwd->dWaveDataStartNode = dNewDataNode;
|
|
else {
|
|
LPWAVEDATANODE lpwdnCur;
|
|
|
|
for (lpwdnCur = LPWDN(pwd, pwd->dWaveDataStartNode); lpwdnCur->dNextWaveDataNode != pwd->dWaveDataCurrentNode; lpwdnCur = LPWDN(pwd, lpwdnCur->dNextWaveDataNode))
|
|
;
|
|
lpwdnCur->dNextWaveDataNode = dNewDataNode;
|
|
}
|
|
lpwdnNew->dNextWaveDataNode = pwd->dWaveDataCurrentNode;
|
|
} else if (dSplitAtData == lpwdn->dDataLength) {
|
|
lpwdnNew->dNextWaveDataNode = lpwdn->dNextWaveDataNode;
|
|
lpwdn->dNextWaveDataNode = dNewDataNode;
|
|
pwd->dVirtualWaveDataStart += lpwdn->dDataLength;
|
|
} else {
|
|
DWORD dEndBlockNode;
|
|
LPWAVEDATANODE lpwdnEnd;
|
|
DWORD dSplitPoint;
|
|
|
|
if ((dEndBlockNode = mwFindAnyFreeBlockNode(pwd)) == -1) {
|
|
RELEASEBLOCKNODE(lpwdnNew);
|
|
return (DWORD)-1;
|
|
}
|
|
lpwdnEnd = LPWDN(pwd, dEndBlockNode);
|
|
if (fTempData) {
|
|
dSplitPoint = ROUNDDATA(pwd, dSplitAtData);
|
|
if (dSplitPoint != dSplitAtData) {
|
|
if (!CopyBlockData(pwd, lpwdn, lpwdnNew, dSplitAtData, 0, dSplitPoint - dSplitAtData)) {
|
|
RELEASEBLOCKNODE(lpwdnNew);
|
|
return (DWORD)-1;
|
|
}
|
|
lpwdnNew->dDataLength = dSplitPoint - dSplitAtData;
|
|
}
|
|
} else
|
|
dSplitPoint = dSplitAtData;
|
|
lpwdnEnd->dNextWaveDataNode = lpwdn->dNextWaveDataNode;
|
|
lpwdnEnd->dDataStart = lpwdn->dDataStart + dSplitPoint;
|
|
lpwdnEnd->dDataLength = lpwdn->dDataLength - dSplitPoint;
|
|
lpwdnEnd->dTotalLength = lpwdn->dTotalLength - dSplitPoint;
|
|
lpwdnNew->dNextWaveDataNode = dEndBlockNode;
|
|
lpwdn->dDataLength = dSplitAtData;
|
|
lpwdn->dTotalLength = dSplitPoint;
|
|
lpwdn->dNextWaveDataNode = dNewDataNode;
|
|
pwd->dVirtualWaveDataStart += lpwdn->dDataLength;
|
|
}
|
|
pwd->dWaveDataCurrentNode = dNewDataNode;
|
|
return dNewDataNode;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func DWORD | GatherAdjacentFreeDataNodes |
|
|
This function is used to attempt to consolidate adjacent temporary
|
|
data pointed to by free nodes so that a write can place data into
|
|
a single node. This is done by repeatedly requesting any free data
|
|
node whose data points to the end of the node's data passed.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm <t>LPWAVEDATANODE<d> | lpwdn |
|
|
Points to the node which is to collect adjacent temporary data.
|
|
|
|
@parm DWORD | dStartPoint |
|
|
Indicates the starting point to use when calculating the amount of
|
|
data retrieved. This is just subtracted from the total length of
|
|
the data attached to the node.
|
|
|
|
@parm DWORD | dBufferLength |
|
|
Indicates the amount of data to retrieve.
|
|
|
|
@rdesc Returns the amount of data actually retrieved, adjusted by
|
|
<d>dStartPoint<d>.
|
|
*/
|
|
|
|
PRIVATE DWORD PASCAL NEAR GatherAdjacentFreeDataNodes(
|
|
PWAVEDESC pwd,
|
|
LPWAVEDATANODE lpwdn,
|
|
DWORD dStartPoint,
|
|
DWORD dBufferLength)
|
|
{
|
|
for (; lpwdn->dTotalLength - dStartPoint < dBufferLength;) {
|
|
DWORD dFreeDataNode;
|
|
LPWAVEDATANODE lpwdnFree;
|
|
|
|
dFreeDataNode = mwFindThisFreeDataNode(pwd, UNMASKDATASTART(lpwdn) + lpwdn->dTotalLength);
|
|
if (dFreeDataNode == -1)
|
|
break;
|
|
lpwdnFree = LPWDN(pwd, dFreeDataNode);
|
|
lpwdn->dTotalLength += lpwdnFree->dTotalLength;
|
|
lpwdnFree->dTotalLength = 0;
|
|
}
|
|
return min(dBufferLength, lpwdn->dTotalLength - dStartPoint);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func <t>LPWAVEDATANODE<d> | NextDataNode |
|
|
Locates a free data node with the specified amount of data, and inserts
|
|
it after the current node, setting the current node to be this new
|
|
node.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm DWORD | dBufferLength |
|
|
Indicates the minimum amount of data that is to be available to the
|
|
new node inserted.
|
|
|
|
@rdesc Returns the newly inserted node, else NULL on error, in which case the
|
|
task error code is set.
|
|
*/
|
|
|
|
PRIVATE LPWAVEDATANODE PASCAL NEAR NextDataNode(
|
|
PWAVEDESC pwd,
|
|
DWORD dBufferLength)
|
|
{
|
|
DWORD dWaveDataNew;
|
|
LPWAVEDATANODE lpwdn;
|
|
LPWAVEDATANODE lpwdnNew;
|
|
|
|
if ((dWaveDataNew = mwFindAnyFreeDataNode(pwd, dBufferLength)) == -1)
|
|
return NULL;
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataCurrentNode);
|
|
lpwdnNew = LPWDN(pwd, dWaveDataNew);
|
|
lpwdnNew->dNextWaveDataNode = lpwdn->dNextWaveDataNode;
|
|
lpwdn->dNextWaveDataNode = dWaveDataNew;
|
|
pwd->dWaveDataCurrentNode = dWaveDataNew;
|
|
pwd->dVirtualWaveDataStart += lpwdn->dDataLength;
|
|
return lpwdnNew;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func BOOL | AdjustLastTempData |
|
|
This function makes two passes through the nodes that are affected by
|
|
an overwrite record. These are nodes that are either no longer needed,
|
|
or whose starting point needs to be adjusted. The two passes allow
|
|
any data to be successfully copied before removing any unneeded nodes.
|
|
This creates a more graceful exit to any failure.
|
|
|
|
The first pass locates the last node affected. If that node points to
|
|
temporary data, and the end of the overwrite does not fall on a block
|
|
aligned boundary, then any extra data must be copied to a block aligned
|
|
boundary. This means that a new node might need to be created if the
|
|
amount of data to be copied is greater than one block's worth. If the
|
|
end of overwrite happens to fall on a block boundary, then no copying
|
|
need be done. In either case the data start point is adjusted to
|
|
compensate for the data logically overwritten in this node, and the
|
|
total overwrite length is adjusted so that this node is not checked on
|
|
the second pass.
|
|
|
|
The second pass just frees nodes that become empty, and removes them
|
|
from the linked list of in-use nodes. When the last node affected is
|
|
encountered, either it will point to temporary data, in which case be
|
|
already adjusted, or point to original data, which must be adjusted.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm <t>LPWAVEDATANODE<d> | lpwdn |
|
|
Points to the node which is being adjusted for. It contains the new
|
|
data.
|
|
|
|
@parm DWORD | dStartPoint |
|
|
Contains the starting point at which data was overwritten.
|
|
|
|
@parm DWORD | dWriteSize |
|
|
Contains the amount of data overwritten.
|
|
|
|
@rdesc Returns TRUE if the nothing needed to be adjusted, or the last node
|
|
in the overwrite pointed to temporary data, and it was moved correctly,
|
|
else FALSE if no memory was available, or a file error occurred. In
|
|
that case the task error code is set.
|
|
*/
|
|
|
|
PRIVATE BOOL PASCAL NEAR AdjustLastTempData(
|
|
PWAVEDESC pwd,
|
|
LPWAVEDATANODE lpwdn,
|
|
DWORD dStartPoint,
|
|
DWORD dWriteSize)
|
|
{
|
|
LPWAVEDATANODE lpwdnCur;
|
|
DWORD dLength;
|
|
|
|
if ((lpwdn->dDataLength - dStartPoint >= dWriteSize) || (lpwdn->dNextWaveDataNode == ENDOFNODES))
|
|
return TRUE;
|
|
dWriteSize -= (lpwdn->dDataLength - dStartPoint);
|
|
for (dLength = dWriteSize, lpwdnCur = lpwdn;;) {
|
|
LPWAVEDATANODE lpwdnNext;
|
|
|
|
lpwdnNext = LPWDN(pwd, lpwdnCur->dNextWaveDataNode);
|
|
if (lpwdnNext->dDataLength >= dLength) {
|
|
DWORD dNewBlockNode;
|
|
DWORD dMoveData;
|
|
|
|
if (!ISTEMPDATA(lpwdnNext) || (lpwdnNext->dDataLength == dLength))
|
|
break;
|
|
if (lpwdnNext->dDataLength - dLength > ROUNDDATA(pwd, 1)) {
|
|
if ((dNewBlockNode = mwFindAnyFreeBlockNode(pwd)) == -1)
|
|
return FALSE;
|
|
} else
|
|
dNewBlockNode = (DWORD)-1;
|
|
dMoveData = min(ROUNDDATA(pwd, dLength), lpwdnNext->dDataLength) - dLength;
|
|
if (dMoveData && !CopyBlockData(pwd, lpwdnNext, lpwdnNext, dLength, 0, dMoveData))
|
|
return FALSE;
|
|
if (dNewBlockNode != -1) {
|
|
lpwdnCur = LPWDN(pwd, dNewBlockNode);
|
|
lpwdnCur->dDataStart = lpwdnNext->dDataStart + dLength + dMoveData;
|
|
lpwdnCur->dDataLength = lpwdnNext->dDataLength - (dLength + dMoveData);
|
|
lpwdnCur->dTotalLength = lpwdnNext->dTotalLength - (dLength + dMoveData);
|
|
lpwdnCur->dNextWaveDataNode = lpwdnNext->dNextWaveDataNode;
|
|
lpwdnNext->dNextWaveDataNode = dNewBlockNode;
|
|
lpwdnNext->dTotalLength = dLength + dMoveData;
|
|
}
|
|
lpwdnNext->dDataLength = dMoveData;
|
|
dWriteSize -= dLength;
|
|
break;
|
|
} else if ((!ISTEMPDATA(lpwdnNext)) && (lpwdnNext->dNextWaveDataNode == ENDOFNODES))
|
|
break;
|
|
dLength -= lpwdnNext->dDataLength;
|
|
lpwdnCur = lpwdnNext;
|
|
}
|
|
for (;;) {
|
|
LPWAVEDATANODE lpwdnNext;
|
|
|
|
lpwdnNext = LPWDN(pwd, lpwdn->dNextWaveDataNode);
|
|
if (lpwdnNext->dDataLength > dWriteSize) {
|
|
if (dWriteSize) {
|
|
lpwdnNext->dDataStart += dWriteSize;
|
|
lpwdnNext->dDataLength -= dWriteSize;
|
|
lpwdnNext->dTotalLength -= dWriteSize;
|
|
}
|
|
return TRUE;
|
|
}
|
|
dWriteSize -= lpwdnNext->dDataLength;
|
|
lpwdn->dNextWaveDataNode = lpwdnNext->dNextWaveDataNode;
|
|
if (!ISTEMPDATA(lpwdnNext))
|
|
lpwdnNext->dTotalLength = 0;
|
|
RELEASEBLOCKNODE(lpwdnNext);
|
|
if (lpwdn->dNextWaveDataNode == ENDOFNODES)
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func BOOL | mwOverWrite |
|
|
This function overwrites data in the wave file from the specified wave
|
|
buffer. The position is taken from the <e>WAVEDESC.dCur<d> pointer,
|
|
which is updated with the number of bytes actually overwritten.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm LPBYTE | lpbBuffer |
|
|
Points to a buffer to containing the data written.
|
|
|
|
@parm DWORD | dBufferLength |
|
|
Indicates the byte length of the buffer.
|
|
|
|
@rdesc Returns TRUE if overwrite succeeded, else FALSE on an error.
|
|
*/
|
|
|
|
PRIVATE BOOL PASCAL NEAR mwOverWrite(
|
|
PWAVEDESC pwd,
|
|
LPBYTE lpbBuffer,
|
|
DWORD dBufferLength)
|
|
{
|
|
LPWAVEDATANODE lpwdn;
|
|
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataCurrentNode);
|
|
for (; dBufferLength;)
|
|
if (ISTEMPDATA(lpwdn)) {
|
|
DWORD dStartPoint;
|
|
DWORD dRemainingSpace;
|
|
DWORD dMaxWrite;
|
|
|
|
dStartPoint = pwd->dCur - pwd->dVirtualWaveDataStart;
|
|
dRemainingSpace = min(dBufferLength, lpwdn->dTotalLength - dStartPoint);
|
|
if (dRemainingSpace == dBufferLength)
|
|
dMaxWrite = dBufferLength;
|
|
else if (UNMASKDATASTART(lpwdn) + lpwdn->dTotalLength == pwd->dWaveTempDataLength) {
|
|
dMaxWrite = dBufferLength;
|
|
lpwdn->dTotalLength += ROUNDDATA(pwd, dBufferLength - dRemainingSpace);
|
|
pwd->dWaveTempDataLength += ROUNDDATA(pwd, dBufferLength - dRemainingSpace);
|
|
} else
|
|
dMaxWrite = GatherAdjacentFreeDataNodes(pwd, lpwdn, dStartPoint, dBufferLength);
|
|
if (dMaxWrite) {
|
|
DWORD dWriteSize;
|
|
|
|
if (_llseek(pwd->hfTempBuffers, UNMASKDATASTART(lpwdn) + dStartPoint, SEEK_SET) == -1) {
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
break;
|
|
}
|
|
dWriteSize = (DWORD)_lwrite(pwd->hfTempBuffers, lpbBuffer, (LONG)dMaxWrite);
|
|
if (dWriteSize != -1) {
|
|
if (!AdjustLastTempData(pwd, lpwdn, dStartPoint, dWriteSize))
|
|
break;
|
|
if (lpwdn->dDataLength < dStartPoint + dWriteSize)
|
|
lpwdn->dDataLength = dStartPoint + dWriteSize;
|
|
lpbBuffer += dWriteSize;
|
|
dBufferLength -= dWriteSize;
|
|
pwd->dCur += dWriteSize;
|
|
if (pwd->dVirtualWaveDataStart + lpwdn->dDataLength > pwd->dSize)
|
|
pwd->dSize = pwd->dVirtualWaveDataStart + lpwdn->dDataLength;
|
|
}
|
|
if (dWriteSize != dMaxWrite) {
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
break;
|
|
}
|
|
}
|
|
if (dBufferLength && !(lpwdn = NextDataNode(pwd, dBufferLength)))
|
|
break;
|
|
} else {
|
|
DWORD dWaveDataNew;
|
|
|
|
if ((dWaveDataNew = mwSplitCurrentDataNode(pwd, dBufferLength)) != -1)
|
|
lpwdn = LPWDN(pwd, dWaveDataNew);
|
|
else
|
|
break;
|
|
}
|
|
return !dBufferLength;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func BOOL | mwInsert |
|
|
This function inserts data to the wave file from the specified wave
|
|
buffer. The position is taken from the <e>WAVEDESC.dCur<d> pointer,
|
|
which is updated with the number of bytes actually written.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm LPBYTE | lpbBuffer |
|
|
Points to a buffer to containing the data written.
|
|
|
|
@parm DWORD | dBufferLength |
|
|
Indicates the byte length of the buffer.
|
|
|
|
@rdesc Returns TRUE if insert succeeded, else FALSE on an error.
|
|
*/
|
|
|
|
PRIVATE BOOL PASCAL NEAR mwInsert(
|
|
PWAVEDESC pwd,
|
|
LPBYTE lpbBuffer,
|
|
DWORD dBufferLength)
|
|
{
|
|
LPWAVEDATANODE lpwdn;
|
|
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataCurrentNode);
|
|
for (; dBufferLength;)
|
|
if (ISTEMPDATA(lpwdn) && (pwd->dCur == pwd->dVirtualWaveDataStart + lpwdn->dDataLength)) {
|
|
DWORD dStartPoint;
|
|
DWORD dRemainingSpace;
|
|
DWORD dMaxInsert;
|
|
|
|
dStartPoint = pwd->dCur - pwd->dVirtualWaveDataStart;
|
|
dRemainingSpace = min(dBufferLength, lpwdn->dTotalLength - lpwdn->dDataLength);
|
|
if (dRemainingSpace == dBufferLength)
|
|
dMaxInsert = dBufferLength;
|
|
else if (UNMASKDATASTART(lpwdn) + lpwdn->dTotalLength == pwd->dWaveTempDataLength) {
|
|
dMaxInsert = dBufferLength;
|
|
lpwdn->dTotalLength += ROUNDDATA(pwd, dBufferLength - dRemainingSpace);
|
|
pwd->dWaveTempDataLength += ROUNDDATA(pwd, dBufferLength - dRemainingSpace);
|
|
} else
|
|
dMaxInsert = GatherAdjacentFreeDataNodes(pwd, lpwdn, dStartPoint, dBufferLength);
|
|
if (dMaxInsert) {
|
|
DWORD dWriteSize;
|
|
|
|
if (_llseek(pwd->hfTempBuffers, UNMASKDATASTART(lpwdn) + dStartPoint, SEEK_SET) == -1) {
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
break;
|
|
}
|
|
dWriteSize = (DWORD)_lwrite(pwd->hfTempBuffers, lpbBuffer, (LONG)dMaxInsert);
|
|
if (dWriteSize != -1) {
|
|
lpwdn->dDataLength += dWriteSize;
|
|
lpbBuffer += dWriteSize;
|
|
dBufferLength -= dWriteSize;
|
|
pwd->dCur += dWriteSize;
|
|
pwd->dSize += dWriteSize;
|
|
}
|
|
if (dWriteSize != dMaxInsert) {
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
break;
|
|
}
|
|
}
|
|
if (dBufferLength && !(lpwdn = NextDataNode(pwd, dBufferLength)))
|
|
break;
|
|
} else {
|
|
DWORD dWaveDataNew;
|
|
|
|
if ((dWaveDataNew = mwSplitCurrentDataNode(pwd, dBufferLength)) != -1)
|
|
lpwdn = LPWDN(pwd, dWaveDataNew);
|
|
else
|
|
break;
|
|
}
|
|
return !dBufferLength;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func DWORD | mwGetLevel |
|
|
This function finds the highest level in the specified wave sample.
|
|
Note that the function assumes that in some cases the sample size
|
|
is evenly divisable by 4.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm LPBYTE | lpbBuffer |
|
|
Points to a buffer containing the sample whose highest level is to be
|
|
returned.
|
|
|
|
@parm int | cbBufferLength |
|
|
Indicates the byte length of the sample buffer.
|
|
|
|
@rdesc Returns the highest level encountered in the sample for PCM data only.
|
|
If the device has been opened with one channel, the level is contained
|
|
in the low-order word. Else if the device has been opened with two
|
|
channels, one channel is in the low-order word, and the other is in the
|
|
high-order word.
|
|
*/
|
|
|
|
PRIVATE DWORD PASCAL NEAR mwGetLevel(
|
|
PWAVEDESC pwd,
|
|
LPBYTE lpbBuffer,
|
|
register int cbBufferLength)
|
|
{
|
|
if (pwd->pwavefmt->wFormatTag != WAVE_FORMAT_PCM)
|
|
return 0;
|
|
else if (pwd->pwavefmt->nChannels == 1) {
|
|
int iMonoLevel;
|
|
|
|
iMonoLevel = 0;
|
|
if (((NPPCMWAVEFORMAT)(pwd->pwavefmt))->wBitsPerSample == 8)
|
|
for (; cbBufferLength--; lpbBuffer++)
|
|
iMonoLevel = max(*lpbBuffer > 128 ? *lpbBuffer - 128 : 128 - *lpbBuffer, iMonoLevel);
|
|
else if (((NPPCMWAVEFORMAT)(pwd->pwavefmt))->wBitsPerSample == 16)
|
|
for (; cbBufferLength; lpbBuffer += sizeof(SHORT)) {
|
|
iMonoLevel = max(abs(*(PSHORT)lpbBuffer), iMonoLevel);
|
|
cbBufferLength -= sizeof(SHORT);
|
|
}
|
|
else
|
|
return 0;
|
|
return (DWORD)iMonoLevel;
|
|
} else if (pwd->pwavefmt->nChannels == 2) {
|
|
int iLeftLevel;
|
|
int iRightLevel;
|
|
|
|
iLeftLevel = 0;
|
|
iRightLevel = 0;
|
|
if (((NPPCMWAVEFORMAT)(pwd->pwavefmt))->wBitsPerSample == 8)
|
|
for (; cbBufferLength;) {
|
|
iLeftLevel = max(*lpbBuffer > 128 ? *lpbBuffer - 128 : 128 - *lpbBuffer, iLeftLevel);
|
|
lpbBuffer++;
|
|
iRightLevel = max(*lpbBuffer > 128 ? *lpbBuffer - 128 : 128 - *lpbBuffer, iRightLevel);
|
|
lpbBuffer++;
|
|
cbBufferLength -= 2;
|
|
}
|
|
else if (((NPPCMWAVEFORMAT)(pwd->pwavefmt))->wBitsPerSample == 16)
|
|
for (; cbBufferLength;) {
|
|
iLeftLevel = max(abs(*(PSHORT)lpbBuffer), iLeftLevel);
|
|
lpbBuffer += sizeof(SHORT);
|
|
iRightLevel = max(abs(*(PSHORT)lpbBuffer), iRightLevel);
|
|
lpbBuffer += sizeof(SHORT);
|
|
cbBufferLength -= 2 * sizeof(SHORT);
|
|
}
|
|
else
|
|
return 0;
|
|
return MAKELONG(iLeftLevel, iRightLevel);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func BOOL | CheckNewCommand |
|
|
This function is called when a New command flag is found during the
|
|
record loop. It determines if the new commands affect current
|
|
recording enough that it must be terminated. This can happen if a
|
|
Stop command is received.
|
|
|
|
Any other record change does not need to stop current recording, as
|
|
they should just release all the buffers from the wave device before
|
|
setting the command.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@rdesc Returns TRUE if the new commands do not affect recording and it should
|
|
continue, else FALSE if the new commands affect the recording, and it
|
|
should be aborted.
|
|
*/
|
|
|
|
REALLYPRIVATE BOOL PASCAL NEAR CheckNewCommand(
|
|
PWAVEDESC pwd)
|
|
{
|
|
if (ISMODE(pwd, COMMAND_STOP))
|
|
return FALSE;
|
|
if (ISMODE(pwd, COMMAND_INSERT))
|
|
ADDMODE(pwd, MODE_INSERT);
|
|
else
|
|
ADDMODE(pwd, MODE_OVERWRITE);
|
|
REMOVEMODE(pwd, COMMAND_NEW);
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func <t>HMMIO<d> | CreateSaveFile |
|
|
This function creates the file to which the current data is to be
|
|
saved to in RIFF format. This is either a temporary file created on
|
|
the same logical disk as the original file (so that this file can
|
|
replace the original file), else a new file.
|
|
|
|
The RIFF header and wave header chunks are written to the new file,
|
|
and the file position is at the start of the data to be copied. Note
|
|
that all the RIFF chunk headers will contain correct lengths, so there
|
|
is no need to ascend out of the data chunk when complete.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@parm SSZ | sszTempSaveFile |
|
|
Points to a buffer to contain the name of the temporary file created,
|
|
if any. This is zero length if a new file is to be created instead of
|
|
a temporary file that would replace the original file.
|
|
|
|
@rdesc Returns the handle to the save file, else NULL if a create error or
|
|
write error occurred.
|
|
|
|
@comm Note that this needs to be fixed so that non-DOS IO systems can save
|
|
the file to the original name by creating a temporary file name through
|
|
MMIO.
|
|
*/
|
|
|
|
PRIVATE HMMIO PASCAL NEAR CreateSaveFile(
|
|
PWAVEDESC pwd,
|
|
LPWSTR sszTempSaveFile)
|
|
{
|
|
MMIOINFO mmioInfo;
|
|
HMMIO hmmio;
|
|
LPWSTR lszFile;
|
|
|
|
InitMMIOOpen(pwd, &mmioInfo);
|
|
if (pwd->szSaveFile) {
|
|
*sszTempSaveFile = (char)0;
|
|
lszFile = pwd->szSaveFile;
|
|
} else {
|
|
lstrcpy(sszTempSaveFile, pwd->aszFile);
|
|
if (!mmioOpen(sszTempSaveFile, &mmioInfo, MMIO_GETTEMP)) {
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
return NULL;
|
|
}
|
|
lszFile = sszTempSaveFile;
|
|
}
|
|
if (0 != (hmmio = mmioOpen(lszFile, &mmioInfo, MMIO_CREATE | MMIO_READWRITE | MMIO_DENYWRITE))) {
|
|
MMCKINFO mmck;
|
|
|
|
mmck.cksize = sizeof(FOURCC) + sizeof(FOURCC) + sizeof(DWORD) + pwd->wFormatSize + sizeof(FOURCC) + sizeof(DWORD) + pwd->dSize;
|
|
if (pwd->wFormatSize & 1)
|
|
mmck.cksize++;
|
|
mmck.fccType = mmioWAVE;
|
|
if (!mmioCreateChunk(hmmio, &mmck, MMIO_CREATERIFF)) {
|
|
mmck.cksize = pwd->wFormatSize;
|
|
mmck.ckid = mmioFMT;
|
|
if (!mmioCreateChunk(hmmio, &mmck, 0) && (mmioWrite(hmmio, (LPSTR)pwd->pwavefmt, (LONG)pwd->wFormatSize) == (LONG)pwd->wFormatSize) && !mmioAscend(hmmio, &mmck, 0)) {
|
|
mmck.cksize = pwd->dSize;
|
|
mmck.ckid = mmioDATA;
|
|
if (!mmioCreateChunk(hmmio, &mmck, 0))
|
|
return hmmio;
|
|
}
|
|
}
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
mmioClose(hmmio, 0);
|
|
} else
|
|
pwd->wTaskError = MCIERR_FILE_NOT_SAVED;
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func VOID | mwSaveData |
|
|
This function is used by the background task to save the data to a
|
|
specified file. This has the effect of making all the temporary data
|
|
now original data, and removing any temporary data file.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@rdesc Nothing.
|
|
*/
|
|
|
|
PUBLIC VOID PASCAL FAR mwSaveData(
|
|
PWAVEDESC pwd)
|
|
{
|
|
LPBYTE lpbBuffer = NULL;
|
|
HANDLE hMem;
|
|
DWORD AllocSize = max(min(pwd->dAudioBufferLen, pwd->dSize),1);
|
|
|
|
// If there is no wave data, we still allocate 1 byte in order to save a NULL
|
|
// file. Otherwise we have no choice but to return an error saying "Out of memory"
|
|
hMem = GlobalAlloc(GMEM_MOVEABLE, AllocSize);
|
|
if (hMem) {
|
|
lpbBuffer = GlobalLock(hMem);
|
|
dprintf3(("mwSaveData allocated %d bytes at %8x, handle %8x",
|
|
AllocSize, lpbBuffer, hMem));
|
|
dprintf3(("pwd->AudioBufferLen = %d, pwd->dSize = %d",
|
|
pwd->dAudioBufferLen, pwd->dSize));
|
|
}
|
|
if (lpbBuffer) {
|
|
WCHAR aszTempSaveFile[_MAX_PATH];
|
|
HMMIO hmmioSave;
|
|
|
|
if (0 != (hmmioSave = CreateSaveFile(pwd, (SSZ)aszTempSaveFile))) {
|
|
LPWAVEDATANODE lpwdn;
|
|
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataStartNode);
|
|
for (;;) {
|
|
DWORD dDataLength;
|
|
BOOL fTempData;
|
|
|
|
fTempData = ISTEMPDATA(lpwdn);
|
|
if (fTempData)
|
|
_llseek(pwd->hfTempBuffers, UNMASKDATASTART(lpwdn), SEEK_SET);
|
|
else
|
|
mmioSeek(pwd->hmmio, pwd->dRiffData + lpwdn->dDataStart, SEEK_SET);
|
|
for (dDataLength = lpwdn->dDataLength; dDataLength;) {
|
|
DWORD dReadSize;
|
|
|
|
dReadSize = min(pwd->dAudioBufferLen, dDataLength);
|
|
|
|
if (dReadSize >= AllocSize) {
|
|
dprintf(("READING TOO MUCH DATA!!"));
|
|
}
|
|
|
|
if (fTempData) {
|
|
if ((DWORD)_lread(pwd->hfTempBuffers, lpbBuffer, (LONG)dReadSize) != dReadSize) {
|
|
pwd->wTaskError = MCIERR_FILE_READ;
|
|
break;
|
|
}
|
|
} else if ((DWORD)mmioRead(pwd->hmmio, lpbBuffer, (LONG)dReadSize) != dReadSize) {
|
|
pwd->wTaskError = MCIERR_FILE_READ;
|
|
break;
|
|
}
|
|
|
|
if ((DWORD)mmioWrite(hmmioSave, lpbBuffer, (LONG)dReadSize) != dReadSize) {
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
break;
|
|
}
|
|
dDataLength -= dReadSize;
|
|
}
|
|
if (pwd->wTaskError)
|
|
break;
|
|
if (lpwdn->dNextWaveDataNode == ENDOFNODES)
|
|
break;
|
|
lpwdn = LPWDN(pwd, lpwdn->dNextWaveDataNode);
|
|
}
|
|
mmioClose(hmmioSave, 0);
|
|
if (!pwd->wTaskError) {
|
|
MMIOINFO mmioInfo;
|
|
MMCKINFO mmckRiff;
|
|
MMCKINFO mmck;
|
|
|
|
if (pwd->hmmio)
|
|
mmioClose(pwd->hmmio, 0);
|
|
InitMMIOOpen(pwd, &mmioInfo);
|
|
if (pwd->szSaveFile)
|
|
lstrcpy(pwd->aszFile, pwd->szSaveFile);
|
|
else {
|
|
if (!mmioOpen(pwd->aszFile, &mmioInfo, MMIO_DELETE))
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
if (!pwd->wTaskError)
|
|
if (mmioRename(aszTempSaveFile, pwd->aszFile, &mmioInfo, 0)) {
|
|
lstrcpy(pwd->aszFile, aszTempSaveFile);
|
|
*aszTempSaveFile = (char)0;
|
|
}
|
|
}
|
|
pwd->hmmio = mmioOpen(pwd->aszFile, &mmioInfo, MMIO_READ | MMIO_DENYWRITE);
|
|
if (!pwd->wTaskError) {
|
|
LPWAVEDATANODE lpwdn;
|
|
|
|
mmckRiff.fccType = mmioWAVE;
|
|
mmioDescend(pwd->hmmio, &mmckRiff, NULL, MMIO_FINDRIFF);
|
|
mmck.ckid = mmioDATA;
|
|
mmioDescend(pwd->hmmio, &mmck, &mmckRiff, MMIO_FINDCHUNK);
|
|
pwd->dRiffData = mmck.dwDataOffset;
|
|
if (pwd->hfTempBuffers != HFILE_ERROR) {
|
|
|
|
_lclose(pwd->hfTempBuffers);
|
|
pwd->dWaveTempDataLength = 0;
|
|
|
|
DeleteFile( pwd->aszTempFile );
|
|
|
|
pwd->hfTempBuffers = HFILE_ERROR;
|
|
}
|
|
if (pwd->lpWaveDataNode) {
|
|
GlobalFreePtr(pwd->lpWaveDataNode);
|
|
pwd->lpWaveDataNode = NULL;
|
|
pwd->dWaveDataNodes = 0;
|
|
}
|
|
pwd->dVirtualWaveDataStart = 0;
|
|
pwd->dWaveDataCurrentNode = 0;
|
|
pwd->dWaveDataStartNode = 0;
|
|
mwAllocMoreBlockNodes(pwd);
|
|
lpwdn = LPWDN(pwd, 0);
|
|
lpwdn->dNextWaveDataNode = (DWORD)ENDOFNODES;
|
|
lpwdn->dDataLength = pwd->dSize;
|
|
lpwdn->dTotalLength = pwd->dSize;
|
|
if (!pwd->szSaveFile && !*aszTempSaveFile)
|
|
pwd->wTaskError = MCIERR_FILE_WRITE;
|
|
}
|
|
}
|
|
}
|
|
GlobalUnlock(hMem);
|
|
} else {
|
|
pwd->wTaskError = MCIERR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (hMem) {
|
|
GlobalFree(hMem);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func VOID | mwDeleteData |
|
|
This function is used by the background task to delete data.
|
|
And that was a crappy non-spec! Specs should say what it has to do!
|
|
|
|
I think it deletes data from pwd->dFrom upto but not including pwd->dTo.
|
|
????? Is there a pre-condition that pdw->dWaveDataCurrentNode must
|
|
????? point to the first node containing data to be deleted
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@rdesc Nothing.
|
|
*/
|
|
|
|
PUBLIC VOID PASCAL FAR mwDeleteData(
|
|
PWAVEDESC pwd)
|
|
{
|
|
DWORD dTotalToDelete;
|
|
LPWAVEDATANODE lpwdn;
|
|
LPWAVEDATANODE lpwdnCur;
|
|
DWORD dVirtualWaveDataStart;
|
|
DWORD dStartOffset; /* start of deletable section - start of block (<===> in picture)
|
|
** 0 for all except first node
|
|
*/
|
|
|
|
/*
|
|
** pwd->dWaveDataCurrentNode
|
|
** \
|
|
** \
|
|
** \ pwd->dVirtualWaveDataStart
|
|
** \ |
|
|
** \ |
|
|
** \ |pwd->dFrom pwd->dTo
|
|
** \ | | |
|
|
** ---------- ---------- ------------ -------------- ----------
|
|
** | | | <----| |------------| |--------> | | |
|
|
** ---------- ---------- ------------ -------------- ----------
|
|
** node node node node node
|
|
** <===>
|
|
** |
|
|
** dStartOffset
|
|
*/
|
|
|
|
|
|
/* ????? !! I'm woried about the WAVEDATANODE dDataStart fields.
|
|
?? Where do they get updated. Shouldn't ALL the nodes after the
|
|
?? deleted portion get this reduced by dDeleteLength
|
|
?? Does dDataStart refer to the position in the LOGICAL file (i.e. the
|
|
?? file made up by concatenating the sections identified by all the
|
|
?? bits that the WAVEDATANODEs identify, or does it refer to the PHYSICAL
|
|
?? position in one of the files (either the main or temporary file)
|
|
??
|
|
?? I'm pretty sure that this code is still broken.
|
|
*/
|
|
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataCurrentNode);
|
|
dTotalToDelete = pwd->dTo - pwd->dFrom;
|
|
|
|
if (dTotalToDelete == pwd->dSize) {
|
|
// The whole wave chunk is to be deleted - nice and simple
|
|
DWORD dNewDataNode;
|
|
|
|
if ((dNewDataNode = mwFindAnyFreeDataNode(pwd, 1)) == -1) {
|
|
dprintf2(("mwDeleteData - no free data node"));
|
|
return;
|
|
}
|
|
RELEASEBLOCKNODE(LPWDN(pwd, dNewDataNode));
|
|
}
|
|
|
|
dprintf3(("mwDeleteData - size to delete = %d", dTotalToDelete));
|
|
|
|
/* step lpwdn (already pointing at the current node) through nodes deleting
|
|
what's available until we have deleted dTotalToDelete. First and last
|
|
nodes visited may need special treatment. Don't delete before dFrom in first,
|
|
don't delete beyond the required amount in the last
|
|
*/
|
|
dVirtualWaveDataStart = pwd->dVirtualWaveDataStart;
|
|
dStartOffset = pwd->dFrom - dVirtualWaveDataStart;
|
|
while (dTotalToDelete>0) {
|
|
DWORD dDeleteLength;
|
|
DWORD dStartOff = dStartOffset; /* we need a copy of the old value of dStartoffset */
|
|
dStartOffset = 0;
|
|
|
|
dDeleteLength = min(dTotalToDelete, lpwdn->dDataLength - dStartOff);
|
|
dprintf4(("mwDelete dTotalToDelete = %d, dDeleteLength = %d", dTotalToDelete, dDeleteLength));
|
|
|
|
if (dDeleteLength==0) {
|
|
// Nothing to be deleted from this block
|
|
dprintf3(("mwDelete skipping to next block"));
|
|
dVirtualWaveDataStart += lpwdn->dDataLength;
|
|
lpwdn = LPWDN(pwd, lpwdn->dNextWaveDataNode);
|
|
continue; // iterate around the while loop
|
|
}
|
|
// Note: the block above is new to NT. Windows 3.1 as shipped fails.
|
|
// The problem can be seen with a wave file > 3 seconds long and
|
|
// the following two commands:
|
|
// delete wave from 1000 to 2000
|
|
// delete wave from 1000 to 2000
|
|
// Because of the fragmentation the second delete fails. It decided
|
|
// that NO data can be deleted from the first block, but never
|
|
// stepped on to the next block.
|
|
|
|
if (ISTEMPDATA(lpwdn)) {
|
|
dprintf3(("mwDeleteData - temporary data"));
|
|
if (dVirtualWaveDataStart + lpwdn->dDataLength <= pwd->dFrom + dTotalToDelete)
|
|
/* the delete goes to or beyond the end of this block */
|
|
lpwdn->dDataLength -= dDeleteLength; // Delete data in this block
|
|
|
|
else { /* the delete stops in the middle of this block */
|
|
|
|
DWORD dNewBlockNode;
|
|
DWORD dEndSplitPoint;
|
|
DWORD dMoveData;
|
|
|
|
dEndSplitPoint = min(ROUNDDATA(pwd, dStartOff + dDeleteLength), lpwdn->dDataLength);
|
|
if (dEndSplitPoint < lpwdn->dDataLength) {
|
|
if ((dNewBlockNode = mwFindAnyFreeBlockNode(pwd)) == -1)
|
|
break;
|
|
} else
|
|
dNewBlockNode = (DWORD)-1;
|
|
dMoveData = dEndSplitPoint - (dStartOff + dDeleteLength);
|
|
if (dMoveData && !CopyBlockData(pwd, lpwdn, lpwdn, dStartOff + dDeleteLength, dStartOff, dMoveData))
|
|
break;
|
|
if (dNewBlockNode != -1) {
|
|
lpwdnCur = LPWDN(pwd, dNewBlockNode);
|
|
lpwdnCur->dDataStart = lpwdn->dDataStart + dEndSplitPoint;
|
|
lpwdnCur->dDataLength = lpwdn->dDataLength - dEndSplitPoint;
|
|
lpwdnCur->dTotalLength = lpwdn->dTotalLength - dEndSplitPoint;
|
|
lpwdnCur->dNextWaveDataNode = lpwdn->dNextWaveDataNode;
|
|
lpwdn->dNextWaveDataNode = dNewBlockNode;
|
|
lpwdn->dTotalLength = dEndSplitPoint;
|
|
}
|
|
lpwdn->dDataLength = dStartOff + dMoveData;
|
|
}
|
|
} else if (0==dStartOff) {
|
|
// Deleting from the beginning of this node. We can
|
|
// simply adjust the total length and start point.
|
|
dprintf4(("mwDeleteData - From == Start, deleting from start of block"));
|
|
lpwdn->dDataStart += dDeleteLength;
|
|
lpwdn->dDataLength -= dDeleteLength;
|
|
lpwdn->dTotalLength = lpwdn->dDataLength;
|
|
} else if (dVirtualWaveDataStart + lpwdn->dDataLength <= pwd->dFrom + dTotalToDelete) {
|
|
// FROM point plus amount to delete takes us to the end of the wave
|
|
// data - meaning that the data block can be truncated. We can
|
|
// simply adjust the total length.
|
|
dprintf4(("mwDeleteData - delete to end of block"));
|
|
lpwdn->dDataLength -= dDeleteLength;
|
|
lpwdn->dTotalLength = lpwdn->dDataLength;
|
|
} else {
|
|
// We have to delete a chunk out of the middle.
|
|
DWORD dNewBlockNode;
|
|
|
|
// The existing single block will now be covered by two blocks
|
|
// Find a new node, then set the current node start->deletefrom
|
|
// and the new node deletefrom+deletelength for the remaining
|
|
// length of this node. It all hinges on finding a free node...
|
|
if ((dNewBlockNode = mwFindAnyFreeBlockNode(pwd)) == -1) {
|
|
dprintf2(("mwDeleteData - cannot find free node"));
|
|
break;
|
|
}
|
|
|
|
lpwdnCur = LPWDN(pwd, dNewBlockNode);
|
|
lpwdnCur->dDataStart = dVirtualWaveDataStart + dStartOff + dDeleteLength;
|
|
lpwdnCur->dDataLength = lpwdn->dDataLength - (dStartOff + dDeleteLength);
|
|
lpwdnCur->dTotalLength = lpwdnCur->dDataLength;
|
|
lpwdnCur->dNextWaveDataNode = lpwdn->dNextWaveDataNode;
|
|
lpwdn->dDataLength = dStartOff;
|
|
lpwdn->dTotalLength = dStartOff;
|
|
lpwdn->dNextWaveDataNode = dNewBlockNode;
|
|
}
|
|
dTotalToDelete -= dDeleteLength;
|
|
// old version: if (!lpwdn->dDataLength && dTotalToDelete) {
|
|
if (dTotalToDelete>0) {
|
|
dVirtualWaveDataStart += lpwdn->dDataLength;
|
|
lpwdn = LPWDN(pwd, lpwdn->dNextWaveDataNode);
|
|
dprintf4(("mwDeleteData - more to delete, iterating"));
|
|
}
|
|
}
|
|
|
|
|
|
/* Step through all nodes. process lpwdnCur, keep lpwdn pointing to the
|
|
previous node (initially NULL)
|
|
*/
|
|
pwd->dSize -= ((pwd->dTo - pwd->dFrom) + dTotalToDelete);
|
|
for (lpwdn = NULL, lpwdnCur = LPWDN(pwd, pwd->dWaveDataStartNode);;) {
|
|
if (!lpwdnCur->dDataLength) {
|
|
/* node is empty, so get rid of it */
|
|
if (lpwdn) {
|
|
/* take it out of the middle of the chain */
|
|
if (pwd->dWaveDataCurrentNode == lpwdn->dNextWaveDataNode)
|
|
pwd->dWaveDataCurrentNode = lpwdnCur->dNextWaveDataNode;
|
|
lpwdn->dNextWaveDataNode = lpwdnCur->dNextWaveDataNode;
|
|
} else {
|
|
/* take it out of the start of the chain */
|
|
if (pwd->dWaveDataCurrentNode == pwd->dWaveDataStartNode)
|
|
pwd->dWaveDataCurrentNode = lpwdnCur->dNextWaveDataNode;
|
|
pwd->dWaveDataStartNode = lpwdnCur->dNextWaveDataNode;
|
|
}
|
|
RELEASEBLOCKNODE(lpwdnCur);
|
|
}
|
|
if (lpwdnCur->dNextWaveDataNode == ENDOFNODES){
|
|
break;
|
|
}
|
|
lpwdn = lpwdnCur;
|
|
lpwdnCur = LPWDN(pwd, lpwdn->dNextWaveDataNode);
|
|
}
|
|
|
|
if (!pwd->dSize) {
|
|
|
|
pwd->dWaveDataStartNode = mwFindAnyFreeDataNode(pwd, 1);
|
|
pwd->dWaveDataCurrentNode = pwd->dWaveDataStartNode;
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataStartNode);
|
|
lpwdn->dNextWaveDataNode = (DWORD)ENDOFNODES;
|
|
|
|
} else if (pwd->dWaveDataCurrentNode == ENDOFNODES) {
|
|
|
|
pwd->dVirtualWaveDataStart = 0;
|
|
pwd->dWaveDataCurrentNode = pwd->dWaveDataStartNode;
|
|
|
|
for (lpwdn = LPWDN(pwd, pwd->dWaveDataStartNode); pwd->dFrom > pwd->dVirtualWaveDataStart + lpwdn->dDataLength;) {
|
|
|
|
pwd->dVirtualWaveDataStart += lpwdn->dDataLength;
|
|
pwd->dWaveDataCurrentNode = lpwdn->dNextWaveDataNode;
|
|
lpwdn = LPWDN(pwd, pwd->dWaveDataCurrentNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/*
|
|
@doc INTERNAL MCIWAVE
|
|
|
|
@func UINT | RecordFile |
|
|
This function is used to Cue or Record wave device input. For normal
|
|
recording mode the function basically queues buffers on the wave
|
|
device, and writes them to a file as they are filled, blocking for
|
|
each buffer. It also makes sure to call <f>mmYield<d> while both
|
|
writing out new buffers, and waiting for buffers to be filled. This
|
|
means that it will try to add all the buffers possible to the input
|
|
wave device, and then write them as fast as possible.
|
|
|
|
For Cue mode, the function also tries to add buffers to the wave
|
|
input device, but nothing is ever written out, and only the highest
|
|
level is calculated.
|
|
|
|
Within the record loop, the function first checks to see if there
|
|
is a Cue mode buffer waiting, and if so, waits for it. This allows
|
|
only one buffer to be added to the device when in Cue mode. The
|
|
current level is calculated with the contents of the buffer.
|
|
|
|
If the function is not in Cue mode, or there is not currently a
|
|
queued buffer, the function tries to add a new buffer to the input
|
|
wave device. This cannot occur if a new command is pending, or there
|
|
are no buffers available. This means that in normal recording mode,
|
|
there will possibly be extra data recorded that does not need to be.
|
|
If an error occurs adding the buffer to the wave device, the record
|
|
function is aborted with an error, else the current outstanding buffer
|
|
count is incremented, and a pointer to the next available recording
|
|
buffer is fetched.
|
|
|
|
If no new buffers can be added, the existing buffers are written to
|
|
the file. This section cannot be entered in Cue mode, as it is
|
|
dealt with in the first condition. The task is blocked pending a
|
|
signal from the wave device that a buffer has been filled. It then
|
|
checks to see if any more data needs to be recorded before attempting
|
|
to write that data. Note that all filled buffers are dealt with one
|
|
after the other without yielding or otherwise adding new record
|
|
buffers. If the input capability is much faster than the machine,
|
|
this means that instead of getting a lot of disconnect samples, large
|
|
gaps will be produced. This loop is broken out of when either all the
|
|
buffers that were added are written, or no more buffers are currently
|
|
ready (checks the WHDR_DONE flag).
|
|
|
|
If no buffers need to be written, the loop checks for the new command
|
|
flag, which can possibly interrupt or change the current recording.
|
|
The only thing that can really make a difference is a stop command,
|
|
and as this case is handled after all buffers are written, the loop
|
|
can immediately exit.
|
|
|
|
The final default condition occurs when all the data has been recorded,
|
|
all the buffers have been released, and no new command was encountered.
|
|
In this case, recording is done, and the record loop is exited.
|
|
|
|
@parm <t>PWAVEDESC<d> | pwd |
|
|
Pointer to the wave device descriptor.
|
|
|
|
@rdesc Returns the number of outstanding buffers added to the wave device.
|
|
This can be used when removing task signal from the message queue.
|
|
In cases of error, the <e>WAVEDESC.wTaskError<d> flag is set. This
|
|
specific error is not currently returned, as the calling task may not
|
|
have waited for the command to complete. But it is at least used for
|
|
notification in order to determine if Failure status should be sent.
|
|
|
|
@xref PlayFile.
|
|
*/
|
|
|
|
PUBLIC UINT PASCAL FAR RecordFile(
|
|
register PWAVEDESC pwd)
|
|
{
|
|
LPWAVEHDR *lplpWaveHdrRecord;
|
|
LPWAVEHDR *lplpWaveHdrWrite;
|
|
UINT wMode;
|
|
register UINT wBuffersOutstanding;
|
|
|
|
if (0 != (pwd->wTaskError = waveInStart(pwd->hWaveIn)))
|
|
return 0;
|
|
|
|
for (wBuffersOutstanding = 0, lplpWaveHdrRecord = lplpWaveHdrWrite = pwd->rglpWaveHdr;;) {
|
|
|
|
if (ISMODE(pwd, COMMAND_CUE) && wBuffersOutstanding) {
|
|
if (TaskBlock() == WM_USER) {
|
|
wBuffersOutstanding--;
|
|
}
|
|
|
|
if (!ISMODE(pwd, COMMAND_NEW)) {
|
|
pwd->dLevel = mwGetLevel(pwd, (*lplpWaveHdrWrite)->lpData, (int)(*lplpWaveHdrWrite)->dwBytesRecorded);
|
|
ADDMODE(pwd, MODE_CUED);
|
|
}
|
|
|
|
lplpWaveHdrWrite = NextWaveHdr(pwd, lplpWaveHdrWrite);
|
|
|
|
} else if (!ISMODE(pwd, COMMAND_NEW) && (wBuffersOutstanding < pwd->wAudioBuffers)) {
|
|
|
|
(*lplpWaveHdrRecord)->dwBufferLength = (wMode & COMMAND_CUE) ? NUM_LEVEL_SAMPLES : min(pwd->dAudioBufferLen, pwd->dTo - pwd->dCur);
|
|
(*lplpWaveHdrRecord)->dwFlags &= ~(WHDR_DONE | WHDR_BEGINLOOP | WHDR_ENDLOOP);
|
|
if (0 != (pwd->wTaskError = waveInAddBuffer(pwd->hWaveIn, *lplpWaveHdrRecord, sizeof(WAVEHDR))))
|
|
break;
|
|
|
|
wBuffersOutstanding++;
|
|
lplpWaveHdrRecord = NextWaveHdr(pwd, lplpWaveHdrRecord);
|
|
|
|
} else if (wBuffersOutstanding) {
|
|
|
|
BOOL fExitRecording;
|
|
|
|
for (fExitRecording = FALSE; wBuffersOutstanding && !fExitRecording;) {
|
|
|
|
if (TaskBlock() == WM_USER) {
|
|
wBuffersOutstanding--;
|
|
}
|
|
if (pwd->dTo == pwd->dCur) {
|
|
fExitRecording = TRUE;
|
|
break;
|
|
}
|
|
if (!(wMode & COMMAND_CUE))
|
|
if (wMode & MODE_INSERT) {
|
|
if (!mwInsert(pwd, (LPBYTE)(*lplpWaveHdrWrite)->lpData, min((*lplpWaveHdrWrite)->dwBytesRecorded, pwd->dTo - pwd->dCur)))
|
|
fExitRecording = TRUE;
|
|
} else if (!mwOverWrite(pwd, (LPBYTE)(*lplpWaveHdrWrite)->lpData, min((*lplpWaveHdrWrite)->dwBytesRecorded, pwd->dTo - pwd->dCur)))
|
|
fExitRecording = TRUE;
|
|
lplpWaveHdrWrite = NextWaveHdr(pwd, lplpWaveHdrWrite);
|
|
if (!((*lplpWaveHdrWrite)->dwFlags & WHDR_DONE))
|
|
break;
|
|
}
|
|
|
|
if (fExitRecording)
|
|
break;
|
|
|
|
} else if (!ISMODE(pwd, COMMAND_NEW) || !CheckNewCommand(pwd))
|
|
break;
|
|
else
|
|
wMode = GETMODE(pwd);
|
|
|
|
mmYield(pwd);
|
|
}
|
|
REMOVEMODE(pwd, MODE_INSERT | MODE_OVERWRITE);
|
|
return wBuffersOutstanding;
|
|
}
|
|
|
|
/************************************************************************/
|