157e2f323SJérôme Duval /*
2739fd34cSBarrett * Copyright 2014, Dario Casalinuovo. All rights reserved.
3683cf2ffSHamish Morrison * Copyright 2005, Jérôme Duval. 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
4657e2f323SJérôme Duval #include "RecorderWindow.h"
4757e2f323SJérôme Duval #include "FileUtils.h"
4857e2f323SJérôme Duval
4957e2f323SJérôme Duval #if ! NDEBUG
5057e2f323SJérôme Duval #define FPRINTF(args) fprintf args
5157e2f323SJérôme Duval #else
5257e2f323SJérôme Duval #define FPRINTF(args)
5357e2f323SJérôme Duval #endif
5457e2f323SJérôme Duval
5557e2f323SJérôme Duval #define DEATH FPRINTF
5657e2f323SJérôme Duval #define CONNECT FPRINTF
5757e2f323SJérôme Duval #define WINDOW FPRINTF
5857e2f323SJérôme Duval
59546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT
60546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "RecorderWindow"
613ee96407SJérôme Duval
623ee96407SJérôme Duval
6357e2f323SJérôme Duval // default window positioning
6457e2f323SJérôme Duval static const float MIN_WIDTH = 400.0f;
65b271ff3fSPhilippe Saint-Pierre static const float MIN_HEIGHT = 175.0f;
6657e2f323SJérôme Duval static const float XPOS = 100.0f;
6757e2f323SJérôme Duval static const float YPOS = 200.0f;
6857e2f323SJérôme Duval
69ad0c65eaSJérôme Duval #define FOURCC(a,b,c,d) ((((uint32)(d)) << 24) | (((uint32)(c)) << 16) \
70ad0c65eaSJérôme Duval | (((uint32)(b)) << 8) | ((uint32)(a)))
7157e2f323SJérôme Duval
7257e2f323SJérôme Duval struct riff_struct
7357e2f323SJérôme Duval {
7457e2f323SJérôme Duval uint32 riff_id; // 'RIFF'
7557e2f323SJérôme Duval uint32 len;
7657e2f323SJérôme Duval uint32 wave_id; // 'WAVE'
7757e2f323SJérôme Duval };
7857e2f323SJérôme Duval
7957e2f323SJérôme Duval struct chunk_struct
8057e2f323SJérôme Duval {
8157e2f323SJérôme Duval uint32 fourcc;
8257e2f323SJérôme Duval uint32 len;
8357e2f323SJérôme Duval };
8457e2f323SJérôme Duval
8557e2f323SJérôme Duval struct format_struct
8657e2f323SJérôme Duval {
8757e2f323SJérôme Duval uint16 format_tag;
8857e2f323SJérôme Duval uint16 channels;
8957e2f323SJérôme Duval uint32 samples_per_sec;
9057e2f323SJérôme Duval uint32 avg_bytes_per_sec;
9157e2f323SJérôme Duval uint16 block_align;
9257e2f323SJérôme Duval uint16 bits_per_sample;
9357e2f323SJérôme Duval };
9457e2f323SJérôme Duval
9557e2f323SJérôme Duval
9657e2f323SJérôme Duval struct wave_struct
9757e2f323SJérôme Duval {
9857e2f323SJérôme Duval struct riff_struct riff;
9957e2f323SJérôme Duval struct chunk_struct format_chunk;
10057e2f323SJérôme Duval struct format_struct format;
10157e2f323SJérôme Duval struct chunk_struct data_chunk;
10257e2f323SJérôme Duval };
10357e2f323SJérôme Duval
10457e2f323SJérôme Duval
RecorderWindow()10571302e7cSJonas Sundström RecorderWindow::RecorderWindow()
10671302e7cSJonas Sundström :
10771302e7cSJonas Sundström BWindow(BRect(XPOS, YPOS, XPOS + MIN_WIDTH, YPOS + MIN_HEIGHT),
108560ff447SJonas Sundström B_TRANSLATE_SYSTEM_NAME("SoundRecorder"), B_TITLED_WINDOW,
10971302e7cSJonas Sundström B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE),
11057e2f323SJérôme Duval fPlayer(NULL),
1115fa77532SJérôme Duval fSoundList(NULL),
11257e2f323SJérôme Duval fPlayFile(NULL),
11357e2f323SJérôme Duval fPlayTrack(NULL),
1147942339dSJérôme Duval fPlayFrames(0),
115948356deSJérôme Duval fLooping(false),
1165fa77532SJérôme Duval fSavePanel(NULL),
117cff7eda5SWim van der Meer fInitCheck(B_OK)
11857e2f323SJérôme Duval {
11957e2f323SJérôme Duval fRoster = NULL;
12057e2f323SJérôme Duval fRecordButton = NULL;
12157e2f323SJérôme Duval fPlayButton = NULL;
12257e2f323SJérôme Duval fStopButton = NULL;
12357e2f323SJérôme Duval fSaveButton = NULL;
124948356deSJérôme Duval fLoopButton = NULL;
12557e2f323SJérôme Duval fInputField = NULL;
126739fd34cSBarrett fRecorder = NULL;
12757e2f323SJérôme Duval fRecording = false;
1287b83e044SDario Casalinuovo fExternalConnection = 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
~RecorderWindow()14857e2f323SJérôme Duval RecorderWindow::~RecorderWindow()
14957e2f323SJérôme Duval {
150739fd34cSBarrett // The MediaRecorder have to be deleted, the dtor
151739fd34cSBarrett // disconnect it from the media_kit.
152739fd34cSBarrett delete fRecorder;
153739fd34cSBarrett
15457e2f323SJérôme Duval delete fPlayer;
1557942339dSJérôme Duval
1567942339dSJérôme Duval if (fPlayTrack && fPlayFile)
1577942339dSJérôme Duval fPlayFile->ReleaseTrack(fPlayTrack);
158739fd34cSBarrett
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
InitCheck()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
CalcSizes(float min_width,float min_height)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
InitWindow()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
237739fd34cSBarrett fRecorder = new BMediaRecorder("Sound Recorder",
238739fd34cSBarrett B_MEDIA_RAW_AUDIO);
239739fd34cSBarrett
240739fd34cSBarrett if (fRecorder->InitCheck() < B_OK)
24157e2f323SJérôme Duval goto bad_mojo;
24257e2f323SJérôme Duval
243739fd34cSBarrett // Set the node to accept only audio data
244739fd34cSBarrett media_format output_format;
245739fd34cSBarrett output_format.type = B_MEDIA_RAW_AUDIO;
246739fd34cSBarrett output_format.u.raw_audio = media_raw_audio_format::wildcard;
247739fd34cSBarrett fRecorder->SetAcceptedFormat(output_format);
248739fd34cSBarrett
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;
252739fd34cSBarrett BBox *background = new BBox(r, "_background",
253739fd34cSBarrett B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW
254739fd34cSBarrett | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
255739fd34cSBarrett
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));
377fa19dd44Slooncraz fSoundList->SetViewUIColor(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;
464739fd34cSBarrett
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;
525739fd34cSBarrett if (fRecorder)
526739fd34cSBarrett 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
QuitRequested()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
MessageReceived(BMessage * message)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
Record(BMessage * message)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 }
706739fd34cSBarrett fRecorder->Start();
70757e2f323SJérôme Duval }
70857e2f323SJérôme Duval
7098e32a27eSPhilippe Saint-Pierre
71057e2f323SJérôme Duval void
Play(BMessage * message)71157e2f323SJérôme Duval RecorderWindow::Play(BMessage * message)
71257e2f323SJérôme Duval {
71357e2f323SJérôme Duval if (fPlayer) {
71457e2f323SJérôme Duval // User pressed Play button and playing
71557e2f323SJérôme Duval if (fPlayer->HasData())
71657e2f323SJérôme Duval fPlayButton->SetPaused();
71757e2f323SJérôme Duval else
71857e2f323SJérôme Duval fPlayButton->SetPlaying();
71957e2f323SJérôme Duval fPlayer->SetHasData(!fPlayer->HasData());
72057e2f323SJérôme Duval return;
72157e2f323SJérôme Duval }
72257e2f323SJérôme Duval
72357e2f323SJérôme Duval SetButtonState(btnPlaying);
72457e2f323SJérôme Duval fPlayButton->SetPlaying();
72557e2f323SJérôme Duval
72657e2f323SJérôme Duval if (!fPlayTrack) {
7273ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot get the file to play"), B_ERROR);
72857e2f323SJérôme Duval return;
72957e2f323SJérôme Duval }
73057e2f323SJérôme Duval
731ad0c65eaSJérôme Duval fPlayLimit = MIN(fPlayFrames, (off_t)(fTrackSlider->RightTime()
732ad0c65eaSJérôme Duval * fPlayFormat.u.raw_audio.frame_rate / 1000000LL));
73357e2f323SJérôme Duval fPlayTrack->SeekToTime(fTrackSlider->MainTime());
73457e2f323SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame();
73557e2f323SJérôme Duval
73657e2f323SJérôme Duval // Create our internal Node which plays sound, and register it.
7373ee96407SJérôme Duval fPlayer = new BSoundPlayer(fAudioMixerNode, &fPlayFormat.u.raw_audio,
7383ee96407SJérôme Duval "Sound Player");
73957e2f323SJérôme Duval status_t err = fPlayer->InitCheck();
740b1ed9a63SJérôme Duval if (err < B_OK)
74157e2f323SJérôme Duval return;
74257e2f323SJérôme Duval
74357e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(fPlayer);
74457e2f323SJérôme Duval fPlayer->SetCallbacks(PlayFile, NotifyPlayFile, this);
74557e2f323SJérôme Duval
74657e2f323SJérôme Duval // And get it going...
74757e2f323SJérôme Duval fPlayer->Start();
74857e2f323SJérôme Duval fPlayer->SetHasData(true);
74957e2f323SJérôme Duval }
75057e2f323SJérôme Duval
7518e32a27eSPhilippe Saint-Pierre
75257e2f323SJérôme Duval void
Stop(BMessage * message)75357e2f323SJérôme Duval RecorderWindow::Stop(BMessage * message)
75457e2f323SJérôme Duval {
75557e2f323SJérôme Duval // User pressed Stop button.
75657e2f323SJérôme Duval // Stop recorder.
75757e2f323SJérôme Duval StopRecording();
75857e2f323SJérôme Duval // Stop player.
75957e2f323SJérôme Duval StopPlaying();
76057e2f323SJérôme Duval }
76157e2f323SJérôme Duval
7628e32a27eSPhilippe Saint-Pierre
76357e2f323SJérôme Duval void
Save(BMessage * message)76457e2f323SJérôme Duval RecorderWindow::Save(BMessage * message)
76557e2f323SJérôme Duval {
76657e2f323SJérôme Duval // User pressed Save button.
76757e2f323SJérôme Duval // Find the item to save.
76857e2f323SJérôme Duval int32 index = fSoundList->CurrentSelection();
76957e2f323SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(index));
770b1ed9a63SJérôme Duval if ((! pItem) || (pItem->Entry().InitCheck() != B_OK))
77157e2f323SJérôme Duval return;
77257e2f323SJérôme Duval
77357e2f323SJérôme Duval // Update the save panel and show it.
77457e2f323SJérôme Duval char filename[B_FILE_NAME_LENGTH];
77557e2f323SJérôme Duval pItem->Entry().GetName(filename);
77657e2f323SJérôme Duval BMessage saveMsg(B_SAVE_REQUESTED);
77757e2f323SJérôme Duval entry_ref ref;
77857e2f323SJérôme Duval pItem->Entry().GetRef(&ref);
77957e2f323SJérôme Duval
780a4797804SJérôme Duval if (saveMsg.AddPointer("sound list item", pItem) != B_OK)
781a4797804SJérôme Duval fprintf(stderr, "failed to add pItem\n");
7825fa77532SJérôme Duval fSavePanel->SetSaveText(filename);
7835fa77532SJérôme Duval fSavePanel->SetMessage(&saveMsg);
7845fa77532SJérôme Duval fSavePanel->Show();
78557e2f323SJérôme Duval }
78657e2f323SJérôme Duval
7878e32a27eSPhilippe Saint-Pierre
78857e2f323SJérôme Duval void
DoSave(BMessage * message)78957e2f323SJérôme Duval RecorderWindow::DoSave(BMessage * message)
79057e2f323SJérôme Duval {
79157e2f323SJérôme Duval // User picked a place to put the file.
79257e2f323SJérôme Duval // Find the location of the old (e.g.
79357e2f323SJérôme Duval // temporary file), and the name of the
79457e2f323SJérôme Duval // new file to save.
79557e2f323SJérôme Duval entry_ref old_ref, new_dir_ref;
79657e2f323SJérôme Duval const char* new_name;
79757e2f323SJérôme Duval SoundListItem* pItem;
79857e2f323SJérôme Duval
79957e2f323SJérôme Duval if ((message->FindPointer("sound list item", (void**) &pItem) == B_OK)
80057e2f323SJérôme Duval && (message->FindRef("directory", &new_dir_ref) == B_OK)
801b1ed9a63SJérôme Duval && (message->FindString("name", &new_name) == B_OK)) {
80257e2f323SJérôme Duval BEntry& oldEntry = pItem->Entry();
80357e2f323SJérôme Duval BFile oldFile(&oldEntry, B_READ_WRITE);
80457e2f323SJérôme Duval if (oldFile.InitCheck() != B_OK)
80557e2f323SJérôme Duval return;
80657e2f323SJérôme Duval
80757e2f323SJérôme Duval BDirectory newDir(&new_dir_ref);
80857e2f323SJérôme Duval if (newDir.InitCheck() != B_OK)
80957e2f323SJérôme Duval return;
81057e2f323SJérôme Duval
81157e2f323SJérôme Duval BFile newFile;
81257e2f323SJérôme Duval newDir.CreateFile(new_name, &newFile);
81357e2f323SJérôme Duval
81457e2f323SJérôme Duval if (newFile.InitCheck() != B_OK)
81557e2f323SJérôme Duval return;
81657e2f323SJérôme Duval
81757e2f323SJérôme Duval status_t err = CopyFile(newFile, oldFile);
81857e2f323SJérôme Duval
81957e2f323SJérôme Duval if (err == B_OK) {
82057e2f323SJérôme Duval // clean up the sound list and item
82157e2f323SJérôme Duval if (pItem->IsTemp())
82257e2f323SJérôme Duval oldEntry.Remove(); // blows away temp file!
82357e2f323SJérôme Duval oldEntry.SetTo(&newDir, new_name);
82457e2f323SJérôme Duval pItem->SetTemp(false); // don't blow the new entry away when we exit!
82557e2f323SJérôme Duval fSoundList->Invalidate();
82657e2f323SJérôme Duval }
82757e2f323SJérôme Duval } else {
82857e2f323SJérôme Duval WINDOW((stderr, "Couldn't save file.\n"));
82957e2f323SJérôme Duval }
83057e2f323SJérôme Duval }
83157e2f323SJérôme Duval
83257e2f323SJérôme Duval
83357e2f323SJérôme Duval void
Input(BMessage * message)83457e2f323SJérôme Duval RecorderWindow::Input(BMessage * message)
83557e2f323SJérôme Duval {
83657e2f323SJérôme Duval // User selected input from pop-up
83757e2f323SJérôme Duval const dormant_node_info * dni = 0;
83857e2f323SJérôme Duval ssize_t size = 0;
839b1ed9a63SJérôme Duval if (message->FindData("node", B_RAW_TYPE, (const void **)&dni, &size))
84057e2f323SJérôme Duval return; // bad input selection message
84157e2f323SJérôme Duval
84257e2f323SJérôme Duval media_node_id node_id;
84357e2f323SJérôme Duval status_t error = fRoster->GetInstancesFor(dni->addon, dni->flavor_id, &node_id);
844b1ed9a63SJérôme Duval if (error != B_OK)
84557e2f323SJérôme Duval fRoster->InstantiateDormantNode(*dni, &fAudioInputNode);
846b1ed9a63SJérôme Duval else
84757e2f323SJérôme Duval fRoster->GetNodeFor(node_id, &fAudioInputNode);
84857e2f323SJérôme Duval }
84957e2f323SJérôme Duval
8508e32a27eSPhilippe Saint-Pierre
85157e2f323SJérôme Duval void
Selected(BMessage * message)85257e2f323SJérôme Duval RecorderWindow::Selected(BMessage * message)
85357e2f323SJérôme Duval {
85457e2f323SJérôme Duval // User selected a sound in list view
855c6f8aa29SJérôme Duval int32 selIdx = fSoundList->CurrentSelection();
856c6f8aa29SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(selIdx));
857c6f8aa29SJérôme Duval if (!pItem)
858c6f8aa29SJérôme Duval return;
859c6f8aa29SJérôme Duval status_t err = UpdatePlayFile(pItem, true);
860c6f8aa29SJérôme Duval if (err != B_OK) {
8613ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot recognize this file as a media file"),
862ad0c65eaSJérôme Duval err == B_MEDIA_NO_HANDLER ? B_OK : err);
863c6f8aa29SJérôme Duval RemoveCurrentSoundItem();
864c6f8aa29SJérôme Duval }
86557e2f323SJérôme Duval UpdateButtons();
86657e2f323SJérôme Duval }
86757e2f323SJérôme Duval
8688e32a27eSPhilippe Saint-Pierre
86957e2f323SJérôme Duval status_t
MakeRecordConnection(const media_node & input)87057e2f323SJérôme Duval RecorderWindow::MakeRecordConnection(const media_node & input)
87157e2f323SJérôme Duval {
87257e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n"));
87357e2f323SJérôme Duval
874739fd34cSBarrett status_t err = B_OK;
875739fd34cSBarrett media_output audioOutput;
876739fd34cSBarrett
877739fd34cSBarrett if (!fRecorder->IsConnected()) {
87857e2f323SJérôme Duval // Find an available output for the given input node.
87957e2f323SJérôme Duval int32 count = 0;
880739fd34cSBarrett err = fRoster->GetFreeOutputsFor(input, &audioOutput, 1,
881739fd34cSBarrett &count, B_MEDIA_RAW_AUDIO);
882739fd34cSBarrett
88357e2f323SJérôme Duval if (err < B_OK) {
884ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
885ad0c65eaSJérôme Duval " couldn't get free outputs from audio input node\n"));
88657e2f323SJérôme Duval return err;
88757e2f323SJérôme Duval }
888739fd34cSBarrett
88957e2f323SJérôme Duval if (count < 1) {
890ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
891ad0c65eaSJérôme Duval " no free outputs from audio input node\n"));
89257e2f323SJérôme Duval return B_BUSY;
89357e2f323SJérôme Duval }
89457e2f323SJérôme Duval
89557e2f323SJérôme Duval // Get a format, any format.
896739fd34cSBarrett fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio;
8977b83e044SDario Casalinuovo fExternalConnection = false;
8987b83e044SDario Casalinuovo } else {
8997b83e044SDario Casalinuovo fRecordFormat.u.raw_audio = fRecorder->AcceptedFormat().u.raw_audio;
9007b83e044SDario Casalinuovo fExternalConnection = true;
9017b83e044SDario Casalinuovo }
9027b83e044SDario Casalinuovo
90388fef303SJérôme Duval fRecordFormat.type = B_MEDIA_RAW_AUDIO;
90457e2f323SJérôme Duval
90557e2f323SJérôme Duval // Tell the consumer where we want data to go.
906739fd34cSBarrett err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this);
907739fd34cSBarrett
90857e2f323SJérôme Duval if (err < B_OK) {
909ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
910ad0c65eaSJérôme Duval " couldn't set the sound recorder's hook functions\n"));
91157e2f323SJérôme Duval return err;
91257e2f323SJérôme Duval }
91357e2f323SJérôme Duval
914739fd34cSBarrett if (!fRecorder->IsConnected()) {
915739fd34cSBarrett
916739fd34cSBarrett err = fRecorder->Connect(input, &audioOutput, &fRecordFormat);
917739fd34cSBarrett
91857e2f323SJérôme Duval if (err < B_OK) {
919ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
920ad0c65eaSJérôme Duval " failed to connect sound recorder to audio input node.\n"));
921739fd34cSBarrett
922739fd34cSBarrett fRecorder->SetHooks(NULL, NULL, NULL);
92357e2f323SJérôme Duval return err;
92457e2f323SJérôme Duval }
925739fd34cSBarrett }
92657e2f323SJérôme Duval
92757e2f323SJérôme Duval return B_OK;
92857e2f323SJérôme Duval }
92957e2f323SJérôme Duval
93057e2f323SJérôme Duval
93157e2f323SJérôme Duval status_t
BreakRecordConnection()93257e2f323SJérôme Duval RecorderWindow::BreakRecordConnection()
93357e2f323SJérôme Duval {
934739fd34cSBarrett return fRecorder->Disconnect();
93557e2f323SJérôme Duval }
93657e2f323SJérôme Duval
9378e32a27eSPhilippe Saint-Pierre
93857e2f323SJérôme Duval status_t
StopRecording()93957e2f323SJérôme Duval RecorderWindow::StopRecording()
94057e2f323SJérôme Duval {
94157e2f323SJérôme Duval if (!fRecording)
94257e2f323SJérôme Duval return B_OK;
94357e2f323SJérôme Duval fRecording = false;
944739fd34cSBarrett
9457b83e044SDario Casalinuovo status_t err = B_OK;
9467b83e044SDario Casalinuovo err = fRecorder->Stop(true);
9477b83e044SDario Casalinuovo if (err < B_OK)
9487b83e044SDario Casalinuovo return err;
9497b83e044SDario Casalinuovo
9507b83e044SDario Casalinuovo // We maintain the connection active
9517b83e044SDario Casalinuovo // if the user connected us from Cortex.
9527b83e044SDario Casalinuovo if (!fExternalConnection) {
95357e2f323SJérôme Duval BreakRecordConnection();
9547b83e044SDario Casalinuovo }
955739fd34cSBarrett
956739fd34cSBarrett fRecorder->SetHooks(NULL, NULL, NULL);
957739fd34cSBarrett
95857e2f323SJérôme Duval if (fRecSize > 0) {
95957e2f323SJérôme Duval
96057e2f323SJérôme Duval wave_struct header;
96157e2f323SJérôme Duval header.riff.riff_id = FOURCC('R','I','F','F');
962a4797804SJérôme Duval header.riff.len = fRecSize + sizeof(header) - 8;
96357e2f323SJérôme Duval header.riff.wave_id = FOURCC('W','A','V','E');
96457e2f323SJérôme Duval header.format_chunk.fourcc = FOURCC('f','m','t',' ');
96557e2f323SJérôme Duval header.format_chunk.len = sizeof(header.format);
96657e2f323SJérôme Duval header.format.format_tag = 1;
96788fef303SJérôme Duval header.format.channels = fRecordFormat.u.raw_audio.channel_count;
96888fef303SJérôme Duval header.format.samples_per_sec = (uint32)fRecordFormat.u.raw_audio.frame_rate;
96988fef303SJérôme Duval header.format.avg_bytes_per_sec = (uint32)(fRecordFormat.u.raw_audio.frame_rate
97088fef303SJérôme Duval * fRecordFormat.u.raw_audio.channel_count
97188fef303SJérôme Duval * (fRecordFormat.u.raw_audio.format & 0xf));
97288fef303SJérôme Duval header.format.bits_per_sample = (fRecordFormat.u.raw_audio.format & 0xf) * 8;
97388fef303SJérôme Duval header.format.block_align = (fRecordFormat.u.raw_audio.format & 0xf)
97488fef303SJérôme Duval * fRecordFormat.u.raw_audio.channel_count;
97557e2f323SJérôme Duval header.data_chunk.fourcc = FOURCC('d','a','t','a');
97657e2f323SJérôme Duval header.data_chunk.len = fRecSize;
97757e2f323SJérôme Duval fRecFile.Seek(0, SEEK_SET);
97857e2f323SJérôme Duval fRecFile.Write(&header, sizeof(header));
97957e2f323SJérôme Duval
9803ee96407SJérôme Duval fRecFile.SetSize(fRecSize + sizeof(header));
9813ee96407SJérôme Duval // We reserve space; make sure we cut off any excess at the end.
98257e2f323SJérôme Duval AddSoundItem(fRecEntry, true);
983b1ed9a63SJérôme Duval } else
98457e2f323SJérôme Duval fRecEntry.Remove();
985b1ed9a63SJérôme Duval
98657e2f323SJérôme Duval // We're done for this time.
98757e2f323SJérôme Duval fRecEntry.Unset();
98857e2f323SJérôme Duval // Close the file.
98957e2f323SJérôme Duval fRecFile.Unset();
99057e2f323SJérôme Duval // No more recording going on.
99157e2f323SJérôme Duval fRecSize = 0;
99257e2f323SJérôme Duval SetButtonState(btnPaused);
99357e2f323SJérôme Duval fRecordButton->SetStopped();
99457e2f323SJérôme Duval
99557e2f323SJérôme Duval return B_OK;
99657e2f323SJérôme Duval }
99757e2f323SJérôme Duval
99857e2f323SJérôme Duval
99957e2f323SJérôme Duval status_t
StopPlaying()100057e2f323SJérôme Duval RecorderWindow::StopPlaying()
100157e2f323SJérôme Duval {
100257e2f323SJérôme Duval if (fPlayer) {
100357e2f323SJérôme Duval fPlayer->Stop();
100457e2f323SJérôme Duval fPlayer->SetCallbacks(0, 0, 0);
100557e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(NULL);
100657e2f323SJérôme Duval delete fPlayer;
100757e2f323SJérôme Duval fPlayer = NULL;
100857e2f323SJérôme Duval }
100957e2f323SJérôme Duval SetButtonState(btnPaused);
101057e2f323SJérôme Duval fPlayButton->SetStopped();
101157e2f323SJérôme Duval fTrackSlider->ResetMainTime();
101257e2f323SJérôme Duval fScopeView->SetMainTime(*fTrackSlider->MainTime());
101357e2f323SJérôme Duval return B_OK;
101457e2f323SJérôme Duval }
101557e2f323SJérôme Duval
101657e2f323SJérôme Duval
101757e2f323SJérôme Duval void
SetButtonState(BtnState state)101857e2f323SJérôme Duval RecorderWindow::SetButtonState(BtnState state)
101957e2f323SJérôme Duval {
102057e2f323SJérôme Duval fButtonState = state;
102157e2f323SJérôme Duval UpdateButtons();
102257e2f323SJérôme Duval }
102357e2f323SJérôme Duval
102457e2f323SJérôme Duval
102557e2f323SJérôme Duval void
UpdateButtons()102657e2f323SJérôme Duval RecorderWindow::UpdateButtons()
102757e2f323SJérôme Duval {
102857e2f323SJérôme Duval bool hasSelection = (fSoundList->CurrentSelection() >= 0);
102957e2f323SJérôme Duval fRecordButton->SetEnabled(fButtonState != btnPlaying);
103057e2f323SJérôme Duval fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
103157e2f323SJérôme Duval fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
103257e2f323SJérôme Duval fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
103357e2f323SJérôme Duval fStopButton->SetEnabled(fButtonState != btnPaused);
103457e2f323SJérôme Duval fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording));
103557e2f323SJérôme Duval fInputField->SetEnabled(fButtonState != btnRecording);
103657e2f323SJérôme Duval }
103757e2f323SJérôme Duval
10385fa77532SJérôme Duval #ifndef __HAIKU__
10393ee96407SJérôme Duval extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(
10403ee96407SJérôme Duval BMediaTrack *self, media_format *inout_format);
10415fa77532SJérôme Duval #endif
104257e2f323SJérôme Duval
10438e32a27eSPhilippe Saint-Pierre
1044c6f8aa29SJérôme Duval status_t
UpdatePlayFile(SoundListItem * item,bool updateDisplay)1045c6f8aa29SJérôme Duval RecorderWindow::UpdatePlayFile(SoundListItem* item, bool updateDisplay)
104657e2f323SJérôme Duval {
1047019ed09bSJérôme Duval fScopeView->CancelRendering();
1048019ed09bSJérôme Duval StopPlaying();
1049019ed09bSJérôme Duval StopRecording();
1050019ed09bSJérôme Duval
1051dbbc34b6SJérôme Duval if (fPlayTrack && fPlayFile) {
10527942339dSJérôme Duval fPlayFile->ReleaseTrack(fPlayTrack);
10537942339dSJérôme Duval fPlayTrack = NULL;
1054dbbc34b6SJérôme Duval }
1055dbbc34b6SJérôme Duval if (fPlayFile) {
1056dbbc34b6SJérôme Duval delete fPlayFile;
10577942339dSJérôme Duval fPlayFile = NULL;
1058dbbc34b6SJérôme Duval }
10597942339dSJérôme Duval
106057e2f323SJérôme Duval status_t err;
1061c6f8aa29SJérôme Duval BEntry& entry = item->Entry();
106257e2f323SJérôme Duval entry_ref ref;
106357e2f323SJérôme Duval entry.GetRef(&ref);
106457e2f323SJérôme Duval fPlayFile = new BMediaFile(&ref); //, B_MEDIA_FILE_UNBUFFERED);
106557e2f323SJérôme Duval if ((err = fPlayFile->InitCheck()) < B_OK) {
106657e2f323SJérôme Duval delete fPlayFile;
1067dbbc34b6SJérôme Duval fPlayFile = NULL;
1068c6f8aa29SJérôme Duval return err;
106957e2f323SJérôme Duval }
107057e2f323SJérôme Duval
107157e2f323SJérôme Duval for (int ix=0; ix < fPlayFile->CountTracks(); ix++) {
107257e2f323SJérôme Duval BMediaTrack * track = fPlayFile->TrackAt(ix);
107357e2f323SJérôme Duval fPlayFormat.type = B_MEDIA_RAW_AUDIO;
10745fa77532SJérôme Duval #ifdef __HAIKU__
10755fa77532SJérôme Duval if ((track->DecodedFormat(&fPlayFormat) == B_OK)
10765fa77532SJérôme Duval #else
10775fa77532SJérôme Duval if ((DecodedFormat__11BMediaTrackP12media_format(track, &fPlayFormat) == B_OK)
10785fa77532SJérôme Duval #endif
10795fa77532SJérôme Duval && (fPlayFormat.type == B_MEDIA_RAW_AUDIO)) {
108057e2f323SJérôme Duval fPlayTrack = track;
108157e2f323SJérôme Duval break;
108257e2f323SJérôme Duval }
108357e2f323SJérôme Duval if (track)
108457e2f323SJérôme Duval fPlayFile->ReleaseTrack(track);
108557e2f323SJérôme Duval }
108657e2f323SJérôme Duval
108757e2f323SJérôme Duval if (!fPlayTrack) {
108857e2f323SJérôme Duval delete fPlayFile;
1089019ed09bSJérôme Duval fPlayFile = NULL;
1090c6f8aa29SJérôme Duval return B_STREAM_NOT_FOUND;
109157e2f323SJérôme Duval }
109257e2f323SJérôme Duval
1093c6f8aa29SJérôme Duval if (!updateDisplay)
1094c6f8aa29SJérôme Duval return B_OK;
1095c6f8aa29SJérôme Duval
10963ee96407SJérôme Duval BString filename = B_TRANSLATE("File name: ");
109757e2f323SJérôme Duval filename << ref.name;
109857e2f323SJérôme Duval fFilename->SetText(filename.String());
109957e2f323SJérôme Duval
11003ee96407SJérôme Duval BString format = B_TRANSLATE("Format: ");
110157e2f323SJérôme Duval media_file_format file_format;
110257e2f323SJérôme Duval if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK)
110357e2f323SJérôme Duval format << file_format.short_name;
11043ee96407SJérôme Duval BString compression = B_TRANSLATE("Compression: ");
110557e2f323SJérôme Duval media_codec_info codec_info;
110657e2f323SJérôme Duval if (fPlayTrack->GetCodecInfo(&codec_info) == B_OK) {
110757e2f323SJérôme Duval if (strcmp(codec_info.short_name, "raw")==0)
11083ee96407SJérôme Duval compression << B_TRANSLATE("None");
110957e2f323SJérôme Duval else
111057e2f323SJérôme Duval compression << codec_info.short_name;
111157e2f323SJérôme Duval }
11123ee96407SJérôme Duval BString channels = B_TRANSLATE("Channels: ");
111357e2f323SJérôme Duval channels << fPlayFormat.u.raw_audio.channel_count;
11143ee96407SJérôme Duval BString samplesize = B_TRANSLATE("Sample size: ");
11153ee96407SJérôme Duval samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf)
11163ee96407SJérôme Duval << B_TRANSLATE(" bits");
11173ee96407SJérôme Duval BString samplerate = B_TRANSLATE("Sample rate: ");
111857e2f323SJérôme Duval samplerate << (int)fPlayFormat.u.raw_audio.frame_rate;
11193ee96407SJérôme Duval BString durationString = B_TRANSLATE("Duration: ");
112057e2f323SJérôme Duval bigtime_t duration = fPlayTrack->Duration();
11213ee96407SJérôme Duval durationString << (float)(duration / 1000000.0) << B_TRANSLATE(" seconds");
112257e2f323SJérôme Duval
112357e2f323SJérôme Duval fFormat->SetText(format.String());
112457e2f323SJérôme Duval fCompression->SetText(compression.String());
112557e2f323SJérôme Duval fChannels->SetText(channels.String());
112657e2f323SJérôme Duval fSampleSize->SetText(samplesize.String());
112757e2f323SJérôme Duval fSampleRate->SetText(samplerate.String());
112857e2f323SJérôme Duval fDuration->SetText(durationString.String());
112957e2f323SJérôme Duval
113057e2f323SJérôme Duval fTrackSlider->SetTotalTime(duration, true);
1131019ed09bSJérôme Duval fScopeView->SetTotalTime(duration, true);
113257e2f323SJérôme Duval fScopeView->RenderTrack(fPlayTrack, fPlayFormat);
11337942339dSJérôme Duval
11347942339dSJérôme Duval fPlayFrames = fPlayTrack->CountFrames();
1135c6f8aa29SJérôme Duval return B_OK;
113657e2f323SJérôme Duval }
113757e2f323SJérôme Duval
113857e2f323SJérôme Duval
113957e2f323SJérôme Duval void
ErrorAlert(const char * action,status_t err)114057e2f323SJérôme Duval RecorderWindow::ErrorAlert(const char * action, status_t err)
114157e2f323SJérôme Duval {
114257e2f323SJérôme Duval char msg[300];
1143c6f8aa29SJérôme Duval if (err != B_OK)
11447f4d1af4SSergei Reznikov sprintf(msg, "%s: %s. [%" B_PRIx32 "]", action, strerror(err), (int32) err);
1145c6f8aa29SJérôme Duval else
11463ee96407SJérôme Duval sprintf(msg, "%s.", action);
1147aed35104SHumdinger BAlert* alert = new BAlert("", msg, B_TRANSLATE("Stop"));
1148aed35104SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1149aed35104SHumdinger alert->Go();
115057e2f323SJérôme Duval }
115157e2f323SJérôme Duval
115257e2f323SJérôme Duval
115357e2f323SJérôme Duval status_t
NewTempName(char * name)115457e2f323SJérôme Duval RecorderWindow::NewTempName(char * name)
115557e2f323SJérôme Duval {
115657e2f323SJérôme Duval int init_count = fTempCount;
115757e2f323SJérôme Duval again:
115857e2f323SJérôme Duval if (fTempCount-init_count > 25) {
115957e2f323SJérôme Duval return B_ERROR;
116057e2f323SJérôme Duval }
116157e2f323SJérôme Duval else {
116257e2f323SJérôme Duval fTempCount++;
116357e2f323SJérôme Duval if (fTempCount==0)
116457e2f323SJérôme Duval sprintf(name, "Audio Clip");
116557e2f323SJérôme Duval else
116657e2f323SJérôme Duval sprintf(name, "Audio Clip %d", fTempCount);
116757e2f323SJérôme Duval BPath path;
116857e2f323SJérôme Duval status_t err;
116957e2f323SJérôme Duval BEntry tempEnt;
117057e2f323SJérôme Duval if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) {
117157e2f323SJérôme Duval return err;
117257e2f323SJérôme Duval }
117357e2f323SJérôme Duval if ((err = tempEnt.GetPath(&path)) < B_OK) {
117457e2f323SJérôme Duval return err;
117557e2f323SJérôme Duval }
117657e2f323SJérôme Duval path.Append(name);
117757e2f323SJérôme Duval int fd;
117857e2f323SJérôme Duval // Use O_EXCL so we know we created the file (sync with other instances)
117957e2f323SJérôme Duval if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
118057e2f323SJérôme Duval goto again;
118157e2f323SJérôme Duval }
118257e2f323SJérôme Duval close(fd);
118357e2f323SJérôme Duval }
118457e2f323SJérôme Duval return B_OK;
118557e2f323SJérôme Duval }
118657e2f323SJérôme Duval
118757e2f323SJérôme Duval
118857e2f323SJérôme Duval void
AddSoundItem(const BEntry & entry,bool temp)118957e2f323SJérôme Duval RecorderWindow::AddSoundItem(const BEntry& entry, bool temp)
119057e2f323SJérôme Duval {
119157e2f323SJérôme Duval // Create list item to display.
119257e2f323SJérôme Duval SoundListItem * listItem = new SoundListItem(entry, temp);
119357e2f323SJérôme Duval fSoundList->AddItem(listItem);
119457e2f323SJérôme Duval fSoundList->Invalidate();
119557e2f323SJérôme Duval fSoundList->Select(fSoundList->IndexOf(listItem));
119657e2f323SJérôme Duval }
119757e2f323SJérôme Duval
1198019ed09bSJérôme Duval
1199019ed09bSJérôme Duval void
RemoveCurrentSoundItem()1200019ed09bSJérôme Duval RecorderWindow::RemoveCurrentSoundItem() {
1201c6f8aa29SJérôme Duval int32 index = fSoundList->CurrentSelection();
1202c6f8aa29SJérôme Duval BListItem *item = fSoundList->RemoveItem(index);
1203019ed09bSJérôme Duval delete item;
1204c6f8aa29SJérôme Duval if (index >= fSoundList->CountItems())
1205c6f8aa29SJérôme Duval index = fSoundList->CountItems() - 1;
1206c6f8aa29SJérôme Duval fSoundList->Select(index);
1207019ed09bSJérôme Duval }
1208019ed09bSJérôme Duval
1209019ed09bSJérôme Duval
121057e2f323SJérôme Duval void
RecordFile(void * cookie,bigtime_t timestamp,void * data,size_t size,const media_format & format)1211ef367a8aSStephan Aßmus RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,
1212739fd34cSBarrett void* data, size_t size, const media_format &format)
121357e2f323SJérôme Duval {
121457e2f323SJérôme Duval // Callback called from the SoundConsumer when receiving buffers.
121557e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie;
121657e2f323SJérôme Duval
121757e2f323SJérôme Duval if (window->fRecording) {
121857e2f323SJérôme Duval // Write the data to file (we don't buffer or guard file access
121957e2f323SJérôme Duval // or anything)
122057e2f323SJérôme Duval window->fRecFile.WriteAt(window->fRecSize, data, size);
1221739fd34cSBarrett window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format);
122257e2f323SJérôme Duval window->fRecSize += size;
122357e2f323SJérôme Duval }
122457e2f323SJérôme Duval }
122557e2f323SJérôme Duval
122657e2f323SJérôme Duval
122757e2f323SJérôme Duval void
NotifyRecordFile(void * cookie,BMediaRecorder::notification code,...)1228683cf2ffSHamish Morrison RecorderWindow::NotifyRecordFile(void * cookie,
1229683cf2ffSHamish Morrison BMediaRecorder::notification code, ...)
123057e2f323SJérôme Duval {
1231683cf2ffSHamish Morrison if (code == BMediaRecorder::B_WILL_STOP) {
123257e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie;
123357e2f323SJérôme Duval // Tell the window we've stopped, if it doesn't
123457e2f323SJérôme Duval // already know.
123557e2f323SJérôme Duval window->PostMessage(STOP_RECORDING);
123657e2f323SJérôme Duval }
123757e2f323SJérôme Duval }
123857e2f323SJérôme Duval
123957e2f323SJérôme Duval
124057e2f323SJérôme Duval void
PlayFile(void * cookie,void * data,size_t size,const media_raw_audio_format & format)1241ad0c65eaSJérôme Duval RecorderWindow::PlayFile(void * cookie, void * data, size_t size,
1242ad0c65eaSJérôme Duval const media_raw_audio_format & format)
124357e2f323SJérôme Duval {
124457e2f323SJérôme Duval // Callback called from the SoundProducer when producing buffers.
124557e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie;
124657e2f323SJérôme Duval int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) *
124757e2f323SJérôme Duval window->fPlayFormat.u.raw_audio.channel_count;
124857e2f323SJérôme Duval
1249948356deSJérôme Duval if ((window->fPlayFrame < window->fPlayLimit) || window->fLooping) {
1250948356deSJérôme Duval if (window->fPlayFrame >= window->fPlayLimit) {
1251948356deSJérôme Duval bigtime_t left = window->fTrackSlider->LeftTime();
1252948356deSJérôme Duval window->fPlayTrack->SeekToTime(&left);
1253948356deSJérôme Duval window->fPlayFrame = window->fPlayTrack->CurrentFrame();
1254948356deSJérôme Duval }
125557e2f323SJérôme Duval int64 frames = 0;
125657e2f323SJérôme Duval window->fPlayTrack->ReadFrames(data, &frames);
1257ad0c65eaSJérôme Duval window->fVUView->ComputeLevels(data, size / frame_size, format.format);
125857e2f323SJérôme Duval window->fPlayFrame += size/frame_size;
125957e2f323SJérôme Duval window->PostMessage(UPDATE_TRACKSLIDER);
126057e2f323SJérôme Duval } else {
126157e2f323SJérôme Duval // we're done!
126257e2f323SJérôme Duval window->PostMessage(STOP_PLAYING);
126357e2f323SJérôme Duval }
126457e2f323SJérôme Duval }
126557e2f323SJérôme Duval
12668e32a27eSPhilippe Saint-Pierre
126757e2f323SJérôme Duval void
NotifyPlayFile(void * cookie,BSoundPlayer::sound_player_notification code,...)1268ad0c65eaSJérôme Duval RecorderWindow::NotifyPlayFile(void * cookie,
1269ad0c65eaSJérôme Duval BSoundPlayer::sound_player_notification code, ...)
127057e2f323SJérôme Duval {
127157e2f323SJérôme Duval if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) {
127257e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie;
127357e2f323SJérôme Duval // tell the window we've stopped, if it doesn't
127457e2f323SJérôme Duval // already know.
127557e2f323SJérôme Duval window->PostMessage(STOP_PLAYING);
127657e2f323SJérôme Duval }
127757e2f323SJérôme Duval }
127857e2f323SJérôme Duval
127957e2f323SJérôme Duval
128057e2f323SJérôme Duval void
RefsReceived(BMessage * msg)128157e2f323SJérôme Duval RecorderWindow::RefsReceived(BMessage *msg)
128257e2f323SJérôme Duval {
128357e2f323SJérôme Duval entry_ref ref;
128457e2f323SJérôme Duval int32 i = 0;
1285c6f8aa29SJérôme Duval int32 countGood = 0;
1286c6f8aa29SJérôme Duval int32 countBad = 0;
128757e2f323SJérôme Duval
128857e2f323SJérôme Duval while (msg->FindRef("refs", i++, &ref) == B_OK) {
128957e2f323SJérôme Duval
129057e2f323SJérôme Duval BEntry entry(&ref, true);
129157e2f323SJérôme Duval BPath path(&entry);
129257e2f323SJérôme Duval BNode node(&entry);
129357e2f323SJérôme Duval
129457e2f323SJérôme Duval if (node.IsFile()) {
1295c6f8aa29SJérôme Duval SoundListItem * listItem = new SoundListItem(entry, false);
1296c6f8aa29SJérôme Duval if (UpdatePlayFile(listItem) == B_OK) {
1297c6f8aa29SJérôme Duval fSoundList->AddItem(listItem);
1298c6f8aa29SJérôme Duval countGood++;
1299c6f8aa29SJérôme Duval continue;
1300c6f8aa29SJérôme Duval }
1301c6f8aa29SJérôme Duval delete listItem;
130257e2f323SJérôme Duval } else if(node.IsDirectory()) {
130357e2f323SJérôme Duval
130457e2f323SJérôme Duval }
1305c6f8aa29SJérôme Duval countBad++;
1306c6f8aa29SJérôme Duval }
1307c6f8aa29SJérôme Duval
1308193a3956SHumdinger if (countBad == 1 && countGood == 0) {
1309aed35104SHumdinger BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1310193a3956SHumdinger B_TRANSLATE("The file doesn't appear to be an audio file."),
1311193a3956SHumdinger B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1312193a3956SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1313193a3956SHumdinger alert->Go();
1314193a3956SHumdinger } else if (countBad > 0 && countGood == 0) {
1315193a3956SHumdinger BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1316193a3956SHumdinger B_TRANSLATE("None of the files appear to be audio files."),
1317aed35104SHumdinger B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1318aed35104SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1319aed35104SHumdinger alert->Go();
1320b1ed9a63SJérôme Duval } else if (countGood > 0) {
1321aed35104SHumdinger if (countBad > 0) {
1322aed35104SHumdinger BAlert* alert = new BAlert(B_TRANSLATE("Invalid audio files"),
1323193a3956SHumdinger B_TRANSLATE("Some of the files don't appear to be audio files."),
13243ee96407SJérôme Duval B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
1325aed35104SHumdinger B_WARNING_ALERT);
1326aed35104SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1327aed35104SHumdinger alert->Go();
1328aed35104SHumdinger }
1329c6f8aa29SJérôme Duval fSoundList->Select(fSoundList->CountItems() - 1);
133057e2f323SJérôme Duval }
133157e2f323SJérôme Duval }
1332019ed09bSJérôme Duval
1333019ed09bSJérôme Duval
1334019ed09bSJérôme Duval void
CopyTarget(BMessage * msg)1335019ed09bSJérôme Duval RecorderWindow::CopyTarget(BMessage *msg)
1336019ed09bSJérôme Duval {
1337019ed09bSJérôme Duval const char *type = NULL;
1338019ed09bSJérôme Duval if (msg->FindString("be:types", &type) == B_OK) {
1339019ed09bSJérôme Duval if (!strcasecmp(type, B_FILE_MIME_TYPE)) {
1340019ed09bSJérôme Duval const char *name;
1341019ed09bSJérôme Duval entry_ref dir;
1342*f7fceb69SMurai Takashi if (msg->FindString("be:filetypes") != NULL
1343019ed09bSJérôme Duval && msg->FindString("name", &name) == B_OK
1344019ed09bSJérôme Duval && msg->FindRef("directory", &dir) == B_OK) {
1345019ed09bSJérôme Duval BDirectory directory(&dir);
1346019ed09bSJérôme Duval BFile file(&directory, name, O_RDWR | O_TRUNC);
1347019ed09bSJérôme Duval
1348019ed09bSJérôme Duval // seek time
1349019ed09bSJérôme Duval bigtime_t start = fTrackSlider->LeftTime();
1350019ed09bSJérôme Duval
1351019ed09bSJérôme Duval // write data
13523ee96407SJérôme Duval bigtime_t diffTime = fTrackSlider->RightTime()
13533ee96407SJérôme Duval - fTrackSlider->LeftTime();
1354ad0c65eaSJérôme Duval int64 framesToWrite = (int64) (diffTime
1355ad0c65eaSJérôme Duval * fPlayFormat.u.raw_audio.frame_rate / 1000000LL);
1356019ed09bSJérôme Duval int32 frameSize = (fPlayFormat.u.raw_audio.format & 0xf)
1357019ed09bSJérôme Duval * fPlayFormat.u.raw_audio.channel_count;
1358019ed09bSJérôme Duval
1359019ed09bSJérôme Duval wave_struct header;
1360019ed09bSJérôme Duval header.riff.riff_id = FOURCC('R','I','F','F');
13613ee96407SJérôme Duval header.riff.len
13623ee96407SJérôme Duval = (frameSize * framesToWrite) + sizeof(header) - 8;
1363019ed09bSJérôme Duval header.riff.wave_id = FOURCC('W','A','V','E');
1364019ed09bSJérôme Duval header.format_chunk.fourcc = FOURCC('f','m','t',' ');
1365019ed09bSJérôme Duval header.format_chunk.len = sizeof(header.format);
1366019ed09bSJérôme Duval header.format.format_tag = 1;
1367019ed09bSJérôme Duval header.format.channels = fPlayFormat.u.raw_audio.channel_count;
13683ee96407SJérôme Duval header.format.samples_per_sec
13693ee96407SJérôme Duval = (uint32)fPlayFormat.u.raw_audio.frame_rate;
13703ee96407SJérôme Duval header.format.avg_bytes_per_sec
13713ee96407SJérôme Duval = (uint32)(fPlayFormat.u.raw_audio.frame_rate
1372019ed09bSJérôme Duval * fPlayFormat.u.raw_audio.channel_count
1373019ed09bSJérôme Duval * (fPlayFormat.u.raw_audio.format & 0xf));
13743ee96407SJérôme Duval header.format.bits_per_sample
13753ee96407SJérôme Duval = (fPlayFormat.u.raw_audio.format & 0xf) * 8;
1376019ed09bSJérôme Duval header.format.block_align = frameSize;
1377019ed09bSJérôme Duval header.data_chunk.fourcc = FOURCC('d','a','t','a');
1378019ed09bSJérôme Duval header.data_chunk.len = frameSize * framesToWrite;
1379019ed09bSJérôme Duval file.Seek(0, SEEK_SET);
1380019ed09bSJérôme Duval file.Write(&header, sizeof(header));
1381019ed09bSJérôme Duval
1382019ed09bSJérôme Duval char *data = (char *)malloc(fPlayFormat.u.raw_audio.buffer_size);
1383019ed09bSJérôme Duval
1384c6f8aa29SJérôme Duval fPlayTrack->SeekToTime(&start);
1385c6f8aa29SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame();
1386019ed09bSJérôme Duval while (framesToWrite > 0) {
1387019ed09bSJérôme Duval int64 frames = 0;
1388019ed09bSJérôme Duval status_t err = fPlayTrack->ReadFrames(data, &frames);
1389c6f8aa29SJérôme Duval if (frames <= 0 || err != B_OK) {
1390c6f8aa29SJérôme Duval if (err != B_OK)
1391c6f8aa29SJérôme Duval fprintf(stderr, "CopyTarget: ReadFrames failed\n");
1392019ed09bSJérôme Duval break;
1393c6f8aa29SJérôme Duval }
1394019ed09bSJérôme Duval file.Write(data, frames * frameSize);
1395019ed09bSJérôme Duval framesToWrite -= frames;
1396019ed09bSJérôme Duval }
1397019ed09bSJérôme Duval
1398019ed09bSJérôme Duval file.Sync();
1399019ed09bSJérôme Duval free(data);
1400019ed09bSJérôme Duval BNodeInfo nodeInfo(&file);
1401019ed09bSJérôme Duval // set type
1402019ed09bSJérôme Duval }
1403019ed09bSJérôme Duval } else {
1404019ed09bSJérôme Duval
1405019ed09bSJérôme Duval }
1406019ed09bSJérôme Duval }
1407019ed09bSJérôme Duval }
1408