xref: /haiku/src/apps/soundrecorder/RecorderWindow.cpp (revision 739fd34cf534560c5bf171ec6e414b85532c1bda)
157e2f323SJérôme Duval /*
257e2f323SJérôme Duval  * Copyright 2005, Jérôme Duval. All rights reserved.
3*739fd34cSBarrett  * Copyright 2014, Dario Casalinuovo. All rights reserved.
457e2f323SJérôme Duval  * Distributed under the terms of the MIT License.
557e2f323SJérôme Duval  *
6ad0c65eaSJérôme Duval  * Inspired by SoundCapture from Be newsletter (Media Kit Basics:
7ad0c65eaSJérôme Duval  *	Consumers and Producers)
857e2f323SJérôme Duval  */
957e2f323SJérôme Duval 
1057e2f323SJérôme Duval #include <Application.h>
1157e2f323SJérôme Duval #include <Alert.h>
1257e2f323SJérôme Duval #include <Debug.h>
1357e2f323SJérôme Duval #include <Screen.h>
1457e2f323SJérôme Duval #include <Button.h>
1557e2f323SJérôme Duval #include <CheckBox.h>
1657e2f323SJérôme Duval #include <TextControl.h>
1757e2f323SJérôme Duval #include <MenuField.h>
1857e2f323SJérôme Duval #include <PopUpMenu.h>
1957e2f323SJérôme Duval #include <MenuItem.h>
2057e2f323SJérôme Duval #include <Box.h>
2157e2f323SJérôme Duval #include <ScrollView.h>
2257e2f323SJérôme Duval #include <Beep.h>
2357e2f323SJérôme Duval #include <StringView.h>
2457e2f323SJérôme Duval #include <String.h>
2557e2f323SJérôme Duval #include <Slider.h>
2657e2f323SJérôme Duval #include <Message.h>
2757e2f323SJérôme Duval 
2857e2f323SJérôme Duval #include <Path.h>
2957e2f323SJérôme Duval #include <FindDirectory.h>
3057e2f323SJérôme Duval #include <MediaAddOn.h>
3157e2f323SJérôme Duval 
3257e2f323SJérôme Duval #include <SoundPlayer.h>
3357e2f323SJérôme Duval 
3457e2f323SJérôme Duval #include <assert.h>
3557e2f323SJérôme Duval #include <stdio.h>
363aeed660SJérôme Duval #include <strings.h>
3757e2f323SJérôme Duval #include <stdlib.h>
3857e2f323SJérôme Duval #include <ctype.h>
3957e2f323SJérôme Duval #include <unistd.h>
40338b8dc3SIngo Weinhold #include <fcntl.h>
4157e2f323SJérôme Duval 
4257e2f323SJérôme Duval #include <MediaRoster.h>
4357e2f323SJérôme Duval #include <TimeSource.h>
44019ed09bSJérôme Duval #include <NodeInfo.h>
4557e2f323SJérôme Duval 
46*739fd34cSBarrett #include "SoundUtils.h"
4757e2f323SJérôme Duval #include "RecorderWindow.h"
4857e2f323SJérôme Duval #include "FileUtils.h"
4957e2f323SJérôme Duval 
5057e2f323SJérôme Duval #if ! NDEBUG
5157e2f323SJérôme Duval #define FPRINTF(args) fprintf args
5257e2f323SJérôme Duval #else
5357e2f323SJérôme Duval #define FPRINTF(args)
5457e2f323SJérôme Duval #endif
5557e2f323SJérôme Duval 
5657e2f323SJérôme Duval #define DEATH FPRINTF
5757e2f323SJérôme Duval #define CONNECT FPRINTF
5857e2f323SJérôme Duval #define WINDOW FPRINTF
5957e2f323SJérôme Duval 
60546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT
61546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "RecorderWindow"
623ee96407SJérôme Duval 
633ee96407SJérôme Duval 
6457e2f323SJérôme Duval // default window positioning
6557e2f323SJérôme Duval static const float MIN_WIDTH = 400.0f;
66b271ff3fSPhilippe Saint-Pierre static const float MIN_HEIGHT = 175.0f;
6757e2f323SJérôme Duval static const float XPOS = 100.0f;
6857e2f323SJérôme Duval static const float YPOS = 200.0f;
6957e2f323SJérôme Duval 
70ad0c65eaSJérôme Duval #define FOURCC(a,b,c,d)	((((uint32)(d)) << 24) | (((uint32)(c)) << 16) \
71ad0c65eaSJérôme Duval 	| (((uint32)(b)) << 8) | ((uint32)(a)))
7257e2f323SJérôme Duval 
7357e2f323SJérôme Duval struct riff_struct
7457e2f323SJérôme Duval {
7557e2f323SJérôme Duval 	uint32 riff_id; // 'RIFF'
7657e2f323SJérôme Duval 	uint32 len;
7757e2f323SJérôme Duval 	uint32 wave_id;	// 'WAVE'
7857e2f323SJérôme Duval };
7957e2f323SJérôme Duval 
8057e2f323SJérôme Duval struct chunk_struct
8157e2f323SJérôme Duval {
8257e2f323SJérôme Duval 	uint32 fourcc;
8357e2f323SJérôme Duval 	uint32 len;
8457e2f323SJérôme Duval };
8557e2f323SJérôme Duval 
8657e2f323SJérôme Duval struct format_struct
8757e2f323SJérôme Duval {
8857e2f323SJérôme Duval 	uint16 format_tag;
8957e2f323SJérôme Duval 	uint16 channels;
9057e2f323SJérôme Duval 	uint32 samples_per_sec;
9157e2f323SJérôme Duval 	uint32 avg_bytes_per_sec;
9257e2f323SJérôme Duval 	uint16 block_align;
9357e2f323SJérôme Duval 	uint16 bits_per_sample;
9457e2f323SJérôme Duval };
9557e2f323SJérôme Duval 
9657e2f323SJérôme Duval 
9757e2f323SJérôme Duval struct wave_struct
9857e2f323SJérôme Duval {
9957e2f323SJérôme Duval 	struct riff_struct riff;
10057e2f323SJérôme Duval 	struct chunk_struct format_chunk;
10157e2f323SJérôme Duval 	struct format_struct format;
10257e2f323SJérôme Duval 	struct chunk_struct data_chunk;
10357e2f323SJérôme Duval };
10457e2f323SJérôme Duval 
10557e2f323SJérôme Duval 
10671302e7cSJonas Sundström RecorderWindow::RecorderWindow()
10771302e7cSJonas Sundström 	:
10871302e7cSJonas Sundström 	BWindow(BRect(XPOS, YPOS, XPOS + MIN_WIDTH, YPOS + MIN_HEIGHT),
109560ff447SJonas Sundström 		B_TRANSLATE_SYSTEM_NAME("SoundRecorder"), B_TITLED_WINDOW,
11071302e7cSJonas Sundström 		B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE),
11157e2f323SJérôme Duval 	fPlayer(NULL),
1125fa77532SJérôme Duval 	fSoundList(NULL),
11357e2f323SJérôme Duval 	fPlayFile(NULL),
11457e2f323SJérôme Duval 	fPlayTrack(NULL),
1157942339dSJérôme Duval 	fPlayFrames(0),
116948356deSJérôme Duval 	fLooping(false),
1175fa77532SJérôme Duval 	fSavePanel(NULL),
118cff7eda5SWim van der Meer 	fInitCheck(B_OK)
11957e2f323SJérôme Duval {
12057e2f323SJérôme Duval 	fRoster = NULL;
12157e2f323SJérôme Duval 	fRecordButton = NULL;
12257e2f323SJérôme Duval 	fPlayButton = NULL;
12357e2f323SJérôme Duval 	fStopButton = NULL;
12457e2f323SJérôme Duval 	fSaveButton = NULL;
125948356deSJérôme Duval 	fLoopButton = NULL;
12657e2f323SJérôme Duval 	fInputField = NULL;
127*739fd34cSBarrett 	fRecorder = NULL;
12857e2f323SJérôme Duval 	fRecording = false;
12957e2f323SJérôme Duval 	fTempCount = -1;
13057e2f323SJérôme Duval 	fButtonState = btnPaused;
13157e2f323SJérôme Duval 
13257e2f323SJérôme Duval 	CalcSizes(MIN_WIDTH, MIN_HEIGHT);
13357e2f323SJérôme Duval 
1345fa77532SJérôme Duval 	fInitCheck = InitWindow();
1355fa77532SJérôme Duval 	if (fInitCheck != B_OK) {
136bdb1d3acSJérôme Duval 		if (fInitCheck == B_NAME_NOT_FOUND)
1373ee96407SJérôme Duval 			ErrorAlert(B_TRANSLATE("Cannot find default audio hardware"),
1383ee96407SJérôme Duval 				fInitCheck);
139bdb1d3acSJérôme Duval 		else
1403ee96407SJérôme Duval 			ErrorAlert(B_TRANSLATE("Cannot connect to media server"),
1413ee96407SJérôme Duval 				fInitCheck);
1425fa77532SJérôme Duval 		PostMessage(B_QUIT_REQUESTED);
1435fa77532SJérôme Duval 	} else
1445fa77532SJérôme Duval 		Show();
14557e2f323SJérôme Duval }
14657e2f323SJérôme Duval 
1478e32a27eSPhilippe Saint-Pierre 
14857e2f323SJérôme Duval RecorderWindow::~RecorderWindow()
14957e2f323SJérôme Duval {
150*739fd34cSBarrett 	//  The MediaRecorder have to be deleted, the dtor
151*739fd34cSBarrett 	//  disconnect it from the media_kit.
152*739fd34cSBarrett 	delete fRecorder;
153*739fd34cSBarrett 
15457e2f323SJérôme Duval 	delete fPlayer;
1557942339dSJérôme Duval 
1567942339dSJérôme Duval 	if (fPlayTrack && fPlayFile)
1577942339dSJérôme Duval 		fPlayFile->ReleaseTrack(fPlayTrack);
158*739fd34cSBarrett 
1597942339dSJérôme Duval 	if (fPlayFile)
1607942339dSJérôme Duval 		delete fPlayFile;
1617942339dSJérôme Duval 	fPlayTrack = NULL;
1627942339dSJérôme Duval 	fPlayFile = NULL;
1637942339dSJérôme Duval 
16457e2f323SJérôme Duval 	//	Clean up items in list view.
16557e2f323SJérôme Duval 	if (fSoundList) {
16657e2f323SJérôme Duval 		fSoundList->DeselectAll();
167ad0c65eaSJérôme Duval 		for (int i = 0; i < fSoundList->CountItems(); i++) {
168ad0c65eaSJérôme Duval 			WINDOW((stderr, "clean up item %d\n", i+1));
169ad0c65eaSJérôme Duval 			SoundListItem* item = dynamic_cast<SoundListItem *>(fSoundList->ItemAt(i));
17057e2f323SJérôme Duval 			if (item) {
171b1ed9a63SJérôme Duval 				if (item->IsTemp())
17257e2f323SJérôme Duval 					item->Entry().Remove();	//	delete temp file
17357e2f323SJérôme Duval 				delete item;
17457e2f323SJérôme Duval 			}
17557e2f323SJérôme Duval 		}
17657e2f323SJérôme Duval 		fSoundList->MakeEmpty();
17757e2f323SJérôme Duval 	}
17857e2f323SJérôme Duval 	//	Clean up currently recording file, if any.
17957e2f323SJérôme Duval 	fRecEntry.Remove();
18057e2f323SJérôme Duval 	fRecEntry.Unset();
18105ea8535SKarsten Heimrich 
18205ea8535SKarsten Heimrich 	delete fSavePanel;
18357e2f323SJérôme Duval }
18457e2f323SJérôme Duval 
18557e2f323SJérôme Duval 
1865fa77532SJérôme Duval status_t
1875fa77532SJérôme Duval RecorderWindow::InitCheck()
1885fa77532SJérôme Duval {
1895fa77532SJérôme Duval 	return fInitCheck;
1905fa77532SJérôme Duval }
1915fa77532SJérôme Duval 
1925fa77532SJérôme Duval 
19357e2f323SJérôme Duval void
194b1ed9a63SJérôme Duval RecorderWindow::CalcSizes(float min_width, float min_height)
19557e2f323SJérôme Duval {
19657e2f323SJérôme Duval 	//	Set up size limits based on new screen size
1975e99b7dfSJérôme Duval 	BScreen screen(this);
198b1ed9a63SJérôme Duval 	BRect rect = screen.Frame();
199b1ed9a63SJérôme Duval 	float width = rect.Width() - 12;
200b1ed9a63SJérôme Duval 	SetSizeLimits(min_width, width, min_height, rect.Height() - 24);
20157e2f323SJérôme Duval 
20257e2f323SJérôme Duval 	//	Don't zoom to cover all of screen; user can resize last bit if necessary.
20357e2f323SJérôme Duval 	//	This leaves other windows visible.
204b1ed9a63SJérôme Duval 	if (width > 640)
205b1ed9a63SJérôme Duval 		width = 640 + (width - 640) / 2;
206b1ed9a63SJérôme Duval 	SetZoomLimits(width, rect.Height() - 24);
20757e2f323SJérôme Duval }
20857e2f323SJérôme Duval 
20957e2f323SJérôme Duval 
21057e2f323SJérôme Duval status_t
21157e2f323SJérôme Duval RecorderWindow::InitWindow()
21257e2f323SJérôme Duval {
21357e2f323SJérôme Duval 	BPopUpMenu * popup = 0;
21457e2f323SJérôme Duval 	status_t error;
21557e2f323SJérôme Duval 
21657e2f323SJérôme Duval 	try {
21757e2f323SJérôme Duval 		//	Find temp directory for recorded sounds.
21857e2f323SJérôme Duval 		BPath path;
2194b7e2196SIngo Weinhold 		if (!(error = find_directory(B_SYSTEM_TEMP_DIRECTORY, &path)))
22057e2f323SJérôme Duval 			error = fTempDir.SetTo(path.Path());
221b1ed9a63SJérôme Duval 		if (error < 0)
22257e2f323SJérôme Duval 			goto bad_mojo;
22357e2f323SJérôme Duval 
22457e2f323SJérôme Duval 		//	Make sure the media roster is there (which means the server is there).
22557e2f323SJérôme Duval 		fRoster = BMediaRoster::Roster(&error);
226b1ed9a63SJérôme Duval 		if (!fRoster)
22757e2f323SJérôme Duval 			goto bad_mojo;
22857e2f323SJérôme Duval 
22957e2f323SJérôme Duval 		error = fRoster->GetAudioInput(&fAudioInputNode);
230b1ed9a63SJérôme Duval 		if (error < B_OK) //	there's no input?
23157e2f323SJérôme Duval 			goto bad_mojo;
23257e2f323SJérôme Duval 
23357e2f323SJérôme Duval 		error = fRoster->GetAudioMixer(&fAudioMixerNode);
234b1ed9a63SJérôme Duval 		if (error < B_OK) //	there's no mixer?
23557e2f323SJérôme Duval 			goto bad_mojo;
23657e2f323SJérôme Duval 
237*739fd34cSBarrett 		fRecorder = new BMediaRecorder("Sound Recorder",
238*739fd34cSBarrett 			B_MEDIA_RAW_AUDIO);
239*739fd34cSBarrett 
240*739fd34cSBarrett 		if (fRecorder->InitCheck() < B_OK)
24157e2f323SJérôme Duval 			goto bad_mojo;
24257e2f323SJérôme Duval 
243*739fd34cSBarrett 		// Set the node to accept only audio data
244*739fd34cSBarrett 		media_format output_format;
245*739fd34cSBarrett 		output_format.type = B_MEDIA_RAW_AUDIO;
246*739fd34cSBarrett 		output_format.u.raw_audio = media_raw_audio_format::wildcard;
247*739fd34cSBarrett 		fRecorder->SetAcceptedFormat(output_format);
248*739fd34cSBarrett 
24957e2f323SJérôme Duval 		//	Create the window header with controls
25057e2f323SJérôme Duval 		BRect r(Bounds());
25157e2f323SJérôme Duval 		r.bottom = r.top + 175;
252*739fd34cSBarrett 		BBox *background = new BBox(r, "_background",
253*739fd34cSBarrett 			B_FOLLOW_LEFT_RIGHT	| B_FOLLOW_TOP, B_WILL_DRAW
254*739fd34cSBarrett 			| B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
255*739fd34cSBarrett 
25657e2f323SJérôme Duval 		AddChild(background);
25757e2f323SJérôme Duval 
25857e2f323SJérôme Duval 		r = background->Bounds();
259a4797804SJérôme Duval 		r.left = 0;
2608881ad65SJérôme Duval 		r.right = r.left + 38;
26157e2f323SJérôme Duval 		r.bottom = r.top + 104;
26257e2f323SJérôme Duval 		fVUView = new VUView(r, B_FOLLOW_LEFT|B_FOLLOW_TOP);
26357e2f323SJérôme Duval 		background->AddChild(fVUView);
26457e2f323SJérôme Duval 
26557e2f323SJérôme Duval 		r = background->Bounds();
2668881ad65SJérôme Duval 		r.left = r.left + 40;
26757e2f323SJérôme Duval 		r.bottom = r.top + 104;
26857e2f323SJérôme Duval 		fScopeView = new ScopeView(r, B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP);
26957e2f323SJérôme Duval 		background->AddChild(fScopeView);
27057e2f323SJérôme Duval 
27157e2f323SJérôme Duval 		r = background->Bounds();
27257e2f323SJérôme Duval 		r.left = 2;
27357e2f323SJérôme Duval 		r.right -= 26;
27457e2f323SJérôme Duval 		r.top = 115;
27557e2f323SJérôme Duval 		r.bottom = r.top + 30;
276ad0c65eaSJérôme Duval 		fTrackSlider = new TrackSlider(r, "trackSlider", new BMessage(POSITION_CHANGED),
277ad0c65eaSJérôme Duval 			B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
27857e2f323SJérôme Duval 		background->AddChild(fTrackSlider);
27957e2f323SJérôme Duval 
28057e2f323SJérôme Duval 		BRect buttonRect;
28157e2f323SJérôme Duval 
28257e2f323SJérôme Duval 		//	Button for rewinding
28357e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
28457e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-7, 25));
2853ee96407SJérôme Duval 		fRewindButton = new TransportButton(buttonRect, B_TRANSLATE("Rewind"),
286ad0c65eaSJérôme Duval 			kSkipBackBitmapBits, kPressedSkipBackBitmapBits,
287ad0c65eaSJérôme Duval 			kDisabledSkipBackBitmapBits, new BMessage(REWIND));
28857e2f323SJérôme Duval 		background->AddChild(fRewindButton);
28957e2f323SJérôme Duval 
29057e2f323SJérôme Duval 		//	Button for stopping recording or playback
29157e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kStopButtonSize);
29257e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-48, 25));
2933ee96407SJérôme Duval 		fStopButton = new TransportButton(buttonRect, B_TRANSLATE("Stop"),
294ad0c65eaSJérôme Duval 			kStopButtonBitmapBits, kPressedStopButtonBitmapBits,
295ad0c65eaSJérôme Duval 			kDisabledStopButtonBitmapBits, new BMessage(STOP));
29657e2f323SJérôme Duval 		background->AddChild(fStopButton);
29757e2f323SJérôme Duval 
29857e2f323SJérôme Duval 		//	Button for starting playback of selected sound
29957e2f323SJérôme Duval 		BRect playRect(BPoint(0,0), kPlayButtonSize);
30057e2f323SJérôme Duval 		playRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-82, 25));
3013ee96407SJérôme Duval 		fPlayButton = new PlayPauseButton(playRect, B_TRANSLATE("Play"),
30257e2f323SJérôme Duval 			new BMessage(PLAY), new BMessage(PLAY_PERIOD), ' ', 0);
30357e2f323SJérôme Duval 		background->AddChild(fPlayButton);
30457e2f323SJérôme Duval 
30557e2f323SJérôme Duval 		//	Button for forwarding
30657e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
30757e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-133, 25));
3083ee96407SJérôme Duval 		fForwardButton = new TransportButton(buttonRect, B_TRANSLATE("Forward"),
309ad0c65eaSJérôme Duval 			kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits,
310ad0c65eaSJérôme Duval 			kDisabledSkipForwardBitmapBits, new BMessage(FORWARD));
31157e2f323SJérôme Duval 		background->AddChild(fForwardButton);
31257e2f323SJérôme Duval 
31357e2f323SJérôme Duval 		//	Button to start recording (or waiting for sound)
31457e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kRecordButtonSize);
31557e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-174, 25));
3163ee96407SJérôme Duval 		fRecordButton = new RecordButton(buttonRect, B_TRANSLATE("Record"),
31757e2f323SJérôme Duval 			new BMessage(RECORD), new BMessage(RECORD_PERIOD));
31857e2f323SJérôme Duval 		background->AddChild(fRecordButton);
31957e2f323SJérôme Duval 
32057e2f323SJérôme Duval 		//	Button for saving selected sound
32157e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kDiskButtonSize);
32257e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-250, 21));
3233ee96407SJérôme Duval 		fSaveButton = new TransportButton(buttonRect, B_TRANSLATE("Save"),
324ad0c65eaSJérôme Duval 			kDiskButtonBitmapsBits, kPressedDiskButtonBitmapsBits,
325ad0c65eaSJérôme Duval 			kDisabledDiskButtonBitmapsBits, new BMessage(SAVE));
32657e2f323SJérôme Duval 		fSaveButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
32757e2f323SJérôme Duval 		background->AddChild(fSaveButton);
32857e2f323SJérôme Duval 
329948356deSJérôme Duval 		//	Button Loop
330948356deSJérôme Duval 		buttonRect = BRect(BPoint(0,0), kArrowSize);
331948356deSJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(23, 48));
3323ee96407SJérôme Duval 		fLoopButton = new DrawButton(buttonRect, B_TRANSLATE("Loop"),
333948356deSJérôme Duval 			kLoopArrowBits, kArrowBits, new BMessage(LOOP));
334948356deSJérôme Duval 		fLoopButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
335948356deSJérôme Duval 		fLoopButton->SetTarget(this);
336948356deSJérôme Duval 		background->AddChild(fLoopButton);
337948356deSJérôme Duval 
33857e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kSpeakerIconBitmapSize);
33957e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(121, 17));
340ad0c65eaSJérôme Duval 		SpeakerView *speakerView = new SpeakerView(buttonRect,
341ad0c65eaSJérôme Duval 			B_FOLLOW_LEFT | B_FOLLOW_TOP);
34257e2f323SJérôme Duval 		speakerView->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
34357e2f323SJérôme Duval 		background->AddChild(speakerView);
34457e2f323SJérôme Duval 
34557e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), BPoint(84, 19));
34657e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(107, 20));
347ad0c65eaSJérôme Duval 		fVolumeSlider = new VolumeSlider(buttonRect, "volumeSlider",
348ad0c65eaSJérôme Duval 			B_FOLLOW_LEFT | B_FOLLOW_TOP);
34957e2f323SJérôme Duval 		fVolumeSlider->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
35057e2f323SJérôme Duval 		background->AddChild(fVolumeSlider);
35157e2f323SJérôme Duval 
35257e2f323SJérôme Duval 		// Button to mask/see sounds list
35357e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kUpDownButtonSize);
35457e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(21, 25));
35557e2f323SJérôme Duval 		fUpDownButton = new UpDownButton(buttonRect, new BMessage(VIEW_LIST));
35657e2f323SJérôme Duval 		fUpDownButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
35757e2f323SJérôme Duval 		background->AddChild(fUpDownButton);
35857e2f323SJérôme Duval 
35957e2f323SJérôme Duval 		r = Bounds();
36057e2f323SJérôme Duval 		r.top = background->Bounds().bottom + 1;
36157e2f323SJérôme Duval 		fBottomBox = new BBox(r, "bottomBox", B_FOLLOW_ALL);
36257e2f323SJérôme Duval 		fBottomBox->SetBorder(B_NO_BORDER);
36357e2f323SJérôme Duval 		AddChild(fBottomBox);
36457e2f323SJérôme Duval 
36557e2f323SJérôme Duval 		//	The actual list of recorded sounds (initially empty) sits
36657e2f323SJérôme Duval 		//	below the header with the controls.
36757e2f323SJérôme Duval 		r = fBottomBox->Bounds();
36857e2f323SJérôme Duval 		r.left += 190;
36957e2f323SJérôme Duval 		r.InsetBy(10, 10);
37057e2f323SJérôme Duval 		r.left -= 10;
37157e2f323SJérôme Duval 		r.top += 4;
37257e2f323SJérôme Duval 		r.right -= B_V_SCROLL_BAR_WIDTH;
37357e2f323SJérôme Duval 		r.bottom -= 25;
3743ee96407SJérôme Duval 		fSoundList = new SoundListView(r, B_TRANSLATE("Sound List"),
3753ee96407SJérôme Duval 			B_FOLLOW_ALL);
37657e2f323SJérôme Duval 		fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED));
377b271ff3fSPhilippe Saint-Pierre 		fSoundList->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
378ad0c65eaSJérôme Duval 		BScrollView *scroller = new BScrollView("scroller", fSoundList,
379ad0c65eaSJérôme Duval 			B_FOLLOW_ALL, 0, false, true, B_FANCY_BORDER);
38057e2f323SJérôme Duval 		fBottomBox->AddChild(scroller);
38157e2f323SJérôme Duval 
38257e2f323SJérôme Duval 		r = fBottomBox->Bounds();
38357e2f323SJérôme Duval 		r.right = r.left + 190;
38457e2f323SJérôme Duval 		r.bottom -= 25;
38557e2f323SJérôme Duval 		r.InsetBy(10, 8);
38657e2f323SJérôme Duval 		r.top -= 1;
387b271ff3fSPhilippe Saint-Pierre 		fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT);
3883ee96407SJérôme Duval 		fFileInfoBox->SetLabel(B_TRANSLATE("File info"));
38957e2f323SJérôme Duval 
390b271ff3fSPhilippe Saint-Pierre 		fFileInfoBox->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
391b271ff3fSPhilippe Saint-Pierre 
392b271ff3fSPhilippe Saint-Pierre 		BFont font = be_plain_font;
393b271ff3fSPhilippe Saint-Pierre 		font.SetSize(font.Size() * 0.92f);
394b271ff3fSPhilippe Saint-Pierre 		font_height height;
395b271ff3fSPhilippe Saint-Pierre 		font.GetHeight(&height);
396b271ff3fSPhilippe Saint-Pierre 		float fontHeight = height.ascent + height.leading + height.descent;
397b271ff3fSPhilippe Saint-Pierre 
39857e2f323SJérôme Duval 		r = fFileInfoBox->Bounds();
39957e2f323SJérôme Duval 		r.left = 8;
400b271ff3fSPhilippe Saint-Pierre 		r.top = fontHeight + 6;
401b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + fontHeight + 3;
40257e2f323SJérôme Duval 		r.right -= 10;
4033ee96407SJérôme Duval 		fFilename = new BStringView(r, "filename", B_TRANSLATE("File name:"));
40457e2f323SJérôme Duval 		fFileInfoBox->AddChild(fFilename);
405b271ff3fSPhilippe Saint-Pierre 		fFilename->SetFont(&font, B_FONT_SIZE);
406b271ff3fSPhilippe Saint-Pierre 		fFilename->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
407b271ff3fSPhilippe Saint-Pierre 
408b271ff3fSPhilippe Saint-Pierre 		r.top += fontHeight;
409b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + fontHeight + 3;
4103ee96407SJérôme Duval 		fFormat = new BStringView(r, "format", B_TRANSLATE("Format:"));
41157e2f323SJérôme Duval 		fFileInfoBox->AddChild(fFormat);
412b271ff3fSPhilippe Saint-Pierre 		fFormat->SetFont(&font, B_FONT_SIZE);
413b271ff3fSPhilippe Saint-Pierre 		fFormat->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
414b271ff3fSPhilippe Saint-Pierre 
415b271ff3fSPhilippe Saint-Pierre 		r.top += fontHeight;
416b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + fontHeight + 3;
4173ee96407SJérôme Duval 		fCompression = new BStringView(r, "compression",
4183ee96407SJérôme Duval 			B_TRANSLATE("Compression:"));
41957e2f323SJérôme Duval 		fFileInfoBox->AddChild(fCompression);
420b271ff3fSPhilippe Saint-Pierre 		fCompression->SetFont(&font, B_FONT_SIZE);
421b271ff3fSPhilippe Saint-Pierre 		fCompression->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
422b271ff3fSPhilippe Saint-Pierre 
423b271ff3fSPhilippe Saint-Pierre 		r.top += fontHeight;
424b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + fontHeight + 3;
4253ee96407SJérôme Duval 		fChannels = new BStringView(r, "channels", B_TRANSLATE("Channels:"));
42657e2f323SJérôme Duval 		fFileInfoBox->AddChild(fChannels);
427b271ff3fSPhilippe Saint-Pierre 		fChannels->SetFont(&font, B_FONT_SIZE);
428b271ff3fSPhilippe Saint-Pierre 		fChannels->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
429b271ff3fSPhilippe Saint-Pierre 
430b271ff3fSPhilippe Saint-Pierre 		r.top += fontHeight;
431b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + fontHeight + 3;
4323ee96407SJérôme Duval 		fSampleSize = new BStringView(r, "samplesize",
4333ee96407SJérôme Duval 			B_TRANSLATE("Sample size:"));
43457e2f323SJérôme Duval 		fFileInfoBox->AddChild(fSampleSize);
435b271ff3fSPhilippe Saint-Pierre 		fSampleSize->SetFont(&font, B_FONT_SIZE);
436b271ff3fSPhilippe Saint-Pierre 		fSampleSize->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
437b271ff3fSPhilippe Saint-Pierre 
438b271ff3fSPhilippe Saint-Pierre 		r.top += fontHeight;
439b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + fontHeight + 3;
4403ee96407SJérôme Duval 		fSampleRate = new BStringView(r, "samplerate",
4413ee96407SJérôme Duval 			B_TRANSLATE("Sample rate:"));
44257e2f323SJérôme Duval 		fFileInfoBox->AddChild(fSampleRate);
443b271ff3fSPhilippe Saint-Pierre 		fSampleRate->SetFont(&font, B_FONT_SIZE);
444b271ff3fSPhilippe Saint-Pierre 		fSampleRate->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
445b271ff3fSPhilippe Saint-Pierre 
446b271ff3fSPhilippe Saint-Pierre 		r.top += fontHeight;
447b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + fontHeight + 3;
4483ee96407SJérôme Duval 		fDuration = new BStringView(r, "duration", B_TRANSLATE("Duration:"));
44957e2f323SJérôme Duval 		fFileInfoBox->AddChild(fDuration);
450b271ff3fSPhilippe Saint-Pierre 		fDuration->SetFont(&font, B_FONT_SIZE);
451b271ff3fSPhilippe Saint-Pierre 		fDuration->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
452b271ff3fSPhilippe Saint-Pierre 
453b271ff3fSPhilippe Saint-Pierre 		fFileInfoBox->ResizeTo(fFileInfoBox->Frame().Width(),
454b271ff3fSPhilippe Saint-Pierre 			r.bottom + fontHeight / 2.0f);
455b271ff3fSPhilippe Saint-Pierre 		fDeployedHeight = MIN_HEIGHT + fFileInfoBox->Bounds().Height() + 40.0f;
45657e2f323SJérôme Duval 
45757e2f323SJérôme Duval 		//	Input selection lists all available physical inputs that produce
45857e2f323SJérôme Duval 		//	buffers with B_MEDIA_RAW_AUDIO format data.
4593ee96407SJérôme Duval 		popup = new BPopUpMenu(B_TRANSLATE("Input"));
460a8ee8b8fSStephan Aßmus 		const int maxInputCount = 64;
461a8ee8b8fSStephan Aßmus 		dormant_node_info dni[maxInputCount];
46257e2f323SJérôme Duval 
463a8ee8b8fSStephan Aßmus 		int32 real_count = maxInputCount;
464*739fd34cSBarrett 
46557e2f323SJérôme Duval 		error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format,
46657e2f323SJérôme Duval 			0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT);
467a8ee8b8fSStephan Aßmus 		if (real_count > maxInputCount) {
4687f4d1af4SSergei Reznikov 			WINDOW((stderr, "dropped %" B_PRId32 " inputs\n", real_count - maxInputCount));
469a8ee8b8fSStephan Aßmus 			real_count = maxInputCount;
47057e2f323SJérôme Duval 		}
4717974d3dcSStephan Aßmus 		char selected_name[B_MEDIA_NAME_LENGTH] = "Default input";
47257e2f323SJérôme Duval 		BMessage * msg;
47357e2f323SJérôme Duval 		BMenuItem * item;
474ad0c65eaSJérôme Duval 		for (int i = 0; i < real_count; i++) {
47557e2f323SJérôme Duval 			msg = new BMessage(INPUT_SELECTED);
476ad0c65eaSJérôme Duval 			msg->AddData("node", B_RAW_TYPE, &dni[i], sizeof(dni[i]));
477ad0c65eaSJérôme Duval 			item = new BMenuItem(dni[i].name, msg);
47857e2f323SJérôme Duval 			popup->AddItem(item);
47957e2f323SJérôme Duval 			media_node_id ni[12];
48057e2f323SJérôme Duval 			int32 ni_count = 12;
481ad0c65eaSJérôme Duval 			error = fRoster->GetInstancesFor(dni[i].addon, dni[i].flavor_id,
482ad0c65eaSJérôme Duval 				ni, &ni_count);
483b1ed9a63SJérôme Duval 			if (error == B_OK) {
484b1ed9a63SJérôme Duval 				for (int j = 0; j < ni_count; j++) {
485ad0c65eaSJérôme Duval 					if (ni[j] == fAudioInputNode.node) {
486ad0c65eaSJérôme Duval 						strcpy(selected_name, dni[i].name);
48757e2f323SJérôme Duval 						break;
48857e2f323SJérôme Duval 					}
48957e2f323SJérôme Duval 				}
490b1ed9a63SJérôme Duval 			}
491b1ed9a63SJérôme Duval 		}
49257e2f323SJérôme Duval 
49357e2f323SJérôme Duval 		//	Create the actual widget
494b271ff3fSPhilippe Saint-Pierre 		r = fFileInfoBox->Bounds();
495b271ff3fSPhilippe Saint-Pierre 		r.top = r.bottom + 2;
496b271ff3fSPhilippe Saint-Pierre 		r.bottom = r.top + 18;
49757e2f323SJérôme Duval 		r.InsetBy(10, 10);
4983ee96407SJérôme Duval 		fInputField = new BMenuField(r, "Input", B_TRANSLATE("Input:"), popup);
499b1ed9a63SJérôme Duval 		fInputField->SetDivider(fInputField->StringWidth(B_TRANSLATE("Input:"))
500b1ed9a63SJérôme Duval 			+ 4.0f);
50157e2f323SJérôme Duval 		fBottomBox->AddChild(fInputField);
50257e2f323SJérôme Duval 
50357e2f323SJérôme Duval 		fBottomBox->AddChild(fFileInfoBox);
50457e2f323SJérôme Duval 
50557e2f323SJérôme Duval 		fBottomBox->Hide();
506b271ff3fSPhilippe Saint-Pierre 		CalcSizes(MIN_WIDTH, MIN_HEIGHT);
507b271ff3fSPhilippe Saint-Pierre 		ResizeTo(Frame().Width(), MIN_HEIGHT);
50857e2f323SJérôme Duval 
50957e2f323SJérôme Duval 		popup->Superitem()->SetLabel(selected_name);
51057e2f323SJérôme Duval 
51157e2f323SJérôme Duval 		// Make sure the save panel is happy.
5125fa77532SJérôme Duval 		fSavePanel = new BFilePanel(B_SAVE_PANEL);
5135fa77532SJérôme Duval 		fSavePanel->SetTarget(this);
51457e2f323SJérôme Duval 	}
51557e2f323SJérôme Duval 	catch (...) {
51657e2f323SJérôme Duval 		goto bad_mojo;
51757e2f323SJérôme Duval 	}
51857e2f323SJérôme Duval 	UpdateButtons();
51957e2f323SJérôme Duval 	return B_OK;
52057e2f323SJérôme Duval 
52157e2f323SJérôme Duval 	//	Error handling.
52257e2f323SJérôme Duval bad_mojo:
523b1ed9a63SJérôme Duval 	if (error >= 0)
52457e2f323SJérôme Duval 		error = B_ERROR;
525*739fd34cSBarrett 	if (fRecorder)
526*739fd34cSBarrett 		delete fRecorder;
52757e2f323SJérôme Duval 
52857e2f323SJérôme Duval 	delete fPlayer;
529b1ed9a63SJérôme Duval 	if (!fInputField)
53057e2f323SJérôme Duval 		delete popup;
53157e2f323SJérôme Duval 	return error;
53257e2f323SJérôme Duval }
53357e2f323SJérôme Duval 
53457e2f323SJérôme Duval 
53557e2f323SJérôme Duval bool
53657e2f323SJérôme Duval RecorderWindow::QuitRequested()	//	this means Close pressed
53757e2f323SJérôme Duval {
53857e2f323SJérôme Duval 	StopRecording();
53957e2f323SJérôme Duval 	StopPlaying();
54057e2f323SJérôme Duval 	be_app->PostMessage(B_QUIT_REQUESTED);
54157e2f323SJérôme Duval 	return true;
54257e2f323SJérôme Duval }
54357e2f323SJérôme Duval 
54457e2f323SJérôme Duval 
54557e2f323SJérôme Duval void
54657e2f323SJérôme Duval RecorderWindow::MessageReceived(BMessage * message)
54757e2f323SJérôme Duval {
54857e2f323SJérôme Duval 	//	Your average generic message dispatching switch() statement.
54957e2f323SJérôme Duval 	switch (message->what) {
55057e2f323SJérôme Duval 	case INPUT_SELECTED:
55157e2f323SJérôme Duval 		Input(message);
55257e2f323SJérôme Duval 		break;
55357e2f323SJérôme Duval 	case SOUND_SELECTED:
55457e2f323SJérôme Duval 		Selected(message);
55557e2f323SJérôme Duval 		break;
55657e2f323SJérôme Duval 	case STOP_PLAYING:
55757e2f323SJérôme Duval 		StopPlaying();
55857e2f323SJérôme Duval 		break;
55957e2f323SJérôme Duval 	case STOP_RECORDING:
56057e2f323SJérôme Duval 		StopRecording();
56157e2f323SJérôme Duval 		break;
56257e2f323SJérôme Duval 	case PLAY_PERIOD:
56357e2f323SJérôme Duval 		if (fPlayer) {
56457e2f323SJérôme Duval 			if (fPlayer->HasData())
56557e2f323SJérôme Duval 				fPlayButton->SetPlaying();
56657e2f323SJérôme Duval 			else
56757e2f323SJérôme Duval 				fPlayButton->SetPaused();
56857e2f323SJérôme Duval 		}
56957e2f323SJérôme Duval 		break;
57057e2f323SJérôme Duval 	case RECORD_PERIOD:
57157e2f323SJérôme Duval 		fRecordButton->SetRecording();
57257e2f323SJérôme Duval 		break;
57357e2f323SJérôme Duval 	case RECORD:
57457e2f323SJérôme Duval 		Record(message);
57557e2f323SJérôme Duval 		break;
57657e2f323SJérôme Duval 	case STOP:
57757e2f323SJérôme Duval 		Stop(message);
57857e2f323SJérôme Duval 		break;
57957e2f323SJérôme Duval 	case PLAY:
58057e2f323SJérôme Duval 		Play(message);
58157e2f323SJérôme Duval 		break;
58257e2f323SJérôme Duval 	case SAVE:
58357e2f323SJérôme Duval 		Save(message);
58457e2f323SJérôme Duval 		break;
58557e2f323SJérôme Duval 	case B_SAVE_REQUESTED:
58657e2f323SJérôme Duval 		DoSave(message);
58757e2f323SJérôme Duval 		break;
58857e2f323SJérôme Duval 	case VIEW_LIST:
58957e2f323SJérôme Duval 		if (fUpDownButton->Value() == B_CONTROL_ON) {
59057e2f323SJérôme Duval 			fBottomBox->Show();
591b271ff3fSPhilippe Saint-Pierre 			CalcSizes(MIN_WIDTH, fDeployedHeight);
592b271ff3fSPhilippe Saint-Pierre 			ResizeTo(Frame().Width(), fDeployedHeight);
59357e2f323SJérôme Duval 		} else {
59457e2f323SJérôme Duval 			fBottomBox->Hide();
595b271ff3fSPhilippe Saint-Pierre 			CalcSizes(MIN_WIDTH, MIN_HEIGHT);
596b271ff3fSPhilippe Saint-Pierre 			ResizeTo(Frame().Width(), MIN_HEIGHT);
59757e2f323SJérôme Duval 
59857e2f323SJérôme Duval 		}
59957e2f323SJérôme Duval 		break;
60057e2f323SJérôme Duval 	case UPDATE_TRACKSLIDER:
60157e2f323SJérôme Duval 		{
60257e2f323SJérôme Duval 			bigtime_t timestamp = fPlayTrack->CurrentTime();
60357e2f323SJérôme Duval 			fTrackSlider->SetMainTime(timestamp, false);
60457e2f323SJérôme Duval 			fScopeView->SetMainTime(timestamp);
60557e2f323SJérôme Duval 		}
60657e2f323SJérôme Duval 		break;
60757e2f323SJérôme Duval 	case POSITION_CHANGED:
60857e2f323SJérôme Duval 		{
60957e2f323SJérôme Duval 			bigtime_t right, left, main;
61057e2f323SJérôme Duval 			if (message->FindInt64("main", &main) == B_OK) {
61157e2f323SJérôme Duval 				if (fPlayTrack) {
61257e2f323SJérôme Duval 					fPlayTrack->SeekToTime(fTrackSlider->MainTime());
61357e2f323SJérôme Duval 					fPlayFrame = fPlayTrack->CurrentFrame();
61457e2f323SJérôme Duval 				}
61557e2f323SJérôme Duval 				fScopeView->SetMainTime(main);
61657e2f323SJérôme Duval 			}
61757e2f323SJérôme Duval 			if (message->FindInt64("right", &right) == B_OK) {
618b1ed9a63SJérôme Duval 				if (fPlayTrack) {
619ad0c65eaSJérôme Duval 					fPlayLimit = MIN(fPlayFrames,
620b1ed9a63SJérôme Duval 						(off_t)(right * fPlayFormat.u.raw_audio.frame_rate
621b1ed9a63SJérôme Duval 							/ 1000000LL));
622b1ed9a63SJérôme Duval 				}
62357e2f323SJérôme Duval 				fScopeView->SetRightTime(right);
62457e2f323SJérôme Duval 			}
625b1ed9a63SJérôme Duval 			if (message->FindInt64("left", &left) == B_OK)
62657e2f323SJérôme Duval 				fScopeView->SetLeftTime(left);
62757e2f323SJérôme Duval 			break;
628b1ed9a63SJérôme Duval 		}
629948356deSJérôme Duval 	case LOOP:
630948356deSJérôme Duval 		fLooping = fLoopButton->ButtonState();
631948356deSJérôme Duval 		break;
63257e2f323SJérôme Duval 	case B_SIMPLE_DATA:
63357e2f323SJérôme Duval 	case B_REFS_RECEIVED:
63457e2f323SJérôme Duval 		{
63557e2f323SJérôme Duval 			RefsReceived(message);
63657e2f323SJérôme Duval 			break;
63757e2f323SJérôme Duval 		}
638019ed09bSJérôme Duval 	case B_COPY_TARGET:
639019ed09bSJérôme Duval 		CopyTarget(message);
640019ed09bSJérôme Duval 		break;
64157e2f323SJérôme Duval 	default:
64257e2f323SJérôme Duval 		BWindow::MessageReceived(message);
64357e2f323SJérôme Duval 		break;
64457e2f323SJérôme Duval 	}
64557e2f323SJérôme Duval }
64657e2f323SJérôme Duval 
64757e2f323SJérôme Duval 
64857e2f323SJérôme Duval void
64957e2f323SJérôme Duval RecorderWindow::Record(BMessage * message)
65057e2f323SJérôme Duval {
65157e2f323SJérôme Duval 	//	User pressed Record button
65257e2f323SJérôme Duval 	fRecording = true;
65357e2f323SJérôme Duval 	if (fButtonState != btnPaused) {
65457e2f323SJérôme Duval 		StopRecording();
65557e2f323SJérôme Duval 		return;			//	user is too fast on the mouse
65657e2f323SJérôme Duval 	}
65757e2f323SJérôme Duval 	SetButtonState(btnRecording);
65857e2f323SJérôme Duval 	fRecordButton->SetRecording();
65957e2f323SJérôme Duval 
66057e2f323SJérôme Duval 	char name[256];
66157e2f323SJérôme Duval 	//	Create a file with a temporary name
66257e2f323SJérôme Duval 	status_t err = NewTempName(name);
66357e2f323SJérôme Duval 	if (err < B_OK) {
6643ee96407SJérôme Duval 		ErrorAlert(B_TRANSLATE("Cannot find an unused name to use for the "
6653ee96407SJérôme Duval 			"new recording"), err);
66657e2f323SJérôme Duval 		return;
66757e2f323SJérôme Duval 	}
66857e2f323SJérôme Duval 	//	Find the file so we can refer to it later
66957e2f323SJérôme Duval 	err = fTempDir.FindEntry(name, &fRecEntry);
67057e2f323SJérôme Duval 	if (err < B_OK) {
6713ee96407SJérôme Duval 		ErrorAlert(B_TRANSLATE("Cannot find the temporary file created to "
6723ee96407SJérôme Duval 			"hold the new recording"), err);
67357e2f323SJérôme Duval 		return;
67457e2f323SJérôme Duval 	}
67557e2f323SJérôme Duval 	err = fRecFile.SetTo(&fTempDir, name, O_RDWR);
67657e2f323SJérôme Duval 	if (err < B_OK) {
6773ee96407SJérôme Duval 		ErrorAlert(B_TRANSLATE("Cannot open the temporary file created to "
6783ee96407SJérôme Duval 			"hold the new recording"), err);
67957e2f323SJérôme Duval 		fRecEntry.Unset();
68057e2f323SJérôme Duval 		return;
68157e2f323SJérôme Duval 	}
68257e2f323SJérôme Duval 	//	Reserve space on disk (creates fewer fragments)
68307db0c6fSJérôme Duval 	err = fRecFile.SetSize(4 * fRecordFormat.u.raw_audio.channel_count
6843ee96407SJérôme Duval 		* fRecordFormat.u.raw_audio.frame_rate
6853ee96407SJérôme Duval 		* (fRecordFormat.u.raw_audio.format
686ad0c65eaSJérôme Duval 			& media_raw_audio_format::B_AUDIO_SIZE_MASK));
68757e2f323SJérôme Duval 	if (err < B_OK) {
6883ee96407SJérôme Duval 		ErrorAlert(B_TRANSLATE("Cannot record a sound that long"), err);
68957e2f323SJérôme Duval 		fRecEntry.Remove();
69057e2f323SJérôme Duval 		fRecEntry.Unset();
69157e2f323SJérôme Duval 		return;
69257e2f323SJérôme Duval 	}
69357e2f323SJérôme Duval 	fRecSize = 0;
69457e2f323SJérôme Duval 
69557e2f323SJérôme Duval 	fRecFile.Seek(sizeof(struct wave_struct), SEEK_SET);
69657e2f323SJérôme Duval 
69757e2f323SJérôme Duval 	//	Hook up input
69857e2f323SJérôme Duval 	err = MakeRecordConnection(fAudioInputNode);
69957e2f323SJérôme Duval 	if (err < B_OK) {
7003ee96407SJérôme Duval 		ErrorAlert(B_TRANSLATE("Cannot connect to the selected sound input"),
7013ee96407SJérôme Duval 			err);
70257e2f323SJérôme Duval 		fRecEntry.Remove();
70357e2f323SJérôme Duval 		fRecEntry.Unset();
70457e2f323SJérôme Duval 		return;
70557e2f323SJérôme Duval 	}
70657e2f323SJérôme Duval 
707*739fd34cSBarrett 	fRecorder->Start();
70857e2f323SJérôme Duval }
70957e2f323SJérôme Duval 
7108e32a27eSPhilippe Saint-Pierre 
71157e2f323SJérôme Duval void
71257e2f323SJérôme Duval RecorderWindow::Play(BMessage * message)
71357e2f323SJérôme Duval {
71457e2f323SJérôme Duval 	if (fPlayer) {
71557e2f323SJérôme Duval 		//	User pressed Play button and playing
71657e2f323SJérôme Duval 		if (fPlayer->HasData())
71757e2f323SJérôme Duval 			fPlayButton->SetPaused();
71857e2f323SJérôme Duval 		else
71957e2f323SJérôme Duval 			fPlayButton->SetPlaying();
72057e2f323SJérôme Duval 		fPlayer->SetHasData(!fPlayer->HasData());
72157e2f323SJérôme Duval 		return;
72257e2f323SJérôme Duval 	}
72357e2f323SJérôme Duval 
72457e2f323SJérôme Duval 	SetButtonState(btnPlaying);
72557e2f323SJérôme Duval 	fPlayButton->SetPlaying();
72657e2f323SJérôme Duval 
72757e2f323SJérôme Duval 	if (!fPlayTrack) {
7283ee96407SJérôme Duval 		ErrorAlert(B_TRANSLATE("Cannot get the file to play"), B_ERROR);
72957e2f323SJérôme Duval 		return;
73057e2f323SJérôme Duval 	}
73157e2f323SJérôme Duval 
732ad0c65eaSJérôme Duval 	fPlayLimit = MIN(fPlayFrames, (off_t)(fTrackSlider->RightTime()
733ad0c65eaSJérôme Duval 		* fPlayFormat.u.raw_audio.frame_rate / 1000000LL));
73457e2f323SJérôme Duval 	fPlayTrack->SeekToTime(fTrackSlider->MainTime());
73557e2f323SJérôme Duval 	fPlayFrame = fPlayTrack->CurrentFrame();
73657e2f323SJérôme Duval 
73757e2f323SJérôme Duval 	// Create our internal Node which plays sound, and register it.
7383ee96407SJérôme Duval 	fPlayer = new BSoundPlayer(fAudioMixerNode, &fPlayFormat.u.raw_audio,
7393ee96407SJérôme Duval 		"Sound Player");
74057e2f323SJérôme Duval 	status_t err = fPlayer->InitCheck();
741b1ed9a63SJérôme Duval 	if (err < B_OK)
74257e2f323SJérôme Duval 		return;
74357e2f323SJérôme Duval 
74457e2f323SJérôme Duval 	fVolumeSlider->SetSoundPlayer(fPlayer);
74557e2f323SJérôme Duval 	fPlayer->SetCallbacks(PlayFile, NotifyPlayFile, this);
74657e2f323SJérôme Duval 
74757e2f323SJérôme Duval 	//	And get it going...
74857e2f323SJérôme Duval 	fPlayer->Start();
74957e2f323SJérôme Duval 	fPlayer->SetHasData(true);
75057e2f323SJérôme Duval }
75157e2f323SJérôme Duval 
7528e32a27eSPhilippe Saint-Pierre 
75357e2f323SJérôme Duval void
75457e2f323SJérôme Duval RecorderWindow::Stop(BMessage * message)
75557e2f323SJérôme Duval {
75657e2f323SJérôme Duval 	//	User pressed Stop button.
75757e2f323SJérôme Duval 	//	Stop recorder.
75857e2f323SJérôme Duval 	StopRecording();
75957e2f323SJérôme Duval 	//	Stop player.
76057e2f323SJérôme Duval 	StopPlaying();
76157e2f323SJérôme Duval }
76257e2f323SJérôme Duval 
7638e32a27eSPhilippe Saint-Pierre 
76457e2f323SJérôme Duval void
76557e2f323SJérôme Duval RecorderWindow::Save(BMessage * message)
76657e2f323SJérôme Duval {
76757e2f323SJérôme Duval 	//	User pressed Save button.
76857e2f323SJérôme Duval 	//	Find the item to save.
76957e2f323SJérôme Duval 	int32 index = fSoundList->CurrentSelection();
77057e2f323SJérôme Duval 	SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(index));
771b1ed9a63SJérôme Duval 	if ((! pItem) || (pItem->Entry().InitCheck() != B_OK))
77257e2f323SJérôme Duval 		return;
77357e2f323SJérôme Duval 
77457e2f323SJérôme Duval 	// Update the save panel and show it.
77557e2f323SJérôme Duval 	char filename[B_FILE_NAME_LENGTH];
77657e2f323SJérôme Duval 	pItem->Entry().GetName(filename);
77757e2f323SJérôme Duval 	BMessage saveMsg(B_SAVE_REQUESTED);
77857e2f323SJérôme Duval 	entry_ref ref;
77957e2f323SJérôme Duval 	pItem->Entry().GetRef(&ref);
78057e2f323SJérôme Duval 
781a4797804SJérôme Duval 	if (saveMsg.AddPointer("sound list item", pItem) != B_OK)
782a4797804SJérôme Duval 		fprintf(stderr, "failed to add pItem\n");
7835fa77532SJérôme Duval 	fSavePanel->SetSaveText(filename);
7845fa77532SJérôme Duval 	fSavePanel->SetMessage(&saveMsg);
7855fa77532SJérôme Duval 	fSavePanel->Show();
78657e2f323SJérôme Duval }
78757e2f323SJérôme Duval 
7888e32a27eSPhilippe Saint-Pierre 
78957e2f323SJérôme Duval void
79057e2f323SJérôme Duval RecorderWindow::DoSave(BMessage * message)
79157e2f323SJérôme Duval {
79257e2f323SJérôme Duval 	// User picked a place to put the file.
79357e2f323SJérôme Duval 	// Find the location of the old (e.g.
79457e2f323SJérôme Duval 	// temporary file), and the name of the
79557e2f323SJérôme Duval 	// new file to save.
79657e2f323SJérôme Duval 	entry_ref old_ref, new_dir_ref;
79757e2f323SJérôme Duval 	const char* new_name;
79857e2f323SJérôme Duval 	SoundListItem* pItem;
79957e2f323SJérôme Duval 
80057e2f323SJérôme Duval 	if ((message->FindPointer("sound list item", (void**) &pItem) == B_OK)
80157e2f323SJérôme Duval 		&& (message->FindRef("directory", &new_dir_ref) == B_OK)
802b1ed9a63SJérôme Duval 		&& (message->FindString("name", &new_name) == B_OK)) {
80357e2f323SJérôme Duval 		BEntry& oldEntry = pItem->Entry();
80457e2f323SJérôme Duval 		BFile oldFile(&oldEntry, B_READ_WRITE);
80557e2f323SJérôme Duval 		if (oldFile.InitCheck() != B_OK)
80657e2f323SJérôme Duval 			return;
80757e2f323SJérôme Duval 
80857e2f323SJérôme Duval 		BDirectory newDir(&new_dir_ref);
80957e2f323SJérôme Duval 		if (newDir.InitCheck() != B_OK)
81057e2f323SJérôme Duval 			return;
81157e2f323SJérôme Duval 
81257e2f323SJérôme Duval 		BFile newFile;
81357e2f323SJérôme Duval 		newDir.CreateFile(new_name, &newFile);
81457e2f323SJérôme Duval 
81557e2f323SJérôme Duval 		if (newFile.InitCheck() != B_OK)
81657e2f323SJérôme Duval 			return;
81757e2f323SJérôme Duval 
81857e2f323SJérôme Duval 		status_t err = CopyFile(newFile, oldFile);
81957e2f323SJérôme Duval 
82057e2f323SJérôme Duval 		if (err == B_OK) {
82157e2f323SJérôme Duval 			// clean up the sound list and item
82257e2f323SJérôme Duval 			if (pItem->IsTemp())
82357e2f323SJérôme Duval 				oldEntry.Remove(); // blows away temp file!
82457e2f323SJérôme Duval 			oldEntry.SetTo(&newDir, new_name);
82557e2f323SJérôme Duval 			pItem->SetTemp(false);	// don't blow the new entry away when we exit!
82657e2f323SJérôme Duval 			fSoundList->Invalidate();
82757e2f323SJérôme Duval 		}
82857e2f323SJérôme Duval 	} else {
82957e2f323SJérôme Duval 		WINDOW((stderr, "Couldn't save file.\n"));
83057e2f323SJérôme Duval 	}
83157e2f323SJérôme Duval }
83257e2f323SJérôme Duval 
83357e2f323SJérôme Duval 
83457e2f323SJérôme Duval void
83557e2f323SJérôme Duval RecorderWindow::Input(BMessage * message)
83657e2f323SJérôme Duval {
83757e2f323SJérôme Duval 	//	User selected input from pop-up
83857e2f323SJérôme Duval 	const dormant_node_info * dni = 0;
83957e2f323SJérôme Duval 	ssize_t size = 0;
840b1ed9a63SJérôme Duval 	if (message->FindData("node", B_RAW_TYPE, (const void **)&dni, &size))
84157e2f323SJérôme Duval 		return;		//	bad input selection message
84257e2f323SJérôme Duval 
84357e2f323SJérôme Duval 	media_node_id node_id;
84457e2f323SJérôme Duval 	status_t error = fRoster->GetInstancesFor(dni->addon, dni->flavor_id, &node_id);
845b1ed9a63SJérôme Duval 	if (error != B_OK)
84657e2f323SJérôme Duval 		fRoster->InstantiateDormantNode(*dni, &fAudioInputNode);
847b1ed9a63SJérôme Duval 	else
84857e2f323SJérôme Duval 		fRoster->GetNodeFor(node_id, &fAudioInputNode);
84957e2f323SJérôme Duval }
85057e2f323SJérôme Duval 
8518e32a27eSPhilippe Saint-Pierre 
85257e2f323SJérôme Duval void
85357e2f323SJérôme Duval RecorderWindow::Selected(BMessage * message)
85457e2f323SJérôme Duval {
85557e2f323SJérôme Duval 	//	User selected a sound in list view
856c6f8aa29SJérôme Duval 	int32 selIdx = fSoundList->CurrentSelection();
857c6f8aa29SJérôme Duval 	SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(selIdx));
858c6f8aa29SJérôme Duval 	if (!pItem)
859c6f8aa29SJérôme Duval 		return;
860c6f8aa29SJérôme Duval 	status_t err = UpdatePlayFile(pItem, true);
861c6f8aa29SJérôme Duval 	if (err != B_OK) {
8623ee96407SJérôme Duval 		ErrorAlert(B_TRANSLATE("Cannot recognize this file as a media file"),
863ad0c65eaSJérôme Duval 			err == B_MEDIA_NO_HANDLER ? B_OK : err);
864c6f8aa29SJérôme Duval 		RemoveCurrentSoundItem();
865c6f8aa29SJérôme Duval 	}
86657e2f323SJérôme Duval 	UpdateButtons();
86757e2f323SJérôme Duval }
86857e2f323SJérôme Duval 
8698e32a27eSPhilippe Saint-Pierre 
87057e2f323SJérôme Duval status_t
87157e2f323SJérôme Duval RecorderWindow::MakeRecordConnection(const media_node & input)
87257e2f323SJérôme Duval {
87357e2f323SJérôme Duval 	CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n"));
87457e2f323SJérôme Duval 
875*739fd34cSBarrett 	status_t err = B_OK;
876*739fd34cSBarrett 	media_output audioOutput;
877*739fd34cSBarrett 
878*739fd34cSBarrett 	if (!fRecorder->IsConnected()) {
87957e2f323SJérôme Duval 		//	Find an available output for the given input node.
88057e2f323SJérôme Duval 		int32 count = 0;
881*739fd34cSBarrett 		err = fRoster->GetFreeOutputsFor(input, &audioOutput, 1,
882*739fd34cSBarrett 			&count, B_MEDIA_RAW_AUDIO);
883*739fd34cSBarrett 
88457e2f323SJérôme Duval 		if (err < B_OK) {
885ad0c65eaSJérôme Duval 			CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
886ad0c65eaSJérôme Duval 				" couldn't get free outputs from audio input node\n"));
88757e2f323SJérôme Duval 			return err;
88857e2f323SJérôme Duval 		}
889*739fd34cSBarrett 
89057e2f323SJérôme Duval 		if (count < 1) {
891ad0c65eaSJérôme Duval 			CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
892ad0c65eaSJérôme Duval 				" no free outputs from audio input node\n"));
89357e2f323SJérôme Duval 			return B_BUSY;
89457e2f323SJérôme Duval 		}
89557e2f323SJérôme Duval 
896*739fd34cSBarrett 	} else {
897ad0c65eaSJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
898*739fd34cSBarrett 			" audio input node already connected\n"));
899*739fd34cSBarrett 
90057e2f323SJérôme Duval 		return B_BUSY;
90157e2f323SJérôme Duval 	}
90257e2f323SJérôme Duval 
90357e2f323SJérôme Duval 	//	Get a format, any format.
904*739fd34cSBarrett 	fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio;
90588fef303SJérôme Duval 	fRecordFormat.type = B_MEDIA_RAW_AUDIO;
90657e2f323SJérôme Duval 
90757e2f323SJérôme Duval 	//	Tell the consumer where we want data to go.
908*739fd34cSBarrett 	err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this);
909*739fd34cSBarrett 
91057e2f323SJérôme Duval 	if (err < B_OK) {
911ad0c65eaSJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
912ad0c65eaSJérôme Duval 			" couldn't set the sound recorder's hook functions\n"));
91357e2f323SJérôme Duval 		return err;
91457e2f323SJérôme Duval 	}
91557e2f323SJérôme Duval 
916*739fd34cSBarrett 	if (!fRecorder->IsConnected()) {
917*739fd34cSBarrett 
918*739fd34cSBarrett 		err = fRecorder->Connect(input, &audioOutput, &fRecordFormat);
919*739fd34cSBarrett 
92057e2f323SJérôme Duval 		if (err < B_OK) {
921ad0c65eaSJérôme Duval 			CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
922ad0c65eaSJérôme Duval 				" failed to connect sound recorder to audio input node.\n"));
923*739fd34cSBarrett 
924*739fd34cSBarrett 			fRecorder->SetHooks(NULL, NULL, NULL);
92557e2f323SJérôme Duval 			return err;
92657e2f323SJérôme Duval 		}
927*739fd34cSBarrett 	}
92857e2f323SJérôme Duval 
92957e2f323SJérôme Duval 	return B_OK;
93057e2f323SJérôme Duval }
93157e2f323SJérôme Duval 
93257e2f323SJérôme Duval 
93357e2f323SJérôme Duval status_t
93457e2f323SJérôme Duval RecorderWindow::BreakRecordConnection()
93557e2f323SJérôme Duval {
936*739fd34cSBarrett 	status_t err = B_OK;
93757e2f323SJérôme Duval 
938*739fd34cSBarrett 	err = fRecorder->Stop(true);
939*739fd34cSBarrett 	if (err < B_OK)
94057e2f323SJérôme Duval 		return err;
941*739fd34cSBarrett 
942*739fd34cSBarrett 	return fRecorder->Disconnect();
94357e2f323SJérôme Duval }
94457e2f323SJérôme Duval 
9458e32a27eSPhilippe Saint-Pierre 
94657e2f323SJérôme Duval status_t
94757e2f323SJérôme Duval RecorderWindow::StopRecording()
94857e2f323SJérôme Duval {
94957e2f323SJérôme Duval 	if (!fRecording)
95057e2f323SJérôme Duval 		return B_OK;
95157e2f323SJérôme Duval 	fRecording = false;
952*739fd34cSBarrett 
95357e2f323SJérôme Duval 	BreakRecordConnection();
954*739fd34cSBarrett 
955*739fd34cSBarrett 	fRecorder->SetHooks(NULL, NULL, NULL);
956*739fd34cSBarrett 
95757e2f323SJérôme Duval 	if (fRecSize > 0) {
95857e2f323SJérôme Duval 
95957e2f323SJérôme Duval 		wave_struct header;
96057e2f323SJérôme Duval 		header.riff.riff_id = FOURCC('R','I','F','F');
961a4797804SJérôme Duval 		header.riff.len = fRecSize + sizeof(header) - 8;
96257e2f323SJérôme Duval 		header.riff.wave_id = FOURCC('W','A','V','E');
96357e2f323SJérôme Duval 		header.format_chunk.fourcc = FOURCC('f','m','t',' ');
96457e2f323SJérôme Duval 		header.format_chunk.len = sizeof(header.format);
96557e2f323SJérôme Duval 		header.format.format_tag = 1;
96688fef303SJérôme Duval 		header.format.channels = fRecordFormat.u.raw_audio.channel_count;
96788fef303SJérôme Duval 		header.format.samples_per_sec = (uint32)fRecordFormat.u.raw_audio.frame_rate;
96888fef303SJérôme Duval 		header.format.avg_bytes_per_sec = (uint32)(fRecordFormat.u.raw_audio.frame_rate
96988fef303SJérôme Duval 			* fRecordFormat.u.raw_audio.channel_count
97088fef303SJérôme Duval 			* (fRecordFormat.u.raw_audio.format & 0xf));
97188fef303SJérôme Duval 		header.format.bits_per_sample = (fRecordFormat.u.raw_audio.format & 0xf) * 8;
97288fef303SJérôme Duval 		header.format.block_align = (fRecordFormat.u.raw_audio.format & 0xf)
97388fef303SJérôme Duval 			* fRecordFormat.u.raw_audio.channel_count;
97457e2f323SJérôme Duval 		header.data_chunk.fourcc = FOURCC('d','a','t','a');
97557e2f323SJérôme Duval 		header.data_chunk.len = fRecSize;
97657e2f323SJérôme Duval 		fRecFile.Seek(0, SEEK_SET);
97757e2f323SJérôme Duval 		fRecFile.Write(&header, sizeof(header));
97857e2f323SJérôme Duval 
9793ee96407SJérôme Duval 		fRecFile.SetSize(fRecSize + sizeof(header));
9803ee96407SJérôme Duval 		//	We reserve space; make sure we cut off any excess at the end.
98157e2f323SJérôme Duval 		AddSoundItem(fRecEntry, true);
982b1ed9a63SJérôme Duval 	} else
98357e2f323SJérôme Duval 		fRecEntry.Remove();
984b1ed9a63SJérôme Duval 
98557e2f323SJérôme Duval 	//	We're done for this time.
98657e2f323SJérôme Duval 	fRecEntry.Unset();
98757e2f323SJérôme Duval 	//	Close the file.
98857e2f323SJérôme Duval 	fRecFile.Unset();
98957e2f323SJérôme Duval 	//	No more recording going on.
99057e2f323SJérôme Duval 	fRecSize = 0;
99157e2f323SJérôme Duval 	SetButtonState(btnPaused);
99257e2f323SJérôme Duval 	fRecordButton->SetStopped();
99357e2f323SJérôme Duval 
99457e2f323SJérôme Duval 	return B_OK;
99557e2f323SJérôme Duval }
99657e2f323SJérôme Duval 
99757e2f323SJérôme Duval 
99857e2f323SJérôme Duval status_t
99957e2f323SJérôme Duval RecorderWindow::StopPlaying()
100057e2f323SJérôme Duval {
100157e2f323SJérôme Duval 	if (fPlayer) {
100257e2f323SJérôme Duval 		fPlayer->Stop();
100357e2f323SJérôme Duval 		fPlayer->SetCallbacks(0, 0, 0);
100457e2f323SJérôme Duval 		fVolumeSlider->SetSoundPlayer(NULL);
100557e2f323SJérôme Duval 		delete fPlayer;
100657e2f323SJérôme Duval 		fPlayer = NULL;
100757e2f323SJérôme Duval 	}
100857e2f323SJérôme Duval 	SetButtonState(btnPaused);
100957e2f323SJérôme Duval 	fPlayButton->SetStopped();
101057e2f323SJérôme Duval 	fTrackSlider->ResetMainTime();
101157e2f323SJérôme Duval 	fScopeView->SetMainTime(*fTrackSlider->MainTime());
101257e2f323SJérôme Duval 	return B_OK;
101357e2f323SJérôme Duval }
101457e2f323SJérôme Duval 
101557e2f323SJérôme Duval 
101657e2f323SJérôme Duval void
101757e2f323SJérôme Duval RecorderWindow::SetButtonState(BtnState state)
101857e2f323SJérôme Duval {
101957e2f323SJérôme Duval 	fButtonState = state;
102057e2f323SJérôme Duval 	UpdateButtons();
102157e2f323SJérôme Duval }
102257e2f323SJérôme Duval 
102357e2f323SJérôme Duval 
102457e2f323SJérôme Duval void
102557e2f323SJérôme Duval RecorderWindow::UpdateButtons()
102657e2f323SJérôme Duval {
102757e2f323SJérôme Duval 	bool hasSelection = (fSoundList->CurrentSelection() >= 0);
102857e2f323SJérôme Duval 	fRecordButton->SetEnabled(fButtonState != btnPlaying);
102957e2f323SJérôme Duval 	fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
103057e2f323SJérôme Duval 	fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
103157e2f323SJérôme Duval 	fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
103257e2f323SJérôme Duval 	fStopButton->SetEnabled(fButtonState != btnPaused);
103357e2f323SJérôme Duval 	fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording));
103457e2f323SJérôme Duval 	fInputField->SetEnabled(fButtonState != btnRecording);
103557e2f323SJérôme Duval }
103657e2f323SJérôme Duval 
10375fa77532SJérôme Duval #ifndef __HAIKU__
10383ee96407SJérôme Duval extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(
10393ee96407SJérôme Duval 	BMediaTrack *self, media_format *inout_format);
10405fa77532SJérôme Duval #endif
104157e2f323SJérôme Duval 
10428e32a27eSPhilippe Saint-Pierre 
1043c6f8aa29SJérôme Duval status_t
1044c6f8aa29SJérôme Duval RecorderWindow::UpdatePlayFile(SoundListItem* item, bool updateDisplay)
104557e2f323SJérôme Duval {
1046019ed09bSJérôme Duval 	fScopeView->CancelRendering();
1047019ed09bSJérôme Duval 	StopPlaying();
1048019ed09bSJérôme Duval 	StopRecording();
1049019ed09bSJérôme Duval 
1050dbbc34b6SJérôme Duval 	if (fPlayTrack && fPlayFile) {
10517942339dSJérôme Duval 		fPlayFile->ReleaseTrack(fPlayTrack);
10527942339dSJérôme Duval 		fPlayTrack = NULL;
1053dbbc34b6SJérôme Duval 	}
1054dbbc34b6SJérôme Duval 	if (fPlayFile) {
1055dbbc34b6SJérôme Duval 		delete fPlayFile;
10567942339dSJérôme Duval 		fPlayFile = NULL;
1057dbbc34b6SJérôme Duval 	}
10587942339dSJérôme Duval 
105957e2f323SJérôme Duval 	status_t err;
1060c6f8aa29SJérôme Duval 	BEntry& entry = item->Entry();
106157e2f323SJérôme Duval 	entry_ref ref;
106257e2f323SJérôme Duval 	entry.GetRef(&ref);
106357e2f323SJérôme Duval 	fPlayFile = new BMediaFile(&ref); //, B_MEDIA_FILE_UNBUFFERED);
106457e2f323SJérôme Duval 	if ((err = fPlayFile->InitCheck()) < B_OK) {
106557e2f323SJérôme Duval 		delete fPlayFile;
1066dbbc34b6SJérôme Duval 		fPlayFile = NULL;
1067c6f8aa29SJérôme Duval 		return err;
106857e2f323SJérôme Duval 	}
106957e2f323SJérôme Duval 
107057e2f323SJérôme Duval 	for (int ix=0; ix < fPlayFile->CountTracks(); ix++) {
107157e2f323SJérôme Duval 		BMediaTrack * track = fPlayFile->TrackAt(ix);
107257e2f323SJérôme Duval 		fPlayFormat.type = B_MEDIA_RAW_AUDIO;
10735fa77532SJérôme Duval #ifdef __HAIKU__
10745fa77532SJérôme Duval 		if ((track->DecodedFormat(&fPlayFormat) == B_OK)
10755fa77532SJérôme Duval #else
10765fa77532SJérôme Duval 		if ((DecodedFormat__11BMediaTrackP12media_format(track, &fPlayFormat) == B_OK)
10775fa77532SJérôme Duval #endif
10785fa77532SJérôme Duval 			&& (fPlayFormat.type == B_MEDIA_RAW_AUDIO)) {
107957e2f323SJérôme Duval 			fPlayTrack = track;
108057e2f323SJérôme Duval 			break;
108157e2f323SJérôme Duval 		}
108257e2f323SJérôme Duval 		if (track)
108357e2f323SJérôme Duval 			fPlayFile->ReleaseTrack(track);
108457e2f323SJérôme Duval 	}
108557e2f323SJérôme Duval 
108657e2f323SJérôme Duval 	if (!fPlayTrack) {
108757e2f323SJérôme Duval 		delete fPlayFile;
1088019ed09bSJérôme Duval 		fPlayFile = NULL;
1089c6f8aa29SJérôme Duval 		return B_STREAM_NOT_FOUND;
109057e2f323SJérôme Duval 	}
109157e2f323SJérôme Duval 
1092c6f8aa29SJérôme Duval 	if (!updateDisplay)
1093c6f8aa29SJérôme Duval 		return B_OK;
1094c6f8aa29SJérôme Duval 
10953ee96407SJérôme Duval 	BString filename = B_TRANSLATE("File name: ");
109657e2f323SJérôme Duval 	filename << ref.name;
109757e2f323SJérôme Duval 	fFilename->SetText(filename.String());
109857e2f323SJérôme Duval 
10993ee96407SJérôme Duval 	BString format = B_TRANSLATE("Format: ");
110057e2f323SJérôme Duval 	media_file_format file_format;
110157e2f323SJérôme Duval 	if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK)
110257e2f323SJérôme Duval 		format << file_format.short_name;
11033ee96407SJérôme Duval 	BString compression = B_TRANSLATE("Compression: ");
110457e2f323SJérôme Duval 	media_codec_info codec_info;
110557e2f323SJérôme Duval 	if (fPlayTrack->GetCodecInfo(&codec_info) == B_OK) {
110657e2f323SJérôme Duval 		if (strcmp(codec_info.short_name, "raw")==0)
11073ee96407SJérôme Duval 			compression << B_TRANSLATE("None");
110857e2f323SJérôme Duval 		else
110957e2f323SJérôme Duval 			compression << codec_info.short_name;
111057e2f323SJérôme Duval 	}
11113ee96407SJérôme Duval 	BString channels = B_TRANSLATE("Channels: ");
111257e2f323SJérôme Duval 	channels << fPlayFormat.u.raw_audio.channel_count;
11133ee96407SJérôme Duval 	BString samplesize = B_TRANSLATE("Sample size: ");
11143ee96407SJérôme Duval 	samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf)
11153ee96407SJérôme Duval 		<< B_TRANSLATE(" bits");
11163ee96407SJérôme Duval 	BString samplerate = B_TRANSLATE("Sample rate: ");
111757e2f323SJérôme Duval 	samplerate << (int)fPlayFormat.u.raw_audio.frame_rate;
11183ee96407SJérôme Duval 	BString durationString = B_TRANSLATE("Duration: ");
111957e2f323SJérôme Duval 	bigtime_t duration = fPlayTrack->Duration();
11203ee96407SJérôme Duval 	durationString << (float)(duration / 1000000.0) << B_TRANSLATE(" seconds");
112157e2f323SJérôme Duval 
112257e2f323SJérôme Duval 	fFormat->SetText(format.String());
112357e2f323SJérôme Duval 	fCompression->SetText(compression.String());
112457e2f323SJérôme Duval 	fChannels->SetText(channels.String());
112557e2f323SJérôme Duval 	fSampleSize->SetText(samplesize.String());
112657e2f323SJérôme Duval 	fSampleRate->SetText(samplerate.String());
112757e2f323SJérôme Duval 	fDuration->SetText(durationString.String());
112857e2f323SJérôme Duval 
112957e2f323SJérôme Duval 	fTrackSlider->SetTotalTime(duration, true);
1130019ed09bSJérôme Duval 	fScopeView->SetTotalTime(duration, true);
113157e2f323SJérôme Duval 	fScopeView->RenderTrack(fPlayTrack, fPlayFormat);
11327942339dSJérôme Duval 
11337942339dSJérôme Duval 	fPlayFrames = fPlayTrack->CountFrames();
1134c6f8aa29SJérôme Duval 	return B_OK;
113557e2f323SJérôme Duval }
113657e2f323SJérôme Duval 
113757e2f323SJérôme Duval 
113857e2f323SJérôme Duval void
113957e2f323SJérôme Duval RecorderWindow::ErrorAlert(const char * action, status_t err)
114057e2f323SJérôme Duval {
114157e2f323SJérôme Duval 	char msg[300];
1142c6f8aa29SJérôme Duval 	if (err != B_OK)
11437f4d1af4SSergei Reznikov 		sprintf(msg, "%s: %s. [%" B_PRIx32 "]", action, strerror(err), (int32) err);
1144c6f8aa29SJérôme Duval 	else
11453ee96407SJérôme Duval 		sprintf(msg, "%s.", action);
1146aed35104SHumdinger 	BAlert* alert = new BAlert("", msg, B_TRANSLATE("Stop"));
1147aed35104SHumdinger 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1148aed35104SHumdinger 	alert->Go();
114957e2f323SJérôme Duval }
115057e2f323SJérôme Duval 
115157e2f323SJérôme Duval 
115257e2f323SJérôme Duval status_t
115357e2f323SJérôme Duval RecorderWindow::NewTempName(char * name)
115457e2f323SJérôme Duval {
115557e2f323SJérôme Duval 	int init_count = fTempCount;
115657e2f323SJérôme Duval again:
115757e2f323SJérôme Duval 	if (fTempCount-init_count > 25) {
115857e2f323SJérôme Duval 		return B_ERROR;
115957e2f323SJérôme Duval 	}
116057e2f323SJérôme Duval 	else {
116157e2f323SJérôme Duval 		fTempCount++;
116257e2f323SJérôme Duval 		if (fTempCount==0)
116357e2f323SJérôme Duval 			sprintf(name, "Audio Clip");
116457e2f323SJérôme Duval 		else
116557e2f323SJérôme Duval 			sprintf(name, "Audio Clip %d", fTempCount);
116657e2f323SJérôme Duval 		BPath path;
116757e2f323SJérôme Duval 		status_t err;
116857e2f323SJérôme Duval 		BEntry tempEnt;
116957e2f323SJérôme Duval 		if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) {
117057e2f323SJérôme Duval 			return err;
117157e2f323SJérôme Duval 		}
117257e2f323SJérôme Duval 		if ((err = tempEnt.GetPath(&path)) < B_OK) {
117357e2f323SJérôme Duval 			return err;
117457e2f323SJérôme Duval 		}
117557e2f323SJérôme Duval 		path.Append(name);
117657e2f323SJérôme Duval 		int fd;
117757e2f323SJérôme Duval 		//	Use O_EXCL so we know we created the file (sync with other instances)
117857e2f323SJérôme Duval 		if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
117957e2f323SJérôme Duval 			goto again;
118057e2f323SJérôme Duval 		}
118157e2f323SJérôme Duval 		close(fd);
118257e2f323SJérôme Duval 	}
118357e2f323SJérôme Duval 	return B_OK;
118457e2f323SJérôme Duval }
118557e2f323SJérôme Duval 
118657e2f323SJérôme Duval 
118757e2f323SJérôme Duval void
118857e2f323SJérôme Duval RecorderWindow::AddSoundItem(const BEntry& entry, bool temp)
118957e2f323SJérôme Duval {
119057e2f323SJérôme Duval 	//	Create list item to display.
119157e2f323SJérôme Duval 	SoundListItem * listItem = new SoundListItem(entry, temp);
119257e2f323SJérôme Duval 	fSoundList->AddItem(listItem);
119357e2f323SJérôme Duval 	fSoundList->Invalidate();
119457e2f323SJérôme Duval 	fSoundList->Select(fSoundList->IndexOf(listItem));
119557e2f323SJérôme Duval }
119657e2f323SJérôme Duval 
1197019ed09bSJérôme Duval 
1198019ed09bSJérôme Duval void
1199019ed09bSJérôme Duval RecorderWindow::RemoveCurrentSoundItem() {
1200c6f8aa29SJérôme Duval 	int32 index = fSoundList->CurrentSelection();
1201c6f8aa29SJérôme Duval 	BListItem *item = fSoundList->RemoveItem(index);
1202019ed09bSJérôme Duval 	delete item;
1203c6f8aa29SJérôme Duval 	if (index >= fSoundList->CountItems())
1204c6f8aa29SJérôme Duval 		index = fSoundList->CountItems() - 1;
1205c6f8aa29SJérôme Duval 	fSoundList->Select(index);
1206019ed09bSJérôme Duval }
1207019ed09bSJérôme Duval 
1208019ed09bSJérôme Duval 
120957e2f323SJérôme Duval void
1210ef367a8aSStephan Aßmus RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,
1211*739fd34cSBarrett 	void* data, size_t size, const media_format &format)
121257e2f323SJérôme Duval {
121357e2f323SJérôme Duval 	//	Callback called from the SoundConsumer when receiving buffers.
121457e2f323SJérôme Duval 	RecorderWindow * window = (RecorderWindow *)cookie;
121557e2f323SJérôme Duval 
121657e2f323SJérôme Duval 	if (window->fRecording) {
121757e2f323SJérôme Duval 		//	Write the data to file (we don't buffer or guard file access
121857e2f323SJérôme Duval 		//	or anything)
121957e2f323SJérôme Duval 		window->fRecFile.WriteAt(window->fRecSize, data, size);
1220*739fd34cSBarrett 		window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format);
122157e2f323SJérôme Duval 		window->fRecSize += size;
122257e2f323SJérôme Duval 	}
122357e2f323SJérôme Duval }
122457e2f323SJérôme Duval 
122557e2f323SJérôme Duval 
122657e2f323SJérôme Duval void
122757e2f323SJérôme Duval RecorderWindow::NotifyRecordFile(void * cookie, int32 code, ...)
122857e2f323SJérôme Duval {
122957e2f323SJérôme Duval 	if ((code == B_WILL_STOP) || (code == B_NODE_DIES)) {
123057e2f323SJérôme Duval 		RecorderWindow * window = (RecorderWindow *)cookie;
123157e2f323SJérôme Duval 		// Tell the window we've stopped, if it doesn't
123257e2f323SJérôme Duval 		// already know.
123357e2f323SJérôme Duval 		window->PostMessage(STOP_RECORDING);
123457e2f323SJérôme Duval 	}
123557e2f323SJérôme Duval }
123657e2f323SJérôme Duval 
123757e2f323SJérôme Duval 
123857e2f323SJérôme Duval void
1239ad0c65eaSJérôme Duval RecorderWindow::PlayFile(void * cookie, void * data, size_t size,
1240ad0c65eaSJérôme Duval 	const media_raw_audio_format & format)
124157e2f323SJérôme Duval {
124257e2f323SJérôme Duval 	//	Callback called from the SoundProducer when producing buffers.
124357e2f323SJérôme Duval 	RecorderWindow * window = (RecorderWindow *)cookie;
124457e2f323SJérôme Duval 	int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) *
124557e2f323SJérôme Duval 		window->fPlayFormat.u.raw_audio.channel_count;
124657e2f323SJérôme Duval 
1247948356deSJérôme Duval 	if ((window->fPlayFrame < window->fPlayLimit) || window->fLooping) {
1248948356deSJérôme Duval 		if (window->fPlayFrame >= window->fPlayLimit) {
1249948356deSJérôme Duval 			bigtime_t left = window->fTrackSlider->LeftTime();
1250948356deSJérôme Duval 			window->fPlayTrack->SeekToTime(&left);
1251948356deSJérôme Duval 			window->fPlayFrame = window->fPlayTrack->CurrentFrame();
1252948356deSJérôme Duval 		}
125357e2f323SJérôme Duval 		int64 frames = 0;
125457e2f323SJérôme Duval 		window->fPlayTrack->ReadFrames(data, &frames);
1255ad0c65eaSJérôme Duval 		window->fVUView->ComputeLevels(data, size / frame_size, format.format);
125657e2f323SJérôme Duval 		window->fPlayFrame += size/frame_size;
125757e2f323SJérôme Duval 		window->PostMessage(UPDATE_TRACKSLIDER);
125857e2f323SJérôme Duval 	} else {
125957e2f323SJérôme Duval 		//	we're done!
126057e2f323SJérôme Duval 		window->PostMessage(STOP_PLAYING);
126157e2f323SJérôme Duval 	}
126257e2f323SJérôme Duval }
126357e2f323SJérôme Duval 
12648e32a27eSPhilippe Saint-Pierre 
126557e2f323SJérôme Duval void
1266ad0c65eaSJérôme Duval RecorderWindow::NotifyPlayFile(void * cookie,
1267ad0c65eaSJérôme Duval 	BSoundPlayer::sound_player_notification code, ...)
126857e2f323SJérôme Duval {
126957e2f323SJérôme Duval 	if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) {
127057e2f323SJérôme Duval 		RecorderWindow * window = (RecorderWindow *)cookie;
127157e2f323SJérôme Duval 		// tell the window we've stopped, if it doesn't
127257e2f323SJérôme Duval 		// already know.
127357e2f323SJérôme Duval 		window->PostMessage(STOP_PLAYING);
127457e2f323SJérôme Duval 	}
127557e2f323SJérôme Duval }
127657e2f323SJérôme Duval 
127757e2f323SJérôme Duval 
127857e2f323SJérôme Duval void
127957e2f323SJérôme Duval RecorderWindow::RefsReceived(BMessage *msg)
128057e2f323SJérôme Duval {
128157e2f323SJérôme Duval 	entry_ref ref;
128257e2f323SJérôme Duval 	int32 i = 0;
1283c6f8aa29SJérôme Duval 	int32 countGood = 0;
1284c6f8aa29SJérôme Duval 	int32 countBad = 0;
128557e2f323SJérôme Duval 
128657e2f323SJérôme Duval 	while (msg->FindRef("refs", i++, &ref) == B_OK) {
128757e2f323SJérôme Duval 
128857e2f323SJérôme Duval 		BEntry entry(&ref, true);
128957e2f323SJérôme Duval 		BPath path(&entry);
129057e2f323SJérôme Duval 		BNode node(&entry);
129157e2f323SJérôme Duval 
129257e2f323SJérôme Duval 		if (node.IsFile()) {
1293c6f8aa29SJérôme Duval 			SoundListItem * listItem = new SoundListItem(entry, false);
1294c6f8aa29SJérôme Duval 			if (UpdatePlayFile(listItem) == B_OK) {
1295c6f8aa29SJérôme Duval 				fSoundList->AddItem(listItem);
1296c6f8aa29SJérôme Duval 				countGood++;
1297c6f8aa29SJérôme Duval 				continue;
1298c6f8aa29SJérôme Duval 			}
1299c6f8aa29SJérôme Duval 			delete listItem;
130057e2f323SJérôme Duval 		} else if(node.IsDirectory()) {
130157e2f323SJérôme Duval 
130257e2f323SJérôme Duval 		}
1303c6f8aa29SJérôme Duval 		countBad++;
1304c6f8aa29SJérôme Duval 	}
1305c6f8aa29SJérôme Duval 
1306193a3956SHumdinger 	if (countBad == 1 && countGood == 0) {
1307aed35104SHumdinger 		BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1308193a3956SHumdinger 			B_TRANSLATE("The file doesn't appear to be an audio file."),
1309193a3956SHumdinger 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1310193a3956SHumdinger 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1311193a3956SHumdinger 		alert->Go();
1312193a3956SHumdinger 	} else if (countBad > 0 && countGood == 0) {
1313193a3956SHumdinger 		BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1314193a3956SHumdinger 			B_TRANSLATE("None of the files appear to be audio files."),
1315aed35104SHumdinger 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1316aed35104SHumdinger 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1317aed35104SHumdinger 		alert->Go();
1318b1ed9a63SJérôme Duval 	} else if (countGood > 0) {
1319aed35104SHumdinger 		if (countBad > 0) {
1320aed35104SHumdinger 			BAlert* alert = new BAlert(B_TRANSLATE("Invalid audio files"),
1321193a3956SHumdinger 			B_TRANSLATE("Some of the files don't appear to be audio files."),
13223ee96407SJérôme Duval 				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
1323aed35104SHumdinger 				B_WARNING_ALERT);
1324aed35104SHumdinger 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1325aed35104SHumdinger 			alert->Go();
1326aed35104SHumdinger 		}
1327c6f8aa29SJérôme Duval 		fSoundList->Select(fSoundList->CountItems() - 1);
132857e2f323SJérôme Duval 	}
132957e2f323SJérôme Duval }
1330019ed09bSJérôme Duval 
1331019ed09bSJérôme Duval 
1332019ed09bSJérôme Duval void
1333019ed09bSJérôme Duval RecorderWindow::CopyTarget(BMessage *msg)
1334019ed09bSJérôme Duval {
1335019ed09bSJérôme Duval 	const char *type = NULL;
1336019ed09bSJérôme Duval 	if (msg->FindString("be:types", &type) == B_OK) {
1337019ed09bSJérôme Duval 		if (!strcasecmp(type, B_FILE_MIME_TYPE)) {
1338019ed09bSJérôme Duval 			const char *name;
1339019ed09bSJérôme Duval 			entry_ref dir;
1340019ed09bSJérôme Duval 			if (msg->FindString("be:filetypes") == B_OK
1341019ed09bSJérôme Duval 				&& msg->FindString("name", &name) == B_OK
1342019ed09bSJérôme Duval 				&& msg->FindRef("directory", &dir) == B_OK) {
1343019ed09bSJérôme Duval 				BDirectory directory(&dir);
1344019ed09bSJérôme Duval 				BFile file(&directory, name, O_RDWR | O_TRUNC);
1345019ed09bSJérôme Duval 
1346019ed09bSJérôme Duval 				// seek time
1347019ed09bSJérôme Duval 				bigtime_t start = fTrackSlider->LeftTime();
1348019ed09bSJérôme Duval 
1349019ed09bSJérôme Duval 				// write data
13503ee96407SJérôme Duval 				bigtime_t diffTime = fTrackSlider->RightTime()
13513ee96407SJérôme Duval 					- fTrackSlider->LeftTime();
1352ad0c65eaSJérôme Duval 				int64 framesToWrite = (int64) (diffTime
1353ad0c65eaSJérôme Duval 					* fPlayFormat.u.raw_audio.frame_rate / 1000000LL);
1354019ed09bSJérôme Duval 				int32 frameSize = (fPlayFormat.u.raw_audio.format & 0xf)
1355019ed09bSJérôme Duval 					* fPlayFormat.u.raw_audio.channel_count;
1356019ed09bSJérôme Duval 
1357019ed09bSJérôme Duval 				wave_struct header;
1358019ed09bSJérôme Duval 				header.riff.riff_id = FOURCC('R','I','F','F');
13593ee96407SJérôme Duval 				header.riff.len
13603ee96407SJérôme Duval 					= (frameSize * framesToWrite) + sizeof(header) - 8;
1361019ed09bSJérôme Duval 				header.riff.wave_id = FOURCC('W','A','V','E');
1362019ed09bSJérôme Duval 				header.format_chunk.fourcc = FOURCC('f','m','t',' ');
1363019ed09bSJérôme Duval 				header.format_chunk.len = sizeof(header.format);
1364019ed09bSJérôme Duval 				header.format.format_tag = 1;
1365019ed09bSJérôme Duval 				header.format.channels = fPlayFormat.u.raw_audio.channel_count;
13663ee96407SJérôme Duval 				header.format.samples_per_sec
13673ee96407SJérôme Duval 					= (uint32)fPlayFormat.u.raw_audio.frame_rate;
13683ee96407SJérôme Duval 				header.format.avg_bytes_per_sec
13693ee96407SJérôme Duval 					= (uint32)(fPlayFormat.u.raw_audio.frame_rate
1370019ed09bSJérôme Duval 					* fPlayFormat.u.raw_audio.channel_count
1371019ed09bSJérôme Duval 					* (fPlayFormat.u.raw_audio.format & 0xf));
13723ee96407SJérôme Duval 				header.format.bits_per_sample
13733ee96407SJérôme Duval 					= (fPlayFormat.u.raw_audio.format & 0xf) * 8;
1374019ed09bSJérôme Duval 				header.format.block_align = frameSize;
1375019ed09bSJérôme Duval 				header.data_chunk.fourcc = FOURCC('d','a','t','a');
1376019ed09bSJérôme Duval 				header.data_chunk.len = frameSize * framesToWrite;
1377019ed09bSJérôme Duval 				file.Seek(0, SEEK_SET);
1378019ed09bSJérôme Duval 				file.Write(&header, sizeof(header));
1379019ed09bSJérôme Duval 
1380019ed09bSJérôme Duval 				char *data = (char *)malloc(fPlayFormat.u.raw_audio.buffer_size);
1381019ed09bSJérôme Duval 
1382c6f8aa29SJérôme Duval 				fPlayTrack->SeekToTime(&start);
1383c6f8aa29SJérôme Duval 				fPlayFrame = fPlayTrack->CurrentFrame();
1384019ed09bSJérôme Duval 				while (framesToWrite > 0) {
1385019ed09bSJérôme Duval 					int64 frames = 0;
1386019ed09bSJérôme Duval 					status_t err = fPlayTrack->ReadFrames(data, &frames);
1387c6f8aa29SJérôme Duval 					if (frames <= 0 || err != B_OK) {
1388c6f8aa29SJérôme Duval 						if (err != B_OK)
1389c6f8aa29SJérôme Duval 							fprintf(stderr, "CopyTarget: ReadFrames failed\n");
1390019ed09bSJérôme Duval 						break;
1391c6f8aa29SJérôme Duval 					}
1392019ed09bSJérôme Duval 					file.Write(data, frames * frameSize);
1393019ed09bSJérôme Duval 					framesToWrite -= frames;
1394019ed09bSJérôme Duval 				}
1395019ed09bSJérôme Duval 
1396019ed09bSJérôme Duval 				file.Sync();
1397019ed09bSJérôme Duval 				free(data);
1398019ed09bSJérôme Duval 				BNodeInfo nodeInfo(&file);
1399019ed09bSJérôme Duval 				// set type
1400019ed09bSJérôme Duval 			}
1401019ed09bSJérôme Duval 		} else {
1402019ed09bSJérôme Duval 
1403019ed09bSJérôme Duval 		}
1404019ed09bSJérôme Duval 	}
1405019ed09bSJérôme Duval }
1406