Ticket #8911: 0001-Replaced-SoundConsumer-with-MediaRecorder.patch
File 0001-Replaced-SoundConsumer-with-MediaRecorder.patch, 59.5 KB (added by , 10 years ago) |
---|
-
new file headers/private/media/MediaRecorder.h
From 5e596cc99dcc2162d8aa3e4f99828cbd48068ded Mon Sep 17 00:00:00 2001 From: Barrett17 <b.vitruvio@gmail.com> Date: Tue, 11 Feb 2014 01:25:39 +0000 Subject: [PATCH] Replaced SoundConsumer with MediaRecorder. --- headers/private/media/MediaRecorder.h | 118 +++++ headers/private/media/MediaRecorderNode.h | 115 +++++ headers/private/media/SoundConsumer.h | 151 ------- src/apps/soundrecorder/RecorderWindow.cpp | 179 ++++---- src/apps/soundrecorder/RecorderWindow.h | 14 +- src/kits/media/Jamfile | 3 +- src/kits/media/MediaRecorder.cpp | 465 ++++++++++++++++++++ src/kits/media/MediaRecorderNode.cpp | 290 +++++++++++++ src/kits/media/SoundConsumer.cpp | 700 ------------------------------ 9 files changed, 1069 insertions(+), 966 deletions(-) create mode 100644 headers/private/media/MediaRecorder.h create mode 100644 headers/private/media/MediaRecorderNode.h delete mode 100644 headers/private/media/SoundConsumer.h create mode 100644 src/kits/media/MediaRecorder.cpp create mode 100644 src/kits/media/MediaRecorderNode.cpp delete mode 100644 src/kits/media/SoundConsumer.cpp diff --git a/headers/private/media/MediaRecorder.h b/headers/private/media/MediaRecorder.h new file mode 100644 index 0000000..dd35b2e
- + 1 /* 2 * Copyright 2014, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #ifndef _MEDIA_RECORDER_H 7 #define _MEDIA_RECORDER_H 8 9 #include <MediaNode.h> 10 #include <TimeSource.h> 11 12 #include "MediaRecorderNode.h" 13 #include "SoundUtils.h" 14 15 typedef void (*ProcessFunc)(void* cookie, 16 bigtime_t timestamp, void* data, size_t datasize, 17 const media_format& format); 18 19 typedef void (*NotifyFunc)(void* cookie, 20 int32 code, ...); 21 22 23 namespace BPrivate { namespace media { 24 25 class BMediaRecorder { 26 public: 27 BMediaRecorder(const char* name, 28 int32 threadPriority = 0, 29 media_type type 30 = B_MEDIA_UNKNOWN_TYPE); 31 32 virtual ~BMediaRecorder(); 33 34 status_t InitCheck() const; 35 36 status_t SetHooks(ProcessFunc recordFunc = NULL, 37 NotifyFunc notifyFunc = NULL, 38 void* cookie = NULL); 39 40 void SetAcceptedFormat( 41 const media_format& format); 42 43 virtual status_t Start(bool force = false); 44 virtual status_t Stop(bool force = false); 45 46 virtual status_t Connect(const media_format& format, 47 uint32 flags = 0); 48 49 virtual status_t Connect(const dormant_node_info& dormantInfo, 50 const media_format* format = NULL, 51 uint32 flags = 0); 52 53 virtual status_t Connect(const media_node& node, 54 const media_output* useOutput = NULL, 55 const media_format* format = NULL, 56 uint32 flags = 0); 57 58 virtual status_t Disconnect(); 59 60 bool IsConnected() const; 61 bool IsRunning() const; 62 63 const media_format& Format() const; 64 65 const media_output& MediaOutput() const; 66 const media_input& MediaInput() const; 67 68 protected: 69 70 virtual void BufferReceived(void* buffer, 71 size_t size, 72 const media_header& header); 73 74 BMediaRecorder(); 75 76 BMediaRecorder(const BMediaRecorder& 77 recorder); 78 79 BMediaRecorder& operator=( 80 const BMediaRecorder& ); 81 82 private: 83 84 status_t _Connect( 85 const media_format* format, 86 uint32 flags, 87 const dormant_node_info* dormantNode, 88 const media_node* mediaNode, 89 const media_output* output); 90 91 status_t fInitErr; 92 93 bool fConnected; 94 bool fRunning; 95 96 BTimeSource* fTimeSource; 97 98 ProcessFunc fRecordHook; 99 NotifyFunc fNotifyHook; 100 101 media_node fOutputNode; 102 media_output fOutput; 103 104 BMediaRecorderNode* fNode; 105 media_input fInput; 106 107 void* fBufferCookie; 108 109 friend class BMediaRecorderNode; 110 }; 111 112 } 113 114 } 115 116 using namespace BPrivate::media; 117 118 #endif // _MEDIA_RECORDER_H -
new file headers/private/media/MediaRecorderNode.h
diff --git a/headers/private/media/MediaRecorderNode.h b/headers/private/media/MediaRecorderNode.h new file mode 100644 index 0000000..8bc4459
- + 1 // MediaRecorderNode.h 2 // ------------------- 3 // Copyright 1999, Be Incorporated. All Rights Reserved. 4 // Copyright 2014, Dario Casalinuovo. All Rights Reserved. 5 // This file may be used under the terms of the Be Sample Code License. 6 7 #ifndef _MEDIA_RECORDER_NODE_H 8 #define _MEDIA_RECORDER_NODE_H 9 10 #include <BufferConsumer.h> 11 #include <MediaEventLooper.h> 12 #include <String.h> 13 14 15 namespace BPrivate { namespace media { 16 17 class BMediaRecorder; 18 19 class BMediaRecorderNode : public BMediaEventLooper, 20 public BBufferConsumer { 21 public: 22 BMediaRecorderNode(const char* name, 23 BMediaRecorder* rec, 24 int32 priority, 25 media_type type 26 = B_MEDIA_UNKNOWN_TYPE); 27 28 // TODO these are not thread safe; we should fix that... 29 void SetAcceptedFormat(const media_format& format); 30 31 status_t GetInput(media_input* outInput); 32 33 void SetDataEnabled(bool enabled); 34 35 protected: 36 37 virtual BMediaAddOn* AddOn(int32* id) const; 38 39 virtual void HandleEvent(const media_timed_event* event, 40 bigtime_t lateness, 41 bool realTimeEvent); 42 43 virtual void Start(bigtime_t performanceTime); 44 45 virtual void Stop(bigtime_t performanceTime, 46 bool immediate); 47 48 virtual void Seek(bigtime_t mediaTime, 49 bigtime_t performanceTime); 50 51 virtual void TimeWarp(bigtime_t realTime, 52 bigtime_t performanceTime); 53 54 virtual status_t HandleMessage(int32 message, 55 const void* data, 56 size_t size); 57 58 // Someone, probably the producer, is asking you about 59 // this format. Give your honest opinion, possibly 60 // modifying *format. Do not ask upstream producer about 61 // the format, since he's synchronously waiting for your 62 // reply. 63 64 virtual status_t AcceptFormat(const media_destination& dest, 65 media_format* format); 66 67 virtual status_t GetNextInput(int32* cookie, 68 media_input* outInput); 69 70 virtual void DisposeInputCookie(int32 cookie); 71 72 virtual void BufferReceived(BBuffer* buffer); 73 74 virtual void ProducerDataStatus( 75 const media_destination& forWhom, 76 int32 status, 77 bigtime_t performanceTime); 78 79 virtual status_t GetLatencyFor( 80 const media_destination& forWhom, 81 bigtime_t* outLatency, 82 media_node_id* outTimesource); 83 84 virtual status_t Connected( 85 const media_source& producer, 86 const media_destination& where, 87 const media_format& withFormat, 88 media_input* outInput); 89 90 virtual void Disconnected( 91 const media_source& producer, 92 const media_destination& where); 93 94 virtual status_t FormatChanged( 95 const media_source& producer, 96 const media_destination& consumer, 97 int32 tag, 98 const media_format& format); 99 100 protected: 101 102 virtual ~BMediaRecorderNode(); 103 104 BMediaRecorder* fRecorder; 105 media_format fOKFormat; 106 media_input fInput; 107 BString fName; 108 }; 109 110 } 111 } 112 113 using namespace BPrivate::media; 114 115 #endif // _MEDIA_RECORDER_NODE_H -
deleted file headers/private/media/SoundConsumer.h
diff --git a/headers/private/media/SoundConsumer.h b/headers/private/media/SoundConsumer.h deleted file mode 100644 index c51dcc3..0000000
+ - 1 /*******************************************************************************2 /3 / File: SoundConsumer.h4 /5 / Description: Record sound from some sound-producing Node.6 /7 / Copyright 1998, Be Incorporated, All Rights Reserved8 /9 *******************************************************************************/10 #ifndef SOUND_CONSUMER_H11 #define SOUND_CONSUMER_H12 13 // To use this Consumer:14 // 1. Create Record and Notify hooks, or subclass SoundConsumer15 // if you'd rather use the inheritance hierarchy.16 // * The Record function should do whatever you want to do17 // when you receive a buffer.18 // * The Notify function should handle whatever events19 // you wish to handle (defined in SoundUtil.h).20 // 2: Create an instance of SoundConsumer, giving it the21 // appropriate hook functions. Or, create an instance of an22 // appropriate subclass if you've made one.23 // 3: Register your new Consumer with the MediaRoster.24 // 4: Connect your Consumer to some Producer.25 // 5: Start or Stop the Consumer if your hook functions26 // implement behavior for these kinds of events.27 // Seek the Consumer to set the offset of the timestamps that28 // your Record function will see.29 // 6: When you're done, disconnect the Consumer, then delete it.30 31 #include <BufferConsumer.h>32 #include "SoundUtils.h"33 34 namespace BPrivate {35 36 37 class SoundConsumer : public BBufferConsumer {38 public:39 SoundConsumer(const char* name,40 SoundProcessFunc recordFunc = NULL,41 SoundNotifyFunc notifyFunc = NULL,42 void* cookie = NULL);43 virtual ~SoundConsumer();44 45 //This function is OK to call from any thread.46 status_t SetHooks(SoundProcessFunc recordFunc = NULL,47 SoundNotifyFunc notifyFunc = NULL,48 void* cookie = NULL);49 50 // The MediaNode interface51 public:52 virtual port_id ControlPort() const;53 virtual BMediaAddOn* AddOn(int32* internalID) const;54 // Who instantiated you -- or NULL for app class55 56 57 protected:58 virtual void Start(bigtime_t performanceTime);59 virtual void Stop(bigtime_t performanceTime, bool immediate);60 virtual void Seek(bigtime_t mediaTime,61 bigtime_t performanceTime);62 virtual void SetRunMode(run_mode mode);63 virtual void TimeWarp(bigtime_t atRealTime,64 bigtime_t to_performanceTime);65 virtual void Preroll();66 virtual void SetTimeSource(BTimeSource* timeSource);67 virtual status_t HandleMessage(int32 message, const void* data,68 size_t size);69 70 // The BufferConsumer interface71 virtual status_t AcceptFormat(const media_destination& dest,72 media_format* format);73 virtual status_t GetNextInput(int32* cookie,74 media_input* _input);75 virtual void DisposeInputCookie(int32 cookie);76 virtual void BufferReceived(BBuffer* buffer);77 virtual void ProducerDataStatus(78 const media_destination& forWhom,79 int32 status, bigtime_t atMediaTime);80 virtual status_t GetLatencyFor(const media_destination& forWhom,81 bigtime_t* _latency,82 media_node_id* _timesource);83 virtual status_t Connected(const media_source& producer,84 const media_destination& where,85 const media_format& format,86 media_input* _input);87 virtual void Disconnected(const media_source& producer,88 const media_destination& where);89 virtual status_t FormatChanged(const media_source& producer,90 const media_destination& consumer,91 int32 fromChangeCount,92 const media_format& format);93 94 protected:95 // Functions called when no hooks are installed.96 // OK to override instead of installing hooks.97 virtual void Record(bigtime_t time, const void* data,98 size_t size,99 const media_raw_audio_format& format);100 virtual void Notify(int32 cause, ...);101 102 private:103 SoundProcessFunc m_recordHook;104 SoundNotifyFunc m_notifyHook;105 void* m_cookie;106 media_input m_input;107 thread_id m_thread;108 port_id m_port;109 110 // The times we need to deal with111 // My notation for times: tr = real time,112 // tp = performance time, tm = media time.113 bigtime_t m_trTimeout;114 // how long to wait on the input port115 bigtime_t m_tpSeekAt;116 // when we Seek117 bigtime_t m_tmSeekTo;118 // target time for Seek119 120 // The transformation from media to peformance time.121 // d = p - m, so m + d = p.122 // Media time is generally governed by the Seek123 // function. In our node, we simply use media time as124 // the time that we report to the record hook function.125 // If we were a producer node, we might use media time126 // to track where we were in playing a certain piece127 // of media. But we aren't.128 bigtime_t m_delta;129 130 // State variables131 bool m_seeking;132 // a Seek is pending133 134 // Functions to calculate timing values. OK to override.135 // ProcessingLatency is the time it takes to process a buffer;136 // TotalLatency is returned to producer; Timeout is passed137 // to call to read_port_etc() in service thread loop.138 virtual bigtime_t Timeout();139 virtual bigtime_t ProcessingLatency();140 virtual bigtime_t TotalLatency();141 // The actual thread doing the work142 static status_t ThreadEntry(void* cookie);143 void ServiceThread();144 void DoHookChange(void* message);145 };146 147 148 }149 150 151 #endif // SOUND_CONSUMER_H -
src/apps/soundrecorder/RecorderWindow.cpp
diff --git a/src/apps/soundrecorder/RecorderWindow.cpp b/src/apps/soundrecorder/RecorderWindow.cpp index f2fb731..7dc85bc 100644
a b 1 1 /* 2 2 * Copyright 2005, Jérôme Duval. All rights reserved. 3 * Copyright 2014, Dario Casalinuovo. All rights reserved. 3 4 * Distributed under the terms of the MIT License. 4 5 * 5 6 * Inspired by SoundCapture from Be newsletter (Media Kit Basics: … … 42 43 #include <TimeSource.h> 43 44 #include <NodeInfo.h> 44 45 46 #include "SoundUtils.h" 45 47 #include "RecorderWindow.h" 46 #include "SoundConsumer.h"47 48 #include "FileUtils.h" 48 49 49 50 #if ! NDEBUG … … RecorderWindow::RecorderWindow() 123 124 fSaveButton = NULL; 124 125 fLoopButton = NULL; 125 126 fInputField = NULL; 126 fRecord Node = 0;127 fRecorder = NULL; 127 128 fRecording = false; 128 129 fTempCount = -1; 129 130 fButtonState = btnPaused; … … RecorderWindow::RecorderWindow() 146 147 147 148 RecorderWindow::~RecorderWindow() 148 149 { 149 // The sound consumer and producer are Nodes; it has to be released and the Roster 150 // will reap it when it's done. 151 if (fRecordNode) 152 fRecordNode->Release(); 150 // The MediaRecorder have to be disconnected and deleted 151 delete fRecorder; 152 153 153 delete fPlayer; 154 154 155 155 if (fPlayTrack && fPlayFile) 156 156 fPlayFile->ReleaseTrack(fPlayTrack); 157 157 158 if (fPlayFile) 158 159 delete fPlayFile; 159 160 fPlayTrack = NULL; … … RecorderWindow::InitWindow() 232 233 if (error < B_OK) // there's no mixer? 233 234 goto bad_mojo; 234 235 235 // Create our internal Node which records sound, and register it.236 fRecordNode = new SoundConsumer("Sound Recorder");237 error = fRoster->RegisterNode(fRecordNode); 238 if ( error< B_OK)236 fRecorder = new BMediaRecorder("Sound Recorder", 237 B_REAL_TIME_PRIORITY, B_MEDIA_RAW_AUDIO); 238 239 if (fRecorder->InitCheck() < B_OK) 239 240 goto bad_mojo; 240 241 242 // Set the node to accept only audio data 243 media_format output_format; 244 output_format.type = B_MEDIA_RAW_AUDIO; 245 output_format.u.raw_audio = media_raw_audio_format::wildcard; 246 fRecorder->SetAcceptedFormat(output_format); 247 241 248 // Create the window header with controls 242 249 BRect r(Bounds()); 243 250 r.bottom = r.top + 175; 244 BBox *background = new BBox(r, "_background", B_FOLLOW_LEFT_RIGHT 245 | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER); 251 BBox *background = new BBox(r, "_background", 252 B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW 253 | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER); 254 246 255 AddChild(background); 247 256 248 257 r = background->Bounds(); … … RecorderWindow::InitWindow() 451 460 dormant_node_info dni[maxInputCount]; 452 461 453 462 int32 real_count = maxInputCount; 454 media_format output_format; 455 output_format.type = B_MEDIA_RAW_AUDIO; 456 output_format.u.raw_audio = media_raw_audio_format::wildcard; 463 457 464 error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format, 458 465 0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); 459 466 if (real_count > maxInputCount) { … … RecorderWindow::InitWindow() 514 521 bad_mojo: 515 522 if (error >= 0) 516 523 error = B_ERROR; 517 if (fRecord Node)518 fRecordNode->Release();524 if (fRecorder) 525 delete fRecorder; 519 526 520 527 delete fPlayer; 521 528 if (!fInputField) … … RecorderWindow::Record(BMessage * message) 696 703 return; 697 704 } 698 705 699 // And get it going... 700 bigtime_t then = fRecordNode->TimeSource()->Now() + 50000LL; 701 fRoster->StartNode(fRecordNode->Node(), then); 702 if (fAudioInputNode.kind & B_TIME_SOURCE) { 703 fRoster->StartNode(fAudioInputNode, 704 fRecordNode->TimeSource()->RealTimeFor(then, 0)); 705 } else 706 fRoster->StartNode(fAudioInputNode, then); 706 fRecorder->Start(); 707 707 } 708 708 709 709 … … RecorderWindow::MakeRecordConnection(const media_node & input) 871 871 { 872 872 CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n")); 873 873 874 // Find an available output for the given input node. 875 int32 count = 0; 876 status_t err = fRoster->GetFreeOutputsFor(input, &fAudioOutput, 1, &count, B_MEDIA_RAW_AUDIO); 877 if (err < B_OK) { 878 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 879 " couldn't get free outputs from audio input node\n")); 880 return err; 881 } 882 if (count < 1) { 883 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 884 " no free outputs from audio input node\n")); 885 return B_BUSY; 886 } 874 status_t err = B_OK; 875 media_output audioOutput; 887 876 888 // Find an available input for our own Node. Note that we go through the 889 // MediaRoster; calling Media Kit methods directly on Nodes in our app is 890 // not OK (because synchronization happens in the service thread, not in 891 // the calling thread). 892 // TODO: explain this 893 err = fRoster->GetFreeInputsFor(fRecordNode->Node(), &fRecInput, 1, &count, B_MEDIA_RAW_AUDIO); 894 if (err < B_OK) { 895 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 896 " couldn't get free inputs for sound recorder\n")); 897 return err; 898 } 899 if (count < 1) { 900 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 901 " no free inputs for sound recorder\n")); 902 return B_BUSY; 903 } 877 if (!fRecorder->IsConnected()) { 878 // Find an available output for the given input node. 879 int32 count = 0; 880 err = fRoster->GetFreeOutputsFor(input, &audioOutput, 1, 881 &count, B_MEDIA_RAW_AUDIO); 904 882 905 // Find out what the time source of the input is. 906 // For most nodes, we just use the preferred time source (the DAC) for synchronization. 907 // However, nodes that record from an input need to synchronize to the audio input node 908 // instead for best results. 909 // MakeTimeSourceFor gives us a "clone" of the time source node that we can manipulate 910 // to our heart's content. When we're done with it, though, we need to call Release() 911 // on the time source node, so that it keeps an accurate reference count and can delete 912 // itself when it's no longer needed. 913 // TODO: what about filters connected to audio input? 914 media_node use_time_source; 915 BTimeSource * tsobj = fRoster->MakeTimeSourceFor(input); 916 if (! tsobj) { 917 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 918 " couldn't clone time source from audio input node\n")); 919 return B_MEDIA_BAD_NODE; 920 } 883 if (err < B_OK) { 884 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 885 " couldn't get free outputs from audio input node\n")); 886 return err; 887 } 921 888 922 // Apply the time source in effect to our own Node. 923 err = fRoster->SetTimeSourceFor(fRecordNode->Node().node, tsobj->Node().node); 924 if (err < B_OK) { 889 if (count < 1) { 890 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 891 " no free outputs from audio input node\n")); 892 return B_BUSY; 893 } 894 895 } else { 925 896 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 926 " couldn't set the sound recorder's time source\n"));927 tsobj->Release(); 928 return err;897 " audio input node already connected\n")); 898 899 return B_BUSY; 929 900 } 930 901 931 902 // Get a format, any format. 932 fRecordFormat.u.raw_audio = fAudioOutput.format.u.raw_audio;903 fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio; 933 904 fRecordFormat.type = B_MEDIA_RAW_AUDIO; 934 905 935 906 // Tell the consumer where we want data to go. 936 err = fRecordNode->SetHooks(RecordFile, NotifyRecordFile, this); 907 err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this); 908 937 909 if (err < B_OK) { 938 910 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 939 911 " couldn't set the sound recorder's hook functions\n")); 940 tsobj->Release();941 912 return err; 942 913 } 943 914 944 // Using the same structs for input and output is OK in 945 // BMediaRoster::Connect(). 946 err = fRoster->Connect(fAudioOutput.source, fRecInput.destination, 947 &fRecordFormat, &fAudioOutput, &fRecInput); 948 if (err < B_OK) { 949 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 950 " failed to connect sound recorder to audio input node.\n")); 951 tsobj->Release(); 952 fRecordNode->SetHooks(0, 0, 0); 953 return err; 954 } 915 if (!fRecorder->IsConnected()) { 916 917 err = fRecorder->Connect(input, &audioOutput, &fRecordFormat); 955 918 956 // Start the time source if it's not running. 957 if ((tsobj->Node() != input) && !tsobj->IsRunning()) 958 fRoster->StartNode(tsobj->Node(), BTimeSource::RealTime()); 919 if (err < B_OK) { 920 CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 921 " failed to connect sound recorder to audio input node.\n")); 922 923 fRecorder->SetHooks(NULL, NULL, NULL); 924 return err; 925 } 926 } 959 927 960 tsobj->Release(); // we're done with this time source instance!961 928 return B_OK; 962 929 } 963 930 … … RecorderWindow::MakeRecordConnection(const media_node & input) 965 932 status_t 966 933 RecorderWindow::BreakRecordConnection() 967 934 { 968 status_t err; 935 status_t err = B_OK; 936 937 err = fRecorder->Stop(true); 938 if (err < B_OK) 939 return err; 940 941 err = fRecorder->Disconnect(); 969 942 970 // If we are the last connection, the Node will stop automatically since it971 // has nowhere to send data to.972 err = fRoster->StopNode(fRecInput.node, 0);973 err = fRoster->Disconnect(fAudioOutput.node.node, fAudioOutput.source,974 fRecInput.node.node, fRecInput.destination);975 fAudioOutput.source = media_source::null;976 fRecInput.destination = media_destination::null;977 943 return err; 978 944 } 979 945 … … RecorderWindow::StopRecording() 984 950 if (!fRecording) 985 951 return B_OK; 986 952 fRecording = false; 953 987 954 BreakRecordConnection(); 988 fRecordNode->SetHooks(NULL,NULL,NULL); 955 956 fRecorder->SetHooks(NULL, NULL, NULL); 957 989 958 if (fRecSize > 0) { 990 959 991 960 wave_struct header; … … RecorderWindow::RemoveCurrentSoundItem() { 1240 1209 1241 1210 void 1242 1211 RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp, 1243 void* data, size_t size, const media_ raw_audio_format &format)1212 void* data, size_t size, const media_format &format) 1244 1213 { 1245 1214 // Callback called from the SoundConsumer when receiving buffers. 1246 1215 RecorderWindow * window = (RecorderWindow *)cookie; … … RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp, 1249 1218 // Write the data to file (we don't buffer or guard file access 1250 1219 // or anything) 1251 1220 window->fRecFile.WriteAt(window->fRecSize, data, size); 1252 window->fVUView->ComputeLevels(data, size, format. format);1221 window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format); 1253 1222 window->fRecSize += size; 1254 1223 } 1255 1224 } -
src/apps/soundrecorder/RecorderWindow.h
diff --git a/src/apps/soundrecorder/RecorderWindow.h b/src/apps/soundrecorder/RecorderWindow.h index 1688334..d3e3087 100644
a b 21 21 #include <Window.h> 22 22 23 23 #include "DrawButton.h" 24 #include "MediaRecorder.h" 24 25 #include "ScopeView.h" 25 26 #include "SoundListView.h" 26 27 #include "TransportButton.h" … … class BScrollView; 40 41 class BSlider; 41 42 class BStringView; 42 43 43 namespace BPrivate {44 class SoundConsumer;45 }46 44 47 48 using BPrivate::SoundConsumer; 45 using BPrivate::media::BMediaRecorder; 49 46 50 47 51 48 class RecorderWindow : public BWindow { … … public: 78 75 }; 79 76 80 77 void AddSoundItem(const BEntry& entry, bool temp = false); 78 81 79 void RemoveCurrentSoundItem(); 82 80 83 81 private: … … private: 95 93 TrackSlider *fTrackSlider; 96 94 UpDownButton * fUpDownButton; 97 95 BMenuField * fInputField; 98 SoundConsumer * fRecordNode;96 BMediaRecorder * fRecorder; 99 97 BSoundPlayer * fPlayer; 100 98 bool fRecording; 101 99 SoundListView * fSoundList; … … private: 128 126 off_t fRecSize; 129 127 130 128 media_node fAudioInputNode; 131 media_output fAudioOutput;132 media_input fRecInput;133 129 134 130 BMediaFile *fPlayFile; 135 131 media_format fPlayFormat; … … private: 171 167 status_t UpdatePlayFile(SoundListItem *item, bool updateDisplay = false); 172 168 void ErrorAlert(const char * action, status_t err); 173 169 174 static void RecordFile(void * cookie, bigtime_t timestamp, void * data, size_t size, const media_ raw_audio_format & format);170 static void RecordFile(void * cookie, bigtime_t timestamp, void * data, size_t size, const media_format & format); 175 171 static void NotifyRecordFile(void * cookie, int32 code, ...); 176 172 177 173 static void PlayFile(void * cookie, void * data, size_t size, const media_raw_audio_format & format); -
src/kits/media/Jamfile
diff --git a/src/kits/media/Jamfile b/src/kits/media/Jamfile index 96dd800..1e52601 100644
a b for architectureObject in [ MultiArchSubDirSetup ] { 19 19 SharedLibrary [ MultiArchDefaultGristFiles libmedia.so ] : 20 20 # Private Media Kit 21 21 !missing_symbols.cpp 22 SoundConsumer.cpp 22 MediaRecorder.cpp 23 MediaRecorderNode.cpp 23 24 24 25 # Public Media Kit 25 26 Buffer.cpp -
new file src/kits/media/MediaRecorder.cpp
diff --git a/src/kits/media/MediaRecorder.cpp b/src/kits/media/MediaRecorder.cpp new file mode 100644 index 0000000..8dde9c9
- + 1 // MediaRecorder.cpp 2 // ----------------- 3 // Copyright 1999, Be Incorporated. All Rights Reserved. 4 // Copyright 2014, Dario Casalinuovo. All Rights Reserved. 5 // This file may be used under the terms of the Be Sample Code License. 6 7 #include "MediaRecorder.h" 8 9 #include <MediaAddOn.h> 10 #include <MediaRoster.h> 11 #include <TimeSource.h> 12 13 #include "MediaDebug.h" 14 #include "MediaRecorderNode.h" 15 16 17 class StNodeRelease { 18 public: 19 StNodeRelease(media_node& mediaNode, bool rel = true) 20 : 21 node(mediaNode), 22 release(rel) 23 {} 24 25 ~StNodeRelease() 26 { 27 if (release) 28 BMediaRoster::Roster()->ReleaseNode(node); 29 } 30 31 void SetRelease(bool rel) 32 { 33 release = rel; 34 } 35 36 private: 37 media_node& node; 38 bool release; 39 }; 40 41 42 BMediaRecorder::BMediaRecorder(const char* name, int32 threadPriority, 43 media_type type) 44 : 45 fInitErr(B_OK), 46 fConnected(false), 47 fRunning(false), 48 fTimeSource(NULL), 49 fRecordHook(NULL), 50 fNotifyHook(NULL), 51 fNode(NULL), 52 fBufferCookie(NULL) 53 { 54 CALLED(); 55 56 BMediaRoster::Roster(&fInitErr); 57 58 if (fInitErr == B_OK) { 59 60 if (threadPriority < 5) 61 threadPriority = 115; 62 63 fNode = new BMediaRecorderNode(name, this, threadPriority, type); 64 fInitErr = BMediaRoster::CurrentRoster()->RegisterNode(fNode); 65 } 66 } 67 68 69 BMediaRecorder::~BMediaRecorder() 70 { 71 CALLED(); 72 73 if (fNode != NULL) { 74 Stop(); 75 Disconnect(); 76 fNode->Release(); 77 } 78 79 if (fTimeSource != NULL) 80 fTimeSource->Release(); 81 82 } 83 84 85 status_t 86 BMediaRecorder::InitCheck() const 87 { 88 CALLED(); 89 90 return fInitErr; 91 } 92 93 94 void 95 BMediaRecorder::SetAcceptedFormat(const media_format& format) 96 { 97 CALLED(); 98 99 fNode->SetAcceptedFormat(format); 100 } 101 102 103 status_t 104 BMediaRecorder::SetHooks(ProcessFunc recordFunc, NotifyFunc notifyFunc, 105 void* cookie) 106 { 107 CALLED(); 108 109 fRecordHook = recordFunc; 110 fNotifyHook = notifyFunc; 111 fBufferCookie = cookie; 112 113 return B_OK; 114 } 115 116 117 void 118 BMediaRecorder::BufferReceived(void* buffer, size_t size, 119 const media_header& header) 120 { 121 CALLED(); 122 123 if (fRecordHook) { 124 (*fRecordHook)(fBufferCookie, header.start_time, 125 buffer, size, Format()); 126 } 127 } 128 129 130 status_t 131 BMediaRecorder::Connect(const media_format& format, uint32 flags) 132 { 133 CALLED(); 134 135 if (fInitErr != B_OK) 136 return fInitErr; 137 138 if (fConnected) 139 return B_MEDIA_ALREADY_CONNECTED; 140 141 return _Connect(&format, flags, NULL, NULL, NULL); 142 } 143 144 145 status_t 146 BMediaRecorder::Connect(const dormant_node_info& dormantInfo, 147 const media_format* format, uint32 flags) 148 { 149 CALLED(); 150 151 if (fInitErr != B_OK) 152 return fInitErr; 153 154 if (fConnected) 155 return B_MEDIA_ALREADY_CONNECTED; 156 157 return _Connect(format, flags, &dormantInfo, NULL, NULL); 158 } 159 160 161 status_t 162 BMediaRecorder::Connect(const media_node& node, 163 const media_output* useOutput, const media_format* format, 164 uint32 flags) 165 { 166 CALLED(); 167 168 if (fInitErr != B_OK) 169 return fInitErr; 170 171 if (fConnected) 172 return B_MEDIA_ALREADY_CONNECTED; 173 174 return _Connect(format, flags, NULL, &node, useOutput); 175 } 176 177 178 status_t 179 BMediaRecorder::Disconnect() 180 { 181 CALLED(); 182 183 status_t err = B_OK; 184 185 if (fInitErr != B_OK) 186 return fInitErr; 187 188 if (!fConnected) 189 return B_MEDIA_NOT_CONNECTED; 190 191 if (!fNode) 192 return B_ERROR; 193 194 if (fRunning) 195 err = Stop(); 196 197 if (err != B_OK) 198 return err; 199 200 // do the disconnect 201 err = BMediaRoster::CurrentRoster()->Disconnect( 202 fOutputNode.node, fOutput.source, 203 fNode->Node().node, fInput.destination); 204 205 BMediaRoster::Roster()->ReleaseNode(fOutputNode); 206 207 if (fTimeSource != NULL) { 208 fTimeSource->Release(); 209 fTimeSource = NULL; 210 } 211 212 fConnected = false; 213 fRunning = false; 214 215 return err; 216 } 217 218 219 status_t 220 BMediaRecorder::Start(bool force) 221 { 222 CALLED(); 223 224 if (fInitErr != B_OK) 225 return fInitErr; 226 227 if (!fConnected) 228 return B_MEDIA_NOT_CONNECTED; 229 230 if (fRunning && !force) 231 return EALREADY; 232 233 if (!fNode) 234 return B_ERROR; 235 236 // start node here 237 status_t err = B_OK; 238 239 if (fNode->Node().kind & B_TIME_SOURCE) { 240 err = BMediaRoster::CurrentRoster()->StartTimeSource( 241 fOutputNode, BTimeSource::RealTime()); 242 } else { 243 err = BMediaRoster::CurrentRoster()->StartNode( 244 fOutputNode, fTimeSource->Now()); 245 } 246 247 // then un-mute it 248 if (err == B_OK) 249 fNode->SetDataEnabled(true); 250 251 fRunning = (err == B_OK); 252 253 return err; 254 } 255 256 257 status_t 258 BMediaRecorder::Stop(bool force) 259 { 260 CALLED(); 261 262 if (fInitErr != B_OK) 263 return fInitErr; 264 265 if (!fRunning && !force) 266 return EALREADY; 267 268 if (!fNode) 269 return B_ERROR; 270 271 // should have the Node mute the output here 272 fNode->SetDataEnabled(false); 273 274 fRunning = false; 275 276 return BMediaRoster::CurrentRoster()->StopNode(fNode->Node(), 0); 277 } 278 279 280 bool 281 BMediaRecorder::IsRunning() const 282 { 283 CALLED(); 284 285 return fRunning; 286 } 287 288 289 bool 290 BMediaRecorder::IsConnected() const 291 { 292 CALLED(); 293 294 return fConnected; 295 } 296 297 298 const media_output& 299 BMediaRecorder::MediaOutput() const 300 { 301 CALLED(); 302 303 return fOutput; 304 } 305 306 307 const media_input& 308 BMediaRecorder::MediaInput() const 309 { 310 CALLED(); 311 312 return fInput; 313 } 314 315 316 const media_format& 317 BMediaRecorder::Format() const 318 { 319 CALLED(); 320 321 return fOutput.format; 322 } 323 324 325 status_t 326 BMediaRecorder::_Connect(const media_format* format, 327 uint32 flags, const dormant_node_info* dormantNode, 328 const media_node* mediaNode, const media_output* output) 329 { 330 CALLED(); 331 332 status_t err = B_OK; 333 media_format ourFormat; 334 media_node node; 335 StNodeRelease away(node, false); 336 media_output ourOutput; 337 338 // argument checking and set-up 339 if (format != NULL) 340 ourFormat = *format; 341 342 if (fNode == NULL) 343 return B_ERROR; 344 345 if (mediaNode == NULL && output != NULL) 346 return B_MISMATCHED_VALUES; 347 348 if (dormantNode != NULL && (mediaNode != NULL || output != NULL)) 349 return B_MISMATCHED_VALUES; 350 351 if (format == NULL && output != NULL) 352 ourFormat = output->format; 353 354 fNode->SetAcceptedFormat(ourFormat); 355 356 // figure out the node to instantiate 357 if (dormantNode != NULL) { 358 359 err = BMediaRoster::Roster()->InstantiateDormantNode( 360 *dormantNode, &node, B_FLAVOR_IS_GLOBAL); 361 362 away.SetRelease(true); 363 364 } else if (mediaNode != NULL) { 365 node = *mediaNode; 366 } else { 367 switch (ourFormat.type) { 368 369 // switch on format for default 370 case B_MEDIA_RAW_AUDIO: 371 err = BMediaRoster::Roster()->GetAudioInput(&node); 372 away.SetRelease(true); 373 break; 374 375 case B_MEDIA_RAW_VIDEO: 376 case B_MEDIA_ENCODED_VIDEO: 377 err = BMediaRoster::Roster()->GetVideoInput(&node); 378 away.SetRelease(true); 379 break; 380 381 // give up? 382 default: 383 return B_MEDIA_BAD_FORMAT; 384 break; 385 } 386 } 387 388 fOutputNode = node; 389 390 // figure out the output provided 391 392 if (output != NULL) { 393 ourOutput = *output; 394 } else if (err == B_OK) { 395 396 media_output outputs[10]; 397 int32 count = 10; 398 399 err = BMediaRoster::Roster()->GetFreeOutputsFor(node, 400 outputs, count, &count, ourFormat.type); 401 402 if (err != B_OK) 403 return err; 404 405 err = B_MEDIA_BAD_SOURCE; 406 407 for (int i = 0; i < count; i++) { 408 if (format_is_compatible(outputs[i].format, ourFormat)) { 409 ourOutput = outputs[i]; 410 err = B_OK; 411 ourFormat = outputs[i].format; 412 break; 413 } 414 415 } 416 417 } else { 418 return err; 419 } 420 421 if (ourOutput.source == media_source::null) 422 return B_MEDIA_BAD_SOURCE; 423 424 // find our Node's free input 425 media_input ourInput; 426 err = fNode->GetInput(&ourInput); 427 428 media_node time_source; 429 if (node.kind & B_TIME_SOURCE) 430 time_source = node; 431 else 432 BMediaRoster::Roster()->GetSystemTimeSource(&time_source); 433 434 // set time source 435 if (err == B_OK) { 436 BMediaRoster::Roster()->SetTimeSourceFor( 437 fNode->Node().node, time_source.node); 438 439 fTimeSource = BMediaRoster::CurrentRoster()->MakeTimeSourceFor( 440 fNode->Node()); 441 } 442 443 // start the recorder node (it's always running) 444 if (err == B_OK) 445 err = BMediaRoster::CurrentRoster()->StartNode( 446 fOutputNode, fTimeSource->Now()); 447 448 449 // perform the connection 450 if (err == B_OK) { 451 fOutput = ourOutput; 452 fInput = ourInput; 453 454 err = BMediaRoster::CurrentRoster()->Connect(fOutput.source, 455 fInput.destination, &ourFormat, &fOutput, &fInput, 456 BMediaRoster::B_CONNECT_MUTED); 457 458 if (err == B_OK) { 459 fConnected = true; 460 away.SetRelease(false); 461 } 462 } 463 464 return err; 465 } -
new file src/kits/media/MediaRecorderNode.cpp
diff --git a/src/kits/media/MediaRecorderNode.cpp b/src/kits/media/MediaRecorderNode.cpp new file mode 100644 index 0000000..6dacaa6
- + 1 // MediaRecorderNode.cpp 2 // --------------------- 3 // Copyright 1999, Be Incorporated. All Rights Reserved. 4 // Copyright 2014, Dario Casalinuovo. All Rights Reserved. 5 // This file may be used under the terms of the Be Sample Code License. 6 7 #include "MediaRecorderNode.h" 8 9 #include <Buffer.h> 10 #include <TimedEventQueue.h> 11 #include <TimeSource.h> 12 13 #include "MediaDebug.h" 14 #include "MediaRecorder.h" 15 16 17 BMediaRecorderNode::BMediaRecorderNode(const char* name, 18 BMediaRecorder* rec, int32 priority, media_type type) 19 : 20 BMediaNode(name), 21 BMediaEventLooper(), 22 BBufferConsumer(type), 23 fRecorder(rec) 24 { 25 CALLED(); 26 27 SetPriority(priority); 28 29 fInput.destination.id = 1; 30 fInput.destination.port = ControlPort(); 31 32 fName.SetTo(name); 33 34 BString str(name); 35 str << " Input"; 36 strcpy(fInput.name, str.String()); 37 } 38 39 40 BMediaRecorderNode::~BMediaRecorderNode() 41 { 42 CALLED(); 43 } 44 45 46 BMediaAddOn* 47 BMediaRecorderNode::AddOn(int32* id) const 48 { 49 CALLED(); 50 51 if (id) 52 *id = -1; 53 54 return NULL; 55 } 56 57 58 void 59 BMediaRecorderNode::SetAcceptedFormat(const media_format& format) 60 { 61 CALLED(); 62 63 fOKFormat = format; 64 } 65 66 67 status_t 68 BMediaRecorderNode::GetInput(media_input* outInput) 69 { 70 CALLED(); 71 72 fInput.node = Node(); 73 *outInput = fInput; 74 75 return B_OK; 76 } 77 78 79 void 80 BMediaRecorderNode::SetDataEnabled(bool enabled) 81 { 82 CALLED(); 83 84 int32 tag; 85 86 SetOutputEnabled(fInput.source, 87 fInput.destination, enabled, NULL, &tag); 88 } 89 90 91 void 92 BMediaRecorderNode::HandleEvent(const media_timed_event* event, 93 bigtime_t lateness, bool realTimeEvent) 94 { 95 CALLED(); 96 97 // we ignore them all! 98 } 99 100 101 void 102 BMediaRecorderNode::Start(bigtime_t performanceTime) 103 { 104 CALLED(); 105 106 if (fRecorder->fNotifyHook) { 107 (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie, 108 B_WILL_START, performanceTime); 109 } 110 111 fRecorder->fRunning = true; 112 } 113 114 115 void 116 BMediaRecorderNode::Stop(bigtime_t performanceTime, bool immediate) 117 { 118 CALLED(); 119 120 if (fRecorder->fNotifyHook) { 121 (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie, 122 B_WILL_STOP, performanceTime, immediate); 123 } 124 125 fRecorder->fRunning = false; 126 } 127 128 129 void 130 BMediaRecorderNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime) 131 { 132 CALLED(); 133 134 if (fRecorder->fNotifyHook) { 135 (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie, 136 B_WILL_SEEK, performanceTime, mediaTime); 137 } 138 } 139 140 141 void 142 BMediaRecorderNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime) 143 { 144 CALLED(); 145 146 // Since buffers will come pre-time-stamped, we only need to look 147 // at them, so we can ignore the time warp as a consumer. 148 if (fRecorder->fNotifyHook) { 149 (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie, 150 B_WILL_TIMEWARP, realTime, performanceTime); 151 } 152 } 153 154 155 status_t 156 BMediaRecorderNode::HandleMessage(int32 message, 157 const void* data, size_t size) 158 { 159 CALLED(); 160 161 if (BBufferConsumer::HandleMessage(message, data, size) < 0 162 && BMediaEventLooper::HandleMessage(message, data, size) < 0 163 && BMediaNode::HandleMessage(message, data, size) < 0) { 164 165 HandleBadMessage(message, data, size); 166 167 return B_ERROR; 168 } 169 170 return B_OK; 171 } 172 173 174 status_t 175 BMediaRecorderNode::AcceptFormat(const media_destination& dest, 176 media_format* format) 177 { 178 CALLED(); 179 180 if (format_is_compatible(*format, fOKFormat)) 181 return B_OK; 182 183 *format = fOKFormat; 184 185 return B_MEDIA_BAD_FORMAT; 186 } 187 188 189 status_t 190 BMediaRecorderNode::GetNextInput(int32* cookie, media_input* outInput) 191 { 192 CALLED(); 193 194 if (*cookie == 0) { 195 *cookie = -1; 196 *outInput = fInput; 197 return B_OK; 198 } 199 200 return B_BAD_INDEX; 201 } 202 203 204 void 205 BMediaRecorderNode::DisposeInputCookie(int32 cookie) 206 { 207 CALLED(); 208 } 209 210 211 void 212 BMediaRecorderNode::BufferReceived(BBuffer* buffer) 213 { 214 CALLED(); 215 216 fRecorder->BufferReceived(buffer->Data(), buffer->SizeUsed(), 217 *buffer->Header()); 218 219 buffer->Recycle(); 220 } 221 222 223 void 224 BMediaRecorderNode::ProducerDataStatus( 225 const media_destination& forWhom, int32 status, 226 bigtime_t performanceTime) 227 { 228 CALLED(); 229 } 230 231 232 status_t 233 BMediaRecorderNode::GetLatencyFor(const media_destination& forWhom, 234 bigtime_t* outLatency, media_node_id* outTimesource) 235 { 236 CALLED(); 237 238 *outLatency = 0; 239 *outTimesource = TimeSource()->ID(); 240 241 return B_OK; 242 } 243 244 245 status_t 246 BMediaRecorderNode::Connected(const media_source &producer, 247 const media_destination &where, const media_format &withFormat, 248 media_input* outInput) 249 { 250 CALLED(); 251 252 fInput.source = producer; 253 fInput.format = withFormat; 254 *outInput = fInput; 255 256 fRecorder->fConnected = true; 257 fRecorder->fInput = fInput; 258 259 return B_OK; 260 } 261 262 263 void 264 BMediaRecorderNode::Disconnected(const media_source& producer, 265 const media_destination& where) 266 { 267 CALLED(); 268 269 fInput.source = media_source::null; 270 271 fRecorder->fConnected = false; 272 273 fRecorder->fInput.format = fOKFormat; 274 } 275 276 277 status_t 278 BMediaRecorderNode::FormatChanged(const media_source& producer, 279 const media_destination& consumer, int32 tag, 280 const media_format& format) 281 { 282 CALLED(); 283 284 if (!format_is_compatible(format, fOKFormat)) 285 return B_MEDIA_BAD_FORMAT; 286 287 fInput.format = format; 288 289 return B_OK; 290 } -
deleted file src/kits/media/SoundConsumer.cpp
diff --git a/src/kits/media/SoundConsumer.cpp b/src/kits/media/SoundConsumer.cpp deleted file mode 100644 index e2ca10f..0000000
+ - 1 /******************************************************************************2 /3 / File: SoundConsumer.cpp4 /5 / Description: Record sound from some sound-producing Node.6 /7 / Copyright 1998-1999, Be Incorporated, All Rights Reserved8 /9 ******************************************************************************/10 #include "SoundConsumer.h"11 12 #include <new>13 #include <stdio.h>14 15 #include <OS.h>16 #include <scheduler.h>17 #include <Buffer.h>18 #include <TimeSource.h>19 20 #include "AutoDeleter.h"21 22 23 using std::nothrow;24 25 26 namespace BPrivate {27 28 29 // If we don't mind the format changing to another format while30 // running, we can define this to 1. Look for the symbol down in the source.31 #define ACCEPT_ANY_FORMAT_CHANGE 032 33 34 // Compiling with NDEBUG means "release" -- it also turns off assert() and35 // other such debugging aids. By contrast, DEBUG turns on extra debugging36 // in some programs.37 #if !NDEBUG38 #define TRACE_SOUNDCONSUMER39 #endif40 41 // Comment out the FPRINTF part of these lines to reduce verbiage.42 // Enabling MESSAGE will kill performance on slower machines, because it43 // prints for each message received (including each buffer).44 #ifdef TRACE_SOUNDCONSUMER45 #define NODE fprintf46 #define MESSAGE fprintf47 #else48 #define NODE(x...)49 #define MESSAGE(x...)50 #endif51 52 53 // This structure is the body of a request that we use to54 // implement SetHooks().55 struct set_hooks_q {56 port_id reply;57 void * cookie;58 SoundProcessFunc process;59 SoundNotifyFunc notify;60 };61 62 63 // All incoming buffers and Media Kit requests arrive at a64 // media node in the form of messages (which are generally65 // dispatched for you by your superclasses' HandleMessage66 // implementations). Each message has a 'type' which is67 // analagous to a BMessage's 'what' field. We'll define our68 // own private message types for our SoundConsumer and69 // SoundProducer to use. The BeOS reserves a range,70 // 0x60000000 to 0x7fffffff, for us to use.71 enum {72 MSG_QUIT_NOW = 0x60000000L,73 MSG_CHANGE_HOOKS74 };75 76 77 SoundConsumer::SoundConsumer(78 const char * name,79 SoundProcessFunc recordFunc,80 SoundNotifyFunc notifyFunc,81 void * cookie) :82 BMediaNode(name ? name : "SoundConsumer"),83 BBufferConsumer(B_MEDIA_RAW_AUDIO)84 {85 NODE(stderr, "SoundConsumer::SoundConsumer(%p, %p, %p, %p)\n", name,86 recordFunc, notifyFunc, cookie);87 88 if (!name) name = "SoundConsumer";89 90 // Set up the hook functions.91 m_recordHook = recordFunc;92 m_notifyHook = notifyFunc;93 m_cookie = cookie;94 95 // Create the port that we publish as our Control Port.96 char pname[32];97 sprintf(pname, "%.20s Control", name);98 m_port = create_port(10, pname);99 100 // Initialize our single media_input. Make sure it knows101 // the Control Port associated with the destination, and102 // the index of the destination (since we only have one,103 // that's trivial).104 m_input.destination.port = m_port;105 m_input.destination.id = 1;106 sprintf(m_input.name, "%.20s Input", name);107 108 // Set up the timing variables that we'll be using.109 m_trTimeout = 0LL;110 m_tpSeekAt = 0;111 m_tmSeekTo = 0;112 m_delta = 0;113 m_seeking = false;114 115 // Create, and run, the thread that we use to service116 // the Control Port.117 sprintf(pname, "%.20s Service", name);118 m_thread = spawn_thread(ThreadEntry, pname, 110, this);119 resume_thread(m_thread);120 }121 122 123 SoundConsumer::~SoundConsumer()124 {125 NODE(stderr, "SoundConsumer::~SoundConsumer()\n");126 // Signal to our thread that it's time to go home.127 write_port(m_port, MSG_QUIT_NOW, 0, 0);128 status_t s;129 while (wait_for_thread(m_thread, &s) == B_INTERRUPTED)130 NODE(stderr, "wait_for_thread() B_INTERRUPTED\n");131 delete_port(m_port);132 }133 134 135 status_t136 SoundConsumer::SetHooks(SoundProcessFunc recordFunc, SoundNotifyFunc notifyFunc,137 void* cookie)138 {139 // SetHooks needs to be synchronized with the service thread, else we may140 // call the wrong hook function with the wrong cookie, which would be bad.141 // Rather than do locking, which is expensive, we streamline the process142 // by sending our service thread a request to change the hooks, and waiting143 // for the acknowledge.144 status_t err = B_OK;145 set_hooks_q cmd;146 cmd.process = recordFunc;147 cmd.notify = notifyFunc;148 cmd.cookie = cookie;149 // If we're not in the service thread, we need to round-trip a message.150 if (find_thread(0) != m_thread) {151 cmd.reply = create_port(1, "SetHooks reply");152 // Send the private message to our service thread.153 err = write_port(ControlPort(), MSG_CHANGE_HOOKS, &cmd, sizeof(cmd));154 if (err >= 0) {155 int32 code;156 // Wait for acknowledge from the service thread.157 err = read_port_etc(cmd.reply, &code, 0, 0, B_TIMEOUT, 6000000LL);158 if (err > 0) err = 0;159 NODE(stderr, "SoundConsumer::SetHooks read reply: %#" B_PRIx32160 "\n", err);161 }162 // Clean up.163 delete_port(cmd.reply);164 } else {165 // Within the service thread, it's OK to just go ahead and do the166 // change.167 DoHookChange(&cmd);168 }169 return err;170 }171 172 173 // #pragma mark -BMediaNode-derived methods174 175 176 port_id177 SoundConsumer::ControlPort() const178 {179 return m_port;180 }181 182 183 BMediaAddOn*184 SoundConsumer::AddOn(int32 * internal_id) const185 {186 // This object is instantiated inside an application.187 // Therefore, it has no add-on.188 if (internal_id) *internal_id = 0;189 return 0;190 }191 192 193 void194 SoundConsumer::Start(bigtime_t performance_time)195 {196 // Since we are a consumer and just blindly accept buffers that are197 // thrown at us, we don't need to do anything special in Start()/Stop().198 // If we were (also) a producer, we'd have to be more elaborate.199 // The only thing we do is immediately perform any queued Seek based on200 // the start time, which is the right thing to do (seeing as we were201 // Seek()-ed when we weren't started).202 if (m_seeking) {203 m_delta = performance_time - m_tmSeekTo;204 m_seeking = false;205 }206 if (m_notifyHook)207 (*m_notifyHook)(m_cookie, B_WILL_START, performance_time);208 else209 Notify(B_WILL_START, performance_time);210 }211 212 213 void214 SoundConsumer::Stop(bigtime_t performance_time, bool immediate)215 {216 // Since we are a consumer and just blindly accept buffers that are217 // thrown at us, we don't need to do anything special in Start()/Stop().218 // If we were (also) a producer, we'd have to be more elaborate.219 // Note that this is not strictly in conformance with The Rules,220 // but since this is not an add-on Node for use with any application;221 // it's a Node over which we have complete control, we can live with222 // treating buffers received before the start time or after the stop223 // time as any other buffer.224 if (m_notifyHook)225 (*m_notifyHook)(m_cookie, B_WILL_STOP, performance_time, immediate);226 else227 Notify(B_WILL_STOP, performance_time, immediate);228 }229 230 231 void232 SoundConsumer::Seek(bigtime_t media_time, bigtime_t performance_time)233 {234 // Seek() on a consumer just serves to offset the time stamp235 // of received buffers passed to our Record hook function.236 // In the hook function, you may wish to save those time stamps237 // to disk or otherwise store them. You may also want to238 // synchronize this node's media time with an upstream239 // producer's media time to make this offset meaningful.240 if (m_notifyHook)241 (*m_notifyHook)(m_cookie, B_WILL_SEEK, performance_time, media_time);242 else243 Notify(B_WILL_SEEK, performance_time, media_time);244 245 m_tpSeekAt = performance_time;246 m_tmSeekTo = media_time;247 m_seeking = true;248 }249 250 251 void252 SoundConsumer::SetRunMode(run_mode mode)253 {254 if (mode == BMediaNode::B_OFFLINE) {255 // BMediaNode::B_OFFLINE means we don't need to run in256 // real time. So, we shouldn't run as a real time257 // thread.258 int32 new_prio = suggest_thread_priority(B_OFFLINE_PROCESSING);259 set_thread_priority(m_thread, new_prio);260 } else {261 // We're running in real time, so we'd better have262 // a big enough thread priority to handle it!263 // Here's where those magic scheduler values264 // come from:265 //266 // * In the worst case, we process one buffer per267 // reschedule (we get rescheduled when we go to268 // look for a message on our Control Port), so269 // in order to keep up with the incoming buffers,270 // the duration of one buffer becomes our271 // scheduling period. If we don't know anything272 // about the buffers, we pick a reasonable273 // default.274 // * We're a simple consumer, so we don't have to275 // be too picky about the jitter. Half a period276 // of jitter means that we'd have to get two277 // consecutive worst-case reschedules before278 // we'd fall behind.279 // * The amount of time we spend processing is280 // our ProcessingLatency().281 bigtime_t period = 10000;282 if (buffer_duration(m_input.format.u.raw_audio) > 0)283 period = buffer_duration(m_input.format.u.raw_audio);284 285 // assuming we're running for 500 us or less per buffer286 int32 new_prio = suggest_thread_priority(B_AUDIO_RECORDING,287 period, period / 2, ProcessingLatency());288 set_thread_priority(m_thread, new_prio);289 }290 }291 292 293 void294 SoundConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)295 {296 // Since buffers will come pre-time-stamped, we only need to look297 // at them, so we can ignore the time warp as a consumer.298 if (m_notifyHook) {299 (*m_notifyHook)(m_cookie, B_WILL_TIMEWARP, at_real_time,300 to_performance_time);301 } else302 Notify(B_WILL_TIMEWARP, at_real_time, to_performance_time);303 }304 305 306 void307 SoundConsumer::Preroll()308 {309 // There is nothing for us to do in Preroll()310 }311 312 313 void314 SoundConsumer::SetTimeSource(BTimeSource* /* time_source */)315 {316 // We don't need to do anything special to take note of the317 // fact that the time source changed, because we get our timing318 // information from the buffers we receive.319 }320 321 322 status_t323 SoundConsumer::HandleMessage(int32 message, const void* data, size_t size)324 {325 // Check with each of our superclasses to see if they326 // understand the message. If none of them do, call327 // BMediaNode::HandleBadMessage().328 if (BBufferConsumer::HandleMessage(message, data, size) < 0329 && BMediaNode::HandleMessage(message, data, size) < 0) {330 HandleBadMessage(message, data, size);331 return B_ERROR;332 }333 return B_OK;334 }335 336 337 // #pragma mark - BBufferConsumer-derived methods338 339 340 status_t341 SoundConsumer::AcceptFormat(const media_destination& dest, media_format* format)342 {343 // We only accept formats aimed at our single input.344 if (dest != m_input.destination)345 return B_MEDIA_BAD_DESTINATION;346 347 if (format->type <= 0) {348 // If no format is specified, we say we want raw audio.349 format->type = B_MEDIA_RAW_AUDIO;350 format->u.raw_audio = media_raw_audio_format::wildcard;351 } else if (format->type != B_MEDIA_RAW_AUDIO) {352 // If a non-raw-audio format is specified, we tell the world what353 // we want, and that the specified format was unacceptable to us.354 format->type = B_MEDIA_RAW_AUDIO;355 format->u.raw_audio = media_raw_audio_format::wildcard;356 return B_MEDIA_BAD_FORMAT;357 }358 #if !ACCEPT_ANY_FORMAT_CHANGE359 // If we're already connected, and this format doesn't go with the360 // format in effect, we dont' accept this new format.361 if (!format_is_compatible(*format, m_input.format)) {362 *format = m_input.format;363 return B_MEDIA_BAD_FORMAT;364 }365 #endif366 // I guess we're OK by now, because we have no particular needs as367 // far as frame rate, sample format, etc go.368 return B_OK;369 }370 371 372 status_t373 SoundConsumer::GetNextInput(int32* cookie, media_input* out_input)374 {375 NODE(stderr, "SoundConsumer: GetNextInput()\n");376 // The "next" is kind of misleading, since it's also used for377 // getting the first (and only) input.378 if (*cookie == 0) {379 if (m_input.source == media_source::null) {380 // If there's no current connection, make sure we return a381 // reasonable format telling the world we accept any raw audio.382 m_input.format.type = B_MEDIA_RAW_AUDIO;383 m_input.format.u.raw_audio = media_raw_audio_format::wildcard;384 m_input.node = Node();385 m_input.destination.port = ControlPort();386 m_input.destination.id = 1;387 }388 *out_input = m_input;389 *cookie = 1;390 return B_OK;391 }392 // There's only one input.393 return B_BAD_INDEX;394 }395 396 397 void398 SoundConsumer::DisposeInputCookie(int32 /* cookie */)399 {400 // We didn't allocate any memory or set any state in GetNextInput()401 // so this function is a no-op.402 }403 404 405 void406 SoundConsumer::BufferReceived(BBuffer* buffer)407 {408 NODE(stderr, "SoundConsumer::BufferReceived()\n");409 // Whee, a buffer! Update the seek info, if necessary.410 if (m_seeking && buffer->Header()->start_time >= m_tpSeekAt) {411 m_delta = m_tpSeekAt - m_tmSeekTo;412 m_seeking = false;413 }414 // If there is a record hook, let the interested party have at it!415 if (m_recordHook) {416 (*m_recordHook)(m_cookie, buffer->Header()->start_time-m_delta,417 buffer->Data(), buffer->Header()->size_used,418 m_input.format.u.raw_audio);419 } else {420 Record(buffer->Header()->start_time-m_delta, buffer->Data(),421 buffer->Header()->size_used, m_input.format.u.raw_audio);422 }423 // Buffers should ALWAYS be recycled, else whomever is producing them424 // will starve.425 buffer->Recycle();426 }427 428 429 void430 SoundConsumer::ProducerDataStatus(const media_destination& for_whom,431 int32 status, bigtime_t at_media_time)432 {433 if (for_whom != m_input.destination)434 return;435 436 // Tell whomever is interested that the upstream producer will or won't437 // send more data in the immediate future.438 if (m_notifyHook) {439 (*m_notifyHook)(m_cookie, B_PRODUCER_DATA_STATUS, status,440 at_media_time);441 } else442 Notify(B_PRODUCER_DATA_STATUS, status, at_media_time);443 }444 445 446 status_t447 SoundConsumer::GetLatencyFor(const media_destination& for_whom,448 bigtime_t* out_latency, media_node_id* out_timesource)449 {450 // We only accept requests for the one-and-only input of our Node.451 if (for_whom != m_input.destination)452 return B_MEDIA_BAD_DESTINATION;453 454 // Tell the world about our latency information (overridable by user).455 *out_latency = TotalLatency();456 *out_timesource = TimeSource()->Node().node;457 return B_OK;458 }459 460 461 status_t462 SoundConsumer::Connected(const media_source& producer,463 const media_destination& where, const media_format& with_format,464 media_input* out_input)465 {466 NODE(stderr, "SoundConsumer::Connected()\n");467 // Only accept connection requests when we're not already connected.468 if (m_input.source != media_source::null)469 return B_MEDIA_BAD_DESTINATION;470 471 // Only accept connection requests on the one-and-only available input.472 if (where != m_input.destination)473 return B_MEDIA_BAD_DESTINATION;474 475 // Other than that, we accept pretty much anything. The format has been476 // pre-cleared through AcceptFormat(), and we accept any format anyway.477 m_input.source = producer;478 m_input.format = with_format;479 // Tell whomever is interested that there's now a connection.480 if (m_notifyHook)481 (*m_notifyHook)(m_cookie, B_CONNECTED, m_input.name);482 else483 Notify(B_CONNECTED, m_input.name);484 485 // This is the most important line -- return our connection information486 // to the world so it can use it!487 *out_input = m_input;488 return B_OK;489 }490 491 492 void493 SoundConsumer::Disconnected(const media_source& producer,494 const media_destination& where)495 {496 // We can't disconnect something which isn't us.497 if (where != m_input.destination)498 return;499 // We can't disconnect from someone who isn't connected to us.500 if (producer != m_input.source)501 return;502 // Tell the interested party that it's time to leave.503 if (m_notifyHook)504 (*m_notifyHook)(m_cookie, B_DISCONNECTED);505 else506 Notify(B_DISCONNECTED);507 // Mark ourselves as not-connected.508 m_input.source = media_source::null;509 }510 511 512 status_t513 SoundConsumer::FormatChanged(const media_source& producer,514 const media_destination& consumer, int32 from_change_count,515 const media_format& format)516 {517 NODE(stderr, "SoundConsumer::Connected()\n");518 // The up-stream guy feels like changing the format. If we can accept519 // arbitrary format changes, we just say "OK". If, however, we're recording520 // to a file, that's not such a good idea; we only accept format changes521 // that are compatible with the format we're already using. You set this522 // behaviour at compile time by defining ACCEPT_ANY_FORMAT_CHANGE to 1 or523 // 0.524 status_t err = B_OK;525 #if ACCEPT_ANY_FORMAT_CHANGE526 media_format fmt(format);527 err = AcceptFormat(m_input.destination, &fmt);528 #else529 if (m_input.source != media_source::null) {530 err = format_is_compatible(format, m_input.format) ? B_OK531 : B_MEDIA_BAD_FORMAT;532 }533 #endif534 if (err >= B_OK) {535 m_input.format = format;536 if (m_notifyHook) {537 (*m_notifyHook)(m_cookie, B_FORMAT_CHANGED,538 &m_input.format.u.raw_audio);539 } else540 Notify(B_FORMAT_CHANGED, &m_input.format.u.raw_audio);541 }542 return err;543 }544 545 546 void547 SoundConsumer::DoHookChange(void* msg)548 {549 // Tell the old guy we're changing the hooks ...550 if (m_notifyHook)551 (*m_notifyHook)(m_cookie, B_HOOKS_CHANGED);552 else553 Notify(B_HOOKS_CHANGED);554 555 // ... and then do it.556 set_hooks_q * ptr = (set_hooks_q *)msg;557 m_recordHook = ptr->process;558 m_notifyHook = ptr->notify;559 m_cookie = ptr->cookie;560 }561 562 563 status_t564 SoundConsumer::ThreadEntry(void* cookie)565 {566 SoundConsumer* consumer = (SoundConsumer*)cookie;567 consumer->ServiceThread();568 return 0;569 }570 571 572 void573 SoundConsumer::ServiceThread()574 {575 // The Big Bad ServiceThread receives messages aimed at this576 // Node and dispatches them (typically to HandleMessage()).577 // If we were a Producer, we might have to do finicky timing and578 // queued Start()/Stop() processing in here. But we ain't.579 580 // A media kit message will never be bigger than B_MEDIA_MESSAGE_SIZE.581 // Avoid wasing stack space by dynamically allocating at start.582 char* msg = new (nothrow) char[B_MEDIA_MESSAGE_SIZE];583 if (msg == NULL)584 return;585 // Make sure we clean up this data when we exit the function.586 ArrayDeleter<char> _(msg);587 int bad = 0;588 while (true) {589 // Call read_port_etc() with a timeout derived from a virtual function,590 // to allow clients to do special processing if necessary.591 bigtime_t timeout = Timeout();592 int32 code = 0;593 status_t err = read_port_etc(m_port, &code, msg, B_MEDIA_MESSAGE_SIZE,594 B_TIMEOUT, timeout);595 MESSAGE(stderr, "SoundConsumer::ServiceThread() port %" B_PRId32596 " message %#" B_PRIx32 "\n", m_port, code);597 // If we received a message, err will be the size of the message598 // (including 0).599 if (err >= B_OK) {600 // Real messages reset the timeout time.601 m_trTimeout = 0;602 bad = 0;603 if (code == MSG_QUIT_NOW) {604 // Check for our private stop message.605 if (m_notifyHook)606 (*m_notifyHook)(m_cookie, B_NODE_DIES, 0);607 else608 Notify(B_NODE_DIES, 0);609 break;610 } else if (code == MSG_CHANGE_HOOKS) {611 // Else check for our private change-hooks message.612 DoHookChange(msg);613 // Write acknowledge to waiting thread.614 write_port(((set_hooks_q *)msg)->reply, 0, 0, 0);615 } else {616 // Else it has to be a regular media kit message;617 // go ahead and dispatch it.618 HandleMessage(code, msg, err);619 }620 } else if (err == B_TIMED_OUT) {621 // Timing out means that there was no buffer. Tell the interested622 // party.623 if (m_notifyHook)624 (*m_notifyHook)(m_cookie, B_OP_TIMED_OUT, timeout);625 else626 Notify(B_OP_TIMED_OUT, timeout);627 } else {628 // Other errors are bad.629 MESSAGE(stderr, "SoundConsumer: error %#" B_PRIx32 "\n", err);630 bad++;631 // If we receive three bad reads with no good messages inbetween,632 // things are probably not going to improve (like the port633 // disappeared or something) so we call it a day.634 if (bad > 3) {635 if (m_notifyHook)636 (*m_notifyHook)(m_cookie, B_NODE_DIES, bad, err, code, msg);637 else638 Notify(B_NODE_DIES, bad, err, code, msg);639 break;640 }641 }642 }643 }644 645 646 bigtime_t647 SoundConsumer::Timeout()648 {649 // Timeout() is called for each call to read_port_etc() in the service650 // thread to figure out a reasonable time-out value. The default behaviour651 // we've picked is to exponentially back off from one second and upwards.652 // While it's true that 44 back-offs will run us out of precision in a653 // bigtime_t, the time to actually reach 44 consecutive back-offs is longer654 // than the expected market longevity of just about any piece of real655 // estate. Is that the sound of an impending year-fifteen-million software656 // problem? :-)657 m_trTimeout = (m_trTimeout < 1000000) ? 1000000 : m_trTimeout*2;658 return m_trTimeout;659 }660 661 662 bigtime_t663 SoundConsumer::ProcessingLatency()664 {665 // We're saying it takes us 500 us to process each buffer. If all we do is666 // copy the data, it probably takes much less than that, but it doesn't667 // hurt to be slightly conservative.668 return 500LL;669 }670 671 672 bigtime_t673 SoundConsumer::TotalLatency()674 {675 // Had we been a producer that passes buffers on, we'd have to676 // include downstream latency in this value. But we are not.677 return ProcessingLatency();678 }679 680 681 // #pragma mark -682 683 684 void685 SoundConsumer::Record(bigtime_t /*time*/, const void* /*data*/,686 size_t /*size*/, const media_raw_audio_format& /*format*/)687 {688 // If there is no record hook installed, we instead call this function689 // for received buffers.690 }691 692 693 void694 SoundConsumer::Notify(int32 /*cause*/, ...)695 {696 // If there is no notification hook installed, we instead call this697 // function for giving notification of various events.698 }699 700 }