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