function wavappend(x, filename, security)
    // This function allows to append audio data to an
    // existent wav file. This is useful when large
    // files must be saved, since it is not feasible 
    // to create a huge signal array because of memory
    // issues. The function preserves any additional
    // information at the end of the original file
    // such as cues or playlists.
    //
    // Usage:
    //        wavappend(x, filename, security)
    //
    // where 
    //        x:        vector or two column array 
    //                  containing the portion of
    //                  signal to append
    //        filename: complete path and name of
    //                  file to host the new data 
    //        security: key to safe mode ('safe')
    //
    // Argument x may be a vector in case of mono signals
    // or a two-column array in case of stereo signals. 
    // In either case its values must be between -1 and
    // +1. Care must be taken when normalising signal to
    // fulfill this requisite, since in no moment the
    // whole signal is in the workplace simultaneously.
    // Rather, it is being created or processed by
    // portions. Thus it is not valid just to normalise
    // the available signal, since the normalisation
    // constant might be different from that of the
    // previously saved signal, creating some unacceptable
    // discontinuities. It is necessary, instead, to 
    // estimate the maximum amplitude of the whole signal
    // and normalise with respect to that value. Though
    // cumbersome, one could save the different portions
    // in different wav files, and once the whole  signal
    // has been created (and, hopefully, its maximum 
    // amplitude already detected!), reread orderly each
    // file, apply a global normalisation constant and
    // then save them sequentially. Argument security is
    // optional. If included, its only meaningful value
    // is the string 'safe'. If this value is included,
    // the original file will not be modified. All 
    // editing will be performed on a copy.
    //
    // NOTE: This function does not currently support 
    // BEXT types
    //
    // ---------------------------------------------------------
    // Author: Federico Miyara
    // Date:   2010-06-13 
    //         2010-07-14
    //         2021-06-17 

    rhs = argn(2);
    
    // Security feature
    if rhs == 3
        if security=="safe"
            filename1 = filename;
            filename = ascii(ascii(filename)(1:$-4)) + "_new.wav";
            copyfile(filename1, filename);
            text = "\nWARNING!\nEditing will be performed on file ";
            text = text + "%s\nFile %s will not be changed\n\n";
            mprintf(text, filename, filename1) 
        end
    end
    
    if rhs<2
    end
    
    // For testing purposes  --DELETE--
    if 1==2
        caso = 2
        select caso
        case 1
            Fs = 44100;
            t = (0:100)/Fs;
            u = 0.3*sin(2*%pi*1000*t);
            x = u; 
            filename = "d:\hola\prueba_tono.wav"
            wavwrite(u, Fs, filename)
        case 2
            filename = "d:\hola\append_prueba.wav";
            [u, Fs, Nbit] = wavread(filename);
            x = [u($:-1:1), u];
        case 3
            Fs = 44100;
            t = [0:10]/Fs;
            u = (t*Fs - 5)/10;
            filename = "d:\hola\append_prueba.wav";
            wavwrite(u, Fs, filename)
        end
    end

    // Open file for reading and writing
    fid = mopen(filename, "r+b");


    // GET HEADER INFORMATION
    //
    // NOTE: Not all the data to be retrieved is 
    //       relevant for the append operation, but
    //       it is kept for future updates.
    // 
    // Read the header section of the file. Actually, 
    // 100 bytes are read to ensure the relevant 
    // information is retrieved
    readlength = 100;
    B = double(mgeti(readlength, 'uc', fid));

    // Search for the first appearance of the 'data'
    // flag. This is  necessary since many different
    // non-essential information chunks could appear
    // before the real audio data. It is also necessary
    // to get the last audio byte since it will be at
    // this point where the new data must be inserted.
    // After the last audio data there may be other
    // chunks such as cues and labels which should be 
    // restored to the wav file after appending the 
    // new audio
    for k = 1:length(B)-4
        if ascii(B(k:k+3))=="data"
            // "data" flag is at the kdata position
            kdata = k;
            break
        end
    end

    // File length   
    Filelength = hexle2dec(B(5:8)) + 8;
    
    // Number of bytes in the audio chunk
    Audiolength = hexle2dec(B(k+4:k+7));

    // Block length (usually 16)
    Blocklength = hexle2dec(B(17:20));

    // Format (1 for linear uncompressed PCM, the only
    // supported format)
    Format = hexle2dec(B(21:22));

    // Number of channels
    Channels = hexle2dec(B(23:24));

    // Sampling rate in Hz
    Fs = hexle2dec(B(25:28));

    // Baudrate or average number of bytes per unit time
    // in bytes/s
    Baudrate = hexle2dec(B(29:32));

    // Block align (bytes per multichannel sample, for
    // instance, if mono and 16 bit it is 2 and if stereo 
    // and 16 bit, it is 4)
    BlockAlign =  hexle2dec(B(33:34));

    // Number of bits per sample per channel, i.e., 
    // resolution
    Nbits =  hexle2dec(B(35:36));

    // Abort function if unsupported format
    if Format~=1
        disp(' ')
        disp('This compressed format is not supported')   
        disp(' ')
        return
    end


    // INFORMATION ON NEW DATA TO APPEND
    // ---------------------------------
    if size(x)(2)==1
        x = x';
    end

    // If the old signal is stereo and the new signal
    // is mono, convert the latter to stereo
    if (Channels==2) & (size(x)(1)==1)
        x = [x; x];
    end

    // If the old signal is mono and the new signal
    // is stereo, convert the latter to mono by
    // mixing both channels at 50 percent
    if (Channels==1) & (size(x)(1)==2)
        x = (x(1,:) + x(2,:))/2;
    end

    // Length of the audio data to append 
    appendaudiolength = prod(size(x)) * Nbits/8;

    // Predict file length after inclusion of
    // the new data
    totalfilelength = Filelength + appendaudiolength;

    // Length of the complete audio data after
    // the inclusion of the new data
    totalaudiolength = Audiolength + appendaudiolength;


    // FORMATTING NEW AUDIO TO APPEND
    // ------------------------------

    // Clip x to acomodate the interval -1 <= z < 1  
    newaudioclipped = max(min(x, 1-2^(1-Nbits)), -1);

    // Convert new audio to signed integer
    newaudio = floor(newaudioclipped * 2^(Nbits-1));

    // Convert stereo data (two-row array) to
    // interleaved row vector by first converting
    // to single column and then transposing to
    // single row     
    newaudio = newaudio(:)';


    // GETTING NON-AUDIO INFORMATION (CUE CHUNK, ETC.)
    // -----------------------------------------------

    // Relocate file position indicator at the end
    // of the audio data chunk
    enddata = kdata + 7 + Audiolength;
    mseek(enddata, fid);

    // The ending section of the file, after the data
    // chunk, is read
    n = Filelength - enddata
    C = mgeti(n,"uc",fid)

    // SUBSTITUTION OF NEW HEADER INFORMATION
    // --------------------------------------

    // Offset to write new file length
    offset = 4;

    // Locate file position indicator
    mseek(offset, fid);

    // Substitute new for old file length
    // Since the file length is saved as a four-byte
    // unsigned integer, the new length is saved as
    // a little-endian 32-bit unsigned integer 
    mput(totalfilelength - 8, "uil", fid);

    // Offset to write new audio length
    offset = kdata + 3;

    // Locate file position indicator
    mseek(offset, fid);
    
    // Substitute new for old audio length 
    mput(totalaudiolength, 'uil', fid );

    // Offset to write append audio data
    offset = enddata;

    // Locate file position indicator
    mseek(offset, fid);
    
    // Append audio, no matter it is overwriting
    // aditional information: 
    // the latter has been saved in vector C
    mput(newaudio, 'sl', fid);

    // If there was no additional informacion 
    // (i.e., C = []) then do not attempt to write
    // inexistent information
    if ~isempty(C)
        // Offset to restore aditional information
        offset = enddata + appendaudiolength;

        // Locate file position indicator
        mseek(offset, fid);

        // Restore aditional information after
        // new appended audio
        mput(C, "c", fid);
    end   

    // Close file for actual disk update (if
    // not closed the information may not be
    // effectively written) 
    mclose(fid);

endfunction

// ------------------------------------------------------
// LOCAL FUNCTION
// ------------------------------------------------------

function y=hexle2dec(z)
    // Conversion of little-endian bytes 
    // (hexadecimal expressed as decimal) 
    // to decimal

    // Length of z
    s = length(z);

    // The number is, essentially, in base 256.
    // First the order is reversed to get the 
    // natural exprsession of numbers.
    y = sum(256.^(0:s-1).*z);
endfunction
