]> git.sesse.net Git - c64tapwav/blob - audioreader.cpp
Compile in native by default, since AVX helps so much.
[c64tapwav] / audioreader.cpp
1 #include <stdio.h>
2
3 extern "C" {
4
5 #define __STDC_CONSTANT_MACROS
6
7 #include <libavcodec/avcodec.h>
8 #include <libavformat/avformat.h>
9 #include <libswresample/swresample.h>
10 #include <libavutil/avutil.h>
11
12 }
13
14 #include <memory>
15 #include <vector>
16
17 namespace {
18
19 struct AVFormatCloserAndDeleter {
20         void operator() (AVFormatContext *ctx) {
21                 avformat_close_input(&ctx);
22                 avformat_free_context(ctx);
23         }
24 };
25
26 struct AVCodecContextDeleter {
27         void operator() (AVCodecContext *ctx) {
28                 avcodec_close(ctx);
29                 av_freep(&ctx);
30         }
31 };
32
33 struct SwrContextDeleter {
34         void operator() (SwrContext *swr) {
35                 swr_free(&swr);
36         }
37 };
38
39 struct AVPacketDeleter {
40         void operator() (AVPacket *pkt) {
41                 av_free_packet(pkt);
42         }
43 };
44
45 #if (LIBAVCODEC_VERSION_MAJOR >= 55)
46 struct AVFrameDeleter {
47         void operator() (AVFrame *frame) {
48                 av_frame_free(&frame);
49         }
50 };
51 #endif
52
53 struct AVSampleDeleter {
54         void operator() (uint8_t *data) {
55                 av_freep(&data);
56         }
57 };
58
59 void convert_samples(SwrContext *swr, int sample_rate, const uint8_t **data, int nb_samples, std::vector<float> *samples)
60 {
61         int max_out_samples = nb_samples + swr_get_delay(swr, sample_rate);
62         if (max_out_samples == 0) {
63                 return;
64         }
65         uint8_t *output;
66         av_samples_alloc(&output, nullptr, 1, max_out_samples, AV_SAMPLE_FMT_FLT, 0);
67         std::unique_ptr<uint8_t, AVSampleDeleter> output_deleter(output);
68
69         int out_samples = swr_convert(swr, &output, max_out_samples, data, nb_samples);
70         if (out_samples > 0) {
71                 const float* start = reinterpret_cast<const float *>(output);
72                 const float* end = start + out_samples;
73                 samples->insert(samples->end(), start, end);
74         }
75 }
76
77 int decode_packet(const char *filename, AVCodecContext *codec_ctx, SwrContext *swr, AVFrame *audio_frame, AVPacket *packet, int *got_frame, std::vector<float> *samples)
78 {
79         *got_frame = 0;
80         int len1 = avcodec_decode_audio4(codec_ctx, audio_frame, got_frame, packet);
81         if (len1 < 0 || !*got_frame) {
82                 return len1;
83         }
84
85         if (audio_frame->channel_layout != codec_ctx->channel_layout ||
86             audio_frame->sample_rate != codec_ctx->sample_rate) {
87                 fprintf(stderr, "%s: Channel layout or sample rate changed mid-file\n", filename);
88                 *got_frame = false;
89                 return len1;
90         }
91         convert_samples(swr, codec_ctx->sample_rate, (const uint8_t **)audio_frame->data, audio_frame->nb_samples, samples);
92         return len1;
93 }
94
95 }  // namespace
96
97 bool read_audio_file(const char *filename, std::vector<float> *samples, int *sample_rate)
98 {
99         av_register_all();
100
101         AVFormatContext *format_ctx = nullptr;
102         if (avformat_open_input(&format_ctx, filename, nullptr, nullptr) != 0) {
103                 fprintf(stderr, "Couldn't open %s\n", filename);
104                 return false;
105         }
106         std::unique_ptr<AVFormatContext, AVFormatCloserAndDeleter> format_ctx_closer(format_ctx);
107
108         if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
109                 fprintf(stderr, "%s: Couldn't find stream information\n", filename);
110                 return false;
111         }
112
113         // Find the first audio stream.
114         int audio_stream_index = -1;
115         for (unsigned i = 0; i < format_ctx->nb_streams; ++i) {
116                 if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
117                         audio_stream_index = i;
118                         break;
119                 }
120         }
121         if (audio_stream_index == -1) {
122                 fprintf(stderr, "%s: Couldn't find an audio stream\n", filename);
123                 return false;
124         }
125
126         AVCodec *codec = avcodec_find_decoder(format_ctx->streams[audio_stream_index]->codec->codec_id);
127         if (codec == nullptr) {
128                 fprintf(stderr, "%s: Unsupported codec\n", filename);
129                 return false;
130         }
131
132         AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
133         std::unique_ptr<AVCodecContext, AVCodecContextDeleter> codec_ctx_deleter(codec_ctx);
134         if (avcodec_copy_context(codec_ctx, format_ctx->streams[audio_stream_index]->codec) != 0) {
135                 fprintf(stderr, "%s: Couldn't copy codec context\n", filename);
136                 return false;
137         }
138
139         if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
140                 fprintf(stderr, "%s: Couldn't open codec\n", filename);
141                 return false;
142         }
143
144         // Init resampler (to downmix to mono and convert to s16).
145         if (codec_ctx->channel_layout == 0) {
146                 codec_ctx->channel_layout = av_get_default_channel_layout(codec_ctx->channels);
147         }
148         SwrContext *swr = swr_alloc_set_opts(
149                 nullptr,
150                 AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_FLT, codec_ctx->sample_rate,
151                 codec_ctx->channel_layout, codec_ctx->sample_fmt, codec_ctx->sample_rate,
152                 0, nullptr);
153         std::unique_ptr<SwrContext, SwrContextDeleter> swr_deleter(swr);
154         if (swr_init(swr) < 0) {
155                 fprintf(stderr, "%s: Couldn't initialize resampler\n", filename);
156                 return false;
157         }
158
159         AVPacket packet;
160 #if (LIBAVCODEC_VERSION_MAJOR >= 55)
161         AVFrame *audio_frame = av_frame_alloc();
162         std::unique_ptr<AVFrame, AVFrameDeleter> audio_frame_deleter(audio_frame);
163 #else
164         AVFrame frame_holder {};
165         AVFrame *audio_frame = &frame_holder;
166 #endif
167         while (av_read_frame(format_ctx, &packet) >= 0) {
168                 std::unique_ptr<AVPacket, AVPacketDeleter> av_packet_deleter(&packet);
169
170                 if (packet.stream_index != audio_stream_index) {
171                         continue;
172                 }
173
174                 while (packet.size > 0) {
175                         int got_frame = 0;
176                         int len1 = decode_packet(filename, codec_ctx, swr, audio_frame, &packet, &got_frame, samples);
177                         if (len1 < 0) {
178                                 fprintf(stderr, "%s: Couldn't decode audio\n", filename);
179                                 return false;
180                         }
181                         if (!got_frame) {
182                                 break;
183                         }
184                         packet.data += len1;
185                         packet.size -= len1;
186                 }
187         }
188
189         // Flush any delayed data from the end.
190         packet.data = nullptr;
191         packet.size = 0;
192         int got_frame = 0;
193         do {
194                 int len1 = decode_packet(filename, codec_ctx, swr, audio_frame, &packet, &got_frame, samples);
195                 if (len1 < 0) {
196                         fprintf(stderr, "%s: Couldn't decode audio\n", filename);
197                         return false;
198                 }
199         } while (got_frame);
200
201         // Convert any leftover samples from the converter.
202         convert_samples(swr, codec_ctx->sample_rate, nullptr, 0, samples);
203
204         *sample_rate = codec_ctx->sample_rate;
205
206         return true;
207 }