am_imelody.cc

来自「Motorola synergy audio component」· CC 代码 · 共 983 行 · 第 1/2 页

CC
983
字号
/* (c) Copyright Motorola 2001, All rights reserved.
   Motorola Confidential Proprietary
   Contains confidential proprietary information of Motorola, Inc.
   Reverse engineering is prohibited.
   The copyright notice does not imply publication.
   
   DESCRIPTION:
       This file contains all iMelody support in AM.

***************************** REVISION HISTORY ******************************
 
   Date        Author      Reference Number
   ========    ========    ================
   02-12-19    w13738      CR - LIBbb71564
               Abnormal downloaded iMelody alert tone
               Merge from T720

   02-10-30    bof010      CR - LIBbb65108
               Invalid format in stored melody (Port the fix from TALON)

   02-20-02    w17860      CR - LIBbb18244
               ATC - problems with playing tones created in Notes editor
               Changed the second NextNonLfcrASCII function call in 
               ConvertNoteLength to take in ptr and not pitch.

   01-03-18    ktang       CR - CSGce90044
               Implemented iMelody parsor

*/
/************** INCLUDES ******************************************************/
#include <SUAPI/suapi.h>
#include <audio/am_custom_tune.h>
#include <audio/AM_iMelody.H>
#include <string.h>

/************** LOCAL CONSTANTS ***********************************************/
/* The 8 values in the below array correpond to the indices to note types A, B, C,
   E, F, G, and R in am_hw_phase_table defined in AM_HW_Primitive_Builder_tone.cc */
static const UINT8 PitchIndexTable[8] = {0, 2, 3, 5, 7, 8, 10, 12};

#define IMELODY_MAX_TEXT_LEN        75

const UINT8 IMELODY_MARKER_begin[]          = "BEGIN:";
const UINT8 IMELODY_MARKER_version[]        = "VERSION:";
const UINT8 IMELODY_MARKER_format[]         = "FORMAT:";
const UINT8 IMELODY_MARKER_name[]           = "NAME:";
const UINT8 IMELODY_MARKER_composer[]       = "COMPOSER:";
const UINT8 IMELODY_MARKER_beat[]           = "BEAT:";
const UINT8 IMELODY_MARKER_style[]          = "STYLE:S";
const UINT8 IMELODY_MARKER_volume[]         = "VOLUME:";
const UINT8 IMELODY_MARKER_melody[]         = "MELODY:";
const UINT8 IMELODY_MARKER_end[]            = "END:";
const UINT8 IMELODY_MARKER_endimelody[]     = "END:IMELODY";
const UINT8 IMELODY_MARKER_default_header[] = \
    "BEGIN:IMELODY\nVERSION:1.2\nFORMAT:CLASS1.0\n";
    
/************** GLOBAL DECLARATIONS  ******************************************/

/************** FUNCTION DEFINITIONS ******************************************/
/* DESCRIPTION:
       Constructor

   INPUTS:
       None

   OUTPUTS:
       None

   IMPORTANT NOTES:
       None
*/
AM_iMelody::AM_iMelody (void)
{
    Reset();
    octaveMethod = VERBOSE_OCTAVE;
}
AM_iMelody::AM_iMelody (IMELODY_OCTAVE_METHOD method)
{
    Reset();
    octaveMethod = method;
}

void
AM_iMelody::Reset()
{
    head = NULL;
    body = NULL;
    tail =  NULL;
    wrt_ptr = NULL;
    octave_ptr = NULL;
    for (UINT8 i = 0; i < NUMBER_OF_FIELDS; i++)
    {
        TuneCharacters[i] = 0;
    }
    tuneDuration = 0;
}

/* DESCRIPTION:
       Translates the user readable musical note stream to AM readable. The
   AM readable format is specified in am_hw_primitive_builder_tone.h

   INPUTS:
       note_stream, pointer to the user readable musical note stream.
       outputStream, pointer to the buffer where the parsed data is to be
                     written to.
       outBuffByteSize, the byte length of the output buffer.
       action, play the whole tune / the last note in the tune / or get the
               duration of hte whole tune only.

   OUTPUTS:
       The duration of the whole tune in ms

   IMPORTANT NOTES:
       The input stream is assumed NULL terminated
*/
UINT8*
AM_iMelody::TuneTranslation (UINT8* inputStream, UINT8* outputStream,
                   UINT16 outBuffByteSize, AUD_CUSTOM_TYPE action)
{
    UINT16 AM_note = AM_CUSTOM_INVALID_NOTE;

    SetInput(inputStream);
    SetOutput(outputStream, outBuffByteSize);

    GetTuneInfoFromHeader(NULL);

    /* The reason why to write a byte of UINT16 at a time is the passed in
       buffer may start from an odd address                                  */
    while (tail > body)
    {
        if ((AM_note = NoteTranslation()) != AM_CUSTOM_INVALID_NOTE)
        {
            // The lower byte is duration field
            UINT8 tmpDuration = (UINT8)(AM_note & 0x00FF);

            *(--wrt_ptr) = tmpDuration;
            /* Take the upper byte of UINT16 and write to the buffer; */
            *(--wrt_ptr) = (UINT8)(AM_note >> 8);
            TuneCharacters[NOTE_COUNT]++;

            /* The time unit in am_hw_note_length_index_table is 125 us, shift it right by 3 for ms */
            TuneCharacters[TUNE_DURATION] += AM_MUSIC_calc_DURATION(TuneCharacters[TEMPO], tmpDuration) >> 3;

            if (action == PLAY_CUSTOM_NOTE)
            {
                /* this will terminate the backward note parsing */
                tail = head;
            }
        }
    }

    if (TuneCharacters[NOTE_COUNT] == 0)
    {
        wrt_ptr = NULL;
    }

    return wrt_ptr;
}

/* DESCRIPTION:
       Translates the user readable musical note to AM readable. The
   AM readable format is specified in am_hw_primitive_builder_tone.h

   INPUTS:
       None

   OUTPUTS:
       The AM readable musical note

   IMPORTANT NOTES:
       None
*/
UINT16
AM_iMelody::NoteTranslation()
{
    UINT8* pitch_ptr = tail;
    UINT8* tmp_ptr;
    UINT8 pitch;
    UINT8 octave;
    UINT16 note_len;
    UINT16 AM_note = AM_CUSTOM_INVALID_NOTE;

    /* Search backwards to look for the next pitch in the stream. */
    do
    {
        --pitch_ptr;
    }
    while (!IsAPitch(*pitch_ptr) && pitch_ptr > body);

    /* if there is such a note to be translated... */
    if (IsAPitch(*pitch_ptr))
    {
        /* if no valid note length, i.e. > 0xff, skip the possible note */
        if ((note_len = ConvertNoteLength(pitch_ptr)) < 0x100)
        {
            octave = GetOctave(pitch_ptr);

            /* pass in the pitch field and modifier to get modified pitch */
            /* octave is passed in since it may need to be changed too */
            pitch = ConvertPitch(pitch_ptr, octave);

            /* Due to the hardware frequency range constraint, only octaves */
            /* 2 - 6 are returned. map 2 and 7 to 2;                        */
            /*                         3 and 8 to 3;                        */
            /*                         4       to 4;                        */
            /*                         0 and 5 to 5; and                    */
            /*                         1 and 6 to 6.                        */
            octave = ((octave+3) % 5) + 2;

            /* puts in the octave field */
            AM_note = (((octave << NOTE_DEF_OCTAVE_SHIFT)
                        | (pitch << NOTE_DEF_PITCH_SHIFT)) << 8) +
                      note_len;
        }
    }

    tail = pitch_ptr;

    return AM_note;
}


/* DESCRIPTION:
       Compares character

   INPUTS:
       a, character 1
       b, character 2

   OUTPUTS:
       FALSE is equal, TRUE is not, follow C string lib convension

   IMPORTANT NOTES:
       If both values passed in are with the range of ASCII
       'a' - 'z' or 'A' - 'Z', diff them case-insensitively
*/
BOOL
AM_iMelody::DiffInsensitive (UINT8 a, UINT8 b)
{
    UINT8 a_other_case = a;

    // if a is an upper case letter
    // its lower case will be a_other_case
    if ((a >= 'A') && (a <= 'Z'))
    {
        a_other_case += 0x20;
    }
    // if a is an low case letter
    // its upper case will be a_other_case
    else if ((a >= 'a') && (a <= 'z'))
    {
        a_other_case -= 0x20;
    }

    return ((a != b) && (a_other_case != b));
}


/* DESCRIPTION:
       Searches the given field in the string within the range from start to
       end (which points to the first uninterested character)
       case insensitive.

   INPUTS:
       start, pointer to the begginning of the string to be searched
       field, a supposedly shorter string to be looked for in another
              string pointed by start.
       end, pointer to the end of the string to be searched

   OUTPUTS:
       Pointer to the charater next to the found field string. If field string
       is not found, it points to the same position end does.

   IMPORTANT NOTES:
       None
*/
UINT8*
AM_iMelody::SearchField(UINT8* start, const UINT8 field[], UINT8* end)
{
    UINT8* ptr = start;
    UINT8 upper_case = 0;
    UINT8 lower_case = 0;
    UINT8 indx = 0;

    /* search until either of the strings is exhausted */
    while ((field[indx] != NULL) && (ptr < end))
    {
        /* need to find the possible beginning of the field */
        if (indx == 0)
        {
            /* find the firt character in the string */
            while ((ptr < end) && DiffInsensitive(field[0], *ptr))
            {
                ptr++;
            }
            if (ptr < end)
            {
                ptr++;
                indx++;
            }
        }
        /* need to match the rest of the charaters. If any one is unmatched,
           we need to go back to beginning of the 'field' research */
        else if (DiffInsensitive(field[indx], *ptr))
        {
            ptr -= (indx - 1);
            indx = 0;
        }
        /* if everything else */
        else
        {
            indx++;
            /* increment by 1, but make sure the next character */
            /* is not LF or CR */
            if ((ptr = NextNonLfcrASCII(end, ptr, 1)) == NULL)
            {
                ptr = end;
            }
        }
    }

    /* Upon exit from the while loop, ptr points to either start[indx], i.e.
       the character in the searched string that is next to pattern 'field'
       just found; or *end, i.e. the end of the searched string*/
    return ptr;
}


/* DESCRIPTION:
       Converts note length from iMeldoy defined values to AM defined.

   INPUTS:
       duration, iMeldoy defined duration value
       modifier, iMeldoy defined duration modifier 

   OUTPUTS:
       AM defined duration value

   IMPORTANT NOTES:
       In case of syntax error, a quarter is taken as default.
*/
UINT16
AM_iMelody::ConvertNoteLength (UINT8* pitch)
{
    UINT16 NoteLen = 0xffff;
    UINT8* ptr = NextNonLfcrASCII(tail, pitch, 1);

    if (ptr != NULL)
    {
        if (*ptr >= '0' && *ptr <= '5')
        {
            NoteLen = *ptr - '0';
        }
        else if (*ptr == NULL)
        {
            NoteLen = 2;
        }
    }

    if (NoteLen != 0xffff)
    {
        // convert from iMelody values to AM values, that
        // musically is of the same duration.
        NoteLen = 64 >> NoteLen;

        ptr = NextNonLfcrASCII(tail, ptr, 1);
        if (ptr != NULL)
        {
            switch (*ptr)
            {
                // dotted note, plus 1/2 of its length
                case '.': NoteLen += NoteLen >> 1; break;
                // double dotted note, plus 3/4 of its length
                case ':': NoteLen += (NoteLen * 3) >> 2; break;
                // plus 2/3 of its length
                case ';': NoteLen += (NoteLen << 1) / 3; break;
                default: break;
            }
        }
    }

    // convert to 0 based
    return (NoteLen - 1);
}

/* DESCRIPTION:
       Converts the iMelody defined pitch to AM octave value

   INPUTS:
       pitch, 'a', 'b', 'c', 'd', 'e', 'f', 'g', and 'r'
       octave, reference to the octave

   OUTPUTS:
       AM pitch value

   IMPORTANT NOTES:
       The lowest pitch in the same ocatve is C, so octaves will need to be
       incremented by 1 when *2#B (which is really *3C)i fhte octave to be
       incremented is not already the highest; and decremented by 1 when
       *2&C (which is really *1B) if octave to be decremented is not
       already the lowest. 
*/
UINT8
AM_iMelody::ConvertPitch (UINT8* pitch_ptr, UINT8& octave)
{
    UINT8 modifier = 0;
    UINT8 pitch = *pitch_ptr;
    UINT8* ptr = NextNonLfcrASCII(body, pitch_ptr, -1);

    if (ptr != NULL)
    {
        modifier = *ptr;
    }


    if (pitch == 'r')
    {
        // make all rest notes' octave 0's.
        octave = 0;
        pitch = 'h';
        // sharp or flat rests are still rests
        modifier = 0;
    }

    pitch = PitchIndexTable[pitch - 'a'];

    switch (modifier)
    {
        case SHARP:
            pitch = (pitch + 1) % NUMBER_OF_PITCHES;
            /* SHARP B is equavilent to C (index == 3) of one octave higher */
            /* increment octave only if it is not already the highest */
            if (pitch == 3 && octave < HIGHEST_OCTAVE)
            {
                octave++;
            }
            break;
        case FLAT:
            pitch = (pitch + NUMBER_OF_PITCHES - 1) % NUMBER_OF_PITCHES;
            /* FLAT C is equavilent to B (index == 2) of one octave lower */
            /* decrement octave only if it is not already the lowest */
            if (pitch == 2 && octave > LOWEST_OCTAVE)
            {
                octave--;
            }
            break;
    }

    return pitch;
}

/* DESCRIPTION:
       Converts the iMelody defined octave to AM octave value, in verbose method

   INPUTS:
       pitch, pointer to the found pitch

   OUTPUTS:
       returns a pionter to the octave if found; NULL otherwise;

   IMPORTANT NOTES:
       None
          
*/
UINT8*
AM_iMelody::GetVerboseOctave(UINT8* pitch)
{
    UINT8* ptr2;
    UINT8* ptr = NextNonLfcrASCII(body, pitch, -1);

    /* search up one more if this is pitch modifier */
    if (ptr != NULL && (*ptr == SHARP || *ptr == FLAT))
    {
        ptr = NextNonLfcrASCII(body, ptr, -1);
    }

    /* if this is not within the octave range, it's garbage */
    if (ptr != NULL && *ptr < LOWEST_OCTAVE && *ptr > HIGHEST_OCTAVE)
    {
        ptr = NULL;
    }

    /* if the next character is not octave marker, the */
    /* one we are looking at is not a valid octave */
    if (ptr != NULL &&
        ((ptr2 = NextNonLfcrASCII(body, ptr, -1)) == NULL ||
         *ptr2 != OCTAVE_MARKER))
    {
        ptr = NULL;
    }

    return ptr;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?