157e2f323SJérôme Duval /* 257e2f323SJérôme Duval * Copyright 2005, Jérôme Duval. All rights reserved. 357e2f323SJérôme Duval * Distributed under the terms of the MIT License. 457e2f323SJérôme Duval * 557e2f323SJérôme Duval * Inspired by SoundCapture from Be newsletter (Media Kit Basics: Consumers and Producers) 657e2f323SJérôme Duval */ 757e2f323SJérôme Duval 857e2f323SJérôme Duval #include <Application.h> 957e2f323SJérôme Duval #include <Alert.h> 1057e2f323SJérôme Duval #include <Debug.h> 1157e2f323SJérôme Duval #include <Screen.h> 1257e2f323SJérôme Duval #include <Button.h> 1357e2f323SJérôme Duval #include <CheckBox.h> 1457e2f323SJérôme Duval #include <TextControl.h> 1557e2f323SJérôme Duval #include <MenuField.h> 1657e2f323SJérôme Duval #include <PopUpMenu.h> 1757e2f323SJérôme Duval #include <MenuItem.h> 1857e2f323SJérôme Duval #include <Box.h> 1957e2f323SJérôme Duval #include <ScrollView.h> 2057e2f323SJérôme Duval #include <Beep.h> 2157e2f323SJérôme Duval #include <StringView.h> 2257e2f323SJérôme Duval #include <String.h> 2357e2f323SJérôme Duval #include <Slider.h> 2457e2f323SJérôme Duval #include <Message.h> 2557e2f323SJérôme Duval 2657e2f323SJérôme Duval #include <Path.h> 2757e2f323SJérôme Duval #include <FindDirectory.h> 2857e2f323SJérôme Duval #include <MediaAddOn.h> 2957e2f323SJérôme Duval 3057e2f323SJérôme Duval #include <SoundPlayer.h> 3157e2f323SJérôme Duval 3257e2f323SJérôme Duval #include <assert.h> 3357e2f323SJérôme Duval #include <stdio.h> 3457e2f323SJérôme Duval #include <string.h> 3557e2f323SJérôme Duval #include <stdlib.h> 3657e2f323SJérôme Duval #include <ctype.h> 3757e2f323SJérôme Duval #include <unistd.h> 3857e2f323SJérôme Duval #include <sys/fcntl.h> 3957e2f323SJérôme Duval 4057e2f323SJérôme Duval #include <MediaRoster.h> 4157e2f323SJérôme Duval #include <TimeSource.h> 4257e2f323SJérôme Duval 4357e2f323SJérôme Duval #include "RecorderWindow.h" 4457e2f323SJérôme Duval #include "SoundConsumer.h" 4557e2f323SJérôme Duval #include "SoundListView.h" 4657e2f323SJérôme Duval #include "array_delete.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 5957e2f323SJérôme Duval // default window positioning 6057e2f323SJérôme Duval static const float MIN_WIDTH = 400.0f; 6157e2f323SJérôme Duval static const float MIN_HEIGHT = 336.0f; 6257e2f323SJérôme Duval static const float XPOS = 100.0f; 6357e2f323SJérôme Duval static const float YPOS = 200.0f; 6457e2f323SJérôme Duval 6557e2f323SJérôme Duval #define FOURCC(a,b,c,d) ((((uint32)(d)) << 24) | (((uint32)(c)) << 16) | (((uint32)(b)) << 8) | ((uint32)(a))) 6657e2f323SJérôme Duval 6757e2f323SJérôme Duval struct riff_struct 6857e2f323SJérôme Duval { 6957e2f323SJérôme Duval uint32 riff_id; // 'RIFF' 7057e2f323SJérôme Duval uint32 len; 7157e2f323SJérôme Duval uint32 wave_id; // 'WAVE' 7257e2f323SJérôme Duval }; 7357e2f323SJérôme Duval 7457e2f323SJérôme Duval struct chunk_struct 7557e2f323SJérôme Duval { 7657e2f323SJérôme Duval uint32 fourcc; 7757e2f323SJérôme Duval uint32 len; 7857e2f323SJérôme Duval }; 7957e2f323SJérôme Duval 8057e2f323SJérôme Duval struct format_struct 8157e2f323SJérôme Duval { 8257e2f323SJérôme Duval uint16 format_tag; 8357e2f323SJérôme Duval uint16 channels; 8457e2f323SJérôme Duval uint32 samples_per_sec; 8557e2f323SJérôme Duval uint32 avg_bytes_per_sec; 8657e2f323SJérôme Duval uint16 block_align; 8757e2f323SJérôme Duval uint16 bits_per_sample; 8857e2f323SJérôme Duval }; 8957e2f323SJérôme Duval 9057e2f323SJérôme Duval 9157e2f323SJérôme Duval struct wave_struct 9257e2f323SJérôme Duval { 9357e2f323SJérôme Duval struct riff_struct riff; 9457e2f323SJérôme Duval struct chunk_struct format_chunk; 9557e2f323SJérôme Duval struct format_struct format; 9657e2f323SJérôme Duval struct chunk_struct data_chunk; 9757e2f323SJérôme Duval }; 9857e2f323SJérôme Duval 9957e2f323SJérôme Duval 10057e2f323SJérôme Duval RecorderWindow::RecorderWindow() : 10157e2f323SJérôme Duval BWindow(BRect(XPOS,YPOS,XPOS+MIN_WIDTH,YPOS+MIN_HEIGHT), "Sound Recorder", B_TITLED_WINDOW, 10257e2f323SJérôme Duval B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE), 10357e2f323SJérôme Duval fPlayer(NULL), 104*5fa77532SJérôme Duval fSoundList(NULL), 10557e2f323SJérôme Duval fPlayFile(NULL), 10657e2f323SJérôme Duval fPlayTrack(NULL), 107*5fa77532SJérôme Duval fSavePanel(NULL), 108*5fa77532SJérôme Duval fInitCheck(B_OK) 10957e2f323SJérôme Duval { 11057e2f323SJérôme Duval fRoster = NULL; 11157e2f323SJérôme Duval fRecordButton = NULL; 11257e2f323SJérôme Duval fPlayButton = NULL; 11357e2f323SJérôme Duval fStopButton = NULL; 11457e2f323SJérôme Duval fSaveButton = NULL; 11557e2f323SJérôme Duval fLengthControl = NULL; 11657e2f323SJérôme Duval fInputField = NULL; 11757e2f323SJérôme Duval fRecordNode = 0; 11857e2f323SJérôme Duval fRecording = false; 11957e2f323SJérôme Duval fTempCount = -1; 12057e2f323SJérôme Duval fButtonState = btnPaused; 12157e2f323SJérôme Duval 12257e2f323SJérôme Duval CalcSizes(MIN_WIDTH, MIN_HEIGHT); 12357e2f323SJérôme Duval 124*5fa77532SJérôme Duval fInitCheck = InitWindow(); 125*5fa77532SJérôme Duval if (fInitCheck != B_OK) { 126*5fa77532SJérôme Duval ErrorAlert("connect to media server", fInitCheck); 127*5fa77532SJérôme Duval PostMessage(B_QUIT_REQUESTED); 128*5fa77532SJérôme Duval } else 129*5fa77532SJérôme Duval Show(); 13057e2f323SJérôme Duval } 13157e2f323SJérôme Duval 13257e2f323SJérôme Duval RecorderWindow::~RecorderWindow() 13357e2f323SJérôme Duval { 13457e2f323SJérôme Duval // The sound consumer and producer are Nodes; it has to be Release()d and the Roster 13557e2f323SJérôme Duval // will reap it when it's done. 13657e2f323SJérôme Duval if (fRecordNode) { 13757e2f323SJérôme Duval fRecordNode->Release(); 13857e2f323SJérôme Duval } 13957e2f323SJérôme Duval if (fPlayer) { 14057e2f323SJérôme Duval delete fPlayer; 14157e2f323SJérôme Duval } 14257e2f323SJérôme Duval // Clean up items in list view. 14357e2f323SJérôme Duval if (fSoundList) { 14457e2f323SJérôme Duval fSoundList->DeselectAll(); 14557e2f323SJérôme Duval for (int ix=0; ix<fSoundList->CountItems(); ix++) { 14657e2f323SJérôme Duval WINDOW((stderr, "clean up item %d\n", ix+1)); 14757e2f323SJérôme Duval SoundListItem * item = dynamic_cast<SoundListItem *>(fSoundList->ItemAt(ix)); 14857e2f323SJérôme Duval if (item) { 14957e2f323SJérôme Duval if (item->IsTemp()) { 15057e2f323SJérôme Duval item->Entry().Remove(); // delete temp file 15157e2f323SJérôme Duval } 15257e2f323SJérôme Duval delete item; 15357e2f323SJérôme Duval } 15457e2f323SJérôme Duval } 15557e2f323SJérôme Duval fSoundList->MakeEmpty(); 15657e2f323SJérôme Duval } 15757e2f323SJérôme Duval // Clean up currently recording file, if any. 15857e2f323SJérôme Duval fRecEntry.Remove(); 15957e2f323SJérôme Duval fRecEntry.Unset(); 16057e2f323SJérôme Duval } 16157e2f323SJérôme Duval 16257e2f323SJérôme Duval 163*5fa77532SJérôme Duval status_t 164*5fa77532SJérôme Duval RecorderWindow::InitCheck() 165*5fa77532SJérôme Duval { 166*5fa77532SJérôme Duval return fInitCheck; 167*5fa77532SJérôme Duval } 168*5fa77532SJérôme Duval 169*5fa77532SJérôme Duval 17057e2f323SJérôme Duval void 17157e2f323SJérôme Duval RecorderWindow::CalcSizes(float min_wid, float min_hei) 17257e2f323SJérôme Duval { 17357e2f323SJérôme Duval // Set up size limits based on new screen size 17457e2f323SJérôme Duval BScreen screen; 17557e2f323SJérôme Duval BRect r = screen.Frame(); 17657e2f323SJérôme Duval float wid = r.Width()-12; 17757e2f323SJérôme Duval SetSizeLimits(min_wid, wid, min_hei, r.Height()-24); 17857e2f323SJérôme Duval 17957e2f323SJérôme Duval // Don't zoom to cover all of screen; user can resize last bit if necessary. 18057e2f323SJérôme Duval // This leaves other windows visible. 18157e2f323SJérôme Duval if (wid > 640) { 18257e2f323SJérôme Duval wid = 640 + (wid-640)/2; 18357e2f323SJérôme Duval } 18457e2f323SJérôme Duval SetZoomLimits(wid, r.Height()-24); 18557e2f323SJérôme Duval } 18657e2f323SJérôme Duval 18757e2f323SJérôme Duval 18857e2f323SJérôme Duval status_t 18957e2f323SJérôme Duval RecorderWindow::InitWindow() 19057e2f323SJérôme Duval { 19157e2f323SJérôme Duval BPopUpMenu * popup = 0; 19257e2f323SJérôme Duval status_t error; 19357e2f323SJérôme Duval 19457e2f323SJérôme Duval try { 19557e2f323SJérôme Duval // Find temp directory for recorded sounds. 19657e2f323SJérôme Duval BPath path; 19757e2f323SJérôme Duval if (!(error = find_directory(B_COMMON_TEMP_DIRECTORY, &path))) { 19857e2f323SJérôme Duval error = fTempDir.SetTo(path.Path()); 19957e2f323SJérôme Duval } 20057e2f323SJérôme Duval if (error < 0) { 20157e2f323SJérôme Duval goto bad_mojo; 20257e2f323SJérôme Duval } 20357e2f323SJérôme Duval 20457e2f323SJérôme Duval // Make sure the media roster is there (which means the server is there). 20557e2f323SJérôme Duval fRoster = BMediaRoster::Roster(&error); 20657e2f323SJérôme Duval if (!fRoster) { 20757e2f323SJérôme Duval goto bad_mojo; 20857e2f323SJérôme Duval } 20957e2f323SJérôme Duval 21057e2f323SJérôme Duval error = fRoster->GetAudioInput(&fAudioInputNode); 21157e2f323SJérôme Duval if (error < B_OK) { // there's no input? 21257e2f323SJérôme Duval goto bad_mojo; 21357e2f323SJérôme Duval } 21457e2f323SJérôme Duval 21557e2f323SJérôme Duval error = fRoster->GetAudioMixer(&fAudioMixerNode); 21657e2f323SJérôme Duval if (error < B_OK) { // there's no mixer? 21757e2f323SJérôme Duval goto bad_mojo; 21857e2f323SJérôme Duval } 21957e2f323SJérôme Duval 22057e2f323SJérôme Duval // Create our internal Node which records sound, and register it. 22157e2f323SJérôme Duval fRecordNode = new SoundConsumer("Sound Recorder"); 22257e2f323SJérôme Duval error = fRoster->RegisterNode(fRecordNode); 22357e2f323SJérôme Duval if (error < B_OK) { 22457e2f323SJérôme Duval goto bad_mojo; 22557e2f323SJérôme Duval } 22657e2f323SJérôme Duval 22757e2f323SJérôme Duval // Create the window header with controls 22857e2f323SJérôme Duval BRect r(Bounds()); 22957e2f323SJérôme Duval r.bottom = r.top + 175; 23057e2f323SJérôme Duval BBox *background = new BBox(r, "_background", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, 23157e2f323SJérôme Duval B_WILL_DRAW|B_FRAME_EVENTS|B_NAVIGABLE_JUMP, B_PLAIN_BORDER); 23257e2f323SJérôme Duval AddChild(background); 23357e2f323SJérôme Duval 23457e2f323SJérôme Duval 23557e2f323SJérôme Duval 23657e2f323SJérôme Duval r = background->Bounds(); 23757e2f323SJérôme Duval r.left = 2; 23857e2f323SJérôme Duval r.right = r.left + 39; 23957e2f323SJérôme Duval r.bottom = r.top + 104; 24057e2f323SJérôme Duval fVUView = new VUView(r, B_FOLLOW_LEFT|B_FOLLOW_TOP); 24157e2f323SJérôme Duval background->AddChild(fVUView); 24257e2f323SJérôme Duval 24357e2f323SJérôme Duval r = background->Bounds(); 24457e2f323SJérôme Duval r.left = r.left + 40; 24557e2f323SJérôme Duval r.bottom = r.top + 104; 24657e2f323SJérôme Duval fScopeView = new ScopeView(r, B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP); 24757e2f323SJérôme Duval background->AddChild(fScopeView); 24857e2f323SJérôme Duval 24957e2f323SJérôme Duval r = background->Bounds(); 25057e2f323SJérôme Duval r.left = 2; 25157e2f323SJérôme Duval r.right -= 26; 25257e2f323SJérôme Duval r.top = 115; 25357e2f323SJérôme Duval r.bottom = r.top + 30; 25457e2f323SJérôme Duval fTrackSlider = new TrackSlider(r, "trackSlider", new BMessage(POSITION_CHANGED), B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP); 25557e2f323SJérôme Duval background->AddChild(fTrackSlider); 25657e2f323SJérôme Duval 25757e2f323SJérôme Duval BRect buttonRect; 25857e2f323SJérôme Duval 25957e2f323SJérôme Duval // Button for rewinding 26057e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSkipButtonSize); 26157e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-7, 25)); 26257e2f323SJérôme Duval fRewindButton = new TransportButton(buttonRect, "Rewind", 26357e2f323SJérôme Duval kSkipBackBitmapBits, kPressedSkipBackBitmapBits, kDisabledSkipBackBitmapBits, 26457e2f323SJérôme Duval new BMessage(REWIND)); 26557e2f323SJérôme Duval background->AddChild(fRewindButton); 26657e2f323SJérôme Duval 26757e2f323SJérôme Duval // Button for stopping recording or playback 26857e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kStopButtonSize); 26957e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-48, 25)); 27057e2f323SJérôme Duval fStopButton = new TransportButton(buttonRect, "Stop", 27157e2f323SJérôme Duval kStopButtonBitmapBits, kPressedStopButtonBitmapBits, kDisabledStopButtonBitmapBits, 27257e2f323SJérôme Duval new BMessage(STOP)); 27357e2f323SJérôme Duval background->AddChild(fStopButton); 27457e2f323SJérôme Duval 27557e2f323SJérôme Duval // Button for starting playback of selected sound 27657e2f323SJérôme Duval BRect playRect(BPoint(0,0), kPlayButtonSize); 27757e2f323SJérôme Duval playRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-82, 25)); 27857e2f323SJérôme Duval fPlayButton = new PlayPauseButton(playRect, "Play", 27957e2f323SJérôme Duval new BMessage(PLAY), new BMessage(PLAY_PERIOD), ' ', 0); 28057e2f323SJérôme Duval background->AddChild(fPlayButton); 28157e2f323SJérôme Duval 28257e2f323SJérôme Duval // Button for forwarding 28357e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSkipButtonSize); 28457e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-133, 25)); 28557e2f323SJérôme Duval fForwardButton = new TransportButton(buttonRect, "Forward", 28657e2f323SJérôme Duval kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits, kDisabledSkipForwardBitmapBits, 28757e2f323SJérôme Duval new BMessage(FORWARD)); 28857e2f323SJérôme Duval background->AddChild(fForwardButton); 28957e2f323SJérôme Duval 29057e2f323SJérôme Duval // Button to start recording (or waiting for sound) 29157e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kRecordButtonSize); 29257e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-174, 25)); 29357e2f323SJérôme Duval fRecordButton = new RecordButton(buttonRect, "Record", 29457e2f323SJérôme Duval new BMessage(RECORD), new BMessage(RECORD_PERIOD)); 29557e2f323SJérôme Duval background->AddChild(fRecordButton); 29657e2f323SJérôme Duval 29757e2f323SJérôme Duval // Button for saving selected sound 29857e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kDiskButtonSize); 29957e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-250, 21)); 30057e2f323SJérôme Duval fSaveButton = new TransportButton(buttonRect, "Save", 30157e2f323SJérôme Duval kDiskButtonBitmapsBits, kPressedDiskButtonBitmapsBits, kDisabledDiskButtonBitmapsBits, new BMessage(SAVE)); 30257e2f323SJérôme Duval fSaveButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 30357e2f323SJérôme Duval background->AddChild(fSaveButton); 30457e2f323SJérôme Duval 30557e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSpeakerIconBitmapSize); 30657e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(121, 17)); 30757e2f323SJérôme Duval SpeakerView *speakerView = new SpeakerView(buttonRect, B_FOLLOW_LEFT | B_FOLLOW_TOP); 30857e2f323SJérôme Duval speakerView->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 30957e2f323SJérôme Duval background->AddChild(speakerView); 31057e2f323SJérôme Duval 31157e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), BPoint(84, 19)); 31257e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(107, 20)); 31357e2f323SJérôme Duval fVolumeSlider = new VolumeSlider(buttonRect, "volumeSlider", B_FOLLOW_LEFT | B_FOLLOW_TOP); 31457e2f323SJérôme Duval fVolumeSlider->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 31557e2f323SJérôme Duval background->AddChild(fVolumeSlider); 31657e2f323SJérôme Duval 31757e2f323SJérôme Duval // Button to mask/see sounds list 31857e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kUpDownButtonSize); 31957e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(21, 25)); 32057e2f323SJérôme Duval fUpDownButton = new UpDownButton(buttonRect, new BMessage(VIEW_LIST)); 32157e2f323SJérôme Duval fUpDownButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 32257e2f323SJérôme Duval background->AddChild(fUpDownButton); 32357e2f323SJérôme Duval 32457e2f323SJérôme Duval r = Bounds(); 32557e2f323SJérôme Duval r.top = background->Bounds().bottom + 1; 32657e2f323SJérôme Duval fBottomBox = new BBox(r, "bottomBox", B_FOLLOW_ALL); 32757e2f323SJérôme Duval fBottomBox->SetBorder(B_NO_BORDER); 32857e2f323SJérôme Duval AddChild(fBottomBox); 32957e2f323SJérôme Duval 33057e2f323SJérôme Duval // The actual list of recorded sounds (initially empty) sits 33157e2f323SJérôme Duval // below the header with the controls. 33257e2f323SJérôme Duval r = fBottomBox->Bounds(); 33357e2f323SJérôme Duval r.left += 190; 33457e2f323SJérôme Duval r.InsetBy(10, 10); 33557e2f323SJérôme Duval r.left -= 10; 33657e2f323SJérôme Duval r.top += 4; 33757e2f323SJérôme Duval r.right -= B_V_SCROLL_BAR_WIDTH; 33857e2f323SJérôme Duval r.bottom -= 25; 33957e2f323SJérôme Duval fSoundList = new SoundListView(r, "Sound List", B_FOLLOW_ALL); 34057e2f323SJérôme Duval fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED)); 34157e2f323SJérôme Duval fSoundList->SetViewColor(216, 216, 216); 34257e2f323SJérôme Duval BScrollView *scroller = new BScrollView("scroller", fSoundList, B_FOLLOW_ALL, 34357e2f323SJérôme Duval 0, false, true, B_FANCY_BORDER); 34457e2f323SJérôme Duval fBottomBox->AddChild(scroller); 34557e2f323SJérôme Duval 34657e2f323SJérôme Duval r = fBottomBox->Bounds(); 34757e2f323SJérôme Duval r.right = r.left + 190; 34857e2f323SJérôme Duval r.bottom -= 25; 34957e2f323SJérôme Duval r.InsetBy(10, 8); 35057e2f323SJérôme Duval r.top -= 1; 35157e2f323SJérôme Duval fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); 35257e2f323SJérôme Duval fFileInfoBox->SetLabel("File Info"); 35357e2f323SJérôme Duval 35457e2f323SJérôme Duval r = fFileInfoBox->Bounds(); 35557e2f323SJérôme Duval r.left = 8; 35657e2f323SJérôme Duval r.top = 13; 35757e2f323SJérôme Duval r.bottom = r.top + 15; 35857e2f323SJérôme Duval r.right -= 10; 35957e2f323SJérôme Duval fFilename = new BStringView(r, "filename", "File Name:"); 36057e2f323SJérôme Duval fFileInfoBox->AddChild(fFilename); 36157e2f323SJérôme Duval r.top += 13; 36257e2f323SJérôme Duval r.bottom = r.top + 15; 36357e2f323SJérôme Duval fFormat = new BStringView(r, "format", "Format:"); 36457e2f323SJérôme Duval fFileInfoBox->AddChild(fFormat); 36557e2f323SJérôme Duval r.top += 13; 36657e2f323SJérôme Duval r.bottom = r.top + 15; 36757e2f323SJérôme Duval fCompression = new BStringView(r, "compression", "Compression:"); 36857e2f323SJérôme Duval fFileInfoBox->AddChild(fCompression); 36957e2f323SJérôme Duval r.top += 13; 37057e2f323SJérôme Duval r.bottom = r.top + 15; 37157e2f323SJérôme Duval fChannels = new BStringView(r, "channels", "Channels:"); 37257e2f323SJérôme Duval fFileInfoBox->AddChild(fChannels); 37357e2f323SJérôme Duval r.top += 13; 37457e2f323SJérôme Duval r.bottom = r.top + 15; 37557e2f323SJérôme Duval fSampleSize = new BStringView(r, "samplesize", "Sample Size:"); 37657e2f323SJérôme Duval fFileInfoBox->AddChild(fSampleSize); 37757e2f323SJérôme Duval r.top += 13; 37857e2f323SJérôme Duval r.bottom = r.top + 15; 37957e2f323SJérôme Duval fSampleRate = new BStringView(r, "samplerate", "Sample Rate:"); 38057e2f323SJérôme Duval fFileInfoBox->AddChild(fSampleRate); 38157e2f323SJérôme Duval r.top += 13; 38257e2f323SJérôme Duval r.bottom = r.top + 15; 38357e2f323SJérôme Duval fDuration = new BStringView(r, "duration", "Duration:"); 38457e2f323SJérôme Duval fFileInfoBox->AddChild(fDuration); 38557e2f323SJérôme Duval 38657e2f323SJérôme Duval // Input selection lists all available physical inputs that produce 38757e2f323SJérôme Duval // buffers with B_MEDIA_RAW_AUDIO format data. 38857e2f323SJérôme Duval popup = new BPopUpMenu("Input"); 38957e2f323SJérôme Duval int max_input_count = 64; 39057e2f323SJérôme Duval dormant_node_info dni[max_input_count]; 39157e2f323SJérôme Duval 39257e2f323SJérôme Duval int32 real_count = max_input_count; 39357e2f323SJérôme Duval media_format output_format; 39457e2f323SJérôme Duval output_format.type = B_MEDIA_RAW_AUDIO; 39557e2f323SJérôme Duval output_format.u.raw_audio = media_raw_audio_format::wildcard; 39657e2f323SJérôme Duval error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format, 39757e2f323SJérôme Duval 0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); 39857e2f323SJérôme Duval if (real_count > max_input_count) { 39957e2f323SJérôme Duval WINDOW((stderr, "dropped %ld inputs\n", real_count - max_input_count)); 40057e2f323SJérôme Duval real_count = max_input_count; 40157e2f323SJérôme Duval } 40257e2f323SJérôme Duval char selected_name[B_MEDIA_NAME_LENGTH] = "Default Input"; 40357e2f323SJérôme Duval BMessage * msg; 40457e2f323SJérôme Duval BMenuItem * item; 40557e2f323SJérôme Duval for (int ix=0; ix<real_count; ix++) { 40657e2f323SJérôme Duval msg = new BMessage(INPUT_SELECTED); 40757e2f323SJérôme Duval msg->AddData("node", B_RAW_TYPE, &dni[ix], sizeof(dni[ix])); 40857e2f323SJérôme Duval item = new BMenuItem(dni[ix].name, msg); 40957e2f323SJérôme Duval popup->AddItem(item); 41057e2f323SJérôme Duval media_node_id ni[12]; 41157e2f323SJérôme Duval int32 ni_count = 12; 41257e2f323SJérôme Duval error = fRoster->GetInstancesFor(dni[ix].addon, dni[ix].flavor_id, ni, &ni_count); 41357e2f323SJérôme Duval if (error == B_OK) 41457e2f323SJérôme Duval for (int iy=0; iy<ni_count; iy++) 41557e2f323SJérôme Duval if (ni[iy] == fAudioInputNode.node) { 41657e2f323SJérôme Duval strcpy(selected_name, dni[ix].name); 41757e2f323SJérôme Duval break; 41857e2f323SJérôme Duval } 41957e2f323SJérôme Duval } 42057e2f323SJérôme Duval 42157e2f323SJérôme Duval // Create the actual widget 42257e2f323SJérôme Duval BRect frame(fBottomBox->Bounds()); 42357e2f323SJérôme Duval r = frame; 42457e2f323SJérôme Duval r.left = 42; 42557e2f323SJérôme Duval r.right = (r.left + r.right) / 2; 42657e2f323SJérôme Duval r.InsetBy(10,10); 42757e2f323SJérôme Duval r.top = r.bottom - 18; 42857e2f323SJérôme Duval fInputField = new BMenuField(r, "Input", "Input:", popup); 42957e2f323SJérôme Duval fInputField->SetDivider(50); 43057e2f323SJérôme Duval fBottomBox->AddChild(fInputField); 43157e2f323SJérôme Duval 43257e2f323SJérôme Duval // Text field for entering length to record (in seconds) 43357e2f323SJérôme Duval r.OffsetBy(r.Width()+20, 1); 43457e2f323SJérôme Duval r.right -= 70; 43557e2f323SJérôme Duval msg = new BMessage(LENGTH_CHANGED); 43657e2f323SJérôme Duval fLengthControl = new BTextControl(r, "Length", "Length:", "8", msg); 43757e2f323SJérôme Duval fLengthControl->SetDivider(60); 43857e2f323SJérôme Duval fLengthControl->SetAlignment(B_ALIGN_CENTER, B_ALIGN_RIGHT); 43957e2f323SJérôme Duval fBottomBox->AddChild(fLengthControl); 44057e2f323SJérôme Duval 44157e2f323SJérôme Duval r.left += r.Width()+5; 44257e2f323SJérôme Duval r.right = r.left + 65; 44357e2f323SJérôme Duval r.bottom -= 1; 44457e2f323SJérôme Duval BStringView* lenUnits = new BStringView(r, "Seconds", "seconds"); 44557e2f323SJérôme Duval fBottomBox->AddChild(lenUnits); 44657e2f323SJérôme Duval 44757e2f323SJérôme Duval fBottomBox->AddChild(fFileInfoBox); 44857e2f323SJérôme Duval 44957e2f323SJérôme Duval fBottomBox->Hide(); 45057e2f323SJérôme Duval CalcSizes(Frame().Width(), MIN_HEIGHT-161); 45157e2f323SJérôme Duval ResizeTo(Frame().Width(), MIN_HEIGHT-161); 45257e2f323SJérôme Duval 45357e2f323SJérôme Duval 45457e2f323SJérôme Duval popup->Superitem()->SetLabel(selected_name); 45557e2f323SJérôme Duval 45657e2f323SJérôme Duval // Make sure the save panel is happy. 457*5fa77532SJérôme Duval fSavePanel = new BFilePanel(B_SAVE_PANEL); 458*5fa77532SJérôme Duval fSavePanel->SetTarget(this); 45957e2f323SJérôme Duval } 46057e2f323SJérôme Duval catch (...) { 46157e2f323SJérôme Duval goto bad_mojo; 46257e2f323SJérôme Duval } 46357e2f323SJérôme Duval UpdateButtons(); 46457e2f323SJérôme Duval return B_OK; 46557e2f323SJérôme Duval 46657e2f323SJérôme Duval // Error handling. 46757e2f323SJérôme Duval bad_mojo: 46857e2f323SJérôme Duval if (error >= 0) { 46957e2f323SJérôme Duval error = B_ERROR; 47057e2f323SJérôme Duval } 47157e2f323SJérôme Duval if (fRecordNode) { 47257e2f323SJérôme Duval fRecordNode->Release(); 47357e2f323SJérôme Duval } 47457e2f323SJérôme Duval 47557e2f323SJérôme Duval delete fPlayer; 47657e2f323SJérôme Duval if (!fInputField) { 47757e2f323SJérôme Duval delete popup; 47857e2f323SJérôme Duval } 47957e2f323SJérôme Duval return error; 48057e2f323SJérôme Duval } 48157e2f323SJérôme Duval 48257e2f323SJérôme Duval 48357e2f323SJérôme Duval bool 48457e2f323SJérôme Duval RecorderWindow::QuitRequested() // this means Close pressed 48557e2f323SJérôme Duval { 48657e2f323SJérôme Duval StopRecording(); 48757e2f323SJérôme Duval StopPlaying(); 48857e2f323SJérôme Duval be_app->PostMessage(B_QUIT_REQUESTED); 48957e2f323SJérôme Duval return true; 49057e2f323SJérôme Duval } 49157e2f323SJérôme Duval 49257e2f323SJérôme Duval 49357e2f323SJérôme Duval void 49457e2f323SJérôme Duval RecorderWindow::MessageReceived(BMessage * message) 49557e2f323SJérôme Duval { 49657e2f323SJérôme Duval // Your average generic message dispatching switch() statement. 49757e2f323SJérôme Duval switch (message->what) { 49857e2f323SJérôme Duval case INPUT_SELECTED: 49957e2f323SJérôme Duval Input(message); 50057e2f323SJérôme Duval break; 50157e2f323SJérôme Duval case LENGTH_CHANGED: 50257e2f323SJérôme Duval Length(message); 50357e2f323SJérôme Duval break; 50457e2f323SJérôme Duval case SOUND_SELECTED: 50557e2f323SJérôme Duval Selected(message); 50657e2f323SJérôme Duval break; 50757e2f323SJérôme Duval case STOP_PLAYING: 50857e2f323SJérôme Duval StopPlaying(); 50957e2f323SJérôme Duval break; 51057e2f323SJérôme Duval case STOP_RECORDING: 51157e2f323SJérôme Duval StopRecording(); 51257e2f323SJérôme Duval break; 51357e2f323SJérôme Duval case PLAY_PERIOD: 51457e2f323SJérôme Duval if (fPlayer) { 51557e2f323SJérôme Duval if (fPlayer->HasData()) 51657e2f323SJérôme Duval fPlayButton->SetPlaying(); 51757e2f323SJérôme Duval else 51857e2f323SJérôme Duval fPlayButton->SetPaused(); 51957e2f323SJérôme Duval } 52057e2f323SJérôme Duval break; 52157e2f323SJérôme Duval case RECORD_PERIOD: 52257e2f323SJérôme Duval fRecordButton->SetRecording(); 52357e2f323SJérôme Duval break; 52457e2f323SJérôme Duval case RECORD: 52557e2f323SJérôme Duval Record(message); 52657e2f323SJérôme Duval break; 52757e2f323SJérôme Duval case STOP: 52857e2f323SJérôme Duval Stop(message); 52957e2f323SJérôme Duval break; 53057e2f323SJérôme Duval case PLAY: 53157e2f323SJérôme Duval Play(message); 53257e2f323SJérôme Duval break; 53357e2f323SJérôme Duval case SAVE: 53457e2f323SJérôme Duval Save(message); 53557e2f323SJérôme Duval break; 53657e2f323SJérôme Duval case B_SAVE_REQUESTED: 53757e2f323SJérôme Duval DoSave(message); 53857e2f323SJérôme Duval break; 53957e2f323SJérôme Duval case VIEW_LIST: 54057e2f323SJérôme Duval if (fUpDownButton->Value() == B_CONTROL_ON) { 54157e2f323SJérôme Duval fBottomBox->Show(); 54257e2f323SJérôme Duval CalcSizes(Frame().Width(), MIN_HEIGHT); 54357e2f323SJérôme Duval ResizeTo(Frame().Width(), MIN_HEIGHT); 54457e2f323SJérôme Duval } else { 54557e2f323SJérôme Duval fBottomBox->Hide(); 54657e2f323SJérôme Duval CalcSizes(Frame().Width(), MIN_HEIGHT-161); 54757e2f323SJérôme Duval ResizeTo(Frame().Width(), MIN_HEIGHT-161); 54857e2f323SJérôme Duval 54957e2f323SJérôme Duval } 55057e2f323SJérôme Duval break; 55157e2f323SJérôme Duval case UPDATE_TRACKSLIDER: 55257e2f323SJérôme Duval { 55357e2f323SJérôme Duval bigtime_t timestamp = fPlayTrack->CurrentTime(); 55457e2f323SJérôme Duval fTrackSlider->SetMainTime(timestamp, false); 55557e2f323SJérôme Duval fScopeView->SetMainTime(timestamp); 55657e2f323SJérôme Duval } 55757e2f323SJérôme Duval break; 55857e2f323SJérôme Duval case POSITION_CHANGED: 55957e2f323SJérôme Duval { 56057e2f323SJérôme Duval bigtime_t right, left, main; 56157e2f323SJérôme Duval if (message->FindInt64("main", &main) == B_OK) { 56257e2f323SJérôme Duval if (fPlayTrack) { 56357e2f323SJérôme Duval fPlayTrack->SeekToTime(fTrackSlider->MainTime()); 56457e2f323SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 56557e2f323SJérôme Duval } 56657e2f323SJérôme Duval fScopeView->SetMainTime(main); 56757e2f323SJérôme Duval } 56857e2f323SJérôme Duval if (message->FindInt64("right", &right) == B_OK) { 56957e2f323SJérôme Duval if (fPlayTrack) 57057e2f323SJérôme Duval fPlayLimit = MIN(fPlayTrack->CountFrames(), (off_t)(right * fPlayFormat.u.raw_audio.frame_rate/1000000LL)); 57157e2f323SJérôme Duval fScopeView->SetRightTime(right); 57257e2f323SJérôme Duval } 57357e2f323SJérôme Duval if (message->FindInt64("left", &left) == B_OK) { 57457e2f323SJérôme Duval fScopeView->SetLeftTime(left); 57557e2f323SJérôme Duval } 57657e2f323SJérôme Duval } 57757e2f323SJérôme Duval break; 57857e2f323SJérôme Duval case B_SIMPLE_DATA: 57957e2f323SJérôme Duval case B_REFS_RECEIVED: 58057e2f323SJérôme Duval { 58157e2f323SJérôme Duval RefsReceived(message); 58257e2f323SJérôme Duval break; 58357e2f323SJérôme Duval } 58457e2f323SJérôme Duval default: 58557e2f323SJérôme Duval BWindow::MessageReceived(message); 58657e2f323SJérôme Duval break; 58757e2f323SJérôme Duval } 58857e2f323SJérôme Duval } 58957e2f323SJérôme Duval 59057e2f323SJérôme Duval 59157e2f323SJérôme Duval void 59257e2f323SJérôme Duval RecorderWindow::Record(BMessage * message) 59357e2f323SJérôme Duval { 59457e2f323SJérôme Duval // User pressed Record button 59557e2f323SJérôme Duval fRecording = true; 59657e2f323SJérôme Duval int seconds = atoi(fLengthControl->Text()); 59757e2f323SJérôme Duval if (seconds < 1) { 59857e2f323SJérôme Duval ErrorAlert("record a sound that's shorter than a second", B_ERROR); 59957e2f323SJérôme Duval return; 60057e2f323SJérôme Duval } 60157e2f323SJérôme Duval 60257e2f323SJérôme Duval if (fButtonState != btnPaused) { 60357e2f323SJérôme Duval StopRecording(); 60457e2f323SJérôme Duval return; // user is too fast on the mouse 60557e2f323SJérôme Duval } 60657e2f323SJérôme Duval SetButtonState(btnRecording); 60757e2f323SJérôme Duval fRecordButton->SetRecording(); 60857e2f323SJérôme Duval 60957e2f323SJérôme Duval char name[256]; 61057e2f323SJérôme Duval // Create a file with a temporary name 61157e2f323SJérôme Duval status_t err = NewTempName(name); 61257e2f323SJérôme Duval if (err < B_OK) { 61357e2f323SJérôme Duval ErrorAlert("find an unused name to use for the new recording", err); 61457e2f323SJérôme Duval return; 61557e2f323SJérôme Duval } 61657e2f323SJérôme Duval // Find the file so we can refer to it later 61757e2f323SJérôme Duval err = fTempDir.FindEntry(name, &fRecEntry); 61857e2f323SJérôme Duval if (err < B_OK) { 61957e2f323SJérôme Duval ErrorAlert("find the temporary file created to hold the new recording", err); 62057e2f323SJérôme Duval return; 62157e2f323SJérôme Duval } 62257e2f323SJérôme Duval err = fRecFile.SetTo(&fTempDir, name, O_RDWR); 62357e2f323SJérôme Duval if (err < B_OK) { 62457e2f323SJérôme Duval ErrorAlert("open the temporary file created to hold the new recording", err); 62557e2f323SJérôme Duval fRecEntry.Unset(); 62657e2f323SJérôme Duval return; 62757e2f323SJérôme Duval } 62857e2f323SJérôme Duval // Reserve space on disk (creates fewer fragments) 62957e2f323SJérôme Duval err = fRecFile.SetSize(seconds*4*48000LL); 63057e2f323SJérôme Duval if (err < B_OK) { 63157e2f323SJérôme Duval ErrorAlert("record a sound that long", err); 63257e2f323SJérôme Duval fRecEntry.Remove(); 63357e2f323SJérôme Duval fRecEntry.Unset(); 63457e2f323SJérôme Duval return; 63557e2f323SJérôme Duval } 63657e2f323SJérôme Duval fRecLimit = seconds*4*48000LL; 63757e2f323SJérôme Duval fRecSize = 0; 63857e2f323SJérôme Duval 63957e2f323SJérôme Duval fRecFile.Seek(sizeof(struct wave_struct), SEEK_SET); 64057e2f323SJérôme Duval 64157e2f323SJérôme Duval // Hook up input 64257e2f323SJérôme Duval err = MakeRecordConnection(fAudioInputNode); 64357e2f323SJérôme Duval if (err < B_OK) { 64457e2f323SJérôme Duval ErrorAlert("connect to the selected sound input", err); 64557e2f323SJérôme Duval fRecEntry.Remove(); 64657e2f323SJérôme Duval fRecEntry.Unset(); 64757e2f323SJérôme Duval return; 64857e2f323SJérôme Duval } 64957e2f323SJérôme Duval 65057e2f323SJérôme Duval // And get it going... 65157e2f323SJérôme Duval bigtime_t then = fRecordNode->TimeSource()->Now()+50000LL; 65257e2f323SJérôme Duval fRoster->StartNode(fRecordNode->Node(), then); 65357e2f323SJérôme Duval if (fAudioInputNode.kind & B_TIME_SOURCE) { 65457e2f323SJérôme Duval fRoster->StartNode(fAudioInputNode, fRecordNode->TimeSource()->RealTimeFor(then, 0)); 65557e2f323SJérôme Duval } 65657e2f323SJérôme Duval else { 65757e2f323SJérôme Duval fRoster->StartNode(fAudioInputNode, then); 65857e2f323SJérôme Duval } 65957e2f323SJérôme Duval } 66057e2f323SJérôme Duval 66157e2f323SJérôme Duval void 66257e2f323SJérôme Duval RecorderWindow::Play(BMessage * message) 66357e2f323SJérôme Duval { 66457e2f323SJérôme Duval if (fPlayer) { 66557e2f323SJérôme Duval // User pressed Play button and playing 66657e2f323SJérôme Duval if (fPlayer->HasData()) 66757e2f323SJérôme Duval fPlayButton->SetPaused(); 66857e2f323SJérôme Duval else 66957e2f323SJérôme Duval fPlayButton->SetPlaying(); 67057e2f323SJérôme Duval fPlayer->SetHasData(!fPlayer->HasData()); 67157e2f323SJérôme Duval return; 67257e2f323SJérôme Duval } 67357e2f323SJérôme Duval 67457e2f323SJérôme Duval SetButtonState(btnPlaying); 67557e2f323SJérôme Duval fPlayButton->SetPlaying(); 67657e2f323SJérôme Duval 67757e2f323SJérôme Duval if (!fPlayTrack) { 67857e2f323SJérôme Duval ErrorAlert("get the file to play", B_ERROR); 67957e2f323SJérôme Duval return; 68057e2f323SJérôme Duval } 68157e2f323SJérôme Duval 68257e2f323SJérôme Duval fPlayLimit = MIN(fPlayTrack->CountFrames(), (off_t)(fTrackSlider->RightTime()*fPlayFormat.u.raw_audio.frame_rate/1000000LL)); 68357e2f323SJérôme Duval fPlayTrack->SeekToTime(fTrackSlider->MainTime()); 68457e2f323SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 68557e2f323SJérôme Duval 68657e2f323SJérôme Duval // Create our internal Node which plays sound, and register it. 68757e2f323SJérôme Duval fPlayer = new BSoundPlayer(fAudioMixerNode, &fPlayFormat.u.raw_audio, "Sound Player"); 68857e2f323SJérôme Duval status_t err = fPlayer->InitCheck(); 68957e2f323SJérôme Duval if (err < B_OK) { 69057e2f323SJérôme Duval return; 69157e2f323SJérôme Duval } 69257e2f323SJérôme Duval 69357e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(fPlayer); 69457e2f323SJérôme Duval fPlayer->SetCallbacks(PlayFile, NotifyPlayFile, this); 69557e2f323SJérôme Duval 69657e2f323SJérôme Duval // And get it going... 69757e2f323SJérôme Duval fPlayer->Start(); 69857e2f323SJérôme Duval fPlayer->SetHasData(true); 69957e2f323SJérôme Duval } 70057e2f323SJérôme Duval 70157e2f323SJérôme Duval void 70257e2f323SJérôme Duval RecorderWindow::Stop(BMessage * message) 70357e2f323SJérôme Duval { 70457e2f323SJérôme Duval // User pressed Stop button. 70557e2f323SJérôme Duval // Stop recorder. 70657e2f323SJérôme Duval StopRecording(); 70757e2f323SJérôme Duval // Stop player. 70857e2f323SJérôme Duval StopPlaying(); 70957e2f323SJérôme Duval } 71057e2f323SJérôme Duval 71157e2f323SJérôme Duval void 71257e2f323SJérôme Duval RecorderWindow::Save(BMessage * message) 71357e2f323SJérôme Duval { 71457e2f323SJérôme Duval // User pressed Save button. 71557e2f323SJérôme Duval // Find the item to save. 71657e2f323SJérôme Duval int32 index = fSoundList->CurrentSelection(); 71757e2f323SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(index)); 71857e2f323SJérôme Duval if ((! pItem) || (pItem->Entry().InitCheck() != B_OK)) { 71957e2f323SJérôme Duval return; 72057e2f323SJérôme Duval } 72157e2f323SJérôme Duval 72257e2f323SJérôme Duval // Update the save panel and show it. 72357e2f323SJérôme Duval char filename[B_FILE_NAME_LENGTH]; 72457e2f323SJérôme Duval pItem->Entry().GetName(filename); 72557e2f323SJérôme Duval BMessage saveMsg(B_SAVE_REQUESTED); 72657e2f323SJérôme Duval entry_ref ref; 72757e2f323SJérôme Duval pItem->Entry().GetRef(&ref); 72857e2f323SJérôme Duval 72957e2f323SJérôme Duval saveMsg.AddPointer("sound list item", pItem); 730*5fa77532SJérôme Duval fSavePanel->SetSaveText(filename); 731*5fa77532SJérôme Duval fSavePanel->SetMessage(&saveMsg); 732*5fa77532SJérôme Duval fSavePanel->Show(); 73357e2f323SJérôme Duval } 73457e2f323SJérôme Duval 73557e2f323SJérôme Duval void 73657e2f323SJérôme Duval RecorderWindow::DoSave(BMessage * message) 73757e2f323SJérôme Duval { 73857e2f323SJérôme Duval // User picked a place to put the file. 73957e2f323SJérôme Duval // Find the location of the old (e.g. 74057e2f323SJérôme Duval // temporary file), and the name of the 74157e2f323SJérôme Duval // new file to save. 74257e2f323SJérôme Duval entry_ref old_ref, new_dir_ref; 74357e2f323SJérôme Duval const char* new_name; 74457e2f323SJérôme Duval SoundListItem* pItem; 74557e2f323SJérôme Duval 74657e2f323SJérôme Duval if ((message->FindPointer("sound list item", (void**) &pItem) == B_OK) 74757e2f323SJérôme Duval && (message->FindRef("directory", &new_dir_ref) == B_OK) 74857e2f323SJérôme Duval && (message->FindString("name", &new_name) == B_OK)) 74957e2f323SJérôme Duval { 75057e2f323SJérôme Duval BEntry& oldEntry = pItem->Entry(); 75157e2f323SJérôme Duval BFile oldFile(&oldEntry, B_READ_WRITE); 75257e2f323SJérôme Duval if (oldFile.InitCheck() != B_OK) 75357e2f323SJérôme Duval return; 75457e2f323SJérôme Duval 75557e2f323SJérôme Duval BDirectory newDir(&new_dir_ref); 75657e2f323SJérôme Duval if (newDir.InitCheck() != B_OK) 75757e2f323SJérôme Duval return; 75857e2f323SJérôme Duval 75957e2f323SJérôme Duval BFile newFile; 76057e2f323SJérôme Duval newDir.CreateFile(new_name, &newFile); 76157e2f323SJérôme Duval 76257e2f323SJérôme Duval if (newFile.InitCheck() != B_OK) 76357e2f323SJérôme Duval return; 76457e2f323SJérôme Duval 76557e2f323SJérôme Duval status_t err = CopyFile(newFile, oldFile); 76657e2f323SJérôme Duval 76757e2f323SJérôme Duval if (err == B_OK) { 76857e2f323SJérôme Duval // clean up the sound list and item 76957e2f323SJérôme Duval if (pItem->IsTemp()) 77057e2f323SJérôme Duval oldEntry.Remove(); // blows away temp file! 77157e2f323SJérôme Duval oldEntry.SetTo(&newDir, new_name); 77257e2f323SJérôme Duval pItem->SetTemp(false); // don't blow the new entry away when we exit! 77357e2f323SJérôme Duval fSoundList->Invalidate(); 77457e2f323SJérôme Duval } 77557e2f323SJérôme Duval } else { 77657e2f323SJérôme Duval WINDOW((stderr, "Couldn't save file.\n")); 77757e2f323SJérôme Duval } 77857e2f323SJérôme Duval } 77957e2f323SJérôme Duval 78057e2f323SJérôme Duval 78157e2f323SJérôme Duval void 78257e2f323SJérôme Duval RecorderWindow::Length(BMessage * message) 78357e2f323SJérôme Duval { 78457e2f323SJérôme Duval // User changed the Length field -- validate 78557e2f323SJérôme Duval const char * ptr = fLengthControl->Text(); 78657e2f323SJérôme Duval const char * start = ptr; 78757e2f323SJérôme Duval const char * anchor = ptr; 78857e2f323SJérôme Duval const char * end = fLengthControl->Text() + fLengthControl->TextView()->TextLength(); 78957e2f323SJérôme Duval while (ptr < end) { 79057e2f323SJérôme Duval // Remember the last start-of-character for UTF-8 compatibility 79157e2f323SJérôme Duval // needed in call to Select() below (which takes bytes). 79257e2f323SJérôme Duval if (*ptr & 0x80) { 79357e2f323SJérôme Duval if (*ptr & 0xc0 == 0xc0) { 79457e2f323SJérôme Duval anchor = ptr; 79557e2f323SJérôme Duval } 79657e2f323SJérôme Duval } 79757e2f323SJérôme Duval else { 79857e2f323SJérôme Duval anchor = ptr; 79957e2f323SJérôme Duval } 80057e2f323SJérôme Duval if (!isdigit(*ptr)) { 80157e2f323SJérôme Duval fLengthControl->TextView()->MakeFocus(true); 80257e2f323SJérôme Duval fLengthControl->TextView()->Select(anchor-start, fLengthControl->TextView()->TextLength()); 80357e2f323SJérôme Duval beep(); 80457e2f323SJérôme Duval break; 80557e2f323SJérôme Duval } 80657e2f323SJérôme Duval ptr++; 80757e2f323SJérôme Duval } 80857e2f323SJérôme Duval } 80957e2f323SJérôme Duval 81057e2f323SJérôme Duval 81157e2f323SJérôme Duval void 81257e2f323SJérôme Duval RecorderWindow::Input(BMessage * message) 81357e2f323SJérôme Duval { 81457e2f323SJérôme Duval // User selected input from pop-up 81557e2f323SJérôme Duval const dormant_node_info * dni = 0; 81657e2f323SJérôme Duval ssize_t size = 0; 81757e2f323SJérôme Duval if (message->FindData("node", B_RAW_TYPE, (const void **)&dni, &size)) { 81857e2f323SJérôme Duval return; // bad input selection message 81957e2f323SJérôme Duval } 82057e2f323SJérôme Duval 82157e2f323SJérôme Duval media_node_id node_id; 82257e2f323SJérôme Duval status_t error = fRoster->GetInstancesFor(dni->addon, dni->flavor_id, &node_id); 82357e2f323SJérôme Duval if (error != B_OK) { 82457e2f323SJérôme Duval fRoster->InstantiateDormantNode(*dni, &fAudioInputNode); 82557e2f323SJérôme Duval } else { 82657e2f323SJérôme Duval fRoster->GetNodeFor(node_id, &fAudioInputNode); 82757e2f323SJérôme Duval } 82857e2f323SJérôme Duval } 82957e2f323SJérôme Duval 83057e2f323SJérôme Duval void 83157e2f323SJérôme Duval RecorderWindow::Selected(BMessage * message) 83257e2f323SJérôme Duval { 83357e2f323SJérôme Duval // User selected a sound in list view 83457e2f323SJérôme Duval UpdatePlayFile(); 83557e2f323SJérôme Duval UpdateButtons(); 83657e2f323SJérôme Duval } 83757e2f323SJérôme Duval 83857e2f323SJérôme Duval status_t 83957e2f323SJérôme Duval RecorderWindow::MakeRecordConnection(const media_node & input) 84057e2f323SJérôme Duval { 84157e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n")); 84257e2f323SJérôme Duval 84357e2f323SJérôme Duval // Find an available output for the given input node. 84457e2f323SJérôme Duval int32 count = 0; 84557e2f323SJérôme Duval status_t err = fRoster->GetFreeOutputsFor(input, &fAudioOutput, 1, &count, B_MEDIA_RAW_AUDIO); 84657e2f323SJérôme Duval if (err < B_OK) { 84757e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't get free outputs from audio input node\n")); 84857e2f323SJérôme Duval return err; 84957e2f323SJérôme Duval } 85057e2f323SJérôme Duval if (count < 1) { 85157e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): no free outputs from audio input node\n")); 85257e2f323SJérôme Duval return B_BUSY; 85357e2f323SJérôme Duval } 85457e2f323SJérôme Duval 85557e2f323SJérôme Duval // Find an available input for our own Node. Note that we go through the 85657e2f323SJérôme Duval // MediaRoster; calling Media Kit methods directly on Nodes in our app is 85757e2f323SJérôme Duval // not OK (because synchronization happens in the service thread, not in 85857e2f323SJérôme Duval // the calling thread). 85957e2f323SJérôme Duval // TODO: explain this 86057e2f323SJérôme Duval err = fRoster->GetFreeInputsFor(fRecordNode->Node(), &fRecInput, 1, &count, B_MEDIA_RAW_AUDIO); 86157e2f323SJérôme Duval if (err < B_OK) { 86257e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't get free inputs for sound recorder\n")); 86357e2f323SJérôme Duval return err; 86457e2f323SJérôme Duval } 86557e2f323SJérôme Duval if (count < 1) { 86657e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): no free inputs for sound recorder\n")); 86757e2f323SJérôme Duval return B_BUSY; 86857e2f323SJérôme Duval } 86957e2f323SJérôme Duval 87057e2f323SJérôme Duval // Find out what the time source of the input is. 87157e2f323SJérôme Duval // For most nodes, we just use the preferred time source (the DAC) for synchronization. 87257e2f323SJérôme Duval // However, nodes that record from an input need to synchronize to the audio input node 87357e2f323SJérôme Duval // instead for best results. 87457e2f323SJérôme Duval // MakeTimeSourceFor gives us a "clone" of the time source node that we can manipulate 87557e2f323SJérôme Duval // to our heart's content. When we're done with it, though, we need to call Release() 87657e2f323SJérôme Duval // on the time source node, so that it keeps an accurate reference count and can delete 87757e2f323SJérôme Duval // itself when it's no longer needed. 87857e2f323SJérôme Duval // TODO: what about filters connected to audio input? 87957e2f323SJérôme Duval media_node use_time_source; 88057e2f323SJérôme Duval BTimeSource * tsobj = fRoster->MakeTimeSourceFor(input); 88157e2f323SJérôme Duval if (! tsobj) { 88257e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't clone time source from audio input node\n")); 88357e2f323SJérôme Duval return B_MEDIA_BAD_NODE; 88457e2f323SJérôme Duval } 88557e2f323SJérôme Duval 88657e2f323SJérôme Duval // Apply the time source in effect to our own Node. 88757e2f323SJérôme Duval err = fRoster->SetTimeSourceFor(fRecordNode->Node().node, tsobj->Node().node); 88857e2f323SJérôme Duval if (err < B_OK) { 88957e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't set the sound recorder's time source\n")); 89057e2f323SJérôme Duval tsobj->Release(); 89157e2f323SJérôme Duval return err; 89257e2f323SJérôme Duval } 89357e2f323SJérôme Duval 89457e2f323SJérôme Duval // Get a format, any format. 89557e2f323SJérôme Duval media_format fmt; 89657e2f323SJérôme Duval fmt.u.raw_audio = fAudioOutput.format.u.raw_audio; 89757e2f323SJérôme Duval fmt.type = B_MEDIA_RAW_AUDIO; 89857e2f323SJérôme Duval 89957e2f323SJérôme Duval // Tell the consumer where we want data to go. 90057e2f323SJérôme Duval err = fRecordNode->SetHooks(RecordFile, NotifyRecordFile, this); 90157e2f323SJérôme Duval if (err < B_OK) { 90257e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't set the sound recorder's hook functions\n")); 90357e2f323SJérôme Duval tsobj->Release(); 90457e2f323SJérôme Duval return err; 90557e2f323SJérôme Duval } 90657e2f323SJérôme Duval 90757e2f323SJérôme Duval // Using the same structs for input and output is OK in BMediaRoster::Connect(). 90857e2f323SJérôme Duval err = fRoster->Connect(fAudioOutput.source, fRecInput.destination, &fmt, &fAudioOutput, &fRecInput); 90957e2f323SJérôme Duval if (err < B_OK) { 91057e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): failed to connect sound recorder to audio input node.\n")); 91157e2f323SJérôme Duval tsobj->Release(); 91257e2f323SJérôme Duval fRecordNode->SetHooks(0, 0, 0); 91357e2f323SJérôme Duval return err; 91457e2f323SJérôme Duval } 91557e2f323SJérôme Duval 91657e2f323SJérôme Duval // Start the time source if it's not running. 91757e2f323SJérôme Duval if ((tsobj->Node() != input) && !tsobj->IsRunning()) { 91857e2f323SJérôme Duval fRoster->StartNode(tsobj->Node(), BTimeSource::RealTime()); 91957e2f323SJérôme Duval } 92057e2f323SJérôme Duval tsobj->Release(); // we're done with this time source instance! 92157e2f323SJérôme Duval return B_OK; 92257e2f323SJérôme Duval } 92357e2f323SJérôme Duval 92457e2f323SJérôme Duval 92557e2f323SJérôme Duval status_t 92657e2f323SJérôme Duval RecorderWindow::BreakRecordConnection() 92757e2f323SJérôme Duval { 92857e2f323SJérôme Duval status_t err; 92957e2f323SJérôme Duval 93057e2f323SJérôme Duval // If we are the last connection, the Node will stop automatically since it 93157e2f323SJérôme Duval // has nowhere to send data to. 93257e2f323SJérôme Duval err = fRoster->StopNode(fRecInput.node, 0); 93357e2f323SJérôme Duval err = fRoster->Disconnect(fAudioOutput.node.node, fAudioOutput.source, fRecInput.node.node, fRecInput.destination); 93457e2f323SJérôme Duval fAudioOutput.source = media_source::null; 93557e2f323SJérôme Duval fRecInput.destination = media_destination::null; 93657e2f323SJérôme Duval return err; 93757e2f323SJérôme Duval } 93857e2f323SJérôme Duval 93957e2f323SJérôme Duval status_t 94057e2f323SJérôme Duval RecorderWindow::StopRecording() 94157e2f323SJérôme Duval { 94257e2f323SJérôme Duval if (!fRecording) 94357e2f323SJérôme Duval return B_OK; 94457e2f323SJérôme Duval fRecording = false; 94557e2f323SJérôme Duval BreakRecordConnection(); 94657e2f323SJérôme Duval fRecordNode->SetHooks(NULL,NULL,NULL); 94757e2f323SJérôme Duval if (fRecSize > 0) { 94857e2f323SJérôme Duval 94957e2f323SJérôme Duval wave_struct header; 95057e2f323SJérôme Duval header.riff.riff_id = FOURCC('R','I','F','F'); 95157e2f323SJérôme Duval header.riff.len = fRecSize + 36; 95257e2f323SJérôme Duval header.riff.wave_id = FOURCC('W','A','V','E'); 95357e2f323SJérôme Duval header.format_chunk.fourcc = FOURCC('f','m','t',' '); 95457e2f323SJérôme Duval header.format_chunk.len = sizeof(header.format); 95557e2f323SJérôme Duval header.format.format_tag = 1; 95657e2f323SJérôme Duval header.format.channels = 2; 95757e2f323SJérôme Duval header.format.samples_per_sec = 48000; 95857e2f323SJérôme Duval header.format.avg_bytes_per_sec = 48000 * 4; 95957e2f323SJérôme Duval header.format.block_align = 4; 96057e2f323SJérôme Duval header.format.bits_per_sample = 16; 96157e2f323SJérôme Duval header.data_chunk.fourcc = FOURCC('d','a','t','a'); 96257e2f323SJérôme Duval header.data_chunk.len = fRecSize; 96357e2f323SJérôme Duval fRecFile.Seek(0, SEEK_SET); 96457e2f323SJérôme Duval fRecFile.Write(&header, sizeof(header)); 96557e2f323SJérôme Duval 96657e2f323SJérôme Duval fRecFile.SetSize(fRecSize + sizeof(header)); // We reserve space; make sure we cut off any excess at the end. 96757e2f323SJérôme Duval AddSoundItem(fRecEntry, true); 96857e2f323SJérôme Duval } 96957e2f323SJérôme Duval else { 97057e2f323SJérôme Duval fRecEntry.Remove(); 97157e2f323SJérôme Duval } 97257e2f323SJérôme Duval // We're done for this time. 97357e2f323SJérôme Duval fRecEntry.Unset(); 97457e2f323SJérôme Duval // Close the file. 97557e2f323SJérôme Duval fRecFile.Unset(); 97657e2f323SJérôme Duval // No more recording going on. 97757e2f323SJérôme Duval fRecLimit = 0; 97857e2f323SJérôme Duval fRecSize = 0; 97957e2f323SJérôme Duval SetButtonState(btnPaused); 98057e2f323SJérôme Duval fRecordButton->SetStopped(); 98157e2f323SJérôme Duval 98257e2f323SJérôme Duval return B_OK; 98357e2f323SJérôme Duval } 98457e2f323SJérôme Duval 98557e2f323SJérôme Duval 98657e2f323SJérôme Duval status_t 98757e2f323SJérôme Duval RecorderWindow::StopPlaying() 98857e2f323SJérôme Duval { 98957e2f323SJérôme Duval if (fPlayer) { 99057e2f323SJérôme Duval fPlayer->Stop(); 99157e2f323SJérôme Duval fPlayer->SetCallbacks(0, 0, 0); 99257e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(NULL); 99357e2f323SJérôme Duval delete fPlayer; 99457e2f323SJérôme Duval fPlayer = NULL; 99557e2f323SJérôme Duval } 99657e2f323SJérôme Duval SetButtonState(btnPaused); 99757e2f323SJérôme Duval fPlayButton->SetStopped(); 99857e2f323SJérôme Duval fTrackSlider->ResetMainTime(); 99957e2f323SJérôme Duval fScopeView->SetMainTime(*fTrackSlider->MainTime()); 100057e2f323SJérôme Duval return B_OK; 100157e2f323SJérôme Duval } 100257e2f323SJérôme Duval 100357e2f323SJérôme Duval 100457e2f323SJérôme Duval void 100557e2f323SJérôme Duval RecorderWindow::SetButtonState(BtnState state) 100657e2f323SJérôme Duval { 100757e2f323SJérôme Duval fButtonState = state; 100857e2f323SJérôme Duval UpdateButtons(); 100957e2f323SJérôme Duval } 101057e2f323SJérôme Duval 101157e2f323SJérôme Duval 101257e2f323SJérôme Duval void 101357e2f323SJérôme Duval RecorderWindow::UpdateButtons() 101457e2f323SJérôme Duval { 101557e2f323SJérôme Duval bool hasSelection = (fSoundList->CurrentSelection() >= 0); 101657e2f323SJérôme Duval fRecordButton->SetEnabled(fButtonState != btnPlaying); 101757e2f323SJérôme Duval fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 101857e2f323SJérôme Duval fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 101957e2f323SJérôme Duval fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 102057e2f323SJérôme Duval fStopButton->SetEnabled(fButtonState != btnPaused); 102157e2f323SJérôme Duval fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording)); 102257e2f323SJérôme Duval fLengthControl->SetEnabled(fButtonState != btnRecording); 102357e2f323SJérôme Duval fInputField->SetEnabled(fButtonState != btnRecording); 102457e2f323SJérôme Duval } 102557e2f323SJérôme Duval 1026*5fa77532SJérôme Duval #ifndef __HAIKU__ 1027*5fa77532SJérôme Duval extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(BMediaTrack *self, media_format *inout_format); 1028*5fa77532SJérôme Duval #endif 102957e2f323SJérôme Duval 103057e2f323SJérôme Duval void 103157e2f323SJérôme Duval RecorderWindow::UpdatePlayFile() 103257e2f323SJérôme Duval { 103357e2f323SJérôme Duval // Get selection. 103457e2f323SJérôme Duval int32 selIdx = fSoundList->CurrentSelection(); 103557e2f323SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(selIdx)); 103657e2f323SJérôme Duval if (! pItem) { 103757e2f323SJérôme Duval return; 103857e2f323SJérôme Duval } 103957e2f323SJérôme Duval 104057e2f323SJérôme Duval status_t err; 104157e2f323SJérôme Duval BEntry& entry = pItem->Entry(); 104257e2f323SJérôme Duval entry_ref ref; 104357e2f323SJérôme Duval entry.GetRef(&ref); 104457e2f323SJérôme Duval fPlayFile = new BMediaFile(&ref); //, B_MEDIA_FILE_UNBUFFERED); 104557e2f323SJérôme Duval if ((err = fPlayFile->InitCheck()) < B_OK) { 104657e2f323SJérôme Duval ErrorAlert("get the file to play", err); 104757e2f323SJérôme Duval delete fPlayFile; 104857e2f323SJérôme Duval return; 104957e2f323SJérôme Duval } 105057e2f323SJérôme Duval 105157e2f323SJérôme Duval ASSERT(fPlayTrack == NULL); 105257e2f323SJérôme Duval 105357e2f323SJérôme Duval for (int ix=0; ix<fPlayFile->CountTracks(); ix++) { 105457e2f323SJérôme Duval BMediaTrack * track = fPlayFile->TrackAt(ix); 105557e2f323SJérôme Duval fPlayFormat.type = B_MEDIA_RAW_AUDIO; 1056*5fa77532SJérôme Duval #ifdef __HAIKU__ 1057*5fa77532SJérôme Duval if ((track->DecodedFormat(&fPlayFormat) == B_OK) 1058*5fa77532SJérôme Duval #else 1059*5fa77532SJérôme Duval if ((DecodedFormat__11BMediaTrackP12media_format(track, &fPlayFormat) == B_OK) 1060*5fa77532SJérôme Duval #endif 1061*5fa77532SJérôme Duval && (fPlayFormat.type == B_MEDIA_RAW_AUDIO)) { 106257e2f323SJérôme Duval fPlayTrack = track; 106357e2f323SJérôme Duval break; 106457e2f323SJérôme Duval } 106557e2f323SJérôme Duval if (track) 106657e2f323SJérôme Duval fPlayFile->ReleaseTrack(track); 106757e2f323SJérôme Duval } 106857e2f323SJérôme Duval 106957e2f323SJérôme Duval if (!fPlayTrack) { 107057e2f323SJérôme Duval ErrorAlert("get the file to play", err); 107157e2f323SJérôme Duval delete fPlayFile; 107257e2f323SJérôme Duval return; 107357e2f323SJérôme Duval } 107457e2f323SJérôme Duval 107557e2f323SJérôme Duval BString filename = "File Name: "; 107657e2f323SJérôme Duval filename << ref.name; 107757e2f323SJérôme Duval fFilename->SetText(filename.String()); 107857e2f323SJérôme Duval 107957e2f323SJérôme Duval BString format = "Format: "; 108057e2f323SJérôme Duval media_file_format file_format; 108157e2f323SJérôme Duval if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK) 108257e2f323SJérôme Duval format << file_format.short_name; 108357e2f323SJérôme Duval BString compression = "Compression: "; 108457e2f323SJérôme Duval media_codec_info codec_info; 108557e2f323SJérôme Duval if (fPlayTrack->GetCodecInfo(&codec_info) == B_OK) { 108657e2f323SJérôme Duval if (strcmp(codec_info.short_name, "raw")==0) 108757e2f323SJérôme Duval compression << "None"; 108857e2f323SJérôme Duval else 108957e2f323SJérôme Duval compression << codec_info.short_name; 109057e2f323SJérôme Duval } 109157e2f323SJérôme Duval BString channels = "Channels: "; 109257e2f323SJérôme Duval channels << fPlayFormat.u.raw_audio.channel_count; 109357e2f323SJérôme Duval BString samplesize = "Sample Size: "; 109457e2f323SJérôme Duval samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf) << " bits"; 109557e2f323SJérôme Duval BString samplerate = "Sample Rate: "; 109657e2f323SJérôme Duval samplerate << (int)fPlayFormat.u.raw_audio.frame_rate; 109757e2f323SJérôme Duval BString durationString = "Duration: "; 109857e2f323SJérôme Duval bigtime_t duration = fPlayTrack->Duration(); 109957e2f323SJérôme Duval durationString << (float)(duration / 1000000.0) << " seconds"; 110057e2f323SJérôme Duval 110157e2f323SJérôme Duval fFormat->SetText(format.String()); 110257e2f323SJérôme Duval fCompression->SetText(compression.String()); 110357e2f323SJérôme Duval fChannels->SetText(channels.String()); 110457e2f323SJérôme Duval fSampleSize->SetText(samplesize.String()); 110557e2f323SJérôme Duval fSampleRate->SetText(samplerate.String()); 110657e2f323SJérôme Duval fDuration->SetText(durationString.String()); 110757e2f323SJérôme Duval 110857e2f323SJérôme Duval fTrackSlider->SetTotalTime(duration, true); 110957e2f323SJérôme Duval fScopeView->SetMainTime(fTrackSlider->LeftTime()); 111057e2f323SJérôme Duval fScopeView->SetTotalTime(duration); 111157e2f323SJérôme Duval fScopeView->SetRightTime(fTrackSlider->RightTime()); 111257e2f323SJérôme Duval fScopeView->SetLeftTime(fTrackSlider->LeftTime()); 111357e2f323SJérôme Duval fScopeView->RenderTrack(fPlayTrack, fPlayFormat); 111457e2f323SJérôme Duval } 111557e2f323SJérôme Duval 111657e2f323SJérôme Duval 111757e2f323SJérôme Duval void 111857e2f323SJérôme Duval RecorderWindow::ErrorAlert(const char * action, status_t err) 111957e2f323SJérôme Duval { 112057e2f323SJérôme Duval char msg[300]; 1121*5fa77532SJérôme Duval sprintf(msg, "Cannot %s: %s. [%lx]", action, strerror(err), (int32) err); 112257e2f323SJérôme Duval (new BAlert("", msg, "Stop"))->Go(); 112357e2f323SJérôme Duval } 112457e2f323SJérôme Duval 112557e2f323SJérôme Duval 112657e2f323SJérôme Duval status_t 112757e2f323SJérôme Duval RecorderWindow::NewTempName(char * name) 112857e2f323SJérôme Duval { 112957e2f323SJérôme Duval int init_count = fTempCount; 113057e2f323SJérôme Duval again: 113157e2f323SJérôme Duval if (fTempCount-init_count > 25) { 113257e2f323SJérôme Duval return B_ERROR; 113357e2f323SJérôme Duval } 113457e2f323SJérôme Duval else { 113557e2f323SJérôme Duval fTempCount++; 113657e2f323SJérôme Duval if (fTempCount==0) 113757e2f323SJérôme Duval sprintf(name, "Audio Clip"); 113857e2f323SJérôme Duval else 113957e2f323SJérôme Duval sprintf(name, "Audio Clip %d", fTempCount); 114057e2f323SJérôme Duval BPath path; 114157e2f323SJérôme Duval status_t err; 114257e2f323SJérôme Duval BEntry tempEnt; 114357e2f323SJérôme Duval if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) { 114457e2f323SJérôme Duval return err; 114557e2f323SJérôme Duval } 114657e2f323SJérôme Duval if ((err = tempEnt.GetPath(&path)) < B_OK) { 114757e2f323SJérôme Duval return err; 114857e2f323SJérôme Duval } 114957e2f323SJérôme Duval path.Append(name); 115057e2f323SJérôme Duval int fd; 115157e2f323SJérôme Duval // Use O_EXCL so we know we created the file (sync with other instances) 115257e2f323SJérôme Duval if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) { 115357e2f323SJérôme Duval goto again; 115457e2f323SJérôme Duval } 115557e2f323SJérôme Duval close(fd); 115657e2f323SJérôme Duval } 115757e2f323SJérôme Duval return B_OK; 115857e2f323SJérôme Duval } 115957e2f323SJérôme Duval 116057e2f323SJérôme Duval 116157e2f323SJérôme Duval void 116257e2f323SJérôme Duval RecorderWindow::AddSoundItem(const BEntry& entry, bool temp) 116357e2f323SJérôme Duval { 116457e2f323SJérôme Duval // Create list item to display. 116557e2f323SJérôme Duval SoundListItem * listItem = new SoundListItem(entry, temp); 116657e2f323SJérôme Duval fSoundList->AddItem(listItem); 116757e2f323SJérôme Duval fSoundList->Invalidate(); 116857e2f323SJérôme Duval fSoundList->Select(fSoundList->IndexOf(listItem)); 116957e2f323SJérôme Duval } 117057e2f323SJérôme Duval 117157e2f323SJérôme Duval void 117257e2f323SJérôme Duval RecorderWindow::RecordFile(void * cookie, bigtime_t timestamp, void * data, size_t size, const media_raw_audio_format & format) 117357e2f323SJérôme Duval { 117457e2f323SJérôme Duval // Callback called from the SoundConsumer when receiving buffers. 117557e2f323SJérôme Duval assert((format.format & 0x02) && format.channel_count); 117657e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 117757e2f323SJérôme Duval 117857e2f323SJérôme Duval if (window->fRecording) { 117957e2f323SJérôme Duval // Write the data to file (we don't buffer or guard file access 118057e2f323SJérôme Duval // or anything) 118157e2f323SJérôme Duval if (window->fRecSize < window->fRecLimit) { 118257e2f323SJérôme Duval window->fRecFile.WriteAt(window->fRecSize, data, size); 118357e2f323SJérôme Duval window->fVUView->ComputeNextLevel(data, size); 118457e2f323SJérôme Duval window->fRecSize += size; 118557e2f323SJérôme Duval } else { 118657e2f323SJérôme Duval // We're done! 118757e2f323SJérôme Duval window->PostMessage(STOP_RECORDING); 118857e2f323SJérôme Duval } 118957e2f323SJérôme Duval } 119057e2f323SJérôme Duval } 119157e2f323SJérôme Duval 119257e2f323SJérôme Duval 119357e2f323SJérôme Duval void 119457e2f323SJérôme Duval RecorderWindow::NotifyRecordFile(void * cookie, int32 code, ...) 119557e2f323SJérôme Duval { 119657e2f323SJérôme Duval if ((code == B_WILL_STOP) || (code == B_NODE_DIES)) { 119757e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 119857e2f323SJérôme Duval // Tell the window we've stopped, if it doesn't 119957e2f323SJérôme Duval // already know. 120057e2f323SJérôme Duval window->PostMessage(STOP_RECORDING); 120157e2f323SJérôme Duval } 120257e2f323SJérôme Duval } 120357e2f323SJérôme Duval 120457e2f323SJérôme Duval 120557e2f323SJérôme Duval void 120657e2f323SJérôme Duval RecorderWindow::PlayFile(void * cookie, void * data, size_t size, const media_raw_audio_format & format) 120757e2f323SJérôme Duval { 120857e2f323SJérôme Duval // Callback called from the SoundProducer when producing buffers. 120957e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 121057e2f323SJérôme Duval int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) * 121157e2f323SJérôme Duval window->fPlayFormat.u.raw_audio.channel_count; 121257e2f323SJérôme Duval 121357e2f323SJérôme Duval if (window->fPlayFrame < window->fPlayLimit) { 121457e2f323SJérôme Duval int64 frames = 0; 121557e2f323SJérôme Duval window->fPlayTrack->ReadFrames(data, &frames); 121657e2f323SJérôme Duval window->fVUView->ComputeNextLevel(data, size/frame_size); 121757e2f323SJérôme Duval window->fPlayFrame += size/frame_size; 121857e2f323SJérôme Duval window->PostMessage(UPDATE_TRACKSLIDER); 121957e2f323SJérôme Duval } else { 122057e2f323SJérôme Duval // we're done! 122157e2f323SJérôme Duval window->PostMessage(STOP_PLAYING); 122257e2f323SJérôme Duval } 122357e2f323SJérôme Duval } 122457e2f323SJérôme Duval 122557e2f323SJérôme Duval void 122657e2f323SJérôme Duval RecorderWindow::NotifyPlayFile(void * cookie, BSoundPlayer::sound_player_notification code, ...) 122757e2f323SJérôme Duval { 122857e2f323SJérôme Duval if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) { 122957e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 123057e2f323SJérôme Duval // tell the window we've stopped, if it doesn't 123157e2f323SJérôme Duval // already know. 123257e2f323SJérôme Duval window->PostMessage(STOP_PLAYING); 123357e2f323SJérôme Duval } 123457e2f323SJérôme Duval } 123557e2f323SJérôme Duval 123657e2f323SJérôme Duval 123757e2f323SJérôme Duval void 123857e2f323SJérôme Duval RecorderWindow::RefsReceived(BMessage *msg) 123957e2f323SJérôme Duval { 124057e2f323SJérôme Duval entry_ref ref; 124157e2f323SJérôme Duval int32 i = 0; 124257e2f323SJérôme Duval 124357e2f323SJérôme Duval while (msg->FindRef("refs", i++, &ref) == B_OK) { 124457e2f323SJérôme Duval 124557e2f323SJérôme Duval BEntry entry(&ref, true); 124657e2f323SJérôme Duval BPath path(&entry); 124757e2f323SJérôme Duval BNode node(&entry); 124857e2f323SJérôme Duval 124957e2f323SJérôme Duval if (node.IsFile()) { 125057e2f323SJérôme Duval AddSoundItem(entry, false); 125157e2f323SJérôme Duval } else if(node.IsDirectory()) { 125257e2f323SJérôme Duval 125357e2f323SJérôme Duval } 125457e2f323SJérôme Duval } 125557e2f323SJérôme Duval } 1256