/* samples.c -- fugue sound data type */

#include <stdio.h>
#ifndef mips
#include "stdlib.h"
#endif
#include "xlisp.h"
#include "sound.h"
#include "falloc.h"
#include "samples.h"


/* snd_from_array -- convert lisp array to sound type */
/**/
sound_type snd_from_array(double t0, double sr, LVAL array)
{
    sound_type result;
    snd_list_type snd_list;
    long total = 0;

    if (!vectorp(array)) xlerror("array expected", array);

    result = sound_create(NULL, t0, sr, 1.0);
    snd_list = result->list;
    while (total < getsize(array)) {
	long togo = min(getsize(array) - total, max_sample_block_len);
	sample_block_type block;
	int i;
	falloc_sample_block(block, "snd_from_array");
	snd_list->block = block;
	for (i = 0; i < togo; i++) {
	    LVAL elem = getelement(array, total + i);
	    sample_type *ptr = block->samples + i;
	    if (fixp(elem)) *ptr = getfixnum(elem);
	    else if (floatp(elem)) *ptr = getflonum(elem);
	    else xlerror("expecting array elem to be number", elem);
	}
	total += togo;
	snd_list->block_len = togo;
	snd_list->u.next = snd_list_create(NULL);
	snd_list = snd_list->u.next;
    }
    snd_list->block_len = max_sample_block_len;
    snd_list->block = zero_block;
    snd_list->logically_stopped = true;
    snd_list->u.next = zero_snd_list;
    return result;
}


/* snd_length -- count how many samples are in a sound */
/**/
long snd_length(sound_type s, long len)
{
    long total = 0;
    long blocklen;

    s = sound_copy(s);
    if (len > s->stop) len = s->stop;
    while (total < len) {
	sample_block_type sampblock = sound_get_next(s, &blocklen);
	if (sampblock == zero_block) break;
	total += blocklen;
    }
    if (total > len) total = len;
    sound_unref(s);
    return total;
}


/* snd_maxsamp -- compute the maximum value of samples in s */
/**/
double snd_maxsamp(sound_type s)
{
    sample_type result = 0;
    long blocklen;
    s = sound_copy(s);

    while (true) {
	sample_block_type sampblock = sound_get_next(s, &blocklen);
	long i;
	sample_block_values_type sbufp = sampblock->samples;
	if (sampblock == zero_block || blocklen == 0) {
	    break;
	}
	for (i = 0; i < blocklen; i++) {
	    register samp = *sbufp++;
	    if (result < samp) result = samp;
	    else if (result < -samp) result = -samp;
	}
    }
    return (double) (s->scale * result);
}


/* snd_samples -- convert sound (prefix) to lisp array */
/**/
LVAL snd_samples(sound_type s, long len)
{
    LVAL v;
    long vx = 0;
    long blocklen;
    register double scale_factor = s->scale;
    len = snd_length(s, len);
    s = sound_copy(s);

    xlsave1(v);
    v = newvector(len);

    while (len > 0) {
	sample_block_type sampblock = sound_get_next(s, &blocklen);
	long togo = min(blocklen, len);
	long i;
	sample_block_values_type sbufp = sampblock->samples;
	for (i = 0; i < togo; i++) {
	    setelement(v, vx++, cvflonum(*sbufp++ * scale_factor));
	}
	len -= togo;
    }
    sound_unref(s);

    /* restore the stack */
    xlpop();
    return v;
}
