/*
            __
           / /_______________  ____  ___
          / __/ ___/ ___/ __ \/ __ \/ _ \
         / /_(__  ) /__/ /_/ / /_/ /  __/
         \__/____/\___/\____/ .___/\___/
                           /_/
    
    sound

    By Michael Stevens

    See license.html for copyright information
*/

#include "../include/tscope.h"
#include "../include/tscope/internal.h"


//------------------------------------------------------------------------
// sound input/output non blocking api

static int playCallback(const void *inputBuffer, void *outputBuffer,
            unsigned long framesPerBuffer,
            const PaStreamCallbackTimeInfo *timeInfo,
            PaStreamCallbackFlags statusFlags,
            void *usersample)
{

    // convert void pointer to snd2_sample pointer
    snd2_sample *samp = (snd2_sample *) usersample;

    // pointer to current sample frame
    float *sampledata = &samp->data[samp->current_frame * samp->channels];

    // pointer to output buffer
    float *bufferdata = (float *) outputBuffer;

    unsigned int i;
    int finished;
    unsigned int framesLeft = samp->frames - samp->current_frame;

    // Prevent unused variable warnings
    (void) inputBuffer;     
    (void) timeInfo;
    (void) statusFlags;
    (void) usersample;

    // are we in the final buffer or not?
    if (framesLeft < framesPerBuffer) {

        // copy the remaining frames
        for (i = 0; i < framesLeft; i++) {

            // increment buffer pointers & copy contents 
            *(bufferdata++) = *(sampledata++);          // left 
            if (samp->channels == 2)
                *(bufferdata++) = *(sampledata++);      // right 
        }

        // pad buffer with 0's
        for (; i < framesPerBuffer; i++) {
            *(bufferdata++) = 0;    
            if (samp->channels == 2)
                *(bufferdata++) = 0;    
        }

        // update current fame index
        samp->current_frame += framesLeft;
        finished = paComplete;
    }
    else {

        // copy one buffer
        for (i = 0; i < framesPerBuffer; i++) {
            *(bufferdata++) = *(sampledata++);  
            if (samp->channels == 2)
                *(bufferdata++) = *(sampledata++);  
        }

        // update current frame index
        samp->current_frame += framesPerBuffer;
        finished = paContinue;
    }
    return finished;
}

static int recordCallback(const void *inputBuffer, void *outputBuffer,
              unsigned long framesPerBuffer,
              const PaStreamCallbackTimeInfo * timeInfo,
              PaStreamCallbackFlags statusFlags,
              void *usersample)
{

    // convert void pointer to snd2_sample pointer
    snd2_sample *samp = (snd2_sample *) usersample;

    // pointer to input buffer
    const float *bufferdata = (const float *) inputBuffer;

    // pointer to current sample frame
    float *wptr = &samp->data[samp->current_frame * samp->channels];

    long framesToCalc;
    long i;
    int finished;
    unsigned long framesLeft = samp->frames - samp->current_frame;

    // Prevent unused variable warnings. 
    (void) outputBuffer;    
    (void) timeInfo;
    (void) statusFlags;
    (void) usersample;

    if (framesLeft < framesPerBuffer) {
        framesToCalc = framesLeft;
        finished = paComplete;
    } 
    else {
        framesToCalc = framesPerBuffer;
        finished = paContinue;
    }

    // is there any input?
    if (inputBuffer == NULL) {
        for (i = 0; i < framesToCalc; i++) {
            *wptr++ = 0;                // left 
            if (samp->channels == 2)
                *wptr++ = 0;            // right
        }
    } 
    else {
        for (i = 0; i < framesToCalc; i++) {
            *wptr++ = *bufferdata++;    /* left */
            if (samp->channels == 2)
                *wptr++ = *bufferdata++;    /* right */
        }
    }
    samp->current_frame += framesToCalc;
    return finished;
}

snd2_stream *snd2_playsample(snd2_sample *samp)
{
    samp->current_frame=0;

    // initialize sound if necessary
    if (!_snd2_flag)
        _snd2_init();

    // set the stream's output parameters
    // - device, outputlatency
    // - sample format & channels
    // - sample rate, number of frames to be played
    PaStreamParameters outparam;
    outparam.device = Pa_GetDefaultOutputDevice();
    if (outparam.device==paNoDevice) 
        ts_fatal("snd2_playsample: no output device found\n");

    outparam.channelCount = samp->channels;
    outparam.sampleFormat = paFloat32;
    outparam.suggestedLatency = Pa_GetDeviceInfo(outparam.device)->defaultLowOutputLatency;
    if (_debug_flag > DEBUG1)
        fprintf(stderr, "suggested output latency: %f\n", outparam.suggestedLatency);
    outparam.hostApiSpecificStreamInfo = NULL;

    // open the stream for output   
    snd2_stream *stream;
    int err;
    err =
    Pa_OpenStream(&stream, NULL, &outparam, samp->samplerate,
              FRAMES_PER_BUFFER, paClipOff, playCallback, samp);
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));

    // play the stream
    if (stream) {
        err = Pa_StartStream(stream);
        if(err) 
            ts_fatal(Pa_GetErrorText( err ));
    }
    return stream;
}

snd2_stream * snd2_recordsample(snd2_sample *samp)
{
    samp->current_frame=0;

    // initialize sound if necessary
    if (!_snd2_flag)
        _snd2_init();

    // set the stream's input parameters
    // - device, inputlatency
    // - sample format & channels
    PaStreamParameters inparam;

    inparam.device = Pa_GetDefaultInputDevice();
    if (inparam.device==paNoDevice) 
        ts_fatal ("snd2_recordsample: no input device found\n");

    inparam.channelCount = samp->channels;
    inparam.sampleFormat = paFloat32;
    inparam.suggestedLatency =
    Pa_GetDeviceInfo(inparam.device)->defaultLowInputLatency;
    if (_debug_flag > DEBUG1)
        fprintf(stderr, "suggested input latency: %f\n", inparam.suggestedLatency);
    inparam.hostApiSpecificStreamInfo = NULL;

    // open the stream for input
    snd2_stream *stream;
    int err;
    err =
    Pa_OpenStream(&stream, &inparam, NULL, samp->samplerate,
              FRAMES_PER_BUFFER, paClipOff, recordCallback, samp);
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));

    // record the stream
    err = Pa_StartStream(stream);
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));

    return stream;
}

int snd2_querysample(snd2_stream * stream)
{
    int err;
        err = Pa_IsStreamActive(stream);
    if (err < 0)
        ts_fatal(Pa_GetErrorText( err ));
    return err;         // 1=active, 0=inactive
}

void snd2_stopsample(snd2_stream * stream)
{
    int err;
    err = Pa_CloseStream(stream);
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));
}

float snd2_getstreamtime(snd2_stream * stream)
{    
    return (Pa_GetStreamTime(stream));
}

__int64 snd2_getsampletime(snd2_sample * samp)
{    
    return (_snd2_filebufferlength + samp->current_frame * samp->channels); 
}

//------------------------------------------------------------------------
// sound input/output blocking api

void snd2_recordsample_blocking(snd2_sample *samp)
{

    // initialize sound if necessary
    if (!_snd2_flag)
        _snd2_init();
    
    // set the stream's input parameters
    // - device, inputlatency
    // - sample format & channels

    PaStreamParameters inparam;

    inparam.device = Pa_GetDefaultInputDevice();
    if (inparam.device==paNoDevice) 
        ts_fatal ("snd2_recordsample_blocking: no input device found\n");

    inparam.channelCount = samp->channels; 
    inparam.sampleFormat =  paFloat32;
    inparam.suggestedLatency = Pa_GetDeviceInfo( inparam.device )->defaultLowInputLatency;
    if (_debug_flag > DEBUG1)
        fprintf(stderr, "snd2_recordsample_blocking: suggested input latency: %f\n", inparam.suggestedLatency);
    inparam.hostApiSpecificStreamInfo = NULL;
    
    // open the stream for input
    snd2_stream *stream;
    int err;    
    err = Pa_OpenStream(&stream, &inparam, NULL, samp->samplerate, FRAMES_PER_BUFFER, \
              paClipOff, NULL, NULL ); 
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));


        
    // record the frames
    err = Pa_StartStream( stream );
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));
    
    err = Pa_ReadStream( stream, samp->data, samp->frames );
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));
    
    err = Pa_CloseStream( stream );
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));
}


void snd2_playsample_blocking(snd2_sample *samp)
{

    // initialize sound if necessary
    if (!_snd2_flag)
        _snd2_init();

    // set the stream's output parameters
    // - device, outputlatency
    // - sample format & channels
    // - sample rate, number of frames to be played
    PaStreamParameters outparam;    
    outparam.device = Pa_GetDefaultOutputDevice(); 
    if (outparam.device==paNoDevice) 
        ts_fatal ("snd2_playsample_blocking: no output device found\n");

    outparam.channelCount = samp->channels;
    outparam.sampleFormat =  paFloat32;
    outparam.suggestedLatency = Pa_GetDeviceInfo( outparam.device )->defaultLowOutputLatency;
    if (_debug_flag > DEBUG1)
        fprintf(stderr, "snd2_playsample_blocking: suggested output latency: %f\n", outparam.suggestedLatency);
    outparam.hostApiSpecificStreamInfo = NULL;
    
    // open the stream for output   
    snd2_stream *stream;
    int err;    
    err = Pa_OpenStream (&stream, NULL, &outparam, samp->samplerate, FRAMES_PER_BUFFER, \
            paClipOff, NULL, NULL);
    if(err) 
        ts_fatal(Pa_GetErrorText( err ));
        
    // play the stream
    if (stream) {
    
        err = Pa_StartStream( stream );
        if(err) 
            ts_fatal(Pa_GetErrorText( err ));
            
        err = Pa_WriteStream(stream, samp->data, samp->frames);
        if(err) 
            ts_fatal(Pa_GetErrorText( err ));

        err = Pa_CloseStream( stream );
        if(err) 
            ts_fatal(Pa_GetErrorText( err ));
    }
}

//------------------------------------------------------------------------
// file buffer input output functions

void snd2_tobuffer(snd2_sample * samp)
{


    FILE *fp;
    fp = fopen ("buff.raw", "a+");
    if (!fp) 
        ts_fatal ("snd2_tobuffer: could not open buffer file\n");
    
    sf_count_t start=samp->current_frame;
    fwrite(samp->data, sizeof(float), start * samp->channels, fp);
    sf_count_t stop=samp->current_frame;

    fclose(fp);

    samp->current_frame=stop-start;
    int i, updated=stop-start;
    for (i=0; i<updated; i++)
        samp->data[i] = samp->data[i+start];    


/*

    sf_count_t start=samp->current_frame;
    FILE *fp;
    fp = fopen ("buff.raw", "a+");
    if (!fp) 
        ts_fatal ("snd2_tobuffer: could not open buffer file\n");
    
    fwrite(samp->data, sizeof(float), samp->current_frame * samp->channels, fp);
    fclose(fp);

    // if the buffer has advanced we don't want to lose information
    sf_count_t stop=samp->current_frame;
    samp->current_frame=stop-start;
 */   
    
    _snd2_filebufferlength += (start * samp->channels); 
}

void snd2_frombuffer(snd2_sample *samp)
{
    FILE *fp;
    fp = fopen ("buff.raw", "r");
    if (!fp) 
        ts_fatal ("snd2_frombuffer: could not open dump file\n");
    
    // compute length of file
    sf_count_t l=0;
    float tmp;
    int ret;
    while((ret=fread(&tmp, sizeof(float), 1, fp)))
        l++;
    rewind(fp);
    
    // allocate memory for sample
    samp=snd2_makesample(0);
        
    samp->current_frame=0;
    samp->samplerate=_snd2_samplerate;
    samp->channels=_snd2_channels;
    samp->frames=l; //(_snd2_samplerate*length)/1000;
    snd2_allocatesample(samp);
 
    // read the buffer
    ret=fread(samp->data, sizeof(float), l, fp);
    if (l!=ret) 
        ts_fatal ("snd2_frombuffer: could not read all samples\n");
    fclose(fp);
}



//------------------------------------------------------------------------
// file input output functions

snd2_sample * snd2_readsample (char *file) 
{
    SF_INFO     sfinfo ;
    SNDFILE     *infile;
    
    // try to open the input file
    if (! (infile = sf_open (file, SFM_READ, &sfinfo)))
        ts_fatal("snd2_readsample: could not open %s\n", file);

    // check the format using libsndfile
    if (! sf_format_check (&sfinfo)) {  
        sf_close (infile) ;
        ts_fatal("snd2_readsample: %s is not in WAV format\n", file) ;
    } 
    
    // test whether the file is a wave 
    // the underlying libraries (libsndfile/portaudio) support other formats
    // but I do not feel like testing all of these, 
    // so they are not supported by tscope
    if ( !(sfinfo.format & SF_FORMAT_WAV) ) {
        sf_close (infile) ;
        ts_fatal("snd2_readsample: %s is not in WAV format\n", file) ;
    }
        
    // samplerate: we only support 11025, 22050 and 44100 Hz
    if (sfinfo.samplerate != 11025 && sfinfo.samplerate != 22050 && sfinfo.samplerate != 44100) {
        sf_close (infile) ;
        ts_fatal("snd2_readsample: %s's samplerate is not 11025, 22050 or 44100\n", file) ;
    }
    
    // channels: we only support mono or stereo
    if (sfinfo.channels!=1 && sfinfo.channels!=2) {
        sf_close (infile) ;
        ts_fatal("snd2_readsample: %s is not mono or stereo\n", file) ;
    }


    // allocate the sample
    snd2_sample *samp;
    samp=snd2_makesample(0);
    
    // copy the sample information
    samp->samplerate=sfinfo.samplerate;
    samp->channels=sfinfo.channels;
    samp->frames=sfinfo.frames;
            
    // allocate memory for sample
    snd2_allocatesample(samp);
    
    // read the frames
    sf_count_t readcount;
    readcount = sf_readf_float(infile, samp->data, sfinfo.frames); 
    
    if (readcount != samp->frames) {
        sf_close (infile) ;
        ts_fatal("snd2_readsample: could not read all frames in %s\n", file) ;
    }

    // close the input file
    sf_close(infile);

    return samp;
}

int snd2_writesample(snd2_sample *samp, char *file)
{
    // write info structure
    SF_INFO sfinfo ;
    sfinfo.frames = samp->frames;
    sfinfo.samplerate = samp->samplerate;
    sfinfo.channels = samp->channels;
    if (_snd2_sampleformat == SAMPLE_INTEGER)
        sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
    else if (_snd2_sampleformat == SAMPLE_FLOAT)
        sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
    else 
        ts_fatal("snd2_writesample has bugs\n", file) ;

    sfinfo.sections = 1;
    sfinfo.seekable =1;
        
    // open file
    SNDFILE *outfile;
    if (! (outfile = sf_open (file, SFM_WRITE, &sfinfo)))
        ts_fatal("write sample: could not open %s\n", file) ;

    // write the frames
    sf_count_t writecount;
    writecount = sf_writef_float(outfile, samp->data, samp->frames);
    if (writecount != samp->frames) {
        sf_close (outfile) ;
        ts_fatal("snd2_writesample: could not write all frames in %s\n", file) ;
    }

    // close the file
    sf_close(outfile);
    return writecount;
}


//------------------------------------------------------------------------
// memory management functions

snd2_sample *snd2_makesample(sf_count_t length)
{

    if (length<0) 
        ts_fatal("snd2_makesample: length should be positive\n");

    snd2_sample *samp=(snd2_sample *)malloc(sizeof(snd2_sample));
    if (samp==NULL) 
        ts_fatal ("snd2_makesample: could not allocate sample\n");
        
    samp->current_frame=0;
    samp->samplerate=_snd2_samplerate;
    samp->channels=_snd2_channels;
    samp->frames=(_snd2_samplerate*length)/1000;
    
    if (length>0) 
        snd2_allocatesample(samp);
    return samp;
}

void snd2_allocatesample(snd2_sample *samp) 
{
    samp->data=(float *)malloc(samp->frames*samp->channels*sizeof(float));  
    memset(samp->data, 0, samp->frames*samp->channels*sizeof(float));
    if (samp->data==NULL) 
        ts_fatal ("snd2_allocatesample: could not allocate memory\n") ;
}
    
void snd2_killsample (snd2_sample *samp)
{
    if (samp && samp->data)
        free (samp->data);

    if (samp)
        free(samp);
}


//------------------------------------------------------------------------
// sound parameter functions
int snd2_channels(int channels) 
{
    int oldchannels=_snd2_channels;
    
    if (channels != MONO && channels !=STEREO) 
        ts_fatal ("snd2_channels: only mono (1) or stereo (2) samples are supported\n");

    _snd2_channels=channels;
    return oldchannels;
}

int snd2_samplerate(int rate)
{
    int oldrate=_snd2_samplerate;
    
    if (rate != 11025 && rate != 22050 && rate != 44100) 
        ts_fatal ("snd2_samplerate: 11025, 22050 and 44100 Hz are the only supported samplerates\n");
    
    _snd2_samplerate=rate;
    return oldrate;
}

int snd2_sampleformat(int format) 
{
    int oldformat=_snd2_sampleformat;   

    if (format != SAMPLE_INTEGER && format != SAMPLE_FLOAT) 
        ts_fatal ("snd2_sampleformat: sample format %l undefined\n", format);

    _snd2_sampleformat=format;  
    return oldformat;
}




top
Persoonlijke pagina Universiteit GentTscope
Allegro | Cygwin | Gcc
© See license.html for copyright information