⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 soundcardpmo.cpp

📁 FreeAMP(MP3播放)程序源代码-用来研究MP3解码
💻 CPP
字号:
/*____________________________________________________________________________
	
	FreeAmp - The Free MP3 Player

	Portions Copyright (C) 1998-1999 EMusic.com

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
	
	$Id: soundcardpmo.cpp,v 1.12 2000/07/11 16:00:13 hiro Exp $
____________________________________________________________________________*/

#define DEBUG 0

/* system headers */
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <iostream.h>

/* BeOS Kits */
#include <media/SoundPlayer.h>
#include <support/Debug.h>

/* project headers */
#include <config.h>
#include "soundcardpmo.h"
#include "soundutils.h"
#include "thread.h"
#include "mutex.h"
#include "facontext.h"
#include "log.h"

LogFile*	g_Log;

int32 SoundCardPMO::s_lastVolume = 100;

extern "C" {

PhysicalMediaOutput* Initialize(FAContext *context)
{
	return new SoundCardPMO(context);
}

} /* extern "C" */

SoundCardPMO::SoundCardPMO( FAContext* context )
:	PhysicalMediaOutput( context ),
    m_player( NULL ),
    m_dataSize( 0 ),
    m_eventSem( "EventSem" ),
    m_pauseLock( "pause lock" ),
    m_bytesPerSample( 0 ),
    m_lastFrame( -1 ),
    m_totalBytesWritten( 0 )
{
	PRINT(( "SoundCardPMO::ctor\n" ));
	m_properlyInitialized = false;

	m_outputInfo = new OutputInfo();

	m_context = context;

	m_eventBufferThread = Thread::CreateThread();
	m_eventBufferThread->Create( _EventBufferThreadHook, this );
//    m_eventBufferThread->SetPriority( B_REAL_TIME_PRIORITY ); // FIXME: don't use beos native prio
}

SoundCardPMO::~SoundCardPMO()
{
	Lock();

	PRINT(( "SoundCardPMO::dtor\n" ));

	m_bExit = true;
	m_pSleepSem->Signal();
	m_pPauseSem->Signal();
	m_pauseLock.Unlock();

	PRINT(( "SoundCardPMO::~SoundCardPMO\n" ));
	if ( m_player )
	{
		PRINT(( "SoundCardPMO::~SoundCardPMO:stopping\n" ));
		m_player->SetHasData( false );
		m_player->Stop();	// blocks till it's done
		PRINT(( "SoundCardPMO::~SoundCardPMO:stopped\n" ));

		delete m_player;
		m_player = NULL;
	}

	if ( m_outputInfo )
	{
		delete m_outputInfo;
		m_outputInfo = NULL;
	}

	if ( m_eventBufferThread )
	{
		delete m_eventBufferThread;
		m_eventBufferThread = NULL;
	}

#if DEBUG_SAVE_PCM
	fclose( m_pcmSaveFile );
#endif
	PRINT(( "SoundCardPMO::~SoundCardPMO done\n" ));
}

void
SoundCardPMO::GetVolume( int32& left, int32& right )
{
	PRINT(( "SoundCardPMO::GetVolume\n" ));
    if ( !m_player )
	{
		left = right = s_lastVolume;
	}
	else
	{
		left = right = int32( m_player->Volume() * 100.0 );
	}
}

void
SoundCardPMO::SetVolume( int32 left, int32 right )
{
	PRINT(( "SoundCardPMO::SetVolume\n" ));
    int32 volume = ( left + right ) / 2;
    if ( m_player )
    {
        m_player->SetVolume( float( volume ) / 100.0 );
    }
	s_lastVolume = volume;
}

void
SoundCardPMO::Pause( void )
{
	PRINT(( "SoundCardPMO::Pause\n" ));
	PhysicalMediaOutput::Pause();
	m_pauseLock.Lock();

	if ( m_player )
	{
		m_player->SetHasData( false );
		m_player->Stop();
	}

	if ( false && m_properlyInitialized )
	{
		Reset( true );
	}
}

void
SoundCardPMO::Resume( void )
{
	PRINT(( "SoundCardPMO::Resume\n" ));
	PhysicalMediaOutput::Resume();
	m_pauseLock.Unlock();

	if ( m_player )
	{
		m_player->Start();
		m_player->SetHasData( true );
	}
	PRINT(( "SoundCardPMO::Resume done\n" ));
}

bool
SoundCardPMO::WaitForDrain( void )
{
	PRINT(( "SoundCardPMO::WaitForDrain\n" ));
#if 0
	if ( m_eventBufferThread )
	{
		m_eventBufferThread->Join();
		delete m_eventBufferThread;
		m_eventBufferThread = NULL;
	}
	PRINT(( "SoundCardPMO::WaitForDrain:event buffer terminated\n" ));
#endif

	if ( !m_player )
	{
		PRINT(( "SoundCardPMO::WaitForDrain:no BSoundPlayer exists?!\n" ));
		return false;
	}

	m_player->SetHasData( false );
	m_player->Stop();
	PRINT(( "SoundCardPMO::WaitForDrain:sound player terminated\n" ));

	return true;
}

Error
SoundCardPMO::Init( OutputInfo* info )
{
    Lock();

	PRINT(( "SoundCardPMO::Init\n" ));
	PRINT(( "OI: bits_per_sample: %d\n", info->bits_per_sample ));
	PRINT(( "OI: number_of_channels: %d\n", info->number_of_channels ));
	PRINT(( "OI: samples_per_second: %d\n", info->samples_per_second ));
	PRINT(( "OI: max_buffer_size: %d\n", info->max_buffer_size ));

	m_properlyInitialized = false;
	if ( !info )
	{
		info = m_outputInfo;
	}
	else
	{
		// got info, so this is the beginning..
		m_dataSize = info->max_buffer_size;
	}

	m_outputInfo->bits_per_sample = info->bits_per_sample;
	m_outputInfo->number_of_channels = info->number_of_channels;
	m_outputInfo->samples_per_second = info->samples_per_second;
	m_outputInfo->max_buffer_size = info->max_buffer_size;

    m_bytesPerSample = info->number_of_channels * ( info->bits_per_sample / 8 );

	if ( ::get_audio_format( m_outputInfo, &m_format ) < B_NO_ERROR )
	{
		printf( "error in output info\n" );
	}

	if ( m_player )
	{
		delete m_player;
	}

	m_player = new BSoundPlayer(
							&m_format,
							BRANDING,
							_PlayerHook,
							_NotifierHook,
							this
							);

	if ( m_player->InitCheck() < B_NO_ERROR )
	{
		PRINT(( "SoundPlayerPMO::Init:error constructing BSoundPlayer\n" ));
		return kError_InitFailed;
	}

	PRINT(( "Frame Rate : %f\n", m_format.frame_rate ));
	PRINT(( "Format : %x\n", (int)m_format.format ));
	PRINT(( "Channel Count : %d\n", (int)m_format.channel_count ));

#if USE_DUMMY_PLAYER
	m_dummyPlayerThread = Thread::CreateThread();
	m_dummyPlayerThread->Create( _DummyPlayerHook, this );
#else
    m_player->SetVolume( float( s_lastVolume ) / 100.0 );
	m_player->Start();
	m_player->SetHasData( true );
#endif

#if DEBUG_SAVE_PCM
	m_pcmSaveFile = fopen( "freeamp.pcmsave", "w" );
#endif

	m_properlyInitialized = true;

    Unlock();

	return kError_NoErr;
}

Error
SoundCardPMO::Reset( bool user_stop )
{
	PRINT(( "SoundCardPMO::Reset...\n" ));

#if USE_DUMMY_PLAYER
#else
	if ( !m_player ) return kError_YouScrewedUp;

	if ( m_player )
	{
		m_player->SetHasData( false );
		PRINT(( "SetHasData done\n" ));
		m_player->Stop( false );
		PRINT(( "player stop done\n" ));
	}

	if ( false &&  m_eventBufferThread )
	{
		m_eventSem.Signal();
		m_eventBufferThread->Join();
	}

	if ( user_stop )
	{
	}
	else
	{
	}
#endif

	return kError_NoErr;
}

void
SoundCardPMO::HandleTimeInfoEvent( PMOTimeInfoEvent* event )
{
    int32 hours, minutes, seconds, total;
    MediaTimeInfoEvent* mtie;

    if ( !m_player ) return;

    if ( event->GetFrameNumber() != m_lastFrame + 1 )
    {
        m_totalBytesWritten = 1152 * event->GetFrameNumber() * m_bytesPerSample;
    }
    m_lastFrame = event->GetFrameNumber();

    if ( m_outputInfo->samples_per_second <= 0 || event->GetFrameNumber() < 2 )
    {
        return;
    }

    total = m_totalBytesWritten /
            ( m_bytesPerSample * m_outputInfo->samples_per_second );
    total %= 86400;

    hours = total / 3600;
    minutes = (total / 60) % 60;
    seconds = total % 60;
    mtie = new MediaTimeInfoEvent( hours, minutes, seconds, 0, total, 0 );
    m_pTarget->AcceptEvent( mtie );
}

void
SoundCardPMO::_EventBufferThreadHook( void* arg )
{
	return ((SoundCardPMO*)arg)->EventBufferThread();
}

void
SoundCardPMO::EventBufferThread( void )
{
	PRINT(( "SoundCardPMO::EventBufferThread:enter\n" ));

	// Don't do anything until resume is called.
	m_pPauseSem->Wait();

	// Prebuffer wait
	PRINT(( "SoundCardPMO::EventBufferThread: prebuffering...\n" ));
	PreBuffer();

	PRINT(( "SoundCardPMO::EventBufferThread: okay now ready to spin!\n" ));

	// don't know why this is necessary, but m_pInputBuffer is
	// still null sometimes.
	EventBuffer* eb = NULL;
	while ( !eb )
	{
		if ( m_bExit ) return;
		eb = dynamic_cast<EventBuffer*>( m_pInputBuffer );
		WasteTime();
	}

	// Handles event.
	while ( !m_bExit )
	{

		// First, wait for the PMO_Init event to propagate.
		if ( !m_properlyInitialized )
		{
			PRINT(( "SoundCardPMO::EventBufferThread:fetching possible init event\n" ));
			Event* event = eb->GetEvent();
			if ( event == NULL )
			{
				m_pLmc->Wake();
				WasteTime();
				continue;
			}

			if ( event->Type() == PMO_Init )
			{
				if ( IsError( Init( ((PMOInitEvent*)event)->GetInfo() ) ) )
				{
					delete event;
					break;
				}
			}
			delete event;

			continue;
		}

		// Now, work hand in hand with the Player method, dealing with
		// events as requested by the Player method.
		m_eventSem.Wait();
//		Event*	event = eb->GetEvent();
		if ( m_event != NULL )
		{
			if ( m_event->Type() == PMO_Init )
			{
				PRINT(( "SoundCardPMO::EventBufferThread:PMO_Init recv'd\n" ));
			}
			else if ( m_event->Type() == PMO_Reset )
			{
				PRINT(( "SoundCardPMO::EventBufferThread:PMO_Reset recv'd\n" ));
			}
			else if ( m_event->Type() == PMO_Info )
			{
				//PRINT(( "SoundCardPMO::EventBufferThread:PMO_Info recv'd\n" ));
                HandleTimeInfoEvent( (PMOTimeInfoEvent*)m_event );
			}
			else if ( m_event->Type() == PMO_Quit )
			{
				PRINT(( "SoundCardPMO::EventBufferThread:PMO_Quit recv'd\n" ));
				delete m_event;
                m_event = NULL;
				m_eventSem.Signal();
				if ( WaitForDrain() )
				{
					m_pTarget->AcceptEvent( new Event( INFO_DoneOutputting ) );
				}
				return;
			}
			delete m_event;
            m_event = NULL;
		}
//		m_eventSem.Signal();	// tell Player I'm done with the event.
	}
	PRINT(( "SoundCardPMO::EventBufferThread:exiting\n" ));
}

void
SoundCardPMO::_DummyPlayerHook( void* arg )
{
	return ((SoundCardPMO*)arg)->DummyPlayer();
}

void
SoundCardPMO::DummyPlayer( void )
{
	void*			bufferIn;
	size_t			bytesToCopy = m_dataSize;
	size_t			bytesCopied = 0;
	Error			err;
	EventBuffer*	eb = (EventBuffer*)m_pInputBuffer;

	while ( !m_bExit )
	{
		m_pauseLock.Lock();

		err = eb->BeginRead( bufferIn, bytesToCopy );
		if ( err == kError_EventPending )
		{
			Event* event = eb->GetEvent();
			delete event;
		}
		else
		{
			if ( IsError( err ) )
			{
				PRINT(( "SoundCardPMO::DummyPlayer: read failed(%d)\n", err ));
				bytesCopied = 0;
			}
			else
			{
				bytesCopied = bytesToCopy;
			}
			eb->EndRead( bytesCopied );
		}
		m_pauseLock.Unlock();
		PRINT(( "FIXME: %d bytes read\n", bytesCopied ));
	}
}

void
SoundCardPMO::_PlayerHook(
				void*							cookie,
				void*							buffer,
				size_t							size,
				const media_raw_audio_format&	format
				)
{
	((SoundCardPMO*)cookie)->Player( buffer, size, format );
}

// this function is a mess. evil, i know.
void
SoundCardPMO::Player(
				void*							bufferOut,
				size_t							size,
				const media_raw_audio_format&	format
				)
{
	Error	err;
	size_t	bytesToCopy;
	size_t	bytesCopied;
	void*	bufferIn;

	if ( format.format != m_format.format )
	{
		PRINT(( "SoundCardPMO::Player:invalid format\n" ));
		return;
	}

	bytesToCopy = size;

	if ( m_pauseLock.LockWithTimeout( 0.1e6 ) != B_OK )
	{
		return;
	}

	EventBuffer*	eb = (EventBuffer*)m_pInputBuffer;
	if ( !eb )
	{
		PRINT(( "SoundCardPMO::Player:null m_pInputBuffer\n" ));
		return;
	}

	for ( int retry = 0; retry < 10; retry++ )
	{
		bytesToCopy = size;
		err = eb->BeginRead( bufferIn, bytesToCopy );
		if ( IsntError( err ) )
		{
			break;
		}

		if ( err == kError_EventPending )
		{
            m_event = eb->GetEvent();
            m_eventSem.Signal();
#if 0
			Event*	event = eb->PeekEvent();
			switch ( event->Type() )
			{
			case PMO_Init:
				PRINT(( "SoundCardPMO::Player: PMO_Init recv'd\n" ));
				m_eventSem.Signal();
				m_eventSem.Wait();
				break;
			case PMO_Reset:
				PRINT(( "SoundCardPMO::Player: PMO_Reset recv'd\n" ));
				m_eventSem.Signal();
				m_eventSem.Wait();
				break;
			case PMO_Info:
				//PRINT(( "SoundCardPMO::Player: PMO_Info recv'd\n" ));
				event = eb->GetEvent();
                HandleTimeInfoEvent( (PMOTimeInfoEvent*)event );
				delete event;
				break;
			case PMO_Quit:
				PRINT(( "SoundCardPMO::Player: PMO_Quit recv'd\n" ));
				m_eventSem.Signal();
				m_eventSem.Wait();
				break;
			}
#endif
		}
		else if ( err == kError_NoDataAvail )
		{
			PRINT(( "SoundCardPMO::Player: killing some time to wait for data to pour in\n" ));
			m_pLmc->Wake();
			WasteTime();
		}
	}

	if ( IsError( err ) )
	{
		PRINT(( "SoundCardPMO::Player: error reading from the buffer(%d)\n", err ));
		bytesCopied = 0;
	}
	else
	{
		if ( bytesToCopy < size )
		{
			PRINT(( "SoundCardPMO::Player: end of data?\n" ));
		}
		memcpy( bufferOut, bufferIn, bytesToCopy );
#if DEBUG_SAVE_PCM
		fwrite( bufferIn, 1, bytesToCopy, m_pcmSaveFile );
#endif
		bytesCopied = bytesToCopy;
	}
	eb->EndRead( bytesCopied );
    m_totalBytesWritten += bytesCopied;

	m_pauseLock.Unlock();
    m_pLmc->Wake();
    UpdateBufferStatus();

	return;
}

void
SoundCardPMO::_NotifierHook(
				void*						cookie,
				BSoundPlayer::sound_player_notification
											what,
				...
				)
{
	((SoundCardPMO*)cookie)->Notifier( what );
}

void
SoundCardPMO::Notifier( BSoundPlayer::sound_player_notification what )
{
	switch ( what )
	{
	case BSoundPlayer::B_STARTED:
		PRINT(( "Notifier: SoundPlayer started\n" ));
		break;
	case BSoundPlayer::B_STOPPED:
		PRINT(( "Notifier: SoundPlayer stoped\n" ));
		break;
	case BSoundPlayer::B_SOUND_DONE:
		PRINT(( "Notifier: SoundPlayer sound done\n" ));
		break;
	default:
		PRINT(( "Notifier: unknown event is reported\n" ));
		break;
	}
}
// vi: set ts=4:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -