1c23a095ea
if (key != EOF) ~~~ ^ ~~~ Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
564 lines
16 KiB
C
564 lines
16 KiB
C
/****************************************************************************
|
|
* apps/examples/fmsynth/mmlplayer_main.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#include <limits.h>
|
|
|
|
#include <nuttx/audio/audio.h>
|
|
#include <audioutils/fmsynth.h>
|
|
#include <audioutils/nxaudio.h>
|
|
#include <audioutils/mml_parser.h>
|
|
|
|
#include "operator_algorithm.h"
|
|
#include "music_scale.h"
|
|
#include "mmlplayer_score.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor
|
|
****************************************************************************/
|
|
|
|
#define APP_FS (48000)
|
|
#define APP_BPS (16)
|
|
#define APP_CHNUM (2)
|
|
#define CARRIER_LEVEL (25.f / 100.f)
|
|
|
|
#define APP_DEFAULT_VOL (1000)
|
|
|
|
/****************************************************************************
|
|
* Private Data Type
|
|
****************************************************************************/
|
|
|
|
struct app_options
|
|
{
|
|
int volume;
|
|
int mode;
|
|
};
|
|
|
|
struct mmlplayer_s
|
|
{
|
|
struct nxaudio_s nxaudio;
|
|
|
|
/* Right hand sound */
|
|
|
|
FAR fmsynth_sound_t *rsound[2]; /* Need 2 sounds for CHORD */
|
|
FAR fmsynth_op_t *rop[2]; /* Need 2 sounds for CHORD */
|
|
|
|
int rtick;
|
|
FAR char *rscore;
|
|
struct music_macro_lang_s rmml;
|
|
|
|
/* Left hand sound */
|
|
|
|
FAR fmsynth_sound_t *lsound;
|
|
FAR fmsynth_op_t *lop;
|
|
int ltick;
|
|
FAR char *lscore;
|
|
struct music_macro_lang_s lmml;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static void app_dequeue_cb(unsigned long arg,
|
|
FAR struct ap_buffer_s *apb);
|
|
static void app_complete_cb(unsigned long arg);
|
|
static void app_user_cb(unsigned long arg,
|
|
FAR struct audio_msg_s *msg, FAR bool *running);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static struct mmlplayer_s g_mmlplayer;
|
|
|
|
static struct nxaudio_callbacks_s cbs =
|
|
{
|
|
app_dequeue_cb,
|
|
app_complete_cb,
|
|
app_user_cb
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* name: print_note
|
|
****************************************************************************/
|
|
|
|
static void print_note(bool LR, int index, int length)
|
|
{
|
|
printf("%c: O%d%c : %d\n", LR ? 'R' : 'L',
|
|
index / 12, "CcDdEFfGgAaB"[index % 12], length);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: print_chord
|
|
****************************************************************************/
|
|
|
|
static void print_chord(bool LR, int index1, int index2, int length)
|
|
{
|
|
printf("%c: [O%d%c, O%d%c] : %d\n", LR ? 'R' : 'L',
|
|
index1 / 12, "CcDdEFfGgAaB"[index1 % 12],
|
|
index2 / 12, "CcDdEFfGgAaB"[index2 % 12],
|
|
length);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: update_righthand_note
|
|
****************************************************************************/
|
|
|
|
static void update_righthand_note(FAR struct mmlplayer_s *fmmsc)
|
|
{
|
|
int mml_ret;
|
|
struct mml_result_s mml_result;
|
|
|
|
fmmsc->rtick = 0;
|
|
do
|
|
{
|
|
mml_ret = parse_mml(&fmmsc->rmml, &fmmsc->rscore, &mml_result);
|
|
switch (mml_ret)
|
|
{
|
|
case MML_TYPE_NOTE:
|
|
fmmsc->rtick = mml_result.length;
|
|
fmsynthsnd_set_soundfreq(fmmsc->rsound[0],
|
|
musical_scale[mml_result.note_idx[0]]);
|
|
fmsynthsnd_set_volume(fmmsc->rsound[0], CARRIER_LEVEL);
|
|
fmsynthsnd_set_volume(fmmsc->rsound[1], 0.f);
|
|
print_note(1, mml_result.note_idx[0], mml_result.length);
|
|
break;
|
|
|
|
case MML_TYPE_CHORD:
|
|
fmmsc->rtick = mml_result.length;
|
|
fmsynthsnd_set_soundfreq(fmmsc->rsound[0],
|
|
musical_scale[mml_result.note_idx[0]]);
|
|
fmsynthsnd_set_soundfreq(fmmsc->rsound[1],
|
|
musical_scale[mml_result.note_idx[1]]);
|
|
fmsynthsnd_set_volume(fmmsc->rsound[0], CARRIER_LEVEL);
|
|
fmsynthsnd_set_volume(fmmsc->rsound[1], CARRIER_LEVEL);
|
|
print_chord(1, mml_result.note_idx[0], mml_result.note_idx[1],
|
|
mml_result.length);
|
|
break;
|
|
|
|
case MML_TYPE_REST:
|
|
fmmsc->rtick = mml_result.length;
|
|
fmsynthsnd_set_volume(fmmsc->rsound[0], 0.f);
|
|
fmsynthsnd_set_volume(fmmsc->rsound[1], 0.f);
|
|
printf("R: Rest : %d\n", mml_result.length);
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Do nothing */
|
|
|
|
break;
|
|
}
|
|
}
|
|
while (!fmmsc->rtick && mml_ret != MML_TYPE_EOF);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: update_lefthand_note
|
|
****************************************************************************/
|
|
|
|
static void update_lefthand_note(FAR struct mmlplayer_s *fmmsc)
|
|
{
|
|
int mml_ret;
|
|
struct mml_result_s mml_result;
|
|
|
|
fmmsc->ltick = 0;
|
|
do
|
|
{
|
|
mml_ret = parse_mml(&fmmsc->lmml, &fmmsc->lscore, &mml_result);
|
|
switch (mml_ret)
|
|
{
|
|
case MML_TYPE_NOTE:
|
|
fmmsc->ltick = mml_result.length;
|
|
fmsynthsnd_set_soundfreq(fmmsc->lsound,
|
|
musical_scale[mml_result.note_idx[0]]);
|
|
fmsynthsnd_set_volume(fmmsc->lsound, CARRIER_LEVEL);
|
|
print_note(0, mml_result.note_idx[0], mml_result.length);
|
|
break;
|
|
|
|
case MML_TYPE_REST:
|
|
fmmsc->ltick = mml_result.length;
|
|
fmsynthsnd_set_volume(fmmsc->lsound, 0.f);
|
|
printf("L: Rest : %d\n", mml_result.length);
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Do nothing */
|
|
|
|
break;
|
|
}
|
|
}
|
|
while (!fmmsc->ltick && mml_ret != MML_TYPE_EOF);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: tick_callback
|
|
****************************************************************************/
|
|
|
|
static void tick_callback(unsigned long arg)
|
|
{
|
|
FAR struct mmlplayer_s *fmmsc = (FAR struct mmlplayer_s *)(uintptr_t)arg;
|
|
|
|
fmmsc->rtick--;
|
|
fmmsc->ltick--;
|
|
|
|
if (fmmsc->rtick <= 0)
|
|
{
|
|
update_righthand_note(fmmsc);
|
|
}
|
|
|
|
if (fmmsc->ltick <= 0)
|
|
{
|
|
update_lefthand_note(fmmsc);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: app_dequeue_cb
|
|
****************************************************************************/
|
|
|
|
static void app_dequeue_cb(unsigned long arg,
|
|
FAR struct ap_buffer_s *apb)
|
|
{
|
|
FAR struct mmlplayer_s *mmlplayer = (struct mmlplayer_s *)(uintptr_t)arg;
|
|
|
|
apb->curbyte = 0;
|
|
apb->flags = 0;
|
|
apb->nbytes = fmsynth_rendering(mmlplayer->lsound,
|
|
(FAR int16_t *)apb->samp,
|
|
apb->nmaxbytes / sizeof(int16_t),
|
|
mmlplayer->nxaudio.chnum,
|
|
tick_callback,
|
|
(unsigned long)(uintptr_t)mmlplayer);
|
|
nxaudio_enqbuffer(&mmlplayer->nxaudio, apb);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: app_complete_cb
|
|
****************************************************************************/
|
|
|
|
static void app_complete_cb(unsigned long arg)
|
|
{
|
|
/* Do nothing.. */
|
|
|
|
printf("Audio loop is Done\n");
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: app_user_cb
|
|
****************************************************************************/
|
|
|
|
static void app_user_cb(unsigned long arg,
|
|
FAR struct audio_msg_s *msg, FAR bool *running)
|
|
{
|
|
/* Do nothing.. */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: autio_loop_thread
|
|
****************************************************************************/
|
|
|
|
static FAR void *audio_loop_thread(pthread_addr_t arg)
|
|
{
|
|
struct mmlplayer_s *mmlplayer = (FAR struct mmlplayer_s *)arg;
|
|
|
|
nxaudio_start(&mmlplayer->nxaudio);
|
|
nxaudio_msgloop(&mmlplayer->nxaudio, &cbs,
|
|
(unsigned long)(uintptr_t)mmlplayer);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: create_audio_thread
|
|
****************************************************************************/
|
|
|
|
static pthread_t create_audio_thread(FAR struct mmlplayer_s *mmlplayer)
|
|
{
|
|
pthread_t pid;
|
|
pthread_attr_t tattr;
|
|
struct sched_param sparam;
|
|
|
|
pthread_attr_init(&tattr);
|
|
sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 9;
|
|
pthread_attr_setschedparam(&tattr, &sparam);
|
|
pthread_attr_setstacksize(&tattr, 4096);
|
|
|
|
pthread_create(&pid, &tattr, audio_loop_thread,
|
|
(pthread_addr_t)mmlplayer);
|
|
pthread_setname_np(pid, "mmlplayer_thread");
|
|
|
|
return pid;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: delete_sounds
|
|
****************************************************************************/
|
|
|
|
static void delete_sounds(FAR struct mmlplayer_s *mmlplayer)
|
|
{
|
|
if (mmlplayer->rop[0])
|
|
{
|
|
fmsynthutil_delete_ops(mmlplayer->rop[0]);
|
|
}
|
|
|
|
if (mmlplayer->rop[1])
|
|
{
|
|
fmsynthutil_delete_ops(mmlplayer->rop[1]);
|
|
}
|
|
|
|
if (mmlplayer->lop)
|
|
{
|
|
fmsynthutil_delete_ops(mmlplayer->lop);
|
|
}
|
|
|
|
if (mmlplayer->rsound[0])
|
|
{
|
|
fmsynthsnd_delete(mmlplayer->rsound[0]);
|
|
}
|
|
|
|
if (mmlplayer->rsound[1])
|
|
{
|
|
fmsynthsnd_delete(mmlplayer->rsound[1]);
|
|
}
|
|
|
|
if (mmlplayer->lsound)
|
|
{
|
|
fmsynthsnd_delete(mmlplayer->lsound);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: init_fmmusi_soundsc
|
|
****************************************************************************/
|
|
|
|
static int init_mmlplayer_sound(FAR struct mmlplayer_s *mmlplayer, int fs,
|
|
int mode)
|
|
{
|
|
CODE fmsynth_op_t *(*opfunc)(void);
|
|
|
|
opfunc = mode == 0 ? fmsynthutil_algorithm0 :
|
|
mode == 1 ? fmsynthutil_algorithm1 :
|
|
mode == 2 ? fmsynthutil_algorithm2 :
|
|
NULL;
|
|
|
|
fmsynth_initialize(fs);
|
|
|
|
mmlplayer->rop[0] = NULL;
|
|
mmlplayer->rop[1] = NULL;
|
|
mmlplayer->lop = NULL;
|
|
|
|
mmlplayer->rsound[0] = fmsynthsnd_create();
|
|
mmlplayer->rsound[1] = fmsynthsnd_create();
|
|
mmlplayer->lsound = fmsynthsnd_create();
|
|
|
|
if (mmlplayer->rsound[0] && mmlplayer->rsound[1] && mmlplayer->lsound)
|
|
{
|
|
mmlplayer->rop[0] = opfunc();
|
|
mmlplayer->rop[1] = opfunc();
|
|
mmlplayer->lop = opfunc();
|
|
|
|
if (mmlplayer->rop[0] && mmlplayer->rop[1] && mmlplayer->lop)
|
|
{
|
|
fmsynthsnd_set_operator(mmlplayer->rsound[0], mmlplayer->rop[0]);
|
|
fmsynthsnd_set_operator(mmlplayer->rsound[1], mmlplayer->rop[1]);
|
|
fmsynthsnd_set_operator(mmlplayer->lsound, mmlplayer->lop);
|
|
fmsynthsnd_set_operator(mmlplayer->lsound, mmlplayer->lop);
|
|
|
|
fmsynthsnd_add_subsound(mmlplayer->lsound, mmlplayer->rsound[0]);
|
|
fmsynthsnd_add_subsound(mmlplayer->lsound, mmlplayer->rsound[1]);
|
|
}
|
|
else
|
|
{
|
|
delete_sounds(mmlplayer);
|
|
return ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete_sounds(mmlplayer);
|
|
return ERROR;
|
|
}
|
|
|
|
mmlplayer->rtick = 0;
|
|
mmlplayer->ltick = 0;
|
|
|
|
init_mml(&mmlplayer->rmml, fs, 120, 4, 4);
|
|
init_mml(&mmlplayer->lmml, fs, 120, 4, 3);
|
|
|
|
mmlplayer->rscore = (FAR char *)floh_walzer_right;
|
|
mmlplayer->lscore = (FAR char *)floh_walzer_left;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: fin_mmlplayer
|
|
****************************************************************************/
|
|
|
|
static void fin_mmlplayer(FAR struct mmlplayer_s *mmlplayer)
|
|
{
|
|
fin_nxaudio(&mmlplayer->nxaudio);
|
|
delete_sounds(mmlplayer);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: print_help
|
|
****************************************************************************/
|
|
|
|
static void print_help(FAR char *name)
|
|
{
|
|
printf("nsh> %s ([-v (volume)]) ([-m (mode)])\n", name);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* name: configure_option
|
|
****************************************************************************/
|
|
|
|
static int configure_option(FAR struct app_options *option,
|
|
int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
option->volume = APP_DEFAULT_VOL;
|
|
option->mode = 0;
|
|
while ((opt = getopt(argc, argv, "hv:m:")) != ERROR)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'v':
|
|
option->volume = atoi(optarg) * 10;
|
|
if (option->volume < 0 || option->volume > 1000)
|
|
{
|
|
option->volume = 400;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
option->mode = atoi(optarg);
|
|
if (option->mode < 0 || option->mode >= 3)
|
|
{
|
|
option->mode = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
return ERROR;
|
|
break;
|
|
|
|
default:
|
|
return ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* name: main
|
|
****************************************************************************/
|
|
|
|
int main(int argc, FAR char *argv[])
|
|
{
|
|
int i;
|
|
int ret;
|
|
int key;
|
|
bool running = true;
|
|
pthread_t pid;
|
|
struct app_options appopt;
|
|
|
|
printf("Start %s\n", argv[0]);
|
|
|
|
if (configure_option(&appopt, argc, argv) != OK)
|
|
{
|
|
print_help(argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
ret = init_nxaudio(&g_mmlplayer.nxaudio, APP_FS, APP_BPS, APP_CHNUM);
|
|
if (ret < 0)
|
|
{
|
|
printf("init_nxaoud() returned with error!!\n");
|
|
return -1;
|
|
}
|
|
|
|
nxaudio_setvolume(&g_mmlplayer.nxaudio, appopt.volume);
|
|
|
|
ret = init_mmlplayer_sound(&g_mmlplayer, APP_FS, appopt.mode);
|
|
if (ret != OK)
|
|
{
|
|
printf("init_mmlplayer_sound() returned error.\n");
|
|
fin_nxaudio(&g_mmlplayer.nxaudio);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < g_mmlplayer.nxaudio.abufnum; i++)
|
|
{
|
|
app_dequeue_cb((unsigned long)&g_mmlplayer,
|
|
g_mmlplayer.nxaudio.abufs[i]);
|
|
}
|
|
|
|
pid = create_audio_thread(&g_mmlplayer);
|
|
|
|
while (running)
|
|
{
|
|
key = getchar();
|
|
if (key != EOF)
|
|
{
|
|
switch (key)
|
|
{
|
|
case 'q':
|
|
running = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
nxaudio_stop(&g_mmlplayer.nxaudio);
|
|
pthread_join(pid, NULL);
|
|
|
|
fin_mmlplayer(&g_mmlplayer);
|
|
|
|
return ret;
|
|
}
|