hydrogen 1.2.3
DiskWriterDriver.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22#include <unistd.h>
23
24
27#include <core/EventQueue.h>
29#include <core/Hydrogen.h>
30#include <core/Basics/Pattern.h>
33
34#include <pthread.h>
35#include <cassert>
36
37#if defined(WIN32) || _DOXYGEN_
38#include <windows.h>
39/*
40 * In Windows the unistd function sleep( seconds ) is not available.
41 * Treat sleep( SECONDS ) as a macro that uses SleepEx.
42 * Convert seconds to milliseconds for the first argument of SleepEx.
43 * Use false for the second argument of SleepEx.
44 * This way SleepEx always returns 0, after the specified time has passed.
45 */
46#define sleep( SECONDS ) SleepEx( SECONDS * 1000, false )
47#endif
48
49namespace H2Core
50{
51
53
54void* diskWriterDriver_thread( void* param )
55{
56 Base * __object = ( Base * )param;
57 DiskWriterDriver *pDriver = ( DiskWriterDriver* )param;
58
60
61 auto pAudioEngine = Hydrogen::get_instance()->getAudioEngine();
62
63 __INFOLOG( "DiskWriterDriver thread start" );
64
65 // always rolling, no user interaction
66 pAudioEngine->play();
67 SF_INFO soundInfo;
68 soundInfo.samplerate = pDriver->m_nSampleRate;
69// soundInfo.frames = -1;//getNFrames(); ///\todo: da terminare
70 soundInfo.channels = 2;
71 //default format
72 int sfformat = 0x010000; //wav format (default)
73 int bits = 0x0002; //16 bit PCM (default)
74 //sf_format switch
75 if( pDriver->m_sFilename.endsWith(".aiff") || pDriver->m_sFilename.endsWith(".AIFF") ){
76 sfformat = 0x020000; //Apple/SGI AIFF format (big endian)
77 }
78 if( pDriver->m_sFilename.endsWith(".flac") || pDriver->m_sFilename.endsWith(".FLAC") ){
79 sfformat = 0x170000; //FLAC lossless file format
80 }
81 if( ( pDriver->m_nSampleDepth == 8 ) && ( pDriver->m_sFilename.endsWith(".aiff") || pDriver->m_sFilename.endsWith(".AIFF") ) ){
82 bits = 0x0001; //Signed 8 bit data works with aiff
83 }
84 if( ( pDriver->m_nSampleDepth == 8 ) && ( pDriver->m_sFilename.endsWith(".wav") || pDriver->m_sFilename.endsWith(".WAV") ) ){
85 bits = 0x0005; //Unsigned 8 bit data needed for Microsoft WAV format
86 }
87 if( pDriver->m_nSampleDepth == 16 ){
88 bits = 0x0002; //Signed 16 bit data
89 }
90 if( pDriver->m_nSampleDepth == 24 ){
91 bits = 0x0003; //Signed 24 bit data
92 }
93 if( pDriver->m_nSampleDepth == 32 ){
94 bits = 0x0004;
95 }
96
97 soundInfo.format = sfformat|bits;
98
99// #ifdef HAVE_OGGVORBIS
100
101 //ogg vorbis option
102 if( pDriver->m_sFilename.endsWith( ".ogg" ) | pDriver->m_sFilename.endsWith( ".OGG" ) ) {
103 soundInfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
104 }
105// #endif
106
107
109// SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian). */
110// SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */
111// SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */
112// SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */
113// SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */
114// SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */
115// SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */
116// SF_FORMAT_VOC = 0x080000, /* VOC files. */
117// SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */
118// SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */
119// SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */
120// SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */
121// SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */
122// SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */
123// SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */
124// SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */
125// SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */
126// SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */
127// SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */
128// SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */
129// SF_FORMAT_CAF = 0x180000, /* Core Audio File format */
130// SF_FORMAT_OGG
132// SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */
133// SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */
134// SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */
135// SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */
137// SF_FORMAT_VORBIS
138
139 if ( !sf_format_check( &soundInfo ) ) {
140 __ERRORLOG( "Error in soundInfo" );
141 pthread_exit( nullptr );
142 return nullptr;
143 }
144
145
146 SNDFILE* m_file = sf_open( pDriver->m_sFilename.toLocal8Bit(), SFM_WRITE, &soundInfo );
147 if ( m_file == nullptr ) {
148 __ERRORLOG( QString( "Unable to open file [%1] using libsndfile: %2" )
149 .arg( pDriver->m_sFilename )
150 .arg( sf_strerror( nullptr ) ) );
151 pthread_exit( nullptr );
152 return nullptr;
153 }
154
155
156 float *pData = new float[ pDriver->m_nBufferSize * 2 ]; // always stereo
157
158 float *pData_L = pDriver->m_pOut_L;
159 float *pData_R = pDriver->m_pOut_R;
160
161
162 Hydrogen* pHydrogen = Hydrogen::get_instance();
163 auto pSong = pHydrogen->getSong();
164 auto pSampler = pHydrogen->getAudioEngine()->getSampler();
165
166 std::vector<PatternList*> *pPatternColumns = pSong->getPatternGroupVector();
167 int nColumns = pPatternColumns->size();
168
169 int nPatternSize, nBufferWriteLength;
170 float fBpm;
171 float fTicksize = 0;
172 int nMaxNumberOfSilentFrames = 200;
173 for ( int patternPosition = 0; patternPosition < nColumns; ++patternPosition ) {
174
175 PatternList *pColumn = ( *pPatternColumns )[ patternPosition ];
176 if ( pColumn->size() != 0 ) {
177 nPatternSize = pColumn->longest_pattern_length();
178 } else {
179 nPatternSize = MAX_NOTES;
180 }
181
182 fBpm = AudioEngine::getBpmAtColumn( patternPosition );
183 fTicksize = AudioEngine::computeTickSize( pDriver->m_nSampleRate, fBpm,
184 pSong->getResolution() );
185
186 //here we have the pattern length in frames dependent from bpm and samplerate
187 int nPatternLengthInFrames = fTicksize * nPatternSize;
188 int nFrameNumber = 0;
189 int nLastRun = 0;
190 int nSuccessiveZeros = 0;
191 while ( ( patternPosition < nColumns - 1 && // render all
192 // frames in
193 // pattern
194 nFrameNumber < nPatternLengthInFrames ) ||
195 ( patternPosition == nColumns - 1 && // render till
196 // all notes are
197 // processed
198 ( nFrameNumber < nPatternLengthInFrames ||
199 pSampler->isRenderingNotes() ) ) ) {
200
201 int nUsedBuffer = pDriver->m_nBufferSize;
202
203 // This will calculate the size from -last- (end of
204 // pattern) used frame buffer, which is mostly smaller
205 // than pDriver->m_nBufferSize. But it only applies for
206 // all patterns except of the last one. The latter we will
207 // let ring until there is no further audio to process.
208 if( patternPosition < nColumns - 1 &&
209 nPatternLengthInFrames - nFrameNumber < pDriver->m_nBufferSize ){
210 nLastRun = nPatternLengthInFrames - nFrameNumber;
211 nUsedBuffer = nLastRun;
212 };
213
214 int ret = pDriver->m_processCallback( nUsedBuffer, nullptr );
215
216 // In case the DiskWriter couldn't acquire the lock of the AudioEngine.
217 while( ret == 2 ) {
218 ret = pDriver->m_processCallback( nUsedBuffer, nullptr );
219 }
220
221 if ( patternPosition == nColumns - 1 &&
222 nPatternLengthInFrames - nFrameNumber < nUsedBuffer ) {
223 // The next buffer at least partially exceeds the song
224 // size in ticks. As soon as it does we start to count
225 // zeros in both audio channels. The moment we
226 // encounter more than X we will stop the audio
227 // export. Just waiting for the Sampler to finish
228 // rendering is not sufficient because the Sample
229 // itself can be zero padded at the end causing the
230 // resulting .wav file to be inconsistent in terms of
231 // length depending on the buffer sized use during
232 // export.
233 //
234 // We are at the last pattern and just waited for the
235 // Sampler to finish rendering all notes (at an
236 // arbitrary point within the buffer).
237 nBufferWriteLength = 0;
238
239 int nSilentFrames = 0;
240 for ( int ii = 0; ii < nUsedBuffer; ++ii ) {
241 ++nBufferWriteLength;
242
243 if ( std::abs( pData_L[ii] ) == 0 &&
244 std::abs( pData_R[ii] ) == 0 ) {
245 ++nSuccessiveZeros;
246 }
247
248 if ( nSuccessiveZeros == nMaxNumberOfSilentFrames ) {
249 break;
250 }
251 }
252 } else {
253 nBufferWriteLength = nUsedBuffer;
254 }
255
256 nFrameNumber += nBufferWriteLength;
257
258 for ( unsigned ii = 0; ii < nBufferWriteLength; ii++ ) {
259 if( pData_L[ ii ] > 1 ) {
260 pData[ ii * 2 ] = 1;
261 } else if( pData_L[ ii ] < -1 ) {
262 pData[ ii * 2 ] = -1;
263 } else {
264 pData[ ii * 2 ] = pData_L[ ii ];
265 }
266
267 if( pData_R[ ii ] > 1 ){
268 pData[ ii * 2 + 1 ] = 1;
269 } else if ( pData_R[ ii ] < -1 ) {
270 pData[ ii * 2 + 1 ] = -1;
271 } else {
272 pData[ ii * 2 + 1 ] = pData_R[ ii ];
273 }
274 }
275
276 const int res = sf_writef_float( m_file, pData, nBufferWriteLength );
277 if ( res != ( int )nBufferWriteLength ) {
278 __ERRORLOG( QString( "Error during sf_write_float. Floats written: [%1], target: [%2]. %3" )
279 .arg( res )
280 .arg( nBufferWriteLength )
281 .arg( sf_strerror( nullptr ) ) );
282 }
283
284 // Sampler is still rendering notes put we seem to have
285 // reached the zero padding at the end of the
286 // corresponding samples.
287 if ( nSuccessiveZeros == nMaxNumberOfSilentFrames ) {
288 break;
289 }
290 }
291
292 // this progress bar method is not exact but ok enough to give users a usable visible progress feedback
293 int nPercent = static_cast<int>( ( float )(patternPosition +1) /
294 ( float )nColumns * 100.0 );
295 if ( nPercent < 100 ) {
297 }
298 }
299
300 // Explicitly mark export as finished.
302
303 delete[] pData;
304 pData = nullptr;
305
306 sf_close( m_file );
307
308 __INFOLOG( "DiskWriterDriver thread end" );
309
310 pthread_exit( nullptr );
311 return nullptr;
312}
313
314
315
317 : AudioOutput()
318 , m_nSampleRate( 4800 )
319 , m_nSampleDepth( 32 )
320 , m_processCallback( processCallback )
321 , m_nBufferSize( 1024 )
322 , m_pOut_L( nullptr )
323 , m_pOut_R( nullptr ) {
324}
325
326
327
330
331
332
333int DiskWriterDriver::init( unsigned nBufferSize )
334{
335 INFOLOG( QString( "Init, buffer size: %1" ).arg( nBufferSize ) );
336
337 m_nBufferSize = nBufferSize;
338
339 m_pOut_L = new float[ m_nBufferSize ];
340 m_pOut_R = new float[ m_nBufferSize ];
341
342 return 0;
343}
344
346{
347 return 0;
348}
349
351{
352 INFOLOG( "" );
353
354 pthread_attr_t attr;
355 pthread_attr_init( &attr );
356
357 pthread_create( &diskWriterDriverThread, &attr, diskWriterDriver_thread, this );
358}
359
362{
363 INFOLOG( "" );
364
365 pthread_join( diskWriterDriverThread, NULL );
366
367 delete[] m_pOut_L;
368 m_pOut_L = nullptr;
369
370 delete[] m_pOut_R;
371 m_pOut_R = nullptr;
372
373}
374
376{
377 return m_nSampleRate;
378}
379};
#define __ERRORLOG(x)
Definition Object.h:251
#define INFOLOG(x)
Definition Object.h:237
#define __INFOLOG(x)
Definition Object.h:249
static float getBpmAtColumn(int nColumn)
static float computeTickSize(const int nSampleRate, const float fBpm, const int nResolution)
Calculates the number of frames that make up a tick.
Sampler * getSampler() const
Base abstract class for audio output classes.
Definition AudioOutput.h:39
Base class.
Definition Object.h:62
Driver for export audio to disk.
virtual void disconnect() override
disconnect
virtual int init(unsigned nBufferSize) override
DiskWriterDriver(audioProcessCallback processCallback)
virtual int connect() override
audioProcessCallback m_processCallback
virtual unsigned getSampleRate() override
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
Definition EventQueue.h:224
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
Hydrogen Audio Engine.
Definition Hydrogen.h:54
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:649
PatternList is a collection of patterns.
Definition PatternList.h:43
int longest_pattern_length(bool bIncludeVirtuals=true) const
Get the length of the longest pattern in the list.
int size() const
returns the numbers of patterns
#define MAX_NOTES
Maximum number of notes.
Definition config.dox:79
int(* audioProcessCallback)(uint32_t, void *)
Definition AudioOutput.h:32
@ EVENT_PROGRESS
Definition EventQueue.h:98
pthread_t diskWriterDriverThread
void * diskWriterDriver_thread(void *param)