from typing import List, Optional
from psynet.modular_page import ModularPage, PushButtonControl
from psynet.page import InfoPage
from psynet.timeline import CodeBlock, Module, Response, join
[docs]
class GMSI(Module):
"""
The `Goldsmiths Musical Sophistication Index` (GMSI) questionnaire.
Parameters
----------
label : str, default: "gmsi"
A label used to distinguish the module from other modules in the timeline. In the case of multiple GMSI instances within a single timeline this label also allows to differentiate between the variable names used to store the participant's final scores; see :class:`~psynet.field.VarStore` for details.
short_version : bool, optional, default: `False`
Whether to use the reduced set of 29 questions.
The citation for this short version is Lin, H. L., Frieler, K., & Müllensiefen, D. (2021).
Development and Validation of Short Forms of the Goldsmiths Musical Sophistication Index in Three Languages.
ICMPC/ESCOM 2021.
See also https://shiny.gold-msi.org/gmsiconfigurator/ and https://www.youtube.com/watch?v=x_0DomCP8Rs.
subscales: List, optional, default: `None`
The subscales to be included in the questionnaire. Possible subscales are `Active Engagement`, `Emotions`, `General`, `Musical Training`, `Perceptual Abilities, `Singing Abilities`, `Absolute Pitch`, `Instrument`, and `Start Age`. If no subscales are provided all subscales are selected. Overrides the `short_version` parameter.
info_page : InfoPage, optional, default: `None`
An :class:`~psynet.page.InfoPage` object to be used as an introductionary first page.
If none is supplied the default one is displayed (see source code).
"""
def __init__(
self,
label: str = "gmsi",
short_version: bool = False,
subscales: Optional[List] = None,
info_page: Optional[InfoPage] = None,
):
self.label = label
self.short_version = short_version
self.subscales = subscales
if info_page is None:
info_page = InfoPage(
"The following questions are designed to assess your musicality. Please answer as accurately as possible.",
time_estimate=5,
)
questions = []
if self.subscales is not None:
questions = {
label: question_data()[label]
for label, data in question_data().items()
if any(subscale in self.subscales for subscale in data["subscales"])
}
elif self.short_version:
questions_labels = [
f"q_{question_number:02d}"
for question_number in [
1,
4,
19,
27,
30,
36,
3,
10,
20,
26,
39,
2,
12,
16,
22,
35,
6,
13,
14,
24,
28,
29,
32,
8,
11,
23,
33,
40,
41,
]
]
questions = {
label: question_data()[label]
for label, data in question_data().items()
if label in questions_labels
}
else:
questions = question_data()
self.elts = join(
info_page,
[
GMSIPage(
label,
data["prompt"],
data["choices"],
labels=data.get("labels"),
arrange_vertically=data["arrange_vertically"],
module_label=self.label,
)
for label, data in questions.items()
],
self.save_scores,
)
super().__init__(self.label, self.elts)
@property
def save_scores(self):
return CodeBlock(
lambda participant: participant.var.set(
self.label, self.compile_results(participant)
)
)
def compile_results(self, participant):
# get all responses for the participant from the database
responses = Response.query.filter_by(participant_id=participant.id)
# filter to retain only gmsi responses for current module
responses = [
response
for response in responses
if response.question in question_data().keys()
and response.metadata["gmsi_label"] == self.label
]
# calculate score for each question
response_scores = {
response.question: self.calculate_score(response.question, response.answer)
for response in responses
}
# group scores by subscale
grouped_scores = {}
for question, score in response_scores.items():
subscales = question_data()[question]["subscales"]
for subscale in subscales:
if subscale in grouped_scores.keys():
grouped_scores[subscale].append(score)
else:
grouped_scores[subscale] = [score]
# calculate arithmetic mean for each subscale
mean_scores_per_scale = {
group[0]: (
round(sum(group[1]) / len(group[1]), 6)
if all(isinstance(item, int) for item in group[1])
else group[1][0]
)
for group in grouped_scores.items()
}
if self.subscales:
# remove subscales not asked for
mean_scores_per_scale = {
subscale: score
for subscale, score in mean_scores_per_scale.items()
if subscale in self.subscales
}
return {
"response_scores": response_scores,
"mean_scores_per_scale": mean_scores_per_scale,
}
@staticmethod
def calculate_score(question, answer):
if isinstance(answer, str):
answer = answer.replace('"', "")
if question == "q_40" and answer == "19":
return None
if question in ["q_32", "q_41"]:
return answer
raw_value = int(answer)
return (8 - raw_value) if question_data()[question]["inverted"] else raw_value
class GMSIPage(ModularPage):
def __init__(
self,
label,
prompt,
choices,
labels=None,
arrange_vertically=False,
module_label="gmsi",
):
self.label = label
self.prompt = prompt
self.choices = choices
self.labels = labels
self.arrange_vertically = arrange_vertically
self.time_estimate = 5
self.module_label = module_label
control = PushButtonControl(
choices=self.choices,
labels=self.labels,
arrange_vertically=self.arrange_vertically,
)
super().__init__(
self.label,
self.prompt,
control=control,
time_estimate=self.time_estimate,
)
def metadata(self, **kwargs):
return {"gmsi_label": self.module_label}
def agreement_scale():
return {
"choices": list(range(1, 8)),
"labels": [
"Completely disagree",
"Strongly disagree",
"Disagree",
"Neither agree nor disagree",
"Agree",
"Strongly agree",
"Completely agree",
],
}
def question_data():
return {
"q_01": {
"prompt": "I spend a lot of my free time doing music-related activities.",
"inverted": False,
"subscales": ["Active Engagement", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_02": {
"prompt": "I engaged in regular, daily practice of a musical instrument (including voice) for the following number of years:",
"inverted": False,
"subscales": ["Musical Training", "General"],
"choices": agreement_scale()["choices"],
"labels": ["0", "1", "2", "3", "4-5", "6-9", "10 or more"],
"arrange_vertically": True,
},
"q_03": {
"prompt": "I sometimes choose music that can trigger shivers down my spine.",
"inverted": False,
"subscales": ["Emotions"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_04": {
"prompt": "I enjoy writing about music, for example on blogs and forums.",
"inverted": False,
"subscales": ["Active Engagement", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_05": {
"prompt": "If somebody starts singing a song I don't know, I can usually join in.",
"inverted": False,
"subscales": ["Singing Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_06": {
"prompt": "I am able to judge whether someone is a good singer or not.",
"inverted": False,
"subscales": ["Perceptual Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_07": {
"prompt": "I usually know when I'm hearing a song for the first time.",
"inverted": False,
"subscales": ["Perceptual Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_08": {
"prompt": "I can sing or play music from memory.",
"inverted": False,
"subscales": ["Singing Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_09": {
"prompt": "I'm intrigued by musical styles I'm not familiar with and want to find out more.",
"inverted": False,
"subscales": ["Active Engagement"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_10": {
"prompt": "Pieces of music rarely evoke emotions for me.",
"inverted": True,
"subscales": ["Emotions"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_11": {
"prompt": "I am able to hit the right notes when I sing along with a recording.",
"inverted": False,
"subscales": ["Singing Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_12": {
"prompt": "At the peak of my interest, I practised my primary instrument (including voice) for the following number of hours per day:",
"inverted": False,
"subscales": ["Musical Training", "General"],
"choices": agreement_scale()["choices"],
"labels": ["0", "0.5", "1", "1.5", "2", "3-4", "5 or more"],
"arrange_vertically": True,
},
"q_13": {
"prompt": "I find it difficult to spot mistakes in a performance of a song even if I know the tune.",
"inverted": True,
"subscales": ["Perceptual Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_14": {
"prompt": "I can compare and discuss differences between two performances or versions of the same piece of music.",
"inverted": False,
"subscales": ["Perceptual Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_15": {
"prompt": "I have trouble recognizing a familiar song when played in a different way or by a different performer.",
"inverted": True,
"subscales": ["Perceptual Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_16": {
"prompt": "I have never been complimented for my talents as a musical performer.",
"inverted": True,
"subscales": ["Musical Training", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_17": {
"prompt": "I have attended the following number of live music events as an audience member in the past twelve months:",
"inverted": False,
"subscales": ["Active Engagement"],
"choices": agreement_scale()["choices"],
"labels": ["0", "1", "2", "3", "4-6", "7-10", "11 or more"],
"arrange_vertically": True,
},
"q_18": {
"prompt": "I have had formal training in music theory for the following number of years",
"inverted": False,
"subscales": ["Musical Training"],
"choices": agreement_scale()["choices"],
"labels": ["0", "0.5", "1", "2", "3", "4-6", "7 or more"],
"arrange_vertically": True,
},
"q_19": {
"prompt": "I often read or search the internet for things related to music.",
"inverted": False,
"subscales": ["Active Engagement", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_20": {
"prompt": "I often pick certain music to motivate or excite me.",
"inverted": False,
"subscales": ["Emotions"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_21": {
"prompt": "I have had formal training on a musical instrument (including voice) during my lifetime for the following number of years:",
"inverted": False,
"subscales": ["Musical Training"],
"choices": agreement_scale()["choices"],
"labels": ["0", "0.5", "1", "2", "3-5", "6-9", "10 or more"],
"arrange_vertically": True,
},
"q_22": {
"prompt": "I can play the following number of musical instruments (including voice):",
"inverted": False,
"subscales": ["Musical Training", "General"],
"choices": agreement_scale()["choices"],
"labels": ["0", "1", "2", "3", "4", "5", "6 or more"],
"arrange_vertically": True,
},
"q_23": {
"prompt": "I am able to sing in harmony when somebody is singing a familiar tune.",
"inverted": False,
"subscales": ["Singing Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_24": {
"prompt": "I can tell when people sing or play out of time with the beat.",
"inverted": False,
"subscales": ["Perceptual Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_25": {
"prompt": "I am able to identify what is special about a given musical piece.",
"inverted": False,
"subscales": ["Emotions", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_26": {
"prompt": "I am able to talk about the emotions that a piece of music evokes for me.",
"inverted": False,
"subscales": ["Emotions"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_27": {
"prompt": "I don't spend much of my disposable income on music.",
"inverted": True,
"subscales": ["Active Engagement"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_28": {
"prompt": "I can tell when people sing or play out of tune.",
"inverted": False,
"subscales": ["Perceptual Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_29": {
"prompt": "When I sing, I have no idea whether I'm in tune or not.",
"inverted": True,
"subscales": ["Perceptual Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_30": {
"prompt": "Music is kind of an addiction for me – I couldn't live without it.",
"inverted": False,
"subscales": ["Active Engagement", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_31": {
"prompt": "I listen attentively to music for the following amount of time per day:",
"inverted": False,
"subscales": ["Active Engagement"],
"choices": agreement_scale()["choices"],
"labels": [
"0-15 min",
"15-30 min",
"30-60 min",
"60-90 min",
"2 hrs",
"2-3 hrs",
"4 hrs or more",
],
"arrange_vertically": True,
},
"q_32": {
"prompt": "The instrument I play best (including voice) is:",
"inverted": False,
"subscales": ["Instrument"],
"choices": [
"I don't play any instrument.",
"voice",
"piano",
"guitar",
"drums",
"xylophone",
"flute",
"oboe",
"clarinet",
"bassoon",
"trumpet",
"trombone",
"tuba",
"saxophone",
"horn",
"violin",
"cello",
"viola",
"double bass",
"harp",
"other",
],
"arrange_vertically": False,
},
"q_33": {
"prompt": "I don't like singing in public because I'm afraid that I would sing wrong notes.",
"inverted": True,
"subscales": ["Singing Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_34": {
"prompt": "When I hear a piece of music I can usually identify its genre.",
"inverted": False,
"subscales": ["Perceptual Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_35": {
"prompt": "I would not consider myself a musician.",
"inverted": True,
"subscales": ["Musical Training", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_36": {
"prompt": "I keep track of new music that I come across (e.g. new artists or recordings).",
"inverted": False,
"subscales": ["Active Engagement"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_37": {
"prompt": "After hearing a new song two or three times, I can usually sing it by myself.",
"inverted": False,
"subscales": ["Singing Abilities", "General"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_38": {
"prompt": "I only need to hear a new tune once and I can sing it back hours later.",
"inverted": False,
"subscales": ["Singing Abilities"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_39": {
"prompt": "Music can evoke my memories of past people and places.",
"inverted": False,
"subscales": ["Emotions"],
"choices": agreement_scale()["choices"],
"labels": agreement_scale()["labels"],
"arrange_vertically": True,
},
"q_40": {
"prompt": "What age did you start to play an instrument?",
"inverted": False,
"subscales": ["Start Age"],
"choices": list(range(2, 20)),
"labels": list(range(2, 19)) + ["I don't play any instrument."],
"arrange_vertically": False,
},
"q_41": {
"prompt": "Do you have absolute pitch? Absolute or perfect pitch is the ability to recognise and name an isolated musical tone without a reference tone, e.g. being able to say 'F#' if someone plays that note on the piano.",
"inverted": False,
"subscales": ["Absolute Pitch"],
"choices": ["Yes", "No"],
"arrange_vertically": False,
},
}