Files
FreeJ/lib/flash/sound.cc
jaromil c8d6e69756 gplflash library is now included for playback of
flash vectorial graphics in a layer



git-svn-id: svn://dyne.org/rastasoft/freej/freej@621 383723c8-4afa-0310-b8a8-b1afb83214fc
2005-08-05 19:29:26 +00:00

680 lines
14 KiB
C++

/////////////////////////////////////////////////////////////
// Flash Plugin and Player
// Copyright (C) 1998,1999 Olivier Debon
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
///////////////////////////////////////////////////////////////
// Author : Olivier Debon <odebon@club-internet.fr>
//
#if ! defined(__linux__)
#warning no sound support for your OS in lib/sound.cc
#define NOSOUND
#endif
#include "swf.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#ifndef NOSOUND
#ifdef __linux__
#include <linux/soundcard.h>
#endif
#endif
#ifdef RCSID
static char *rcsid = "$Id: sound.cc,v 1.7 2004/12/23 17:13:22 tgc Exp $";
#endif
#define PRINT 0
//////////// SOUND
Sound::Sound(long id) : Character(SoundType, id)
{
samples = 0;
stereo = 0;
soundRate = 0;
sampleSize = 1;
playbackStarted = 0;
}
Sound::~Sound()
{
if (samples) {
delete samples;
}
}
void
Sound::setSoundFlags(long f) {
switch (GET_SOUND_RATE_CODE(f)) {
case 0:
soundRate = 5500;
break;
case 1:
soundRate = 11000;
break;
case 2:
soundRate = 22000;
break;
case 3:
soundRate = 44000;
break;
}
if (f & soundIs16bit) {
sampleSize = 2;
}
if (f & soundIsStereo) {
stereo = 1;
}
format = GET_SOUND_FORMAT_CODE(f);
#if PRINT
printf("-----\nFlags = %2x\n", f);
printf("Rate = %d kHz ", soundRate);
printf("SampleSize = %d byte(s) ", sampleSize);
if (f & soundIsStereo) {
printf("Stereo ");
} else {
printf("Mono ");
}
if (f & soundIsADPCMCompressed) {
printf("ADPCM\n");
} else {
printf("Raw\n");
}
#endif
}
char *
Sound::setNbSamples(long n) {
long size;
nbSamples = n;
size = nbSamples * (stereo ? 2 : 1) * sampleSize;
return setBuffer(size);
}
char *
Sound::setBuffer(long n) {
samples = new char[ n ];
memset((char *)samples,0, n);
return samples;
}
char *
Sound::resetBuffer(long n) {
char* tmpSamples;
long tmpSoundSize;
tmpSoundSize = n + soundSize;
tmpSamples = samples;
samples = new char[ tmpSoundSize ];
memset((char *)samples,0, tmpSoundSize);
memcpy(samples, tmpSamples, soundSize);
delete tmpSamples;
soundSize = tmpSoundSize;
return samples;
}
void
Sound::setSoundSize(long n) {
soundSize=n;
}
void
Sound::setPlaybackStarted() {
playbackStarted=1;
}
void
Sound::setSound(SoundList *sl) {
sound=sl;
}
long
Sound::getFormat() {
return format;
}
long
Sound::getRate() {
return soundRate;
}
long
Sound::getChannel() {
return stereo ? 2 : 1;
}
long
Sound::getNbSamples() {
return nbSamples;
}
long
Sound::getSampleSize() {
return sampleSize;
}
char *
Sound::getSamples() {
return samples;
}
long
Sound::getSoundSize() {
return soundSize;
}
SoundList *
Sound::getSound() {
return sound;
}
long
Sound::getPlaybackStarted() {
return playbackStarted;
}
//////////// SOUND MIXER
long SoundMixer::dsp = -1; // Init of descriptor
long SoundMixer::blockSize = 0; // Driver sound buffer size
long SoundMixer::nbInst = 0; // Nb SoundMixer instances
long SoundMixer::sampleSize = 0;
long SoundMixer::stereo = 0;
long SoundMixer::soundRate = 0;
char *SoundMixer::buffer = 0;
SoundMixer::SoundMixer(char *device)
{
#ifndef NOSOUND
int status;
long fmt;
list = 0; // No sound to play
if (nbInst++) {
// Device is already open
return;
}
dsp = open(device,O_WRONLY | O_NONBLOCK);
if (dsp < 0) {
perror("open dsp");
return;
}
// Reset device
status = ioctl(dsp, SNDCTL_DSP_RESET);
if (status < 0) perror("ioctl SNDCTL_DSP_RESET");
// Set sample size
fmt = AFMT_S16_LE;
sampleSize = 2;
status = ioctl(dsp, SNDCTL_DSP_SETFMT, &fmt);
if (status < 0) perror("ioctl SNDCTL_DSP_SETFMT");
if (status) {
fmt = AFMT_U8;
sampleSize = 1;
status = ioctl(dsp, SNDCTL_DSP_SETFMT, &fmt);
if (status < 0) perror("ioctl SNDCTL_DSP_SETFMT");
}
// Set stereo channel
stereo = 1;
status = ioctl(dsp, SNDCTL_DSP_STEREO, &stereo);
if (status) {
stereo = 0;
}
// Set sound rate in Hertz
soundRate = 11000;
status = ioctl(dsp, SNDCTL_DSP_SPEED, &soundRate);
if (status < 0) perror("ioctl SNDCTL_DSP_SPEED");
// Get device buffer size
status = ioctl(dsp, SNDCTL_DSP_GETBLKSIZE, &blockSize);
if (status < 0) perror("ioctl SNDCTL_DSP_GETBLKSIZE");
if (blockSize < 1024) {
blockSize = 32768;
}
blockSize *= 2;
buffer = (char *)malloc(blockSize);
if (buffer == 0) {
close(dsp);
dsp = -1;
}
#if PRINT
int caps;
ioctl(dsp,SNDCTL_DSP_GETCAPS, &caps);
printf("Audio capabilities = %x\n", caps);
printf("Sound Rate = %d\n", soundRate);
printf("Stereo = %d\n", stereo);
printf("Sample Size = %d\n", sampleSize);
printf("Buffer Size = %d\n", blockSize);
#endif /* PRINT */
#endif /* NOSOUND */
}
SoundMixer::~SoundMixer()
{
if (--nbInst == 0) {
if (dsp > 0) {
close(dsp);
free(buffer);
}
}
stopSounds();
}
void
SoundMixer::stopSounds()
{
#ifndef NOSOUND
SoundList *sl,*del;
for(sl = list; sl; ) {
del = sl;
sl = sl->next;
// If MP3-format, de-init the mad-decoder
if (del->currentMp3) uninitMp3Sounds(del);
delete del;
}
list = 0;
#endif
}
void
SoundMixer::startSound(Sound *sound)
{
#ifndef NOSOUND
SoundList *sl;
if (sound) {
// Add sound in list
sl = new SoundList;
sl->rate = sound->getRate();
sl->stereo = (sound->getChannel() == 2);
sl->sampleSize = sound->getSampleSize();
if (sound->getFormat() == 2) {
//Mp3 specific data
sl->currentMp3 = sound->getSamples();
sl->remainingMp3 = sound->getSoundSize();
sl->originalMp3 = sl->currentMp3;
sl->remaining = 0;
// Init the mad-decoder
mad_stream_init (&sl->mp3Stream);
mad_frame_init (&sl->mp3Frame);
mad_synth_init (&sl->mp3Synth);
mad_stream_buffer (&sl->mp3Stream, (unsigned char*)sl->currentMp3, sl->remainingMp3);
fprintf(stderr,"init mp3-decoder, size: %d\n", sound->getSoundSize());
// make the buffer ready
sl->current = (char*) malloc(1200);
memset((void*)sl->current, 0, 1200);
// save the starting point of the buffer
sl->original = sl->current;
} else {
sl->current = sound->getSamples();
sl->remaining = sound->getSampleSize()*sound->getNbSamples()*sound->getChannel();
sl->remainingMp3 = 0;
}
sl->next = list;
list = sl;
sound->setPlaybackStarted();
sound->setSound(sl);
}
#endif
}
void
SoundMixer::uninitMp3Sounds(SoundList *sl)
{
#ifndef NOSOUND
fprintf(stderr,"dismantle mp3-decoder\n");
mad_synth_finish (&sl->mp3Synth);
mad_frame_finish (&sl->mp3Frame);
mad_stream_finish (&sl->mp3Stream);
// Seems it sometimes is free before this ???
//free(sl->current);
#endif
}
long
SoundMixer::playSounds()
{
#ifndef NOSOUND
audio_buf_info bufInfo;
long nbBytes, n;
SoundList *sl,*prev;
int status;
// Init failed
if (dsp < 0) return 0;
// No sound to play
if (list == 0) return 0;
// Get free DMA buffer space
status = ioctl(dsp, SNDCTL_DSP_GETOSPACE, &bufInfo);
// Free space is not large enough to output data without blocking
// But there are still sounds to play. We must wait.
if (bufInfo.bytes < blockSize) return 1;
nbBytes = 0;
// Fill buffer with silence.
memset((void*)buffer, 0, blockSize);
prev = 0;
sl = list;
while(sl) {
// Ask sound to fill the buffer
// according to device capabilities
n = fillSoundBuffer(sl, buffer, blockSize);
// Remember the largest written size
if (n > nbBytes) {
nbBytes = n;
}
// No more samples for this sound
if ((sl->remaining == 0) && (sl->remainingMp3==0)) {
// If MP3-format, de-init the mad-decoder
if (sl->currentMp3) {
uninitMp3Sounds(sl);
}
// Remove sound from list
if (prev) {
prev->next = sl->next;
delete sl;
sl = prev->next;
} else {
list = sl->next;
delete sl;
sl = list;
}
} else {
prev = sl;
sl = sl->next;
}
}
if (nbBytes) {
// At last ! Play It !
write(dsp,buffer,nbBytes);
status = ioctl(dsp, SNDCTL_DSP_POST);
}
return nbBytes;
#else
return 0;
#endif
}
long
SoundMixer::fillSoundBuffer(SoundList *sl, char *buff, long buffSize)
{
long sampleLeft, sampleRight;
long skipOut, skipOutInit;
long skipIn, skipInInit;
long freqRatio;
long totalOut = 0;
sampleLeft = sampleRight = 0;
skipOutInit = skipInInit = 0;
freqRatio = sl->rate / soundRate;
if (freqRatio) {
skipOutInit = freqRatio - 1;
skipInInit = 0;
}
freqRatio = soundRate / sl->rate;
if (freqRatio) {
skipInInit = freqRatio - 1;
skipOutInit = 0;
}
skipOut = skipOutInit;
skipIn = skipInInit;
// If mp3-format, and if there is something to decode and
// there is nothing in the buffer, decode and fill the buffer
if ((sl->remainingMp3 > 0) && (sl->remaining < 1)) Mp3Decompress(sl);
while (buffSize && sl->remaining) {
if (skipIn-- == 0) {
// Get sampleLeft
if (sl->sampleSize == 2) {
sampleLeft = (long)(*(short *)(sl->current));
if (sampleSize == 1) {
sampleLeft = (sampleLeft >> 8) &0xff;
}
} else {
sampleLeft = (long)*(sl->current);
if (sampleSize == 2) {
sampleLeft <<= 8;
}
}
sl->current += sl->sampleSize;
sl->remaining -= sl->sampleSize;
if (sl->stereo) {
// Get sampleRight
if (sl->sampleSize == 2) {
sampleRight = (long)(*(short *)(sl->current));
if (sampleSize == 1) {
sampleRight = (sampleRight >> 8) &0xff;
}
} else {
sampleRight = (long)*(sl->current);
if (sampleSize == 2) {
sampleRight <<= 8;
}
}
sl->current += sl->sampleSize;
sl->remaining -= sl->sampleSize;
} else {
sampleRight = sampleLeft;
}
skipIn = skipInInit;
}
if (skipOut-- == 0) {
// Output
if (stereo) {
if (sampleSize == 2) {
*((short *)buff) += sampleLeft/2;
buffSize -= sampleSize;
buff += sampleSize;
*((short *)buff) += sampleRight/2;
buffSize -= sampleSize;
buff += sampleSize;
} else {
*((char *)buff) += sampleLeft/2;
buffSize -= sampleSize;
buff += sampleSize;
*((char *)buff) += sampleRight/2;
buffSize -= sampleSize;
buff += sampleSize;
}
totalOut += 2*sampleSize;
} else {
if (sampleSize == 2) {
*((short *)buff) += (sampleLeft+sampleRight)>>2;
buffSize -= sampleSize;
buff += sampleSize;
} else {
*((char *)buff) += (sampleLeft+sampleRight)>>2;
buffSize -= sampleSize;
buff += sampleSize;
}
totalOut += sampleSize;
}
skipOut = skipOutInit;
}
// If mp3-format, and if there is something to decode and
// there is nothing in the buffer, decode and fill the buffer
if ((sl->remainingMp3 >0)&& (sl->remaining < 1)) Mp3Decompress(sl);
}
return totalOut;
}
/* Taken from 'minimad.c'
* The following utility routine performs simple rounding, clipping, and
* scaling of MAD's high-resolution samples down to 16 bits. It does not
* perform any dithering or noise shaping, which would be recommended to
* obtain any exceptional audio quality. It is therefore not recommended to
* use this routine if high-quality output is desired.
*/
int SoundMixer::Mp3Scale(mad_fixed_t sample)
{
/* round */
sample += (1L << (MAD_F_FRACBITS - 16));
/* clip */
if (sample >= MAD_F_ONE)
sample = MAD_F_ONE - 1;
else if (sample < -MAD_F_ONE)
sample = -MAD_F_ONE;
/* quantize */
return sample >> (MAD_F_FRACBITS + 1 - 16);
}
void
SoundMixer::Mp3Decompress(SoundList *sl)
{
int ret;
signed int sample;
// reset the pointer to the start of the buffer
sl->current = sl->original;
// the actual decoding
ret = mad_frame_decode (&sl->mp3Frame, &sl->mp3Stream);
if (ret == -1) {
switch (sl->mp3Stream.error) {
case MAD_ERROR_BUFLEN:
// End of buffer - no more to decode
sl->remainingMp3=0;
return;
case MAD_ERROR_BUFPTR:
case MAD_ERROR_NOMEM:
fprintf(stderr,"MAD: out of memory\n");
sl->remainingMp3=0;
return;
case MAD_ERROR_BADCRC:
fprintf(stderr,"MAD: checksum error\n");
sl->remainingMp3=0;
return;
case MAD_ERROR_LOSTSYNC:
fprintf(stderr,"MAD: synchronization lost\n");
sl->remainingMp3=0;
return;
case MAD_ERROR_BADLAYER:
case MAD_ERROR_BADBITRATE:
case MAD_ERROR_BADSAMPLERATE:
case MAD_ERROR_BADEMPHASIS:
case MAD_ERROR_BADBITALLOC:
case MAD_ERROR_BADSCALEFACTOR:
case MAD_ERROR_BADFRAMELEN:
case MAD_ERROR_BADBIGVALUES:
case MAD_ERROR_BADBLOCKTYPE:
case MAD_ERROR_BADSCFSI:
case MAD_ERROR_BADDATAPTR:
case MAD_ERROR_BADPART3LEN:
case MAD_ERROR_BADHUFFTABLE:
case MAD_ERROR_BADHUFFDATA:
case MAD_ERROR_BADSTEREO:
fprintf(stderr,"MAD: file contains invalid data\n");
sl->remainingMp3=0;
return;
default:
fprintf(stderr,"MAD: unknown error 0x%X.\n",sl->mp3Stream.error);
sl->remainingMp3=0;
return;
}
}
mad_synth_frame (&sl->mp3Synth, &sl->mp3Frame);
// stereo or not?
int sn = sl->stereo ? 2 : 1;
// Set the size
sl->remaining = sl->mp3Synth.pcm.length * sl->sampleSize * sn;
// Setup the buffer
short *dst = (short *) sl->current;
// transfer the decoded samples into the buffer
for(int i = 0; i < sl->mp3Synth.pcm.length; i++)
{
for (int e = 0; e < sn; e++){
sample = Mp3Scale(sl->mp3Synth.pcm.samples[e][i]);
if ( sample != (short)sample )
sample = sample < 0 ? -32768 : 32767;
*dst++ = sample;
}
}
}