- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我必须构建合成器,并且我正在使用 C 来对我的 ATmega128A 进行编程。我需要记录按下的键盘并在一段时间后播放它们。对于键盘按下,我在 main.c 中使用轮询。为了弹奏键盘,我使用 Timer1。每次计时器到期时,我都会存储键盘频率和增量计数器。在播放过程中,我首先计算持续时间,然后播放该间隔。当我想播放存储的歌曲时,它会滴答一段时间并开始发出长声音。
此外,我希望能够同时按下、录制和播放键盘。你能为此建议一些算法吗?
main.c
#include <avr/io.h>
#include <avr/interrupt.h>
#include "keypad.h"
unsigned char temp; // to get keyboard input to play a note
unsigned char option; //to choose the embedded music to play
#define DELAY 1000
int main(void)
{
DDRG = 0xff; // To send sound to BUZ speakers (BUZ is connected to PG.4)
DDRD = 0x00; // Make it input, to get corresponding key to play a note
PORTD = 0xff; // All bits are 1s, so no button is pressed in the beginning
sei(); //Set Interrupt flag as enabled in SREG register
option = no_music; //No music is played on startup, this is default mode for free playing
// This loop keeps playing forever, so the main functionality
// of the program is below
DDRB = 0xff;
DDRD = 0x00; //ready for input
while(1)
{
temp = PIND; //store keyboard input for temporary variable
//PORTB = PIND;
switch(temp)
{
case 254: { // if 1st pin of PORTD is pressed
play_note(notes5[0]); // play corresponding note from octave 5 for 200ms
break;
}
case 253: { // if 2nd pin of PORTD is pressed
play_note(notes5[1]);
break;
}
case 251: { // if 3rd pin of PORTD is pressed
play_note(notes5[2]);
break;
}
case 247: { // if 4th pin of PORTD is pressed
play_note(notes5[3]);
break;
}
case 239: { // if 5th pin of PORTD is pressed
play_note(notes5[4]);
break;
}
case 223: { // if 6th pin of PORTD is pressed
play_note(notes5[5]);
break;
}
case 191: { // if 7th pin of PORTD is pressed
play_note(notes5[6]);
break;
}
case 127: {
if(isRecordingEnabled){
disableRecording();
//toggling LED as the sign of playing the record
toggleLED();
custom_delay_ms(DELAY);
toggleLED();
custom_delay_ms(DELAY);
custom_delay_ms(DELAY);
play_record();
}else{
//toggling LED as the sign of record start
toggleLED();
enableRecording();
}
}
}
}
return 0;
}
键盘.c
#include "structs.h"
#include "play.h"
#define F_CPU 16000000UL // 16 MHz
#include <util/delay.h>
#define BUFFER_SIZE 100
struct played_note buffer[BUFFER_SIZE];
int i = 0;
int8_t isRecordingEnabled = 0;
int8_t recordIndex = 0;
int8_t pressedNote;
int8_t isPressed = 0;
int8_t isPlaying = 0;
unsigned int ms_count = 0;
#define INTERVAL 100
#define DELAY_VALUE 0xFF
ISR(TIMER1_COMPA_vect){
// every time when timer0 reaches corresponding frequency,
// invert the output signal for BUZ, so it creates reflection, which leads to sound generation
//check whether the key was pressed because
//when the recording is enabled the interrupt is working make sound
if(isPressed || isPlaying)
PORTG = ~(PORTG);
if(isRecordingEnabled){
if(PIND == DELAY_VALUE)
pressedNote = DELAY_VALUE;
if(i == 0){
buffer[i].note = pressedNote;
buffer[i].counter = 0;
i++;
}else{
if(buffer[i - 1].note == pressedNote){
//the same note is being pressed
buffer[i - 1].counter++;
}else{
buffer[i++].note = pressedNote;
buffer[i].counter = 0;
}
}
}
}
void initTimer1(){
TIMSK = (1 << OCIE1A); //Timer1 Comparator Interrupt is enabled
TCCR1B |= (1 << WGM12) | (1 << CS12); //CTC mode, prescale = 256
}
void stopTimer1(){
TIMSK &= ~(1UL << OCIE1A);
TCCR1A = 0; //stop the timer1
TIFR = (1 << OCF1A); //Clear the timer1 Comparator Match flag
}
void enableRecording(){
isRecordingEnabled = 1;
i = 0;
ms_count = 0;
initTimer1();
}
void disableRecording(){
isRecordingEnabled = 0;
stopTimer1();
}
//Timer1A
void play_note_during(unsigned int note, unsigned int duration){
OCR1A = note;
pressedNote = note;
isPressed = 1;
initTimer1();
custom_delay_ms(duration);
stopTimer1();
isPressed = 0;
}
//Timer1A
void play_note(unsigned int note){
play_note_during(note, INTERVAL);
}
void play_record(){
isPlaying = 1;
recordIndex = 0;
int duration;
while(recordIndex < i){
PORTB = buffer[return].counter << 8;
duration = INTERVAL * buffer[recordIndex].counter;
if(buffer[recordIndex].note == DELAY_VALUE)
custom_delay_ms(duration);
else
play_note_during(buffer[recordIndex].note, duration);
recordIndex++;
}
isPlaying = 0;
}
可以在以下 github 存储库中找到更多引用: https://github.com/bedilbek/music_simulation
最佳答案
实际上,您关于如何录制和重放按键的问题应该由另一个关于如何同时播放多个声音的问题来解决。现在您只使用可变频率的 PWM 输出。但这只允许您生成单个方形波形。您不能演奏两个音符(除非使用另一个计时器和另一个 PWM 输出)。相反,我建议您使用最高频率的 PWM,并应用 RC 或 LC 滤波器将高频 PWM 信号平滑为波形,然后将该波形应用于放大器和扬声器以发出声音。通过这种方法,您可以生成不同的波形,将它们混合在一起,使它们更响亮或更安静,甚至应用“淡出”效果使它们听起来像钢琴。但你的问题不是关于如何生成那种波形,所以如果你想知道你应该开始另一个问题。
那么,回到你的问题。
而不是有一个单一的过程,即开始音符,暂停,然后才返回;我建议你有几个程序,一个 play_note(note)
- 它将开始播放音符并立即返回(同时音符继续播放)。当然,stop_note(note)
- 如果指定的音符被播放,它将停止。另外,我建议您将音符编号而不是频率或计时器周期传递给 play
函数。我们假设 0 是可能的最低音符(例如 C2),然后它们按半音顺序排列:1 - C#2、2 - D2、.... 11 - B2、12 - C3 ... 等等。
第一次,您可以重新制作单音符演奏程序以匹配这一点。
// #include <avr/pgmspace.h> to store tables in the flash memory
PROGMEM uint16_t const note_ocr_table[] = {
OCR1A_VALUE_FOR_C2, OCR1A_VALUE_FOR_C2_SHARP, ... etc
}; // a table to map note number into OCR1A value
#define NOTE_NONE 0xFF
static uint8_t current_note = NOTE_NONE;
void play_note(uint8_t note) {
if (note >= (sizeof(note_ocr_table) / sizeof(note_ocr_table[0])) return; // do nothing on the wrong parameter;
uint16_t ocr1a_val = pgm_read_word(¬e_ocr_table[note]);
TIMSK = (1 << OCIE1A); //Timer1 Comparator Interrupt is enabled // why you need this? May be you want to use just inverting OC1A output?
TCCR1B |= (1 << WGM12) | (1 << CS12); //CTC mode, prescale = 256 // you may want to use lesser prescalers and higher OCR1A values ?
OCR1A = ocr1a_val;
if (TCNT1 >= ocr1a_val) TCNT1 = 0; // do not miss the compare match when ORC1A is changed to lower values;
current_note = note;
}
void stop_note(uint8_t note) {
if (note == current_note) { // ignore stop for non-current note.
TIMSK &= ~(1UL << OCIE1A);
TCCR1A = 0; //stop the timer1
TIFR = (1 << OCF1A); //Clear the timer1 Comparator Match flag
current_note = NOTE_NONE; // No note is playing
}
}
所以,现在你的任务非常简单:你应该定期拉取按键的状态,假设每秒 61 次(基于一些具有 1:1024 预分频器溢出的 8 位定时器),并且需要注意:哪些键改变了状态。如果按下某个键,则调用 play_note
开始相应的音符。如果松开按键,您将调用 stop_note
,同时您还会计算自上次事件以来经过了多少个计时器周期。录制时,您只需将这些事件插入数组“按下X键”或“释放X键”或“X定时器周期到期”。播放时,您只需执行向后过程,扫描数组并执行命令:调用 play_note
、stop_note
或等待确切数量的计时器周期(如果是)暂停。
您也可以使用表格来扫描按钮,而不是编写巨大的switch
语句
// number of element in the port-state arrays
#define A 0
#define B 1
#define C 2
#define D 3
#define E 4
#define F 5
typedef struct {
port_index uint8_t;
mask uint8_t;
} KeyLocation;
PROGMEM KeyLocation const key_location[] = {
{ B, (1 << 1) }, // where C2 is located, e.g. PB1
{ E, (1 << 3) }, // where C#2 is located, e.g. PE3
...
}
uint16_t ticks_from_prev_event = 0;
uint8_t port_state_prev[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0XFF};
for (;;) { // main loop
wait_tick_timer_to_overflow();
// latching state of the pins
uint8_t port_state[6] = {PINA, PINB, PINC, PIND, PINE, PINF};
for (uint8_t i = 0 ; i < (sizeof(key_location) / sizeof(key_location[0])) ; i++) {
uint8_t port_idx = pgm_read_byte(&key_location[i].port_index);
uint8_t mask = pgm_read_byte(&key_location[i].mask);
if ((port_state[port_idx] & mask) != (port_state_prev[port_idx] & mask)) { // if pin state was changed
if (is_recording && (ticks_from_prev_event > 0)) {
put_into_record_pause(ticks_from_prev_event); // implement it on your own
}
if ((port_state[port_idx] & mask) == 0) { // key is pressed
play_note(i);
if (is_recording) {
put_into_record_play_note(i); // implement
}
} else { // key is released
stop_note(i);
if (is_recording) {
put_into_record_stop_note(i); // implement
}
}
}
}
// the current state of the pins now becomes a previous
for (uint8_t i = 0 ; i < (sizeof(port_state) / sizeof(port_state[0])) ; i++) {
port_state_prev[i] = port_state[i];
}
if (ticks_from_prev_event < 65535) ticks_from_prev_event++;
}
put_into_record_...
按照您的意愿实现。
播放将同样简单(在模板下方,您将从函数名称中建议它们应该做什么)
while (has_more_data_in_the_recording()) {
if (next_is_play()) {
play_note(get_note_from_recording())
} else if (next_is_stop()) {
play_note(get_note_from_recording())
} else {
uint16_t pause = get_pause_value_from_recording();
while (pause > 0) {
pause--;
wait_tick_timer_to_overflow();
}
}
}
这种方法有两个好处:
1) 无论演奏模块可以演奏多少个音符,按键在按下和释放时都会被记录,因此,所有同时出现的按键将被同时记录和重放。
2) 在同一时刻按下多少个音符并不重要。由于暂停和按键事件是分开记录的,因此在重放期间,所有同时按下的按键都会同时重放,不会产生“琶音”效果
关于c - 合成器 - 记录按下的键盘,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50075635/
我必须构建合成器,并且我正在使用 C 来对我的 ATmega128A 进行编程。我需要记录按下的键盘并在一段时间后播放它们。对于键盘按下,我在 main.c 中使用轮询。为了弹奏键盘,我使用 Time
我正在寻找可以与我的 .NET 应用程序集成的合成器。那里有可用的 .NET 合成器吗?有没有办法将 VST 插件集成到您的 .NET 应用程序中? 我想创建基于 MIDI 输入的声音,最好是比正弦波
我似乎无法改变乐器。我切换了仪器的值,但输出没有任何不同。无论我尝试什么值(value),我都只能得到一个钢琴乐器来演奏。这是下面的简单代码。有没有人有什么建议?还是我缺少仪器对象的基础知识? imp
我想网络上并没有“wayland 服务器编程初学者指南”,而 weston 的源代码看起来令人望而生畏。 是否有一个准系统 Wayland 服务器可以让我了解从哪里开始?我可以在 X 下运行我可以确认
我一直在研究人们的 Java 合成器,但不知道如何生成他们程序的简单版本。我的目标是用 java 创建一个简单的测试音,用户可以通过按某些键来更改它。例如,按“r”将提高幅度,或“f”将增加频率。我只
在我的 Java 应用程序中,我有一个 JTabbedPane 和一个合成器外观。 l&f 由 xml 文件定义。这对我来说非常有效。 现在的挑战是:我希望 JTabbedPane 选项卡中的文本垂直
我想开发一个非常简单的程序来将 PC 键盘映射到钢琴键盘:每次用户按下一个键时,都会生成一个 MIDI 事件,并且一个独立的采样器/合成器(例如 SFZ+ 或其他)接收这些事件并播放声音。 我能够生成
我在 codebuild 上运行 cdk synth 时遇到问题,它只是说:You are not authorized to perform this operation. 这是一个 CDK 应用程
我的 JTextField 无法获得白色背景,我做错了什么?(我知道图像没有画在中心以显示文本,但我将背景设置为白色;没有效果) -- XML --
我想用 Java 实现一个 Midi 合成器。(合成器 = 代表一个或多个乐器的新设备) 我实现的是javax.sound.midi.Receiver: package at.bachmann.se.
我正在检查今天的 Moog Synth Google 涂鸦,献给 Robert Moog, 当我遇到以下一段 html 代码时: (您可以查看源代码并为 执行 Ctrl+F,您将在第一
我已经在 Introjucer 中为简单的 vst 合成器生成了源文件和项目文件。代码编译时出现一个奇怪的错误:错误 C2259:“SimpleSynthAudioProcessor”:无法实例化抽象
在过去的几周里,我一直在从事一个对我来说非常新的项目,并且我正在边做边学。我正在使用 Raspberry Pi 2 构建一个合成器,并使用 Python3 对其进行编码,因为我对该语言有一些基本知识,
我有一个 Javascript 应用程序可以创建一些音频并播放它们。音调生成为 WAV 文件,然后像这样注入(inject).... document.getElementById("play
我是一名优秀的程序员,十分优秀!