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 * 5ad0c65eaSJérôme Duval * Inspired by SoundCapture from Be newsletter (Media Kit Basics: 6ad0c65eaSJérôme Duval * Consumers and Producers) 757e2f323SJérôme Duval */ 857e2f323SJérôme Duval 957e2f323SJérôme Duval #include <Application.h> 1057e2f323SJérôme Duval #include <Alert.h> 1157e2f323SJérôme Duval #include <Debug.h> 1257e2f323SJérôme Duval #include <Screen.h> 1357e2f323SJérôme Duval #include <Button.h> 1457e2f323SJérôme Duval #include <CheckBox.h> 1557e2f323SJérôme Duval #include <TextControl.h> 1657e2f323SJérôme Duval #include <MenuField.h> 1757e2f323SJérôme Duval #include <PopUpMenu.h> 1857e2f323SJérôme Duval #include <MenuItem.h> 1957e2f323SJérôme Duval #include <Box.h> 2057e2f323SJérôme Duval #include <ScrollView.h> 2157e2f323SJérôme Duval #include <Beep.h> 2257e2f323SJérôme Duval #include <StringView.h> 2357e2f323SJérôme Duval #include <String.h> 2457e2f323SJérôme Duval #include <Slider.h> 2557e2f323SJérôme Duval #include <Message.h> 2657e2f323SJérôme Duval 2757e2f323SJérôme Duval #include <Path.h> 2857e2f323SJérôme Duval #include <FindDirectory.h> 2957e2f323SJérôme Duval #include <MediaAddOn.h> 3057e2f323SJérôme Duval 3157e2f323SJérôme Duval #include <SoundPlayer.h> 3257e2f323SJérôme Duval 3357e2f323SJérôme Duval #include <assert.h> 3457e2f323SJérôme Duval #include <stdio.h> 3557e2f323SJérôme Duval #include <string.h> 3657e2f323SJérôme Duval #include <stdlib.h> 3757e2f323SJérôme Duval #include <ctype.h> 3857e2f323SJérôme Duval #include <unistd.h> 39338b8dc3SIngo Weinhold #include <fcntl.h> 4057e2f323SJérôme Duval 4157e2f323SJérôme Duval #include <MediaRoster.h> 4257e2f323SJérôme Duval #include <TimeSource.h> 43019ed09bSJérôme Duval #include <NodeInfo.h> 4457e2f323SJérôme Duval 4557e2f323SJérôme Duval #include "RecorderWindow.h" 4657e2f323SJérôme Duval #include "SoundConsumer.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 59*3ee96407SJérôme Duval #undef B_TRANSLATE_CONTEXT 60*3ee96407SJérôme Duval #define B_TRANSLATE_CONTEXT "RecorderWindow" 61*3ee96407SJérôme Duval 62*3ee96407SJérôme Duval 6357e2f323SJérôme Duval // default window positioning 6457e2f323SJérôme Duval static const float MIN_WIDTH = 400.0f; 6557e2f323SJérôme Duval static const float MIN_HEIGHT = 336.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 10557e2f323SJérôme Duval RecorderWindow::RecorderWindow() : 106ad0c65eaSJérôme Duval BWindow(BRect(XPOS,YPOS,XPOS+MIN_WIDTH,YPOS+MIN_HEIGHT), "SoundRecorder", 107ad0c65eaSJérôme Duval B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE), 10857e2f323SJérôme Duval fPlayer(NULL), 1095fa77532SJérôme Duval fSoundList(NULL), 11057e2f323SJérôme Duval fPlayFile(NULL), 11157e2f323SJérôme Duval fPlayTrack(NULL), 1127942339dSJérôme Duval fPlayFrames(0), 113948356deSJérôme Duval fLooping(false), 1145fa77532SJérôme Duval fSavePanel(NULL), 115*3ee96407SJérôme Duval fInitCheck(B_OK), 116*3ee96407SJérôme Duval fAppCatalog(NULL) 11757e2f323SJérôme Duval { 11857e2f323SJérôme Duval fRoster = NULL; 11957e2f323SJérôme Duval fRecordButton = NULL; 12057e2f323SJérôme Duval fPlayButton = NULL; 12157e2f323SJérôme Duval fStopButton = NULL; 12257e2f323SJérôme Duval fSaveButton = NULL; 123948356deSJérôme Duval fLoopButton = NULL; 12457e2f323SJérôme Duval fInputField = NULL; 12557e2f323SJérôme Duval fRecordNode = 0; 12657e2f323SJérôme Duval fRecording = false; 12757e2f323SJérôme Duval fTempCount = -1; 12857e2f323SJérôme Duval fButtonState = btnPaused; 12957e2f323SJérôme Duval 13057e2f323SJérôme Duval CalcSizes(MIN_WIDTH, MIN_HEIGHT); 13157e2f323SJérôme Duval 132*3ee96407SJérôme Duval be_locale->GetAppCatalog(&fAppCatalog); 133*3ee96407SJérôme Duval 134*3ee96407SJérôme Duval SetTitle(B_TRANSLATE("SoundRecorder")); 135*3ee96407SJérôme Duval 1365fa77532SJérôme Duval fInitCheck = InitWindow(); 1375fa77532SJérôme Duval if (fInitCheck != B_OK) { 138bdb1d3acSJérôme Duval if (fInitCheck == B_NAME_NOT_FOUND) 139*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot find default audio hardware"), 140*3ee96407SJérôme Duval fInitCheck); 141bdb1d3acSJérôme Duval else 142*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot connect to media server"), 143*3ee96407SJérôme Duval fInitCheck); 1445fa77532SJérôme Duval PostMessage(B_QUIT_REQUESTED); 1455fa77532SJérôme Duval } else 1465fa77532SJérôme Duval Show(); 14757e2f323SJérôme Duval } 14857e2f323SJérôme Duval 1498e32a27eSPhilippe Saint-Pierre 15057e2f323SJérôme Duval RecorderWindow::~RecorderWindow() 15157e2f323SJérôme Duval { 152ad0c65eaSJérôme Duval // The sound consumer and producer are Nodes; it has to be released and the Roster 15357e2f323SJérôme Duval // will reap it when it's done. 15457e2f323SJérôme Duval if (fRecordNode) { 15557e2f323SJérôme Duval fRecordNode->Release(); 15657e2f323SJérôme Duval } 15757e2f323SJérôme Duval if (fPlayer) { 15857e2f323SJérôme Duval delete fPlayer; 15957e2f323SJérôme Duval } 1607942339dSJérôme Duval 1617942339dSJérôme Duval if (fPlayTrack && fPlayFile) 1627942339dSJérôme Duval fPlayFile->ReleaseTrack(fPlayTrack); 1637942339dSJérôme Duval if (fPlayFile) 1647942339dSJérôme Duval delete fPlayFile; 1657942339dSJérôme Duval fPlayTrack = NULL; 1667942339dSJérôme Duval fPlayFile = NULL; 1677942339dSJérôme Duval 16857e2f323SJérôme Duval // Clean up items in list view. 16957e2f323SJérôme Duval if (fSoundList) { 17057e2f323SJérôme Duval fSoundList->DeselectAll(); 171ad0c65eaSJérôme Duval for (int i = 0; i < fSoundList->CountItems(); i++) { 172ad0c65eaSJérôme Duval WINDOW((stderr, "clean up item %d\n", i+1)); 173ad0c65eaSJérôme Duval SoundListItem* item = dynamic_cast<SoundListItem *>(fSoundList->ItemAt(i)); 17457e2f323SJérôme Duval if (item) { 17557e2f323SJérôme Duval if (item->IsTemp()) { 17657e2f323SJérôme Duval item->Entry().Remove(); // delete temp file 17757e2f323SJérôme Duval } 17857e2f323SJérôme Duval delete item; 17957e2f323SJérôme Duval } 18057e2f323SJérôme Duval } 18157e2f323SJérôme Duval fSoundList->MakeEmpty(); 18257e2f323SJérôme Duval } 18357e2f323SJérôme Duval // Clean up currently recording file, if any. 18457e2f323SJérôme Duval fRecEntry.Remove(); 18557e2f323SJérôme Duval fRecEntry.Unset(); 18605ea8535SKarsten Heimrich 18705ea8535SKarsten Heimrich delete fSavePanel; 18857e2f323SJérôme Duval } 18957e2f323SJérôme Duval 19057e2f323SJérôme Duval 1915fa77532SJérôme Duval status_t 1925fa77532SJérôme Duval RecorderWindow::InitCheck() 1935fa77532SJérôme Duval { 1945fa77532SJérôme Duval return fInitCheck; 1955fa77532SJérôme Duval } 1965fa77532SJérôme Duval 1975fa77532SJérôme Duval 19857e2f323SJérôme Duval void 19957e2f323SJérôme Duval RecorderWindow::CalcSizes(float min_wid, float min_hei) 20057e2f323SJérôme Duval { 20157e2f323SJérôme Duval // Set up size limits based on new screen size 2025e99b7dfSJérôme Duval BScreen screen(this); 20357e2f323SJérôme Duval BRect r = screen.Frame(); 20457e2f323SJérôme Duval float wid = r.Width()-12; 20557e2f323SJérôme Duval SetSizeLimits(min_wid, wid, min_hei, r.Height()-24); 20657e2f323SJérôme Duval 20757e2f323SJérôme Duval // Don't zoom to cover all of screen; user can resize last bit if necessary. 20857e2f323SJérôme Duval // This leaves other windows visible. 20957e2f323SJérôme Duval if (wid > 640) { 21057e2f323SJérôme Duval wid = 640 + (wid-640)/2; 21157e2f323SJérôme Duval } 21257e2f323SJérôme Duval SetZoomLimits(wid, r.Height()-24); 21357e2f323SJérôme Duval } 21457e2f323SJérôme Duval 21557e2f323SJérôme Duval 21657e2f323SJérôme Duval status_t 21757e2f323SJérôme Duval RecorderWindow::InitWindow() 21857e2f323SJérôme Duval { 21957e2f323SJérôme Duval BPopUpMenu * popup = 0; 22057e2f323SJérôme Duval status_t error; 22157e2f323SJérôme Duval 22257e2f323SJérôme Duval try { 22357e2f323SJérôme Duval // Find temp directory for recorded sounds. 22457e2f323SJérôme Duval BPath path; 22557e2f323SJérôme Duval if (!(error = find_directory(B_COMMON_TEMP_DIRECTORY, &path))) { 22657e2f323SJérôme Duval error = fTempDir.SetTo(path.Path()); 22757e2f323SJérôme Duval } 22857e2f323SJérôme Duval if (error < 0) { 22957e2f323SJérôme Duval goto bad_mojo; 23057e2f323SJérôme Duval } 23157e2f323SJérôme Duval 23257e2f323SJérôme Duval // Make sure the media roster is there (which means the server is there). 23357e2f323SJérôme Duval fRoster = BMediaRoster::Roster(&error); 23457e2f323SJérôme Duval if (!fRoster) { 23557e2f323SJérôme Duval goto bad_mojo; 23657e2f323SJérôme Duval } 23757e2f323SJérôme Duval 23857e2f323SJérôme Duval error = fRoster->GetAudioInput(&fAudioInputNode); 23957e2f323SJérôme Duval if (error < B_OK) { // there's no input? 24057e2f323SJérôme Duval goto bad_mojo; 24157e2f323SJérôme Duval } 24257e2f323SJérôme Duval 24357e2f323SJérôme Duval error = fRoster->GetAudioMixer(&fAudioMixerNode); 24457e2f323SJérôme Duval if (error < B_OK) { // there's no mixer? 24557e2f323SJérôme Duval goto bad_mojo; 24657e2f323SJérôme Duval } 24757e2f323SJérôme Duval 24857e2f323SJérôme Duval // Create our internal Node which records sound, and register it. 24957e2f323SJérôme Duval fRecordNode = new SoundConsumer("Sound Recorder"); 25057e2f323SJérôme Duval error = fRoster->RegisterNode(fRecordNode); 25157e2f323SJérôme Duval if (error < B_OK) { 25257e2f323SJérôme Duval goto bad_mojo; 25357e2f323SJérôme Duval } 25457e2f323SJérôme Duval 25557e2f323SJérôme Duval // Create the window header with controls 25657e2f323SJérôme Duval BRect r(Bounds()); 25757e2f323SJérôme Duval r.bottom = r.top + 175; 258ad0c65eaSJérôme Duval BBox *background = new BBox(r, "_background", B_FOLLOW_LEFT_RIGHT 259ad0c65eaSJérôme Duval | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER); 26057e2f323SJérôme Duval AddChild(background); 26157e2f323SJérôme Duval 26257e2f323SJérôme Duval 26357e2f323SJérôme Duval 26457e2f323SJérôme Duval r = background->Bounds(); 265a4797804SJérôme Duval r.left = 0; 2668881ad65SJérôme Duval r.right = r.left + 38; 26757e2f323SJérôme Duval r.bottom = r.top + 104; 26857e2f323SJérôme Duval fVUView = new VUView(r, B_FOLLOW_LEFT|B_FOLLOW_TOP); 26957e2f323SJérôme Duval background->AddChild(fVUView); 27057e2f323SJérôme Duval 27157e2f323SJérôme Duval r = background->Bounds(); 2728881ad65SJérôme Duval r.left = r.left + 40; 27357e2f323SJérôme Duval r.bottom = r.top + 104; 27457e2f323SJérôme Duval fScopeView = new ScopeView(r, B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP); 27557e2f323SJérôme Duval background->AddChild(fScopeView); 27657e2f323SJérôme Duval 27757e2f323SJérôme Duval r = background->Bounds(); 27857e2f323SJérôme Duval r.left = 2; 27957e2f323SJérôme Duval r.right -= 26; 28057e2f323SJérôme Duval r.top = 115; 28157e2f323SJérôme Duval r.bottom = r.top + 30; 282ad0c65eaSJérôme Duval fTrackSlider = new TrackSlider(r, "trackSlider", new BMessage(POSITION_CHANGED), 283ad0c65eaSJérôme Duval B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 28457e2f323SJérôme Duval background->AddChild(fTrackSlider); 28557e2f323SJérôme Duval 28657e2f323SJérôme Duval BRect buttonRect; 28757e2f323SJérôme Duval 28857e2f323SJérôme Duval // Button for rewinding 28957e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSkipButtonSize); 29057e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-7, 25)); 291*3ee96407SJérôme Duval fRewindButton = new TransportButton(buttonRect, B_TRANSLATE("Rewind"), 292ad0c65eaSJérôme Duval kSkipBackBitmapBits, kPressedSkipBackBitmapBits, 293ad0c65eaSJérôme Duval kDisabledSkipBackBitmapBits, new BMessage(REWIND)); 29457e2f323SJérôme Duval background->AddChild(fRewindButton); 29557e2f323SJérôme Duval 29657e2f323SJérôme Duval // Button for stopping recording or playback 29757e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kStopButtonSize); 29857e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-48, 25)); 299*3ee96407SJérôme Duval fStopButton = new TransportButton(buttonRect, B_TRANSLATE("Stop"), 300ad0c65eaSJérôme Duval kStopButtonBitmapBits, kPressedStopButtonBitmapBits, 301ad0c65eaSJérôme Duval kDisabledStopButtonBitmapBits, new BMessage(STOP)); 30257e2f323SJérôme Duval background->AddChild(fStopButton); 30357e2f323SJérôme Duval 30457e2f323SJérôme Duval // Button for starting playback of selected sound 30557e2f323SJérôme Duval BRect playRect(BPoint(0,0), kPlayButtonSize); 30657e2f323SJérôme Duval playRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-82, 25)); 307*3ee96407SJérôme Duval fPlayButton = new PlayPauseButton(playRect, B_TRANSLATE("Play"), 30857e2f323SJérôme Duval new BMessage(PLAY), new BMessage(PLAY_PERIOD), ' ', 0); 30957e2f323SJérôme Duval background->AddChild(fPlayButton); 31057e2f323SJérôme Duval 31157e2f323SJérôme Duval // Button for forwarding 31257e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSkipButtonSize); 31357e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-133, 25)); 314*3ee96407SJérôme Duval fForwardButton = new TransportButton(buttonRect, B_TRANSLATE("Forward"), 315ad0c65eaSJérôme Duval kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits, 316ad0c65eaSJérôme Duval kDisabledSkipForwardBitmapBits, new BMessage(FORWARD)); 31757e2f323SJérôme Duval background->AddChild(fForwardButton); 31857e2f323SJérôme Duval 31957e2f323SJérôme Duval // Button to start recording (or waiting for sound) 32057e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kRecordButtonSize); 32157e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-174, 25)); 322*3ee96407SJérôme Duval fRecordButton = new RecordButton(buttonRect, B_TRANSLATE("Record"), 32357e2f323SJérôme Duval new BMessage(RECORD), new BMessage(RECORD_PERIOD)); 32457e2f323SJérôme Duval background->AddChild(fRecordButton); 32557e2f323SJérôme Duval 32657e2f323SJérôme Duval // Button for saving selected sound 32757e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kDiskButtonSize); 32857e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-250, 21)); 329*3ee96407SJérôme Duval fSaveButton = new TransportButton(buttonRect, B_TRANSLATE("Save"), 330ad0c65eaSJérôme Duval kDiskButtonBitmapsBits, kPressedDiskButtonBitmapsBits, 331ad0c65eaSJérôme Duval kDisabledDiskButtonBitmapsBits, new BMessage(SAVE)); 33257e2f323SJérôme Duval fSaveButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 33357e2f323SJérôme Duval background->AddChild(fSaveButton); 33457e2f323SJérôme Duval 335948356deSJérôme Duval // Button Loop 336948356deSJérôme Duval buttonRect = BRect(BPoint(0,0), kArrowSize); 337948356deSJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(23, 48)); 338*3ee96407SJérôme Duval fLoopButton = new DrawButton(buttonRect, B_TRANSLATE("Loop"), 339948356deSJérôme Duval kLoopArrowBits, kArrowBits, new BMessage(LOOP)); 340948356deSJérôme Duval fLoopButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 341948356deSJérôme Duval fLoopButton->SetTarget(this); 342948356deSJérôme Duval background->AddChild(fLoopButton); 343948356deSJérôme Duval 34457e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSpeakerIconBitmapSize); 34557e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(121, 17)); 346ad0c65eaSJérôme Duval SpeakerView *speakerView = new SpeakerView(buttonRect, 347ad0c65eaSJérôme Duval B_FOLLOW_LEFT | B_FOLLOW_TOP); 34857e2f323SJérôme Duval speakerView->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 34957e2f323SJérôme Duval background->AddChild(speakerView); 35057e2f323SJérôme Duval 35157e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), BPoint(84, 19)); 35257e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(107, 20)); 353ad0c65eaSJérôme Duval fVolumeSlider = new VolumeSlider(buttonRect, "volumeSlider", 354ad0c65eaSJérôme Duval B_FOLLOW_LEFT | B_FOLLOW_TOP); 35557e2f323SJérôme Duval fVolumeSlider->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 35657e2f323SJérôme Duval background->AddChild(fVolumeSlider); 35757e2f323SJérôme Duval 35857e2f323SJérôme Duval // Button to mask/see sounds list 35957e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kUpDownButtonSize); 36057e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(21, 25)); 36157e2f323SJérôme Duval fUpDownButton = new UpDownButton(buttonRect, new BMessage(VIEW_LIST)); 36257e2f323SJérôme Duval fUpDownButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 36357e2f323SJérôme Duval background->AddChild(fUpDownButton); 36457e2f323SJérôme Duval 36557e2f323SJérôme Duval r = Bounds(); 36657e2f323SJérôme Duval r.top = background->Bounds().bottom + 1; 36757e2f323SJérôme Duval fBottomBox = new BBox(r, "bottomBox", B_FOLLOW_ALL); 36857e2f323SJérôme Duval fBottomBox->SetBorder(B_NO_BORDER); 36957e2f323SJérôme Duval AddChild(fBottomBox); 37057e2f323SJérôme Duval 37157e2f323SJérôme Duval // The actual list of recorded sounds (initially empty) sits 37257e2f323SJérôme Duval // below the header with the controls. 37357e2f323SJérôme Duval r = fBottomBox->Bounds(); 37457e2f323SJérôme Duval r.left += 190; 37557e2f323SJérôme Duval r.InsetBy(10, 10); 37657e2f323SJérôme Duval r.left -= 10; 37757e2f323SJérôme Duval r.top += 4; 37857e2f323SJérôme Duval r.right -= B_V_SCROLL_BAR_WIDTH; 37957e2f323SJérôme Duval r.bottom -= 25; 380*3ee96407SJérôme Duval fSoundList = new SoundListView(r, B_TRANSLATE("Sound List"), 381*3ee96407SJérôme Duval B_FOLLOW_ALL); 38257e2f323SJérôme Duval fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED)); 38357e2f323SJérôme Duval fSoundList->SetViewColor(216, 216, 216); 384ad0c65eaSJérôme Duval BScrollView *scroller = new BScrollView("scroller", fSoundList, 385ad0c65eaSJérôme Duval B_FOLLOW_ALL, 0, false, true, B_FANCY_BORDER); 38657e2f323SJérôme Duval fBottomBox->AddChild(scroller); 38757e2f323SJérôme Duval 38857e2f323SJérôme Duval r = fBottomBox->Bounds(); 38957e2f323SJérôme Duval r.right = r.left + 190; 39057e2f323SJérôme Duval r.bottom -= 25; 39157e2f323SJérôme Duval r.InsetBy(10, 8); 39257e2f323SJérôme Duval r.top -= 1; 39357e2f323SJérôme Duval fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); 394*3ee96407SJérôme Duval fFileInfoBox->SetLabel(B_TRANSLATE("File info")); 39557e2f323SJérôme Duval 39657e2f323SJérôme Duval r = fFileInfoBox->Bounds(); 39757e2f323SJérôme Duval r.left = 8; 39857e2f323SJérôme Duval r.top = 13; 39957e2f323SJérôme Duval r.bottom = r.top + 15; 40057e2f323SJérôme Duval r.right -= 10; 401*3ee96407SJérôme Duval fFilename = new BStringView(r, "filename", B_TRANSLATE("File name:")); 40257e2f323SJérôme Duval fFileInfoBox->AddChild(fFilename); 40357e2f323SJérôme Duval r.top += 13; 40457e2f323SJérôme Duval r.bottom = r.top + 15; 405*3ee96407SJérôme Duval fFormat = new BStringView(r, "format", B_TRANSLATE("Format:")); 40657e2f323SJérôme Duval fFileInfoBox->AddChild(fFormat); 40757e2f323SJérôme Duval r.top += 13; 40857e2f323SJérôme Duval r.bottom = r.top + 15; 409*3ee96407SJérôme Duval fCompression = new BStringView(r, "compression", 410*3ee96407SJérôme Duval B_TRANSLATE("Compression:")); 41157e2f323SJérôme Duval fFileInfoBox->AddChild(fCompression); 41257e2f323SJérôme Duval r.top += 13; 41357e2f323SJérôme Duval r.bottom = r.top + 15; 414*3ee96407SJérôme Duval fChannels = new BStringView(r, "channels", B_TRANSLATE("Channels:")); 41557e2f323SJérôme Duval fFileInfoBox->AddChild(fChannels); 41657e2f323SJérôme Duval r.top += 13; 41757e2f323SJérôme Duval r.bottom = r.top + 15; 418*3ee96407SJérôme Duval fSampleSize = new BStringView(r, "samplesize", 419*3ee96407SJérôme Duval B_TRANSLATE("Sample size:")); 42057e2f323SJérôme Duval fFileInfoBox->AddChild(fSampleSize); 42157e2f323SJérôme Duval r.top += 13; 42257e2f323SJérôme Duval r.bottom = r.top + 15; 423*3ee96407SJérôme Duval fSampleRate = new BStringView(r, "samplerate", 424*3ee96407SJérôme Duval B_TRANSLATE("Sample rate:")); 42557e2f323SJérôme Duval fFileInfoBox->AddChild(fSampleRate); 42657e2f323SJérôme Duval r.top += 13; 42757e2f323SJérôme Duval r.bottom = r.top + 15; 428*3ee96407SJérôme Duval fDuration = new BStringView(r, "duration", B_TRANSLATE("Duration:")); 42957e2f323SJérôme Duval fFileInfoBox->AddChild(fDuration); 43057e2f323SJérôme Duval 43157e2f323SJérôme Duval // Input selection lists all available physical inputs that produce 43257e2f323SJérôme Duval // buffers with B_MEDIA_RAW_AUDIO format data. 433*3ee96407SJérôme Duval popup = new BPopUpMenu(B_TRANSLATE("Input")); 434a8ee8b8fSStephan Aßmus const int maxInputCount = 64; 435a8ee8b8fSStephan Aßmus dormant_node_info dni[maxInputCount]; 43657e2f323SJérôme Duval 437a8ee8b8fSStephan Aßmus int32 real_count = maxInputCount; 43857e2f323SJérôme Duval media_format output_format; 43957e2f323SJérôme Duval output_format.type = B_MEDIA_RAW_AUDIO; 44057e2f323SJérôme Duval output_format.u.raw_audio = media_raw_audio_format::wildcard; 44157e2f323SJérôme Duval error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format, 44257e2f323SJérôme Duval 0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); 443a8ee8b8fSStephan Aßmus if (real_count > maxInputCount) { 444a8ee8b8fSStephan Aßmus WINDOW((stderr, "dropped %ld inputs\n", real_count - maxInputCount)); 445a8ee8b8fSStephan Aßmus real_count = maxInputCount; 44657e2f323SJérôme Duval } 4477974d3dcSStephan Aßmus char selected_name[B_MEDIA_NAME_LENGTH] = "Default input"; 44857e2f323SJérôme Duval BMessage * msg; 44957e2f323SJérôme Duval BMenuItem * item; 450ad0c65eaSJérôme Duval for (int i = 0; i < real_count; i++) { 45157e2f323SJérôme Duval msg = new BMessage(INPUT_SELECTED); 452ad0c65eaSJérôme Duval msg->AddData("node", B_RAW_TYPE, &dni[i], sizeof(dni[i])); 453ad0c65eaSJérôme Duval item = new BMenuItem(dni[i].name, msg); 45457e2f323SJérôme Duval popup->AddItem(item); 45557e2f323SJérôme Duval media_node_id ni[12]; 45657e2f323SJérôme Duval int32 ni_count = 12; 457ad0c65eaSJérôme Duval error = fRoster->GetInstancesFor(dni[i].addon, dni[i].flavor_id, 458ad0c65eaSJérôme Duval ni, &ni_count); 45957e2f323SJérôme Duval if (error == B_OK) 460ad0c65eaSJérôme Duval for (int j = 0; j < ni_count; j++) 461ad0c65eaSJérôme Duval if (ni[j] == fAudioInputNode.node) { 462ad0c65eaSJérôme Duval strcpy(selected_name, dni[i].name); 46357e2f323SJérôme Duval break; 46457e2f323SJérôme Duval } 46557e2f323SJérôme Duval } 46657e2f323SJérôme Duval 46757e2f323SJérôme Duval // Create the actual widget 46857e2f323SJérôme Duval BRect frame(fBottomBox->Bounds()); 46957e2f323SJérôme Duval r = frame; 47057e2f323SJérôme Duval r.left = 42; 47157e2f323SJérôme Duval r.right = (r.left + r.right) / 2; 47257e2f323SJérôme Duval r.InsetBy(10,10); 47357e2f323SJérôme Duval r.top = r.bottom - 18; 474*3ee96407SJérôme Duval fInputField = new BMenuField(r, "Input", B_TRANSLATE("Input:"), popup); 475*3ee96407SJérôme Duval fInputField->SetDivider(fInputField->StringWidth(B_TRANSLATE("Input:")) + 4.0f); 47657e2f323SJérôme Duval fBottomBox->AddChild(fInputField); 47757e2f323SJérôme Duval 47857e2f323SJérôme Duval fBottomBox->AddChild(fFileInfoBox); 47957e2f323SJérôme Duval 48057e2f323SJérôme Duval fBottomBox->Hide(); 48157e2f323SJérôme Duval CalcSizes(Frame().Width(), MIN_HEIGHT-161); 48257e2f323SJérôme Duval ResizeTo(Frame().Width(), MIN_HEIGHT-161); 48357e2f323SJérôme Duval 48457e2f323SJérôme Duval 48557e2f323SJérôme Duval popup->Superitem()->SetLabel(selected_name); 48657e2f323SJérôme Duval 48757e2f323SJérôme Duval // Make sure the save panel is happy. 4885fa77532SJérôme Duval fSavePanel = new BFilePanel(B_SAVE_PANEL); 4895fa77532SJérôme Duval fSavePanel->SetTarget(this); 49057e2f323SJérôme Duval } 49157e2f323SJérôme Duval catch (...) { 49257e2f323SJérôme Duval goto bad_mojo; 49357e2f323SJérôme Duval } 49457e2f323SJérôme Duval UpdateButtons(); 49557e2f323SJérôme Duval return B_OK; 49657e2f323SJérôme Duval 49757e2f323SJérôme Duval // Error handling. 49857e2f323SJérôme Duval bad_mojo: 49957e2f323SJérôme Duval if (error >= 0) { 50057e2f323SJérôme Duval error = B_ERROR; 50157e2f323SJérôme Duval } 50257e2f323SJérôme Duval if (fRecordNode) { 50357e2f323SJérôme Duval fRecordNode->Release(); 50457e2f323SJérôme Duval } 50557e2f323SJérôme Duval 50657e2f323SJérôme Duval delete fPlayer; 50757e2f323SJérôme Duval if (!fInputField) { 50857e2f323SJérôme Duval delete popup; 50957e2f323SJérôme Duval } 51057e2f323SJérôme Duval return error; 51157e2f323SJérôme Duval } 51257e2f323SJérôme Duval 51357e2f323SJérôme Duval 51457e2f323SJérôme Duval bool 51557e2f323SJérôme Duval RecorderWindow::QuitRequested() // this means Close pressed 51657e2f323SJérôme Duval { 51757e2f323SJérôme Duval StopRecording(); 51857e2f323SJérôme Duval StopPlaying(); 51957e2f323SJérôme Duval be_app->PostMessage(B_QUIT_REQUESTED); 52057e2f323SJérôme Duval return true; 52157e2f323SJérôme Duval } 52257e2f323SJérôme Duval 52357e2f323SJérôme Duval 52457e2f323SJérôme Duval void 52557e2f323SJérôme Duval RecorderWindow::MessageReceived(BMessage * message) 52657e2f323SJérôme Duval { 52757e2f323SJérôme Duval // Your average generic message dispatching switch() statement. 52857e2f323SJérôme Duval switch (message->what) { 52957e2f323SJérôme Duval case INPUT_SELECTED: 53057e2f323SJérôme Duval Input(message); 53157e2f323SJérôme Duval break; 53257e2f323SJérôme Duval case SOUND_SELECTED: 53357e2f323SJérôme Duval Selected(message); 53457e2f323SJérôme Duval break; 53557e2f323SJérôme Duval case STOP_PLAYING: 53657e2f323SJérôme Duval StopPlaying(); 53757e2f323SJérôme Duval break; 53857e2f323SJérôme Duval case STOP_RECORDING: 53957e2f323SJérôme Duval StopRecording(); 54057e2f323SJérôme Duval break; 54157e2f323SJérôme Duval case PLAY_PERIOD: 54257e2f323SJérôme Duval if (fPlayer) { 54357e2f323SJérôme Duval if (fPlayer->HasData()) 54457e2f323SJérôme Duval fPlayButton->SetPlaying(); 54557e2f323SJérôme Duval else 54657e2f323SJérôme Duval fPlayButton->SetPaused(); 54757e2f323SJérôme Duval } 54857e2f323SJérôme Duval break; 54957e2f323SJérôme Duval case RECORD_PERIOD: 55057e2f323SJérôme Duval fRecordButton->SetRecording(); 55157e2f323SJérôme Duval break; 55257e2f323SJérôme Duval case RECORD: 55357e2f323SJérôme Duval Record(message); 55457e2f323SJérôme Duval break; 55557e2f323SJérôme Duval case STOP: 55657e2f323SJérôme Duval Stop(message); 55757e2f323SJérôme Duval break; 55857e2f323SJérôme Duval case PLAY: 55957e2f323SJérôme Duval Play(message); 56057e2f323SJérôme Duval break; 56157e2f323SJérôme Duval case SAVE: 56257e2f323SJérôme Duval Save(message); 56357e2f323SJérôme Duval break; 56457e2f323SJérôme Duval case B_SAVE_REQUESTED: 56557e2f323SJérôme Duval DoSave(message); 56657e2f323SJérôme Duval break; 56757e2f323SJérôme Duval case VIEW_LIST: 56857e2f323SJérôme Duval if (fUpDownButton->Value() == B_CONTROL_ON) { 56957e2f323SJérôme Duval fBottomBox->Show(); 57057e2f323SJérôme Duval CalcSizes(Frame().Width(), MIN_HEIGHT); 57157e2f323SJérôme Duval ResizeTo(Frame().Width(), MIN_HEIGHT); 57257e2f323SJérôme Duval } else { 57357e2f323SJérôme Duval fBottomBox->Hide(); 57457e2f323SJérôme Duval CalcSizes(Frame().Width(), MIN_HEIGHT-161); 57557e2f323SJérôme Duval ResizeTo(Frame().Width(), MIN_HEIGHT-161); 57657e2f323SJérôme Duval 57757e2f323SJérôme Duval } 57857e2f323SJérôme Duval break; 57957e2f323SJérôme Duval case UPDATE_TRACKSLIDER: 58057e2f323SJérôme Duval { 58157e2f323SJérôme Duval bigtime_t timestamp = fPlayTrack->CurrentTime(); 58257e2f323SJérôme Duval fTrackSlider->SetMainTime(timestamp, false); 58357e2f323SJérôme Duval fScopeView->SetMainTime(timestamp); 58457e2f323SJérôme Duval } 58557e2f323SJérôme Duval break; 58657e2f323SJérôme Duval case POSITION_CHANGED: 58757e2f323SJérôme Duval { 58857e2f323SJérôme Duval bigtime_t right, left, main; 58957e2f323SJérôme Duval if (message->FindInt64("main", &main) == B_OK) { 59057e2f323SJérôme Duval if (fPlayTrack) { 59157e2f323SJérôme Duval fPlayTrack->SeekToTime(fTrackSlider->MainTime()); 59257e2f323SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 59357e2f323SJérôme Duval } 59457e2f323SJérôme Duval fScopeView->SetMainTime(main); 59557e2f323SJérôme Duval } 59657e2f323SJérôme Duval if (message->FindInt64("right", &right) == B_OK) { 59757e2f323SJérôme Duval if (fPlayTrack) 598ad0c65eaSJérôme Duval fPlayLimit = MIN(fPlayFrames, 599ad0c65eaSJérôme Duval (off_t)(right * fPlayFormat.u.raw_audio.frame_rate/1000000LL)); 60057e2f323SJérôme Duval fScopeView->SetRightTime(right); 60157e2f323SJérôme Duval } 60257e2f323SJérôme Duval if (message->FindInt64("left", &left) == B_OK) { 60357e2f323SJérôme Duval fScopeView->SetLeftTime(left); 60457e2f323SJérôme Duval } 60557e2f323SJérôme Duval } 60657e2f323SJérôme Duval break; 607948356deSJérôme Duval case LOOP: 608948356deSJérôme Duval fLooping = fLoopButton->ButtonState(); 609948356deSJérôme Duval break; 61057e2f323SJérôme Duval case B_SIMPLE_DATA: 61157e2f323SJérôme Duval case B_REFS_RECEIVED: 61257e2f323SJérôme Duval { 61357e2f323SJérôme Duval RefsReceived(message); 61457e2f323SJérôme Duval break; 61557e2f323SJérôme Duval } 616019ed09bSJérôme Duval case B_COPY_TARGET: 617019ed09bSJérôme Duval CopyTarget(message); 618019ed09bSJérôme Duval break; 61957e2f323SJérôme Duval default: 62057e2f323SJérôme Duval BWindow::MessageReceived(message); 62157e2f323SJérôme Duval break; 62257e2f323SJérôme Duval } 62357e2f323SJérôme Duval } 62457e2f323SJérôme Duval 62557e2f323SJérôme Duval 62657e2f323SJérôme Duval void 62757e2f323SJérôme Duval RecorderWindow::Record(BMessage * message) 62857e2f323SJérôme Duval { 62957e2f323SJérôme Duval // User pressed Record button 63057e2f323SJérôme Duval fRecording = true; 63157e2f323SJérôme Duval if (fButtonState != btnPaused) { 63257e2f323SJérôme Duval StopRecording(); 63357e2f323SJérôme Duval return; // user is too fast on the mouse 63457e2f323SJérôme Duval } 63557e2f323SJérôme Duval SetButtonState(btnRecording); 63657e2f323SJérôme Duval fRecordButton->SetRecording(); 63757e2f323SJérôme Duval 63857e2f323SJérôme Duval char name[256]; 63957e2f323SJérôme Duval // Create a file with a temporary name 64057e2f323SJérôme Duval status_t err = NewTempName(name); 64157e2f323SJérôme Duval if (err < B_OK) { 642*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot find an unused name to use for the " 643*3ee96407SJérôme Duval "new recording"), err); 64457e2f323SJérôme Duval return; 64557e2f323SJérôme Duval } 64657e2f323SJérôme Duval // Find the file so we can refer to it later 64757e2f323SJérôme Duval err = fTempDir.FindEntry(name, &fRecEntry); 64857e2f323SJérôme Duval if (err < B_OK) { 649*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot find the temporary file created to " 650*3ee96407SJérôme Duval "hold the new recording"), err); 65157e2f323SJérôme Duval return; 65257e2f323SJérôme Duval } 65357e2f323SJérôme Duval err = fRecFile.SetTo(&fTempDir, name, O_RDWR); 65457e2f323SJérôme Duval if (err < B_OK) { 655*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot open the temporary file created to " 656*3ee96407SJérôme Duval "hold the new recording"), err); 65757e2f323SJérôme Duval fRecEntry.Unset(); 65857e2f323SJérôme Duval return; 65957e2f323SJérôme Duval } 66057e2f323SJérôme Duval // Reserve space on disk (creates fewer fragments) 66107db0c6fSJérôme Duval err = fRecFile.SetSize(4 * fRecordFormat.u.raw_audio.channel_count 662*3ee96407SJérôme Duval * fRecordFormat.u.raw_audio.frame_rate 663*3ee96407SJérôme Duval * (fRecordFormat.u.raw_audio.format 664ad0c65eaSJérôme Duval & media_raw_audio_format::B_AUDIO_SIZE_MASK)); 66557e2f323SJérôme Duval if (err < B_OK) { 666*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot record a sound that long"), err); 66757e2f323SJérôme Duval fRecEntry.Remove(); 66857e2f323SJérôme Duval fRecEntry.Unset(); 66957e2f323SJérôme Duval return; 67057e2f323SJérôme Duval } 67157e2f323SJérôme Duval fRecSize = 0; 67257e2f323SJérôme Duval 67357e2f323SJérôme Duval fRecFile.Seek(sizeof(struct wave_struct), SEEK_SET); 67457e2f323SJérôme Duval 67557e2f323SJérôme Duval // Hook up input 67657e2f323SJérôme Duval err = MakeRecordConnection(fAudioInputNode); 67757e2f323SJérôme Duval if (err < B_OK) { 678*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot connect to the selected sound input"), 679*3ee96407SJérôme Duval err); 68057e2f323SJérôme Duval fRecEntry.Remove(); 68157e2f323SJérôme Duval fRecEntry.Unset(); 68257e2f323SJérôme Duval return; 68357e2f323SJérôme Duval } 68457e2f323SJérôme Duval 68557e2f323SJérôme Duval // And get it going... 68657e2f323SJérôme Duval bigtime_t then = fRecordNode->TimeSource()->Now() + 50000LL; 68757e2f323SJérôme Duval fRoster->StartNode(fRecordNode->Node(), then); 68857e2f323SJérôme Duval if (fAudioInputNode.kind & B_TIME_SOURCE) { 689ad0c65eaSJérôme Duval fRoster->StartNode(fAudioInputNode, 690ad0c65eaSJérôme Duval fRecordNode->TimeSource()->RealTimeFor(then, 0)); 69157e2f323SJérôme Duval } 69257e2f323SJérôme Duval else { 69357e2f323SJérôme Duval fRoster->StartNode(fAudioInputNode, then); 69457e2f323SJérôme Duval } 69557e2f323SJérôme Duval } 69657e2f323SJérôme Duval 6978e32a27eSPhilippe Saint-Pierre 69857e2f323SJérôme Duval void 69957e2f323SJérôme Duval RecorderWindow::Play(BMessage * message) 70057e2f323SJérôme Duval { 70157e2f323SJérôme Duval if (fPlayer) { 70257e2f323SJérôme Duval // User pressed Play button and playing 70357e2f323SJérôme Duval if (fPlayer->HasData()) 70457e2f323SJérôme Duval fPlayButton->SetPaused(); 70557e2f323SJérôme Duval else 70657e2f323SJérôme Duval fPlayButton->SetPlaying(); 70757e2f323SJérôme Duval fPlayer->SetHasData(!fPlayer->HasData()); 70857e2f323SJérôme Duval return; 70957e2f323SJérôme Duval } 71057e2f323SJérôme Duval 71157e2f323SJérôme Duval SetButtonState(btnPlaying); 71257e2f323SJérôme Duval fPlayButton->SetPlaying(); 71357e2f323SJérôme Duval 71457e2f323SJérôme Duval if (!fPlayTrack) { 715*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot get the file to play"), B_ERROR); 71657e2f323SJérôme Duval return; 71757e2f323SJérôme Duval } 71857e2f323SJérôme Duval 719ad0c65eaSJérôme Duval fPlayLimit = MIN(fPlayFrames, (off_t)(fTrackSlider->RightTime() 720ad0c65eaSJérôme Duval * fPlayFormat.u.raw_audio.frame_rate / 1000000LL)); 72157e2f323SJérôme Duval fPlayTrack->SeekToTime(fTrackSlider->MainTime()); 72257e2f323SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 72357e2f323SJérôme Duval 72457e2f323SJérôme Duval // Create our internal Node which plays sound, and register it. 725*3ee96407SJérôme Duval fPlayer = new BSoundPlayer(fAudioMixerNode, &fPlayFormat.u.raw_audio, 726*3ee96407SJérôme Duval "Sound Player"); 72757e2f323SJérôme Duval status_t err = fPlayer->InitCheck(); 72857e2f323SJérôme Duval if (err < B_OK) { 72957e2f323SJérôme Duval return; 73057e2f323SJérôme Duval } 73157e2f323SJérôme Duval 73257e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(fPlayer); 73357e2f323SJérôme Duval fPlayer->SetCallbacks(PlayFile, NotifyPlayFile, this); 73457e2f323SJérôme Duval 73557e2f323SJérôme Duval // And get it going... 73657e2f323SJérôme Duval fPlayer->Start(); 73757e2f323SJérôme Duval fPlayer->SetHasData(true); 73857e2f323SJérôme Duval } 73957e2f323SJérôme Duval 7408e32a27eSPhilippe Saint-Pierre 74157e2f323SJérôme Duval void 74257e2f323SJérôme Duval RecorderWindow::Stop(BMessage * message) 74357e2f323SJérôme Duval { 74457e2f323SJérôme Duval // User pressed Stop button. 74557e2f323SJérôme Duval // Stop recorder. 74657e2f323SJérôme Duval StopRecording(); 74757e2f323SJérôme Duval // Stop player. 74857e2f323SJérôme Duval StopPlaying(); 74957e2f323SJérôme Duval } 75057e2f323SJérôme Duval 7518e32a27eSPhilippe Saint-Pierre 75257e2f323SJérôme Duval void 75357e2f323SJérôme Duval RecorderWindow::Save(BMessage * message) 75457e2f323SJérôme Duval { 75557e2f323SJérôme Duval // User pressed Save button. 75657e2f323SJérôme Duval // Find the item to save. 75757e2f323SJérôme Duval int32 index = fSoundList->CurrentSelection(); 75857e2f323SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(index)); 75957e2f323SJérôme Duval if ((! pItem) || (pItem->Entry().InitCheck() != B_OK)) { 76057e2f323SJérôme Duval return; 76157e2f323SJérôme Duval } 76257e2f323SJérôme Duval 76357e2f323SJérôme Duval // Update the save panel and show it. 76457e2f323SJérôme Duval char filename[B_FILE_NAME_LENGTH]; 76557e2f323SJérôme Duval pItem->Entry().GetName(filename); 76657e2f323SJérôme Duval BMessage saveMsg(B_SAVE_REQUESTED); 76757e2f323SJérôme Duval entry_ref ref; 76857e2f323SJérôme Duval pItem->Entry().GetRef(&ref); 76957e2f323SJérôme Duval 770a4797804SJérôme Duval if (saveMsg.AddPointer("sound list item", pItem) != B_OK) 771a4797804SJérôme Duval fprintf(stderr, "failed to add pItem\n"); 7725fa77532SJérôme Duval fSavePanel->SetSaveText(filename); 7735fa77532SJérôme Duval fSavePanel->SetMessage(&saveMsg); 7745fa77532SJérôme Duval fSavePanel->Show(); 77557e2f323SJérôme Duval } 77657e2f323SJérôme Duval 7778e32a27eSPhilippe Saint-Pierre 77857e2f323SJérôme Duval void 77957e2f323SJérôme Duval RecorderWindow::DoSave(BMessage * message) 78057e2f323SJérôme Duval { 78157e2f323SJérôme Duval // User picked a place to put the file. 78257e2f323SJérôme Duval // Find the location of the old (e.g. 78357e2f323SJérôme Duval // temporary file), and the name of the 78457e2f323SJérôme Duval // new file to save. 78557e2f323SJérôme Duval entry_ref old_ref, new_dir_ref; 78657e2f323SJérôme Duval const char* new_name; 78757e2f323SJérôme Duval SoundListItem* pItem; 78857e2f323SJérôme Duval 78957e2f323SJérôme Duval if ((message->FindPointer("sound list item", (void**) &pItem) == B_OK) 79057e2f323SJérôme Duval && (message->FindRef("directory", &new_dir_ref) == B_OK) 79157e2f323SJérôme Duval && (message->FindString("name", &new_name) == B_OK)) 79257e2f323SJérôme Duval { 79357e2f323SJérôme Duval BEntry& oldEntry = pItem->Entry(); 79457e2f323SJérôme Duval BFile oldFile(&oldEntry, B_READ_WRITE); 79557e2f323SJérôme Duval if (oldFile.InitCheck() != B_OK) 79657e2f323SJérôme Duval return; 79757e2f323SJérôme Duval 79857e2f323SJérôme Duval BDirectory newDir(&new_dir_ref); 79957e2f323SJérôme Duval if (newDir.InitCheck() != B_OK) 80057e2f323SJérôme Duval return; 80157e2f323SJérôme Duval 80257e2f323SJérôme Duval BFile newFile; 80357e2f323SJérôme Duval newDir.CreateFile(new_name, &newFile); 80457e2f323SJérôme Duval 80557e2f323SJérôme Duval if (newFile.InitCheck() != B_OK) 80657e2f323SJérôme Duval return; 80757e2f323SJérôme Duval 80857e2f323SJérôme Duval status_t err = CopyFile(newFile, oldFile); 80957e2f323SJérôme Duval 81057e2f323SJérôme Duval if (err == B_OK) { 81157e2f323SJérôme Duval // clean up the sound list and item 81257e2f323SJérôme Duval if (pItem->IsTemp()) 81357e2f323SJérôme Duval oldEntry.Remove(); // blows away temp file! 81457e2f323SJérôme Duval oldEntry.SetTo(&newDir, new_name); 81557e2f323SJérôme Duval pItem->SetTemp(false); // don't blow the new entry away when we exit! 81657e2f323SJérôme Duval fSoundList->Invalidate(); 81757e2f323SJérôme Duval } 81857e2f323SJérôme Duval } else { 81957e2f323SJérôme Duval WINDOW((stderr, "Couldn't save file.\n")); 82057e2f323SJérôme Duval } 82157e2f323SJérôme Duval } 82257e2f323SJérôme Duval 82357e2f323SJérôme Duval 82457e2f323SJérôme Duval void 82557e2f323SJérôme Duval RecorderWindow::Input(BMessage * message) 82657e2f323SJérôme Duval { 82757e2f323SJérôme Duval // User selected input from pop-up 82857e2f323SJérôme Duval const dormant_node_info * dni = 0; 82957e2f323SJérôme Duval ssize_t size = 0; 83057e2f323SJérôme Duval if (message->FindData("node", B_RAW_TYPE, (const void **)&dni, &size)) { 83157e2f323SJérôme Duval return; // bad input selection message 83257e2f323SJérôme Duval } 83357e2f323SJérôme Duval 83457e2f323SJérôme Duval media_node_id node_id; 83557e2f323SJérôme Duval status_t error = fRoster->GetInstancesFor(dni->addon, dni->flavor_id, &node_id); 83657e2f323SJérôme Duval if (error != B_OK) { 83757e2f323SJérôme Duval fRoster->InstantiateDormantNode(*dni, &fAudioInputNode); 83857e2f323SJérôme Duval } else { 83957e2f323SJérôme Duval fRoster->GetNodeFor(node_id, &fAudioInputNode); 84057e2f323SJérôme Duval } 84157e2f323SJérôme Duval } 84257e2f323SJérôme Duval 8438e32a27eSPhilippe Saint-Pierre 84457e2f323SJérôme Duval void 84557e2f323SJérôme Duval RecorderWindow::Selected(BMessage * message) 84657e2f323SJérôme Duval { 84757e2f323SJérôme Duval // User selected a sound in list view 848c6f8aa29SJérôme Duval int32 selIdx = fSoundList->CurrentSelection(); 849c6f8aa29SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(selIdx)); 850c6f8aa29SJérôme Duval if (!pItem) 851c6f8aa29SJérôme Duval return; 852c6f8aa29SJérôme Duval status_t err = UpdatePlayFile(pItem, true); 853c6f8aa29SJérôme Duval if (err != B_OK) { 854*3ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot recognize this file as a media file"), 855ad0c65eaSJérôme Duval err == B_MEDIA_NO_HANDLER ? B_OK : err); 856c6f8aa29SJérôme Duval RemoveCurrentSoundItem(); 857c6f8aa29SJérôme Duval } 85857e2f323SJérôme Duval UpdateButtons(); 85957e2f323SJérôme Duval } 86057e2f323SJérôme Duval 8618e32a27eSPhilippe Saint-Pierre 86257e2f323SJérôme Duval status_t 86357e2f323SJérôme Duval RecorderWindow::MakeRecordConnection(const media_node & input) 86457e2f323SJérôme Duval { 86557e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n")); 86657e2f323SJérôme Duval 86757e2f323SJérôme Duval // Find an available output for the given input node. 86857e2f323SJérôme Duval int32 count = 0; 86957e2f323SJérôme Duval status_t err = fRoster->GetFreeOutputsFor(input, &fAudioOutput, 1, &count, B_MEDIA_RAW_AUDIO); 87057e2f323SJérôme Duval if (err < B_OK) { 871ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 872ad0c65eaSJérôme Duval " couldn't get free outputs from audio input node\n")); 87357e2f323SJérôme Duval return err; 87457e2f323SJérôme Duval } 87557e2f323SJérôme Duval if (count < 1) { 876ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 877ad0c65eaSJérôme Duval " no free outputs from audio input node\n")); 87857e2f323SJérôme Duval return B_BUSY; 87957e2f323SJérôme Duval } 88057e2f323SJérôme Duval 88157e2f323SJérôme Duval // Find an available input for our own Node. Note that we go through the 88257e2f323SJérôme Duval // MediaRoster; calling Media Kit methods directly on Nodes in our app is 88357e2f323SJérôme Duval // not OK (because synchronization happens in the service thread, not in 88457e2f323SJérôme Duval // the calling thread). 88557e2f323SJérôme Duval // TODO: explain this 88657e2f323SJérôme Duval err = fRoster->GetFreeInputsFor(fRecordNode->Node(), &fRecInput, 1, &count, B_MEDIA_RAW_AUDIO); 88757e2f323SJérôme Duval if (err < B_OK) { 888ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 889ad0c65eaSJérôme Duval " couldn't get free inputs for sound recorder\n")); 89057e2f323SJérôme Duval return err; 89157e2f323SJérôme Duval } 89257e2f323SJérôme Duval if (count < 1) { 893ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 894ad0c65eaSJérôme Duval " no free inputs for sound recorder\n")); 89557e2f323SJérôme Duval return B_BUSY; 89657e2f323SJérôme Duval } 89757e2f323SJérôme Duval 89857e2f323SJérôme Duval // Find out what the time source of the input is. 89957e2f323SJérôme Duval // For most nodes, we just use the preferred time source (the DAC) for synchronization. 90057e2f323SJérôme Duval // However, nodes that record from an input need to synchronize to the audio input node 90157e2f323SJérôme Duval // instead for best results. 90257e2f323SJérôme Duval // MakeTimeSourceFor gives us a "clone" of the time source node that we can manipulate 90357e2f323SJérôme Duval // to our heart's content. When we're done with it, though, we need to call Release() 90457e2f323SJérôme Duval // on the time source node, so that it keeps an accurate reference count and can delete 90557e2f323SJérôme Duval // itself when it's no longer needed. 90657e2f323SJérôme Duval // TODO: what about filters connected to audio input? 90757e2f323SJérôme Duval media_node use_time_source; 90857e2f323SJérôme Duval BTimeSource * tsobj = fRoster->MakeTimeSourceFor(input); 90957e2f323SJérôme Duval if (! tsobj) { 910ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 911ad0c65eaSJérôme Duval " couldn't clone time source from audio input node\n")); 91257e2f323SJérôme Duval return B_MEDIA_BAD_NODE; 91357e2f323SJérôme Duval } 91457e2f323SJérôme Duval 91557e2f323SJérôme Duval // Apply the time source in effect to our own Node. 91657e2f323SJérôme Duval err = fRoster->SetTimeSourceFor(fRecordNode->Node().node, tsobj->Node().node); 91757e2f323SJérôme Duval if (err < B_OK) { 918ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 919ad0c65eaSJérôme Duval " couldn't set the sound recorder's time source\n")); 92057e2f323SJérôme Duval tsobj->Release(); 92157e2f323SJérôme Duval return err; 92257e2f323SJérôme Duval } 92357e2f323SJérôme Duval 92457e2f323SJérôme Duval // Get a format, any format. 92588fef303SJérôme Duval fRecordFormat.u.raw_audio = fAudioOutput.format.u.raw_audio; 92688fef303SJérôme Duval fRecordFormat.type = B_MEDIA_RAW_AUDIO; 92757e2f323SJérôme Duval 92857e2f323SJérôme Duval // Tell the consumer where we want data to go. 92957e2f323SJérôme Duval err = fRecordNode->SetHooks(RecordFile, NotifyRecordFile, this); 93057e2f323SJérôme Duval if (err < B_OK) { 931ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 932ad0c65eaSJérôme Duval " couldn't set the sound recorder's hook functions\n")); 93357e2f323SJérôme Duval tsobj->Release(); 93457e2f323SJérôme Duval return err; 93557e2f323SJérôme Duval } 93657e2f323SJérôme Duval 937*3ee96407SJérôme Duval // Using the same structs for input and output is OK in 938*3ee96407SJérôme Duval // BMediaRoster::Connect(). 939*3ee96407SJérôme Duval err = fRoster->Connect(fAudioOutput.source, fRecInput.destination, 940*3ee96407SJérôme Duval &fRecordFormat, &fAudioOutput, &fRecInput); 94157e2f323SJérôme Duval if (err < B_OK) { 942ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 943ad0c65eaSJérôme Duval " failed to connect sound recorder to audio input node.\n")); 94457e2f323SJérôme Duval tsobj->Release(); 94557e2f323SJérôme Duval fRecordNode->SetHooks(0, 0, 0); 94657e2f323SJérôme Duval return err; 94757e2f323SJérôme Duval } 94857e2f323SJérôme Duval 94957e2f323SJérôme Duval // Start the time source if it's not running. 95057e2f323SJérôme Duval if ((tsobj->Node() != input) && !tsobj->IsRunning()) { 95157e2f323SJérôme Duval fRoster->StartNode(tsobj->Node(), BTimeSource::RealTime()); 95257e2f323SJérôme Duval } 95357e2f323SJérôme Duval tsobj->Release(); // we're done with this time source instance! 95457e2f323SJérôme Duval return B_OK; 95557e2f323SJérôme Duval } 95657e2f323SJérôme Duval 95757e2f323SJérôme Duval 95857e2f323SJérôme Duval status_t 95957e2f323SJérôme Duval RecorderWindow::BreakRecordConnection() 96057e2f323SJérôme Duval { 96157e2f323SJérôme Duval status_t err; 96257e2f323SJérôme Duval 96357e2f323SJérôme Duval // If we are the last connection, the Node will stop automatically since it 96457e2f323SJérôme Duval // has nowhere to send data to. 96557e2f323SJérôme Duval err = fRoster->StopNode(fRecInput.node, 0); 966ad0c65eaSJérôme Duval err = fRoster->Disconnect(fAudioOutput.node.node, fAudioOutput.source, 967ad0c65eaSJérôme Duval fRecInput.node.node, fRecInput.destination); 96857e2f323SJérôme Duval fAudioOutput.source = media_source::null; 96957e2f323SJérôme Duval fRecInput.destination = media_destination::null; 97057e2f323SJérôme Duval return err; 97157e2f323SJérôme Duval } 97257e2f323SJérôme Duval 9738e32a27eSPhilippe Saint-Pierre 97457e2f323SJérôme Duval status_t 97557e2f323SJérôme Duval RecorderWindow::StopRecording() 97657e2f323SJérôme Duval { 97757e2f323SJérôme Duval if (!fRecording) 97857e2f323SJérôme Duval return B_OK; 97957e2f323SJérôme Duval fRecording = false; 98057e2f323SJérôme Duval BreakRecordConnection(); 98157e2f323SJérôme Duval fRecordNode->SetHooks(NULL,NULL,NULL); 98257e2f323SJérôme Duval if (fRecSize > 0) { 98357e2f323SJérôme Duval 98457e2f323SJérôme Duval wave_struct header; 98557e2f323SJérôme Duval header.riff.riff_id = FOURCC('R','I','F','F'); 986a4797804SJérôme Duval header.riff.len = fRecSize + sizeof(header) - 8; 98757e2f323SJérôme Duval header.riff.wave_id = FOURCC('W','A','V','E'); 98857e2f323SJérôme Duval header.format_chunk.fourcc = FOURCC('f','m','t',' '); 98957e2f323SJérôme Duval header.format_chunk.len = sizeof(header.format); 99057e2f323SJérôme Duval header.format.format_tag = 1; 99188fef303SJérôme Duval header.format.channels = fRecordFormat.u.raw_audio.channel_count; 99288fef303SJérôme Duval header.format.samples_per_sec = (uint32)fRecordFormat.u.raw_audio.frame_rate; 99388fef303SJérôme Duval header.format.avg_bytes_per_sec = (uint32)(fRecordFormat.u.raw_audio.frame_rate 99488fef303SJérôme Duval * fRecordFormat.u.raw_audio.channel_count 99588fef303SJérôme Duval * (fRecordFormat.u.raw_audio.format & 0xf)); 99688fef303SJérôme Duval header.format.bits_per_sample = (fRecordFormat.u.raw_audio.format & 0xf) * 8; 99788fef303SJérôme Duval header.format.block_align = (fRecordFormat.u.raw_audio.format & 0xf) 99888fef303SJérôme Duval * fRecordFormat.u.raw_audio.channel_count; 99957e2f323SJérôme Duval header.data_chunk.fourcc = FOURCC('d','a','t','a'); 100057e2f323SJérôme Duval header.data_chunk.len = fRecSize; 100157e2f323SJérôme Duval fRecFile.Seek(0, SEEK_SET); 100257e2f323SJérôme Duval fRecFile.Write(&header, sizeof(header)); 100357e2f323SJérôme Duval 1004*3ee96407SJérôme Duval fRecFile.SetSize(fRecSize + sizeof(header)); 1005*3ee96407SJérôme Duval // We reserve space; make sure we cut off any excess at the end. 100657e2f323SJérôme Duval AddSoundItem(fRecEntry, true); 100757e2f323SJérôme Duval } 100857e2f323SJérôme Duval else { 100957e2f323SJérôme Duval fRecEntry.Remove(); 101057e2f323SJérôme Duval } 101157e2f323SJérôme Duval // We're done for this time. 101257e2f323SJérôme Duval fRecEntry.Unset(); 101357e2f323SJérôme Duval // Close the file. 101457e2f323SJérôme Duval fRecFile.Unset(); 101557e2f323SJérôme Duval // No more recording going on. 101657e2f323SJérôme Duval fRecSize = 0; 101757e2f323SJérôme Duval SetButtonState(btnPaused); 101857e2f323SJérôme Duval fRecordButton->SetStopped(); 101957e2f323SJérôme Duval 102057e2f323SJérôme Duval return B_OK; 102157e2f323SJérôme Duval } 102257e2f323SJérôme Duval 102357e2f323SJérôme Duval 102457e2f323SJérôme Duval status_t 102557e2f323SJérôme Duval RecorderWindow::StopPlaying() 102657e2f323SJérôme Duval { 102757e2f323SJérôme Duval if (fPlayer) { 102857e2f323SJérôme Duval fPlayer->Stop(); 102957e2f323SJérôme Duval fPlayer->SetCallbacks(0, 0, 0); 103057e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(NULL); 103157e2f323SJérôme Duval delete fPlayer; 103257e2f323SJérôme Duval fPlayer = NULL; 103357e2f323SJérôme Duval } 103457e2f323SJérôme Duval SetButtonState(btnPaused); 103557e2f323SJérôme Duval fPlayButton->SetStopped(); 103657e2f323SJérôme Duval fTrackSlider->ResetMainTime(); 103757e2f323SJérôme Duval fScopeView->SetMainTime(*fTrackSlider->MainTime()); 103857e2f323SJérôme Duval return B_OK; 103957e2f323SJérôme Duval } 104057e2f323SJérôme Duval 104157e2f323SJérôme Duval 104257e2f323SJérôme Duval void 104357e2f323SJérôme Duval RecorderWindow::SetButtonState(BtnState state) 104457e2f323SJérôme Duval { 104557e2f323SJérôme Duval fButtonState = state; 104657e2f323SJérôme Duval UpdateButtons(); 104757e2f323SJérôme Duval } 104857e2f323SJérôme Duval 104957e2f323SJérôme Duval 105057e2f323SJérôme Duval void 105157e2f323SJérôme Duval RecorderWindow::UpdateButtons() 105257e2f323SJérôme Duval { 105357e2f323SJérôme Duval bool hasSelection = (fSoundList->CurrentSelection() >= 0); 105457e2f323SJérôme Duval fRecordButton->SetEnabled(fButtonState != btnPlaying); 105557e2f323SJérôme Duval fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 105657e2f323SJérôme Duval fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 105757e2f323SJérôme Duval fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 105857e2f323SJérôme Duval fStopButton->SetEnabled(fButtonState != btnPaused); 105957e2f323SJérôme Duval fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording)); 106057e2f323SJérôme Duval fInputField->SetEnabled(fButtonState != btnRecording); 106157e2f323SJérôme Duval } 106257e2f323SJérôme Duval 10635fa77532SJérôme Duval #ifndef __HAIKU__ 1064*3ee96407SJérôme Duval extern "C" status_t DecodedFormat__11BMediaTrackP12media_format( 1065*3ee96407SJérôme Duval BMediaTrack *self, media_format *inout_format); 10665fa77532SJérôme Duval #endif 106757e2f323SJérôme Duval 10688e32a27eSPhilippe Saint-Pierre 1069c6f8aa29SJérôme Duval status_t 1070c6f8aa29SJérôme Duval RecorderWindow::UpdatePlayFile(SoundListItem* item, bool updateDisplay) 107157e2f323SJérôme Duval { 1072019ed09bSJérôme Duval fScopeView->CancelRendering(); 1073019ed09bSJérôme Duval StopPlaying(); 1074019ed09bSJérôme Duval StopRecording(); 1075019ed09bSJérôme Duval 1076dbbc34b6SJérôme Duval if (fPlayTrack && fPlayFile) { 10777942339dSJérôme Duval fPlayFile->ReleaseTrack(fPlayTrack); 10787942339dSJérôme Duval fPlayTrack = NULL; 1079dbbc34b6SJérôme Duval } 1080dbbc34b6SJérôme Duval if (fPlayFile) { 1081dbbc34b6SJérôme Duval delete fPlayFile; 10827942339dSJérôme Duval fPlayFile = NULL; 1083dbbc34b6SJérôme Duval } 10847942339dSJérôme Duval 108557e2f323SJérôme Duval status_t err; 1086c6f8aa29SJérôme Duval BEntry& entry = item->Entry(); 108757e2f323SJérôme Duval entry_ref ref; 108857e2f323SJérôme Duval entry.GetRef(&ref); 108957e2f323SJérôme Duval fPlayFile = new BMediaFile(&ref); //, B_MEDIA_FILE_UNBUFFERED); 109057e2f323SJérôme Duval if ((err = fPlayFile->InitCheck()) < B_OK) { 109157e2f323SJérôme Duval delete fPlayFile; 1092dbbc34b6SJérôme Duval fPlayFile = NULL; 1093c6f8aa29SJérôme Duval return err; 109457e2f323SJérôme Duval } 109557e2f323SJérôme Duval 109657e2f323SJérôme Duval for (int ix=0; ix < fPlayFile->CountTracks(); ix++) { 109757e2f323SJérôme Duval BMediaTrack * track = fPlayFile->TrackAt(ix); 109857e2f323SJérôme Duval fPlayFormat.type = B_MEDIA_RAW_AUDIO; 10995fa77532SJérôme Duval #ifdef __HAIKU__ 11005fa77532SJérôme Duval if ((track->DecodedFormat(&fPlayFormat) == B_OK) 11015fa77532SJérôme Duval #else 11025fa77532SJérôme Duval if ((DecodedFormat__11BMediaTrackP12media_format(track, &fPlayFormat) == B_OK) 11035fa77532SJérôme Duval #endif 11045fa77532SJérôme Duval && (fPlayFormat.type == B_MEDIA_RAW_AUDIO)) { 110557e2f323SJérôme Duval fPlayTrack = track; 110657e2f323SJérôme Duval break; 110757e2f323SJérôme Duval } 110857e2f323SJérôme Duval if (track) 110957e2f323SJérôme Duval fPlayFile->ReleaseTrack(track); 111057e2f323SJérôme Duval } 111157e2f323SJérôme Duval 111257e2f323SJérôme Duval if (!fPlayTrack) { 111357e2f323SJérôme Duval delete fPlayFile; 1114019ed09bSJérôme Duval fPlayFile = NULL; 1115c6f8aa29SJérôme Duval return B_STREAM_NOT_FOUND; 111657e2f323SJérôme Duval } 111757e2f323SJérôme Duval 1118c6f8aa29SJérôme Duval if (!updateDisplay) 1119c6f8aa29SJérôme Duval return B_OK; 1120c6f8aa29SJérôme Duval 1121*3ee96407SJérôme Duval BString filename = B_TRANSLATE("File name: "); 112257e2f323SJérôme Duval filename << ref.name; 112357e2f323SJérôme Duval fFilename->SetText(filename.String()); 112457e2f323SJérôme Duval 1125*3ee96407SJérôme Duval BString format = B_TRANSLATE("Format: "); 112657e2f323SJérôme Duval media_file_format file_format; 112757e2f323SJérôme Duval if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK) 112857e2f323SJérôme Duval format << file_format.short_name; 1129*3ee96407SJérôme Duval BString compression = B_TRANSLATE("Compression: "); 113057e2f323SJérôme Duval media_codec_info codec_info; 113157e2f323SJérôme Duval if (fPlayTrack->GetCodecInfo(&codec_info) == B_OK) { 113257e2f323SJérôme Duval if (strcmp(codec_info.short_name, "raw")==0) 1133*3ee96407SJérôme Duval compression << B_TRANSLATE("None"); 113457e2f323SJérôme Duval else 113557e2f323SJérôme Duval compression << codec_info.short_name; 113657e2f323SJérôme Duval } 1137*3ee96407SJérôme Duval BString channels = B_TRANSLATE("Channels: "); 113857e2f323SJérôme Duval channels << fPlayFormat.u.raw_audio.channel_count; 1139*3ee96407SJérôme Duval BString samplesize = B_TRANSLATE("Sample size: "); 1140*3ee96407SJérôme Duval samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf) 1141*3ee96407SJérôme Duval << B_TRANSLATE(" bits"); 1142*3ee96407SJérôme Duval BString samplerate = B_TRANSLATE("Sample rate: "); 114357e2f323SJérôme Duval samplerate << (int)fPlayFormat.u.raw_audio.frame_rate; 1144*3ee96407SJérôme Duval BString durationString = B_TRANSLATE("Duration: "); 114557e2f323SJérôme Duval bigtime_t duration = fPlayTrack->Duration(); 1146*3ee96407SJérôme Duval durationString << (float)(duration / 1000000.0) << B_TRANSLATE(" seconds"); 114757e2f323SJérôme Duval 114857e2f323SJérôme Duval fFormat->SetText(format.String()); 114957e2f323SJérôme Duval fCompression->SetText(compression.String()); 115057e2f323SJérôme Duval fChannels->SetText(channels.String()); 115157e2f323SJérôme Duval fSampleSize->SetText(samplesize.String()); 115257e2f323SJérôme Duval fSampleRate->SetText(samplerate.String()); 115357e2f323SJérôme Duval fDuration->SetText(durationString.String()); 115457e2f323SJérôme Duval 115557e2f323SJérôme Duval fTrackSlider->SetTotalTime(duration, true); 1156019ed09bSJérôme Duval fScopeView->SetTotalTime(duration, true); 115757e2f323SJérôme Duval fScopeView->RenderTrack(fPlayTrack, fPlayFormat); 11587942339dSJérôme Duval 11597942339dSJérôme Duval fPlayFrames = fPlayTrack->CountFrames(); 1160c6f8aa29SJérôme Duval return B_OK; 116157e2f323SJérôme Duval } 116257e2f323SJérôme Duval 116357e2f323SJérôme Duval 116457e2f323SJérôme Duval void 116557e2f323SJérôme Duval RecorderWindow::ErrorAlert(const char * action, status_t err) 116657e2f323SJérôme Duval { 116757e2f323SJérôme Duval char msg[300]; 1168c6f8aa29SJérôme Duval if (err != B_OK) 1169*3ee96407SJérôme Duval sprintf(msg, "%s: %s. [%lx]", action, strerror(err), (int32) err); 1170c6f8aa29SJérôme Duval else 1171*3ee96407SJérôme Duval sprintf(msg, "%s.", action); 1172*3ee96407SJérôme Duval (new BAlert("", msg, B_TRANSLATE("Stop")))->Go(); 117357e2f323SJérôme Duval } 117457e2f323SJérôme Duval 117557e2f323SJérôme Duval 117657e2f323SJérôme Duval status_t 117757e2f323SJérôme Duval RecorderWindow::NewTempName(char * name) 117857e2f323SJérôme Duval { 117957e2f323SJérôme Duval int init_count = fTempCount; 118057e2f323SJérôme Duval again: 118157e2f323SJérôme Duval if (fTempCount-init_count > 25) { 118257e2f323SJérôme Duval return B_ERROR; 118357e2f323SJérôme Duval } 118457e2f323SJérôme Duval else { 118557e2f323SJérôme Duval fTempCount++; 118657e2f323SJérôme Duval if (fTempCount==0) 118757e2f323SJérôme Duval sprintf(name, "Audio Clip"); 118857e2f323SJérôme Duval else 118957e2f323SJérôme Duval sprintf(name, "Audio Clip %d", fTempCount); 119057e2f323SJérôme Duval BPath path; 119157e2f323SJérôme Duval status_t err; 119257e2f323SJérôme Duval BEntry tempEnt; 119357e2f323SJérôme Duval if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) { 119457e2f323SJérôme Duval return err; 119557e2f323SJérôme Duval } 119657e2f323SJérôme Duval if ((err = tempEnt.GetPath(&path)) < B_OK) { 119757e2f323SJérôme Duval return err; 119857e2f323SJérôme Duval } 119957e2f323SJérôme Duval path.Append(name); 120057e2f323SJérôme Duval int fd; 120157e2f323SJérôme Duval // Use O_EXCL so we know we created the file (sync with other instances) 120257e2f323SJérôme Duval if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) { 120357e2f323SJérôme Duval goto again; 120457e2f323SJérôme Duval } 120557e2f323SJérôme Duval close(fd); 120657e2f323SJérôme Duval } 120757e2f323SJérôme Duval return B_OK; 120857e2f323SJérôme Duval } 120957e2f323SJérôme Duval 121057e2f323SJérôme Duval 121157e2f323SJérôme Duval void 121257e2f323SJérôme Duval RecorderWindow::AddSoundItem(const BEntry& entry, bool temp) 121357e2f323SJérôme Duval { 121457e2f323SJérôme Duval // Create list item to display. 121557e2f323SJérôme Duval SoundListItem * listItem = new SoundListItem(entry, temp); 121657e2f323SJérôme Duval fSoundList->AddItem(listItem); 121757e2f323SJérôme Duval fSoundList->Invalidate(); 121857e2f323SJérôme Duval fSoundList->Select(fSoundList->IndexOf(listItem)); 121957e2f323SJérôme Duval } 122057e2f323SJérôme Duval 1221019ed09bSJérôme Duval 1222019ed09bSJérôme Duval void 1223019ed09bSJérôme Duval RecorderWindow::RemoveCurrentSoundItem() { 1224c6f8aa29SJérôme Duval int32 index = fSoundList->CurrentSelection(); 1225c6f8aa29SJérôme Duval BListItem *item = fSoundList->RemoveItem(index); 1226019ed09bSJérôme Duval delete item; 1227c6f8aa29SJérôme Duval if (index >= fSoundList->CountItems()) 1228c6f8aa29SJérôme Duval index = fSoundList->CountItems() - 1; 1229c6f8aa29SJérôme Duval fSoundList->Select(index); 1230019ed09bSJérôme Duval } 1231019ed09bSJérôme Duval 1232019ed09bSJérôme Duval 123357e2f323SJérôme Duval void 1234ef367a8aSStephan Aßmus RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp, 1235ef367a8aSStephan Aßmus void* data, size_t size, const media_raw_audio_format &format) 123657e2f323SJérôme Duval { 123757e2f323SJérôme Duval // Callback called from the SoundConsumer when receiving buffers. 123857e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 123957e2f323SJérôme Duval 124057e2f323SJérôme Duval if (window->fRecording) { 124157e2f323SJérôme Duval // Write the data to file (we don't buffer or guard file access 124257e2f323SJérôme Duval // or anything) 124357e2f323SJérôme Duval window->fRecFile.WriteAt(window->fRecSize, data, size); 1244ad0c65eaSJérôme Duval window->fVUView->ComputeLevels(data, size, format.format); 124557e2f323SJérôme Duval window->fRecSize += size; 124657e2f323SJérôme Duval } 124757e2f323SJérôme Duval } 124857e2f323SJérôme Duval 124957e2f323SJérôme Duval 125057e2f323SJérôme Duval void 125157e2f323SJérôme Duval RecorderWindow::NotifyRecordFile(void * cookie, int32 code, ...) 125257e2f323SJérôme Duval { 125357e2f323SJérôme Duval if ((code == B_WILL_STOP) || (code == B_NODE_DIES)) { 125457e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 125557e2f323SJérôme Duval // Tell the window we've stopped, if it doesn't 125657e2f323SJérôme Duval // already know. 125757e2f323SJérôme Duval window->PostMessage(STOP_RECORDING); 125857e2f323SJérôme Duval } 125957e2f323SJérôme Duval } 126057e2f323SJérôme Duval 126157e2f323SJérôme Duval 126257e2f323SJérôme Duval void 1263ad0c65eaSJérôme Duval RecorderWindow::PlayFile(void * cookie, void * data, size_t size, 1264ad0c65eaSJérôme Duval const media_raw_audio_format & format) 126557e2f323SJérôme Duval { 126657e2f323SJérôme Duval // Callback called from the SoundProducer when producing buffers. 126757e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 126857e2f323SJérôme Duval int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) * 126957e2f323SJérôme Duval window->fPlayFormat.u.raw_audio.channel_count; 127057e2f323SJérôme Duval 1271948356deSJérôme Duval if ((window->fPlayFrame < window->fPlayLimit) || window->fLooping) { 1272948356deSJérôme Duval if (window->fPlayFrame >= window->fPlayLimit) { 1273948356deSJérôme Duval bigtime_t left = window->fTrackSlider->LeftTime(); 1274948356deSJérôme Duval window->fPlayTrack->SeekToTime(&left); 1275948356deSJérôme Duval window->fPlayFrame = window->fPlayTrack->CurrentFrame(); 1276948356deSJérôme Duval } 127757e2f323SJérôme Duval int64 frames = 0; 127857e2f323SJérôme Duval window->fPlayTrack->ReadFrames(data, &frames); 1279ad0c65eaSJérôme Duval window->fVUView->ComputeLevels(data, size / frame_size, format.format); 128057e2f323SJérôme Duval window->fPlayFrame += size/frame_size; 128157e2f323SJérôme Duval window->PostMessage(UPDATE_TRACKSLIDER); 128257e2f323SJérôme Duval } else { 128357e2f323SJérôme Duval // we're done! 128457e2f323SJérôme Duval window->PostMessage(STOP_PLAYING); 128557e2f323SJérôme Duval } 128657e2f323SJérôme Duval } 128757e2f323SJérôme Duval 12888e32a27eSPhilippe Saint-Pierre 128957e2f323SJérôme Duval void 1290ad0c65eaSJérôme Duval RecorderWindow::NotifyPlayFile(void * cookie, 1291ad0c65eaSJérôme Duval BSoundPlayer::sound_player_notification code, ...) 129257e2f323SJérôme Duval { 129357e2f323SJérôme Duval if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) { 129457e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 129557e2f323SJérôme Duval // tell the window we've stopped, if it doesn't 129657e2f323SJérôme Duval // already know. 129757e2f323SJérôme Duval window->PostMessage(STOP_PLAYING); 129857e2f323SJérôme Duval } 129957e2f323SJérôme Duval } 130057e2f323SJérôme Duval 130157e2f323SJérôme Duval 130257e2f323SJérôme Duval void 130357e2f323SJérôme Duval RecorderWindow::RefsReceived(BMessage *msg) 130457e2f323SJérôme Duval { 130557e2f323SJérôme Duval entry_ref ref; 130657e2f323SJérôme Duval int32 i = 0; 1307c6f8aa29SJérôme Duval int32 countGood = 0; 1308c6f8aa29SJérôme Duval int32 countBad = 0; 130957e2f323SJérôme Duval 131057e2f323SJérôme Duval while (msg->FindRef("refs", i++, &ref) == B_OK) { 131157e2f323SJérôme Duval 131257e2f323SJérôme Duval BEntry entry(&ref, true); 131357e2f323SJérôme Duval BPath path(&entry); 131457e2f323SJérôme Duval BNode node(&entry); 131557e2f323SJérôme Duval 131657e2f323SJérôme Duval if (node.IsFile()) { 1317c6f8aa29SJérôme Duval SoundListItem * listItem = new SoundListItem(entry, false); 1318c6f8aa29SJérôme Duval if (UpdatePlayFile(listItem) == B_OK) { 1319c6f8aa29SJérôme Duval fSoundList->AddItem(listItem); 1320c6f8aa29SJérôme Duval countGood++; 1321c6f8aa29SJérôme Duval continue; 1322c6f8aa29SJérôme Duval } 1323c6f8aa29SJérôme Duval delete listItem; 132457e2f323SJérôme Duval } else if(node.IsDirectory()) { 132557e2f323SJérôme Duval 132657e2f323SJérôme Duval } 1327c6f8aa29SJérôme Duval countBad++; 1328c6f8aa29SJérôme Duval } 1329c6f8aa29SJérôme Duval 1330c6f8aa29SJérôme Duval if (countBad > 0 && countGood == 0) 1331*3ee96407SJérôme Duval (new BAlert(B_TRANSLATE("Nothing to play"), B_TRANSLATE("None of the " 1332*3ee96407SJérôme Duval "files appear to be audio files"), B_TRANSLATE("OK"), NULL, NULL, 1333*3ee96407SJérôme Duval B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go(); 1334c6f8aa29SJérôme Duval else if (countGood > 0) { 1335c6f8aa29SJérôme Duval if (countBad > 0) 1336*3ee96407SJérôme Duval (new BAlert(B_TRANSLATE("Invalid audio files"), B_TRANSLATE("Some " 1337*3ee96407SJérôme Duval "of the files don't appear to be audio files"), 1338*3ee96407SJérôme Duval B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 1339*3ee96407SJérôme Duval B_WARNING_ALERT))->Go(); 1340c6f8aa29SJérôme Duval fSoundList->Select(fSoundList->CountItems() - 1); 134157e2f323SJérôme Duval } 134257e2f323SJérôme Duval } 1343019ed09bSJérôme Duval 1344019ed09bSJérôme Duval 1345019ed09bSJérôme Duval void 1346019ed09bSJérôme Duval RecorderWindow::CopyTarget(BMessage *msg) 1347019ed09bSJérôme Duval { 1348019ed09bSJérôme Duval const char *type = NULL; 1349019ed09bSJérôme Duval if (msg->FindString("be:types", &type) == B_OK) { 1350019ed09bSJérôme Duval if (!strcasecmp(type, B_FILE_MIME_TYPE)) { 1351019ed09bSJérôme Duval const char *name; 1352019ed09bSJérôme Duval entry_ref dir; 1353019ed09bSJérôme Duval if (msg->FindString("be:filetypes") == B_OK 1354019ed09bSJérôme Duval && msg->FindString("name", &name) == B_OK 1355019ed09bSJérôme Duval && msg->FindRef("directory", &dir) == B_OK) { 1356019ed09bSJérôme Duval BDirectory directory(&dir); 1357019ed09bSJérôme Duval BFile file(&directory, name, O_RDWR | O_TRUNC); 1358019ed09bSJérôme Duval 1359019ed09bSJérôme Duval // seek time 1360019ed09bSJérôme Duval bigtime_t start = fTrackSlider->LeftTime(); 1361019ed09bSJérôme Duval 1362019ed09bSJérôme Duval // write data 1363*3ee96407SJérôme Duval bigtime_t diffTime = fTrackSlider->RightTime() 1364*3ee96407SJérôme Duval - fTrackSlider->LeftTime(); 1365ad0c65eaSJérôme Duval int64 framesToWrite = (int64) (diffTime 1366ad0c65eaSJérôme Duval * fPlayFormat.u.raw_audio.frame_rate / 1000000LL); 1367019ed09bSJérôme Duval int32 frameSize = (fPlayFormat.u.raw_audio.format & 0xf) 1368019ed09bSJérôme Duval * fPlayFormat.u.raw_audio.channel_count; 1369019ed09bSJérôme Duval 1370019ed09bSJérôme Duval wave_struct header; 1371019ed09bSJérôme Duval header.riff.riff_id = FOURCC('R','I','F','F'); 1372*3ee96407SJérôme Duval header.riff.len 1373*3ee96407SJérôme Duval = (frameSize * framesToWrite) + sizeof(header) - 8; 1374019ed09bSJérôme Duval header.riff.wave_id = FOURCC('W','A','V','E'); 1375019ed09bSJérôme Duval header.format_chunk.fourcc = FOURCC('f','m','t',' '); 1376019ed09bSJérôme Duval header.format_chunk.len = sizeof(header.format); 1377019ed09bSJérôme Duval header.format.format_tag = 1; 1378019ed09bSJérôme Duval header.format.channels = fPlayFormat.u.raw_audio.channel_count; 1379*3ee96407SJérôme Duval header.format.samples_per_sec 1380*3ee96407SJérôme Duval = (uint32)fPlayFormat.u.raw_audio.frame_rate; 1381*3ee96407SJérôme Duval header.format.avg_bytes_per_sec 1382*3ee96407SJérôme Duval = (uint32)(fPlayFormat.u.raw_audio.frame_rate 1383019ed09bSJérôme Duval * fPlayFormat.u.raw_audio.channel_count 1384019ed09bSJérôme Duval * (fPlayFormat.u.raw_audio.format & 0xf)); 1385*3ee96407SJérôme Duval header.format.bits_per_sample 1386*3ee96407SJérôme Duval = (fPlayFormat.u.raw_audio.format & 0xf) * 8; 1387019ed09bSJérôme Duval header.format.block_align = frameSize; 1388019ed09bSJérôme Duval header.data_chunk.fourcc = FOURCC('d','a','t','a'); 1389019ed09bSJérôme Duval header.data_chunk.len = frameSize * framesToWrite; 1390019ed09bSJérôme Duval file.Seek(0, SEEK_SET); 1391019ed09bSJérôme Duval file.Write(&header, sizeof(header)); 1392019ed09bSJérôme Duval 1393019ed09bSJérôme Duval char *data = (char *)malloc(fPlayFormat.u.raw_audio.buffer_size); 1394019ed09bSJérôme Duval 1395c6f8aa29SJérôme Duval fPlayTrack->SeekToTime(&start); 1396c6f8aa29SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 1397019ed09bSJérôme Duval while (framesToWrite > 0) { 1398019ed09bSJérôme Duval int64 frames = 0; 1399019ed09bSJérôme Duval status_t err = fPlayTrack->ReadFrames(data, &frames); 1400c6f8aa29SJérôme Duval if (frames <= 0 || err != B_OK) { 1401c6f8aa29SJérôme Duval if (err != B_OK) 1402c6f8aa29SJérôme Duval fprintf(stderr, "CopyTarget: ReadFrames failed\n"); 1403019ed09bSJérôme Duval break; 1404c6f8aa29SJérôme Duval } 1405019ed09bSJérôme Duval file.Write(data, frames * frameSize); 1406019ed09bSJérôme Duval framesToWrite -= frames; 1407019ed09bSJérôme Duval } 1408019ed09bSJérôme Duval 1409019ed09bSJérôme Duval file.Sync(); 1410019ed09bSJérôme Duval free(data); 1411019ed09bSJérôme Duval BNodeInfo nodeInfo(&file); 1412019ed09bSJérôme Duval // set type 1413019ed09bSJérôme Duval } 1414019ed09bSJérôme Duval } else { 1415019ed09bSJérôme Duval 1416019ed09bSJérôme Duval } 1417019ed09bSJérôme Duval } 1418019ed09bSJérôme Duval } 1419