Update of /cvsroot/audacity/lib-src/portburn
In directory sc8-pr-cvs11.sourceforge.net:/tmp/cvs-serv22057

Added Files:
        Makefile.macosx clip.wav portburn.h portburn.sln 
        portburn.vcproj portburn_macosx.c portburn_staging.c 
        portburn_staging.h portburn_winxp.cpp test_portburn.cpp 
Log Message:
Checking in portburn

--- NEW FILE: portburn_winxp.cpp ---
/*
 * PortBurn
 * Windows XP IMAPI implementation
 *
 * Authors:
 *   Dominic Mazzoni
 *
 * The following MSDN page was used as a guide:
 *   http://msdn2.microsoft.com/en-us/library/aa366236.aspx
 *
 * License: LGPL
 */

#include "portburn.h"

#define _WIN32_WINNT 0x0400

#include "windows.h"
#include "tchar.h"
#include "malloc.h"
#include "imapi.h"
#include "stdio.h"

#define DEBUG(a) printf a

typedef struct {
   HRESULT                     hres;
   IStream                    *pStream;
   IDiscMaster                *pIDiscMaster;
   IRedbookDiscMaster         *pIRedbookDiscMaster;
   IEnumDiscRecorders         *pEnumDiscRecorders;
   IDiscRecorder              *pDiscRecorder;
   IDiscMasterProgressEvents  *piEvents;
   HANDLE                      hThread;
   int                         burning;
   int                         cancel;
   float                       fraction;
   UINT_PTR                    pnCookie;
   int                         simulate;
   int                         ejectAfterBurn;
} PBHandle;

void *PortBurn_Open()
{   
   PBHandle *h;

   h = (PBHandle *) HeapAlloc(GetProcessHeap(),
                              HEAP_ZERO_MEMORY,
                              sizeof(PBHandle));
   if (h == NULL) {
      return NULL;
   }

   CoInitializeEx(NULL, COINIT_MULTITHREADED);

   h->hres = CoCreateInstance(__uuidof(MSDiscMasterObj), 
//CLSID_MSDiscMasterObj,
                              NULL,
                              CLSCTX_ALL,
                              IID_IDiscMaster,
                              (void **)&h->pIDiscMaster);
   if (FAILED(h->hres)) {
      free(h);
      return NULL;
   }

   h->hres = CoMarshalInterThreadInterfaceInStream(IID_IDiscMaster,
                                                   h->pIDiscMaster,
                                                   &h->pStream);
   if (FAILED(h->hres)) {
      free(h);
      return NULL;
   }

   h->hres = h->pIDiscMaster->Open();
   if (h->hres != S_OK) {
      free(h);
      return NULL;
   }

   /* Is it necessary to call EnumDiscMasterFormats?  We know we only
      want Redbook, so let's just try to open it. */
   IEnumDiscMasterFormats *pp;
   h->hres = h->pIDiscMaster->EnumDiscMasterFormats(&pp);
   h->hres = h->pIDiscMaster->SetActiveDiscMasterFormat
      (IID_IRedbookDiscMaster, (void **)&h->pIRedbookDiscMaster);
   if (h->hres != S_OK) {
      h->pIDiscMaster->Close();
      free(h);
      return NULL;
   }

   return h;
}

/* Cleanup */
void PortBurn_Close(void *handle) {
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return;

   if (h->pEnumDiscRecorders)
      h->pEnumDiscRecorders->Release();

   h->pIDiscMaster->Close();
   h->pIDiscMaster->Release();

   CoUninitialize();

   HeapFree(GetProcessHeap(), 0, h);
}


/* Return a human-readable error string for the last operating system
   specific error (NOT human readable strings for the PortBurn error
   codes).  Caller should dispose of the returned string using free(). */
const char *PortBurn_LastError(void *handle)
{
   PBHandle *h = (PBHandle *)handle;
   HRESULT hr;
   LPTSTR windowsErrorString;
   char *errorString;
   size_t len;

   if (!h)
      return NULL;

   /* Have Windows allocate a buffer for us and format the error
      message in windowsErrorString */
   hr = h->hres;
   if (HRESULT_FACILITY(hr) == FACILITY_WINDOWS)
      hr = HRESULT_CODE(hr);
   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                 NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                 (LPTSTR)&windowsErrorString, 0, NULL);

   /* Allocate a buffer of the same size using malloc() and copy the
      string, then free the one Windows gave us */
   len = strlen((char *)windowsErrorString);
   errorString = (char *)malloc(+1);
   strcpy_s(errorString, len, (char *)windowsErrorString);
   LocalFree(windowsErrorString);

   return errorString;
}

/* Get the number of devices capable of burning audio CDs.
   If the result is N, then calls to GetDeviceName and OpenDevice
   are guaranteed to be valid for indices from 0 up to N-1, until
   the next time you call GetNumDevices.  At that point, the list of
   devices will be rescanned, and may be different. */
int PortBurn_GetNumDevices(void *handle)
{
   PBHandle *h = (PBHandle *)handle;
   IDiscRecorder *pDiscRecorder;
   ULONG fetched;
   int count = 0;


   if (!h)
      return NULL;

   if (h->pEnumDiscRecorders) {
      h->pEnumDiscRecorders->Release();
      h->pEnumDiscRecorders = NULL;
   }

   h->hres = h->pIDiscMaster->EnumDiscRecorders(&h->pEnumDiscRecorders);
   if (h->hres != S_OK)
      return 0;

   while(S_OK == h->pEnumDiscRecorders->Next(1, &pDiscRecorder, &fetched)) {
      pDiscRecorder->Release();
      count++;
   }

   return count;
}

/* Get the name of the device with a given index.  Only valid
   after a call to GetNumDevices. */
char *PortBurn_GetDeviceName(void *handle, int index)
{
   PBHandle *h = (PBHandle *)handle;
   IDiscRecorder *pDiscRecorder;
   BSTR bVendor = NULL;
   BSTR bProduct = NULL;
   BSTR bRevision = NULL;
   ULONG fetched;
   int len;
   wchar_t *wname;
   char *name;

   if (!h)
      return NULL;

   h->hres = S_OK;

   if (!h->pEnumDiscRecorders)
      return NULL;

   h->pEnumDiscRecorders->Reset();
   if (index != 0) {
      h->pEnumDiscRecorders->Skip(index - 1);
   }
   h->hres = h->pEnumDiscRecorders->Next(1, &pDiscRecorder, &fetched);
   if (h->hres != S_OK)
      return NULL;

   h->hres = pDiscRecorder->GetDisplayNames
      (&bVendor, &bProduct, &bRevision);
   pDiscRecorder->Release();
   if (h->hres != S_OK)
      return NULL;

   len = SysStringLen(bVendor) + 1 + SysStringLen(bProduct) + 1 +
      SysStringLen(bRevision);

   wname = (LPWSTR)alloca((len + 1)*sizeof(wchar_t));
   if (wname == NULL)
      return NULL;

   _stprintf_s(wname,
               (len + 1),
               _T("%s %s %s"),
               (LPCWSTR) bVendor,
               (LPCWSTR) bProduct,
               (LPCWSTR) bRevision);

   name = (char *)malloc(len + 1);
   if (name == NULL)
      return NULL;

   WideCharToMultiByte(CP_ACP, 0, wname, -1, name, len, NULL, NULL);
   name[len] = '\0';

   return name;
}

class PortBurnProgress : public IDiscMasterProgressEvents
{
 public:
   static HRESULT STDMETHODCALLTYPE
      CreateInstance(IDiscMasterProgressEvents** ppEvents);

   void SetPortBurnHandle(PBHandle *h);

 private:
   PortBurnProgress();

   PBHandle *h;

 protected:
   STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppv);
   STDMETHOD_(ULONG, AddRef)(VOID);
   STDMETHOD_(ULONG, Release)(VOID);

 protected:
   STDMETHOD(QueryCancel)(boolean *pbCancel);
   STDMETHOD(NotifyPnPActivity)(VOID);
   STDMETHOD(NotifyAddProgress)(LONG nCompletedSteps, LONG nTotalSteps);
   STDMETHOD(NotifyBlockProgress)(LONG nCompleted, LONG nTotal);
   STDMETHOD(NotifyTrackProgress)(LONG nCurrentTrack, LONG nTotalTracks);
   STDMETHOD(NotifyPreparingBurn)(LONG nEstimatedSeconds);
   STDMETHOD(NotifyClosingDisc)(LONG nEstimatedSeconds);
   STDMETHOD(NotifyBurnComplete)(HRESULT status);
   STDMETHOD(NotifyEraseComplete)(HRESULT status);

 private:
   ULONG m_cRefs;
};

PortBurnProgress::PortBurnProgress():
   h(0), m_cRefs(0)
{
}

void PortBurnProgress::SetPortBurnHandle(PBHandle *h)
{
   this->h = h;
}

STDMETHODIMP
PortBurnProgress::CreateInstance(IDiscMasterProgressEvents
                                            **ppEvents)
{
   HRESULT hr = E_NOINTERFACE;
   if (!(ppEvents))
      hr = E_POINTER;
   else
   {
      PortBurnProgress* pThis = new
         PortBurnProgress();
      hr = pThis->QueryInterface(IID_IDiscMasterProgressEvents,
                                 (VOID**)ppEvents);
   }

   return hr;
}

STDMETHODIMP PortBurnProgress::QueryInterface(REFIID riid,
                                                         LPVOID* ppv)
{
   HRESULT hr = E_NOINTERFACE;
   if (!(ppv))
      hr = E_POINTER;
   else
   {
      *ppv = NULL;

      if ((IID_IUnknown == riid) || (IID_IDiscMasterProgressEvents == riid))
      {
         *ppv = this;
         AddRef();
         hr = S_OK;
      }
   }
   return hr;
}

STDMETHODIMP_(ULONG) PortBurnProgress::AddRef(VOID)
{
   return InterlockedIncrement((LONG*)&m_cRefs);
}

STDMETHODIMP_(ULONG) PortBurnProgress::Release(VOID)
{
//   ASSERT(0 != m_cRefs);
   ULONG cRef = InterlockedDecrement((LONG*)&m_cRefs);
   if (0 == cRef)
   {
      delete this;
   }
   return cRef;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::QueryCancel(boolean *pbCancel)
{
//   DEBUG(("QueryCancel() -> %d\n", h->cancel));
   *pbCancel = h->cancel;
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyPnPActivity(VOID)
{
   DEBUG(("NotifyPnActivity\n"));
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyAddProgress(
    LONG nCompletedSteps,
    LONG nTotalSteps)
{
//   DEBUG(("NotifyAddProgress %d %d\n", nCompletedSteps, nTotalSteps));
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyBlockProgress(LONG nCompleted,
                                                             LONG nTotal)
{
   DEBUG(("NotifyBlockProgress %d %d\n", nCompleted, nTotal));

   /* This should never actually reach 1.0; we wait until the BurnComplete
      message for a 1.0 */
   h->fraction = nCompleted / (float)(nTotal + 1);
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyTrackProgress(
    LONG nCurrentTrack,
    LONG nTotalTracks)
{
   DEBUG(("NotifyTrackProgress %d %d\n", nCurrentTrack, nTotalTracks));
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyPreparingBurn
                                              (LONG nEstimatedSeconds)
{
   DEBUG(("NotifyPreparingBurn %d\n", nEstimatedSeconds));
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyClosingDisc
                                              (LONG nEstimatedSeconds)
{
   DEBUG(("NotifyClosingDisc %d\n", nEstimatedSeconds));
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyBurnComplete(HRESULT status)
{
   DEBUG(("NotifyBurnComplete %08x\n", status));
   h->fraction = 1.0;
   return S_OK;
}

STDMETHODIMP_(HRESULT) PortBurnProgress::NotifyEraseComplete(HRESULT status)
{
   DEBUG(("NotifyEraseComplete %08x\n", status));
   return S_OK;
}

/* Recording Thread */
DWORD WINAPI PortBurn_RecordDisc(LPVOID lpParam)
{
   PBHandle *h = (PBHandle *)lpParam;
   IDiscMaster *pIDiscMaster;

   h->hres = CoInitializeEx(0, COINIT_MULTITHREADED);
   if (SUCCEEDED(h->hres)) {
      h->hres = CoGetInterfaceAndReleaseStream(h->pStream,
                                               IID_IDiscMaster,
                                               (void**) &pIDiscMaster);
      if (SUCCEEDED(h->hres)) {
         h->hres = pIDiscMaster->RecordDisc(h->simulate,
                                            h->ejectAfterBurn);
      }
   }

   h->fraction = 1.0;

   CoUninitialize();

   if (FAILED(h->hres))
      return pbErrCannotStartBurning;
 
   return pbSuccess;
}

/* Open a particular device by index number.  Returns 0 on success;
   any nonzero value indicates an error, for example if the device is
   already open by some other program. */
int PortBurn_OpenDevice(void *handle, int index)
{
   ULONG fetched;
   PBHandle *h = (PBHandle *)handle;
   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pEnumDiscRecorders)
      return pbErrMustCallGetNumDevices;

   h->pEnumDiscRecorders->Reset();
   if (index != 0) {
      h->pEnumDiscRecorders->Skip(index - 1);
   }

   h->hres = h->pEnumDiscRecorders->Next(1, &h->pDiscRecorder, &fetched);
   if (h->hres != S_OK)
      return pbErrCannotAccessDevice;

   h->hres = h->pIDiscMaster->SetActiveDiscRecorder(h->pDiscRecorder);
   if (h->hres != S_OK) {
      h->pDiscRecorder->Release();
      h->pDiscRecorder = NULL;
      return pbErrCannotReserveDevice;
   }

   h->hres = h->pDiscRecorder->OpenExclusive();
   if (h->hres != S_OK) {
      h->pDiscRecorder->Release();
      h->pDiscRecorder = NULL;
      return pbErrCannotReserveDevice;
   }
   return pbSuccess;
}

/* Close a device */
int PortBurn_CloseDevice(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   h->burning = 0;
   h->cancel = 0;
   h->fraction = 0.0;
   if (h->hThread) {
      CloseHandle(h->hThread);
      h->hThread = NULL;
   }

   if (h->piEvents) {
      h->pIDiscMaster->ProgressUnadvise(h->pnCookie);
      h->piEvents->Release();
      h->piEvents = NULL;
   }

   h->hres = h->pDiscRecorder->Close();
   h->pDiscRecorder->Release();
   h->pDiscRecorder = NULL;

   if (h->hres != S_OK)
      return pbErrCannotCloseDevice;

   return pbSuccess;
}

/* Eject the media in the currently opened device */
int PortBurn_EjectDevice(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   h->hres = h->pDiscRecorder->Eject();
   if (S_OK != h->hres)
      return pbErrCannotEject;

   return pbSuccess;
}

/* Erase the media in the currently opened device */
int PortBurn_EraseDevice(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   h->hres = h->pDiscRecorder->Erase(false);
   if (S_OK != h->hres)
      return pbErrCannotEject;

   return pbSuccess;
}

/* This indicates you're ready to start staging audio data for the
   currently opened device.  At this point you are committing to
   exclusive access to the CD burner, and this is the function that
   will fail if another program is using the device, or if there is
   no writable CD media in the device at this point. */
int PortBurn_StartStaging(void *handle, const char *tmpdir)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   if (h->piEvents)
      return pbErrAlreadyStagingOrBurning;

//   h->hres = h->pDiscRecorder->OpenExclusive();
//   if (h->hres != S_OK)
//      return pbErrCannotReserveDevice;

   h->piEvents = NULL;
   h->hres = PortBurnProgress::CreateInstance(&h->piEvents);
   if (h->hres != S_OK) {
      h->pDiscRecorder->Close();
      return pbErrCannotPrepareToBurn;
   }

   ((PortBurnProgress *)h->piEvents)->SetPortBurnHandle(h);

   h->hres = h->pIDiscMaster->ProgressAdvise(h->piEvents,
                                             &h->pnCookie);
   if (h->hres != S_OK) {
      h->piEvents->Release();
      h->pDiscRecorder->Close();
      return pbErrCannotPrepareToBurn;
   }

   return pbSuccess;
}

/* Start a new audio track.  Pass the name of the track, and the
   length in CD Audio frames (each frame is 1/75.0 of a second, exactly). */
int PortBurn_StartTrack(void *handle, const char *name, int frames)
{
   PBHandle *h = (PBHandle *)handle;
   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   // this fails if the disc isn't writable...
   h->hres = h->pIRedbookDiscMaster->CreateAudioTrack(frames);
   if (S_OK != h->hres)
      return pbErrCannotStageTrack;

   return pbSuccess;
}

/* Add one frame of audio to the current track.  The buffer must be exactly
   1176 elements long, containing interleaved left and right audio samples.
   The values should be signed 16-bit numbers in the native endianness of
   this computer. */
int PortBurn_AddFrame(void *handle, short *buffer)
{
   PBHandle *h = (PBHandle *)handle;
   int oneBlockByteCount = 1176 * 2;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   h->hres = h->pIRedbookDiscMaster->AddAudioTrackBlocks((byte *)buffer,
                                                         oneBlockByteCount);
   if (S_OK != h->hres)
      return pbErrCannotWriteToStagingFile;

   return pbSuccess;
}

/* Finish the current audio track. */
int PortBurn_EndTrack(void *handle)
{
   PBHandle *h = (PBHandle *)handle;
   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   h->hres = h->pIRedbookDiscMaster->CloseAudioTrack();
   if (S_OK != h->hres)
      return pbErrCannotUseStagedFileForBurning;

   return pbSuccess;
}

/* Begin burning the disc. */
int PortBurn_StartBurning(void *handle)
{
   PBHandle *h = (PBHandle *)handle;
   DWORD dwID;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   h->hres = h->pDiscRecorder->Close();

   h->cancel = 0;
   h->fraction = 0.0;

   h->simulate = 0;
   h->ejectAfterBurn = 1;

   h->hThread = CreateThread(NULL,
                             0,
                             PortBurn_RecordDisc,
                             h,
                             0,
                             &dwID);
   if (h->hThread == NULL)
      return pbErrCannotStartBurning;

   h->burning = 1;

   return pbSuccess;
}

/* Cancel if burning was in progress.  It might take a while for
   this to take effect; wait until GetStatus says 1.0 to close
   the device. */
int PortBurn_CancelBurning(void *handle)
{
   PBHandle *h = (PBHandle *)handle;
   float frac = 0.0;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   if (!h->burning)
      return pbErrNotCurrentlyBurning;

   h->cancel = 1;

   return pbSuccess;
}

/* During burning, returns the fraction complete in the given
   float pointer, from 0.0 to 1.0.  If this function returns
   nonzero, the disc burning has failed and should be aborted.
   If *out_fraction_complete is equal to 1.0, the burning is done;
   you can call PortBurn_CloseDevice.
*/
int PortBurn_GetStatus(void *handle, float *out_fraction_complete)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;
   h->hres = S_OK;

   if (!h->pDiscRecorder)
      return pbErrDeviceNotOpen;

   if (!h->burning)
      return pbErrNotCurrentlyBurning;

   WaitForSingleObject(h->hThread, 100);

   *out_fraction_complete = h->fraction;

   return pbSuccess;
}

--- NEW FILE: portburn.vcproj ---
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
        ProjectType="Visual C++"
        Version="8.00"
        Name="portburn"
        ProjectGUID="{1FD542A5-859B-4514-8632-CEF42DA3882A}"
        RootNamespace="portburn"
        Keyword="Win32Proj"
        >
        <Platforms>
                <Platform
                        Name="Win32"
                />
        </Platforms>
        <ToolFiles>
        </ToolFiles>
        <Configurations>
                <Configuration
                        Name="Debug|Win32"
                        OutputDirectory="$(SolutionDir)$(ConfigurationName)"
                        IntermediateDirectory="$(ConfigurationName)"
                        ConfigurationType="1"
                        CharacterSet="1"
                        >
                        <Tool
                                Name="VCPreBuildEventTool"
                        />
                        <Tool
                                Name="VCCustomBuildTool"
                        />
                        <Tool
                                Name="VCXMLDataGeneratorTool"
                        />
                        <Tool
                                Name="VCWebServiceProxyGeneratorTool"
                        />
                        <Tool
                                Name="VCMIDLTool"
                        />
                        <Tool
                                Name="VCCLCompilerTool"
                                Optimization="0"
                                PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
                                MinimalRebuild="true"
                                BasicRuntimeChecks="3"
                                RuntimeLibrary="1"
                                UsePrecompiledHeader="0"
                                AssemblerOutput="3"
                                GenerateXMLDocumentationFiles="true"
                                WarningLevel="3"
                                Detect64BitPortabilityProblems="true"
                                DebugInformationFormat="4"
                                CompileAs="0"
                        />
                        <Tool
                                Name="VCManagedResourceCompilerTool"
                        />
                        <Tool
                                Name="VCResourceCompilerTool"
                        />
                        <Tool
                                Name="VCPreLinkEventTool"
                        />
                        <Tool
                                Name="VCLinkerTool"
                                LinkIncremental="2"
                                GenerateDebugInformation="true"
                                SubSystem="1"
                                TargetMachine="1"
                        />
                        <Tool
                                Name="VCALinkTool"
                        />
                        <Tool
                                Name="VCManifestTool"
                        />
                        <Tool
                                Name="VCXDCMakeTool"
                        />
                        <Tool
                                Name="VCBscMakeTool"
                        />
                        <Tool
                                Name="VCFxCopTool"
                        />
                        <Tool
                                Name="VCAppVerifierTool"
                        />
                        <Tool
                                Name="VCWebDeploymentTool"
                        />
                        <Tool
                                Name="VCPostBuildEventTool"
                        />
                </Configuration>
                <Configuration
                        Name="Release|Win32"
                        OutputDirectory="$(SolutionDir)$(ConfigurationName)"
                        IntermediateDirectory="$(ConfigurationName)"
                        ConfigurationType="1"
                        CharacterSet="1"
                        WholeProgramOptimization="1"
                        >
                        <Tool
                                Name="VCPreBuildEventTool"
                        />
                        <Tool
                                Name="VCCustomBuildTool"
                        />
                        <Tool
                                Name="VCXMLDataGeneratorTool"
                        />
                        <Tool
                                Name="VCWebServiceProxyGeneratorTool"
                        />
                        <Tool
                                Name="VCMIDLTool"
                        />
                        <Tool
                                Name="VCCLCompilerTool"
                                PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
                                RuntimeLibrary="2"
                                UsePrecompiledHeader="0"
                                WarningLevel="3"
                                Detect64BitPortabilityProblems="true"
                                DebugInformationFormat="3"
                        />
                        <Tool
                                Name="VCManagedResourceCompilerTool"
                        />
                        <Tool
                                Name="VCResourceCompilerTool"
                        />
                        <Tool
                                Name="VCPreLinkEventTool"
                        />
                        <Tool
                                Name="VCLinkerTool"
                                LinkIncremental="1"
                                GenerateDebugInformation="true"
                                SubSystem="1"
                                OptimizeReferences="2"
                                EnableCOMDATFolding="2"
                                TargetMachine="1"
                        />
                        <Tool
                                Name="VCALinkTool"
                        />
                        <Tool
                                Name="VCManifestTool"
                        />
                        <Tool
                                Name="VCXDCMakeTool"
                        />
                        <Tool
                                Name="VCBscMakeTool"
                        />
                        <Tool
                                Name="VCFxCopTool"
                        />
                        <Tool
                                Name="VCAppVerifierTool"
                        />
                        <Tool
                                Name="VCWebDeploymentTool"
                        />
                        <Tool
                                Name="VCPostBuildEventTool"
                        />
                </Configuration>
        </Configurations>
        <References>
        </References>
        <Files>
                <Filter
                        Name="Source Files"
                        Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
                        
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
                        >
                        <File
                                RelativePath=".\portburn_winxp.cpp"
                                >
                        </File>
                        <File
                                RelativePath=".\test_portburn.cpp"
                                >
                        </File>
                </Filter>
                <Filter
                        Name="Header Files"
                        Filter="h;hpp;hxx;hm;inl;inc;xsd"
                        
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
                        >
                        <File
                                RelativePath=".\portburn.h"
                                >
                        </File>
                </Filter>
        </Files>
        <Globals>
        </Globals>
</VisualStudioProject>

--- NEW FILE: portburn.sln ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: portburn_staging.h ---
/*
 * PortBurn
 * Common utilities for staging audio files in a temporary directory.
 *
 * Dominic Mazzoni
 * License: LGPL
 *
 * A library for cross-platform audio CD burning
 *
 * On some of the platforms supported by PortBurn, we need to stage
 * the audio data ourselves.  This file defines an interface to the
 * common functions.
 */

#ifndef __PORTBURN_STAGING__
#define __PORTBURN_STAGING__

void *PortBurn_TempDirStaging(const char *temporary_directory);

int PortBurn_StartStagingTrack(void *handle, const char *name, int frames);
int PortBurn_AddStagingFrame(void *handle, short *buffer);
int PortBurn_EndStagingTrack(void *handle);

int PortBurn_FinishStaging(void *handle);

int PortBurn_GetNumStagedTracks(void *handle);
const char *PortBurn_GetStagedFilename(void *handle, int index);
const char *PortBurn_GetStagedTrackNname(void *handle, int index);
int PortBurn_GetStagedLengthInFrames(void *handle, int index);

void PortBurn_CleanupStaging(void *handle);

#endif /* __PORTBURN_STAGING__ */

--- NEW FILE: portburn.h ---
/*
 * PortBurn
 *
 * Dominic Mazzoni
 * License: LGPL
 *
 * A library for cross-platform audio CD burning
 *
 * All functions return 0 on success and nonzero when there's an error,
 * unless otherwise specified.  The error codes will come from the enum
 * below.  When one of the CD burning or filesystem errors was returned,
 * you might be able to call PortBurn_LastError to get operating-system
 * specific information about what went wrong.  If PortBurn_LastError
 * returns no error, no additional information was available.
 */

#ifndef __PORTBURN__
#define __PORTBURN__

typedef enum {
   pbSuccess = 0,

   /* CD burning errors */
   pbErrCannotEject = -1,
   pbErrCannotAccessDevice = -2,
   pbErrNoMediaInDrive = -3,
   pbErrMediaIsNotBlankAndWritable = -4,
   pbErrCannotReserveDevice = -5,
   pbErrCannotPrepareToBurn = -6,
   pbErrCannotStartBurning = -7,
   pbErrCannotGetBurnStatus = -8,
   pbErrBurnFailed = -9,
   pbErrCannotCloseDevice = -10,

   /* Filesystem errors */
   pbErrCannotCreateStagingDirectory = -101,
   pbErrCannotCreateStagingFile = -102,
   pbErrCannotWriteToStagingFile = -103,
   pbErrCannotStageTrack = -104,
   pbErrCannotAccessStagedFile = -105,
   pbErrCannotUseStagedFileForBurning = -106,

   /* API errors: if these happen, you are not using PortBurn correctly */
   pbErrNoHandle = -501,
   pbErrMustCallGetNumDevices = -502,
   pbErrDeviceNotOpen = -503,
   pbErrAlreadyStagingOrBurning = -504,
   pbErrMustCallStartStaging = -505,
   pbErrMustCallStartTrack = -506,
   pbErrNotCurrentlyBurning = -507
   
} PortBurn_Result;

/* Returns a handle if burning capability is available on this system,
   and NULL otherwise */
void *PortBurn_Open();

/* Cleanup */
void PortBurn_Close(void *handle);

/* Return a human-readable error string for the last operating system
   specific error (NOT human readable strings for the PortBurn error
   codes).  Caller should dispose of the returned string using free(). */
const char *PortBurn_LastError(void *handle);

/* Get the number of devices capable of burning audio CDs.
   If the result is N, then calls to GetDeviceName and OpenDevice
   are guaranteed to be valid for indices from 0 up to N-1, until
   the next time you call GetNumDevices.  At that point, the list of
   devices will be rescanned, and may be different. */
int PortBurn_GetNumDevices(void *handle);

/* Get the name of the device with a given index.  Only valid
   after a call to GetNumDevices. */
char *PortBurn_GetDeviceName(void *handle, int index);

/* Open a particular device by index number.  You can open a device
   even before you're sure if you're going to go through with the
   burn, for example to make Eject available. */
int PortBurn_OpenDevice(void *handle, int index);

/* Close a device */
int PortBurn_CloseDevice(void *handle);

/* Eject the media in the currently opened device */
int PortBurn_EjectDevice(void *handle);

/* Erase the media in the currently opened device */
int PortBurn_EraseDevice(void *handle);

/* This indicates you're ready to start staging audio data for the
   currently opened device.  At this point you are committing to
   exclusive access to the CD burner, and this is the function that
   will fail if another program is using the device, or if there is
   no writable CD media in the device at this point.
   You should pass in the path to a temporary directory that has at
   least 700 MB of free space, to stage the audio, although note that
   not all implementations will make use of this directory. */
int PortBurn_StartStaging(void *handle, const char *tmpdir);

/* Start a new audio track.  Pass the name of the track, and the
   length in CD Audio frames (each frame is 1/75.0 of a second, exactly). */
int PortBurn_StartTrack(void *handle, const char *name, int frames);

/* Add one frame of audio to the current track.  The buffer must be exactly
   1176 elements long, containing interleaved left and right audio samples.
   The values should be signed 16-bit numbers in the native endianness of
   this computer. */
int PortBurn_AddFrame(void *handle, short *buffer);

/* Finish the current audio track. */
int PortBurn_EndTrack(void *handle);

/* Begin burning the disc. */
int PortBurn_StartBurning(void *handle);

/* Cancel if burning was in progress.  It might take a while for
   this to take effect; wait until GetStatus says 1.0 to close
   the device. */
int PortBurn_CancelBurning(void *handle);

/* During burning, returns the fraction complete in the given
   float pointer, from 0.0 to 1.0.  If this function returns
   nonzero, the disc burning has failed and should be aborted.
   If *out_fraction_complete is equal to 1.0, the burning is done;
   you can call PortBurn_CloseDevice.
*/
int PortBurn_GetStatus(void *handle, float *out_fraction_complete);

#endif /* __PORTBURN__ */

--- NEW FILE: Makefile.macosx ---
test_portburn: portburn_macosx.c portburn_staging.c test_portburn.c Makefile
        gcc \
                -framework Carbon -framework Cocoa -framework System \
                -framework DiscRecording \
                -Wall -g -O \
                -o test_portburn \
                portburn_macosx.c \
                portburn_staging.c \
                test_portburn.cpp

--- NEW FILE: clip.wav ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: portburn_macosx.c ---
/*
 * PortBurn
 * Mac OS X implementation
 *
 * Authors:
 *   Dominic Mazzoni
 *
 * With assistance from Apple's sample code:
 *   /Developer/Examples/DiscRecording/C/
 *
 * License: LGPL
 */

#include "portburn.h"
#include "portburn_staging.h"

#include <string.h>

#include <DiscRecording/DiscRecording.h>

typedef struct {
   OSStatus           err;
   CFArrayRef         deviceList;
   DRDeviceRef        device;
   CFMutableArrayRef  trackArray;
   DRBurnRef          burn;
   CFArrayRef         trackLayout;
   void              *staging;
   float              frac;
} PBHandle;

char *PortBurn_CStringFromCFString(CFStringRef cfname) {
   char *name;
   CFIndex len;

   len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfname),
                                           kCFStringEncodingASCII);
   name = (char *)malloc(len + 1);
   name[0] = 0;
   CFStringGetCString(cfname, name, len + 1, kCFStringEncodingASCII);
   return name;
}

void *PortBurn_Open()
{   
   PBHandle *h;

   h = (PBHandle *)malloc(sizeof(PBHandle));
   memset(h, 0, sizeof(PBHandle));

   return h;
}

/* Cleanup */
void PortBurn_Close(void *handle) {
   PBHandle *h = (PBHandle *)handle;
   if (!h)
      return;

   if (h->deviceList)
      CFRelease(h->deviceList);

   free(h);
}


/* Return a human-readable error string for the last operating system
   specific error (NOT human readable strings for the PortBurn error
   codes).  Caller should dispose of the returned string using free(). */
const char *PortBurn_LastError(void *handle)
{
   PBHandle *h = (PBHandle *)handle;
   CFStringRef cferr;

   if (!h)
      return NULL;

   cferr = DRCopyLocalizedStringForDiscRecordingError(h->err);
   if (cferr) {
      char *result = PortBurn_CStringFromCFString(cferr);
      CFRelease(cferr);
      return result;
   }
   else
      return NULL;
}

/* Get the number of devices capable of burning audio CDs.
   If the result is N, then calls to GetDeviceName and OpenDevice
   are guaranteed to be valid for indices from 0 up to N-1, until
   the next time you call GetNumDevices.  At that point, the list of
   devices will be rescanned, and may be different. */
int PortBurn_GetNumDevices(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return 0;

   if (h->deviceList) {
      CFRelease(h->deviceList);
      h->deviceList = NULL;
   }

   h->deviceList = DRCopyDeviceArray();
   if (!h->deviceList)
      return 0;

   return (int)CFArrayGetCount(h->deviceList);
}

/* Get the name of the device with a given index.  Only valid
   after a call to GetNumDevices. */
char *PortBurn_GetDeviceName(void *handle, int index)
{
   PBHandle *h = (PBHandle *)handle;
   CFDictionaryRef deviceInfo;
   CFStringRef bus, vendor, product;
   CFStringRef cfname;
   char *result;

   if (!h)
      return NULL;

   if (!h->deviceList)
      return NULL;

   DRDeviceRef device;
   device = (DRDeviceRef)CFArrayGetValueAtIndex(h->deviceList, index);

   deviceInfo = DRDeviceCopyInfo(device);
   bus = (CFStringRef)CFDictionaryGetValue(deviceInfo,
                                           kDRDevicePhysicalInterconnectKey);
   if (CFEqual(bus, kDRDevicePhysicalInterconnectFireWire))
      bus = CFSTR("FireWire: ");
   else if (CFEqual(bus, kDRDevicePhysicalInterconnectUSB))
      bus = CFSTR("USB: ");
   else if (CFEqual(bus, kDRDevicePhysicalInterconnectATAPI))
      bus = CFSTR("ATAPI: ");
   else if (CFEqual(bus, kDRDevicePhysicalInterconnectSCSI))
      bus = CFSTR("SCSI: ");
   else
      bus = CFSTR("");

   vendor = CFDictionaryGetValue(deviceInfo, kDRDeviceVendorNameKey);
   product = CFDictionaryGetValue(deviceInfo, kDRDeviceProductNameKey);

   cfname = CFStringCreateWithFormat(NULL, NULL,
                                     CFSTR("[EMAIL PROTECTED]@ %@"),
                                     bus, vendor, product);

   result = PortBurn_CStringFromCFString(cfname);

   CFRelease(cfname);
   CFRelease(deviceInfo);

   return result;
}

/* Open a particular device by index number.  Returns 0 on success;
   any nonzero value indicates an error, for example if the device is
   already open by some other program. */
int PortBurn_OpenDevice(void *handle, int index)
{
   PBHandle *h = (PBHandle *)handle;
   if (!h)
      return pbErrNoHandle;

   if (!h->deviceList)
      return pbErrMustCallGetNumDevices;

   h->device = (DRDeviceRef)CFArrayGetValueAtIndex(h->deviceList, index);

   /* This just indicates interest; it doesn't return an error. */
   DRDeviceAcquireMediaReservation(h->device);

   return pbSuccess;
}

/* Close a device */
int PortBurn_CloseDevice(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;

   if (!h->device)
      return pbErrDeviceNotOpen;

   if (h->burn != NULL) {
      CFRelease(h->burn);
      h->burn = NULL;
   }

   if (h->trackArray) {
      CFRelease(h->trackArray);
      h->trackArray = NULL;
   }
 
   DRDeviceReleaseMediaReservation(h->device);
   CFRelease(h->device);
   h->device = NULL;

   if (h->staging) {
      PortBurn_CleanupStaging(h->staging);
      h->staging = NULL;
   }

   return pbSuccess;
}

/* Eject the media in the currently opened device */
int PortBurn_EjectDevice(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;

   if (!h->device)
      return pbErrDeviceNotOpen;

   h->err = DRDeviceOpenTray(h->device);
   if (noErr == h->err)
      return pbSuccess;  /* success */

   h->err = DRDeviceEjectMedia(h->device);
   if (noErr == h->err)
      return pbSuccess;  /* success */

   return pbErrCannotEject;
}

/* This indicates you're ready to start staging audio data for the
   currently opened device.  At this point you are committing to
   exclusive access to the CD burner, and this is the function that
   will fail if another program is using the device, or if there is
   no writable CD media in the device at this point.
   You should pass in the path to a temporary directory that has at
   least 700 MB of free space, to stage the audio, although note that
   not all implementations will make use of this directory. */
int PortBurn_StartStaging(void *handle, const char *tmpdir)

{
   PBHandle *h = (PBHandle *)handle;
   CFDictionaryRef deviceStatus;
   CFStringRef mediaState;
   CFDictionaryRef mediaInfo;
   CFBooleanRef blank;
   CFBooleanRef writable;
   CFBooleanRef reserved;

   if (!h)
      return pbErrNoHandle;

   if (!h->device)
      return pbErrDeviceNotOpen;

   if (h->staging)
      return pbErrAlreadyStagingOrBurning;

   /* First we check to see that we have blank media in the drive. */
   deviceStatus = DRDeviceCopyStatus(h->device);
   mediaState = (CFStringRef)CFDictionaryGetValue(deviceStatus,
                                                  kDRDeviceMediaStateKey);
   if (mediaState == NULL) {
      CFRelease(deviceStatus);
      return pbErrCannotAccessDevice;
   }

   if (!CFEqual(mediaState, kDRDeviceMediaStateMediaPresent)) {
      CFRelease(deviceStatus);
      return pbErrNoMediaInDrive;
   }

   mediaInfo = (CFDictionaryRef)CFDictionaryGetValue(deviceStatus,
                                                     kDRDeviceMediaInfoKey);
   blank = (CFBooleanRef)CFDictionaryGetValue(mediaInfo,
                                              kDRDeviceMediaIsBlankKey);

   if (blank == NULL || !CFBooleanGetValue(blank)) {
      CFRelease(deviceStatus);
      return pbErrMediaIsNotBlankAndWritable;
   }

   writable = (CFBooleanRef)CFDictionaryGetValue(mediaInfo,
                                                kDRDeviceMediaIsAppendableKey);

   if (writable == NULL || !CFBooleanGetValue(writable)) {
      CFRelease(deviceStatus);
      return pbErrMediaIsNotBlankAndWritable;
   }

   reserved = (CFBooleanRef)CFDictionaryGetValue(mediaInfo,
                                                 kDRDeviceMediaIsReservedKey);
   if (reserved == NULL || !CFBooleanGetValue(reserved)) {
      CFRelease(deviceStatus);
      return pbErrCannotReserveDevice;
   }
        
   CFRelease(deviceStatus);

   h->staging = PortBurn_TempDirStaging(tmpdir);

   if (!h->staging) {
      return pbErrCannotCreateStagingDirectory;
   }

   h->trackArray = CFArrayCreateMutable(kCFAllocatorDefault, 0,
                                        &kCFTypeArrayCallBacks);

   return pbSuccess;
}

/* Start a new audio track.  Pass the name of the track, and the
   length in CD Audio frames (each frame is 1/75.0 of a second, exactly). */
int PortBurn_StartTrack(void *handle, const char *name, int frames)
{
   PBHandle *h = (PBHandle *)handle;
   if (!h)
      return pbErrNoHandle;

   if (!h->staging)
      return pbErrMustCallStartStaging;

   if (0 == PortBurn_StartStagingTrack(h->staging, name, frames))
      return pbSuccess;
   else
      return pbErrCannotCreateStagingFile;
}

/* Add one frame of audio to the current track.  The buffer must be exactly
   1176 elements long, containing interleaved left and right audio samples.
   The values should be signed 16-bit numbers in the native endianness of
   this computer. */
int PortBurn_AddFrame(void *handle, short *buffer)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;

   if (!h->staging)
      return pbErrMustCallStartTrack;

   if (0 == PortBurn_AddStagingFrame(h->staging, buffer))
      return pbSuccess;
   else
      return pbErrCannotWriteToStagingFile;
}

/* Finish the current audio track. */
int PortBurn_EndTrack(void *handle)
{
   PBHandle *h = (PBHandle *)handle;
   DRAudioTrackRef track;
   Boolean isDirectory;
   const char *filename;
   FSRef fsref;
   int index;

   if (!h)
      return pbErrNoHandle;

   if (!h->staging)
      return pbErrMustCallStartStaging;

   if (0 != PortBurn_EndStagingTrack(h->staging))
      return pbErrCannotStageTrack;

   index = PortBurn_GetNumStagedTracks(h->staging);
   if (index <= 0)
      return pbErrCannotStageTrack;

   filename = PortBurn_GetStagedFilename(h->staging, index - 1);
   printf("Filename: '%s'\n", filename);
   h->err = FSPathMakeRef((const UInt8*)filename, &fsref, &isDirectory);
   if (h->err != noErr)
      return pbErrCannotAccessStagedFile;
   if (isDirectory)
      return pbErrCannotAccessStagedFile;

   track = DRAudioTrackCreate(&fsref);

   CFArrayAppendValue(h->trackArray, track);
   CFRelease(track);

   if (track)
      return pbSuccess;
   else
      return pbErrCannotUseStagedFileForBurning;
}

/* Begin burning the disc. */
int PortBurn_StartBurning(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;

   if (!h->device)
      return pbErrDeviceNotOpen;

   h->burn = DRBurnCreate(h->device);
   if (!h->burn)
      return pbErrCannotPrepareToBurn;

   h->err = DRBurnWriteLayout(h->burn, h->trackArray);
   if (h->err != noErr) {
      DRBurnAbort(h->burn);
      CFRelease(h->burn);
      h->burn = NULL;
      return pbErrCannotStartBurning;
   }

   h->frac = 0.0;

   return pbSuccess;
}

/* Cancel if burning was in progress.  It might take a while for
   this to take effect; wait until GetStatus says 1.0 to close
   the device. */
int PortBurn_CancelBurning(void *handle)
{
   PBHandle *h = (PBHandle *)handle;

   if (!h)
      return pbErrNoHandle;

   if (!h->burn)
      return pbErrNotCurrentlyBurning;

   DRBurnAbort(h->burn);

   return pbSuccess;
}

/* During burning, returns the fraction complete in the given
   float pointer, from 0.0 to 1.0.  If this function returns
   nonzero, the disc burning has failed and should be aborted.
   If *out_fraction_complete is equal to 1.0, the burning is done;
   you can call PortBurn_CloseDevice.
*/

int PortBurn_GetStatus(void *handle, float *out_fraction_complete)
{
   PBHandle *h = (PBHandle *)handle;
   CFDictionaryRef status;
   CFStringRef stateRef;
   CFNumberRef fracRef;
   CFNumberRef trackNumRef;
   float frac = 0.0;
   int trackNum = 0;

   *out_fraction_complete = h->frac;

   if (!h)
      return pbErrNoHandle;

   if (!h->burn)
      return pbErrNotCurrentlyBurning;

   status = DRBurnCopyStatus(h->burn);
   if (!status)
      return pbErrCannotGetBurnStatus;

   trackNumRef = (CFNumberRef)
      CFDictionaryGetValue(status, kDRStatusCurrentTrackKey);
   if (trackNumRef != NULL) {
      CFNumberGetValue(trackNumRef, kCFNumberIntType, &trackNum);
   }

   fracRef = (CFNumberRef)
      CFDictionaryGetValue(status, kDRStatusPercentCompleteKey);
   if (fracRef != NULL) {
      CFNumberGetValue(fracRef, kCFNumberFloatType, &frac);
   }

   stateRef = (CFStringRef)
      CFDictionaryGetValue(status, kDRStatusStateKey);
   if (stateRef == NULL) {
      /* Stick with the last percentage */
      return pbSuccess;
   }

   if (CFEqual(stateRef, kDRStatusStateNone)) {
      /* Stick with the last percentage */
      return pbSuccess;
   }

   if (CFEqual(stateRef, kDRStatusStatePreparing)) {
      /* This takes about 1 second */
      h->frac = 0.01;
   }

   if (CFEqual(stateRef, kDRStatusStateSessionOpen)) {
      /* This takes about 3 seconds */
      h->frac = 0.02;
   }

   if (CFEqual(stateRef, kDRStatusStateTrackOpen) ||
       CFEqual(stateRef, kDRStatusStateTrackWrite) ||
       CFEqual(stateRef, kDRStatusStateTrackClose) ||
       CFEqual(stateRef, kDRStatusStateSessionClose)) {
      /* The fraction ("percentage") complete will be valid during 
         the majority of this range */
      float newFrac = 0.0;
      if (frac > 0.0 && frac <= 1.0)
         newFrac = frac;

      /* Scale it to the range 0.05 - 0.99 */
      newFrac = 0.05 + 0.94 * newFrac;

      /* Only use that value if it's larger than the previous value */
      if (newFrac > h->frac)
         h->frac = newFrac;
   }

   if (CFEqual(stateRef, kDRStatusStateDone)) {
      /* Returning a fraction complete of 1.0 means we're done! */
      h->frac = 1.0;
   }

   if (CFEqual(stateRef, kDRStatusStateFailed)) {
      CFRelease(status);
      return pbErrBurnFailed;
   }

   *out_fraction_complete = h->frac;

   CFRelease(status);   
   return pbSuccess;
}

--- NEW FILE: test_portburn.cpp ---
/*
 * Test PortBurn
 *
 * Dominic Mazzoni
 * License: LGPL
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//#include <unistd.h>
#include <Windows.h>

#include "portburn.h"

const char *filename = "clip.wav";
const int file_frames = 166;
const int frame_size = 588;

#define swap_uint16(x) \
       ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))

void FailErr(int result) {
   if (result != 0) {
      printf("Failed: %d\n", result);
      exit(-1);
   }
}

int main(int argc, char **argv) {
   FILE *fp;
   short *buffer = (short *)malloc(file_frames * frame_size * 4);
   void *handle = PortBurn_Open();
   int count = PortBurn_GetNumDevices(handle);
   int i, j;
   unsigned int swaptest;
   int needswap;

   swaptest = 0x01020304;
   if (((unsigned char *)&swaptest)[0] == 1) {
      printf("Big-endian machine, will do byteswapping\n");
      needswap = 1;
   }
   else {
      printf("Little-endian machine, will not do byteswapping\n");
      needswap = 0;
   }

   if (count == 0) {
      printf("No devices found!\n");
      return -1;
   }

   for(i = 0; i < count; i++) {
      char *name = PortBurn_GetDeviceName(handle, i);
      printf("Device %d: '%s'\n", i, name);
      free(name);
   }

   printf("Using the first device\n");

   FailErr(PortBurn_OpenDevice(handle, 0));
//   FailErr(PortBurn_EjectDevice(handle));
//   FailErr(PortBurn_EraseDevice(handle));

   printf("Press enter when the device is ready\n");
   getchar();

   printf("Staging audio\n");
   FailErr(PortBurn_StartStaging(handle, "c:\temp"));

   fp = fopen(filename, "r");
   if (!fp) {
      printf("Couldn't open file: %s\n", filename);
      return -1;
   }
   fseek(fp, 44, SEEK_SET);
   fread(buffer, 2, file_frames * frame_size * 2, fp);
   fclose(fp);

   if (needswap) {
      for(i = 0; i < file_frames * frame_size * 2; i++) {
         ((unsigned short *)buffer)[i] =
            swap_uint16(((unsigned short *)buffer)[i]);
      }
   }

   FailErr(PortBurn_StartTrack(handle, "80 loops", 80 * file_frames));
   for(j = 0; j < 80; j++) {
      for(i = 0; i < file_frames; i++) {
         FailErr(PortBurn_AddFrame(handle, &buffer[i * 2 * frame_size]));
      }
   }
   FailErr(PortBurn_EndTrack(handle));

   FailErr(PortBurn_StartTrack(handle, "100 loops", 100 * file_frames));
   for(j = 0; j < 100; j++) {
      for(i = 0; i < file_frames; i++) {
         FailErr(PortBurn_AddFrame(handle, &buffer[i * 2 * frame_size]));
      }
   }
   FailErr(PortBurn_EndTrack(handle));

   printf("Burning!!!\n");
   FailErr(PortBurn_StartBurning(handle));

   for(;;) {
      float frac;
      int result;

      result = PortBurn_GetStatus(handle, &frac);
      if (result != 0)
         break;

      printf("Status: %.3f\n", frac);

      if (frac == 1.0) {
         printf("Completed!!!\n");
         break;
      }
   }

   printf("Cleaning up\n");
   PortBurn_CloseDevice(handle);
   PortBurn_Close(handle);

   printf("Press enter to end\n");
   getchar();

   return 0;
}

--- NEW FILE: portburn_staging.c ---
/*
 * PortBurn
 * Common utilities for staging audio files in a temporary directory.
 *
 * Dominic Mazzoni
 * License: LGPL
 *
 * A library for cross-platform audio CD burning
 *
 * On some of the platforms supported by PortBurn, we need to stage
 * the audio data ourselves.  This file defines an interface to the
 * common functions.
 */

#include "portburn_staging.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/stat.h>
#include <sys/types.h>

#define swap_uint16(x) \
       ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#define swap_uint32(x) \
       ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |    \
        (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))

inline const unsigned int FourCharInt32(const char *str) {
   return swap_uint32(*(const unsigned int *)(str));
}

typedef struct {
   int      ntracks;
   int      cur;
   FILE    *curfp;
   char    *tmpdir;
   char    *filename[99];
   char    *trackname[99];
   int      frames[99];
   int      needswap;
} StagingHandle;

static void PortBurn_Put32(StagingHandle *handle, unsigned int data)
{
   if (handle->needswap)
      data = swap_uint32(data);
   fwrite((void *)&data, 4, 1, handle->curfp);
}

static void PortBurn_Put16(StagingHandle *handle, unsigned short data)
{
   if (handle->needswap)
      data = swap_uint16(data);
   fwrite((void *)&data, 2, 1, handle->curfp);
}

void *PortBurn_TempDirStaging(const char *temporary_directory) {
   StagingHandle *h;
   volatile unsigned int swaptest;
   FILE *fp;
   char *tmp2;
   char *tmp;
   int len;
   int i;

   if (temporary_directory == NULL)
      return NULL;

   len = strlen(temporary_directory);
   if (len < 2)
      return NULL;

   tmp = (char *)malloc(len+1);
   tmp2 = (char *)malloc(len+6);
   strcpy(tmp, temporary_directory);

   /* Make all of the parent directories, in case they don't exist. */
   for(i=1; i<len; i++) {
      if (tmp[i]=='/') {
         tmp[i] = 0;
         mkdir(tmp, 0777);
         tmp[i] = '/';
      }
   }
   mkdir(tmp, 0777);

   /* The path shouldn't end in a '/' */
   if (tmp[len-1] == '/') {
      tmp[len-1] = 0;
      len--;
   }

   /* Make sure we can write and read files in this directory */
   sprintf(tmp2, "%s/test", tmp);
   fp = fopen(tmp2, "w");
   if (!fp) {
      free(tmp);
      free(tmp2);
      return NULL;
   }
   fprintf(fp, "test");
   fclose(fp);
   fp = fopen(tmp2, "r");
   if (!fp) {
      unlink(tmp2);
      free(tmp);
      free(tmp2);
      return NULL;
   }
   fclose(fp);
   unlink(tmp2);

   h = (StagingHandle *)malloc(sizeof(StagingHandle));
   memset(h, 0, sizeof(StagingHandle));
   h->tmpdir = tmp;
   free(tmp2);

   swaptest = 0x01020304;
   if (((unsigned char *)&swaptest)[0] == 1)
      h->needswap = 1;
   else
      h->needswap = 0;

   h->cur = -1;

   return h;
}

int PortBurn_StartStagingTrack(void *handle, const char *name, int frames)
{
   StagingHandle *h = (StagingHandle *)handle;
   if (!h)
      return -1;

   if (h->cur != -1)
      return -1;

   if (name == NULL)
      return -1;

   if (h->ntracks == 99)
      return -1;

   h->cur = h->ntracks;
   h->trackname[h->cur] = (char *)malloc(strlen(name)+1);
   strcpy(h->trackname[h->cur], name);
   h->filename[h->cur] = (char *)malloc(strlen(h->tmpdir)+14);
   sprintf(h->filename[h->cur], "%s/track%03d.wav", h->tmpdir, h->cur+1);
   h->curfp = fopen(h->filename[h->cur], "wb");
   if (!h->curfp) {
      free(h->trackname[h->cur]);
      free(h->filename[h->cur]);
      h->cur = -1;
      return -1;
   }
   h->frames[h->cur] = frames;

   struct WavFormatChunk {
      unsigned short format;
      unsigned short channels;
      unsigned int samplerate;
      unsigned int byterate;
      unsigned short blockAlign;
      unsigned short bitsPerSample;
   };

   PortBurn_Put32(h, FourCharInt32("RIFF"));
   PortBurn_Put32(h, 44 + (4 * 1176 * frames));
   PortBurn_Put32(h, FourCharInt32("WAVE"));

   PortBurn_Put32(h, FourCharInt32("fmt "));
   PortBurn_Put32(h, 16);
   PortBurn_Put16(h, 1);       /* format */
   PortBurn_Put16(h, 2);       /* channels */
   PortBurn_Put32(h, 44100);   /* samplerate */
   PortBurn_Put32(h, 176400);  /* byterate */
   PortBurn_Put16(h, 4);       /* block align */
   PortBurn_Put16(h, 16);      /* bits per sample */

   PortBurn_Put32(h, FourCharInt32("data"));
   PortBurn_Put32(h, 4 * 1176 * frames);

   if (ferror(h->curfp)) {
      /* Finish this track and let the cleanup function handle it */
      fclose(h->curfp);
      h->ntracks++;
      h->cur = -1;
      return -1;
   }

   return 0;
}

int PortBurn_AddStagingFrame(void *handle, short *buffer)
{
   StagingHandle *h = (StagingHandle *)handle;
   int i;

   if (!h)
      return -1;

   if (h->cur != h->ntracks)
      return -1;

   if (!buffer)
      return -1;

   for(i = 0; i < 1176; i++) {
      PortBurn_Put16(h, buffer[i]);
   }

   if (ferror(h->curfp)) {
      /* Finish this track and let the cleanup function handle it */
      fclose(h->curfp);
      h->ntracks++;
      h->cur = -1;
      return -1;
   }

   return 0;
}

int PortBurn_EndStagingTrack(void *handle)
{
   StagingHandle *h = (StagingHandle *)handle;
   if (!h)
      return -1;

   if (h->cur != h->ntracks)
      return -1;

   fclose(h->curfp);
   h->ntracks++;
   h->cur = -1;

   return 0;
}

int PortBurn_FinishStaging(void *handle)
{
   return 0;
}

int PortBurn_GetNumStagedTracks(void *handle)
{
   StagingHandle *h = (StagingHandle *)handle;
   if (!h)
      return -1;

   return h->ntracks;
}

const char *PortBurn_GetStagedFilename(void *handle, int index)
{
   StagingHandle *h = (StagingHandle *)handle;
   if (!h)
      return NULL;

   return h->filename[index];
}

const char *PortBurn_GetStagedTrackName(void *handle, int index)
{
   StagingHandle *h = (StagingHandle *)handle;
   if (!h)
      return NULL;

   return h->trackname[index];
}

int PortBurn_GetStagedLengthInFrames(void *handle, int index)
{
   StagingHandle *h = (StagingHandle *)handle;
   if (!h)
      return -1;

   return h->frames[index];
}

void PortBurn_CleanupStaging(void *handle)
{
   StagingHandle *h = (StagingHandle *)handle;
   int i;

   if (!h)
      return;

   free(h->tmpdir);
   for(i = 0; i < h->ntracks; i++) {
      unlink(h->filename[i]);
      free(h->filename[i]);
      free(h->trackname[i]);
   }
}


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Audacity-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/audacity-cvs

Reply via email to