PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
sdl/PlatformAudioOut.cpp
Go to the documentation of this file.
1//* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
3// Name: src/platforms/sdl/PlatformAudioOut.cpp
4// Purpose: Contains platform-specific audio playing routines
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2003-2026 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9//
10//
11// This file is part of the PEBL project.
12//
13// PEBL is free software; you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation; either version 2 of the License, or
16// (at your option) any later version.
17//
18// PEBL is distributed in the hope that it will be usefu2l,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with PEBL; if not, write to the Free Software
25// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
28#include "PlatformAudioOut.h"
29#include "../../devices/PAudioOut.h"
30
31#include "../../utility/PEBLPath.h"
32#include "../../utility/PError.h"
33#include "../../libs/PEBLEnvironment.h"
34
35
36#ifdef PEBL_EMSCRIPTEN
37#include "../../base/Evaluator-es.h"
38#else
39#include "../../base/Evaluator.h"
40#endif
41
42#include "SDL.h"
43#include "SDL_audio.h"
44
45#ifdef PEBL_MIXER
46#include "SDL_mixer.h"
47#endif
48
49#include <cmath>
50#include <fstream>
51#include <memory.h>
52#include <cerrno>
53#include <cstring>
54
55void PlayCallBack(void * dummy, Uint8 * stream, int len);
56
57#ifndef PEBL_MIXER
58//initiate static data for callback.
60
61#endif
62
63using namespace std;
64using std::string;
65using std::cerr;
66// cout removed - use cerr for debug output
67using std::endl;
68
69bool PlatformAudioOut::mLoaded = false;
70
71
74 mRepeats(0)
75{
76
77 mChannel=-1;
78 mAmplitudeLeft=1.0;
79 mAmplitudeRight=1.0;
80
81#ifdef PEBL_MIXER
82 //Initialize mMixerSample to NULL
83 mMixerSample = NULL;
84 mRecordPos = 0; // Initialize recorded position to 0
85 SDL_zero(mOriginalSpec); // Initialize audio spec to zero
86
87 if(!mLoaded)
88 {
89 int result = Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 512);
90 if( result < 0 )
91 {
92 fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
93 exit(-1);
94 }
95 Initialize();
96 mLoaded = true;
97 }
98#endif
99
100}
101
102
103
104PlatformAudioOut::PlatformAudioOut(const string & soundfilename):
106 mRepeats(0)
107{
108
109 //Check to see if we can find the sound file; if not, call everything off.
110 string mFilename = Evaluator::gPath.FindFile(soundfilename);
111
112 if(mFilename == "")
113 PError::SignalFatalError(string("Unable to find sound file [") + soundfilename + string("]."));
114
115 // Initialize();
116 LoadSoundFile(mFilename);
117}
118
119
121{
122
123 mLoaded = false;
124#ifdef PEBL_MIXER
125 if(mMixerSample) {
126 // For audio input buffers, the buffer is owned by AudioInfo, not by us
127 // Set abuf to NULL before freeing to prevent Mix_FreeChunk from freeing shared memory
128 if(mFilename == "<INTERNALLY GENERATED>") {
129 mMixerSample->abuf = NULL;
130 }
131 Mix_FreeChunk(mMixerSample);
132 }
133
134 Mix_CloseAudio(); //This will be called for each file
135 Mix_Quit();
136
137#else
138
139 SDL_FreeWAV(mWave.audio);
140#endif
141
142}
143
144
145bool PlatformAudioOut::LoadSoundFile(const string & soundfilename)
146{
147 //Check to see if we can find the sound file; if not, call everything off.
148 mFilename = Evaluator::gPath.FindFile(soundfilename);
149
150 if(mFilename == "")
151 PError::SignalFatalError(string("Unable to find sound file [") + soundfilename + string("]."));
152
153#ifdef PEBL_MIXER
154 // std::cerr << "Loading : " << soundfilename << endl;
155 // std::cerr << "located at: " << mFilename << endl;
156
157
158 //Mix_LoadWAV destroys the filename, so let's make a copy.
159 char * copy = (char*)malloc(strlen(mFilename.c_str()) + 1);
160 strcpy(copy, mFilename.c_str());
161
162
163
164 mMixerSample = Mix_LoadWAV(mFilename.c_str());
165
166 /* Load the wave file into memory*/
167 if (mMixerSample == NULL)
168 {
169 std::cerr << "Couldn't load " << copy << ": " << SDL_GetError() << std::endl;
170 return false;
171 } else {
172
173
174 }
175 free(copy);
176#else
177 /* Load the wave file into memory */
178 if ( SDL_LoadWAV(mFilename.c_str(), &mWave.spec, &mWave.audio, &mWave.audiolen) == NULL )
179 {
180 std::cerr << "Couldn't load " << mFilename << ": " << SDL_GetError() << std::endl;
181 return false;
182 }
183
184
185
186 //If gWaveStream is loaded, we already have a frequency/format/channels set.
187 //we need to convert the current sound to that format.
188
189
190 if(gWaveStream)
191 {
192
193 bool samefreq = mWave.spec.freq==gWaveStream->spec.freq;
194 bool sameformat = mWave.spec.format==gWaveStream->spec.format;
195 bool samechannels = mWave.spec.channels==gWaveStream->spec.channels;
196
197
198 {
199 //Only convert if we have a different format
200 if(!(samefreq & sameformat&samechannels & samechannels))
201 {
202 PError::SignalWarning("Warning: input file will be converted to new playback format.");
204 }}
205
206 }
207
209
210 mLoaded = true;
211
212 mWave.name = mFilename.c_str();
213 mWave.spec.callback = PlayCallBack;
214 mWave.spec.userdata = &mWave;
215
216 //Set the global playback wave to the current wave.
217 if(!gWaveStream)
218 {
219 gWaveStream = &mWave;
220 }
221#endif
222
223 return true;
224
225}
226
227// This converts the given audio in mWave to the to the
228// audio format info
229//
230#ifndef PEBL_MIXER
232{
233
234
235
236
237 // Code heavily borrowed from some SDL tutorial
238
239 SDL_AudioCVT cvt; /* audio format conversion structure */
240 SDL_AudioSpec loaded = mWave.spec; /* format of the loaded data */
241 SDL_AudioSpec target = info.spec;
242 Uint8 *new_buf;
243
244
245 /* Build a conversion structure for converting the samples.
246 This structure contains the data SDL needs to quickly
247 convert between sample formats. */
248
249 std::cerr << "Converting from " << loaded.format<<"|"<<(int)loaded.channels<<"|"<< loaded.freq<<"to "<<
250 target.format<<"|"<< (int)target.channels<<"|"<<target.freq<<std::endl;
251
252 SDL_AudioSpec * desired=(SDL_AudioSpec *)malloc(sizeof(SDL_AudioSpec));
253 desired->freq=target.freq;
254 desired->format=target.format;
255 desired->channels = target.channels;
256 desired->samples=target.samples;
257 desired->callback=NULL;
258 desired->userdata=NULL;
259
260
261 if (SDL_BuildAudioCVT(&cvt,
262 loaded.format, loaded.channels, loaded.freq,
263 desired->format, desired->channels, desired->freq) < 0)
264 {
265 PError::SignalFatalError(Variant("Unable to convert sound: ") + Variant(SDL_GetError()));
266 }
267
268 if(cvt.needed==0)
269 {
270 PError::SignalWarning(Variant("Unable to convert sound. Be sure all of your sounds are saved in the same format.") + Variant(SDL_GetError()));
271 }
272
273 //set cvt.len to the size of the sourcedata.
274 cvt.len = mWave.audiolen;
275
276 //Allocate a big enough buffer to do the conversion:
277 new_buf = (Uint8 *) malloc(cvt.len * cvt.len_mult);
278 if (new_buf == NULL)
279 {
280 PError::SignalFatalError("Memory allocation failed in PlatformAudioOut::ConvertAudio");
281 }
282
283 /* Copy the sound samples into the new buffer. */
284 memcpy(new_buf, mWave.audio, mWave.audiolen);
285
286 /* Perform the conversion on the new buffer. */
287 cvt.buf = new_buf;
288
289
290
291#if 0
292 cerr << "Conversion information: " ;
293 std::cerr <<"Needed: "<< cvt.needed << std::endl;
294 std::cerr <<"srcformat: "<< cvt.src_format << std::endl;
295 // std::cerr <<"destformat: "<< cvt.dest_format << std::endl;
296 std::cerr <<"rate_incr: "<< cvt.rate_incr << std::endl;
297 std::cerr <<"len: "<< cvt.len << std::endl;
298 std::cerr <<"len_cvt: "<< cvt.len_cvt << std::endl;
299 std::cerr <<"len_mult: "<< cvt.len_mult<< std::endl;
300 std::cerr <<"ratio: "<< cvt.len_ratio<< std::endl;
301
302 cerr << "CONVERTING\n";
303#endif
304
305 if (SDL_ConvertAudio(&cvt) < 0)
306 {
307
308 PError::SignalFatalError(Variant("Audio conversion error:")+ Variant(SDL_GetError()));
309 }
310
311
312 /* Swap the converted data for the original. */
313 SDL_FreeWAV(mWave.audio);
314
315
316 mWave.audio = new_buf;
317 mWave.audiolen = mWave.audiolen * cvt.len_mult;
318
319
320 mWave.spec.freq=info.spec.freq;
321 mWave.spec.format=info.spec.format;
322 mWave.spec.channels=info.spec.channels;
323 mWave.spec.silence=info.spec.silence;
324 // mWave.spec.samples=info.spec.samples;
325 // mWave.spec.size=info.spec.size;
326
327
328#if 1
329 switch(mWave.spec.format)
330 {
331
332 case AUDIO_U8:
333 mWave.bytesPerSample = 8;
334 break;
335
336 case AUDIO_U16:
337 mWave.bytesPerSample = 2;
338 break;
339
340
341 case AUDIO_S8:
342 mWave.bytesPerSample = 1;
343 break;
344
345 case AUDIO_S16:
346 mWave.bytesPerSample = 2;
347 break;
348
349
350 default:
351 mWave.bytesPerSample = 2;
352 }
353
354#endif
355
356
357
358 return true;
359 }
360#endif
361
362bool PlatformAudioOut::CreateSineWave(float freq, long unsigned int mslength, long double amplitude)
363{
364
365
366#ifdef PEBL_MIXER
367 return false;
368#else
369 /* Allocate a desired SDL_AudioSpec */
370 SDL_AudioSpec *spec = (SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec));
371
372
373 if(gWaveStream)
374 {
375 //Use the already-available spec.
376 //cerr <<"using preloaded sound spec\n";
377
378 memcpy(spec,&(gWaveStream->spec),sizeof(SDL_AudioSpec));
379
380 }
381 else
382 {
383 //Create a new spec
384
385 /* Allocate space for the obtained SDL_AudioSpec */
386 // obtained = (SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec));
387
388 spec->freq =44100;
389 spec->format=AUDIO_U8;
390 spec->channels=1;
391 spec->silence=0x80;
392 spec->samples=4096;
393 spec->callback= PlayCallBack;
394 spec->userdata=&mWave;
395 }
396
397 //mslength is time in ms.
398 //compute length in samples
399
400 int bits=0;
401 if((spec->format == AUDIO_U8) |
402 (spec->format == AUDIO_S8) )
403 {
404 bits=1;
405
406 }else if((spec->format == AUDIO_S16 )|
407 (spec->format == AUDIO_U16))
408 {
409 bits =2;
410
411 }
412
413 long unsigned int length = mslength/1000.0*spec->freq*spec->channels*bits;
414
415 Uint8 *data = new Uint8[length];
416 int dat;
417 double base=0.0;
418
419 for(unsigned int i=0; i<length;i+=spec->channels)
420 {
421
422 if(spec->format==AUDIO_U8)
423 {
424 //base needs to be different for different formats
425 base = (sin(i*6.28/(spec->freq/freq))*amplitude+1)/2;
426 }
427
428 if(base<0)base=0;
429 if(base>1)base=1;
430 //base is bounded between 0 and 1
431 dat = int(base*256);
432 //cerr << base << "," << amplitude << ","<< dat << endl;
433
434 //Copy dat to each channel
435 for(int j = 0; j < spec->channels;j+=bits)
436 data[i+j] = dat;
437 }
438
439
440
441 LoadSoundFromData(data,length,spec);
442
443 return true;
444#endif
445}
446
447
448
449
450//;unsigned int freq,int size)
452 long unsigned int size,
453 SDL_AudioSpec *spec,
454 Uint32 recordpos)
455{
456
457#ifdef PEBL_MIXER
458 // Create a Mix_Chunk from the raw audio buffer
459 // This is needed for audio input buffers to work with SDL_mixer
460
461 mMixerSample = (Mix_Chunk*)malloc(sizeof(Mix_Chunk));
462 if(!mMixerSample) {
463 PError::SignalWarning("Failed to allocate Mix_Chunk in LoadSoundFromData()");
464 return false;
465 }
466
467 // For audio input buffers, we DON'T copy the buffer - we share it!
468 // The recording callback will write directly to this buffer.
469 // IMPORTANT: We must NOT free this buffer in the destructor since it's owned by AudioInfo
470 mMixerSample->abuf = buffer; // Share the buffer, don't copy
471 mMixerSample->alen = size;
472 mMixerSample->volume = MIX_MAX_VOLUME; // Default to max volume (128)
473
474 // Store the actual recorded position
475 // If recordpos is 0 (default), use the full buffer size
476 mRecordPos = (recordpos > 0) ? recordpos : size;
477
478 // Store the original audio spec from recording (contains correct channel count)
479 // This is critical for saving WAV files with the correct format
480 if(spec) {
481 mOriginalSpec = *spec;
482 }
483
484 mFilename = "<INTERNALLY GENERATED>";
485 mLoaded = true;
486
487 cerr << "------------------------------------\n";
488 cerr << "Loading Sound Data from buffer.\n";
489 cerr << "Buffer address: " << (void*)buffer << "\n";
490 cerr << "Buffer size: " << size << " bytes\n";
491 cerr << "Recorded size: " << mRecordPos << " bytes\n";
492 cerr << "Original spec: " << spec->freq << "Hz, " << (int)spec->channels << " channels, format=" << spec->format << "\n";
493 cerr << "------------------------------------\n";
494
495 return true;
496#else
497
498 /* setup audio */
499 //SDL_AudioSpec spec;
500 // SDL_AudioSpec obtained;
501
502
503 /* Allocate a desired SDL_AudioSpec */
504 SDL_AudioSpec *spec2 = (SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec));
505 memcpy(spec2,spec,sizeof(SDL_AudioSpec));
506
507 /* Allocate space for the obtained SDL_AudioSpec */
508 // obtained = (SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec));
509
510
511 /*
512 spec.freq =freq;
513 spec.format=AUDIO_U8;
514 spec.channels=1;
515 spec.silence=0x80;
516 spec.samples=4096;
517 spec.callback= PlayCallBack;
518 spec.userdata=&mWave;
519 */
520
521
522 mWave.spec = *spec2;
523 mWave.audio=buffer;
524 mWave.audiopos=0;
525 mWave.audiolen=(unsigned int)size;
526 mWave.volume=100;
527 mWave.name="Generated data";
528
529
530 /* Load the wave file into memory */
531 // if ( SDL_LoadWAV_RW(rw,1, &mWave.spec, &mWave.audio, &mWave.audiolen) == NULL )
532 // {
533 // std::cerr << "Couldn't load created audio data: " << SDL_GetError() << std::endl;
534 // return false;
535 // }
536
537
538 cerr << "------------------------------------\n";
539 cerr << "Loading Sound Data.\n";
541 mLoaded = true;
542
543
544
545
546 //Check to see if we can find the sound file; if not, call everything off.
547 mFilename = "<INTERNALLY GENERATED>";
548
549
550 mWave.name = mFilename.c_str();
551 mWave.spec.callback = PlayCallBack;
552 mWave.spec.userdata = &mWave;
553
554
555 //Set the global playback wave to the current wave.
556 if(!gWaveStream)
557 {
558 gWaveStream = &mWave;
559 }
560
561 return true;
562#endif
563}
564
565
566//
567// This is nearly identical to a piece of code in PlatformAudioIn
568//
570{
571#ifdef PEBL_MIXER
572 std::cerr << "=== ENTERING SaveBufferToWave ===\n";
573 std::cerr << "mRecordPos at function entry: " << mRecordPos << "\n";
574 std::cerr << "mMixerSample pointer: " << (void*)mMixerSample << "\n";
575
576 // SDL_mixer mode: extract audio data from Mix_Chunk and write to WAV file
577 if(!mMixerSample) {
578 PError::SignalWarning("No audio buffer to save in SaveBufferToWave()");
579 return;
580 }
581
582 std::cerr << "mMixerSample->alen: " << mMixerSample->alen << "\n";
583 std::cerr << "mMixerSample->abuf: " << (void*)mMixerSample->abuf << "\n";
584
585 // Use the original audio spec from recording (not Mix_QuerySpec which returns playback format)
586 // For audio input buffers, mOriginalSpec contains the correct recording format (e.g., mono)
587 // Mix_QuerySpec() returns the playback format which is stereo (2 channels)
588 int freq = mOriginalSpec.freq;
589 Uint16 format = mOriginalSpec.format;
590 int channels = mOriginalSpec.channels;
591
592 std::cerr << "Using original recording spec: " << freq << "Hz, " << channels << " channels, format=" << format << "\n";
593
594 // Calculate bits per sample
595 int bitsPerSample = 16; // Default to 16-bit
596 switch(format) {
597 case AUDIO_U8:
598 case AUDIO_S8:
599 bitsPerSample = 8;
600 break;
601 case AUDIO_U16LSB:
602 case AUDIO_S16LSB:
603 case AUDIO_U16MSB:
604 case AUDIO_S16MSB:
605 bitsPerSample = 16;
606 break;
607 }
608
609 int bytesPerSample = bitsPerSample / 8;
610 int subchunk1size = 16;
611 int numChannels = channels;
612 int subchunk2size = mRecordPos; // Use actual recorded size, not full buffer
613 int chunksize = 36 + subchunk2size;
614 int audioFormat = 1; // PCM
615 int sampleRate = freq;
616 int byteRate = freq * numChannels * bitsPerSample / 8;
617 int blockAlign = numChannels * bitsPerSample / 8;
618
619 cerr << "--------------------------------------------\n";
620 cerr << "saving file [" << filename << "]\n";
621 cerr << "bitspersample: " << bitsPerSample << endl;
622 cerr << "Channels: " << numChannels << endl;
623 cerr << "frequency: " << sampleRate << endl;
624 cerr << "byterate: " << byteRate << endl;
625 cerr << "buffer size: " << mMixerSample->alen << " bytes" << endl;
626 cerr << "recorded size: " << mRecordPos << " bytes" << endl;
627
628 std::fstream myFile(filename.GetString().c_str(), ios::out | ios::binary);
629 if(!myFile.is_open()) {
630 PError::SignalWarning(Variant("Failed to open file for writing: ") + filename);
631 return;
632 }
633
634 // write the wav file per the wav file format
635 myFile.seekp(0, ios::beg);
636 myFile.write("RIFF", 4); // chunk id
637 myFile.write((char*)&chunksize, 4); // chunk size (36 + SubChunk2Size)
638 myFile.write("WAVE", 4); // format
639 myFile.write("fmt ", 4); // subchunk1ID
640 myFile.write((char*)&subchunk1size, 4); // subchunk1size (16 for PCM)
641 myFile.write((char*)&audioFormat, 2); // AudioFormat (1 for PCM)
642 myFile.write((char*)&numChannels, 2); // NumChannels
643 myFile.write((char*)&sampleRate, 4); // sample rate
644 myFile.write((char*)&byteRate, 4); // byte rate
645 myFile.write((char*)&blockAlign, 2); // block align
646 myFile.write((char*)&bitsPerSample, 2); // bits per sample
647 myFile.write("data", 4); // subchunk2ID
648
649 std::cerr << "About to write data chunk header. subchunk2size = " << subchunk2size << "\n";
650 std::cerr << "About to write audio data. mRecordPos = " << mRecordPos << "\n";
651
652 myFile.write((char*)&subchunk2size, 4); // subchunk2size
653
654 std::cerr << "File position before audio data write: " << myFile.tellp() << "\n";
655 std::cerr << "About to write " << mRecordPos << " bytes from buffer at " << (void*)(mMixerSample->abuf) << "\n";
656
657 // Check buffer validity before writing
658 if(mMixerSample->abuf == NULL) {
659 std::cerr << "ERROR: Buffer is NULL!\n";
660 } else {
661 std::cerr << "Buffer appears valid, first byte value: " << (int)(mMixerSample->abuf[0]) << "\n";
662 }
663
664 myFile.write((char*)(mMixerSample->abuf), mRecordPos); // Write only recorded data
665
666 std::cerr << "File position after audio data write: " << myFile.tellp() << "\n";
667 if(myFile.fail()) {
668 std::cerr << "FILE WRITE FAILED! Error state detected.\n";
669 std::cerr << "errno: " << errno << " (" << strerror(errno) << ")\n";
670 }
671 std::cerr << "File good state: " << myFile.good() << "\n";
672 std::cerr << "File fail state: " << myFile.fail() << "\n";
673 std::cerr << "File bad state: " << myFile.bad() << "\n";
674
675 myFile.close();
676 cerr << "File saved successfully.\n";
677 cerr << "--------------------------------------------\n";
678
679#else
680 //Code here adapted from
681 //http://www.codeproject.com/Messages/3208219/How-to-write-mic-data-to-wav-file.aspx
682
683 int bitsPerSample = mWave.bytesPerSample*8;
684
685 //Unclear about these chunk things:
686 int subchunk1size = 16;
687 int numChannels = mWave.spec.channels;
688
689 int subchunk2size = mWave.audiolen;
690 int chunksize = 36+subchunk2size;
691
692
693 int audioFormat = 1; //PCM
694
695 int sampleRate = mWave
696 .spec.freq;
697 int byteRate = mWave.spec.freq*numChannels*bitsPerSample/8;
698 int blockAlign = numChannels*bitsPerSample/8;
699
700
701 cerr <<"--------------------------------------------\n";
702 cerr << "saving file ["<< filename<<"]\n";
703 cerr << "bitspersample: " << bitsPerSample <<endl;
704 cerr << "Channels: " << numChannels <<endl;
705 cerr << "frequency: " << sampleRate << endl;
706 cerr << "byterate: " << byteRate << endl;
707
708
709 std::fstream myFile (filename.GetString().c_str(), ios::out | ios::binary);
710
711 // write the wav file per the wav file format
712 myFile.seekp (0, ios::beg);
713 myFile.write ("RIFF", 4); // chunk id
714 myFile.write ((char*) &chunksize, 4); // chunk size (36 + SubChunk2Size))
715 myFile.write ("WAVE", 4); // format
716 myFile.write ("fmt ", 4); // subchunk1ID
717 myFile.write ((char*) &subchunk1size, 4); // subchunk1size (16 for PCM)
718 myFile.write ((char*) &audioFormat, 2); // AudioFormat (1 for PCM)
719 myFile.write ((char*) &numChannels, 2); // NumChannels
720 myFile.write ((char*) &sampleRate, 4); // sample rate
721 myFile.write ((char*) &byteRate, 4); // byte rate (SampleRate * NumChannels * BitsPerSample/8)
722 myFile.write ((char*) &blockAlign, 2); // block align (NumChannels * BitsPerSample/8)
723 myFile.write ((char*) &bitsPerSample, 2); // bits per sample
724
725 myFile.write ("data", 4); // subchunk2ID
726 myFile.write ((char*) &subchunk2size, 4); // subchunk2size (NumSamples * NumChannels * BitsPerSample/8)
727
728 myFile.write ((char*)(mWave.audio), mWave.audiolen); // data
729
730#endif
731}
732
733
734
735
736//This must be called after the audio is initialized but before it can
737//be played. It actually opens the audio device for playing.
739{
740
741
742#ifdef PEBL_MIXER
743
744 //This should only get called once
745
746 // Don't reset mMixerSample to NULL here - it may have been set by LoadSoundFromData()
747 // for audio input buffers. If it hasn't been set yet, it will be NULL from constructor.
748
749 //Initialize with the proper file libraries:
750 // Set up the audio stream
751
752
753 int flags=MIX_INIT_OGG|MIX_INIT_MP3|MIX_INIT_FLAC|MIX_INIT_MOD;
754 int initted = Mix_Init(flags);
755
756 cerr << "Attempted: " << flags << endl;
757 cerr << "support code: " << initted << endl;
758 //initted stores the formats currently supported.
759 std::string supportOGG = (initted & MIX_INIT_OGG)?"yes":"no";
760 std::string supportMP3 = (initted & MIX_INIT_MP3)?"yes":"no";
761 std::string supportFLAC = (initted & MIX_INIT_FLAC)?"yes":"no";
762 std::string supportMOD = (initted & MIX_INIT_MOD)?"yes":"no";
763
764
765 // get and print the audio format in use
766 int numtimesopened, frequency, channels;
767 Uint16 format;
768 numtimesopened=Mix_QuerySpec(&frequency, &format, &channels);
769 if(!numtimesopened) {
770 printf("Mix_QuerySpec: %s\n",Mix_GetError());
771 }
772 else {
773 std::string format_str="Unknown";
774 switch(format) {
775
776
777 case AUDIO_U8: format_str="U8"; break;
778 case AUDIO_S8: format_str="S8"; break;
779 case AUDIO_U16LSB: format_str="U16LSB"; break;
780 case AUDIO_S16LSB: format_str="S16LSB"; break;
781 case AUDIO_U16MSB: format_str="U16MSB"; break;
782 case AUDIO_S16MSB: format_str="S16MSB"; break;
783 }
784
785 std::cerr << "------------------------------------------------\n";
786 std::cerr << "Loading PEBL Audio Framework using SDL_Mixer\n";
787 std::cerr << "Opened (times): [" << numtimesopened <<"]" << endl;
788 std::cerr << "Frequency (Hz): [" << frequency << "]" <<endl;
789 std::cerr << "Format: [" << format_str << "]" << endl;
790 std::cerr << "Channels: [" << channels << "]" << endl;
791 std::cerr << "\nFile formats supported:\n";
792 std::cerr << " .wav: (builtin) [yes]\n";
793 std::cerr << " .mp3: (mpg123) ["<< supportMP3<<"]\n";
794 std::cerr << " .ogg: (libvorbis) ["<< supportOGG<<"]\n";
795 std::cerr << " .flac: (libflac) ["<< supportFLAC<<"]\n";
796 std::cerr << " .midi (libmikmod) ["<< supportMOD<<"]\n";
797 std::cerr << "------------------------------------------------\n";
798 }
799
800
801 //This allocates mixing channels. We probably just need
802 //fewer, but sixteen should give us flexibility.
803
804 Mix_AllocateChannels(16);
805
806
807#else
808
809 if (mLoaded && SDL_OpenAudio(&mWave.spec, NULL) < 0 ) {
810 fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
811 return false;
812 }
813
814
815 //Reset the position to the beginning.
816 mWave.audiopos = 0;
817#endif
818
819 return true;
820}
821
822// This plays the sound using the callback mixer function
823// (in the background)
824//
826{
827
828#ifdef PEBL_MIXER
829
830
831 int lefti = floor(mAmplitudeLeft * 255.9999999);
832 int righti = floor(mAmplitudeRight * 255.999999);
833
834
835
836
837 mChannel = Mix_PlayChannel(mChannel, mMixerSample, mRepeats);
838 //Panning has to happen after the channel has been assigned and
839 //I guess is starting to play?
840 if(!Mix_SetPanning(mChannel, lefti,righti)) {
841 printf("BG Mix_SetPanning: %s\n", Mix_GetError());
842 // no panning, is it ok?
843 }
844
845
846
847
848#else
849
850 SDL_LockAudio();
851 mWave.audiopos = 0;
852 gWaveStream = &mWave;
853 SDL_UnlockAudio();
854
855 // Initialize();
856
857 SDL_PauseAudio(0);
858#endif
859
860
861 return true;
862}
863
864
865//This will play the file, not returning until it is complete.
867{
868
869#ifdef PEBL_MIXER
870
871 int lefti = floor(mAmplitudeLeft * 255.9999999);
872 int righti = floor(mAmplitudeRight * 255.999999);
873
874
875
876 if(!Mix_SetPanning(mChannel, lefti,righti)) {
877 printf("FG Mix_SetPanning: %s\n", Mix_GetError());
878 // no panning, is it ok?
879 }
880
881
882 mChannel = Mix_PlayChannel(mChannel, mMixerSample, mRepeats);
883
884 while(Mix_Playing(mChannel))
885 {
887 }
888 Mix_HaltChannel(mChannel);
889
890#else
891
892
893
894 SDL_LockAudio();
895
896 mWave.audiopos = 0;
897 gWaveStream = &mWave;
898
899 SDL_UnlockAudio();
900
901
902
903 SDL_PauseAudio(0);
904 while(SDL_GetAudioStatus() == SDL_AUDIO_PLAYING)
905 {
906 //Wait at least 10 ms before checking again.
908 //cerr << "---------- playing ["<<SDL_GetTicks() << endl;
909 }
910
911 SDL_PauseAudio(1);
912#endif
913
914 return true;
915
916}
917
918#ifdef PEBL_MIXER
920{
921
922
923 mAmplitudeLeft = left;
924 mAmplitudeRight = right;
925
926
929
932
933
934
935
936
937 int lefti = floor(mAmplitudeLeft * 255.9999999);
938 int righti = floor(mAmplitudeRight * 255.999999);
939
940
941
942 //If mChannel is -1, don't set it yet because it won't work.
943 if(mChannel>=0)
944 {
945 if(!Mix_SetPanning(mChannel, lefti,righti)) {
946 printf("Live Mix_SetPanning on channel [%d]: %s\n", mChannel,Mix_GetError());
947 }
948 }
949
950
951 return true;
952}
953#endif
954
955
957{
958
959#ifdef PEBL_MIXER
960 //cerr << "Stopping: " << mChannel << endl;
961 if(mChannel>=0)
962 Mix_HaltChannel(mChannel);
963
964#else
965 SDL_PauseAudio(1);
966 //Set the audio stream back to the beginning.
967 //SDL_CloseAudio();
968 mWave.audiopos=0;
969#endif
970 return true;
971}
972
973
974
975
976
977
978
979#if !defined(PEBL_MIXER) || defined(PEBL_AUDIOIN)
981{
982#ifdef PEBL_MIXER
983 // When using SDL_mixer, extract audio data from Mix_Chunk
984 if(!mMixerSample) {
985 PError::SignalWarning("No audio loaded in GetAudioInfo()");
986 return counted_ptr<AudioInfo>(); // Return NULL counted_ptr
987 }
988
989 AudioInfo * tmp = new AudioInfo();
990
991 // Mix_Chunk contains: Uint8 *abuf, Uint32 alen, int volume
992 tmp->audio = mMixerSample->abuf;
993 tmp->audiolen = mMixerSample->alen;
994 tmp->audiopos = 0;
995 tmp->volume = mMixerSample->volume;
996
997 // Get the audio spec from SDL_mixer
998 int freq, channels;
999 Uint16 format;
1000 Mix_QuerySpec(&freq, &format, &channels);
1001
1002 tmp->spec.freq = freq;
1003 tmp->spec.format = format;
1004 tmp->spec.channels = channels;
1005 tmp->spec.silence = (format == AUDIO_U8) ? 0x80 : 0;
1006 tmp->spec.samples = 4096; // Standard buffer size
1007 tmp->spec.callback = NULL;
1008 tmp->spec.userdata = NULL;
1009
1010 // Calculate bytes per sample
1011 switch(format) {
1012 case AUDIO_U8:
1013 case AUDIO_S8:
1014 tmp->bytesPerSample = 1;
1015 break;
1016 case AUDIO_U16LSB:
1017 case AUDIO_S16LSB:
1018 case AUDIO_U16MSB:
1019 case AUDIO_S16MSB:
1020 tmp->bytesPerSample = 2;
1021 break;
1022 default:
1023 tmp->bytesPerSample = 2;
1024 }
1025
1026 tmp->recordpos = mRecordPos; // Return the actual recorded position
1027 tmp->counter = 0;
1028 tmp->name = mFilename.c_str();
1029
1030 // CRITICAL: Mark that AudioInfo does NOT own this buffer
1031 // The buffer is shared with mMixerSample->abuf and will be freed by Mix_FreeChunk
1032 // or by PlatformAudioOut destructor (which sets abuf=NULL before freeing)
1033 tmp->ownsBuffer = false;
1034
1035 // Wrap in counted_ptr before returning
1036 return counted_ptr<AudioInfo>(tmp);
1037#else
1038 // Original non-mixer implementation
1039 AudioInfo * tmp = new AudioInfo(mWave);
1040#if 0
1041
1042 cerr << "---------------------------\n";
1043 cerr << "getting info in pao:getaudioinfo\n";
1044 cerr << "freq "<<mWave.spec.freq << " -- " << tmp->spec.freq <<endl;
1045 cerr << "length: " <<mWave.audiolen<< "--" << tmp->audiolen << endl;
1046 cerr << "---------------------------\n";
1047#endif
1048
1049 // Wrap in counted_ptr before returning
1050 return counted_ptr<AudioInfo>(tmp);
1051#endif
1052};
1053
1054#endif // !defined(PEBL_MIXER) || defined(PEBL_AUDIOIN)
1055
1056#ifndef PEBL_MIXER
1057void PlayCallBack(void * udata, Uint8 * stream, int len)
1058{
1059
1060 SDL_memset(stream, 0, len);
1061
1062 Uint8 * waveptr;
1063 int waveleft;
1064
1065 //Cast udata to a proper form--this is dangerous and nasty.
1066 AudioInfo * wave = gWaveStream;//(AudioInfo*)(udata);
1067
1068
1069 //Put pointer at the proper place in the buffer.
1070 waveptr = wave->audio + wave->audiopos;
1071 waveleft = wave->audiolen - wave->audiopos;
1072
1073 // cerr << "waveleft: " << waveleft << " len:" << len << endl;
1074 if(waveleft >= len)
1075 {
1076
1077 SDL_MixAudio(stream, waveptr, len, SDL_MIX_MAXVOLUME);
1078 //This may appear in future formats.
1079 //SDL_MixAudioFormat(stream,waveptr,wave->spec,len,SDL_MIX_MAXVOLUME);
1080 wave->audiopos += len;
1081
1082 }
1083 else
1084 {
1085 //This plays the of the file and stops playing.
1086 SDL_MixAudio(stream, waveptr, waveleft, SDL_MIX_MAXVOLUME);
1087 //SDL_MixAudioFormat(stream,waveptr,wave->spec,waveleft,SDL_MIX_MAXVOLUME);
1088
1089 wave->audiopos += waveleft;
1090 SDL_PauseAudio(1);
1091 wave->audiopos=0; //Reset it back to the beginning.
1092
1093 }
1094
1095}
1096
1097#endif // PEBL_MIXER
1098
1099#ifndef PEBL_MIXER
1101{
1102
1103 cerr << "------------------------------------\n";
1104 cerr << "Filename : " << mFilename << endl;
1105 cerr << "Audio specs:\n";
1106 cerr << "Frequency: [" << mWave.spec.freq << "]\n";
1107 Variant form = "";
1108 switch(mWave.spec.format)
1109 {
1110
1111 case AUDIO_U8:
1112 form="AUDIO_U8";
1113 break;
1114
1115 case AUDIO_U16LSB:
1116 form = "AUDIO_U16LSB";
1117 break;
1118
1119
1120 case AUDIO_S8:
1121 form="AUDIO_S8";
1122 break;
1123
1124 case AUDIO_S16:
1125 form="AUDIO_S16";
1126 break;
1127
1128
1129 default:
1130 form = "UNKNOWN";
1131
1132 }
1133
1134
1135
1136 cerr << "Format: [" << form << "]\n";
1137 cerr << "Channels: [" << (int)(mWave.spec.channels) << "]\n";
1138 cerr << "Silence: [" << mWave.spec.silence << "]\n";
1139 cerr << "Samples: [" << mWave.spec.samples << "]\n";
1140 cerr << "Size: [" << mWave.spec.size << "]\n";
1141 cerr << "Length(bytes)["<<mWave.audiolen<<"]\n";
1142 cerr << "Bytes per sample: [" << mWave.bytesPerSample <<"]\n";
1143 cerr << "Total samples: [" << (double)mWave.audiolen/mWave.bytesPerSample <<"]\n";
1144 cerr << "Playback: ["<<mWave.audiopos<<"]\n";
1145 cerr << "------------------------------------\n";
1146
1147}
1148#endif
#define NULL
Definition BinReloc.cpp:317
#define pDouble
Definition Defs.h:7
@ CDT_AUDIOOUT
Definition PEBLObject.h:47
static PEBLPath gPath
virtual bool SetPanning(const double left, const double right)
Definition PAudioOut.h:52
long double mAmplitudeRight
Definition PAudioOut.h:85
long double mAmplitudeLeft
Definition PAudioOut.h:84
int mChannel
Definition PAudioOut.h:83
std::string FindFile(const string &filename)
Definition PEBLPath.cpp:368
bool ConvertAudio(AudioInfo &info)
void SaveBufferToWave(Variant filename)
bool LoadSoundFromData(Uint8 *buffer, long unsigned int size, SDL_AudioSpec *spec, Uint32 recordpos=0)
bool CreateSineWave(float freq, double length, int amplitude)
bool LoadSoundFile(const char *filename)
friend void PlayCallBack(void *dummy, Uint8 *stream, int len)
counted_ptr< AudioInfo > GetAudioInfo()
virtual void Sleep(unsigned long int msecs)
std::string GetString() const
Definition Variant.cpp:1056
PlatformTimer myTimer
void SignalWarning(const std::string &message)
Definition PError.cpp:119
void SignalFatalError(const std::string &message)
AudioInfo * gWaveStream
void PlayCallBack(void *dummy, Uint8 *stream, int len)
unsigned int bytesPerSample
Uint8 * audio
const char * name
SDL_AudioSpec spec
Uint32 audiolen