1/* 2 * Copyright 1999, Be Incorporated. 3 * Copyright (c) 1999-2000, Eric Moon. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions, and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions, and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 33#include "NodeHarnessWin.h" 34#include "ToneProducer.h" 35#include <app/Application.h> 36#include <interface/Button.h> 37#include <storage/Entry.h> 38#include <media/MediaRoster.h> 39#include <media/MediaAddOn.h> 40#include <media/TimeSource.h> 41#include <media/MediaTheme.h> 42#include <stdio.h> 43 44const int32 BUTTON_CONNECT = 'Cnct'; 45const int32 BUTTON_START = 'Strt'; 46const int32 BUTTON_STOP = 'Stop'; 47 48#define TEST_WITH_AUDIO 1 49 50// -------------------- 51// static utility functions 52static void ErrorCheck(status_t err, const char* msg) 53{ 54 if (err) 55 { 56 fprintf(stderr, "* FATAL ERROR (%s): %s\n", strerror(err), msg); 57 exit(1); 58 } 59} 60 61// -------------------- 62// NodeHarnessWin implementation 63NodeHarnessWin::NodeHarnessWin(BRect frame, const char *title) 64 : BWindow(frame, title, B_TITLED_WINDOW, B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS), 65 mToneNode(NULL), mIsConnected(false), mIsRunning(false) 66{ 67 // build the UI 68 BRect r(10, 10, 100, 40); 69 mConnectButton = new BButton(r, "Connect", "Connect", new BMessage(BUTTON_CONNECT)); 70 mConnectButton->SetEnabled(true); 71 AddChild(mConnectButton); 72 r.OffsetBy(0, 40); 73 mStartButton = new BButton(r, "Start", "Start", new BMessage(BUTTON_START)); 74 mStartButton->SetEnabled(false); 75 AddChild(mStartButton); 76 r.OffsetBy(0, 40); 77 mStopButton = new BButton(r, "Stop", "Stop", new BMessage(BUTTON_STOP)); 78 mStopButton->SetEnabled(false); 79 AddChild(mStopButton); 80 81 // e.moon 2jun99: create the node 82 BMediaRoster* roster = BMediaRoster::Roster(); 83 mToneNode = new ToneProducer(); 84 85 status_t err = roster->RegisterNode(mToneNode); 86 ErrorCheck(err, "unable to register ToneProducer node!\n"); 87 // make sure the Media Roster knows that we're using the node 88 roster->GetNodeFor(mToneNode->Node().node, &mConnection.producer); 89} 90 91NodeHarnessWin::~NodeHarnessWin() 92{ 93 BMediaRoster* r = BMediaRoster::Roster(); 94 95 // tear down the node network 96 if (mIsRunning) StopNodes(); 97 if (mIsConnected) 98 { 99 status_t err; 100 101 // Ordinarily we'd stop *all* of the nodes in the chain at this point. However, 102 // one of the nodes is the System Mixer, and stopping the Mixer is a Bad Idea (tm). 103 // So, we just disconnect from it, and release our references to the nodes that 104 // we're using. We *are* supposed to do that even for global nodes like the Mixer. 105 err = r->Disconnect(mConnection.producer.node, mConnection.source, 106 mConnection.consumer.node, mConnection.destination); 107 if (err) 108 { 109 fprintf(stderr, "* Error disconnecting nodes: %ld (%s)\n", err, strerror(err)); 110 } 111 112 r->ReleaseNode(mConnection.producer); 113 r->ReleaseNode(mConnection.consumer); 114 } 115} 116 117void 118NodeHarnessWin::Quit() 119{ 120 be_app->PostMessage(B_QUIT_REQUESTED); 121 BWindow::Quit(); 122} 123 124void 125NodeHarnessWin::MessageReceived(BMessage *msg) 126{ 127 status_t err; 128 129 switch (msg->what) 130 { 131 case BUTTON_CONNECT: 132 mIsConnected = true; 133 134 // set the button states appropriately 135 mConnectButton->SetEnabled(false); 136 mStartButton->SetEnabled(true); 137 138 // set up the node network 139 { 140 BMediaRoster* r = BMediaRoster::Roster(); 141 142 // connect to the mixer 143 err = r->GetAudioMixer(&mConnection.consumer); 144 ErrorCheck(err, "unable to get the system mixer"); 145 146 // fire off a window with the ToneProducer's controls in it 147 BParameterWeb* web; 148 r->GetParameterWebFor(mConnection.producer, &web); 149 BView* view = BMediaTheme::ViewFor(web); 150 BWindow* win = new BWindow(BRect(250, 200, 300, 300), "Controls", 151 B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS); 152 win->AddChild(view); 153 win->ResizeTo(view->Bounds().Width(), view->Bounds().Height()); 154 win->Show(); 155 156 // set the producer's time source to be the "default" time source, which 157 // the Mixer uses too. 158 r->GetTimeSource(&mTimeSource); 159 r->SetTimeSourceFor(mConnection.producer.node, mTimeSource.node); 160 161 // got the nodes; now we find the endpoints of the connection 162 media_input mixerInput; 163 media_output soundOutput; 164 int32 count = 1; 165 err = r->GetFreeOutputsFor(mConnection.producer, &soundOutput, 1, &count); 166 ErrorCheck(err, "unable to get a free output from the producer node"); 167 count = 1; 168 err = r->GetFreeInputsFor(mConnection.consumer, &mixerInput, 1, &count); 169 ErrorCheck(err, "unable to get a free input to the mixer"); 170 171 // got the endpoints; now we connect it! 172 media_format format; 173 format.type = B_MEDIA_RAW_AUDIO; 174 format.u.raw_audio = media_raw_audio_format::wildcard; 175 err = r->Connect(soundOutput.source, mixerInput.destination, &format, &soundOutput, &mixerInput); 176 ErrorCheck(err, "unable to connect nodes"); 177 178 // the inputs and outputs might have been reassigned during the 179 // nodes' negotiation of the Connect(). That's why we wait until 180 // after Connect() finishes to save their contents. 181 mConnection.format = format; 182 mConnection.source = soundOutput.source; 183 mConnection.destination = mixerInput.destination; 184 185 // Set an appropriate run mode for the producer 186 r->SetRunModeNode(mConnection.producer, BMediaNode::B_INCREASE_LATENCY); 187 } 188 break; 189 190 case BUTTON_START: 191 mStartButton->SetEnabled(false); 192 mStopButton->SetEnabled(true); 193 194 // start the producer running 195 { 196 BMediaRoster* r = BMediaRoster::Roster(); 197 BTimeSource* ts = r->MakeTimeSourceFor(mConnection.producer); 198 if (!ts) 199 { 200 fprintf(stderr, "* ERROR - MakeTimeSourceFor(producer) returned NULL!\n"); 201 exit(1); 202 } 203 204 // make sure we give the producer enough time to run buffers through 205 // the node chain, otherwise it'll start up already late 206 bigtime_t latency = 0; 207 r->GetLatencyFor(mConnection.producer, &latency); 208 r->StartNode(mConnection.producer, ts->Now() + latency); 209 ts->Release(); 210 mIsRunning = true; 211 } 212 break; 213 214 case BUTTON_STOP: 215 StopNodes(); 216 break; 217 218 default: 219 BWindow::MessageReceived(msg); 220 break; 221 } 222} 223 224// Private routines 225void 226NodeHarnessWin::StopNodes() 227{ 228 mStartButton->SetEnabled(true); 229 mStopButton->SetEnabled(false); 230 231 // generally, one only stops the initial producer(s), and lets the rest of the node 232 // chain just follow along as the buffers drain out. 233 { 234 BMediaRoster* r = BMediaRoster::Roster(); 235 r->StopNode(mConnection.producer, 0, true); // synchronous stop 236 } 237} 238