//////////////////////////////////////////////////////////////////////////////// // Name: sndfrag.cpp // Purpose: wxMMedia // Author: Guilhem Lavaux // Created: 1997 // Updated: 1998 // Copyright: (C) 1997, 1998, Guilhem Lavaux // License: wxWindows license //////////////////////////////////////////////////////////////////////////////// #ifdef __GNUG__ #pragma implementation "sndfrag.h" #endif #include #ifdef WX_PRECOMP #include "wx_prec.h" #else #include "wx/wx.h" #endif #include "sndfrag.h" #ifdef __BORLANDC__ #pragma hdrstop #endif wxFragmentBuffer::wxFragmentBuffer(wxSound& io_drv) : m_iodrv(&io_drv), m_maxoq(0), m_maxiq(0), m_lstoptrs(NULL), m_lstiptrs(NULL), m_buf2free(FALSE), m_dontq(FALSE), m_freeing(FALSE) { } wxFragmentBuffer::~wxFragmentBuffer() { } void wxFragmentBuffer::AbortBuffer(wxSndBuffer *buf) { for (wxUint8 i=0;iMember(buf)) { if (m_lstoptrs[i].state == wxBUFFER_PLAYING) // TODO: Do something. ; m_lstoptrs[i].state = wxBUFFER_TOFREE; } for (wxUint8 i=0;iMember(buf)) { if (m_lstiptrs[i].state == wxBUFFER_PLAYING) // Do something. ; m_lstiptrs[i].state = wxBUFFER_TOFREE; } } wxFragmentBuffer::wxFragBufPtr *wxFragmentBuffer::FindFreeBuffer( wxFragBufPtr *list, wxUint8 max_queue) { if (!list) return NULL; for (wxUint8 i=0;iGetCurrentCodec(); if (!m_iodrv->OnSetupDriver(*buf, wxSND_OUTPUT)) return FALSE; while (1) { // Find the first free (at least partly free) output buffer ptr = FindFreeBuffer(m_lstoptrs, m_maxoq); // No free : go out ! if (ptr == NULL) return FALSE; // Normally, these three functions could be called only once. codec->SetOutStream(ptr->sndbuf); codec->InitIO(m_drvformat); codec->InitMode(wxSoundCodec::DECODING); // Fill it up codec->Decode(); // No data to fill the buffer: dequeue the current wxSndBuffer if (codec->Available() == 0) { if (buf->IsNotSet(wxSND_KEEPQUEUED)) { buf->Set(wxSND_UNQUEUEING); m_iodrv->m_buffers.DeleteObject(buf); } return TRUE; } // Data: append it to the list ptr->buffers->Append(buf); // Output buffer full: send it to the driver if (ptr->sndbuf->GetDataLeft() == 0) { ptr->state = wxBUFFER_FFILLED; OnBufferFilled(ptr, wxSND_OUTPUT); } } } bool wxFragmentBuffer::NotifyInputBuffer(wxSndBuffer *buf) { wxFragBufPtr *ptr; size_t inc; if (!m_iodrv->OnSetupDriver(*buf, wxSND_INPUT)) return FALSE; while (1) { ptr = FindFreeBuffer(m_lstiptrs, m_maxiq); if (ptr == NULL) return FALSE; if (buf->Available() == 0) { if (buf->IsNotSet(wxSND_KEEPQUEUED)) { buf->Set(wxSND_UNQUEUEING); m_iodrv->m_buffers.DeleteObject(buf); } if (LastBuffer() == NULL && ptr->sndbuf->GetIntPosition() != 0) { ptr->state = wxBUFFER_FFILLED; if (!OnBufferFilled(ptr, wxSND_INPUT)) return FALSE; } return TRUE; } ptr->buffers->Append(buf); // TODO: Add an "incrementer" in wxStreamBuffer. inc = (buf->Available() < ptr->sndbuf->GetDataLeft()) ? buf->Available() : ptr->sndbuf->GetDataLeft(); ptr->sndbuf->SetIntPosition(ptr->sndbuf->GetIntPosition() + inc); if (ptr->sndbuf->GetDataLeft() == 0) { ptr->state = wxBUFFER_FFILLED; if (!OnBufferFilled(ptr, wxSND_INPUT)) return FALSE; } } return TRUE; } void wxFragmentBuffer::FreeBufToFree(bool force) { wxUint8 i; // Garbage collecting m_dontq = TRUE; m_buf2free = FALSE; for (i=0;ibuffers->First(); while (node) { buf = (wxSndBuffer *)node->Data(); if (buf->GetMode() == wxSND_OUTPUT) { buf->OnBufferOutFinished(); } else { codec = buf->GetCurrentCodec(); // Normally, these three functions could be called only once. codec->SetInStream(ptr->sndbuf); codec->InitIO(m_drvformat); codec->InitMode(wxSoundCodec::ENCODING); // As there is an "auto-stopper" in the codec, we don't worry ... codec->Encode(); } if (buf->IsSet(wxSND_UNQUEUEING)) buf->Clear(wxSND_UNQUEUEING | wxSND_BUFLOCKED | wxSND_BUFREADY); delete node; node = ptr->buffers->First(); } ptr->sndbuf->ResetBuffer(); ptr->state = wxBUFFER_FREE; } void wxFragmentBuffer::OnBufferFinished(wxFragBufPtr *ptr) { wxNode *node; wxSndBuffer *buf; bool ret = TRUE; if (m_freeing) { ptr->state = wxBUFFER_TOFREE; m_buf2free = TRUE; return; } m_freeing = TRUE; // Clean up the old buffer. if (ptr && ptr->state != wxBUFFER_FREE) ClearBuffer(ptr); // Garbage collecting ... if (m_buf2free) FreeBufToFree(); // If we are queueing, return immediately. if (m_dontq) { m_freeing = FALSE; return; } node = m_iodrv->m_buffers.First(); while (node && ret) { buf = (wxSndBuffer *)node->Data(); node = node->Next(); buf->HardLock(); // Stop request on this buffer. if (buf->IsSet(wxSND_BUFSTOP)) { buf->Clear(wxSND_BUFSTOP); continue; } switch (buf->GetMode()) { case wxSND_OUTPUT: ret = NotifyOutputBuffer(buf); break; case wxSND_INPUT: ret = NotifyInputBuffer(buf); break; case wxSND_DUPLEX: case wxSND_OTHER_IO: // ret = NotifyDuplexBuffer(buf); break; } buf->HardUnlock(); } m_freeing = FALSE; }