今天福利彩票开奖号码:How it’s made: flashcards with Italian slang for musicians

March 7th, 2018. Tagged: ffmpeg, Music, react, tools

Update: Now in French too
Update 2: and in German
Update 3: now with Web Speech API (scroll to the bottom)

Here's a little app that gives you flashcards of Italian words used in music:
https://www.onlinemusictools.com/italiano/
It also pronounces the words in four different voices.

The code for the tool:
https://github.com/stoyan/italiano

A few implementation notes after the break (screenshot).

React CRA-ft

The tool is a little React app. Its bones are generated by create-react-app. It also uses a wee additional tool I call CRAFT (Create React App From Template). More about these here.

Wikipedia Table-to-JSON

The Italian words I found on Wikipedia, neatly divided into sections and tables. Just as I opened the browser console to start hacking on a script to scrape these tables, I remembered I already have a tool for that!

The process wasn't completely lacking manual intervention, but relatively painlessly I got a nice chunk of JSON files, one for each category of words, .

Speak

The cute part about this tool is the pronunciation of the words. For this, I reached to the help of MacOS's say command-line tool. This tool comes free with the OS and you can tweak the voices in your Accessibility preferences (short post about all that here).

I thought I'd write a script to loop thought the JSON files and then say each word of each file with each of the 4 Italian voices that are available.

You can see the whole script but here's just the main loop:

readDir(dataDir).forEach(f => {
  if (f.startsWith('.')) {
    return; // no .DS_Store etc, thank you
  }
  const file = path.resolve(dataDir, f);
  const jsonData = require(file);
  [
    "Alice",
    "Federica",
    "Luca",
    "Paola",
  ].forEach(voice => {    
    jsonData.forEach(definition => {
      const word = definition[0];
      const outfile = `voices/${voice}/${justLetters(word)}`; // .aiff is assumed
      console.log(outfile);
      spawn('say', ['-v', voice, '-o', outfile, word]);
    });
  });
});

So if you have the word "Soprano" the script runs:

say -v Alice -o voices/Alice/soprano Soprano

... then Federica instead of Alice and so on, for each of the 4 voices. And you end up with voices/Alice/soprano.aiff audio file.

Once all is done, you go in each voice's dir and convert all AIFF files to smaller, compressed MP3 using ffmpeg:

for f in *.aiff; do ffmpeg -i $f "${f%.*}.mp3"; done

And delete the sources:

rm -rf *.aiff

Reuse the language data

Please. My tool/UI is out there for you to practice, but I know there are tons of flashcard-style and language-learning apps out there. If you want to take the structured data I hereby slaved over and import it to your favorite app, the JSON and MP3 files are self-contained in this directory:
tree/master/public/italiano.

Let me know if you do something with this.

say -v Stoyan Ciao cari!

Thanks for reading! Enjoy the flashcards and say and all that.

Update: Web Speech API

Thanks to Marcel Duran's tweet I figured I was living under a rock and missed out on all the fun that is the Web Speech API.

So for browsers that support that API which is a lot of browsers, people don't need to download MP3 and the whole say jazz is unnecessary. These words can be generated in the browser. Yeweeyeye! Yaw! Yeet!

First bump though - browsers. See what happens when you try to check what voices are available:

Huh? You call the same thing and get different results. Not cool. Turns out in FF and Chrome this API is asynchronous. And the right way is to subscribe to an event:

speechSynthesis.onvoiceschanged = () => {
  voices = speechSynthesis.getVoices().filter(v => v.lang === 'it-IT');
}

Cool. Turns out in Safari there's no onvoiceschanged. But getVoices() appeared synchronous in my testing.

So with all the browser sniffing, here's what I ended up with in order to get a list of Italian-speaking voices:

let webvoices = null;
if (
  'SpeechSynthesisUtterance' in window &&
  'speechSynthesis' in window
) {
  if ('onvoiceschanged' in speechSynthesis) {
    speechSynthesis.onvoiceschanged = () => {
      webvoices = getVoices();
    }
  } else if (speechSynthesis.getVoices) {
      webvoices = getVoices();
  }
}

function getVoices() {
  return speechSynthesis.getVoices().filter(v => v.lang === 'it-IT' && v.localService);
}

(The localService bit is so that there's no download, because Chrome offers more voices but they require internet connection)

Now webvoices is my array of Italian speakers and I randomly pick one every time you hit Say.

If webvoices is still null, I fall back to what I had before.

    if (webvoices) {
      const u = new SpeechSynthesisUtterance(term[0]);
      u.voice = webvoices[Math.floor(Math.random() * webvoices.length)];
      speechSynthesis.speak(u);
    } else {
      this.state.audio[Math.floor(Math.random() * this.state.audio.length)].play();  
    }

Awesome! Here's the diff and the Safari follow-up.

Update: moved back to the MP3 while keeping the web speech for offline use. I just didn't like how it sounds in French, especially words like "prelude" (sounds like prelune) and "rapide" (again sounds like rapine)

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam.

Meanwhile, hit me up on twitter @stoyanstefanov


  • 运城市在长三角招商引资149.9亿元 2019-04-08
  • 济南五胞胎雪虎宝宝亮相 四雌一雄萌态十足 2019-04-08
  • 去产能迎年中考 煤炭、钢铁企业债务问题依然存在 2019-04-07
  • 最高检等四部门出台意见 指导依法办理恐怖活动和极端主义犯罪案件 2019-04-07
  • 广州市黄埔区人民法院公告专栏 2019-03-28
  • 这个“海之宁”是个死抱着相对论旧谬误不放,疯狂反对科学新真理的跳梁“小丑”,这个跳梁“小丑”根本就不懂得尊重客观事实及其规律,总是无视、脱离、歪曲客观... 2019-03-22
  • 如何理解孔子这句话?北大教授胡军动情论生死 2019-03-21
  • 海外舆论关注中国最新军备 称赞习近平主席强军号令 2019-03-19
  • 女性之声——全国妇联 2019-03-19
  • 美司法部科米在希拉里邮件门调查中存在过失 2019-03-17
  • “四新”彰显党的十九大思想灵魂和精髓要义 2018-08-17
  • 热身赛-U17女篮73-68立陶宛 杨舒宇19分刘禹彤13分 2018-08-16
  • 七月流火:一半是甜蜜 一半是仁慈 2018-08-15
  • “嫦娥”飞向月球背面,将会揭开哪些秘密? 2018-08-15
  • 一代枭雄身后事:“曹操墓”认定过程缘何一波三折? 2018-08-04
  • 829| 277| 141| 536| 64| 813| 788| 722| 730| 686|