diff --git a/.gitignore b/.gitignore index fac58fb31..acbf75258 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ Praat.exe # files created by R scripts in the test folders .Rdata .Rhistory + +\.autotools +\.project +\.cproject +\.settings/ diff --git a/fon/Feature.cpp b/fon/Feature.cpp new file mode 100644 index 000000000..3eac449a2 --- /dev/null +++ b/fon/Feature.cpp @@ -0,0 +1,417 @@ +/* + * Feature.cpp + * + * Copyright (C) 1992-2012,2013,2014,2015 Paul Boersma + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Created on: Feb 4, 2016 + * Author: ivan + */ + +#include "Feature.h" + +#define my me -> + +/** + * Given a tierFeatures it returns a pointer to the current feature. + * If there is no feature created then it creates the first and returns it. + */ +Feature* TierFeatures_getCurrentFeature(TierFeatures* data){ + if(data->currentFeature == nullptr){ + data->firstFeature = new Feature(); + data->currentFeature = data->firstFeature; + } + return data->currentFeature; +} + +/** + * Given a tierFeatures it returns a pointer to the next feature after the current. + * If there is no feature after the current one, then it creates and returns it. + */ +Feature* TierFeatures_getNextFeature(TierFeatures* data){ + if(data->currentFeature == nullptr){ + data->firstFeature = new Feature(); + data->currentFeature = data->firstFeature; + }else{ + if(data->currentFeature->nxtPtr == nullptr){ + data->currentFeature->nxtPtr = new Feature(); + data->currentFeature = data->currentFeature->nxtPtr; + } + } + return data->currentFeature; +} + + +/** + * A states machine that reads the text and extracts its features data creating and saving it into a tierFeatures type structure. + * Structure as follows: + * State 0: Accepts any char until "@{@". Everything from the beginning is the interval or point text. + * State 1: Accepts blank spaces until "\"". + * State 2: Accepts any char until "\"". Everything from state 1 is a feature label. + * State 3: Accepts blank spaces until "=". + * State 4: Accepts blank spaces until "\"". + * State 5: Accepts any char until "\"". Everything from state 4 is a feature value. + * State 6: Accepts blank spaces until "," in which case it returns to state 2 and expects a new feature or "@}@" in which case it finishes. + * State 7: Ended correctly + * State 8: Error the whole string will be considered interval or point text. + */ +TierFeatures* TierFeatures_extractFromText(const char32* text){ + if(text == nullptr) return new TierFeatures(); + int state = 0; + int64 pos = 0; + const char32* currentPos = text; + const char32* lastCheckPoint = text; + TierFeatures* data = new TierFeatures(); + const char32* temp; + do{ + char32 currentChar = *currentPos++; + pos++; + + switch(state){ + case 0: + if(currentChar != U'@') break; + if(str32len(currentPos) < 2) break; + temp = currentPos; + if(*temp++ == U'{' && *temp++ == U'@'){ + currentPos = temp; + MelderString_ncopy(&data->headText, lastCheckPoint, pos-1); + lastCheckPoint = currentPos; + pos = 0; + state = 1; + } + break; + case 1: + if(currentChar == U'@'){ + if(str32len(currentPos) < 2){ state = 8; break;} + temp = currentPos; + if(*temp++ == U'}' && *temp++ == U'@'){ + currentPos = temp; + pos+=2; + lastCheckPoint = currentPos; + pos = 0; + state = 7; + break; + }else{ + state = 8; + break; + } + } + if(currentChar != U'"' && currentChar != U' '){ state = 8; break;} + if(currentChar != U'"') break; + lastCheckPoint = currentPos; + pos = 0; + state = 2; + break; + case 2: + if(currentChar == U'\\'){ + if(str32len(currentPos) < 1) break; + currentPos++; + pos++; + break; + } + if(currentChar != U'"') break; + MelderString_ncopy(&TierFeatures_getNextFeature(data)->label, lastCheckPoint, pos-1); + lastCheckPoint = currentPos; + pos = 0; + state = 3; + break; + case 3: + if(currentChar != U'=' && currentChar != U' '){ state = 8; break;} + if(currentChar != U'=') break; + lastCheckPoint = currentPos; + pos = 0; + state = 4; + break; + case 4: + if(currentChar != U'"' && currentChar != U' '){ state = 8; break;} + if(currentChar != U'"') break; + lastCheckPoint = currentPos; + pos = 0; + state = 5; + break; + case 5: + if(currentChar == U'\\'){ + if(str32len(currentPos) < 1) break; + currentPos++; + pos++; + break; + } + if(currentChar != U'"') break; + MelderString_ncopy(&TierFeatures_getCurrentFeature(data)->value, lastCheckPoint, pos-1); + lastCheckPoint = currentPos; + pos = 0; + state = 6; + break; + case 6: + if(currentChar == U'@'){ + if(str32len(currentPos) < 2){ state = 8; break;} + temp = currentPos; + if(*temp++ == U'}' && *temp++ == U'@'){ + currentPos = temp; + pos+=2; + lastCheckPoint = currentPos; + pos = 0; + state = 7; + break; + }else{ + state = 8; + break; + } + } + if(currentChar != U',' && currentChar != U' '){ state = 8; break;} + if(currentChar != U',') break; + lastCheckPoint = currentPos; + pos = 0; + state = 1; + break; + } + }while(state != 8 && state != 7 && *currentPos != U'\0'); + + if(state != 7){ + delete data; + data = new TierFeatures(); + MelderString_copy(&data->headText, text); + } + + return data; +} + +/** + * It encodes a Feature text by adding backslash before special characters so it can be stores inside Tierfeatures text + */ +void Feature_encodeText(MelderString* me){ + int sizeNeeded = my length; + for(int i = 0;i < my length; i++){ + if(my string [i] == U'"' || my string [i] == U'\\') + sizeNeeded++; + } + + if (sizeNeeded > my bufferSize) MelderString_expand (me, sizeNeeded); + + for(int newSize = sizeNeeded, oldSize = my length; newSize >= 0 && oldSize >= 0; newSize--, oldSize--){ + my string [newSize] = my string [oldSize]; + if(my string [newSize] == U'"' || my string [newSize] == U'\\'){ + newSize --; + my string [newSize] = U'\\'; + } + } + my length = sizeNeeded; +} + +/** + * It decodes a Feature text coming from TierFeatures text by deleting backslash preceding special characters + */ +void Feature_decodeText(MelderString* me){ + int newLenght = my length; + for(int newPos = 0, oldPos = 0; newPos <= my length && oldPos <= my length; newPos++, oldPos++){ + if(my string [oldPos] == U'\\'){ + oldPos ++; + newLenght --; + } + my string [newPos] = my string [oldPos]; + } + my length = newLenght; +} + +/** + * Given a tierFeatures and a label, returns the Feature with the same label. Null if the feature does not exist. + */ +Feature* TierFeatures_getExistentFeature(const TierFeatures* data, const char32* label){ + if(data == nullptr) return nullptr; + + Feature* current = data->firstFeature; + while(current != nullptr){ + if(str32equ(current->label.string, label)) + return current; + current = current->nxtPtr; + } + return nullptr; +} + +/** + * Adds a new feature with label and value to the given TierFeatures. + * If the feature already exists overwrites its value. + */ +void Feature_addFeatureToTierFeatures(TierFeatures* data, MelderString* label, MelderString* value){ + if(data == nullptr) return; + Feature_encodeText(label); + Feature_encodeText(value); + + Feature* ftr = TierFeatures_getExistentFeature(data, label->string); + if(ftr != nullptr){ + MelderString_copy(&ftr->value, value->string); + }else{ + MelderString_copy(&TierFeatures_getNextFeature(data)->label, label->string); + MelderString_copy(&TierFeatures_getCurrentFeature(data)->value, value->string); + } + +} + +/** + * Deletes the Feature with given label from the TierFeatures. + */ +void Feature_deleteFeatureFromTierFeatures(TierFeatures* data, MelderString* label){ + if(data == nullptr) return; + Feature_encodeText(label); + Feature* ftr = TierFeatures_getExistentFeature(data, label->string); + if(ftr == nullptr){ + return; + } + Feature* tmp = data->firstFeature; + if(tmp->nxtPtr == nullptr){ + data->firstFeature = nullptr; + data->currentFeature = nullptr; + delete ftr; + return; + }else if(tmp == ftr){ + data->firstFeature = tmp->nxtPtr; + delete ftr; + return; + }else{ + while(tmp->nxtPtr != ftr){ + tmp = tmp->nxtPtr; + } + if(data->currentFeature == ftr) data->currentFeature = tmp; + if(tmp->nxtPtr->nxtPtr != nullptr) tmp->nxtPtr = tmp->nxtPtr->nxtPtr; + else tmp->nxtPtr = nullptr; + delete ftr; + } +} + +/** + * Returns an integer representing the total length of the text of the TierFeatures. + */ +int countTierFeaturesLength(TierFeatures* data){ + if(data == nullptr) return 0; + int totalLenght = 0; + //if(data->text != nullptr) + totalLenght += data->headText.length; + totalLenght += 4; + Feature* tempPtr = data->firstFeature; + while(tempPtr != nullptr){ + totalLenght += 1; + totalLenght += tempPtr->label.length; + totalLenght += 5; + totalLenght += tempPtr->value.length; + totalLenght += 1; + tempPtr = tempPtr->nxtPtr; + if(tempPtr != nullptr) + totalLenght += 2; + } + totalLenght += 5; + return totalLenght; +} + +/** + * It sets in a result MelderString the text of the TierFeatures. + */ +void TierFeatures_generateText(TierFeatures* data, MelderString * result){ + if(data == nullptr) return; + int totalLenght = countTierFeaturesLength(data); + if(totalLenght <= 0) return; + + if(data->headText.bufferSize > 0) + MelderString_append(result, data->headText.string); + if(data->firstFeature != nullptr){ + MelderString_append(result, U"@{@ "); + Feature* tempPtr = data->firstFeature; + while(tempPtr != nullptr){ + MelderString_append(result, U"\""); + if(tempPtr->label.bufferSize > 0) + MelderString_append(result, tempPtr->label.string); + MelderString_append(result, U"\" = \""); + if(tempPtr->value.bufferSize > 0) + MelderString_append(result, tempPtr->value.string); + MelderString_append(result, U"\""); + tempPtr = tempPtr->nxtPtr; + if(tempPtr != nullptr) + MelderString_append(result, U", "); + } + MelderString_append(result, U" @}@"); + } + MelderString_append(result, U"\0"); +} + +/** + * Sets in result MelderString the given text plus a new feature with the given label and value. + * If feature already exists it is overwritten with given value. + */ +void Feature_addFeatureToText(const char32* text, MelderString* label, MelderString* value, MelderString* result){ + TierFeatures* features = TierFeatures_extractFromText(text); + Feature_addFeatureToTierFeatures(features, label, value); + TierFeatures_generateText(features, result); +} + +/** + * Sets in result MelderString the given text without the feature with the given label. + */ +void Feature_deleteFeatureFromText(const char32* text, MelderString* label, MelderString* result){ + TierFeatures* features = TierFeatures_extractFromText(text); + Feature_deleteFeatureFromTierFeatures(features, label); + TierFeatures_generateText(features, result); +} + +/** + * Changes the content of headText in the TierFeatures for a copy of the given one. + */ +void TierFeatures_replaceHeadText(TierFeatures* data, const char32* text){ + if(data == nullptr) return; + MelderString_ncopy(&data->headText, text, str32len(text)); +} + +/** + * Returns a pointer to the Feature in the given position. Melder error if feature does not exist. + */ +Feature* TierFeatures_getFeature(TierFeatures* data, int position){ + Feature* tmp = data->firstFeature; + int count = 0; + while(tmp != nullptr && count < position -1){ + count ++; + tmp = tmp->nxtPtr; + } + if(tmp == nullptr){ + Melder_throw (U"Feature does not exist."); + } +} + +/** + * Return the number of Features in a TierFeature. + */ +int TierFeatures_getNumberOfFeatures(TierFeatures* data){ + Feature* tmp = data->firstFeature; + int result = 0; + while(tmp != nullptr){ + result ++; + tmp = tmp->nxtPtr; + } + return result; +} + +/** + * Sets in result MelderString a readable representation of the features in the TierFeatures. + */ +void TierFeatures_getFeaturesString(TierFeatures* data, MelderString* result){ + Feature* tmp = data->firstFeature; + while(tmp != nullptr){ + Feature_decodeText(&tmp->label); + Feature_decodeText(&tmp->value); + MelderString_append(result, tmp->label.string, U": ", tmp->value.string, U"\n"); + + tmp = tmp->nxtPtr; + } +} + + diff --git a/fon/Feature.h b/fon/Feature.h new file mode 100644 index 000000000..f0ec98d6b --- /dev/null +++ b/fon/Feature.h @@ -0,0 +1,76 @@ +#ifndef _Feature_h_ +#define _Feature_h_ +/* + * Feature.h + * + * Copyright (C) 1992-2012,2013,2014,2015 Paul Boersma + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Created on: Feb 4, 2016 + * Author: Ivan Latorre + */ + +#include "Preferences.h" + +struct Feature{ + autoMelderString label; + autoMelderString value; + Feature* nxtPtr; + Feature() + { + nxtPtr=nullptr; + } + ~Feature(){ + } +}; + +struct TierFeatures{ + autoMelderString headText; + Feature* firstFeature; + Feature* currentFeature; + TierFeatures() + { + firstFeature=nullptr; + currentFeature=nullptr; + } + ~TierFeatures(){ + Feature* tmp = firstFeature; + while(tmp != nullptr){ + Feature* toDelete = tmp; + tmp = tmp->nxtPtr; + delete toDelete; + } + } +}; + + +void Feature_encodeText(MelderString *me); +void Feature_decodeText(MelderString *me); +void Feature_addFeatureToText(const char32* text, MelderString* label, MelderString* value, MelderString* result); +void Feature_deleteFeatureFromText(const char32* text, MelderString* label, MelderString* result); + +TierFeatures* TierFeatures_extractFromText(const char32* text); +Feature* TierFeatures_getCurrentFeature(TierFeatures* data); +Feature* TierFeatures_getNextFeature(TierFeatures* data); +Feature* TierFeatures_getExistentFeature(const TierFeatures* data, const char32* label); +void TierFeatures_generateText(TierFeatures* data, MelderString * result); +void TierFeatures_replaceHeadText(TierFeatures* data, const char32* text); +Feature* TierFeatures_getFeature(TierFeatures* data, int position); +int TierFeatures_getNumberOfFeatures(TierFeatures* data); +void TierFeatures_getFeaturesString(TierFeatures* data, MelderString* result); + +/* End of file Feature.h */ +#endif diff --git a/fon/Makefile b/fon/Makefile index 2e3ca5fff..34f008b6a 100644 --- a/fon/Makefile +++ b/fon/Makefile @@ -36,9 +36,10 @@ OBJECTS = Transition.o Distributions_and_Transition.o \ manual_soundFiles.o manual_tutorials.o manual_references.o \ manual_programming.o manual_Fon.o manual_voice.o Praat_tests.o \ manual_glossary.o manual_Sampling.o manual_exampleSound.o \ - manual_sound.o manual_pitch.o manual_spectrum.o manual_formant.o manual_annotation.o \ + manual_sound.o manual_pitch.o manual_spectrum.o manual_formant.o manual_annotation.o \ praat_TimeFunction.o praat_TimeTier.o praat_TimeFrameSampled.o \ - praat_Sound.o praat_Matrix.o praat_Tiers.o praat_TextGrid_init.o praat_Fon.o + praat_Sound.o praat_Matrix.o praat_Tiers.o praat_TextGrid_init.o praat_Fon.o \ + Feature.o .PHONY: all clean diff --git a/fon/TextGrid.cpp b/fon/TextGrid.cpp index 55cac3dc9..3dc2978f6 100644 --- a/fon/TextGrid.cpp +++ b/fon/TextGrid.cpp @@ -18,6 +18,7 @@ #include "TextGrid.h" #include "longchar.h" +#include "Feature.h" #include "oo_DESTROY.h" #include "TextGrid_def.h" @@ -1217,6 +1218,24 @@ void TextGrid_setIntervalText (TextGrid me, integer tierNumber, integer interval } } + +void TextGrid_setIntervalHeadText (TextGrid me, integer tierNumber, integer intervalNumber, const char32 *text) { + try { + IntervalTier intervalTier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber); + if (intervalNumber < 1 || intervalNumber > intervalTier -> intervals.size) + Melder_throw (U"Interval ", intervalNumber, U" does not exist on tier ", tierNumber, U"."); + TextInterval interval = intervalTier -> intervals.at [intervalNumber]; + TierFeatures* features = TierFeatures_extractFromText(interval->text); + TierFeatures_replaceHeadText(features, text); + autoMelderString result; + TierFeatures_generateText(features, &result); + TextInterval_setText (interval, result.string); + delete features; + } catch (MelderError) { + Melder_throw (me, U": interval head text not set."); + } +} + void TextGrid_insertPoint (TextGrid me, integer tierNumber, double time, const char32 *mark) { try { TextTier textTier = TextGrid_checkSpecifiedTierIsPointTier (me, tierNumber); @@ -1261,6 +1280,23 @@ void TextGrid_setPointText (TextGrid me, integer tierNumber, integer pointNumber } } +void TextGrid_setPointHeadText (TextGrid me, integer tierNumber, integer pointNumber, const char32 *text) { + try { + TextTier textTier = TextGrid_checkSpecifiedTierIsPointTier (me, tierNumber); + if (pointNumber < 1 || pointNumber > textTier -> points.size) + Melder_throw (U"Point ", pointNumber, U" does not exist on tier ", tierNumber, U"."); + TextPoint point = textTier -> points.at [pointNumber]; + TierFeatures* features = TierFeatures_extractFromText(point->mark); + TierFeatures_replaceHeadText(features, text); + autoMelderString result; + TierFeatures_generateText(features, &result); + TextPoint_setText (point, result.string); + delete features; + } catch (MelderError) { + Melder_throw (me, U": point head text not set."); + } +} + static void sgmlToPraat (char *text) { char *sgml = text, *praat = text; for (;;) { diff --git a/fon/TextGrid.h b/fon/TextGrid.h index 27c42aaba..bd41a0ade 100644 --- a/fon/TextGrid.h +++ b/fon/TextGrid.h @@ -30,18 +30,25 @@ Collection_define (FunctionList, OrderedOf, Function) { #include "TextGrid_def.h" autoTextPoint TextPoint_create (double time, const char32 *mark); - void TextPoint_setText (TextPoint me, const char32 *text); +long TextPoint_labelLength (TextPoint me); +void TextPoint_removeText (TextPoint me); autoTextInterval TextInterval_create (double tmin, double tmax, const char32 *text); - void TextInterval_setText (TextInterval me, const char32 *text); +long TextInterval_labelLength (TextInterval me); +void TextInterval_removeText (TextInterval me); autoTextTier TextTier_create (double tmin, double tmax); - void TextTier_addPoint (TextTier me, double time, const char32 *mark); autoTextTier TextTier_readFromXwaves (MelderFile file); autoPointProcess TextTier_getPoints (TextTier me, const char32 *text); +void TextTier_removePoint (TextTier me, long ipoint); +long TextTier_maximumLabelLength (TextTier me); +void TextTier_removeText (TextTier me); +void TextTier_removePoints (TextTier me, int which_Melder_STRING, const char32 *criterion); +autoTableOfReal TextTier_downto_TableOfReal (TextTier me, const char32 *label); +autoTableOfReal TextTier_downto_TableOfReal_any (TextTier me); autoIntervalTier IntervalTier_create (double tmin, double tmax); autoIntervalTier IntervalTier_readFromXwaves (MelderFile file); @@ -57,6 +64,7 @@ autoPointProcess IntervalTier_getEndPoints (IntervalTier me, const char32 *text) autoPointProcess IntervalTier_getCentrePoints (IntervalTier me, const char32 *text); autoPointProcess IntervalTier_PointProcess_startToCentre (IntervalTier tier, PointProcess point, double phase); autoPointProcess IntervalTier_PointProcess_endToCentre (IntervalTier tier, PointProcess point, double phase); + void IntervalTier_removeLeftBoundary (IntervalTier me, integer intervalNumber); void TextTier_removePoint (TextTier me, integer pointNumber); @@ -102,6 +110,7 @@ integer TextPoint_labelLength (TextPoint me); integer IntervalTier_maximumLabelLength (IntervalTier me); integer TextTier_maximumLabelLength (TextTier me); integer TextGrid_maximumLabelLength (TextGrid me); + void TextGrid_convertToBackslashTrigraphs (TextGrid me); void TextGrid_convertToUnicode (TextGrid me); @@ -116,18 +125,24 @@ void TextGrid_removePoints (TextGrid me, integer tierNumber, kMelder_string whic void TextGrid_insertBoundary (TextGrid me, integer tierNumber, double t); void TextGrid_removeBoundaryAtTime (TextGrid me, integer tierNumber, double t); void TextGrid_setIntervalText (TextGrid me, integer tierNumber, integer intervalNumber, const char32 *text); +void TextGrid_setIntervalHeadText (TextGrid me, integer tierNumber, integer intervalNumber, const char32 *text); void TextGrid_insertPoint (TextGrid me, integer tierNumber, double t, const char32 *mark); void TextGrid_setPointText (TextGrid me, integer tierNumber, integer pointNumber, const char32 *text); +void TextGrid_setPointHeadText (TextGrid me, integer tierNumber, integer pointNumber, const char32 *text); void TextGrid_writeToChronologicalTextFile (TextGrid me, MelderFile file); autoTextGrid TextGrid_readFromChronologicalTextFile (MelderFile file); autoTextGrid TextGrid_readFromCgnSyntaxFile (MelderFile file); - autoTable TextGrid_downto_Table (TextGrid me, bool includeLineNumbers, int timeDecimals, bool includeTierNames, bool includeEmptyIntervals); void TextGrid_list (TextGrid me, bool includeLineNumbers, int timeDecimals, bool includeTierNames, bool includeEmptyIntervals); - void TextGrid_correctRoundingErrors (TextGrid me); autoTextGrid TextGrids_concatenate (OrderedOf* me); +autoTextGrid Label_to_TextGrid (Label me, double duration); +autoTextGrid Label_Function_to_TextGrid (Label me, Function function); + +autoTextTier PointProcess_upto_TextTier (PointProcess me, const char32 *text); +autoTextGrid PointProcess_to_TextGrid_vuv (PointProcess me, double maxT, double meanT); + /* End of file TextGrid.h */ #endif diff --git a/fon/TextGridEditor.cpp b/fon/TextGridEditor.cpp index 90a002a65..1563102ea 100644 --- a/fon/TextGridEditor.cpp +++ b/fon/TextGridEditor.cpp @@ -654,6 +654,218 @@ static void menu_cb_InsertIntervalOnTier6 (TextGridEditor me, EDITOR_ARGS_DIRECT static void menu_cb_InsertIntervalOnTier7 (TextGridEditor me, EDITOR_ARGS_DIRECT) { do_insertIntervalOnTier (me, 7); } static void menu_cb_InsertIntervalOnTier8 (TextGridEditor me, EDITOR_ARGS_DIRECT) { do_insertIntervalOnTier (me, 8); } +#define LABEL_INSERT_DATA U"Insert feature" +#define LABEL_SHOW_DATA U"Show features" +#define LABEL_DELETE_DATA U"Delete feature" +#define LABEL_GET_DATA U"Get feature" +static void menu_cb_InsertFeature (TextGridEditor me, EDITOR_ARGS_FORM) { + EDITOR_FORM (LABEL_INSERT_DATA, nullptr) + SENTENCE (labelVar, U"Label", U"") + SENTENCE (valueVar, U"Value", U"") + EDITOR_OK + SET_STRING (labelVar, U"") + SET_STRING (valueVar, U"") + EDITOR_DO + TextGrid grid = (TextGrid) my data; + checkTierSelection (me, LABEL_INSERT_DATA); + + if (my selectedTier) { + IntervalTier intervalTier; + TextTier textTier; + _AnyTier_identifyClass (grid -> tiers->at [my selectedTier], & intervalTier, & textTier); + Editor_save (me, LABEL_INSERT_DATA); + + autoMelderString label; + autoMelderString value; + MelderString_copy(&label, labelVar); + MelderString_copy(&value, valueVar); + + if(MelderString_isEmptyAfterTrim(&label) || MelderString_isEmptyAfterTrim(&value)){ + Melder_throw (U"Neither label nor value can be empty. Please fill the fields."); + } + + if(intervalTier != nullptr){ + long selectedInterval = getSelectedInterval (me); + if (selectedInterval) { + TextInterval interval = intervalTier -> intervals.at [selectedInterval]; + + autoMelderString newText; + Feature_addFeatureToText(interval->text, &label, &value, &newText); + TextInterval_setText (interval, newText.string); + FunctionEditor_updateText (me); + FunctionEditor_redraw (me); + Editor_broadcastDataChanged (me); + + }else{ + Melder_throw (U"Selected tier is not a valid tier. Please select an interval or a point in a valid tier by clicking it."); + } + } else { + long selectedPoint = getSelectedPoint (me); + if (selectedPoint) { + TextPoint point = textTier -> points.at [selectedPoint]; + + autoMelderString newText; + Feature_addFeatureToText(point -> mark, &label, &value, &newText); + TextPoint_setText (point, newText.string); + FunctionEditor_updateText (me); + FunctionEditor_redraw (me); + Editor_broadcastDataChanged (me); + } + else{ + Melder_throw (U"Selected tier is not a valid tier. Please select an interval or a point in a valid tier by clicking it."); + } + } + + } + EDITOR_END +} + +static void menu_cb_ShowFeatures (TextGridEditor me, EDITOR_ARGS_FORM) { + TextGrid grid = (TextGrid) my data; + checkTierSelection (me, LABEL_INSERT_DATA); + if (my selectedTier) { + IntervalTier intervalTier; + TextTier textTier; + _AnyTier_identifyClass (grid -> tiers->at [my selectedTier], & intervalTier, & textTier); + if(intervalTier != nullptr){ + long selectedInterval = getSelectedInterval (me); + if (selectedInterval) { + TextInterval interval = intervalTier -> intervals.at [selectedInterval]; + TierFeatures* features = TierFeatures_extractFromText(interval->text); + autoMelderString output; + TierFeatures_getFeaturesString(features, &output); + Melder_clearInfo (); + MelderInfo_open (); + MelderInfo_writeLine (output.string); + MelderInfo_close (); + delete features; + } + } else { + long selectedPoint = getSelectedPoint (me); + if (selectedPoint) { + TextPoint point = textTier -> points.at [selectedPoint]; + TierFeatures* features = TierFeatures_extractFromText(point->mark); + autoMelderString output; + TierFeatures_getFeaturesString(features, &output); + Melder_clearInfo (); + MelderInfo_open (); + MelderInfo_writeLine (output.string); + MelderInfo_close (); + delete features; + } + else{ + Melder_throw (U"Selected tier is not a valid tier. Please select an interval or a point in a valid tier by clicking it."); + } + } + } +} + +static void menu_cb_DeleteFeature (TextGridEditor me, EDITOR_ARGS_FORM) { + EDITOR_FORM (LABEL_DELETE_DATA, nullptr) + SENTENCE (labelVar, U"Label", U"") + EDITOR_OK + SET_STRING (labelVar, U"") + EDITOR_DO + TextGrid grid = (TextGrid) my data; + checkTierSelection (me, LABEL_DELETE_DATA); + if (my selectedTier) { + IntervalTier intervalTier; + TextTier textTier; + _AnyTier_identifyClass (grid -> tiers->at [my selectedTier], & intervalTier, & textTier); + Editor_save (me, LABEL_DELETE_DATA); + + autoMelderString label; + MelderString_copy(&label, labelVar); + if(MelderString_isEmptyAfterTrim(&label)){ + Melder_throw (U"Label cannot be empty. Please fill the field."); + } + + if(intervalTier != nullptr){ + long selectedInterval = getSelectedInterval (me); + if (selectedInterval) { + TextInterval interval = intervalTier -> intervals.at [selectedInterval]; + autoMelderString newText; + Feature_deleteFeatureFromText(interval->text, &label, &newText); + TextInterval_setText (interval, newText.string); + FunctionEditor_updateText (me); + FunctionEditor_redraw (me); + Editor_broadcastDataChanged (me); + } + }else { + long selectedPoint = getSelectedPoint (me); + if (selectedPoint) { + TextPoint point = textTier -> points.at [selectedPoint]; + autoMelderString newText; + Feature_deleteFeatureFromText(point->mark, &label, &newText); + TextPoint_setText (point, newText.string); + FunctionEditor_updateText (me); + FunctionEditor_redraw (me); + Editor_broadcastDataChanged (me); + } + else{ + Melder_throw (U"Selected tier is not a valid tier. Please select an interval or a point in a valid tier by clicking it."); + } + } + } + EDITOR_END +} + +static void menu_cb_GetFeature (TextGridEditor me, EDITOR_ARGS_FORM) { + EDITOR_FORM (LABEL_GET_DATA, nullptr) + SENTENCE (labelVar, U"Label", U"") + EDITOR_OK + SET_STRING (labelVar, U"") + EDITOR_DO + TextGrid grid = (TextGrid) my data; + checkTierSelection (me, LABEL_GET_DATA); + if (my selectedTier) { + IntervalTier intervalTier; + TextTier textTier; + _AnyTier_identifyClass (grid -> tiers->at [my selectedTier], & intervalTier, & textTier); + + autoMelderString label; + MelderString_copy(&label, labelVar); + MelderString_trim(&label); + Feature_encodeText(&label); + + if(intervalTier != nullptr){ + long selectedInterval = getSelectedInterval (me); + if (selectedInterval) { + TextInterval interval = intervalTier -> intervals.at [selectedInterval]; + TierFeatures* result = TierFeatures_extractFromText(interval->text); + Feature* ann = TierFeatures_getExistentFeature(result, label.string); + if(ann != nullptr){ + Melder_clearInfo (); + MelderInfo_open (); + Feature_decodeText(&ann->value); + MelderInfo_writeLine (ann->value.string); + MelderInfo_close (); + } + delete result; + } + } else { + long selectedPoint = getSelectedPoint (me); + if (selectedPoint) { + TextPoint point = textTier -> points.at [selectedPoint]; + TierFeatures* result = TierFeatures_extractFromText(point->mark); + Feature* ann = TierFeatures_getExistentFeature(result, label.string); + if(ann != nullptr){ + Melder_clearInfo (); + MelderInfo_open (); + Feature_decodeText(&ann->value); + MelderInfo_writeLine (ann->value.string); + MelderInfo_close (); + } + delete result; + } + else{ + Melder_throw (U"Selected tier is not a valid tier. Please select an interval or a point in a valid tier by clicking it."); + } + } + } + EDITOR_END +} + static void menu_cb_AlignInterval (TextGridEditor me, EDITOR_ARGS_DIRECT) { TextGrid grid = (TextGrid) my data; checkTierSelection (me, U"align words"); @@ -1219,6 +1431,11 @@ void structTextGridEditor :: v_createMenus () { EditorMenu_addCommand (menu, U"-- extract tier --", 0, nullptr); EditorMenu_addCommand (menu, U"Extract to list of objects:", GuiMenu_INSENSITIVE, menu_cb_PublishTier /* dummy */); EditorMenu_addCommand (menu, U"Extract entire selected tier", 0, menu_cb_PublishTier); + EditorMenu_addCommand (menu, U"-- annotations --", 0, nullptr); + EditorMenu_addCommand (menu, LABEL_INSERT_DATA, 0, menu_cb_InsertFeature); + EditorMenu_addCommand (menu, LABEL_DELETE_DATA, 0, menu_cb_DeleteFeature); + EditorMenu_addCommand (menu, LABEL_GET_DATA, 0, menu_cb_GetFeature); + EditorMenu_addCommand (menu, LABEL_SHOW_DATA, 0, menu_cb_ShowFeatures); if (spellingChecker) { menu = Editor_addMenu (this, U"Spell", 0); @@ -1411,7 +1628,9 @@ static void do_drawIntervalTier (TextGridEditor me, IntervalTier tier, int itier double t1 = my startWindow > tmin ? my startWindow : tmin; double t2 = my endWindow < tmax ? my endWindow : tmax; Graphics_setColour (my graphics.get(), intervalIsSelected ? Graphics_RED : Graphics_BLACK); - Graphics_textRect (my graphics.get(), t1, t2, 0.0, 1.0, interval -> text); + TierFeatures* ttd = TierFeatures_extractFromText(interval -> text); + Graphics_textRect (my graphics.get(), t1, t2, 0.0, 1.0, ttd->headText.string); + delete ttd; Graphics_setColour (my graphics.get(), Graphics_BLACK); } @@ -1483,7 +1702,11 @@ static void do_drawTextTier (TextGridEditor me, TextTier tier, int itier) { Graphics_setLineWidth (my graphics.get(), 1.0); } Graphics_setColour (my graphics.get(), pointIsSelected ? Graphics_RED : Graphics_BLUE); - if (point -> mark) Graphics_text (my graphics.get(), t, 0.5, point -> mark); + if (point -> mark){ + TierFeatures* ttd = TierFeatures_extractFromText(point -> mark); + Graphics_text (my graphics.get(), t, 0.5, ttd->headText.string); + delete ttd; + } } } Graphics_setPercentSignIsItalic (my graphics.get(), true); diff --git a/fon/TextGridEditor.h b/fon/TextGridEditor.h index 70f603ec9..9c6dad268 100644 --- a/fon/TextGridEditor.h +++ b/fon/TextGridEditor.h @@ -21,7 +21,8 @@ #include "TimeSoundAnalysisEditor.h" #include "TextGrid.h" #include "SpellingChecker.h" -#include "Preferences.h" +//#include "Preferences.h" +#include "Feature.h" #include "TextGridEditor_enums.h" diff --git a/fon/praat_TextGrid_init.cpp b/fon/praat_TextGrid_init.cpp index 5d89161a7..2e8ca8a57 100644 --- a/fon/praat_TextGrid_init.cpp +++ b/fon/praat_TextGrid_init.cpp @@ -34,6 +34,7 @@ static const char32 *STRING_FROM_FREQUENCY_HZ = U"left Frequency range (Hz)"; static const char32 *STRING_TO_FREQUENCY_HZ = U"right Frequency range (Hz)"; static const char32 *STRING_TIER_NUMBER = U"Tier number"; static const char32 *STRING_INTERVAL_NUMBER = U"Interval number"; +static const char32 *STRING_FEATURE_NUMBER = U"Feature number"; static const char32 *STRING_POINT_NUMBER = U"Point number"; // MARK: - ANYTIER (generic) @@ -1042,6 +1043,306 @@ DO STRING_ONE_END } +FORM (MODIFY_TextGrid_insertFeatureToInterval, U"TextGrid: Insert feature to interval", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + SENTENCE (labelText, U"Label", U"") + SENTENCE (valueText, U"Value", U"") + OK +DO + MODIFY_EACH (TextGrid) + TextInterval interval = pr_TextGrid_peekInterval (me, tierNumber, intervalNumber); + + MelderString label { 0 }; + MelderString value { 0 }; + MelderString_copy(&label, labelText); + MelderString_copy(&value, valueText); + + if(MelderString_isEmptyAfterTrim(&label) || MelderString_isEmptyAfterTrim(&value)){ + Melder_throw (U"Neither label nor value can be empty. Please fill the fields."); + } + + autoMelderString newText; + Feature_addFeatureToText(interval->text, &label, &value, &newText); + TextInterval_setText(interval, newText.string); + MODIFY_EACH_END +} + +FORM (MODIFY_TextGrid_deleteFeatureFromInterval, U"TextGrid: Remove feature from interval", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + SENTENCE (labelText, U"Label", U"") + OK +DO + MODIFY_EACH (TextGrid) + TextInterval interval = pr_TextGrid_peekInterval (me, tierNumber, intervalNumber); + + MelderString label { 0 }; + MelderString_copy(&label, labelText); + + if(MelderString_isEmptyAfterTrim(&label)){ + Melder_throw (U"Label cannot be empty. Please fill the field."); + } + autoMelderString newText; + Feature_deleteFeatureFromText(interval->text, &label, &newText); + TextInterval_setText (interval, newText.string); + MODIFY_EACH_END +} + +FORM (STRING_TextGrid_getFeatureFromInterval, U"TextGrid: Get feature from interval", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + SENTENCE (labelText, U"Label", U"") + OK +DO + STRING_ONE (TextGrid) + TextInterval interval = pr_TextGrid_peekInterval (me, tierNumber, intervalNumber); + + MelderString label { 0 }; + MelderString_copy(&label, labelText); + + if(MelderString_isEmptyAfterTrim(&label)){ + Melder_throw (U"Label cannot be empty. Please fill the field."); + } + + Feature_encodeText(&label); + + TierFeatures* features = TierFeatures_extractFromText(interval->text); + Feature* ann = TierFeatures_getExistentFeature(features, label.string); + + autoMelderString value; + if(ann != nullptr){ + MelderString_copy(&value, ann->value.string); + Feature_decodeText(&value); + } + const char32* result = value.string; + + delete features; + STRING_ONE_END +} + +FORM (STRING_TextGrid_showFeaturesInInterval, U"TextGrid: Show features in interval", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + OK +DO + STRING_ONE (TextGrid) + TextInterval interval = pr_TextGrid_peekInterval (me, tierNumber, intervalNumber); + TierFeatures* features = TierFeatures_extractFromText(interval->text); + autoMelderString output; + TierFeatures_getFeaturesString(features, &output); + const char32* result = output.string; + delete features; + STRING_ONE_END +} + +FORM (INTEGER_TextGrid_numberFeaturesInInterval, U"TextGrid: Get number of features in interval", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + OK +DO + INTEGER_ONE(TextGrid) + TextInterval interval = pr_TextGrid_peekInterval (me, tierNumber, intervalNumber); + TierFeatures* features = TierFeatures_extractFromText(interval->text); + int result = TierFeatures_getNumberOfFeatures(features); + delete features; + INTEGER_ONE_END(U"") +} + +FORM (STRING_TextGrid_labelFeaturesInInterval, U"TextGrid: Get label of feature in interval", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + NATURAL (featureNumber, STRING_FEATURE_NUMBER, U"1") + OK +DO + STRING_ONE(TextGrid) + TextInterval interval = pr_TextGrid_peekInterval (me, tierNumber, intervalNumber); + TierFeatures* features = TierFeatures_extractFromText(interval->text); + Feature* feature = TierFeatures_getFeature(features, featureNumber); + autoMelderString label; + MelderString_copy(&label, feature->label.string); + Feature_decodeText(&label); + const char32* result = label.string; + + delete features; + STRING_ONE_END +} + +FORM (STRING_TextGrid_headOfInterval, U"TextGrid: Get head of interval", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + OK +DO + STRING_ONE(TextGrid) + TextInterval interval = pr_TextGrid_peekInterval (me, tierNumber, intervalNumber); + TierFeatures* features = TierFeatures_extractFromText(interval->text); + autoMelderString head; + MelderString_copy(&head, features->headText.string); + const char32* result = head.string; + delete features; + STRING_ONE_END +} + +FORM (MODIFY_TextGrid_setIntervalHeadText, U"TextGrid: Set interval head text", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (intervalNumber, STRING_INTERVAL_NUMBER, U"1") + TEXTFIELD (text, U"Text:", U"") + OK +DO + MODIFY_EACH (TextGrid) + TextGrid_setIntervalHeadText (me, tierNumber, intervalNumber, text); + praat_dataChanged (me); + MODIFY_EACH_END +} + +FORM (MODIFY_TextGrid_setPointHeadText, U"TextGrid: Set point head text", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + TEXTFIELD (text, U"Text:", U"") + OK +DO + MODIFY_EACH (TextGrid) + TextGrid_setPointHeadText (me, tierNumber, pointNumber, text); + praat_dataChanged (me); + MODIFY_EACH_END +} + +FORM (STRING_TextGrid_headOfPoint, U"TextGrid: Get head of point", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + OK +DO + STRING_ONE(TextGrid) + TextPoint point = pr_TextGrid_peekPoint (me, tierNumber, pointNumber); + TierFeatures* features = TierFeatures_extractFromText(point->mark); + autoMelderString head; + MelderString_copy(&head, features->headText.string); + const char32* result = head.string; + delete features; + + STRING_ONE_END +} + +FORM (INTEGER_TextGrid_numberFeaturesInPoint, U"TextGrid: Get number of features in point", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + OK +DO + INTEGER_ONE(TextGrid) + TextPoint point = pr_TextGrid_peekPoint (me, tierNumber, pointNumber); + TierFeatures* features = TierFeatures_extractFromText(point->mark); + int result = TierFeatures_getNumberOfFeatures(features); + delete features; + INTEGER_ONE_END(U"") +} + +FORM (STRING_TextGrid_labelFeaturesInPoint, U"TextGrid: Get label of feature in point", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + NATURAL (featureNumber, STRING_FEATURE_NUMBER, U"1") + OK +DO + STRING_ONE(TextGrid) + TextPoint point = pr_TextGrid_peekPoint (me, tierNumber, pointNumber); + TierFeatures* features = TierFeatures_extractFromText(point->mark); + Feature* feature = TierFeatures_getFeature(features, featureNumber); + autoMelderString label; + MelderString_copy(&label, feature->label.string); + Feature_decodeText(&label); + const char32* result = label.string; + + delete features; + STRING_ONE_END +} + +FORM (MODIFY_TextGrid_insertFeatureToPoint, U"TextGrid: Insert feature to point", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + SENTENCE (labelText, U"Label", U"") + SENTENCE (valueText, U"Value", U"") + OK +DO + MODIFY_EACH (TextGrid) + TextPoint point = pr_TextGrid_peekPoint(me, tierNumber, pointNumber); + MelderString label { 0 }; + MelderString value { 0 }; + MelderString_copy(&label, labelText); + MelderString_copy(&value, valueText); + + if(MelderString_isEmptyAfterTrim(&label) || MelderString_isEmptyAfterTrim(&value)){ + Melder_throw (U"Neither label nor value can be empty. Please fill the fields."); + } + autoMelderString newText; + Feature_addFeatureToText(point->mark, &label, &value, &newText); + TextPoint_setText(point, newText.string); + MODIFY_EACH_END +} + +FORM (MODIFY_TextGrid_deleteFeatureFromPoint, U"TextGrid: Remove feature from point", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + SENTENCE (labelText, U"Label", U"") + OK +DO + MODIFY_EACH (TextGrid) + TextPoint point = pr_TextGrid_peekPoint(me, tierNumber, pointNumber); + MelderString label { 0 }; + MelderString_copy(&label, labelText); + + if(MelderString_isEmptyAfterTrim(&label)){ + Melder_throw (U"Label cannot be empty. Please fill the field."); + } + autoMelderString newText; + Feature_deleteFeatureFromText(point->mark, &label, &newText); + TextPoint_setText (point, newText.string); + MODIFY_EACH_END +} + +FORM (STRING_TextGrid_getFeatureFromPoint, U"TextGrid: Get feature from point", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + SENTENCE (labelText, U"Label", U"") + OK +DO + STRING_ONE(TextGrid) + TextPoint point = pr_TextGrid_peekPoint(me, tierNumber, pointNumber); + MelderString label { 0 }; + MelderString_copy(&label, labelText); + + if(MelderString_isEmptyAfterTrim(&label)){ + Melder_throw (U"Label cannot be empty. Please fill the field."); + } + Feature_encodeText(&label); + + TierFeatures* features = TierFeatures_extractFromText(point->mark); + Feature* ann = TierFeatures_getExistentFeature(features, label.string); + + autoMelderString value; + if(ann != nullptr){ + MelderString_copy(&value, ann->value.string); + Feature_decodeText(&value); + } + const char32* result = value.string; + + delete features; + STRING_ONE_END +} + +FORM (STRING_TextGrid_showFeaturesInPoint, U"TextGrid: Show features in point", nullptr) { + NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") + NATURAL (pointNumber, STRING_POINT_NUMBER, U"1") + OK +DO + STRING_ONE(TextGrid) + TextPoint point = pr_TextGrid_peekPoint(me, tierNumber, pointNumber); + TierFeatures* features = TierFeatures_extractFromText(point->mark); + autoMelderString output; + TierFeatures_getFeaturesString(features, &output); + const char32* result = output.string; + delete features; + STRING_ONE_END +} + FORM (INTEGER_TextGrid_getLowIndexFromTime, U"Get low index", U"AnyTier: Get low index from time...") { NATURAL (tierNumber, STRING_TIER_NUMBER, U"1") REAL (time, U"Time (s)", U"0.5") @@ -1586,24 +1887,36 @@ void praat_uvafon_TextGrid_init () { praat_addAction1 (classTextGrid, 1, U"Get end time of interval...", nullptr, praat_DEPTH_2, REAL_TextGrid_getEndTimeOfInterval); praat_addAction1 (classTextGrid, 1, U"Get end point...", U"*Get end time of interval...", praat_DEPTH_2 | praat_DEPRECATED_2016, REAL_TextGrid_getEndTimeOfInterval); praat_addAction1 (classTextGrid, 1, U"Get label of interval...", nullptr, praat_DEPTH_2, STRING_TextGrid_getLabelOfInterval); + praat_addAction1 (classTextGrid, 1, U"Get head of interval...", nullptr, praat_DEPTH_2, STRING_TextGrid_headOfInterval); praat_addAction1 (classTextGrid, 1, U"-- query interval from time --", nullptr, praat_DEPTH_2, nullptr); - praat_addAction1 (classTextGrid, 1, U"Get interval at time...", nullptr, 2, INTEGER_TextGrid_getIntervalAtTime); - praat_addAction1 (classTextGrid, 1, U"Get low interval at time...", nullptr, 2, INTEGER_TextGrid_getLowIntervalAtTime); - praat_addAction1 (classTextGrid, 1, U"Get high interval at time...", nullptr, 2, INTEGER_TextGrid_getHighIntervalAtTime); - praat_addAction1 (classTextGrid, 1, U"Get interval edge from time...", nullptr, 2, INTEGER_TextGrid_getIntervalEdgeFromTime); - praat_addAction1 (classTextGrid, 1, U"Get interval boundary from time...", nullptr, 2, INTEGER_TextGrid_getIntervalBoundaryFromTime); - praat_addAction1 (classTextGrid, 1, U"-- query interval labels --", nullptr, 2, nullptr); - praat_addAction1 (classTextGrid, 1, U"Count intervals where...", nullptr, 2, INTEGER_TextGrid_countIntervalsWhere); + praat_addAction1 (classTextGrid, 1, U"Get interval at time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getIntervalAtTime); + praat_addAction1 (classTextGrid, 1, U"Get low interval at time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getLowIntervalAtTime); + praat_addAction1 (classTextGrid, 1, U"Get high interval at time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getHighIntervalAtTime); + praat_addAction1 (classTextGrid, 1, U"Get interval edge from time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getIntervalEdgeFromTime); + praat_addAction1 (classTextGrid, 1, U"Get interval boundary from time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getIntervalBoundaryFromTime); + praat_addAction1 (classTextGrid, 1, U"-- query interval labels --", nullptr, praat_DEPTH_2, nullptr); + praat_addAction1 (classTextGrid, 1, U"Count intervals where...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_countIntervalsWhere); + praat_addAction1 (classTextGrid, 1, U"-- --", nullptr, praat_DEPTH_2, nullptr); + praat_addAction1 (classTextGrid, 1, U"Get number of features in interval...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_numberFeaturesInInterval); + praat_addAction1 (classTextGrid, 1, U"Get label of feature in interval...", nullptr, praat_DEPTH_2, STRING_TextGrid_labelFeaturesInInterval); + praat_addAction1 (classTextGrid, 1, U"Get feature from interval...", nullptr, praat_DEPTH_2, STRING_TextGrid_getFeatureFromInterval); + praat_addAction1 (classTextGrid, 1, U"Show features in interval...", nullptr, praat_DEPTH_2, STRING_TextGrid_showFeaturesInInterval); praat_addAction1 (classTextGrid, 1, U"Query point tier", nullptr, 1, nullptr); - praat_addAction1 (classTextGrid, 1, U"Get number of points...", nullptr, 2, INTEGER_TextGrid_getNumberOfPoints); - praat_addAction1 (classTextGrid, 1, U"Get time of point...", nullptr, 2, REAL_TextGrid_getTimeOfPoint); - praat_addAction1 (classTextGrid, 1, U"Get label of point...", nullptr, 2, STRING_TextGrid_getLabelOfPoint); - praat_addAction1 (classTextGrid, 1, U"-- query point from time --", nullptr, 2, nullptr); - praat_addAction1 (classTextGrid, 1, U"Get low index from time...", nullptr, 2, INTEGER_TextGrid_getLowIndexFromTime); - praat_addAction1 (classTextGrid, 1, U"Get high index from time...", nullptr, 2, INTEGER_TextGrid_getHighIndexFromTime); - praat_addAction1 (classTextGrid, 1, U"Get nearest index from time...", nullptr, 2, INTEGER_TextGrid_getNearestIndexFromTime); - praat_addAction1 (classTextGrid, 1, U"-- query point labels --", nullptr, 2, nullptr); - praat_addAction1 (classTextGrid, 1, U"Count points where...", nullptr, 2, INTEGER_TextGrid_countPointsWhere); + praat_addAction1 (classTextGrid, 1, U"Get number of points...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getNumberOfPoints); + praat_addAction1 (classTextGrid, 1, U"Get time of point...", nullptr, praat_DEPTH_2, REAL_TextGrid_getTimeOfPoint); + praat_addAction1 (classTextGrid, 1, U"Get label of point...", nullptr, praat_DEPTH_2, STRING_TextGrid_getLabelOfPoint); + praat_addAction1 (classTextGrid, 1, U"Get head of point...", nullptr, praat_DEPTH_2, STRING_TextGrid_headOfPoint); + praat_addAction1 (classTextGrid, 1, U"-- query point from time --", nullptr, praat_DEPTH_2, nullptr); + praat_addAction1 (classTextGrid, 1, U"Get low index from time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getLowIndexFromTime); + praat_addAction1 (classTextGrid, 1, U"Get high index from time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getHighIndexFromTime); + praat_addAction1 (classTextGrid, 1, U"Get nearest index from time...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_getNearestIndexFromTime); + praat_addAction1 (classTextGrid, 1, U"-- query point labels --", nullptr, praat_DEPTH_2, nullptr); + praat_addAction1 (classTextGrid, 1, U"Count points where...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_countPointsWhere); + praat_addAction1 (classTextGrid, 1, U"-- --", nullptr, praat_DEPTH_2, nullptr); + praat_addAction1 (classTextGrid, 1, U"Get number of features in point...", nullptr, praat_DEPTH_2, INTEGER_TextGrid_numberFeaturesInPoint); + praat_addAction1 (classTextGrid, 1, U"Get label of feature in point...", nullptr, praat_DEPTH_2, STRING_TextGrid_labelFeaturesInPoint); + praat_addAction1 (classTextGrid, 1, U"Get feature from point...", nullptr, praat_DEPTH_2, STRING_TextGrid_getFeatureFromPoint); + praat_addAction1 (classTextGrid, 1, U"Show features in point...", nullptr, praat_DEPTH_2, STRING_TextGrid_showFeaturesInPoint); praat_addAction1 (classTextGrid, 1, U"-- query labels --", nullptr, praat_DEPTH_1 | praat_DEPRECATED_2015, nullptr); praat_addAction1 (classTextGrid, 1, U"Count labels...", nullptr, praat_DEPTH_1 | praat_DEPRECATED_2015, INTEGER_TextGrid_countLabels); praat_addAction1 (classTextGrid, 0, U"Modify -", nullptr, 0, nullptr); @@ -1622,11 +1935,19 @@ void praat_uvafon_TextGrid_init () { praat_addAction1 (classTextGrid, 0, U"Remove right boundary...", nullptr, 2, MODIFY_TextGrid_removeRightBoundary); praat_addAction1 (classTextGrid, 0, U"Remove boundary at time...", nullptr, 2, MODIFY_TextGrid_removeBoundaryAtTime); praat_addAction1 (classTextGrid, 0, U"Set interval text...", nullptr, 2, MODIFY_TextGrid_setIntervalText); + praat_addAction1 (classTextGrid, 0, U"Set interval head text...", nullptr, praat_DEPTH_2, MODIFY_TextGrid_setIntervalHeadText); + praat_addAction1 (classTextGrid, 1, U"-- --", nullptr, 2, nullptr); + praat_addAction1 (classTextGrid, 1, U"Insert feature to interval...", nullptr, praat_DEPTH_2, MODIFY_TextGrid_insertFeatureToInterval); + praat_addAction1 (classTextGrid, 1, U"Remove feature from interval...", nullptr, praat_DEPTH_2, MODIFY_TextGrid_deleteFeatureFromInterval); praat_addAction1 (classTextGrid, 0, U"Modify point tier", nullptr, 1, nullptr); praat_addAction1 (classTextGrid, 0, U"Insert point...", nullptr, 2, MODIFY_TextGrid_insertPoint); praat_addAction1 (classTextGrid, 0, U"Remove point...", nullptr, 2, MODIFY_TextGrid_removePoint); praat_addAction1 (classTextGrid, 0, U"Remove points...", nullptr, 2, MODIFY_TextGrid_removePoints); praat_addAction1 (classTextGrid, 0, U"Set point text...", nullptr, 2, MODIFY_TextGrid_setPointText); + praat_addAction1 (classTextGrid, 0, U"Set point head text...", nullptr, praat_DEPTH_2, MODIFY_TextGrid_setPointHeadText); + praat_addAction1 (classTextGrid, 1, U"-- --", nullptr, 2, nullptr); + praat_addAction1 (classTextGrid, 1, U"Insert feature to point...", nullptr, praat_DEPTH_2, MODIFY_TextGrid_insertFeatureToPoint); + praat_addAction1 (classTextGrid, 1, U"Remove feature from point...", nullptr, praat_DEPTH_2, MODIFY_TextGrid_deleteFeatureFromPoint); praat_addAction1 (classTextGrid, 0, U"Analyse", nullptr, 0, nullptr); praat_addAction1 (classTextGrid, 0, U"Extract one tier...", nullptr, 0, NEW1_TextGrid_extractOneTier); praat_addAction1 (classTextGrid, 0, U"Extract tier...", U"*Extract one tier...", praat_DEPRECATED_2010, NEW1_TextGrid_extractTier); diff --git a/sys/melder.h b/sys/melder.h index a46bc8c36..8ef86bc00 100644 --- a/sys/melder.h +++ b/sys/melder.h @@ -1690,6 +1690,9 @@ void MelderString_copy (MelderString *me, Melder_14_OR_15_ARGS); void MelderString_copy (MelderString *me, Melder_16_TO_19_ARGS); void MelderString_ncopy (MelderString *me, const char32 *source, int64 n); +void MelderString_trim(MelderString *me); +bool MelderString_isEmptyAfterTrim(MelderString *me); + inline static void MelderString_append (MelderString *me, Melder_1_ARG) { const char32 *s1 = arg1._arg ? arg1._arg : U""; integer length1 = str32len (s1); integer sizeNeeded = me -> length + length1 + 1; diff --git a/sys/melder_strings.cpp b/sys/melder_strings.cpp index 358cd90a5..6af25d3ec 100644 --- a/sys/melder_strings.cpp +++ b/sys/melder_strings.cpp @@ -431,6 +431,30 @@ void MelderString_ncopy (MelderString *me, const char32 *source, int64 n) { my length = length; } +void MelderString_trim(MelderString *me){ + if(my bufferSize == 0) return; + const char32 * forwardPtr = my string; + int initialBlanks = 0; + while(*forwardPtr != U'\0' && *forwardPtr == U' '){ + initialBlanks++; + forwardPtr++; + } + + MelderString_copy(me, forwardPtr); + //Melder_free(forwardPtr); + + char32 * backwardPtr = my string + my length -1; + while(backwardPtr+1 != my string && *backwardPtr == U' '){ + *backwardPtr-- = U'\0'; + } + //Melder_free(backwardPtr); +} + +bool MelderString_isEmptyAfterTrim(MelderString *me){ + MelderString_trim(me); + return my length == 0; +} + /*void MelderString_append (MelderString *me, Melder_1_ARG) { const char32 *s1 = arg1._arg ? arg1._arg : U""; int64 length1 = _str32len (s1); int64 sizeNeeded = my length + length1 + 1;