/* This file is part of the KDE project. Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 or 3 of the License. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "audiograph.h" #include "quicktimeaudioplayer.h" #include "medianode.h" QT_BEGIN_NAMESPACE namespace Phonon { namespace QT7 { AudioGraph::AudioGraph(MediaNode *root) : MediaNode(AudioGraphNode, 0, root), m_root(root) { m_audioGraphRef = 0; m_initialized = false; m_startedLogically = false; m_graphCannotPlay = false; m_paused = false; } AudioGraph::~AudioGraph() { deleteGraph(); } void AudioGraph::startAllOverFromScratch() { MediaNodeEvent event(MediaNodeEvent::AudioGraphAboutToBeDeleted, this); m_root->notify(&event); deleteGraph(); } void AudioGraph::deleteGraph() { if (m_audioGraphRef){ AUGraphStop(m_audioGraphRef); AUGraphUninitialize(m_audioGraphRef); AUGraphClose(m_audioGraphRef); DisposeAUGraph(m_audioGraphRef); m_initialized = false; m_graphCannotPlay = false; DEBUG_AUDIO_GRAPH("Graph ref in" << int(this) << "is deleted") } } MediaNode *AudioGraph::root() { return m_root; } AUGraph AudioGraph::audioGraphRef() { return m_audioGraphRef; } void AudioGraph::setStatusCannotPlay() { DEBUG_AUDIO_GRAPH("Graph" << int(this) << "received 'cannot play' request") if (!m_graphCannotPlay){ stop(); m_graphCannotPlay = true; MediaNodeEvent e(MediaNodeEvent::AudioGraphCannotPlay, this); m_root->notify(&e); } } void AudioGraph::rebuildGraph() { DEBUG_AUDIO_GRAPH("Graph" << int(this) << "is rebuilding") startAllOverFromScratch(); if (!openAndInit()){ setStatusCannotPlay(); } else { tryStartGraph(); m_graphCannotPlay = false; } } bool AudioGraph::graphCannotPlay() { return m_graphCannotPlay; } void AudioGraph::updateStreamSpecifications() { if (!m_initialized){ if (m_graphCannotPlay) rebuildGraph(); return; } AudioConnection rootConnection(m_root); bool updateOk = updateStreamSpecificationRecursive(&rootConnection); if (!updateOk){ DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.") rebuildGraph(); } } bool AudioGraph::updateStreamSpecificationRecursive(AudioConnection *connection) { bool updateOk = connection->updateStreamSpecification(); if (!updateOk) return false; for (int i=0; im_sink->m_audioSinkList.size(); ++i){ if (!updateStreamSpecificationRecursive(connection->m_sink->m_audioSinkList[i])) return false; } return true; } bool AudioGraph::openAndInit() { OSStatus err; err = NewAUGraph(&m_audioGraphRef); BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false) MediaNodeEvent eventNew(MediaNodeEvent::NewAudioGraph, this); m_root->notify(&eventNew); AudioConnection rootConnection(m_root); createAndConnectAuNodesRecursive(&rootConnection); err = AUGraphOpen(m_audioGraphRef); BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false) if (!createAudioUnitsRecursive(&rootConnection)) return false; err = AUGraphInitialize(m_audioGraphRef); BACKEND_ASSERT3(err == noErr, "Could not initialize audio graph.", NORMAL_ERROR, false) m_initialized = true; MediaNodeEvent eventInit(MediaNodeEvent::AudioGraphInitialized, this); m_root->notify(&eventInit); return true; } void AudioGraph::createAndConnectAuNodesRecursive(AudioConnection *connection) { connection->m_sink->m_audioNode->createAndConnectAUNodes(); for (int i=0; im_sink->m_audioSinkList.size(); ++i){ AudioConnection *c = connection->m_sink->m_audioSinkList[i]; createAndConnectAuNodesRecursive(c); bool ok = c->connect(this); BACKEND_ASSERT2(ok, "Could not connect an audio nodes pair in the audio graph.", NORMAL_ERROR) } } bool AudioGraph::createAudioUnitsRecursive(AudioConnection *connection) { connection->m_sink->m_audioNode->createAudioUnits(); bool ok = connection->updateStreamSpecification(); if (!ok) return false; for (int i=0; im_sink->m_audioSinkList.size(); ++i){ if (!createAudioUnitsRecursive(connection->m_sink->m_audioSinkList[i])) return false; } return true; } void AudioGraph::tryStartGraph() { // The graph will only start if the background AUGraph // is valid. Therefore we just try. If it fails, user // actions like connect etc. migh make the graph valid // at a later point. if (m_startedLogically && !isRunning()){ OSStatus err = AUGraphStart(m_audioGraphRef); if (err == noErr) DEBUG_AUDIO_GRAPH("Graph" << int(this) << "started") else DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not start") } } bool AudioGraph::isRunning() { Boolean running = false; AUGraphIsRunning(m_audioGraphRef, &running); return running; } void AudioGraph::setPaused(bool pause) { // This function should only make // a difference if the graph is // running before pausing. if (pause){ if (isRunning()){ stop(); m_paused = true; } } else if (m_paused){ start(); m_paused = false; } } void AudioGraph::connectLate(AudioConnection *connection) { MediaNodeEvent event(MediaNodeEvent::NewAudioGraph, this); connection->m_sink->notify(&event); if (!m_initialized) return; DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "create and connect audio sink after init:" << int(connection->m_sink->m_audioNode)) AudioConnection startConnection(connection->m_source); createAndConnectAuNodesRecursive(&startConnection); if (!createAudioUnitsRecursive(&startConnection)){ DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.") rebuildGraph(); } } void AudioGraph::disconnectLate(AudioConnection *connection) { if (!m_initialized) return; DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "disconnect audio sink after init:" << int(connection->m_sink->m_audioNode)) if (!connection->disconnect(this)){ DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not disconnect audio sink. Rebuild.") rebuildGraph(); } } void AudioGraph::update() { if (m_startedLogically){ if (m_initialized){ // Quick solution: AUGraphUpdate(m_audioGraphRef, 0); tryStartGraph(); } else rebuildGraph(); } } int AudioGraph::nodeCount() { if (!m_audioGraphRef) return 0; UInt32 count; AUGraphGetNodeCount(m_audioGraphRef, &count); return int(count); } void AudioGraph::prepare() { if (!m_initialized) rebuildGraph(); } void AudioGraph::start() { // Start does not mean 'start to play // music'. It means 'prepare to receive // audio from the player units'. DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to start (cannot play:" << m_graphCannotPlay << ")") m_startedLogically = true; if (m_graphCannotPlay) return; if (!m_initialized) rebuildGraph(); if (!m_graphCannotPlay) tryStartGraph(); } void AudioGraph::stop() { DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to stop") if (m_audioGraphRef) AUGraphStop(m_audioGraphRef); m_startedLogically = false; } void AudioGraph::notify(const MediaNodeEvent *event, bool propagate) { switch (event->type()){ case MediaNodeEvent::StartConnectionChange: if (m_graphCannotPlay) startAllOverFromScratch(); break; case MediaNodeEvent::EndConnectionChange: update(); break; default: break; } m_root->notify(event, propagate); } }} // namespace Phonon::QT7 QT_END_NAMESPACE