From 6c358a842baaf6d211a5c8c1717d815e7813ed96 Mon Sep 17 00:00:00 2001 From: JSDurand Date: Sun, 11 Jul 2021 18:45:38 +0800 Subject: First commit Now I have a kind of piano like instrument. A violin-like instrument is being constructed. --- main.c | 588 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 588 insertions(+) create mode 100644 main.c (limited to 'main.c') diff --git a/main.c b/main.c new file mode 100644 index 0000000..43bf52b --- /dev/null +++ b/main.c @@ -0,0 +1,588 @@ +/* This is a program that can read in a text file and convert it to a + sound file. Since this is going to depend on ffmpeg's libraries, + otherwise I am only able to produce raw audio files, which are too + large and not very convenient to use, a makefile is included to + make the compiling process easier. */ + +/* include stuff */ +#include +#include +#include + +/* include FFMPEG stuff */ +#include + +#include +#include +#include +#include + +/* Self-Dependencies */ +#include "util.h" +#include "instrument.h" +#include "parser.h" + +/* #include */ + +/* helper functions */ + +/* FFMPEG helpers */ + +/* check that a given sample format is supported by the encoder */ +UNUSED +static int +check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) +{ + const enum AVSampleFormat *p = codec->sample_fmts; + + /* the array is terminated by -1 */ + for (int i = 0; *(p+i) != -1; i++) + fprintf(stderr, "supports %s\n", av_get_sample_fmt_name(*(p+i))); + + while (*p != AV_SAMPLE_FMT_NONE) { + if (*p == sample_fmt) + return 1; + p++; + } + return 0; +} + +__attribute__((__hot__, __unused__)) +static void +/* encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, + * FILE *output) */ +encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output) +{ + int ret = 0; + + /* send the frame for encoding */ + ret = avcodec_send_frame(ctx, frame); + if (ret < 0) { + fprintf(stderr, "Error sending the frame to the encoder\n"); + exit(1); + } + + /* read all the available output packets (in general there may be any + * number of them */ + while (ret >= 0) { + ret = avcodec_receive_packet(ctx, pkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + return; + else if (ret < 0) { + fprintf(stderr, "Error encoding audio frame\n"); + exit(1); + } + + fwrite(pkt->data, 1, pkt->size, output); + /* av_write_frame(afc, pkt); */ + av_packet_unref(pkt); + } +} + +/* select layout with the highest channel count */ +UNUSED +static int +select_channel_layout(const AVCodec *codec) +{ + const uint64_t *p; + uint64_t best_ch_layout = 0; + int best_nb_channels = 0; + + if (!codec->channel_layouts) + return AV_CH_LAYOUT_STEREO; + + p = codec->channel_layouts; + while (*p) { + int nb_channels = av_get_channel_layout_nb_channels(*p); + + if (nb_channels > best_nb_channels) { + best_ch_layout = *p; + best_nb_channels = nb_channels; + } + p++; + } + return best_ch_layout; +} + +UHA_ATTR +static inline Pulse +square_wave(float counter) +{ + return (fmod(counter, 2*M_PI) < M_PI) ? 1 : -1; +} + +UHA_ATTR +static inline Pulse +synthesize_wave(float theta) +{ + + /* return -1.0f + fmod(theta, 2*M_PI)/M_PI; */ + + /* return sin(theta); */ + + /* return asin(sin(theta)) * 2/M_PI; */ + + /* return pow(theta/2*M_PI, 2); */ + + /* return sin(theta)+0.442f*sin(2.0f*theta)+0.315f*sin(3.0f*theta) + * +0.083f*sin(4.0f*theta)+cos(theta)+0.442f*cos(2.0f*theta); */ + + /* piano like */ + + /* return sin(theta)+1.869*sin(2.0f*theta)+0.042f*sin(3.0f*theta) + * +0.022f*sin(4.0f*theta)+cos(theta); */ + + /* excellent piano like */ + + return ((0.65f*sin(theta)+0.5f*sin(2.0f*theta))*exp(-0.004f*theta)); + +} + +/* This is replaced by make_sound function. */ +/* UH_ATTR + * static inline WaveFrag + * oscillator(Volume v, Hertz h, Seconds s, Instrument *ins) + * { + * + * return 0.0f; + * } */ + +UHA_ATTR +static inline float +frequency_modulation(float theta, Hertz h, float fm) +{ + return theta+FM_AMP*theta*sin(fm); +} + +UH_ATTR +static WaveFrag +hstow(Volume v, Hertz h, Seconds s) +{ + LENT sample_num = (LENT) floor(SAMPLE_RATE * s); + float attack_step = 1.0f / (float) (sample_num * ATTACK_P); + float decay_step = 1.0f / (float) (sample_num * DECAY_P); + float sustain_step = 1.0f / (float) (sample_num * SUSTAIN_P); + float release_step = 1.0f / (float) (sample_num * RELEASE_P); + + float step = (float) (h * 2 * M_PI) / SAMPLE_RATE; + + float fm_step = (float) ((FM_FREQUENCY)*2*M_PI) / SAMPLE_RATE; + + unsigned char phase = 0, sustain_flag = 0; + + Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num); + + float adsrcounter = 0.0f, counter = 0.0f, fm_counter = 0.0f; + float theta = 0.0f, temp = 0.0f; + + for (LENT i = 0; i < sample_num; i++, counter += step, fm_counter += fm_step) { + /* theta = frequency_modulation(counter, (float) h*i/((float) SAMPLE_RATE), fm_counter); */ + theta=counter; + temp = synthesize_wave(theta); + temp += temp*temp*temp; + temp = synthesize_wave(temp); + temp += theta*exp(-2.0f*theta); + *(w+i) = (float) v * temp; + /* (synthesize_wave(temp)/\* + + * * 0.01*(2.0f*(float)rand()/(float) RAND_MAX - 1.0f) *\/) *//* * + * ((sustain_flag) ? SUSTAIN_LEVEL : adsrcounter) ;*/ + switch (phase) { + case 0: /* attack phase */ + adsrcounter += attack_step; + if (adsrcounter >= 1.0f) { + adsrcounter = 1.0f; + phase++; + } + break; + case 1: /* decay phase */ + adsrcounter -= decay_step; + if (adsrcounter <= SUSTAIN_LEVEL) { + adsrcounter = 0.0f; + sustain_flag = 1; + phase++; + } + break; + case 2: /* sustain phase */ + adsrcounter += sustain_step; + if (adsrcounter >= 1.0f) { + adsrcounter = SUSTAIN_LEVEL; + sustain_flag = 0; + phase++; + } + break; + default: /* release phase */ + adsrcounter -= release_step; + if (adsrcounter <= 0.0f) adsrcounter = 0.0f; + break; + } + } + + return (WaveFrag) { w, sample_num }; +} + +H_ATTR +static WaveFrag +sbtow(Volume v, Semitones st, Beats b) +{ + return hstow(v, stoh(st), (b * BEAT_DUR)); +} + +/* compose */ + +D_ATTR("play_sheet") +static WaveFrag +compose(Volume *vs, Semitones *sts, Beats *bs, LENT len) +{ + WaveFrag wf, *temp = MYALLOC(WaveFrag, len); + + LENT total_len = 0; + + for (LENT i = 0; i < len; i++) { + if (*(sts+i) >= -50) + *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i)); + else + *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i)); + total_len += (temp+i)->n; + } + + wf.n = total_len; + + wf.w = MYALLOC(Pulse, total_len); + + LENT counter = 0; + + for (int i = 0; i < len; i++) { + LENT local = (temp+i)->n; + + for (LENT j = 0; j < local; j++) + *(wf.w+counter++) = *((temp+i)->w+j); + + free((temp+i)->w); + } + + free(temp); + + return wf; +} + +/* Reading */ + +/* The file should contain an even number of numbers. For any natural + number N, the 2*N th number specifies the Semitone of the Nth note, + and the 2*N+1 number specifies the beats of the Nth note. */ + +U_ATTR +static void +read_sb(const char *str, int str_len, + int *len, Semitones **sts, Beats **bs) +{ + int str_counter = 0; + + float temp = 0; + + int semi_len = 0, b_len = 0; + Node_t semis = EMPTY_NODE, beats = EMPTY_NODE; + Node_t sn = EMPTY_NODE, bn = EMPTY_NODE; + Node_t *sp = NULL, *bp = NULL; + + LENT nth = 0; + + for (;str_counter < str_len; nth++) { + int number_chars_read = 0; + int result = sscanf(str+str_counter, "%f %n", &temp, &number_chars_read); + if (result < 1) break; + + str_counter += number_chars_read; + + if (nth % 2) { + /* odd */ + PUSH_NODE(b_len, bp, beats, temp, f); + } else { + /* even */ + PUSH_NODE(semi_len, sp, semis, temp, f); + } + } + + if (nth % 2) { + fprintf(stderr, "There should be an even number of floats in " + "the file, but found %lu.\n", + nth); + exit(1); + } + + *len = floor(nth/2.0f); + *sts = realloc(*sts, sizeof(float) * (*len)); + *bs = realloc(*bs, sizeof(float) * (*len)); + + for (int i = 0; i < *len; i++) { + POP_NODE(semi_len, temp, semis, sn, f); + *(*sts+*len-1-i) = temp; + + POP_NODE(b_len, temp, beats, bn, f); + *(*bs+*len-1-i) = temp; + } +} + +/* main */ + +int main(int argc, char **argv) +{ + const AVCodec *codec; + AVCodecContext *c = NULL; + /* AVOutputFormat *of; + * AVFormatContext *oc; */ + AVFrame *frame; + AVPacket *pkt; + int ret = 0; + float *samples; + + /* output format setup */ + + /* if (!(of = av_guess_format(NULL, DEFAULT_OUTPUT_NAME, "audio/aac"))) { + * fprintf(stderr, "cannot guess output format\n"); + * exit(1); + * } */ + + /* avformat_alloc_output_context2(&oc, of, NULL, NULL); */ + /* oc = avformat_alloc_context(); + * + * if (!oc) { + * fprintf(stderr, "cannot allocate output context\n"); + * exit(1); + * } */ + + /* oc->oformat = of; */ + /* oc->url = MYALLOC(char, sizeof(DEFAULT_OUTPUT_NAME)); + * for (int i = 0; i < sizeof(DEFAULT_OUTPUT_NAME); i++) + * oc->url[i] = DEFAULT_OUTPUT_NAME[i]; */ + + /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_READ_WRITE); + * + * if (ret<0) { + * fprintf(stderr, "cannot open %s\n", DEFAULT_OUTPUT_NAME); + * exit(1); + * } */ + + /* find the encoder */ + codec = avcodec_find_encoder(AV_CODEC_ID_MP3); + if (!codec) { + fprintf(stderr, "Codec not found\n"); + exit(1); + } + + /* set an option to place MOOV atom first. */ + + /* AVDictionary *opt = NULL; + * av_dict_set(&opt, "movflags", "faststart", 0); + * + * printf("of->audio_codec = %d\n", of->audio_codec); + * + * if (oc->oformat->flags & AVFMT_GLOBALHEADER) + * c->flags |= AVFMT_GLOBALHEADER; */ + + /* return 0; */ + + /* STD_BASE = (float) pow(2, 1.0f/12.0f); */ + + UNUSED int notes_len = 0; + + /* UNUSED Volume *vs = NULL; + * Semitones *sts = MYALLOC(Semitones, 1); + * Beats *bs = MYALLOC(Beats, 1); */ + + char *notes_content = MYALLOC(char, 500); + + LENT notes_str_len = 0; + + notes_str_len = (LENT) read_entire_file("notes.txt", ¬es_content); + +#ifdef DEBUG + fprintf(stderr, "notes content = %s\n", notes_content); +#endif + + PSheet psh = read_sheet(notes_content, notes_str_len); + + if (argc >= 2 && **(argv+1) == 'p') + print_sheet(psh); + +#ifdef DEBUG + fprintf(stderr, "after printing\n"); +#endif + + /* read_sb(notes_content, notes_str_len, ¬es_len, + * &sts, &bs); */ + +#ifdef DEBUG + fprintf(stderr, "notes len = %d\nfirst bs = %.2f\n", + notes_len, *bs); +#endif + + /* vs = MYALLOC(Volume, notes_len); + * for (int i = 0; i < notes_len;) *(vs+i++) = 0.3f; */ + + /* WaveFrag wf = compose(vs, sts, bs, notes_len); */ + WaveFrag wf = play_sheet(psh, 0.3f); + + destroy_sheet(psh); + + printf("wf n = %lu\n", wf.n); + + /* AVStream *os = avformat_new_stream(oc, NULL); */ + + c = avcodec_alloc_context3(codec); + if (!c) { + fprintf(stderr, "Could not allocate audio codec context\n"); + exit(1); + } + + /* check that the encoder supports FLTP pcm input */ + /* The P marks that this is "planar", i.e. the input is not + interleaved between channels. */ + c->sample_fmt = AV_SAMPLE_FMT_FLTP; + if (!check_sample_fmt(codec, c->sample_fmt)) { + fprintf(stderr, "Encoder does not support sample format %s", + av_get_sample_fmt_name(c->sample_fmt)); + exit(1); + } + + /* set other audio parameters supported by the encoder */ + c->sample_rate = (int) SAMPLE_RATE; + c->channel_layout = select_channel_layout(codec); + c->channels = av_get_channel_layout_nb_channels(c->channel_layout); + + /* this should be sizeof(float) * 8 * nb_channels * SAMPLE_RATE + + We divide by 1000 since the unit is KB.*/ + c->bit_rate = sizeof(float) * 8 * SAMPLE_RATE * c->channels / 1000; + + /* os->time_base = (AVRational) { 1, (int) SAMPLE_RATE }; */ + + /* open it */ + if (avcodec_open2(c, codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + exit(1); + } + + /* /\* set stream data *\/ + * os->codecpar->sample_rate = SAMPLE_RATE; + * os->codecpar->format = c->sample_fmt; + * os->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + * os->codecpar->codec_id = of->audio_codec; + * os->codecpar->bit_rate = c->bit_rate; + * os->codecpar->channels = c->channels; + * os->codecpar->channel_layout = c->channel_layout; + * os->codecpar->frame_size = c->frame_size; + * + * if (avformat_write_header(oc, &opt) < 0) { + * fprintf(stderr, "cannot write header\n"); + * exit(1); + * } + * + * av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */ + + /* return 0; */ + + /* audio_st.st->time_base = (AVRational){ 1, c->sample_rate }; + * + * if (oc->oformat->flags & AVFMT_GLOBALHEADER) + * c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; */ + + /* av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */ + + /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_WRITE); + * if (ret < 0) { + * fprintf(stderr, "Could not open '%s': %s\n", DEFAULT_OUTPUT_NAME, + * av_err2str(ret)); + * return 1; + * } + * + * ret = avformat_write_header(oc, &opt); + * if (ret < 0) { + * fprintf(stderr, "Error occurred when opening output file: %s\n", + * av_err2str(ret)); + * return 1; + * } */ + + printf("sample rate = %d, channels = %d, bit rate = %lu\n" + "frame size = %d\n", + c->sample_rate, c->channels, (long) c->bit_rate, + c->frame_size); + /* return 0; */ + + FILE *file = fopen(DEFAULT_OUTPUT_NAME, "wb"); + if (!file) { + fprintf(stderr, "Could not open %s\n", DEFAULT_OUTPUT_NAME); + exit(1); + } + + /* packet for holding encoded output */ + pkt = av_packet_alloc(); + if (!pkt) { + fprintf(stderr, "could not allocate the packet\n"); + exit(1); + } + + /* pkt->stream_index = os->index; */ + + /* frame containing input raw audio */ + frame = av_frame_alloc(); + if (!frame) { + fprintf(stderr, "Could not allocate audio frame\n"); + exit(1); + } + + frame->nb_samples = c->frame_size; + frame->format = c->sample_fmt; + frame->channel_layout = c->channel_layout; + + /* allocate the data buffers */ + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) { + fprintf(stderr, "Could not allocate audio data buffers\n"); + fprintf(stderr, "nbs = %d, format = %s, layout = %d\n", + frame->nb_samples, av_get_sample_fmt_name(frame->format), + av_get_channel_layout_nb_channels(frame->channel_layout)); + exit(1); + } + + /* write to the frame */ + + long frame_num = floor((float) wf.n/(float) c->frame_size) + 1; + long w_counter = 0; + + for (int i = 0; i < frame_num; i++, w_counter += c->frame_size) { + ret = av_frame_make_writable(frame); + if (ret < 0) { + fprintf(stderr, "cannot make frame writable\n"); + exit(1); + } + + for (int j = 0; j < c->channels; j++) { + samples = (float *)frame->data[j]; + + for (int k = 0; k < c->frame_size && k+w_counter < wf.n; k++) + samples[k] = *(wf.w+w_counter+k); + } + + encode(c, frame, pkt, file); + } + + /* fwrite(wf.w, sizeof(float), wf.n, file); */ + + /* flush the encoder */ + encode(c, NULL, pkt, file); + + /* free(vs); */ + /* free(sts); + * free(bs); */ + free(wf.w); + free(notes_content); + fclose(file); + + av_frame_free(&frame); + av_packet_free(&pkt); + avcodec_free_context(&c); + /* av_dict_free(&opt); */ + + return 0; +} -- cgit v1.2.3-18-g5258