Krotos Modules 3
Loading...
Searching...
No Matches
KwidgetProcessor_Recorder.cpp
Go to the documentation of this file.
2namespace krotos
3{
5 : KwidgetProcessor(owner), Thread("Recorder Operations Thread")
6 {
7 // create a new temp directory for this recorder
8 auto uniqueId = m_uniqueId.toString();
10
11 // start the background thread
12 m_backgroundThread.startThread();
13
14 m_kwidgetRecorder = dynamic_cast<Kwidget_Recorder*>(&getOwner());
15 }
16
18 {
19 // delete this recorders temp dir on recorder destruction
20 if (m_tempDir.isDirectory())
21 {
22 if (!m_tempDir.deleteRecursively())
23 {
24 jassertfalse; // could not delete this recorders temp directory
25 }
26 }
27 }
28
29 void KwidgetProcessor_Recorder::prepare(double sampleRate, int samplesPerBlock)
30 {
31 m_sampleRate = sampleRate;
32 m_lastSamplesPerBlock = samplesPerBlock;
33
34 // currently writing file - located in this recorders temp dir till writing is completed
35 m_writingFile = File::addTrailingSeparator(m_tempDir.getFullPathName()) + m_kwidgetRecorder->getKwidgetID() +
36 "_writing.wav";
37
38 // finished recording - located in this recorders temp dir until another performance is captured
39 m_latestFileName = File::addTrailingSeparator(m_tempDir.getFullPathName()) + m_kwidgetRecorder->getKwidgetID() +
40 "_latest.wav";
41
43
44 // if recording request has been made, call startRecording
46 {
47 m_recordingPending = false;
49 }
50 }
51
53 {
54 // For safety, if the stop thread is running, don't attempt to start recording - shouldn't happen
55 if (isThreadRunning())
56 {
57 jassertfalse; // Tried to start recording while stop thread is still running
58 return;
59 }
60
61 // clear pointer to m_activeWriter and reset threadedWriter
63
64 // if prepare has been called and we are not currently recording..
65 if (m_sampleRate > 0 && m_recording == false)
66 {
68
69 // create outputStream to write to the destination File
70 m_writingFile.deleteFile();
71
72 if (auto fileStream = std::unique_ptr<FileOutputStream>(m_writingFile.createOutputStream()))
73 {
74 WavAudioFormat wavFormat;
75 // Create WAV writer Object that writes to our output steam
76 if (auto writer =
77 wavFormat.createWriterFor(fileStream.get(), m_sampleRate, NUM_BUFFER_CHANNELS, 24, {}, 0))
78 {
79 fileStream.release(); // passes responsibility for deleting the stream to the writer object that is
80 // now using it
81
82 // Now we'll create one of these helper objects which will act as a FIFO buffer, and will
83 // write the data to disk on our background thread
84 m_threadedWriter.reset(
85 new AudioFormatWriter::ThreadedWriter(writer, m_backgroundThread, NUM_BUFFER_SAMPLES));
86 // Now swap over our active writer pointer so that the processBlock will start using it
87 const ScopedLock sl(m_writerLock);
89 m_recording = true;
90 }
91 }
92 }
93 else // if prepare has not yet been called...
94 {
95 // when recalling state it is possible for startRecording to be called prior to prepare
96 // therefor queue a recording request with this flag to be actioned by prepare
97 m_recordingPending = true;
98 }
99 }
100
101 bool KwidgetProcessor_Recorder::bufferIsSilent(AudioSampleBuffer& inputBuffer)
102 {
103 auto maxRmsLevel = minus_infinity_dB;
104 for (int i = 0; i < inputBuffer.getNumChannels(); i++)
105 {
106 auto rmsLevel = Decibels::gainToDecibels(inputBuffer.getRMSLevel(i, 0, inputBuffer.getNumSamples()),
108 maxRmsLevel = rmsLevel > maxRmsLevel ? rmsLevel : maxRmsLevel;
109 }
110 return maxRmsLevel == minus_infinity_dB;
111 }
112
114 {
115 m_recording = false;
116 if (m_activeWriter != nullptr)
117 {
118 // clear pointer to m_activeWriter and reset threadedWriter
119 resetWriter();
120
121 // copy the writing file over to the temp file. so that only the temp file is dragged.
122 File latestRecording(m_latestFileName);
123 m_writingFile.copyFileTo(latestRecording);
124
125 if (m_writingFile.exists() == true)
126 {
127 if (!m_writingFile.deleteFile())
128 {
129 jassertfalse; // writing file not being deleted
130 }
131 }
132
136 }
137 }
138
140 {
141 m_stopRecordingRequest = false; // Stop thread is about to be called so we can cancel any pending request
142 startThread();
143 }
144
145 void KwidgetProcessor_Recorder::process(AudioBuffer<float>& inputBuffer)
146 {
147 if (!m_recording) // If recording is not enabled, then there is nothing to do here
148 {
149 return;
150 }
151
152 // there are situations where prepare is not called when there is a block size or sr change
153 // therefor we re-calculate the inactive frames before stopping (KST-2245)
154 // NOTE: this logic only recalculates for block size changes currently, not sr changes
155 const int currentSamplesPerBlock = inputBuffer.getNumSamples();
156 if (currentSamplesPerBlock != m_lastSamplesPerBlock && currentSamplesPerBlock > 0)
157 {
158 m_inactiveFramesBeforeStop = jmax<int>(1, m_postRollSamples / currentSamplesPerBlock);
159 m_lastSamplesPerBlock = currentSamplesPerBlock; // keep track of the last block size
160 }
161
163 {
165 {
167 }
168 }
169 else
170 {
171 m_tailFramesCounter = m_inactiveFramesBeforeStop; // Reset tail timeout
172 }
173
174 // The most accurate place to calculate when to stop the recorder is here in the precess method,
175 // however, because it is time consuming, the process of stopping the recorder is launched
176 // from here on its own thread
177 // As a failsafe, we always test to see if we need to stop, and whether stopping is already in progress
178 // If we are in the body of the process method, we know that the m_recording flag is true
179 if ((m_tailFramesCounter == 0) && bufferIsSilent(inputBuffer)) // Have conditions for stopping been met ?
180 {
181 // If the stop thread is already running then we don't need to do anything
182 // If the thread finishes and has not stopped the recording for whatever reason
183 // execution will return here and the thread will be restarted
184 if (!isThreadRunning())
185 {
186 stopRecording(); // Otherwise start the thread which will stop the recording
187 }
188 }
189 else
190 {
191 // Recording is active, this is where we actually write to the stream
192 const ScopedLock sl(m_writerLock);
193 m_activeWriter.load()->write(inputBuffer.getArrayOfReadPointers(), inputBuffer.getNumSamples());
194 }
195 }
196
197 void KwidgetProcessor_Recorder::noteOn(int midiNoteNumber, float velocity, int)
198 {
199 ignoreUnused(velocity);
200
201 // Clear pending stop recording requests
203
204 if (m_recording)
205 {
206 // Reset tail counter to extend recording time
208 }
209 else
210 {
211 // Start the recorder
215 }
216 }
217
219 {
220 ignoreUnused(velocity);
221 if (m_recording)
222 {
224 }
225 }
226
228 {
229 // First clear the pointer to the activeWriter to stop the process from using it
230 const ScopedLock sl(m_writerLock);
231 m_activeWriter = nullptr;
232 // Now we can delete the writer object. It's done in this order because the deletion could
233 // take a little time while remaining data gets flushed to disk, so it's best to avoid blocking
234 // the audio callback while this happens.
235 m_threadedWriter.reset();
236 }
237
239 {
240 stop();
241 }
242
244 {
245 // setting the time in seconds of a recording release after silence is detected
246 using KAP = KwidgetAudioProcessor;
247
248 const auto recorderReleaseTimeValue = KAP::settingsFile.getSetting(KAP::recorderReleaseTime);
249
250 // 1.5 second fallback time
251 const float releaseTime =
252 recorderReleaseTimeValue.isVoid() ? 1.5f : static_cast<float>(recorderReleaseTimeValue);
253
254 m_postRollSamples = releaseTime * static_cast<float>(m_sampleRate);
255
257 }
258} // namespace krotos
static File newTempDirectory(const String &)
Definition AssetManager.cpp:333
Definition Kwidget_Recorder.h:4
void addFilename(String filename)
Definition Kwidget_Recorder.h:31
void setDraggable(bool draggableState)
Definition Kwidget_Recorder.cpp:62
void setActive(bool activeState)
Definition Kwidget_Recorder.cpp:56
Definition KwidgetAudioProcessor.h:12
Definition Kwidget.h:8
const String & getKwidgetID() const
Definition Kwidget.cpp:367
std::atomic< AudioFormatWriter::ThreadedWriter * > m_activeWriter
Definition KwidgetProcessor_Recorder.h:77
int m_postRollSamples
Definition KwidgetProcessor_Recorder.h:64
volatile std::atomic< int > m_inactiveFramesBeforeStop
Definition KwidgetProcessor_Recorder.h:62
void updateReleaseTime()
Update the cached release time from the internal settings file.
Definition KwidgetProcessor_Recorder.cpp:243
void process(AudioBuffer< float > &buffer) override
Definition KwidgetProcessor_Recorder.cpp:145
int m_tailFramesCounter
Definition KwidgetProcessor_Recorder.h:65
Kwidget_Recorder * m_kwidgetRecorder
Definition KwidgetProcessor_Recorder.h:87
void noteOn(int midiNoteNumber, float velocity, int) override
Definition KwidgetProcessor_Recorder.cpp:197
std::unique_ptr< AudioFormatWriter::ThreadedWriter > m_threadedWriter
Definition KwidgetProcessor_Recorder.h:75
void startRecording()
Definition KwidgetProcessor_Recorder.cpp:52
void run() override
Definition KwidgetProcessor_Recorder.cpp:238
volatile std::atomic< bool > m_recording
Definition KwidgetProcessor_Recorder.h:59
bool bufferIsSilent(AudioSampleBuffer &inputBuffer)
Definition KwidgetProcessor_Recorder.cpp:101
CriticalSection m_writerLock
Definition KwidgetProcessor_Recorder.h:76
const int NUM_BUFFER_CHANNELS
Definition KwidgetProcessor_Recorder.h:80
Uuid m_uniqueId
Definition KwidgetProcessor_Recorder.h:82
void noteOff(float velocity) override
Definition KwidgetProcessor_Recorder.cpp:218
void prepare(double sampleRate, int samplesPerBlock) override
Definition KwidgetProcessor_Recorder.cpp:29
const int NUM_BUFFER_SAMPLES
Definition KwidgetProcessor_Recorder.h:79
File m_writingFile
Definition KwidgetProcessor_Recorder.h:68
double m_sampleRate
Definition KwidgetProcessor_Recorder.h:71
KwidgetProcessor_Recorder(Kwidget &owner)
Definition KwidgetProcessor_Recorder.cpp:4
bool m_recordingPending
Definition KwidgetProcessor_Recorder.h:66
TimeSliceThread m_backgroundThread
Definition KwidgetProcessor_Recorder.h:74
volatile std::atomic< bool > m_stopRecordingRequest
Definition KwidgetProcessor_Recorder.h:60
~KwidgetProcessor_Recorder()
Definition KwidgetProcessor_Recorder.cpp:17
File m_tempDir
Definition KwidgetProcessor_Recorder.h:83
int m_lastSamplesPerBlock
Definition KwidgetProcessor_Recorder.h:63
const float minus_infinity_dB
Definition KwidgetProcessor_Recorder.h:85
void resetWriter()
Definition KwidgetProcessor_Recorder.cpp:227
void stop()
Definition KwidgetProcessor_Recorder.cpp:113
String m_latestFileName
Definition KwidgetProcessor_Recorder.h:69
void stopRecording()
Definition KwidgetProcessor_Recorder.cpp:139
An interface for an audio processor designed for modular use.
Definition KwidgetProcessor.h:8
Kwidget & getOwner() const
Definition KwidgetProcessor.h:164
Definition AirAbsorptionFilter.cpp:2