nuttx-apps/examples/fmsynth/keyboard_main.c

450 lines
12 KiB
C

/****************************************************************************
* apps/examples/fmsynth/keyboard_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 "operator_algorithm.h"
#include "music_scale.h"
/****************************************************************************
* Pre-processor
****************************************************************************/
#define APP_FS (48000)
#define APP_BPS (16)
#define APP_CHNUM (2)
#define APP_DEFAULT_VOL (400)
/****************************************************************************
* Private Data Type
****************************************************************************/
struct app_options
{
int volume;
int mode;
};
struct kbd_s
{
struct nxaudio_s nxaudio;
FAR fmsynth_sound_t *sound;
FAR fmsynth_op_t *carrier;
volatile int request_scale;
};
struct key_convert_s
{
int key;
char key_str;
FAR const char *dispstr;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
extern int board_external_amp_mute_control(bool en);
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 kbd_s g_kbd;
static struct nxaudio_callbacks_s cbs =
{
app_dequeue_cb,
app_complete_cb,
app_user_cb
};
static struct key_convert_s key_convert[] =
{
{ OCTAVE(4, MUSIC_SCALE_C), 'a', "O4C" },
{ OCTAVE(4, MUSIC_SCALE_CS), 'w', "O4C+" },
{ OCTAVE(4, MUSIC_SCALE_D), 's', "O4D" },
{ OCTAVE(4, MUSIC_SCALE_DS), 'e', "O4D+" },
{ OCTAVE(4, MUSIC_SCALE_E), 'd', "O4E" },
{ OCTAVE(4, MUSIC_SCALE_F), 'f', "O4F" },
{ OCTAVE(4, MUSIC_SCALE_FS), 't', "O4F+" },
{ OCTAVE(4, MUSIC_SCALE_G), 'g', "O4G" },
{ OCTAVE(4, MUSIC_SCALE_GS), 'y', "O4G+" },
{ OCTAVE(4, MUSIC_SCALE_A), 'h', "O4A" },
{ OCTAVE(4, MUSIC_SCALE_AS), 'u', "O4A+" },
{ OCTAVE(4, MUSIC_SCALE_B), 'j', "O4B" },
{ OCTAVE(5, MUSIC_SCALE_C), 'k', "O5C" },
{ OCTAVE(5, MUSIC_SCALE_CS), 'o', "O5C+" },
{ OCTAVE(5, MUSIC_SCALE_D), 'l', "O5D" },
{ OCTAVE(5, MUSIC_SCALE_DS), 'p', "O5D+" },
{ OCTAVE(5, MUSIC_SCALE_E), ';', "O5E" },
};
#define MAX_KEYCONVERT (sizeof(key_convert)/sizeof(key_convert[0]))
/****************************************************************************
* Private functions
****************************************************************************/
/****************************************************************************
* name: convert_key2idx
****************************************************************************/
static int convert_key2idx(char key)
{
int i;
for (i = 0; i < MAX_KEYCONVERT; i++)
{
if (key == key_convert[i].key_str)
{
return i;
}
}
return -1;
}
/****************************************************************************
* name: tick_callback
****************************************************************************/
static void tick_callback(unsigned long arg)
{
FAR struct kbd_s *kbd = (FAR struct kbd_s *)(uintptr_t)arg;
int scale = kbd->request_scale;
if (scale != -1)
{
fmsynthsnd_set_soundfreq(kbd->sound,
musical_scale[scale]);
kbd->request_scale = -1;
}
}
/****************************************************************************
* name: app_dequeue_cb
****************************************************************************/
static void app_dequeue_cb(unsigned long arg,
FAR struct ap_buffer_s *apb)
{
FAR struct kbd_s *kbd = (FAR struct kbd_s *)(uintptr_t)arg;
apb->curbyte = 0;
apb->flags = 0;
if (kbd->request_scale != -1)
{
apb->nbytes = fmsynth_rendering(kbd->sound,
(FAR int16_t *)apb->samp,
apb->nmaxbytes / sizeof(int16_t),
kbd->nxaudio.chnum,
tick_callback, (unsigned long)kbd);
}
else
{
apb->nbytes = fmsynth_rendering(kbd->sound,
(FAR int16_t *)apb->samp,
apb->nmaxbytes / sizeof(int16_t),
kbd->nxaudio.chnum,
NULL, 0);
}
nxaudio_enqbuffer(&kbd->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: audio_loop_thread
****************************************************************************/
static FAR void *audio_loop_thread(pthread_addr_t arg)
{
FAR struct kbd_s *kbd = (FAR struct kbd_s *)arg;
nxaudio_start(&kbd->nxaudio);
nxaudio_msgloop(&kbd->nxaudio, &cbs, (unsigned long)kbd);
return NULL;
}
/****************************************************************************
* name: create_audio_thread
****************************************************************************/
static pthread_t create_audio_thread(FAR struct kbd_s *kbd)
{
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)kbd);
pthread_setname_np(pid, "musickeyboard_thread");
return pid;
}
/****************************************************************************
* name: init_fmmusi_soundsc
****************************************************************************/
static int init_keyboard_sound(FAR struct kbd_s *kbd, int fs, int mode)
{
int ret = ERROR;
fmsynth_initialize(fs);
kbd->sound = fmsynthsnd_create();
if (kbd->sound)
{
kbd->carrier = mode == 0 ? fmsynthutil_algorithm0() :
mode == 1 ? fmsynthutil_algorithm1() :
mode == 2 ? fmsynthutil_algorithm2() :
NULL;
if (!kbd->carrier)
{
fmsynthsnd_delete(kbd->sound);
return ret;
}
fmsynthsnd_set_operator(kbd->sound, kbd->carrier);
kbd->request_scale = -1;
ret = OK;
}
return ret;
}
/****************************************************************************
* name: fin_keyboard
****************************************************************************/
static void fin_keyboard(FAR struct kbd_s *kbd)
{
fin_nxaudio(&kbd->nxaudio);
fmsynthutil_delete_ops(kbd->carrier);
fmsynthsnd_delete(kbd->sound);
}
/****************************************************************************
* name: print_help
****************************************************************************/
static void print_help(FAR char *name)
{
printf("nsh> %s ([-v (volume from 0 to 100)]) ([-m (mode 0, 1 or 2)])\n",
name);
}
/****************************************************************************
* name: configure_option
****************************************************************************/
static int configure_option(FAR struct app_options *option,
int argc, FAR 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;
default:
return ERROR;
}
}
return OK;
}
/****************************************************************************
* name: print_keyusage()
****************************************************************************/
static void print_keyusage(void)
{
int i;
printf("press any key below to make a sound.\n");
for (i = 0; i < MAX_KEYCONVERT; i++)
{
printf(" [KEY]: %c, [CODE]: %s\n",
key_convert[i].key_str,
key_convert[i].dispstr);
}
printf("\n");
}
/****************************************************************************
* 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;
int key_idx;
if (configure_option(&appopt, argc, argv) != OK)
{
print_help(argv[0]);
return -1;
}
ret = init_nxaudio(&g_kbd.nxaudio, APP_FS, APP_BPS, APP_CHNUM);
if (ret < 0)
{
printf("init_nxaoud() returned with error!!\n");
return -1;
}
nxaudio_setvolume(&g_kbd.nxaudio, appopt.volume);
ret = init_keyboard_sound(&g_kbd, APP_FS, appopt.mode);
if (ret != OK)
{
fin_nxaudio(&g_kbd.nxaudio);
printf("init_keyboard_sound() error!!\n");
return -1;
}
/* Render audio samples in audio buffers */
for (i = 0; i < g_kbd.nxaudio.abufnum; i++)
{
app_dequeue_cb((unsigned long)&g_kbd,
g_kbd.nxaudio.abufs[i]);
}
pid = create_audio_thread(&g_kbd);
printf("Start %s\n", argv[0]);
print_keyusage();
while (running)
{
key = getchar();
if (key != EOF)
{
switch (key)
{
case 'q':
running = false;
break;
default:
key_idx = convert_key2idx(key);
if (key_idx >= 0)
{
g_kbd.request_scale = key_convert[key_idx].key;
printf("%s \n", key_convert[key_idx].dispstr);
fflush(stdout);
}
break;
}
}
}
board_external_amp_mute_control(true);
nxaudio_stop(&g_kbd.nxaudio);
pthread_join(pid, NULL);
fin_keyboard(&g_kbd);
return ret;
}